ARM11 Physical memory regions
|0x0||0x10000||Bootrom (super secret code/data @ 0x8000)|
|0x17E00000||0x2000||MPCore private memory region|
|0x1F000000||0x400000||New_3DS additional memory|
|0x28000000||0x8000000||New_3DS FCRAM extension|
 ARM9 Physical memory regions
|0x00000000||0x08000000||Instruction TCM, repeating each 0x8000 bytes.|
|0x01FF8000||0x8000||Instruction TCM (Accessed by the kernel and process by this address)|
|0x07FF8000||0x8000||Instruction TCM (Accessed by bootrom by this address)|
|0x08000000||0x00100000||ARM9-only internal memory|
|0xFFF00000||0x4000||Data TCM (Mapped during bootrom)|
|0xFFFF0000||0x10000||Bootrom, the main region is at +0x8000, which is disabled during system boot.|
|0x01FF8000||0x10000||Instruction TCM (Accessed by the kernel and process by this address)|
|0x07FF8000||0x10000||Instruction TCM (Accessed by bootrom by this address) (?)|
|0x08000000||0x00200000||ARM9-only internal memory|
|0xFFF00000||0x4000||Data TCM (Mapped during bootrom) (?)|
|0xFFFF0000||0x10000||Bootrom, the main region is at +0x8000, which is disabled during system boot.|
This area is used by QTM Services(starting at offset 0x200000 size 0x180000). This area is not accessible to the GPU on Old3DS. Old3DS/New3DS GSP module has vaddr->physaddr conversion code for this entire region. On New3DS, only the first 0x200000-bytes(half of this memory) are accessible to the GPU.
 Memory map by firmware
- Virtual address mapping FW0B
- Virtual address mapping FW1F
- Virtual address mapping FW25
- Virtual address mapping FW2E
- Virtual address mapping FW37
 ARM11 Detailed physical memory map
18000000 - 18600000: VRAM 1FF80000 - 1FFAB000: Kernel code 1FFAB000 - 1FFF0000: SlabHeap [temporarily contains boot processes] 1FFF0000 - 1FFF1000: ? 1FFF1000 - 1FFF2000: ? 1FFF2000 - 1FFF3000: ? 1FFF3000 - 1FFF4000: ? 1FFF4000 - 1FFF5000: Exception vectors 1FFF5000 - 1FFF5800: Unused? 1FFF5800 - 1FFF5C00: 256-entry L2 MMU table for VA FF4xx000 1FFF5C00 - 1FFF6000: 256-entry L2 MMU table for VA FF5xx000 1FFF6000 - 1FFF6400: 256-entry L2 MMU table for VA FF6xx000 1FFF6400 - 1FFF6800: 256-entry L2 MMU table for VA FF7xx000 1FFF6800 - 1FFF6C00: 256-entry L2 MMU table for VA FF8xx000 1FFF6C00 - 1FFF7000: 256-entry L2 MMU table for VA FF9xx000 1FFF7000 - 1FFF7400: 256-entry L2 MMU table for VA FFAxx000 1FFF7400 - 1FFF7800: 256-entry L2 MMU table for VA FFBxx000 1FFF7800 - 1FFF7C00: MMU table but unused? 1FFF7C00 - 1FFF8000: 256-entry L2 MMU table for VA FFFxx000 1FFF8000 - 1FFFC000: 4096-entry L1 MMU table for VA xxx00000 (CPU 0) 1FFFC000 - 20000000: 4096-entry L1 MMU table for VA xxx00000 (CPU 1) 20000000 - 28000000: Main memory
The entire FCRAM is cleared during NATIVE_FIRM boot. This is probably done by the ARM11 kernel(after loading FIRM launch parameters from FCRAM)?
 FCRAM memory-regions layout
|Configmem-APPMEMTYPE Value||Base address relative to FCRAM+0, for APPLICATION mem-region||Region size, for APPLICATION mem-region||Base address relative to FCRAM+0, for SYSTEM mem-region||Region size, for SYSTEM mem-region||Base address relative to FCRAM+0, for BASE mem-region||Region size, for BASE mem-region|
|0 (default with regular 3DS kernel, used when the type is not 2-5)||0x0||0x04000000(64MB)||0x04000000||0x02C00000||0x06C00000||0x01400000|
|6 (This is the default on New3DS. With New_3DS kernel this is the type used when the value is not 7)||0x0||0x07C00000(124MB)||0x07C00000||0x06400000||0x0E000000||0x02000000|
The SYSTEM mem-region size is calculated with: size = FCRAMTOTALSIZE - (APPLICATION_MEMREGIONSIZE + BASE_MEMREGIONSIZE).
Support for type6/7 was implemented in NS with 8.0.0-18, these are only supported in the New_3DS ARM11-kernel not the regular 3DS kernel. These two types are the only ones supported by the New3DS kernel.
Free/used memory on 4.5.0-10 with Home Menu / Internet Browser, with the default APPMEMTYPE on retail:
|Region||Base address relative to FCRAM+0||Region size||Used memory once Home Menu finishes loading for system boot, on 4.5.0-10||Used memory with Internet Browser running instead of Home Menu, on 4.5.0-10||Free memory once Home Menu finishes loading for system boot, on 4.5.0-10||Free memory with Internet Browser running instead of Home Menu, on 4.5.0-10|
 ARM11 Detailed virtual memory map
(valid only for FW0B, see Memory map by firmware for subsequent versions)
E8000000 - E8600000: mapped VRAM (18000000 - 18600000) EFF00000 - F0000000: mapped Internal memory (1FF00000 - 20000000) F0000000 - F8000000: mapped Main memory FF401000 - FF402000: mapped ? (27FC7000 - 27FC8000) FF403000 - FF404000: mapped ? (27FC2000 - 27FC3000) FF405000 - FF406000: mapped ? (27FBB000 - 27FBC000) FF407000 - FF408000: mapped ? (27FB3000 - 27FB4000) FF409000 - FF40A000: mapped ? (27F8E000 - 27F8F000) FFF00000 - FFF45000: mapped SlabHeap FFF60000 - FFF8B000: mapped Kernel code FFFCC000 - FFFCD000: mapped IO I2C second bus (10144000 - 10145000) FFFCE000 - FFFCF000: mapped IO PDC(LCD) (10400000 - 10401000) FFFD0000 - FFFD1000: mapped IO PDN (10141000 - 10142000) FFFD2000 - FFFD3000: mapped IO PXI (10163000 - 10164000) FFFD4000 - FFFD5000: mapped IO PAD (10146000 - 10147000) FFFD6000 - FFFD7000: mapped IO LCD (10202000 - 10203000) FFFD8000 - FFFD9000: mapped IO DSP (10140000 - 10141000) FFFDA000 - FFFDB000: mapped IO XDMA (10200000 - 10201000) FFFDC000 - FFFE0000: mapped ? (1FFF8000 - 1FFFC000) FFFE1000 - FFFE2000: mapped ? (1FFF0000 - 1FFF1000) FFFE3000 - FFFE4000: mapped ? (1FFF2000 - 1FFF3000) FFFE5000 - FFFE9000: mapped L1 MMU table for VA xxx00000 FFFEA000 - FFFEB000: mapped ? (1FFF1000 - 1FFF2000) FFFEC000 - FFFED000: mapped ? (1FFF3000 - 1FFF4000) FFFEE000 - FFFF0000: mapped IO IRQ (17E00000 - 17E02000) FFFF0000 - FFFF1000: mapped Exception vectors FFFF2000 - FFFF6000: mapped L1 MMU table for VA xxx00000 FFFF7000 - FFFF8000: mapped ? (1FFF1000 - 1FFF2000) FFFF9000 - FFFFA000: mapped ? (1FFF3000 - 1FFF4000) FFFFB000 - FFFFE000: mapped L2 MMU tables (1FFF5000 - 1FFF8000)
Each thread is allocated a 0x1000-byte page in this region: the first page at 0xFF401000 is for the first created thread, 0xFF403000 for the second thread. This region is used to store the SVC-mode stack for the thread, and thread context data used for context switching. When the IRQ handler, prefetch/data abort handlers, and undefined instruction handler are entered where the SPSR-mode=user, these handlers then store LR+SPSR for the current mode on the SVC-mode stack, then these handlers switch to SVC-mode.
This page does not contain a dedicated block for storing R0-PC(etc). For user-mode, the user-mode regs are instead saved on the SVC-mode stack when IRQs such as timers for context switching are triggered.
Structure of this page, relative to page_endaddr-0xC8:
|0x18||0x28||SVC-mode saved registers, stored/loaded during context switches: R4-R9, SL, FP, SP, LR. After loading these registers, the context switch code will jump to the loaded LR.|
|0xC0||4||fpexc from vmrs, used during context switches with the above saved registers.|
For NATIVE_FIRM the memory pages for this region are located in FCRAM, however for TWL_FIRM these are located in AXI WRAM. For TWL_FIRM v6704 the first thread's page for this region is located at physical address 0x1FF93000, the next one at 0x1FF92000, etc.
 ARM11 User-land memory regions
 NATIVE_FIRM/SAFE_MODE_FIRM Userland Memory
|Virtual Address Base||Physical Address Base||Region Max Size||Description|
|0x00100000 / 0x14000000||0x03F00000||The ExeFS:/.code is loaded here, executables must be loaded to the 0x00100000 region when the exheader "special memory" flag is clear. The 0x03F00000-byte size restriction only applies when this flag is clear. Executables are usually loaded to 0x14000000 when the exheader "special memory" flag is set, however this address can be arbitrary.|
|0x04000000||?||?||Used for mapping buffers during IPC, see IPC Command Structure.|
|0x08000000||Main stack physaddr - <heap size for the allocated vaddr 0x08000000 memory>||0x08000000||Heap mapped by ControlMemory|
|0x10000000-StackSize||.bss physical address - total stack pages||StackSize from process exheader||Stack for the main-thread, initialized by the ARM11 kernel. The StackSize from the exheader is usually 0x4000, therefore the stack-bottom is usually 0x0FFFC000. The stack for the other threads is normally located in the process .data section however this can be arbitrary.|
|0x14000000||FCRAM+0||0x08000000||Can be mapped by ControlMemory, this is used for processes' LINEAR/GSP heap.|
|0x1E800000||0x1F000000||0x00400000||New_3DS additional memory, access to this is specified by the exheader. Added with 8.0.0-18, see above section regarding this memory.|
|0x1EC00000||0x10100000||0x01000000||IO registers, the mapped IO pages which each process can access is specified in the CXI exheader.(Applications normally don't have access to registers in this range)|
|0x1F000000||0x18000000||0x00600000||VRAM, access to this is specified by the exheader.|
|0x1FF00000||0x1FF00000||0x00080000||DSP memory, access to this is specified by the exheader.|
|0x1FF80000||FCRAM memory page allocated by the ARM11 kernel.||0x1000||Configuration Memory, all processes have read-only access to this.|
|0x1FF81000||FCRAM memory page allocated by the ARM11 kernel.||0x1000||Shared page, all processes have read-access to this. Write access to this is specified by the exheader "Shared page writing" kernel flag.|
|0x1FFA0000?||?||?||Thread Local Storage|
|0x30000000||FCRAM+0||0x08000000(Old3DS) / 0x10000000(New_3DS)||This LINEAR memory mapping was added with 8.0.0-18, see here. This replaces the original 0x14000000 mapping, for system(memory-region=BASE)/newer titles. The Old3DS kernel uses size 0x08000000 for LINEAR-memory address range checks, while the New3DS kernel uses size 0x10000000 for those range checks. Old3DS/New3DS system-module code doing vaddr->phys-addr conversion uses size 0x10000000.|
|0x20000000 / 0x40000000||This is the end-address of userland memory, memory under this address is process-unique. Memory starting at this address is only accessible in privileged-mode. This address was changed from 0x20000000 to 0x40000000 with 8.0.0-18.|
All executable pages are read-only, and data pages have the execute-never permission set. Normally .text from the loaded ExeFS:/.code is the only mapped executable memory. Executable CROs can be loaded into memory, once loaded the CRO .text section memory page permissions are changed via ControlProcessMemory from RW- to R-X. The address and size of each ExeFS:/.code section is stored in the exheader, the permissions for each section is: .text R-X, .rodata R--, .data RW-, and .bss RW-. The loaded .code is mapped to the addresses specified in the exheader by the ARM11 kernel. The stack permissions is initialized by the ARM11 kernel: RW-. The heap permissions is normally RW-.
All userland memory is mapped with RW permissions for privileged-mode. However, normally the ARM11 kernel only uses userland read/write instructions(or checks that the memory can be written from userland first) for accessing memory specified by SVCs.
Processes can't directly access memory for other processes. When service commands are used, the kernel maps memory in the destination process for input/output buffers, where the addresses in the command received by the process is replaced by this mapped memory. When this is an input buffer, the buffer data is copied to the mapped memory. When this is an output buffer, the data stored in the mapped memory is copied to the destination buffer specified in the command.
The physical address which memory for the application memory-type is mapped to begins at FCRAM+0, the total memory allocated for this memory-type is stored in Configuration_Memory. Applications' .text + .rodata + .data under the application memory-type is mapped at FCRAM + APPMEMALLOC - (aligned page-size for .text + .rodata + .data). The application .bss is mapped at CODEADDR - .bss size aligned down to the page size.
 TWL_FIRM Userland Memory
|Virtual Address Base||Physical Address Base||Size||Description|
|0x00100000||0x1FFAB000 (with newer TWL_FIRM such as v6704 this is located at 0x1FFAC000)||0x00055000||Code + .(ro)data copied from the process 0x00300000 region is located here(.bss is located here as well).|
|0x00300000||0x24000000||0x04000000||The beginning of the ARM11 process .text is located here.|
|0x1FF00000||0x1FF00000||0x00080000||This is mapped to the DSP memory.|
The above regions are mapped by the ARM11 kernel. Later when the ARM11 process uses svcKernelSetState with type4, the kernel unmaps(?) the following regions: 0x00300000..0x04300000, 0x08000000..0x0FE00000, and 0x10000000..0xF8000000.
 Detailed TWL_FIRM ARM11 Memory
|Process Virtual Address||Physical Address||Size||Description|
|0x08000000||0x20000000||0x01000000*4||DS(i) 0x02000000 RAM. Vaddr = (DSRAMOffset*4) + 0x08000000, where DSRAMOffset is DSRAMAddr-0x02000000.|
|0x0FC00000||0x27C00000||Loaded SRL binary, initially the dev DSi launcher SRL is located here(copied here by the ARM11 process).|
|0x0FD00000||0x27D00000||The data located here is copied to here by the ARM11 process. The data located here is a TWL NAND bootloader image, using the same format+encryption/verification methods as the DSi NAND bootloader(stage2). The keyX for this bootloader keyslot is initially set to the retail DSi key-data, however when TWL_FIRM is launched this keyX key-data is replaced with a separate keyX. TWL_FIRM can use either the retail DSi bootloader RSA-1024 modulo, or a seperate modulo: normally only the latter is used(the former is only used when loading the image from FS instead of FCRAM). When using the image from FCRAM(default code-path), TWL_FIRM will not calculate+check the hashes for the bootloader code binaries(this is done when loading from FS however).|
 System memory details
0xFFFF9000 Pointer to the current KThread instance 0xFFFF9004 Pointer to the current KProcess instance 0xFFFF9010 Pointer a KThread (not sure what its role is)
The handle 0xFFFF8001 is a reference to the current KProcess.
 IO Process/Kernel virtual addressing equivalence
It seems an IO register's process virtual address can be calculated by adding 0xEB00000 to its physical address.
 VRAM Map While Running System Applets
- 0x1E6000-0x22C500 -- top screen 3D left framebuffer 0(240x400x3) (The "3D right first-framebuf" addr stored in the LCD register is set to this, when the 3D is set to "off")
- 0x22C800-0x272D00 -- top screen 3D left framebuffer 1(240x400x3)
- 0x273000-0x2B9500 -- top screen 3D right framebuffer 0(240x400x3)
- 0x2B9800-0x2FFD00 -- top screen 3D right framebuffer 1(240x400x3)
- 0x48F000-0x4C7400 -- bottom screen framebuffer 0(240x320x3)
- 0x4C7800-0x4FF800 -- bottom screen framebuffer 1(240x320x3)
These LCD framebuffer addresses are not changed by the system when launching regular applications, the application itself handles that if needed. These VRAM framebuffers are cleared when launching regular applications.