| .. _x86_developer_guide: |
| |
| x86 Developer Guide |
| ################### |
| |
| Overview |
| ******** |
| |
| This page contains information on certain aspects when developing for |
| x86-based platforms. |
| |
| Virtual Memory |
| ************** |
| |
| During very early boot, page tables are loaded so technically the kernel |
| is executing in virtual address space. By default, physical and virtual |
| memory are identity mapped and thus giving the appearance of execution |
| taking place in physical address space. The physical address space is |
| marked by kconfig :kconfig:`CONFIG_SRAM_BASE_ADDRESS` and |
| :kconfig:`CONFIG_SRAM_SIZE` while the virtual address space is marked by |
| :kconfig:`CONFIG_KERNEL_VM_BASE` and :kconfig:`CONFIG_KERNEL_VM_SIZE`. |
| Note that :kconfig:`CONFIG_SRAM_OFFSET` controls where the Zephyr kernel |
| is being placed in the memory, and its counterpart |
| :kconfig:`CONFIG_KERNEL_VM_OFFSET`. |
| |
| Separate Virtual Address Space from Physical Address Space |
| ========================================================== |
| |
| On 32-bit x86, it is possible to have separate phyiscal and virtual |
| address space. Code and data are linked in virtual address space, |
| but are still loaded in physical memory. However, during boot, code |
| and data must be available and also addressable in physical address |
| space before ``vm_enter`` inside :file:`arch/x86/core/ia32/crt0.S`. |
| After ``vm_enter``, code execution is done via virtual addresses |
| and data can be referred via their virtual addresses. This is |
| possible as the page table generation script |
| (:file:`arch/x86/gen_mmu.py`) identity maps the physical addresses |
| at the page directory level, in addition to mapping virtual addresses |
| to the physical memory. Later in the boot process, |
| the entries for identity mapping at the page directory level are |
| cleared in :c:func:`z_x86_mmu_init()`, effectively removing |
| the identity mapping of physical memory. This unmapping must be done |
| for userspace isolation or else they would be able to access |
| restricted memory via physical addresses. Since the identity mapping |
| is done at the page directory level, there is no need to allocate |
| additional space for the page table. However, additional space may |
| still be required for additional page directory table. |
| |
| There are restrictions on where virtual address space can be: |
| |
| - Physical and virtual address spaces must be disjoint. This is |
| required as the entries in page directory table will be cleared. |
| If they are not disjoint, it would clear the entries needed for |
| virtual addresses. |
| |
| - If :kconfig:`CONFIG_X86_PAE` is enabled (``=y``), each address space |
| must reside in their own 1GB region, due to each entry of PDP |
| (Page Directory Pointer) covers 1GB of memory. For example: |
| |
| - Assuming ``CONFIG_SRAM_OFFSET`` and ``CONFIG_KERNEL_VM_OFFSET`` |
| are both ``0x0``. |
| |
| - ``CONFIG_SRAM_BASE_ADDRESS == 0x00000000`` and |
| ``CONFIG_KERNEL_VM_BASE = 0x40000000`` is valid, while |
| |
| - ``CONFIG_SRAM_BASE_ADDRESS == 0x00000000`` and |
| ``CONFIG_KERNEL_VM_BASE = 0x20000000`` is not. |
| |
| - If :kconfig:`CONFIG_X86_PAE` is disabled (``=n``), each address space |
| must reside in their own 4MB region, due to each entry of PD |
| (Page Directory) covers 4MB of memory. |
| |
| - Both ``CONFIG_SRAM_BASE_ADDRESS`` and ``CONFIG_KERNEL_VM_BASE`` |
| must also align with the starting addresses of targeted regions. |
| |
| Specifying Additional Memory Mappings at Build Time |
| *************************************************** |
| |
| The page table generation script (:file:`arch/x86/gen_mmu.py`) generates |
| the necessary multi-level page tables for code execution and data access |
| using the kernel image produced by the first linker pass. Additional |
| command line arguments can be passed to the script to generate additional |
| memory mappings. This is useful for static mappings and/or device MMIO |
| access during very early boot. To pass extra command line arguments to |
| the script, populate a CMake list named ``X86_EXTRA_GEN_MMU_ARGUMENTS`` |
| in the board configuration file. Here is an example: |
| |
| .. code-block:: cmake |
| |
| set(X86_EXTRA_GEN_MMU_ARGUMENTS |
| --map 0xA0000000,0x2000 |
| --map 0x80000000,0x400000,LWUX,0xB0000000) |
| |
| The argument ``--map`` takes the following value: |
| ``<physical address>,<size>[,<flags:LUWX>[,<virtual adderss>]]``, where: |
| |
| - ``<physical address>`` is the physical address of the mapping. (Required) |
| |
| - ``<size>`` is the size of the region to be mapped. (Required) |
| |
| - ``<flags>`` is the flag associated with the mapping: (Optional) |
| |
| - ``L``: Large page at the page directory level. |
| |
| - ``U``: Allow userspace access. |
| |
| - ``W``: Read/write. |
| |
| - ``X``: Allow execution. |
| |
| - ``D``: Cache disabled. |
| |
| - Default is small page (4KB), supervisor only, read only, and |
| execution disabled. |
| |
| - ``<virtual address`` is the virtual address of the mapping. (Optional) |
| |
| Note that specifying additional memory mappings requires larger storage |
| space for the pre-allocated page tables (both kernel and per-domain |
| tables). :kconfig:`CONFIG_X86_EXTRA_PAGE_TABLE_PAGES` is needed to |
| specify how many more memory pages to be reserved for the page tables. |
| If the needed space is not exactly the same as required space, |
| the ``gen_mmu.py`` script will print out a message indicating what |
| needs to be the value for the kconfig. |