Adventures of Linux Source Code《0.11–Boot Start》

After booting, the bios is powered on, and the bios will copy the 512 bytes of the first sector of the boot disk to the location 0x7c00. Then the register CS:IP of CUP will point to the position of 0x7c00 and start running.
And the first sector puts the content of bootsect.s, that is, it starts to run from bootsect.s. That is to execute the initial command of the operating system.

bootsect.s

Here is the source code:

// boot/bootsect.s
SETUPLEN = 4 ; nr of setup-sectors
BOOTSEG = 0x07c0 ; original address of boot-sector
INITSEG = 0x9000 ; we move boot here - out of the way
SETUPSEG = 0x9020 ; setup starts here
SYSSEG = 0x1000 ; system loaded at 0x10000 (65536).
ENDSEG = SYSSEG + SYSSIZE ; where to stop loading

; ROOT_DEV: 0x000 - same type of floppy as boot.
; 0x301 - first partition on first drive etc.
ROOT_DEV = 0x306

entry start
start:
mov ax, #BOOTSEG
mov ds,ax
mov ax, #INITSEG
mov es,ax
mov cx,#256
sub si,si
sub di, di
rep
movw
jmpi go, INITSEG

The above behavior is to move the 512-byte code originally at 0x7c00 to 0x90000.

 jmpi go,INITSEG
// Jump to 0x90000: go execution
//Where go is a symbol, it will be converted to the relative offset address of the current file when compiling,
//So it is executed in the place where go goes.
go: mov ax,cs // cs = 0x9000
mov ds,ax // ds = 0x9000
mov es,ax // es = 0x9000
; put stack at 0x9ff00.
mov ss,ax // ss = 0x9000
mov sp,#0xFF00 ; arbitrary value >>512
// ss:sp = 0x9FF00 stack bottom position

The main thing that this code of go does is to assign values to each register. Review the role of the register:

ax bx cx dx
cs code segment ds data segment ss stack segment es additional segment
sp stack segment pointer bp base address pointer si source index di destination address
ip instruction pointer flags flag idtr interrupt descriptor table index gdtr global descriptor table index

Here the values of various registers are set. Next continue:

load_setup:
mov dx,#0x0000 ; drive 0, head 0
mov cx,#0x0002 ; sector 2, track 0
mov bx,#0x0200 ; address = 512, in INITSEG
mov ax,#0x0200 + SETUPLEN ; service 2, nr of sectors
int 0x13 ; read it
jnc ok_load_setup ; ok - continue
mov dx,#0x0000
mov ax,#0x0000 ; reset the diskette
int 0x13
j load_setup

int 0x13 is an interrupt, and the assignment of the first four registers are all parameters for this interrupt program. Here it is constantly trying to load 2-5 sectors of the disk at address 0x90200. ok jumps to ok_load_setup to execute. Otherwise keep repeating.

ok_load_setup:
; Get disk drive parameters, specifically nr of sectors/track
...
; Print some inane message
...
mov ax, #SYSSEG
mov es,ax ; segment of 0x010000
call read_it
...

The first part of this part is to read the disk information and call int 0x13 interrupt. Then load the 240 sectors from the 6th sector to 0x10000. Finally start to jump, enter setup.s to execute:

 jmpi 0,SETUPSEG // 0x90200 Enter the code part of setupseg;

setup.s

INITSEG = 0x9000 ; we move boot here - out of the way
SYSSEG = 0x1000 ; system loaded at 0x10000 (65536).
SETUPSEG = 0x9020 ; this is the current segment

.globl begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text

entry start
start:

; ok, the read went well so we get current cursor position and save it for
; posterity.

mov ax,#INITSEG ; this is done in bootsect already, but... ax = 0x9000
mov ds,ax // ds = 0x9000
mov ah,#0x03 ; read cursor pos
xor bh, bh
int 0x10 ; save it in known place, con_init fetches
mov [0],dx ; it from 0x90000.

int 0x10 The interrupt calls the display service interrupt routine. The following is to obtain various information:

; Get memory size (extended mem, kB) memory information

mov ah,#0x88
int 0x15
mov[2],ax

; Get video-card data: video card information

mov ah,#0x0f
int 0x10
mov [4],bx ; bh = display page
mov [6],ax ; al = video mode, ah = window width

; check for EGA/VGA and some config parameters

mov ah,#0x12
mov bl,#0x10
int 0x10
mov[8],ax
mov[10],bx
mov[12],cx

; Get hd0 data No.1 HDD information

mov ax,#0x0000
mov ds,ax
lds si,[4*0x41]
mov ax, #INITSEG
mov es,ax
mov di,#0x0080
mov cx,#0x10
rep
movsb

; Get hd1 data 2 hard disk information

mov ax,#0x0000
mov ds,ax
lds si,[4*0x46]
mov ax, #INITSEG
mov es,ax
mov di,#0x0090
mov cx,#0x10
rep
movsb

If executed as above, various information can be stored in memory, and their distribution in memory is as follows:

Address Length Remarks
0x90000 2 Cursor position
0x90002 2 extended memory Number
0x90004 2 display page
0x90006 1 display mode
0x90007 1 number of character columns td>
0x90008 2 Unknown
0x9000A 1 display memory
0x9000B 1 display status
0x9000C 2 Graphic card characteristic parameters
0x9000E 1 Screen rows
0x9000F 1 Screen columns
0x90080 16 hard disk 1 parameter list
0x90090 16 hard disk 2 parameter table
0x901FC 2 root device number

The next thing to do is to overwrite the interrupt vector table of the BIOS, and introduce the interrupt table of the operating system itself. At the same time, move the memory to another location.

; now we want to move to protected mode...

cli ; no interrupts allowed ; first turn off interrupts;

; first we move the system to it's rightful place

mov ax,#0x0000
cld ; 'direction'=0, movs moves forward
do_move:
mov es,ax ; destination segment
add ax,#0x1000
cmp ax,#0x9000
jz end_move
mov ds,ax ; source segment
sub di, di
sub si,si
mov cx,#0x8000
rep
movsw
jmp do_move

The content here is to move the memory from 0x10000 to 0x90000 to the beginning, and directly overwrite the original bios information: (the picture below is found from the Internet)

This organizes most of the memory distribution.

memory address content
Stack top address
0x90200- setup
0x90000-0x901FF Temporary variable
0x0 – 0x80000 The content of the operating system (sector 6-266)

Enter protected mode in the next section.