從軟盤複製資料到記憶體
本文闡述在計算機載入程式中可以用來複制核心程式碼的程式,平臺:centos,語言:AT&T格式的x86彙編,虛擬機器:bochs,編譯器:gcc。
設定核心程式碼緊跟在512位元組的boot程式後面
利用bios複製程式:
movw $0x9000,%ax //將要寫入的位置的段地址存入es movw %ax,%es //由於沒有直接將運算元存入es的指令,所以需要通過ax中轉 movb $0,%ch //ch存入要讀取的柱面,這裡是第一個柱面 movb $0,%dh //dh存入磁頭號,在軟盤中,如果是雙面軟盤,有兩個磁頭0號和1號,在這裡,核心程式處於0號磁頭的讀取範圍 movb $2,%cl //cl存入扇區號,在軟盤中,一個扇區是256位元組,boot程式佔了軟盤的前512位元組,即0和1扇區。核心緊跟在boot之後,所以這裡將扇區2存入cl movb $0x02,%ah //ah存入要呼叫的功能,功能0x2為讀操作 movb $1,%al //al存入要讀取的扇區數,這裡假設核心的大小隻有1個扇區(即256位元組,但一般是不可能這麼小的) movw $0,%bx //bx為待寫入的偏移地址,es:bx共同指向了要寫入的地址 movb $0x00,%dl //dl存入驅動器號,軟盤為0x0~0x7f,硬碟為0x80~0xff int $0x13 //使用int指令呼叫13號軟中斷
以上程式碼完成了從軟盤(或硬碟)讀入資料到記憶體,但要使讀入的程式可以工作,就要一定要記得設定資料段暫存器,然後再跳轉到執行的地址:
movw $0x9000,%ax
movw %ax,%ds //使用ax對資料段暫存器操作
movw $0,%bx //ds:bx共同指向資料段
ljmp $0x9000,$0 //段間跳轉要使用長跳轉指令,ljmp的作用就相當於修改cs:ip(cs為程式碼段暫存器),使其指向下一個要執行的指令,同時我們也可以推測出jmp指令就是修改指令指標暫存器ip
完整程式如下,共分為2個部分,boot.s和kernel.s
boot.s:
.code16
jmp _start
//這裡儲存要顯示的資訊
.section .data
warnning:
.ascii "Load the kernel!\n\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\0"
.section .text
.globl _start
//fat12格式,這一步可以不用設定
.byte 0x90
.ascii "HARIBOTE"
.word 512
.byte 1
.word 1
.byte 2
.word 224
.word 2880
.byte 0xf0
.word 9,18,2
.int 0,2880
.byte 0,0,0x29
.int 0xffffffff
.ascii "HARIBOTEOS "
.ascii "FAT12 "
.int 0,0,0,0
.byte 0,0
_start:
//關中斷
cli
cld
//設定段暫存器
xorw %ax,%ax
movw %ax,%es
movw %ax,%ss
movw %ax,%ds
//載入核心至0x90000
movw $0x9000,%ax
movw %ax,%es
movb $0,%ch
movb $0,%dh
movb $2,%cl
movb $0x02,%ah
movb $1,%al
movw $0,%bx
movb $0x00,%dl
int $0x13
pushw $warnning |
|
//提示使用者正在載入核心 |
call print
//設定資料段
movw $0x9000,%ax
movw %ax,%ds
movw $0,%bx
//跳轉到0x90000處
ljmp $0x9000,$0
fin:
hlt
jmp fin
/**********************呼叫的函式************************/
//顯示一段字串,使用pushw推入字串首地址
.type print,@function
print:
.code16
pushw %bp
movw %sp,%bp
movw 4(%bp),%si
putloop:
movb (%si),%al
cmp $0,%al
je exit
movb $0x0e,%ah
movw $15,%bx
int $0x10
incw %si
jmp putloop
exit:
movw %bp,%sp
popw %bp
ret
kernel.s:
//這部分程式碼將被放到映象的前512位元組之後
.code16
.section .data
msg:
.ascii "\nhello,lindorx\b\b\b\b\b\b\b\b\b\b\b\b\b\n\0"
.section .text
.globl entry
entry:
pushw $msg
call print
spin:
hlt
jmp spin
/**********************呼叫的函式************************/
//顯示一段字元
.type print,@function
print:
.code16
pushw %bp
movw %sp,%bp
movw 4(%bp),%si
putloop:
movb (%si),%al
cmp $0,%al
je fin
movb $0x0e,%ah
movw $15,%bx
int $0x10
incw %si
jmp putloop
fin:
movw %bp,%sp
popw %bp
ret
makefile部分:
F1=boot.o boot.out kernel.o kernel.out
F2=lindorx.img boot kernel
IMAGES=lindorx.img
KERNEL_SIZE=2799
SIZE=2880
DD_BS=512
#boot為引導部分,kernel為主要的核心程式,這段程式碼建立一個空白映象檔案,然後寫入引導和核心
$(IMAGES):boot kernel
dd if=/dev/zero of=$(IMAGES) bs=$(DD_BS) count=$(SIZE)
dd if=boot of=$(IMAGES) conv=notrunc
dd if=kernel of=$(IMAGES) conv=notrunc bs=$(DD_BS) count=$(KERNEL_SIZE) skip=0 seek=1
#本段程式碼截取出編譯好的載入程式中的載入程式部分,擴充為512位元組,並將最後兩位元組設定為AA55,生成引導檔案boot
boot:boot.out sign.pl
objcopy -S -O binary -j .text -j .data boot.out boot.bin
cp boot.bin boot
perl sign.pl boot
-rm -rf boot.bin
#將編譯好的程式中的核心程式截取出來,生成檔案kernel
kernel:kernel.out
objcopy -S -O binary -j .text -j .data kernel.out kernel
#dd if=/dev/zero of=kernel bs=$(DD_BS) count=$(KERNEL_SIZE)
#dd if=kernel.bin of= kernel conv=notrunc
#連結載入程式中間檔案
boot.out:boot.o
ld -m elf_i386 -N -e _start -Ttext 0x7c00 -o boot.out boot.o
#連結核心程式中間檔案
kernel.out:kernel.o
ld -m elf_i386 -N -e entry -Ttext 0 -o kernel.out kernel.o
#編譯所有彙編程式(.s檔案),生成中間檔案(.o檔案)
%.o:%.s
gcc -nostdinc -fno-stack-protector -fno-tree-ch -Wall -Wno-format -Wno-unused -Werror -gstabs -m32 -fno-omit-frame-pointer -DJOS_KERNEL -gstabs -c -o [email protected] $<
#呼叫qemu測試映象
qemu:$(IMAGES)
qemu-system-i386 $(IMAGES)
bochs:$(IMAGES)
bochs -f bochsrc
#clean用來清除所有中間檔案
clean:
-rm -rf $(F1)
#clean-all用來清除所有中間檔案及二進位制載入程式,核心程式,核心映象
clean-all:
-rm -rf $(F1) $(F2)
生成時需要用到的檔案sign.pl(來自mit6.828課程):
#!/usr/bin/perl
open(BB, $ARGV[0]) || die "open $ARGV[0]: $!";
binmode BB;
my $buf;
read(BB, $buf, 1000);
$n = length($buf);
if($n > 510){
print STDERR "boot block too large: $n bytes (max 510)\n";
exit 1;
}
print STDERR "boot block is $n bytes (max 510)\n";
$buf .= "\0" x (510-$n);
$buf .= "\x55\xAA";
open(BB, ">$ARGV[0]") || die "open >$ARGV[0]: $!";
binmode BB;
print BB $buf;
close BB;
結果如下: