Linux kernel boot. Part 1

 3r31250. 3r3-31. From bootloader to kernel 3r31180.  3r31250. 3r31180.  3r31250. If you read the previous Article 3r31236. , you know about my new hobby for low-level programming. I have written several articles about programming in assembler for 3r31172. x86_64 Linux and at the same time began to dive into the source code of the Linux kernel. 3r31180.  3r31250. 3r31180.  3r31250. I am very interested in understanding how low-level things work: how programs run on my computer, how they are located in memory, how the kernel manages processes and memory, how the network stack works at a low level and much more. So, I decided to write another series of articles on the Linux kernel for the architecture. x86_64 . 3r31180.  3r31250. 3r31180.  3r31250. Please note that I am not a professional kernel developer and do not write kernel code at work. This is just a hobby. I just like low-level things and it's interesting to dig into them. Therefore, if you notice any confusion or have any questions /comments, please contact me 3r31162. on twitter , on 3r31164. mail
or just create ticket 3r31236. . I will be grateful. 3r31180.  3r31250. 3r330. 3r31236. 3r31180.  3r31250. All articles are published in 3r3334. GitHub repositories. , and if something is wrong with my English or article content, feel free to send a pull-request. 3r31180.  3r31250. 3r31180.  3r31250. Please note that this is not official documentation, but simply training and knowledge sharing. 3r33333. 3r31180.  3r31250. 3r31180.  3r31250. Required knowledge 3r31180.  3r31250. 3r31180.  3r31250. 3r31182.  3r31250. 3r31234. Understanding C
Code.  3r31250. 3r31234. Understanding the assembler code (syntax AT & T) 3r3-31237.  3r31250. 3r31239. 3r31180.  3r31250. Anyway, if you are just starting to learn such tools, I will try to explain something in this and the following articles. Okay, with the introduction finished, it's time to dive into the Linux kernel and low-level stuff. 3r31180.  3r31250. 3r31180.  3r31250. I started writing this book in the days of the Linux ??? kernel, and a lot could have changed since then. If there are changes, I will update the articles accordingly. 3r31180.  3r31250. 3r31180.  3r31250. 3r31178. Magic power button, what's next? 3r31179. 3r31180.  3r31250. Although these are articles on the Linux kernel, we have not yet reached it - at least in this paragraph. As soon as you press the magic power button on your laptop or desktop computer, it starts working. The motherboard sends a signal to r3r31230. power supply
. After receiving the signal, it provides the computer with the necessary amount of electricity. As soon as the motherboard gets 3r31235. “Power OK” signal 3r31236. then trying to run the CPU. It drops all the remaining data in its registers and sets the predefined values ​​for each of them. 3r31180.  3r31250. 3r31180.  3r31250. In processors 3r31200. 80386 3r31236. and later versions after a reboot there should be such values ​​in the CPU registers: 3r31180.  3r31250. 3r31180.  3r31250. 3r31144. IP 0xfff0
CS selector 0xf000
CS base 0xffff0000
3r31180.  3r31250. The processor starts working in 3r31210. real mode
. Let's go back a bit and try to understand memory segmentation in this mode. Real mode is supported on all x86-compatible processors: from 8086 3r31236. Up-to-date 64-bit Intel processors. The processor 8086 uses a 20-bit address bus, that is, it can work with an address space of 3r31172. 0-0xFFFFF or 1 megabyte . But it has only 16-bit registers with a maximum address of 3r31172. 2 ^ 16-1 or 0xffff (64 kilobytes). 3r31180.  3r31250. 3r31180.  3r31250. 3r3113. Segmentation memory needed to use the entire available address space. All memory is divided into small fixed-size segments of 65536 bytes (64k). Since with 16-bit registers we cannot access memory above 64 KB, an alternative method was developed. 3r31180.  3r31250. 3r31180.  3r31250. The address consists of two parts: 1) a segment selector with a base address; 2) offset from the base address. In real mode, the base address of the segment selector is 3r31172. segment selector * 16 . Thus, to get a physical address in memory, multiply the segment selector by 16 and add an offset to it: 3r31180.  3r31250. 3r31180.  3r31250. 3r31144. 3r31172. Physical Address = Segment Selector * 16 + Offset 3r31147. 3r31180.  3r31250. For example, if the register has CS: IP 3r31173. The value of 0x2000: 0x0010 , then the corresponding physical address would be:
 3r31250. 3r31180.  3r31250. 3r31144. 3r31172. hex ((0x2000 4) + 0x0010)
