linux源代码剖析之二boot

“boot”
boot.s
|
| boot.s
|
| boot.s is loaded at 0x7c00 by the bios-startup routines, and moves itself
| out of the way to address 0x90000, and jumps there.
|
| It then loads the system at 0x10000, using BIOS interrupts. Thereafter
| it disables all interrupts, moves the system down to 0x0000, changes
| to protected mode, and calls the start of system. System then must
| RE-initialize the protected mode in it’s own tables, and enable
| interrupts as needed.
|
| NOTE! currently system is at most 8*65536 bytes long. This should be no
| problem, even in the future. I want to keep it simple. This 512 kB
| kernel size should be enough - in fact more would mean we’d have to move
| not just these start-up routines, but also do something about the cache-
| memory (block IO devices). The area left over in the lower 640 kB is meant
| for these. No other memory is assumed to be “physical”, ie all memory
| over 1Mb is demand-paging. All addresses under 1Mb are guaranteed to match
| their physical addresses.
|
| NOTE1 abouve is no longer valid in it’s entirety. cache-memory is allocated
| above the 1Mb mark as well as below. Otherwise it is mainly correct.
|
| NOTE 2! The boot disk type must be set at compile-time, by setting
| the following equ. Having the boot-up procedure hunt for the right
| disk type is severe brain-damage.
| The loader has been made as simple as possible (had to, to get it
| in 512 bytes with the code to move to protected mode), and continuos
| read errors will result in a unbreakable loop. Reboot by hand. It
| loads pretty fast by getting whole sectors at a time whenever possible.
在这里插入图片描述

| 1.44Mb disks:
sectors = 18
| 1.2Mb disks:
| sectors = 15
| 720kB disks:
| sectors = 9

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

BOOTSEG = 0x07c0
INITSEG = 0x9000
SYSSEG = 0x1000 | system loaded at 0x10000 (65536).
ENDSEG = SYSSEG + SYSSIZE

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
go: mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov sp,#0x400 | arbitrary value >>512

mov	ah,#0x03	| read cursor pos
xor	bh,bh
int	0x10

mov	cx,#24
mov	bx,#0x0007	| page 0, attribute 7 (normal)
mov	bp,#msg1
mov	ax,#0x1301	| write string, move cursor
int	0x10

| 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

| if the read went well we get current cursor position ans save it for
| posterity.
在这里插入图片描述

mov	ah,#0x03	| read cursor pos
xor	bh,bh
int	0x10		| save it in known place, con_init fetches
mov	[510],dx	| it from 0x90510.

| now we want to move to protected mode …

cli			| no interrupts allowed !

| 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
j do_move

| then we load the segment descriptors
在这里插入图片描述

end_move:

mov	ax,cs		| right, forgot this at first. didn't work :-)
mov	ds,ax
lidt	idt_48		| load idt with 0,0
lgdt	gdt_48		| load gdt with whatever appropriate

| that was painless, now we enable A20
在这里插入图片描述

call	empty_8042
mov	al,#0xD1		| command write
out	#0x64,al
call	empty_8042
mov	al,#0xDF		| A20 on
out	#0x60,al
call	empty_8042

| well, that went ok, I hope. Now we have to reprogram the interrupts 😦
| we put them right after the intel-reserved hardware interrupts, at
| int 0x20-0x2F. There they won’t mess up anything. Sadly IBM really
| messed this up with the original PC, and they haven’t been able to
| rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f,
| which is used for the internal hardware interrupts as well. We just
| have to reprogram the 8259’s, and it isn’t fun.
在这里插入图片描述

mov	al,#0x11		| initialization sequence
out	#0x20,al		| send it to 8259A-1
.word	0x00eb,0x00eb		| jmp $+2, jmp $+2
out	#0xA0,al		| and to 8259A-2
.word	0x00eb,0x00eb
mov	al,#0x20		| start of hardware int's (0x20)
out	#0x21,al
.word	0x00eb,0x00eb
mov	al,#0x28		| start of hardware int's 2 (0x28)
out	#0xA1,al
.word	0x00eb,0x00eb
mov	al,#0x04		| 8259-1 is master
out	#0x21,al
.word	0x00eb,0x00eb
mov	al,#0x02		| 8259-2 is slave
out	#0xA1,al
.word	0x00eb,0x00eb
mov	al,#0x01		| 8086 mode for both
out	#0x21,al
.word	0x00eb,0x00eb
out	#0xA1,al
.word	0x00eb,0x00eb
mov	al,#0xFF		| mask off all interrupts for now
out	#0x21,al
.word	0x00eb,0x00eb
out	#0xA1,al

