X86 series CPUs can run in 16-bit real-time mode and 32-bit protection mode. The real-time mode features that the address bus only has 20 bits, that is, only 1 MB of addressing space, to be compatible with old CPUs, Intel x86 series CPUs, including the latest CPUs, run in 16-bit real-time mode during power-on. At the same time, on the hardware, the strong lines set CS to 0xF000 and IP to 0xFFF0, CS: the IP address points to the address 0xFFFF0, that is, the code is executed from the address when the power-on is started, and the BIOS is stored in this location. You can imagine that, what will happen if no BIOS is connected.
The BIOS program is stored in a ROM chip on the computer motherboard. First, the BIOS program reads 512 bytes of the first sector in the boot disk to the 0x7C00 address of the memory and jumps to this location, the content of this sector is exactly boot/bootsect. s, so the CPU control will be transferred from BOIS to the operating system side, look at the Code:
entry startstart: mov ax,#BOOTSEGmov ds,axmov ax,#INITSEGmov es,axmov cx,#256sub si,sisub di,direpmovwjmpigo,INITSEG
The value of BOOTSEG is 0x07c0, and the value of INITSEG is 0x9000. Then, the movw Command copies the 512 bytes of address 0x07C00 to the address 0x90000, and jumps to the go label.
go: mov ax,csmov ds,axmov es,ax! put stack at 0x9ff00.mov ss,axmov sp,#0xFF00 ! arbitrary value >>512
From the jmpi command, we can see that the CIDR block address is 0x9000, so the ds, es, and ss registers are both 0x9000, And the sp value is 0xFF00, that is, the stack pointer points to the address 0x9FF00.
The first-stage loading has been completed, and the second-stage setup is executed. The Code is as follows:
! load the setup-sectors directly after the bootblock.! Note that 'es' is already set up.load_setup: mov dx,#0x0000 ! drive 0, head 0mov cx,#0x0002 ! sector 2, track 0mov bx,#0x0200 ! address = 512, in INITSEGmov ax,#0x0200+SETUPLEN ! service 2, nr of sectorsint 0x13 ! read itjnc ok_load_setup ! ok - continuemov dx,#0x0000mov ax,#0x0000 ! reset the disketteint 0x13j load_setup
In the second stage, the setup code starts from 2nd sectors and consists of 4 sectors. The Code of these 4 sectors is copied to the address 0x90200 through the interrupt processing function of int 0x13, that is, it follows the bootsect code. If the code is copied successfully, it jumps to the symbol OK _load_setup.
Ah = 2 indicates the number of read sector al read sectors ch channel number cl sector number dh head number (for A floppy disk that is the surface number) dl drive number, for example, 0: Soft drive A 1: Soft drive Bes: bx points to the buffer for receiving data
Next, at OK _load_setup, the Code is as follows:
ok_load_setup:! Get disk drive parameters, specifically nr of sectors/trackmov dl,#0x00mov ax,#0x0800 ! AH=8 is get drive parametersint 0x13mov ch,#0x00seg csmov sectors,cxmov ax,#INITSEGmov es,ax
Call the int 13 H Interrupt Routine to read the drive parameters. Here, the maximum number of sectors of the track are read. The read result package contains 6 lower bits in the cl register (0 ~ 5), then store the cx register value in the sectors label, and reset the es register value to 0x9000.
! Print some inane message mov ah,#0x03 ! read cursor posxor bh,bhint 0x10mov cx,#24mov bx,#0x0007 ! page 0, attribute 7 (normal)mov bp,#msg1mov ax,#0x1301 ! write string, move cursorint 0x10
Call the int 10h Interrupt Routine to read the cursor position, and then call the int 10h Interrupt Routine to display a string "Loading system ...". The reason for this is that the loading of the system takes a long time. This information is displayed to prevent improper operations during the loading process that the user mistakenly considers as a machine fault.
A message is displayed. Load the system part to the memory 0x10000 address.
! ok, we've written the message, now! we want to load the system (at 0x10000) mov ax,#SYSSEG mov es,ax ! segment of 0x010000 call read_it call kill_motor
Call the read_it sub-function to complete loading.
! This routine loads the system at address 0x10000, making sure! no 64kB boundaries are crossed. We try to load it as fast as! possible, loading whole tracks whenever we can.!! in: es - starting address segment (normally 0x1000)!sread: .word 1+SETUPLEN ! sectors read of current trackhead: .word 0 ! current headtrack: .word 0 ! current trackread_it: mov ax,es test ax,#0x0fffdie: jne die ! es must be at 64kB boundary xor bx,bx ! bx is starting address within segmentrp_read: mov ax,es cmp ax,#ENDSEG ! have we loaded all yet? jb ok1_read retok1_read: seg cs mov ax,sectors sub ax,sread mov cx,ax shl cx,#9 add cx,bx jnc ok2_read je ok2_read xor ax,ax sub ax,bx shr ax,#9ok2_read: call read_track mov cx,ax add ax,sread seg cs cmp ax,sectors jne ok3_read mov ax,#1 sub ax,head jne ok4_read inc trackok4_read: mov head,ax xor ax,axok3_read: mov sread,ax shl cx,#9 add bx,cx jnc rp_read mov ax,es add ax,#0x1000 mov es,ax xor bx,bx jmp rp_readread_track: push ax push bx push cx push dx mov dx,track mov cx,sread inc cx mov ch,dl mov dx,head mov dh,dl mov dl,#0 and dx,#0x0100 mov ah,#2 int 0x13 jc bad_rt pop dx pop cx pop bx pop ax retbad_rt: mov ax,#0 mov dx,#0 int 0x13 pop dx pop cx pop bx pop ax jmp read_track
From the above section, we can see that the system part is loaded to the memory address 0x10000 through the int 13 H Interrupt Routine, and the system size is 192 k. After the system part is loaded, call the kill_motor sub-function.
/* * This procedure turns off the floppy drive motor, so * that we enter the kernel in a known state, and * don't have to worry about it later. */kill_motor:push dxmov dx,#0x3f2mov al,#0outbpop dxret
Kill_motor is used to disable a floppy drive motor.
! After that we check which root-device to use. If the device is! defined (!= 0), nothing is done and the given device is used.! Otherwise, either /dev/PS0 (2,28) or /dev/at0 (2,8), depending! on the number of sectors that the BIOS reports currently. seg csmov ax,root_devcmp ax,#0jne root_definedseg csmov bx,sectorsmov ax,#0x0208 ! /dev/ps0 - 1.2Mbcmp bx,#15je root_definedmov ax,#0x021c ! /dev/PS0 - 1.44Mbcmp bx,#18je root_definedundef_root:jmp undef_rootroot_defined:seg csmov root_dev,ax
Determine whether the root file system device is defined. If the device has been defined, it will jump to the root_defined symbol. If the device is not defined, the root file system device will be defined based on the number of track sectors, write back to root_dev.
! after that (everyting loaded), we jump to! the setup-routine loaded directly after! the bootblock: jmpi 0,SETUPSEG
The program has been loaded and the root file system device has been confirmed. Jump to the setup code (0x90200 ).
To sum up, this part of code mainly loads the three-phase code of the operating system from the boot disk to the memory, and finally jumps to the second-stage code.
Reference books:
1. The art of Linux kernel design
2. the Linux kernel is fully annotated with v3.0