Before discussing the individual memory initialization operations, we need to examine the situation in RAM after the boot loader has copied the kernel into memory and the assembler part of the initialization routines has completed. I concentrate on the default case in which the kernel is loaded to a fixed position in physical RAM that is determined at compile time.
It is also possible to configure the initial position of the kernel binary in physical RAM if the crash dump mechanism is enabled. Additionally, some embedded systems will require this ability. The configuration option physical_start determines the position in RAM in this case, subjected to physical alignment specified by the configuration option physical_align.
Additionally, the kernel can be built as a relocatable binary, and the physical start address given at compile time is completely ignored in this case. The boot loader can decide where to put the kernel. Since both options are either only required in corner cases or are still considered experimental, I will not discuss them any further.
Figure 3-11 shows the lowest megabytes of physical RAM memory in which the various parts of the kernel image reside.
OxlOOOOtt (1 MiB)
OxlOOOOtt (1 MiB)
First page frame ^ Kernel text Available
Figure 3-11: Arrangement of the Linux kernel in RAM memory.
The figure shows the first megabytes of physical memory — how much is exactly required depends on how big the kernel binary is. The first 4,096 KiB — the first page frame — are omitted because they are often reserved for the BIOS. The next 640 KiB would be usable in principle, but are again not used for kernel loading. The reason is that this area is immediately followed by an area reserved for the system into which various ROM ranges are mapped (typically the system BIOS and the graphic card ROM). It is not possible to write to these areas. However, the kernel should always be loaded into a contiguous memory range, and this would be possible only for kernels smaller than 640 KiB if the start address of RAM memory were used as the start position for the kernel image.
To resolve these problems, IA-32 kernels use 0x100000 as the start address; this corresponds to the start of the first megabyte in RAM memory. There is sufficient contiguous memory at this point to hold the entire kernel.
The memory occupied by the kernel is split into several sections whose bounds are held in variables.
□ _text and _etext are the start and end address of the text section that contains the compiled kernel code.
□ The data section in which most kernel variables are kept is located between _etext and _edata.
□ Initialization data no longer needed after the kernel boot process is finished (among others, e.g., the BSS segment that contains all static global variables initialized to 0) are held in the last section, which extends from _edata to _end. Once kernel initialization has completed, most of the data can be removed from memory leaving more space for applications. The interval is split into smaller subintervals to control what can be removed and what cannot, but this is not of importance for our purposes now.
Although the variables used to define section bounds are defined in the kernel source code (arch/x86/kernel/setup_32.c), no values are assigned to them at this point. This is simply not possible. How can the compiler know at compilation time how large the kernel will be? The exact value is only established when the object files are linked, and it is then patched into the binary file. This action is controlled by arch/arch/vmlinux.ld.S (for IA-32, the file is arch/x8 6/vmlinux_32.ld.S), where the kernel memory layout is also defined.
The exact value varies according to kernel configuration as each configuration has text and data sections of different sizes — depending on which parts of the kernel are enabled and which are not used. Only the start address (_text) is always the same.
Each time the kernel is compiled, a file named System.map is generated and stored in the source base directory. Besides the addresses of all other (global) variables, procedures, and functions defined in the kernel, this file also includes the values of the constants shown in Figure 3-11, [email protected]> cat System.map c0100000 A _text c0381ecd A _etext c04704e0 A _edata c04c3f44 A _end
All values have the offset 0xC0000000, which is the start address of the kernel segment if the standard 3 :1 split between user and kernel address space is chosen. The addresses are virtual addresses because RAM memory is mapped into the virtual address space as a linear mapping starting at this address. The corresponding physical addresses are obtained by subtraction from 0xC0000000.
/proc/iomem also provides information on the sections into which RAM memory is divided.
[email protected]> cat /proc/iomem System RAM reserved Video RAM area Video ROM System ROM System RAM : Kernel code : Kernel data
00000000-0009e7ff 0009e800-0009ffff 000a0000-000bffff 000c0000-000c7fff 000f0000-000fffff 00100000-17ceffff 00100000-00381ecc 00381ecd-004704df
The kernel image begins above the first megabyte (0x00100000). The size of the code is approximately 2.5 MiB, and the data section accounts for about 0.9 MiB.
The same information is also available for AMD64 systems. Here the kernel starts 2 MiB after the first page frame, and physical memory is mapped into the virtual address space from 0xffffffff80000000 onward. The relevant entries in System.map are as follows:
[email protected]> cat System.map ffffffff80200000 A _text ffffffff8041fc6f A _etext ffffffff8056c060 A _edata ffffffff8077548c A _end This information is also contained in /proc/iomem for the running kernel:
[email protected] # cat /proc/iomem
00100000-cff7ffff : System RAM 00200000-0041fc6e : Kernel code 0041fc6f-0056c05f : Kernel data 006b6000-0077548b : Kernel bss
Was this article helpful?