1. The role of the loader

Loader is used to switch the CPU running mode and load the kernel files on hard disks. Since the kernel file is large, we copy it byte by byte to below 1M memory.

The flowchart of the loader file is as follows:

2. The realization of the loader

2.1 Searching in the File Systemkernelfile

Searching for the kernel file in loader is the same as searching for the loader file in boot. The difference is that the file name we need to find is changed from Loader BIN to kernel BIN.

2.2 findkernelafter

After finding the kernel file, similar to boot, we first need to obtain the file size and sector location of the kernel file. Use same Func_DirPointerToStartSectorOfFile and Func_DirPointerToStartSectorOfFilePointer used in the boot. So the code before loading the kernel file should be

; === Open the memory space to store the start sector of the kernel file and the size of the sector occupied by the kernel file. === Define the protected mode segment descriptor gdt_STARt_DESC DQ 0x0000000000000000 GDT_CODE_DESC DQ 0x00CF9a000000FFFF GDT_datA_desc Dq 0x00cf92000000ffff gdt_size dw $ - gdt_start_desc gdt_base dd gdt_start_desc Label_KernelFound: ; Convert the obtained kernel directory pointer to the start sector of the kernel file and the size of the sector occupied by the kernel file call Func_DirPointerToSectorOfFile mov word[SectorOfKernel], ax call Func_DirPointerToStartSectorOfFilePointer mov word[StartSectorOfKernel], ax ; === load protected mode segment descriptor table LGDT [gdt_size]; In al, 0x92 or al, 00000010B out 0x92, al; Mov eax, cr0 or eax, 1 mov cr0, eax; Mov AX, 0x0010 mov fs, ax; Mov eax, cr0 and al, 11111110b mov cr0, eaxCopy the code

The protected mode segment descriptors are detailed in Writing an operating system from scratch – 1.5 real mode and protected mode. Once you have found and obtained the size and location of the kernel, you can start loading the kernel file. And load the loader file, the kernel file overall size may be beyond the scope of the BIOS interrupt 13 h can read, as well as the required placed through real mode after 1 m memory addressing, so we need to open protected mode, the protection mode of segment descriptor in fs segment registers, and then exit the protected mode. Although it has exited protected mode at this time, but the FS segment register is still protected mode segment descriptor, so we can use BIOS interrupt in real mode at this time, also can access the content after 1M memory through protected mode addressing.

2.3 loadingkernel

Loading a kernel sector into the buffer is the same as loading a loader. The difference is that we only need it to load a sector for us, while copying a sector into 1M memory is the following function

OffsetOfCache equ 0x9000 OffsetOfKernel equ 0x100000 ByteIndexOfKernel dd 0 Func_CopySingleSectorOfKernel: ; The loop instruction will loop the number of times in the Cx register mov Cx, 512; The loDSB instruction will load the data in the memory address pointed to by the SI register one by one into the AL register mov si, OffsetOfCache; Mov EDI, OffsetOfKernel Label_CopySingleByteOfSector: LOdsb mov EDx, Dword [ByteIndexOfKernel]; Mov byte[fs:edi +edx], al; Each time a data is placed, the current kernel index adds one Inc dword[ByteIndexOfKernel] loop Label_CopySingleByteOfSector RETCopy the code

Therefore, the overall program for loading kernel files is

Label_LoadKernel: mov word[Index], 0 Label_ForIndexInSectorOfKernel: ; Mov Si, 1; Mov di, word[StartSectorOfKernel]; Mov dx, OffsetOfCache call Func_ReadSector Call Func_CopySingleSectorOfKernel inc word[Index] inc word[StartSectorOfKernel] mov ax, word[Index] cmp ax, word[SectorOfKernel] jne Label_ForIndexInSectorOfKernel ; Mov eax, cr0 or eax, 1 mov cr0, eax; Mov AX, 0x0010 mov DS, AX; Jump to IA-32E and enter segment JMP 0x0008:Label_ToLongModeCopy the code

3. Switch toIA - 32 e model

See 1.6 IA-32E mode for details about how to write an operating system from scratch. Similar to protected mode, IA-32E mode addressing is also addressed through the GDT global descriptor table. So our first step is to create the global descriptor table as we did for the protected mode.

gdt64_start_desc dq 0x0000000000000000
gdt64_code_desc  dq 0x0020980000000000
gdt64_data_desc  dq 0x0000920000000000

gdt64_size  dw  $ - gdt64_start_desc
gdt64_base  dd  gdt64_start_desc
Copy the code

The IA-32E mode eliminates the segment base and segment length limits for protected mode mid-segment descriptors, allowing the descriptor to directly address the entire address space. Then we need to create the page table:

PML4:
    mov dword[0x90000], 0x91007
PDPT:
    mov dword[0x91000], 0x92007
PDT:
    mov dword[0x92000], 0x000087
Copy the code

In the third-level page table PDT, the ending 0x87 indicates the use of 2MB pages. The next step is to enable IA-32E mode:

; Load IA-32E mode segment descriptor LGDT [gdT64_size]; Mov AX, 0x10 MOV DS, AX MOV ES, AX MOV FS, AX MOV GS, AX MOV SS, AX; Mov eax, cr4 BTS eax, 5 mov cr4, eax; Mov eax, 0x90000 mov cr3, eAX; Mov ECx, 0x0C0000080 RDMSR BTS eAX, 8 WRMSR; Mov eax, cr0 BTS eax, 0 BTS eax, 31 mov cr0, eaxCopy the code

Finally, the JMP 0x0008:0x100000 long jump causes the program to start running the kernel program we placed in 1M memory. The next step is the development of kernel program.