现在的位置: 首页 > 综合 > 正文

GeekOS源代码学习(1) fd_boot.asm 和 setup.asm

2014年01月04日 ⁄ 综合 ⁄ 共 11569字 ⁄ 字号 评论关闭

这两个汇编文件完成的功能和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

./src/geekos/setup.asm

; 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的话,这里稍微看一下就明白了。。

抱歉!评论已关闭.