这两个汇编文件完成的功能和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 <daveho@cs.umd.edu>
; Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
; $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 <daveho@cs.umd.edu>
; $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就是这样,相信大家如果看过之前的sagalinux的话,这里稍微看一下就明白了。。
本文主要介绍了GeekOS操作系统中fd_boot.asm和setup.asm两个汇编文件的源代码学习,相较于Sagalinux,它们提供了更好的扩展性和初始化功能。内容以代码展示为主,作为作者个人学习的记录,可能对读者帮助有限。欢迎大家留言交流。
1949

被折叠的 条评论
为什么被折叠?



