// user_layout: user-space virtual address layout. // // Single source of truth for the regions the ELF loader populates and // the page-fault path classifies. Imported by src/fork.zig // (prepare_move_to_user_elf) and src/mm_user.zig (do_data_abort, // map_page) so both sides agree on text/data/heap/stack. // // Region map (matches DOCUMENTATION.md ยง3 "User virtual layout"): // // 0x0000_0000_0000_0000 text (RWX โ€” writable; no read-only bit yet) // 0x0000_0000_0010_0000 data (RW-) // 0x0000_0000_0020_0000 heap (RW-, grows up via brk) // 0x0000_0FFF_FFFF_F000 stack (RW-, grows down, guard below) // // Region flags are plumbed through map_page: prepare_move_to_user_elf // stamps per-region attributes on each PT_LOAD segment, while // do_data_abort applies the combined-permission default bag to pages // it demand-allocates for the heap and stack. pub const PAGE_SIZE u64 = 1 << 12 pub const TEXT_BASE u64 = 0x0000_0000_0000_0000 pub const DATA_BASE u64 = 0x0000_0000_0010_0000 pub const HEAP_BASE u64 = 0x0000_0000_0020_0000 pub const STACK_TOP u64 = 0x0000_0FFF_FFFF_F000 // Stack budget: largest legal stack VA range is [STACK_LOW, STACK_TOP). // prepare_move_to_user_elf eagerly maps the top page; do_data_abort // demand-allocates the rest within the budget. Below STACK_LOW sits a // 1-page guard region [STACK_GUARD_LOW, STACK_GUARD_HIGH) that // do_data_abort treats as a stack-overflow signal โ€” it prints a // diagnostic and zombies the offending task (the parent's sys_wait // reaps as usual). 64 KiB matches sys_brk's upper-bound check, so the // heap can't grow into either the stack or its guard. pub const STACK_BUDGET u64 = 16 * PAGE_SIZE pub const STACK_LOW u64 = STACK_TOP - STACK_BUDGET pub const STACK_GUARD_PAGES u64 = 1 pub const STACK_GUARD_HIGH u64 = STACK_LOW pub const STACK_GUARD_LOW u64 = STACK_LOW - STACK_GUARD_PAGES * PAGE_SIZE // Stage-1 page-descriptor bit 54: UXN (Unprivileged eXecute Never), // AArch64 ARM D5-2750. PXN (privileged XN) is bit 53; user data/heap/ // stack pages set UXN to forbid EL0 execution. User text clears it. pub const TD_USER_XN u64 = 1 << 54 // MMU descriptor sub-flags shared with src/mm_user.zig. Kept here so // the assembled per-region bags (TEXT/DATA/...) below can be derived // in one place; mm_user.zig keeps its own copies for the page-table- // walk internals (table flags etc.) that the loader does not stamp. const TD_VALID u64 = 1 << 0 const TD_PAGE u64 = 1 << 1 const TD_USER_PERMS u64 = 1 << 6 const TD_INNER_SHARABLE u64 = 3 << 8 const TD_ACCESS u64 = 1 << 10 // Default user-page permission bag โ€” the historical // TD_USER_PAGE_FLAGS from src/mm_user.zig. The baseline the ELF loader // (prepare_move_to_user_elf) ORs per-region flags onto, the bag // do_data_abort stamps on demand-allocated heap/stack pages, and the // attributes copy_virt_memory inherits across fork. pub const TD_USER_PAGE_FLAGS_DEFAULT u64 = TD_ACCESS | TD_INNER_SHARABLE | TD_USER_PERMS | TD_PAGE | TD_VALID