1. 程式人生 > >從軟盤複製資料到記憶體

從軟盤複製資料到記憶體

本文闡述在計算機載入程式中可以用來複制核心程式碼的程式,平臺: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;

 

結果如下: