Description
Frame::new_contiguous(frame_count, align_to) claims to allocate contiguous physical pages aligned to align_to, but the returned physical address is not actually guaranteed to be aligned to align_to.
Typical trigger: the RISC-V G-stage root page table requires 16 KiB contiguous memory that is also 16 KiB aligned. The code allocates it via
Frame::new_contiguous(4, 16 * 1024) (see src/arch/riscv64/paging.rs), but the resulting physical address may be only 4 KiB aligned, violating the hardware requirement.
Root cause
Allocation is backed by bitmap_allocator, whose alloc_contiguous(size, align_log2) aligns the returned bitmap index idx.
The final physical address is composed as (src/memory/frame.rs):
idx * PAGE_SIZE + self.base
idx * PAGE_SIZE is indeed aligned to 2^align_log2 * PAGE_SIZE;
- but
self.base is then added on top.
And self.base is only aligned to PAGE_SIZE (4 KiB) in init, via align_up:
fn init(&mut self, base: PhysAddr, size: usize) {
self.base = align_up(base); // 4 KiB alignment only
...
}
The origin of base is mem_pool_start = __core_end + MAX_CPU_NUM * PER_CPU_SIZE:
__core_end is only . = ALIGN(4K) in the linker scripts (4 KiB aligned);
PER_CPU_SIZE = 512 KiB is a multiple of 16 KiB, so it does not change the phase;
PHYS_VIRT_OFFSET used by virt_to_phys is not guaranteed to be 16 KiB aligned.
Therefore the 16 KiB phase of base is undefined. When base is not 16 KiB aligned, adding it to idx * PAGE_SIZE (which is 16 KiB aligned) yields a final physical address that is no longer 16 KiB aligned — only 4 KiB alignment holds.
Conclusion: the alignment applies to the index relative to base, while the phase of base breaks the alignment guarantee on the final physical address.
Description
Frame::new_contiguous(frame_count, align_to)claims to allocate contiguous physical pages aligned toalign_to, but the returned physical address is not actually guaranteed to be aligned toalign_to.Typical trigger: the RISC-V G-stage root page table requires 16 KiB contiguous memory that is also 16 KiB aligned. The code allocates it via
Frame::new_contiguous(4, 16 * 1024)(seesrc/arch/riscv64/paging.rs), but the resulting physical address may be only 4 KiB aligned, violating the hardware requirement.Root cause
Allocation is backed by
bitmap_allocator, whosealloc_contiguous(size, align_log2)aligns the returned bitmap indexidx.The final physical address is composed as (
src/memory/frame.rs):idx * PAGE_SIZEis indeed aligned to2^align_log2 * PAGE_SIZE;self.baseis then added on top.And
self.baseis only aligned toPAGE_SIZE(4 KiB) ininit, viaalign_up:The origin of
baseismem_pool_start = __core_end + MAX_CPU_NUM * PER_CPU_SIZE:__core_endis only. = ALIGN(4K)in the linker scripts (4 KiB aligned);PER_CPU_SIZE = 512 KiBis a multiple of 16 KiB, so it does not change the phase;PHYS_VIRT_OFFSETused byvirt_to_physis not guaranteed to be 16 KiB aligned.Therefore the 16 KiB phase of
baseis undefined. Whenbaseis not 16 KiB aligned, adding it toidx * PAGE_SIZE(which is 16 KiB aligned) yields a final physical address that is no longer 16 KiB aligned — only 4 KiB alignment holds.Conclusion: the alignment applies to the index relative to
base, while the phase ofbasebreaks the alignment guarantee on the final physical address.