'0x20010'
3r31147. 3r31180.  3r31250. But if you take the selector of the largest segment and the offset 0xffff: 0xffff , it turns out the address:
 3r31250. 3r31180.  3r31250. 3r31144. 3r31172. hex ((0xffff 4) + 0xffff)
'0x10ffef' 3r31147. 3r31180.  3r31250. that is, 65520 bytes after the first megabyte. Since only one megabyte is available in real mode, 0x10ffef becoming 3r31172. 0x00ffef with disabled line A20 . 3r31180.  3r31250. 3r31180.  3r31250. Well, now we know a little about real mode and memory addressing in this mode. Returning to the discussion of register values ​​after a reset. 3r31180.  3r31250. 3r31180.  3r31250. Register CS consists of two parts: a visible segment selector and a hidden base address. Although the base address is usually formed by multiplying the value of the segment selector by 1? but during a hardware reset, the segment selector in the CS register is set to 3r31172. 0xf000 and the base address is 0xffff0000 . The processor uses this special base address until the CS changes. 3r31180.  3r31250. 3r31180.  3r31250. The starting address is formed by adding the base address to the value in the EIP register: 3r31180.  3r31250. 3r31180.  3r31250. 3r31144. 3r31172. 0xffff0000 + 0xfff0
'0xfffffff0' 3r31147. 3r31180.  3r31250. We get 0xfffffff0 , which is 16 bytes below 4 GB. This point is called reset vector 3r31236. . This is the memory location where the CPU waits for the first instruction to be executed after a reset: the transition operation (3r3202. Jmp ), Which usually points to the BIOS entry point. For example, if you look at the source code coreboot ( Src /cpu /x86 /16bit /reset16.inc ), We will see:
 3r31250. 3r31180.  3r31250. 3r31144. 3r31172. .section ".reset", "ax",% progbits
