从软盘复制数据到内存

本文阐述在计算机引导程序中可以用来复制内核代码的程序,平台: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 $@ $<

 

#调用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;

 

结果如下:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值