| well, that certainly wasn’t fun 😦. Hopefully it works, and we don’t
| need no steenking BIOS anyway (except for the initial loading 😃.
| The BIOS-routine wants lots of unnecessary data, and it’s less
| “interesting” anyway. This is how REAL programmers do it.
|
| Well, now’s the time to actually move into protected mode. To make
| things as simple as possible, we do no register set-up or anything,
| we let the gnu-compiled 32-bit programs do that. We just jump to
| absolute address 0x00000, in 32-bit protected mode.
在这里插入图片描述

mov	ax,#0x0001	| protected mode (PE) bit
lmsw	ax		| This is it!
jmpi	0,8		| jmp offset 0 of segment 8 (cs)

| This routine checks that the keyboard command queue is empty
| No timeout is used - if this hangs there is something wrong with
| the machine, and we probably couldn’t proceed anyway.
在这里插入图片描述

empty_8042:
.word 0x00eb,0x00eb
in al,#0x64 | 8042 status port
test al,#2 | is input buffer full?
jnz empty_8042 | yes - loop
ret

| 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)
|
| This routine has to be recompiled to fit another drive type,
| just change the “sectors” variable at the start of the file
| (originally 18, for a 1.44Mb drive)
|
在这里插入图片描述

sread: .word 1 | sectors read of current track
head: .word 0 | current head
track: .word 0 | current track
read_it:
mov ax,es
test ax,#0x0fff
die: jne die | es must be at 64kB boundary
xor bx,bx | bx is starting address within segment
rp_read:
mov ax,es
cmp ax,#ENDSEG | have we loaded all yet?
jb ok1_read
ret
ok1_read:
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,#9
ok2_read:
call read_track
mov cx,ax
add ax,sread
cmp ax,#sectors
jne ok3_read
mov ax,#1
sub ax,head
jne ok4_read
inc track
ok4_read:
mov head,ax
xor ax,ax
ok3_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_read

read_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
ret
bad_rt: mov ax,#0
mov dx,#0
int 0x13
pop dx
pop cx
pop bx
pop ax
jmp read_track

/*

  • 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 dx
mov dx,#0x3f2
mov al,#0
outb
pop dx
ret

gdt:
.word 0,0,0,0 | dummy

.word	0x07FF		| 8Mb - limit=2047 (2048*4096=8Mb)
.word	0x0000		| base address=0
.word	0x9A00		| code read/exec
.word	0x00C0		| granularity=4096, 386

.word	0x07FF		| 8Mb - limit=2047 (2048*4096=8Mb)
.word	0x0000		| base address=0
.word	0x9200		| data read/write
.word	0x00C0		| granularity=4096, 386

idt_48:
.word 0 | idt limit=0
.word 0,0 | idt base=0L

gdt_48:
.word 0x800 | gdt limit=2048, 256 GDT entries
.word gdt,0x9 | gdt base = 0X9xxxx

msg1:
.byte 13,10
.ascii “Loading system …”
.byte 13,10,13,10

.text
endtext:
.data
enddata:
.bss
endbss:



head.s
/*

  • head.s contains the 32-bit startup code.
  • NOTE!!! Startup happens at absolute address 0x00000000, which is also where
  • the page directory will exist. The startup code will be overwritten by
  • the page directory.
    */
    在这里插入图片描述

.text
.globl _idt,_gdt,_pg_dir
_pg_dir:
startup_32:
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
mov %ax,%fs
mov %ax,%gs
lss _stack_start,%esp
call setup_idt
call setup_gdt
movl $0x10,%eax # reload all the segment registers
mov %ax,%ds # after changing gdt. CS was already
mov %ax,%es # reloaded in ‘setup_gdt’
mov %ax,%fs
mov %ax,%gs
lss _stack_start,%esp
xorl %eax,%eax
1: incl %eax # check that A20 really IS enabled
movl %eax,0x000000
cmpl %eax,0x100000
je 1b
movl %cr0,%eax # check math chip
andl $0x80000011,%eax # Save PG,ET,PE
testl $0x10,%eax
jne 1f # ET is set - 387 is present
orl $4,%eax # else set emulate bit
1: movl %eax,%cr0
jmp after_page_tables