.code16 3r31250. .globl _start
_start: 3r31250. .byte 0xe9
.int _start16bit - (. + 2)
3r31173. 3r31147. 3r31180.  3r31250. Here we see the operation code ( Opcode ) jmp , namely 3r31172. 0xe9 , and the destination address is 3r31172. _start16bit - (. + 2) . 3r31180.  3r31250. 3r31180.  3r31250. We also see that section reset is 16 bytes, and it is compiled to run from 0xfffff0 ( Src /cpu /x86 /16bit /reset16.ld ):
 3r31250. 3r31180.  3r31250. 3r31144. 3r31172. SECTIONS {
/* Trigger an error if I have an unuseable start address * /3r31250. _bogus = ASSERT (_start16bit> = 0xffff000? "_start16bit too low. Please report."); 3r31250. _ROMTOP = 0xfffffff0; 3r31250. . = _ROMTOP; 3r31250. .reset. : {
* (. reset); 3r31250. . = 15; 3r31250. BYTE (0x00); 3r31250.}
} 3r31147. 3r31180.  3r31250. Now the BIOS is running; After initializing and checking the BIOS hardware, you need to find the boot device. The boot order is saved in the BIOS configuration. When trying to boot from the hard disk BIOS tries to find the boot sector. On disks with 3r33232. partition markup MBR the boot sector is stored in the first 446 bytes of the first sector, where each sector is 512 bytes. The last two bytes of the first sector - 3r31172. 0x55 and 3r31172. 0xaa . They show the BIOS that it is a bootable device. 3r31180.  3r31250. 3r31180.  3r31250. For example: 3r31180.  3r31250. 3r31180.  3r31250. 3r31144. 3r31172. ; 3r31250. ; Note: this example is written in the Intel x86
assembler syntax. ; 3r31250.[BITS 16]3r31250. 3r31250. boot:
mov al, '!' 3r31250. mov ah, 0x0e
mov bh, 0x00
mov bl, 0x07
3r31250. int 0x10
jmp $ 3r31250. 3r31250. times 510 - ($ - $$) db 0
3r31250. db 0x55
db 0xaa 3r31147. 3r31180.  3r31250. Putting it together and running:
 3r31250. 3r31180.  3r31250. 3r31172. nasm -f bin boot.nasm && qemu-system-x86_64 boot 3r31180.  3r31250. 3r31180.  3r31250. 3r3309. QEMU 3r31236. receives a command to use the binary file. boot that we just created as a disk image. Since the binary file generated above meets the requirements of the boot sector (start at 3r31172. 0x7c00 And end with a magic sequence), QEMU will treat the binary file as the master boot record (MBR) of the disk image. 3r31180.  3r31250. 3r31180.  3r31250. You will see:
 3r31250. 3r31180.  3r31250. Linux kernel boot. Part 1 in mind. After starting, it causes an interrupt 0x10 which simply prints the symbol ! 3r31173. ; fills the remaining 510 bytes with zeros and ends with two magic bytes 0xaa and 3r31172. 0x55 . 3r31180.  3r31250. 3r31180.  3r31250. The binary dump can be viewed with the utility objdump :
 3r31250. 3r31180.  3r31250. 3r31172. nasm -f bin boot.nasm
 3r31250. objdump -D -b binary -mi386 -Maddr1? data1? intel boot 3r31180.  3r31250. 3r31180.  3r31250. Of course, in the real boot sector - the code to continue the boot process and the partition table instead of a heap of zeros and an exclamation mark :). From now on, the BIOS transfers control to the bootloader. 3r31180.  3r31250. 3r31180.  3r31250. [i] Note
: as explained above, the CPU is in real mode; where the calculation of the physical address in memory is as follows: 3r31180.  3r31250. 3r31180.  3r31250. 3r31144. 3r31172. Physical Address = Segment Selector * 16 + Offset 3r31147. 3r31180.  3r31250. We have only 16-bit general purpose registers, and the maximum value of the 16-bit register is 0xffff therefore, at the highest values, the result will be:
 3r31250. 3r31180.  3r31250. 3r31144. 3r31172. hex ((0xffff * 16) + 0xffff)
'0x10ffef' 3r31147. 3r31180.  3r31250. where 0x10ffef equals 1 MB + 64KB - 16 bytes 3r31173. . In the processor 8086 3r31236. (first processor with real mode) 20-bit address line. Since 2 ^ 20 = 1048576 then the actual available memory is 1 MB. 3r31180.  3r31250. 3r31180.  3r31250. In general, real-mode memory addressing is as follows: 3r31180.  3r31250. 3r31180.  3r31250. 3r31144. 0x00000000 - 0x000003FF - the table of the real-mode interrupt vectors 3r3-31250. 0x00000400 - 0x000004FF - BIOS data area
0x00000500 - 0x00007BFF - not used 3r31250. 0x00007C00 - 0x00007DFF - our loader
0x00007E00 - 0x0009FFFF - not used 3r31250. 0x000A0000 - 0x000BFFFF - Video RAM (VRAM) 3r31250 memory. 0x000B0000 - 0x000B7777 - video memory of monochrome mode
0x000B8000 - 0x000BFFFF - the video memory of color mode
0x000C0000 - 0x000C7FFF - Video ROM BIOS 3r31250. 0x000C8000 - 0x000EFFFF - the shadow area (BIOS Shadow)
0x000F0000 - 0x000FFFFF - the system BIOS 3r31180.  3r31250. At the beginning of the article it is written that the first instruction for the processor is located at 0xFFFFFFF0 , which is much more 0xFFFFF (1 MB) How does the CPU access this address in real mode? The answer is in the documentation 3r33419. coreboot
:
 3r31250. 3r31180.  3r31250. 3r31172. 0xFFFE_0000 - 0xFFFF_FFFF: 128 kilobytes of ROM are translated into the address space 3r31173. 3r31180.  3r31250. 3r31180.  3r31250. At the beginning of the execution of the BIOS is not in RAM, but in ROM. 3r31180.  3r31250. 3r31180.  3r31250. 3r31178. The loader 3r31180.  3r31250. The Linux kernel can be loaded with various boot loaders, such as GRUB 2 3r31236. and 3r3441. syslinux . The kernel has a boot protocol that defines boot requirements for implementing Linux support. In this example, we are working with GRUB 2.
 3r31250. 3r31180.  3r31250. Continuing the boot process, the BIOS chose the boot device and transferred control to the boot sector, execution starts from 3r3447. boot.img . Due to the limited size, this is a very simple code. It contains a pointer to go to the main image of GRUB 2. It starts at diskboot.img and is usually stored immediately after the first sector in unused space before the first partition. The above code loads into memory the rest of the image that contains the GRUB 2 kernel and the drivers for processing file systems. After that, the function is executed. grub_main . 3r31180.  3r31250. 3r31180.  3r31250. Function grub_main initializes the console, returns the base address for the modules, sets the root device, loads /parses the grub configuration file, loads the modules, etc. At the end of execution, it puts grub into normal mode. Function grub_normal_execute (from the source file 3r31172. grub-core /normal /main.c 3r3r7311.) completes the final preparations and shows the menu for selecting the operating system. When we select one of the grub menu items, the function starts. grub_menu_execute_entry that executes the grub command boot and loads the selected OS. 3r31180.  3r31250. 3r31180.  3r31250. As stated in the kernel boot protocol, the bootloader must read and fill in some of the kernel installation header fields, which start at offset 0x01f1 from the kernel installation code. This offset is indicated in script linker . The title of the kernel is 3r33535. arch /x86 /boot /header.S starts with:
 3r31250. 3r31180.  3r31250. 3r31144. 3r31172. .globl hdr
hdr:
setup_sects: .byte 0
root_flags: .word ROOT_RDONLY
syssize: .long 0
ram_size: .word 0
vid_mode: .word SVGA_MODE
root_dev: .word 0
boot_flag: .word 0xAA55
3r31147. 3r31180.  3r31250. The loader must fill this and other headers (which are marked only as type 3r31172. Write In the Linux boot protocol, as in this example) with values ​​that you received from the command line or calculated at boot time. Now we will not dwell on the descriptions and explanations for all the header fields. We will discuss later how the kernel uses them. For a description of all fields, see download protocol 3r31236. . 3r31180.  3r31250. 3r31180.  3r31250. As you can see in the kernel boot protocol, the memory will be displayed as follows:
 3r31250. 3r31180.  3r31250. 3r31144. | Kernel protected mode | 3r31250. 100000 + ------------------------ +
| I /O mapping | 3r31250. 0A0000 + ------------------------ +
| Reserved. for BIOS | Leave as much as possible free
~ ~
| Command Line | (may also be behind the X + 10000 mark) 3r31250. X + 1?000 + ------------------------ +
| Stack /pile | To use the real kernel mode code
X + 08000 + ------------------------ +
| Kernel installation | The real kernel mode code is
| Kernel boot sector | The legacy boot sector of the
kernel. X + ------------------------ +
| Bootloader | 3r33521. 001000 + ------------------------ +
| Reserved. for MBR /BIOS | 3r31250. 000800 + ------------------------ +
| Usually used MBR | 3r31250. 000600 + ------------------------ +
| Use BIOS only | 3r31250. 000000 + ------------------------ +
3r31147. 3r31180.  3r31250. So, when the loader transfers control to the kernel, it starts at: 3r3r1180.  3r31250. 3r31180.  3r31250. 3r31144. 3r31172. X + sizeof (KernelBootSector) + 1 3r31147. 3r31180.  3r31250. where X 3r3r?173. - address of the kernel boot sector. In our case, X 3r3r?173. equal to r3r31172. 0x10000 as seen in the memory dump: 3r3r1180.  3r31250. 3r31180.  3r31250. 3r???. 3r31180.  3r31250. 3r31180.  3r31250. The loader transferred the Linux kernel to memory, filled in the header fields, and then switched to the appropriate memory address. Now we can go directly to the kernel installation code. 3r31180.  3r31250. 3r31180.  3r31250. 3r31178. Begin the kernel installation step 3r31180.  3r31250. Finally we are at the core! Although technically it is not yet running. First, the kernel installation part has to configure something, including the decompressor and some things with memory management. After all this, she will unpack the real core and go to it. Installation starts at 3r33565. arch /x86 /boot /header.S
from symbol 3r3567. _start 3r31236. . 3r31180.  3r31250. 3r31180.  3r31250. At first glance, this may seem a bit strange, since there are several instructions in front of it. But a long time ago, the Linux kernel had its own loader. Now, if you run, for example, 3r3r1180.  3r31250. 3r31180.  3r31250. 3r31172. qemu-system-x86_64 vmlinuz-???-generic 3r31180.  3r31250. 3r31180.  3r31250. You will see:
 3r31250. 3r31180.  3r31250. 3r33587. 3r31180.  3r31250. 3r31180.  3r31250. Actually, file 3r31172. header.S starts with the magic number 3r3r94. MZ 3r31236. (see screenshot of the dump above), the text of the error message and the header 3r-3596. PE 3r31236. :
 3r31250. 3r31180.  3r31250. 3r31144. 3r31172. #ifdef CONFIG_EFI_STUB
# "MZ", MS-DOS header
.byte 0x4d
.byte 0x5a
#endif
3r31250. 3r31250. 3r31250. pe_header:
.ascii "PE"
.word 0 3r31147. 3r31180.  3r31250. It is needed to load the operating system with support for 3r33618. UEFI 3r31236. . His device will consider in the following chapters. 3r31180.  3r31250. 3r31180.  3r31250. The actual entry point for installing the kernel is
 3r31250. 3r31180.  3r31250. 3r31144. 3r31172. //header.S line 292
.globl _start
_start: 3r31147. 3r31180.  3r31250. The loader (grub2 and others) knows about this point (offset 0x200 From MZ ) And goes straight to it, although header.S starts with section .bstext where is the text of the error message: 3r31180.  3r31250. 3r31180.  3r31250. 3r31144. 3r31172. //
//arch /x86 /boot /setup.ld
//
. = 0; //current position
.bstext: {* (. bstext)} //put .bstext section to position 0
.bsdаta: {* (. bsdata)} 3r31147. 3r31180.  3r31250. Kernel installation entry point:
 3r31250. 3r31180.  3r31250. 3r31144. 3r31172. .globl _start
_start: 3r31250. .byte 0xeb
.byte start_of_setup-1f
1:
//
//rest of the header
// 3r31147. 3r31180.  3r31250. Here we see the operation code jmp ( 0xeb ), Which goes to the point start_of_setup-1f . In the notation Nf for example, 3r31172. 2f3r31173. refers to the local tag 3r31172. 2: . In our case, this is 1 3r3r1173. which is present immediately after the transition, and it contains the rest of the setup header. Immediately after the installation header, we see section .entrytext that starts with the label start_of_setup . 3r31180.  3r31250. 3r31180.  3r31250. This is the first actually executed code (except for the previous transition instructions, of course). After part of the kernel installation gets control from the bootloader, the first instruction is jmp is located at offset 0x200 3r31173. from the beginning of the real kernel mode, that is, after the first 512 bytes. This can be seen both in the Linux kernel boot protocol and in the grub2 source code:
 3r31250. 3r31180.  3r31250. 3r31144. 3r31172. segment = grub_linux_real_target 4; 3r31250. state.gs = state.fs = state.es = state.ds = state.ss = segment; 3r31250. state.cs = segment + 0x20; 3r31173. 3r31147. 3r31180.  3r31250. In our case, the kernel is loaded at 0x10000 . This means that after starting the kernel installation, the registers of the segments will have the following values: 3r31180.  3r31250. 3r31180.  3r31250. 3r31172. gs = fs = es = ds = ss = 0x10000
 3r31250. cs = 0x10200
3r31180.  3r31250. 3r31180.  3r31250. After the transition to start_of_setup The kernel should do the following:
 3r31250. 3r31180.  3r31250. 3r31182.  3r31250. 3r31234. Ensure that all segment register values ​​are the same 3r31237.  3r31250. 3r31234. If necessary, set up the correct stack.  3r31250. 3r31234. Customize bss 3r31237.  3r31250. 3r31234. Go to code C at arch /x86 /boot /main. with 3r31236. 3r31237.  3r31250. 3r31239. 3r31180.  3r31250. Let's see how this is implemented. 3r31180.  3r31250. 3r31180.  3r31250. 3r31178. Alignment of segment registers 3r31180.  3r31250. First of all, the kernel verifies that the registers of segments are ds and 3r31172. es
point to the same address. Then clears the direction flag with instruction cld 3r31173. :
 3r31250. 3r31180.  3r31250. 3r31144. 3r31172. movw% ds,% ax
movw% ax,% es
cld 3r31173. 3r31147. 3r31180.  3r31250. As I wrote earlier, by default grub2 loads the kernel installation code at 3r31172. 0x10000
, but cs at 3r31172. 0x10200 , because execution does not start from the beginning of the file, but from the transition here:
 3r31250. 3r31180.  3r31250. 3r31144. 3r31172. _start: 3r31250. .byte 0xeb
.byte start_of_setup-1f 3r31147. 3r31180.  3r31250. This offset is at 512 3r31173. bytes from
4d 5a . It is also necessary to align cs from 3r31172. 0x10200 to 3r31172. 0x10000 , like all other registers of segments. After this set the stack:
 3r31250. 3r31180.  3r31250. 3r31144. 3r31172. pushw% ds
pushw $ 6f
lretw 3r31147. 3r31180.  3r31250. This instruction pushes the value 3r3r1172 onto the stack. ds followed by the address of the label 6 and instruction lretw which loads the address of the tag. 6 in the register 3r3829. command counter 3r31236. and loads cs with the value ds . After that, ds and 3r31172. cs will be the same values. 3r31180.  3r31250. 3r31180.  3r31250. 3r31178. Setting up the stack 3r31180.  3r31250. Almost all of this code is part of the process of preparing the environment for the C language in real mode. The next step is to check the value of the register ss and create the correct stack, if the value is ss invalid: 3r31180.  3r31250. 3r31180.  3r31250. 3r31144. 3r31172. movw% ss,% dx
cmpw% ax,% dx
movw% sp,% dx
je 2f 3r31147. 3r31180.  3r31250. This can trigger three different scenarios: 3r3r1180.  3r31250. 3r31180.  3r31250. 3r31182.  3r31250. 3r31234. 3r31172. ss Acceptable value 0x1000 (Like all other registers, except for cs )  3r31250. 3r31234. 3r31172. ss invalid value, and flag CAN_USE_HEAP installed (see below) 3r3r1237.  3r31250. 3r31234. 3r31172. ss invalid value, and flag CAN_USE_HEAP not installed (see below) 3r31237.  3r31250. 3r31239. 3r31180.  3r31250. Consider all the scenarios in order:
 3r31250. 3r31180.  3r31250. 3r31182.  3r31250. 3r31234. U 3r31172. ss permissible value ( 0x1000 ). In this case, we go to label 2:  3r31250. 3r31239. 3r31180.  3r31250. 3r31144. 3r31172. 2: andw $ ~ ?% dx
jnz 3f
movw $ 0xfffc,% dx
3: movw% ax,% ss
movzwl% dx,% esp
sti 3r31173. 3r31147. 3r31180.  3r31250. Here we set the register alignment to dx (which contains the value sp , indicated by the loader) at 4 bytes and check for zero. If it is zero, then we put it in dx The value of 0xfffc (aligned on 4 bytes address before the maximum segment size of 64 KB). If it is not equal to zero, then continue to use the value sp specified by the loader ( 0xf7f4 in our case). Then put the value ax in 3r31172. ss That keeps the correct address of the segment 0x1000 and sets the correct 3r31172. sp . Now we have the right stack:
 3r31250. 3r31180.  3r31250. 3r3951. 3r31180.  3r31250. 3r31180.  3r31250. 3r31182.  3r31250. 3r31234. In the second scenario, ss! = ds . First, put the value _end (address of the end of the installation code) in 3r31172. dx and check the header field 3r3r1172. loadflags using instruction testb to check if heap can be used. 3r33939. loadflags - This is the bitmask header, which is defined as follows: 3r31237.  3r31250. 3r31239. 3r31180.  3r31250. 3r31144. 3r31172. #define LOADED_HIGH (10)
#define QUIET_FLAG (15)
#define KEEP_SEGMENTS (16)
#define CAN_USE_HEAP (17) 3r31147. 3r31180.  3r31250. and, as indicated in the download protocol:
 3r31250. 3r31180.  3r31250. 3r31172. Field Name: loadflags
 3r31250. 3r31180.  3r31250. This field is a bitmask. 3r31180.  3r31250. 3r31180.  3r31250. Bit 7 (write): CAN_USE_HEAP
 3r31250. Set this bit to 1 to indicate that the value is
 3r31250. heap_end_ptr is valid. If this field is empty,
will be disabled.  3r31250. part of the functionality of the installation. 3r31173. 3r31180.  3r31250. 3r31180.  3r31250. If bit 3r31172 is set. CAN_USE_HEAP then in 3r31172. dx set the value heap_end_ptr (which points to r3r31172. _end ) and add to it r3r31172. STACK_SIZE (the minimum stack size is 1024 bytes). After this, go to the label 2 (as in the previous case) and doing the right stack. 3r31180.  3r31250. 3r31180.  3r31250. 3r3-1027. 3r31180.  3r31250. 3r31180.  3r31250. 3r31182.  3r31250. 3r31234. If CAN_USE_HEAP not installed, just use the minimum stack of _end to 3r31172. _end + STACK_SIZE : 3r31237.  3r31250. 3r31239. 3r31180.  3r31250.  3r31250. 3r31180.  3r31250. 3r31144. 3r31172. cmpl $ 0x5a5aaa5? setup_sig
jne setup_bad 3r31147. 3r31180.  3r31250. The instruction simply compares setup_sig with the magic number 0x5a5aaa55. If they are not equal, a fatal error is reported. 3r31180.  3r31250. 3r31180.  3r31250. If the magic number is the same and we have a set of correct segment registers and a stack, then it remains only to configure the BSS section before proceeding to code C. 3r3r1180.  3r31250. 3r31180.  3r31250. The BSS section is used to store statically allocated uninitialized data. Linux carefully checks that this area of ​​memory has zeroed out: 3r31180.  3r31250. 3r31180.  3r31250. 3r31144. 3r31172. movw $ __ bss_start,% di
movw $ _end + ?% cx
xorl% eax,% eax
subw% di,% cx
shrw $ ?% cx
rep; stosl 3r31147. 3r31180.  3r31250. The first thing is the starting address __bss_start moves to r3r31172. di . Then the address is 3r31172. _end + 3 (+3 for alignment by 4 bytes) is moved to cx . Register eax is cleared (using the instruction xor ), the bss partition size is calculated ( cx-di ) and it is placed in 3r31172. cx . Then cx is divided into four (the size of the "words") and the instruction is used repeatedly. stosl while maintaining the value of eah 3r31173. (zero) to the address pointing to 3r3r1172. di , automatically increasing di by four and repeating it until sk will not reach zero). The net effect of this code is that zeros are written to all words in memory from 3r31172. __bss_start to 3r31172. _end :
 3r31250. 3r31180.  3r31250. 3r31180.  3r31250. That's it: we have a stack and a BSS, so you can go to function 3r31172. main () C:
 3r31250. 3r31180.  3r31250. 3r31144. 3r31172. calll main 3r31147. 3r31180.  3r31250. Function main () is located in 3r31152. arch /x86 /boot /main.c . We will talk about it in the next part. 3r31180.  3r31250. 3r31180.  3r31250. 3r31178. Conclusion 3r31180.  3r31250. This is the end of the first part about the Linux kernel device. If you have any questions or suggestions, contact me on twitter , on 3r31164. mail or just create ticket 3r31236. . In the next section, we will see the first C code that is executed when installing the Linux kernel, the implementation of memory routines, such as 3r31172. memset , 3r31172. memcpy , 3r31172. earlyprintk , early implementation and console initialization, and more. 3r31180.  3r31250. 3r31180.  3r31250. 3r31178. References
3r31180.  3r31250. 3r31182.  3r31250. 3r31234. 3r31185. Intel 8038? Programmer's Reference, 1986 3r31236. 3r31237.  3r31250. 3r31234. 3r31190. The minimum boot loader for the Intel
architecture. 3r31237.  3r31250. 3r31234. 3r31195. 8086 3r31236. 3r31237.  3r31250. 3r31234. 3r31200. 80386 3r31236. 3r31237.  3r31250. 3r31234. Reset vector 3r31236. 3r31237.  3r31250. 3r31234.
Real mode
3r31237.  3r31250. 3r31234. 3r31215. Linux kernel boot protocol
3r31237.  3r31250. 3r31234. 3r31220. Coreboot, 3r3r1236 Developer Guide. 3r31237.  3r31250. 3r31234. 3r31225. Ralph Brown interrupt list
3r31237.  3r31250. 3r31234. 3r31230. Power source 3r31236. 3r31237.  3r31250. 3r31234.
The power is normal signal is 3r31236. 3r31237.  3r31250. 3r31239. 3r31246. 3r31250. 3r31250. 3r31243. ! function (e) {function t (t, n) {if (! (n in e)) {for (var r, a = e.document, i = a.scripts, o = i.length; o-- ;) if (-1! == i[o].src.indexOf (t)) {r = i[o]; break} if (! r) {r = a.createElement ("script"), r.type = "text /jаvascript", r.async =! ? r.defer =! ? r.src = t, r.charset = "UTF-8"; var d = function () {var e = a.getElementsByTagName ("script")[0]; e.parentNode.insertBefore (r, e)}; "[object Opera]" == e.opera? a.addEventListener? a.addEventListener ("DOMContentLoaded", d! ): d ()}}} t ("//mediator.mail.ru/script/2820404/"""_mediator") () (); 3r31244. 3r31250. 3r31246. 3r31250. 3r31250. 3r31250. 3r31250.
+ 0 -

Add comment