clean_dcache_by_address misses the last cache line when addr is not line-aligned

Đang mở Phù hợp với người mới
#708 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 2 ngày

@jseketa đang làm issue này rồi.

Từ ngày 3/11/2026.

  • #709 của @jseketa — đang mở

Đánh giá

Độ khó
2/5
Thời gian dự kiến
1-3 giờ
Mức phù hợp với người mới
79/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
rust
Lĩnh vực
embedded-iot

Hướng nghiên cứu

Tìm clean_dcache_by_address và clean_invalidate_dcache_by_address, mà theo báo cáo sử dụng cùng một phép tính số dòng; lần theo các helper slice và tham chiếu gọi hàm clean. Trước tiên, hãy kiểm tra kích thước dòng cache và cách làm tròn địa chỉ trong các phần triển khai đó. Công việc hoàn tất khi mọi dòng cache bị tác động bởi các buffer không căn chỉnh đều được clean và hành vi ở ranh giới nhất quán với phép tính byte cuối cùng được báo cáo.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

cortex-m 0.7.10 (same code on main), Cortex-M7.

Steps to reproduce (on a PC, the line count only):

  1. cargo new lines && cd lines
  2. Put the program below in src/main.rs.
  3. cargo run -q

What the program does:

  1. Copies the line count of clean_dcache_by_address.
  2. Takes a 1460-byte buffer at three addresses.
  3. The first address is on a line.
  4. The second is 20 bytes into a line.
  5. The third is 28 bytes into a line.
  6. Computes the range the function cleans for each.
  7. Prints how many bytes at the end of the buffer are not cleaned.

Expected: 0 bytes not cleaned for every address.

The docs say the function cleans "until all of slice has been cleaned".
The docs only recommend alignment.

Got:

addr 0x24000000 size 1460: cleans 0x24000000..0x240005c0, buffer ends 0x240005b4, last 0 bytes not cleaned
addr 0x24000014 size 1460: cleans 0x24000000..0x240005c0, buffer ends 0x240005c8, last 8 bytes not cleaned
addr 0x2400001c size 1460: cleans 0x24000000..0x240005c0, buffer ends 0x240005d0, last 16 bytes not cleaned

On hardware (STM32H753, Cortex-M7, cortex-m 0.7.9, which has the same code):

  1. A 1460-byte buffer on the stack.
  2. The CPU writes the buffer.
  3. clean_dcache_by_slice on the buffer.
  4. The USART's TX DMA sends the buffer.

Expected: the receiver gets the bytes the CPU wrote.

Got: the last 8 bytes were stale in 609 of 685 writes.

With the buffer aligned to 32 bytes: 0 wrong.

Cause:

  1. clean_dcache_by_address rounds addr down to its cache line: let mut addr = addr & mask;
  2. It counts the lines from size alone: let num_lines = ((size - 1) / line_size) + 1;
  3. A buffer that starts inside a line reaches one line further than size alone says.
  4. That last line is never cleaned.
  5. The DMA reads old bytes from RAM there.

Also affected:

  • clean_invalidate_dcache_by_address has the same computation.
  • clean_dcache_by_slice calls clean_dcache_by_address.
  • clean_dcache_by_ref calls clean_dcache_by_address.

Not affected in the same way:

  • invalidate_dcache_by_address has the same computation.
  • But it is unsafe.
  • And it requires an aligned addr and size.

Fix:

  1. Round addr down to its line: the first line.
  2. Round the last byte, addr + size - 1, down to its line: the last line.
  3. Count the lines from the first line to the last line.
let first = addr & mask;
let last = addr.saturating_add(size - 1) & mask;
let num_lines = (last - first) / line_size + 1;

Why the last byte and not the end:

  • The end, addr + size, rounded up can overflow at the top of the address space.
  • The last byte's address always fits.
  • saturating_add stops a range that runs past the top at the top.

Workaround until a release has the fix:

  • Align the buffer to 32 bytes.
  • Or round addr down and addr + size up yourself before the call.

Program:

// The line count of cortex-m 0.7.10's SCB::clean_dcache_by_address, next to the lines a buffer touches.
const LINE: usize = 32;

fn cleaned(addr: usize, size: usize) -> (usize, usize) {
    let num_lines = ((size - 1) / LINE) + 1; // as in clean_dcache_by_address
    let start = addr & !(LINE - 1);
    (start, start + num_lines * LINE) // [start, end) the function cleans
}

fn main() {
    let size = 1460;
    for addr in [0x2400_0000, 0x2400_0014, 0x2400_001c] {
        let (start, end) = cleaned(addr, size);
        let missed = (addr + size).saturating_sub(end);
        println!("addr {addr:#x} size {size}: cleans {start:#x}..{end:#x}, buffer ends {:#x}, last {missed} bytes not cleaned", addr + size);
    }
}
Ngôn ngữ chính
Rust
Star
1k
Fork
203
Merge trung bình
1 ngày 4 giờ
Pull request đã merge (30 ngày)
2

Chuẩn bị môi trường

Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của rust-embedded/cortex-m

Tất cả issue của rust-embedded/cortex-m

Issue tương tự

Thêm issue về Rust

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.