/*

  • setup_idt
  • sets up a idt with 256 entries pointing to
  • ignore_int, interrupt gates. It then loads
  • idt. Everything that wants to install itself
  • in the idt-table may do so themselves. Interrupts
  • are enabled elsewhere, when we can be relatively
  • sure everything is ok. This routine will be over-
  • written by the page tables.
    */
    在这里插入图片描述

setup_idt:
lea ignore_int,%edx
movl $0x00080000,%eax
movw %dx,%ax /* selector = 0x0008 = cs /
movw $0x8E00,%dx /
interrupt gate - dpl=0, present */

lea _idt,%edi
mov $256,%ecx

rp_sidt:
movl %eax,(%edi)
movl %edx,4(%edi)
addl $8,%edi
dec %ecx
jne rp_sidt
lidt idt_descr
ret

/*

  • setup_gdt
  • This routines sets up a new gdt and loads it.
  • Only two entries are currently built, the same
  • ones that were built in init.s. The routine
  • is VERY complicated at two whole lines, so this
  • rather long comment is certainly needed 😃.
  • This routine will beoverwritten by the page tables.
    */
    在这里插入图片描述

setup_gdt:
lgdt gdt_descr
ret

.org 0x1000
pg0:

.org 0x2000
pg1:

.org 0x3000
pg2: # This is not used yet, but if you
# want to expand past 8 Mb, you’ll have
# to use it.

.org 0x4000
after_page_tables:
pushl $0 # These are the parameters to main 😃
pushl $0
pushl $0
pushl $L6 # return address for main, if it decides to.
pushl $_main
jmp setup_paging
L6:
jmp L6 # main should never return here, but
# just in case, we know what happens.

/* This is the default interrupt “handler” 😃 */
.align 2
ignore_int:
incb 0xb8000+160 # put something on the screen
movb $2,0xb8000+161 # so that we know something
iret # happened

/*

  • Setup_paging
  • This routine sets up paging by setting the page bit
  • in cr0. The page tables are set up, identity-mapping
  • the first 8MB. The pager assumes that no illegal
  • addresses are produced (ie >4Mb on a 4Mb machine).
  • NOTE! Although all physical memory should be identity
  • mapped by this routine, only the kernel page functions
  • use the >1Mb addresses directly. All “normal” functions
  • use just the lower 1Mb, or the local data space, which
  • will be mapped to some other place - mm keeps track of
  • that.
  • For those with more memory than 8 Mb - tough luck. I’ve
  • not got it, why should you 😃 The source is here. Change
  • it. (Seriously - it shouldn’t be too difficult. Mostly
  • change some constants etc. I left it at 8Mb, as my machine
  • even cannot be extended past that (ok, but it was cheap 😃
  • I’ve tried to show which constants to change by having
  • some kind of marker at them (search for “8Mb”), but I
  • won’t guarantee that’s all 😦 )
    */
    在这里插入图片描述

.align 2
setup_paging:
movl $10243,%ecx
xorl %eax,%eax
xorl %edi,%edi /
pg_dir is at 0x000 /
cld;rep;stosl
movl $pg0+7,_pg_dir /
set present bit/user r/w /
movl $pg1+7,_pg_dir+4 /
--------- " " --------- /
movl $pg1+4092,%edi
movl $0x7ff007,%eax /
8Mb - 4096 + 7 (r/w user,p) /
std
1: stosl /
fill pages backwards - more efficient 😃 /
subl $0x1000,%eax
jge 1b
xorl %eax,%eax /
pg_dir is at 0x0000 /
movl %eax,%cr3 /
cr3 - page directory start /
movl %cr0,%eax
orl $0x80000000,%eax
movl %eax,%cr0 /
set paging (PG) bit /
ret /
this also flushes prefetch-queue */

.align 2
.word 0
idt_descr:
.word 2568-1 # idt contains 256 entries
.long _idt
.align 2
.word 0
gdt_descr:
.word 256
8-1 # so does gdt (not that that’s any
.long _gdt # magic number, but it works for me :^)

.align 3

_idt: .fill 256,8,0 # idt is uninitialized

_gdt: .quad 0x0000000000000000 /* NULL descriptor /
.quad 0x00c09a00000007ff /
8Mb /
.quad 0x00c09200000007ff /
8Mb /
.quad 0x0000000000000000 /
TEMPORARY - don’t use /
.fill 252,8,0 /
space for LDT’s and TSS’s etc */

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

低调的小哥哥

你的关注就是我为你服务的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值