继
读京山游侠《使用 GCC 和 GNU Binutils 编写能在 x86 实模式运行的 16 位代码》一文之后,进行了从硬盘载入系统的试验。
参照的是
这篇网文。
试验在bochs下以硬盘启动通过:(环境为Ubuntu18(64bit),apt-get install bochs bochs-x hexedit)
mbr.asm( for bochs ):
;mbr程序,加载用户程序
;日期:2020年1月5日
bits 16
LOADER_LBA_START equ 1 ; 用户程序在硬盘的扇区
;SECTION mbr align=16
;[section .data]
;msg_fail: db "Fail!",0x0d,0x0a,0
;[section .text align=16]
SECTION mbr align=16 vstart=0x7c00
global _start
_start:
;读取硬盘中的loader
xor ax, ax ;ax寄存器清零
mov ss, ax ;堆栈段
mov sp, ax ;栈顶指针
mov ax, LOADER_LBA_START ;低16位LBA地址
push ax
xor ax, ax; 清零
push ax ; 高16位地址
push ax; 偏移地址为0
mov ax, [boot_loader_memory_start_base_address]
push ax
call readDataFromHDD
; 为了能够验证是否读取了硬盘中的数据
mov ds, ax
xor bx, bx
add bx, 4
mov cx, [bx]
jmp 0x1000:0x0
jmp $
readDataFromHDD:
; 输入参数1 LBA地址低16位
; 输入参数2 LBA地址高16位
; 输入参数3 目的起始位置的偏移地址
; 输入参数4 目的起始位置的段基地址
push ax
push bx
push cx
push dx
push ds
mov dx, 0x1f2 ;硬盘控制命令字
mov al, 1 ; 读取的扇区数目
out dx, al
; 取出参数
; 从栈中取出LBA地址低16位
mov bp, sp
add bp, 18
mov ax, [bp]
inc dx ; 端口 0x1f3
out dx, al
inc dx ; 端口自增 0x1f4
mov al, ah ; 输出只能用al
out dx, al
; 从栈中取出LBA地址高16位
sub bp, 2
mov ax, [bp]
; 输入高16位地址
inc dx ; 0x1f5
out dx, al
inc dx ; 0x1f6
mov al, 0xe0 ; LBA28模式,主盘
or al, ah
out dx, al
inc dx ;0x1f7
mov al, 0x20 ; 读扇区控制命令字
out dx, al
;=========================================
; 等待硬盘准备阶段
.wait:
in al, dx ; 读取硬盘是否忙碌状态
and al, 0x88 ; 只关心这几位
cmp al, 0x08 ; 比较忙碌标志位是否忙碌
jnz .wait ; 忙碌则跳转
;=========================================
; 读取数据阶段
mov cx, 256 ; 256个字
mov dx, 0x1f0 ; 硬盘的输出端口
; 从栈中获取目标位置的偏移地址
sub bp, 2
mov bx, [bp] ; 偏移地址保存到基址寄存器中
; 从栈中读取目标位置的段基地址
sub bp, 2
mov ax, [bp]
mov ds, ax
.readw:
; 读取字
in ax, dx
mov [bx], ax
add bx, 2
loop .readw
pop ds
pop dx
pop cx
pop bx
pop ax
ret 5
;boot loader 加载到内存的位置
boot_loader_memory_start_base_address dw 0x1000 ; 将用户程序加载到内存的段基地址
msg: db "OK!",0x0d,0x0a,0
len equ 3
len_fail equ 5
DispOK:
mov ax, msg
mov bp, ax
mov cx, len
mov dh, 0x00
mov dl, 0x00
mov ah, 0x13
mov al, 0x1
mov bh, 0x00
mov bl, 0x0a
int 0x10
ret
;DispFail:
;movw ax, msg_fail
;movw bp, ax
;movw $len_fail, %cx
;movb $18, %dh
;movb $70, %dl
;movb $0x13, %ah
;movb $0x1, %al
;movb $0x00,%bh
;movb $0x0c,%bl
;int $0x10
;ret
times 510-($-$$) db 0
db 0x55, 0xaa
data2.s( for bochs ):
#bits 16
.code16
#LOADER_LBA_START equ 1 ; 用户程序在硬盘的扇区
.text
.global _start
_start:
mov %cs, %ax
mov %ax, %ds
mov %ax, %es
call DispFail
jmp .
.set len, 3
.set len_fail, 5
#len equ 3
#len_fail equ 5
#DispOK:
# mov ax, msg
# mov bp, ax
# mov cx, len
# mov dh, 0x00
# mov dl, 0x00
# mov ah, 0x13
# mov al, 0x1
# mov bh, 0x00
# mov bl, 0x0a
# int 0x10
# ret
DispFail:
pushw %bp
movw %sp, %bp
pushw %ax
pushw %bp
pushw %cx
pushw %dx
pushw %bx
movw $msg_fail, %ax
movw %ax, %bp
movw $len_fail, %cx
movb $18, %dh
movb $70, %dl
movb $0x13, %ah
movb $0x1, %al
movb $0x00,%bh
movb $0x0c,%bl
int $0x10
popw %bx
popw %dx
popw %cx
popw %bp
popw %ax
leave
ret
#msg: .asciz "OK!"
msg_fail: .asciz "Fail!"
.org 510
.word 0xaa55
编译脚本如下:
make_HDD.sh(for bochs):
#!/bin/bash
rm -f *.o *.bin *.elf *.out disassem*.asm
nasm -o mbr.bin mbr.asm
nasm -f elf32 -o mbr.elf mbr.asm
objdump -D -m i8086 mbr.elf > disassem1.asm
as --32 data2.s -o data2.o
ld -e _start -Ttext 0x0 -m elf_i386 data2.o -o boot.elf
#ld -e _start -T boot_HDD.lds -m elf_i386 data2.o -o boot.elf
objcopy -R .pdr -R .comment -R .note -S -O binary boot.elf data2.bin
objdump -D -m i8086 boot.elf > disassem2.asm
dd if=mbr.bin of=boot_HDD.img bs=512 count=1 conv=notrunc
dd if=data2.bin of=boot_HDD.img bs=512 seek=1 count=1 conv=notrunc
运行bochs配置文件如下:
bochsrc_HDD(for bochs):
config_interface: textconfig
#display_library: sdl
#romimage: file=/usr/share/bochs/BIOS-bochs-latest, address=0xf0000
romimage: file=/usr/share/bochs/BIOS-bochs-latest
megs: 32
vgaromimage: file=/usr/share/vgabios/vgabios.bin
#floppya: type=1_44, 1_44="boot.img", status=inserted
ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
ata1: enabled=0, ioaddr1=0x170, ioaddr2=0x370, irq=15
ata2: enabled=0, ioaddr1=0x1e8, ioaddr2=0x3e0, irq=11
ata3: enabled=0, ioaddr1=0x168, ioaddr2=0x360, irq=9
ata0-master: type=disk, path="boot_HDD.img", mode=flat, cylinders=121, heads=16, spt=63
ata0-slave: type=cdrom, path="/dev/cdrom", status=inserted
boot: disk
#ips: 1000000
floppy_bootsig_check: disabled=0
log: /dev/stdout
panic: action=ask
error: action=report
info: action=report
debug: action=ignore
debugger_log: -
com1: enabled=1, dev=/dev/ttyS0
parport1: enabled=1, file="/dev/lp0"
sb16: midimode=1, midi=/dev/midi00, wavemode=1, wave=/dev/dsp, loglevel=2, log=/dev/stdout, dmatimer=600000
vga_update_interval: 300000
keyboard_serial_delay: 250
keyboard_paste_delay: 100000
#floppy_command_delay: 500
mouse: enabled=1
private_colormap: enabled=0
#ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:00, ethmod=linux, ethdev=eth0
#keyboard_mapping: enabled=0, map=/usr/share/bochs/keymaps/x11-pc-de.map
#keyboard_type: mf
#user_shortcut: keys=ctrlaltdel
#magic_break: enabled=1
#cmosimage: cmos.img
#load32bitOSImage: os=nullkernel, path=../kernel.img, iolog=../vga_io.log
#load32bitOSImage: os=linux, path=../linux.img, iolog=../vga_io.log, initrd=../initrd.img
#i440fxsupport: enabled=1
#usb1: enabled=1, ioaddr=0xFF80, irq=10
#text_snapshot_check: enable
运行命令:bochs -f ./bochsrc_HDD
(虚拟硬盘boot_HDD.img是用bximage生成的60M硬盘。)
至此试验初步就告完成。感谢上面网文作者。
源文件打包在此:
test_my_HDD_0.99.tgz。