这两个汇编文件完成的功能和sagalinux中的基本一致,只不过实现的更好一点,增强了一些扩展性。
就把代码贴在这里。
看看就行,好像我一直是在贴代码。。。。这只算是我自己学习的一个记录吧。。。可能对大家帮助不大。
欢迎同学们留言讨论哦。
./src/geekos/boot.asm
; fd_boot.asm编译出的二进制代码存放在软盘的0号扇区内,BIOS会将此代码复制到0x07c00运行时它先将自身复制到0x90000处,然后跳到0x90000执行。 ; 执行时把setup代码和kernel代码载入到内存0x90200处和0x10000,boot代码执行完后 跳到内存中的0x90200处执行setup代码 ; Boot sector for GeekOS ; Copyright (c) 2001,2004 David H. Hovemeyer <[email protected]> ; Copyright (c) 2003, Jeffrey K. Hollingsworth <[email protected]> ; $Revision: 1.5 $ ; This is free software. You are permitted to use, ; redistribute, and modify it as specified in the file "COPYING". ; Loads setup code and a program image from sectors 1..n of a floppy ; and executes the setup code (which will in turn execute ; the program image). ; Some of this code is adapted from Kernel Toolkit 0.2 ; and Linux version 2.2.x, so the following copyrights apply: ; Copyright (C) 1991, 1992 Linus Torvalds ; modified by Drew Eckhardt ; modified by Bruce Evans (bde) ; adapted for Kernel Toolkit by Luigi Sgro %include "defs.asm" ; Pad to desired offset from start symbol. ; Usage: Pad_From_Symbol offset, symbol ;带两个参数的宏,在剩下的空间填充0,使达到一个扇区的大小 %macro Pad_From_Symbol 2 times (%1 - ($ - %2)) db 0 %endmacro ; ---------------------------------------------------------------------- ; The actual code ; ---------------------------------------------------------------------- [BITS 16] [ORG 0x0] BeginText: ; needed to calculate padding bytes to fill the sector ; Copy the boot sector into INITSEG. mov ax, BOOTSEG ; mov ax, 0x7c0 mov ds, ax ; source segment for string copy xor si, si ; source index for string copy mov ax, INITSEG ; mov ax, 0x900 mov es, ax ; destination segment for string copy xor di, di ; destination index for string copy cld ; clear direction flag mov cx, 256 ; number of words to copy rep movsw ; copy 512 bytes jmp INITSEG:after_move ; after_move is a offset.the offset in a segment won't change even the place of segment changes. after_move: ; Now we're executing in INITSEG ; We want the data segment to refer to INITSEG ; (since we've defined variables in the same place as the code) mov ds, ax ; ax still contains INITSEG ; Put the stack in the place where we were originally loaded. ; By definition, there is nothing important there now. mov ax, 0 mov ss, ax mov sp, (BOOTSEG << 4) + 512 - 2 load_setup: ; Load the setup code. mov ax, word [setupStart] ;mov ax, 1,setup从扇区号1开始 mov word [sec_count], ax ;mov word [sec_count], 1 add ax, [setupSize] ;注意这里的setupSize依赖于setup.s编译出的二进制文件大小,在根目录下的Makefile中由已编译出的setup.bin文件获得 mov word [max_sector], ax ;fd_boot.bin和setup.bin在软盘上扇区号0~[max_sector],(setup.bin占1个扇区的话就是0~2) .again: mov ax, [sec_count] ; push ax ; 1st param to ReadSector (log sec num),setup从扇区1开始存放 push word SETUPSEG ; 2nd param to ReadSector (seg base),push 0x9020,setup代码开始的内存段地址 sub ax, [setupStart] ; convert to 0-indexed,使ax中变成0 shl ax, 9 ; multiply by 512 push ax ; ...to get 3rd param (byte offset) ; read the sector from the floppy call ReadSector ;前面入栈了3个参数,ReasSector读取软盘上一个扇区到内存 add sp, 6 ; clear 3 word params ; on to next sector inc word [sec_count] ;读完一个扇区,增加扇区号 ; are we done? mov bx, word [max_sector] cmp word [sec_count], bx ;是否读完全部扇区 jl .again load_kernel: ;接下来载入内核,内核代码在软盘上紧挨setup代码。(下一个扇区) ; Load the kernel image from sectors KERN_START_SEC..n of the ; floppy into memory at KERNSEG. Note that there are 128 sectors ; per 64K segment. So, when figuring out which segment to ; load the sector into, we shift right by 7 bits (which is ; equivalent to dividing by 128). ; Figure out start sector and max sector mov ax, word [kernelStart] ;和上面载入setup的代码类似,[kernelStart]为kernel开始的逻辑扇区号 mov word [sec_count], ax add ax, word [kernelSize] mov word [max_sector], ax .again: mov ax, [sec_count] ; logical sector on the floppy,将把[sec_count]号扇区读到内存 push ax ; 1st param to ReadSector (log sec num) sub ax, [kernelStart] ; convert to 0-indexed,(可能扇区号ax值已经很大了,导致一个段内已经被填满了,需要切换到下一个段中去) mov cx, ax ; save in cx shr ax, 7 ; divide by 128,要放到KERNSEG后的第几个段内?,0x1234后一个段基址是0x2234.0x3234..... shl ax, 12 ; ...and multiply by 0x1000 add ax, KERNSEG ; ...to get base relative to KERNSEG push ax ; 2nd param to ReadSector (seg base) and cx, 0x7f ; mod sector by 128,除以一个段的大小64K==128Sector。得到剩下的扇区数偏移 shl cx, 9 ; ...and multiply by 512 push cx ; to get offset in segment (3rd parm),软盘上的偏移就是内存中相对于段基址的偏移 ; read the sector from the floppy call ReadSector add sp, 6 ; clear 3 word params ; on to next sector inc word [sec_count] ; have we loaded all of the sectors? mov bx, word [max_sector] cmp word [sec_count], bx jl .again ; Now we've loaded the setup code and the kernel image. ; Jump to setup code.已将setup和kernel载入到内存,boot任务完成,跳到setup代码处去执行 jmp SETUPSEG:0 ; Read a sector from the floppy drive. ; This code (and the rest of this boot sector) will have to ; be re-written at some point so it reads more than one ; sector at a time. ; ; Parameters: ; - "logical" sector number [bp+8] ; - destination segment [bp+6] ; - destination offset [bp+4] ReadSector: push bp ; set up stack frame,建立栈帧 mov bp, sp ; " pusha ; save all registers %if 0 ; debug params mov dx, [bp+8] call PrintHex call PrintNL mov dx, [bp+6] call PrintHex call PrintNL mov dx, [bp+4] call PrintHex call PrintNL %endif ; Sector = log_sec % SECTORS_PER_TRACK,得到扇区号,0~18 ; Head = (log_sec / SECTORS_PER_TRACK) % HEADS,得到磁头号,0 or 1 mov ax, [bp+8] ; get logical sector number from stack,得到setup.bin代码开始的逻辑扇区号,从0~n xor dx, dx ; dx is high part of dividend (== 0) mov bx, SECTORS_PER_TRACK ; divisor,bx=18 div bx ; do the division mov [sec], dx ; sector is the remainder,ax存扇区数,dx存余数 and ax, 1 ; same as mod by HEADS==2 (slight hack),ax如果是奇数的话就是磁头1,偶数的话就是磁头0,得到磁头号,搞点hack mov [head], ax ; 连续扇区是这样子的:柱面0:磁头0扇区0....磁头0扇区18,磁头1扇区0....磁头1扇区18。柱面1:磁头0扇区0....磁头0扇区18,磁头1扇区0....磁头1扇区18 ; Track = log_sec / (SECTORS_PER_TRACK*HEADS)得到柱面号,柱面号从0开始 mov ax, [bp+8] ; get logical sector number again xor dx, dx ; dx is high part of dividend mov bx, SECTORS_PER_TRACK*2 ; divisor,一个柱面上有36个扇区 div bx ; do the division,ax中得到柱面号 mov [track], ax ; track is quotient %if 0 ; debugging code mov dx, [sec] call PrintHex call PrintNL mov dx, [head] call PrintHex call PrintNL mov dx, [track] call PrintHex call PrintNL %endif ; Now, try to actually read the sector from the floppy, ; retrying up to 3 times. mov [num_retries], byte 0 .again: ;将setup.bin从软盘读到内存0x90200处 mov ax, [bp+6] ; dest segment...读到内存的段基址 mov es, ax ; goes in es mov ax, (0x02 << 8) | 1 ; function = 02h in ah, ; # secs = 1 in al mov bx, [track] ; track number... mov ch, bl ; goes in ch mov bx, [sec] ; sector number... mov cl, bl ; goes in cl... inc cl ; but it must be 1-based, not 0-based mov bx, [head] ; head number... mov dh, bl ; goes in dh xor dl, dl ; hard code drive=0 mov bx, [bp+4] ; offset goes in bx ; (es:bx points to buffer) ; Call the BIOS Read Diskette Sectors service int 0x13 ; If the carry flag is NOT set, then there was no error ; and we're done.读完一个扇区 jnc .done ; Error - code stored in ah mov dx, ax call PrintHex inc byte [num_retries] cmp byte [num_retries], 3 jne .again ; If we got here, we failed thrice, so we give up mov dx, 0xdead call PrintHex .here: jmp .here .done: ;成功读至内存 popa ; restore all regisiters pop bp ; leave stack frame ret ; Include utility routines %include "util.asm" ; ---------------------------------------------------------------------- ; Variables ; ---------------------------------------------------------------------- ; These are used by ReadSector head: dw 0 track: dw 0 sec: dw 0 num_retries: db 0 ; Used for loops reading sectors from floppy sec_count: dw 0 max_sector: dw 0 ; Padding to make the PFAT Boot Record sit just before the BIOS signature. Pad_From_Symbol PFAT_BOOT_RECORD_OFFSET, BeginText ; PFAT boot record ; Describes how to load the setup program and kernel. ; The default values are appropriate for creating a boot ; floppy by concatenating the boot sector, setup program, ; and kernel image. The buildFat program will change ; these values if the boot floppy is formatted as a PFAT ; filesystem. dw 0 dw 0 dw 0 dw 0 dw 0 dw 0 dw 0 dw 0 dw 0 dw 0 ;; part of pfat boot record setupStart: dw 1 ; by default, setup is at first sector ;; part of pfat boot record setupSize: dw NUM_SETUP_SECTORS ; number of sectors in setup ;; part of pfat boot record kernelStart: dw 1+NUM_SETUP_SECTORS ; default start sector for kernel ;; part of pfat boot record kernelSize: dw NUM_KERN_SECTORS ; number of sectors in kernel ; Finish by writing the BIOS signature to mark this as ; a valid boot sector.510 Pad_From_Symbol BIOS_SIGNATURE_OFFSET, BeginText Signature dw 0xAA55 ; BIOS controls this to ensure this is a boot sector
; GeekOS setup code ; Copyright (c) 2001,2004 David H. Hovemeyer <[email protected]> ; $Revision: 1.10 $ ; This is free software. You are permitted to use, ; redistribute, and modify it as specified in the file "COPYING". ; A lot of this code is adapted from Kernel Toolkit 0.2 ; and Linux version 2.2.x, so the following copyrights apply: ; Copyright (C) 1991, 1992 Linus Torvalds ; modified by Drew Eckhardt ; modified by Bruce Evans (bde) ; adapted for Kernel Toolkit by Luigi Sgro %include "defs.asm" [BITS 16] [ORG 0x0] start_setup: ; Redefine the data segment so we can access variables ; declared in this file. mov ax, SETUPSEG mov ds, ax ; Use int 15h to find out size of extended memory in KB. ; Extended memory is the memory above 1MB. So by ; adding 1MB to this amount, we get the total amount ; of system memory. We can only detect 64MB this way, ; but that's OK for now. mov ah, 0x88 int 0x15 add ax, 1024 ; 1024 KB == 1 MB,调试中ax=7c00h+400h=8000h mov [mem_size_kbytes], ax ; Kill the floppy motor. call Kill_Motor ; Block interrupts, since we can't meaningfully handle them yet ; and we no longer need BIOS services. cli ; Set up IDT and GDT registers,设置idtr和gdtr寄存器 lidt [IDT_Pointer] lgdt [GDT_Pointer] ; Initialize the interrupt controllers, and enable the ; A20 address line,开启A20地址线才能寻址1M以上的内存地址 call Init_PIC call Enable_A20 ; Switch to protected mode! mov ax, 0x01 lmsw ax ; Jump to 32 bit code.关键性的跳转,从16位代码跳至32位代码段 jmp dword KERNEL_CS:(SETUPSEG << 4) + setup_32 [BITS 32] setup_32: ; set up data segment registers mov ax, KERNEL_DS mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax ; Create the stack for the initial kernel thread. mov esp, KERN_STACK + 4096 ; Build Boot_Info struct on stack. ; Note that we push the fields on in reverse order, ; since the stack grows downwards. xor eax, eax mov ax, [(SETUPSEG<<4)+mem_size_kbytes] push eax ; memSizeKB //这里连续入栈的两个数,在main函数中作为Boot_Info结构参数 push dword 8 ; bootInfoSize ; Pass pointer to Boot_Info struct as argument to kernel ; entry point. push esp ; Push return address to make this look like a call ; XXX - untested push dword (SETUPSEG<<4)+.returnAddr ; Far jump into kernel,跳入内核代码,setup任务结束!ENTRY_POINT从内核符号表kernel.syms中获得 jmp KERNEL_CS:ENTRY_POINT .returnAddr: ; We shouldn't return here. .here: jmp .here [BITS 16] ; Kill the floppy motor. ; This code was shamelessly stolen from Linux. Kill_Motor: mov dx, 0x3f2 xor al, al out dx, al ret Init_PIC: ; Initialize master and slave PIC! mov al, ICW1 out 0x20, al ; ICW1 to master call Delay out 0xA0, al ; ICW1 to slave call Delay mov al, ICW2_MASTER out 0x21, al ; ICW2 to master call Delay mov al, ICW2_SLAVE out 0xA1, al ; ICW2 to slave call Delay mov al, ICW3_MASTER out 0x21, al ; ICW3 to master call Delay mov al, ICW3_SLAVE out 0xA1, al ; ICW3 to slave call Delay mov al, ICW4 out 0x21, al ; ICW4 to master call Delay out 0xA1, al ; ICW4 to slave call Delay mov al, 0xff ; mask all ints in slave out 0xA1, al ; OCW1 to slave call Delay mov al, 0xfb ; mask all ints but 2 in master out 0x21, al ; OCW1 to master call Delay ret ; Linux uses this code. ; The idea is that some systems issue port I/O instructions ; faster than the device hardware can deal with them. Delay: jmp .done .done: ret ; Enable the A20 address line, so we can correctly address ; memory above 1MB. Enable_A20: mov al, 0xD1 out 0x64, al call Delay mov al, 0xDF out 0x60, al call Delay ret ; ---------------------------------------------------------------------- ; Setup data ; ---------------------------------------------------------------------- mem_size_kbytes: dw 0 ; ---------------------------------------------------------------------- ; The GDT. Creates flat 32-bit address space for the kernel ; code, data, and stack. Note that this GDT is just used ; to create an environment where we can start running 32 bit ; code. The kernel will create and manage its own GDT. ; ---------------------------------------------------------------------- ; GDT initialization stuff NUM_GDT_ENTRIES equ 3 ; number of entries in GDT GDT_ENTRY_SZ equ 8 ; size of a single GDT entry align 8, db 0 GDT: ; Descriptor 0 is not used dw 0 dw 0 dw 0 dw 0 ; Descriptor 1: kernel code segment dw 0xFFFF ; bytes 0 and 1 of segment size dw 0x0000 ; bytes 0 and 1 of segment base address db 0x00 ; byte 2 of segment base address db 0x9A ; present, DPL=0, non-system, code, non-conforming, ; readable, not accessed db 0xCF ; granularity=page, 32 bit code, upper nibble of size db 0x00 ; byte 3 of segment base address ; Descriptor 2: kernel data and stack segment ; NOTE: what Intel calls an "expand-up" segment ; actually means that the stack will grow DOWN, ; towards lower memory. So, we can use this descriptor ; for both data and stack references. dw 0xFFFF ; bytes 0 and 1 of segment size dw 0x0000 ; bytes 0 and 1 of segment base address db 0x00 ; byte 2 of segment base address db 0x92 ; present, DPL=0, non-system, data, expand-up, ; writable, not accessed db 0xCF ; granularity=page, big, upper nibble of size db 0x00 ; byte 3 of segment base address GDT_Pointer: dw NUM_GDT_ENTRIES*GDT_ENTRY_SZ ; limit dd (SETUPSEG<<4) + GDT ; base address IDT_Pointer: dw 0 dd 00