Ren*_*ata 1 x86 assembly qemu osdev bootloader
我正在开发自己的引导加载程序 + 内核。我创建了一个项目放在github上:https : //github.com/rprata/ubootlua(分支tmp-libc-implementation)
我尝试使用 QEMU 运行我的 boot.bin:
qemu-system-i386 -fda boot.bin -nographic -serial stdio -monitor none
但是会发生崩溃:
> qemu-system-i386 -fda ./deploy/boot.bin -nographic -serial stdio -monitor none
> WARNING: Image format was not specified for './deploy/boot.bin' and probing guessed raw.
> Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
> Specify the 'raw' format explicitly to remove the restrictions.
> qemu: fatal: Trying to execute code outside RAM or ROM at 0x000a0000
>
> EAX=00000055 EBX=00018eb4 ECX=00018eb3 EDX=00000000
ESI=00000001 EDI=00000000 EBP=00016058 ESP=00015f94
EIP=0009ffae EFL=00000896 [-OS-AP-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 00007c36 00000018
IDT= 00000000 000003ff
CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000
DR6=ffff0ff0 DR7=00000400
CCS=00000055 CCD=000000d1 CCO=ADDB
EFER=0000000000000000
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
FPR0=0000000000000000 0000 FPR1=0000000000000000 0000
FPR2=0000000000000000 0000 FPR3=0000000000000000 0000
FPR4=0000000000000000 0000 FPR5=0000000000000000 0000
FPR6=0000000000000000 0000 FPR7=0000000000000000 0000
XMM00=00000000000000000000000000000000 XMM01=00000000000000000000000000000000
XMM02=00000000000000000000000000000000 XMM03=00000000000000000000000000000000
XMM04=00000000000000000000000000000000 XMM05=00000000000000000000000000000000
XMM06=00000000000000000000000000000000 XMM07=00000000000000000000000000000000
> makefile:26: recipe for target 'run' failed
> make: *** [run] Aborted (core dumped)
Run Code Online (Sandbox Code Playgroud)
我的 boot.asm 和 linker.ld:
section .boot
bits 16 ; We're working at 16-bit mode here
global boot
boot:
mov ax, 0x2401
int 0x15 ; Enable A20 bit
mov ax, 0x3 ; Set VGA text mode 3
int 0x10 ; Otherwise, call interrupt for printing the char
mov [disk],dl
mov ah, 0x2 ;read sectors
mov al, 60 ;sectors to read
mov ch, 0 ;cylinder idx
mov dh, 0 ;head idx
mov cl, 2 ;sector idx
mov dl, [disk] ;disk idx
mov bx, copy_target ;target pointer
int 0x13
cli ; Disable the interrupts
lgdt [gdt_pointer] ; Load the gdt table
mov eax, cr0 ; Init swap cr0...
or eax,0x1 ; Set the protected mode bit on special CPU reg cr0
mov cr0, eax
jmp CODE_SEG:boot32 ; Long jump to the code segment
; base a 32 bit value describing where the segment begins
; limit a 20 bit value describing where the segment ends, can be multiplied by 4096 if granularity = 1
; present must be 1 for the entry to be valid
; ring level an int between 0-3 indicating the kernel Ring Level
; direction:
; > 0 = segment grows up from base, 1 = segment grows down for a data segment
; > 0 = can only execute from ring level, 1 = prevent jumping to higher ring levels
; read/write if you can read/write to this segment
; accessed if the CPU has accessed this segment
; granularity 0 = limit is in 1 byte blocks, 1 = limit is multiples of 4KB blocks
; size 0 = 16 bit mode, 1 = 32 bit protected mode
gdt_start:
dq 0x0
gdt_code:
dw 0xFFFF
dw 0x0
db 0x0
db 10011010b
db 11001111b
db 0x0
gdt_data:
dw 0xFFFF
dw 0x0
db 0x0
db 10010010b
db 11001111b
db 0x0
gdt_end:
gdt_pointer:
dw gdt_end - gdt_start
dd gdt_start
disk:
db 0x0
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
;; Magic numbers
times 510 - ($ - $$) db 0
dw 0xaa55
copy_target:
bits 32
msg: db "Hello, World more than 512 bytes!", 0
boot32:
mov ax, DATA_SEG
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
;mov esi, msg ; SI now points to our message
;mov ebx, 0xb8000 ; vga memory position (0)
.loop lodsb ; Loads SI into AL and increments SI [next char]
or al, al ; Checks if the end of the string
jz halt ; Jump to halt if the end
or eax,0x0200 ; The top byte defines the character colour in the buffer as an int value from 0-15 with 0 = black, 1 = blue and 15 = white.
; The bottom byte defines an ASCII code point
mov word [ebx], ax
add ebx, 2
jmp .loop ; Next iteration of the loop
halt:
mov esp, kernel_stack_top
extern __start
call __start
cli
hlt ; CPU command to halt the execution
section .bss
align 4
kernel_stack_bottom: equ $
resb 16384 ; 16 KB
kernel_stack_top:
Run Code Online (Sandbox Code Playgroud)
ENTRY(boot)
OUTPUT_FORMAT("binary")
SECTIONS {
. = 0x7c00;
.text :
{
*(.boot)
*(.text)
}
.rodata :
{
*(.rodata)
}
.data :
{
*(.data)
}
.bss :
{
*(.bss)
}
}
Run Code Online (Sandbox Code Playgroud)
我的 makefile 的相关部分是:
NASM:=nasm
CC:=gcc
SRC_NASM:=./src/init/boot.asm
SRC_C:=./src/init/boot.c ./src/init/init.c ./src/init/version.c
LINKER:=./src/init/linker.ld
DEPLOY=./deploy
BUILD:=./build
BIN:=$(DEPLOY)/boot.bin
OBJ_NASM:=$(BUILD)/boot.o
CFLAGS:=-Wall -Werror -m32 -fno-pie -ffreestanding -mno-red-zone -fno-exceptions -nostdlib -I./src/include
LDFLAGS:=
export ARCH:=i386
export ZLIB_SUPPORT:=false
DEPENDENCIES:=libc
ifeq ($(ZLIB_SUPPORT),true)
DEPENDENCIES:=$(DEPENDENCIES) zlib
endif
all: $(DEPENDENCIES)
mkdir -p $(DEPLOY)
mkdir -p $(BUILD)
$(NASM) $(SRC_NASM) -f elf32 -o $(OBJ_NASM)
$(CC) $(SRC_C) $(OBJ_NASM) -o $(BIN) $(CFLAGS) -T $(LINKER) $(LDFLAGS)
run:
qemu-system-i386 -fda $(BIN) -nographic -serial stdio -monitor none
Run Code Online (Sandbox Code Playgroud)
为什么它会以这种方式失败,我该如何解决?
主要问题是您没有将整个内核读入内存。您的代码最终会执行未初始化的内存(很可能用零填充),到达扩展 BIOS 数据区(位于 0xa0000 处的视频内存下方),然后最终在 0xa0000 处开始执行视频内存。QEMU 不允许执行视频内存,因此是您得到错误的根源。
解决这个问题并不像乍看起来那么容易。你在我系统上的代码大约是 47300 字节。1 个扇区用于 MBR,92 个扇区用于内核。第一个问题是并非所有硬件(和模拟器)都可以一次读取 92 个扇区。QEMU 和 BOCH 的最大软盘驱动器为 72,硬盘驱动器为 128。对于某些硬件(低至每个磁道的扇区数),此数字可能较小。
某些硬件不会读取扇区:
使用 BOCHS 和 QEMU 加载高达 64KiB 的内核的一个简单技巧是将 64 个扇区 (32KiB) 读取到物理地址 0x0000:0x8000,然后将 64 个扇区的第二个副本复制到 0x1000:0x0000。您可以通过读取额外的 32KiB 块来读取更大的内核。0x0000:0x7e00 和 0x0000:0x8000 之间的 512 个字节将不被使用。唯一真正的问题是确定用于Int 21h/AH=02磁盘读取的柱面扇区 (CHS) 值1。
其他事宜:
.bss部分被归零填充。.bss在调用C入口点之前,使用链接描述文件来确定该部分的范围并将内存清零。.note.gnu.build-id,该部分会干扰您的链接器脚本。要解决此问题,您可以告诉 GCC 使用LDFLAGS:=-Wl,--build-id=none. 如果您直接与 LD 链接,则不会创建此部分。考虑到所有这些变化:
链接器.ld:
ENTRY(boot)
SECTIONS {
. = 0x7c00;
.boot :
{
*(.boot)
}
/* Place kernel right after boot sector on disk but set the
* VMA (ORiGin point) to 0x8000 */
. = 0x8000;
__kernel_start = .;
__kernel_start_seg = __kernel_start >> 4;
.text : AT(0x7e00)
{
*(.text.start)
*(.text*)
}
.rodata :
{
*(.rodata*)
}
.data :
{
*(.data)
}
/* Compute number of sectors that the kernel uses */
__kernel_end = .;
__kernel_size_sectors = (__kernel_end - __kernel_start + 511) / 512;
.bss :
{
__bss_start = .;
*(COMMON)
*(.bss)
. = ALIGN(4);
__bss_end = .;
/* Compute number of DWORDS that BSS section uses */
__bss_sizel = (__bss_end - __bss_start) / 4;
}
}
Run Code Online (Sandbox Code Playgroud)
引导.asm :
section .boot
bits 16 ; We're working at 16-bit mode here
global boot
boot:
xor ax, ax
mov ds, ax
mov ss, ax
mov sp, 0x7c00 ; Set SS:SP just below bootloader
cld ; DF=0 : string instruction forward movement
mov ax, 0x2401
int 0x15 ; Enable A20 bit
mov ax, 0x3 ; Set VGA text mode 3
int 0x10 ; Otherwise, call interrupt for printing the char
mov [disk],dl
; Read 64 sectors from LBA 1, CHS=0,0,2 to address 0x0800:0
mov ax, 0x0800
mov es, ax ;ES = 0x800
mov ah, 0x2 ;read sectors
mov al, 64 ;sectors to read
mov ch, 0 ;cylinder idx
mov dh, 0 ;head idx
mov cl, 2 ;sector idx
mov dl, [disk] ;disk idx
mov bx, 0 ;target pointer, ES:BX=0x0800:0x0000
int 0x13
; Read 64 sectors from LBA 65, CHS=1,1,12 to address 0x1000:0
mov ax, 0x1000
mov es, ax ;ES=0x1000
mov ah, 0x2 ;read sectors
mov al, 64 ;sectors to read
mov ch, 1 ;cylinder idx
mov dh, 1 ;head idx
mov cl, 12 ;sector idx
mov dl, [disk] ;disk idx
mov bx, 0x0000 ;target pointer, ES:BX=0x1000:0x0000
int 0x13
cli ; Disable the interrupts
lgdt [gdt_pointer] ; Load the gdt table
mov eax, cr0 ; Init swap cr0...
or eax,0x1 ; Set the protected mode bit on special CPU reg cr0
mov cr0, eax
jmp CODE_SEG:boot32 ; Long jump to the code segment
; base a 32 bit value describing where the segment begins
; limit a 20 bit value describing where the segment ends, can be multiplied by 4096
; if granularity = 1
; present must be 1 for the entry to be valid
; ring level an int between 0-3 indicating the kernel Ring Level
; direction:
; > 0 = segment grows up from base, 1 = segment grows down for a data segment
; > 0 = can only execute from ring level, 1 = prevent jumping to higher ring levels
; read/write if you can read/write to this segment
; accessed if the CPU has accessed this segment
; granularity 0 = limit is in 1 byte blocks, 1 = limit is multiples of 4KB blocks
; size 0 = 16 bit mode, 1 = 32 bit protected mode
gdt_start:
dq 0x0
gdt_code:
dw 0xFFFF
dw 0x0
db 0x0
db 10011010b
db 11001111b
db 0x0
gdt_data:
dw 0xFFFF
dw 0x0
db 0x0
db 10010010b
db 11001111b
db 0x0
gdt_end:
gdt_pointer:
dw gdt_end - gdt_start
dd gdt_start
disk:
db 0x0
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
;; Magic numbers
times 510 - ($ - $$) db 0
dw 0xaa55
section .data
msg: db "Hello, World more than 512 bytes!", 0
bits 32
section .text.start
boot32:
mov ax, DATA_SEG
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov esi, msg ; SI now points to our message
mov ebx, 0xb8000 ; vga memory position (0)
.loop:
lodsb ; Loads SI into AL and increments SI [next char]
or al, al ; Checks if the end of the string
jz halt ; Jump to halt if the end
or eax,0x0200 ; The top byte defines the character colour in the buffer as
; an int value from 0-15 with 0 = black, 1 = blue and 15 = white.
; The bottom byte defines an ASCII code point
mov word [ebx], ax
add ebx, 2
jmp .loop ; Next iteration of the loop
halt:
mov esp, kernel_stack_top
extern __start
extern __bss_start
extern __bss_sizel
; Zero the BSS section
mov ecx, __bss_sizel
mov edi, __bss_start
xor eax, eax
rep stosd
; Call C entry point
call __start
cli
hlt ; CPU command to halt the execution
section .bss
align 4
kernel_stack_bottom:
resb 16384 ; 16 KB stack
kernel_stack_top:
Run Code Online (Sandbox Code Playgroud)
通过添加这些 make 变量来修改makefile:
OC:=objcopy
DD:=dd
ELF:=$(DEPLOY)/boot.elf
Run Code Online (Sandbox Code Playgroud)
修改生成文件通过更改LDFLAGS到:
LDFLAGS:=-Wl,--build-id=none
Run Code Online (Sandbox Code Playgroud)
通过将规则更改为以下内容来修改makefileall:
all: $(DEPENDENCIES)
mkdir -p $(DEPLOY)
mkdir -p $(BUILD)
$(NASM) $(SRC_NASM) -f elf32 -o $(OBJ_NASM)
$(CC) $(SRC_C) $(OBJ_NASM) -o $(ELF) $(CFLAGS) -T $(LINKER) $(LDFLAGS)
$(OC) -O binary $(ELF) $(BIN)
$(DD) if=/dev/zero of=$(BIN).tmp count=1440 bs=1024
$(DD) if=$(BIN) of=$(BIN).tmp conv=notrunc
mv $(BIN).tmp $(BIN)
Run Code Online (Sandbox Code Playgroud)
鉴于Int 13/AH=2读取失败的方式有很多,可以通过一次读取一个扇区并始终读取可被 512 整除的内存位置来避免大多数问题。
当使用链接器脚本与内核一起构建引导加载程序时,您可以使用链接器来确定内核的大小并计算需要读取的扇区数。
可以完成所需工作的上述先前代码的修订可能如下所示。
链接器文件
ENTRY(boot)
SECTIONS {
. = 0x7c00;
.boot :
{
*(.boot)
}
__kernel_start = .;
__kernel_start_seg = __kernel_start >> 4;
.text :
{
*(.text.start)
*(.text*)
}
.rodata :
{
*(.rodata*)
}
.data :
{
*(.data)
}
/* Compute number of sectors that the kernel uses */
__kernel_end = .;
__kernel_size_sectors = (__kernel_end - __kernel_start + 511) / 512;
.bss :
{
__bss_start = .;
*(COMMON)
*(.bss)
. = ALIGN(4);
__bss_end = .;
/* Compute number of DWORDS that BSS section uses */
__bss_sizel = (__bss_end - __bss_start) / 4;
}
}
Run Code Online (Sandbox Code Playgroud)
主要区别在于此链接描述文件开始将内核加载到物理内存中的 0x07e00 而不是 0x08000。更精细的boot.asm可以使用链接器生成的值循环遍历所需的扇区,一次一个读取它们,直到完成:
extern __kernel_size_sectors ; Size of kernel in 512 byte sectors
extern __kernel_start_seg ; Segment start of kernel will be laoded at
global boot
STAGE2_LBA_START equ 1 ; Logical Block Address(LBA) Stage2 starts on
; LBA 1 = sector after boot sector
; Logical Block Address(LBA) Stage2 ends at
STAGE2_LBA_END equ STAGE2_LBA_START + __kernel_size_sectors
DISK_RETRIES equ 3 ; Number of times to retry on disk error
bits 16
section .boot
boot:
; Include a BPB (1.44MB floppy with FAT12) to be more compatible with USB floppy media
;%include "src/init/bpb.inc"
boot_start:
xor ax, ax ; DS=SS=ES=0 for stage2 loading
mov ds, ax
mov ss, ax ; Stack at 0x0000:0x7c00
mov sp, 0x7c00
cld ; Set string instructions to use forward movement
; Read Stage2 1 sector at a time until stage2 is completely loaded
load_stage2:
mov [bootDevice], dl ; Save boot drive
mov di, __kernel_start_seg ; DI = Current segment to read into
mov si, STAGE2_LBA_START ; SI = LBA that stage2 starts at
jmp .chk_for_last_lba ; Check to see if we are last sector in stage2
.read_sector_loop:
mov bp, DISK_RETRIES ; Set disk retry count
call lba_to_chs ; Convert current LBA to CHS
mov es, di ; Set ES to current segment number to read into
xor bx, bx ; Offset zero in segment
.retry:
mov ax, 0x0201 ; Call function 0x02 of int 13h (read sectors)
; AL = 1 = Sectors to read
int 0x13 ; BIOS Disk interrupt call
jc .disk_error ; If CF set then disk error
.success:
add di, 512>>4 ; Advance to next 512 byte segment (0x20*16=512)
inc si ; Next LBA
.chk_for_last_lba:
cmp si, STAGE2_LBA_END ; Have we reached the last stage2 sector?
jl .read_sector_loop ; If we haven't then read next sector
.stage2_loaded:
jmp stage2 ; Jump to second stage
.disk_error:
xor ah, ah ; Int13h/AH=0 is drive reset
int 0x13
dec bp ; Decrease retry count
jge .retry ; If retry count not exceeded then try again
error_end:
; Unrecoverable error; print drive error; enter infinite loop
mov si, diskErrorMsg ; Display disk error message
call print_string
cli
.error_loop:
hlt
jmp .error_loop
; Function: print_string
; Display a string to the console on display page 0
;
; Inputs: SI = Offset of address to print
; Clobbers: AX, BX, SI
print_string:
mov ah, 0x0e ; BIOS tty Print
xor bx, bx ; Set display page to 0 (BL)
jmp .getch
.repeat:
int 0x10 ; print character
.getch:
lodsb ; Get character from string
test al,al ; Have we reached end of string?
jnz .repeat ; if not process next character
.end:
ret
; Function: lba_to_chs
; Description: Translate Logical block address to CHS (Cylinder, Head, Sector).
; Works for all valid FAT12 compatible disk geometries.
;
; Resources: http://www.ctyme.com/intr/rb-0607.htm
; https://en.wikipedia.org/wiki/Logical_block_addressing#CHS_conversion
; /sf/ask/3180442961/
; Sector = (LBA mod SPT) + 1
; Head = (LBA / SPT) mod HEADS
; Cylinder = (LBA / SPT) / HEADS
;
; Inputs: SI = LBA
; Outputs: DL = Boot Drive Number
; DH = Head
; CH = Cylinder (lower 8 bits of 10-bit cylinder)
; CL = Sector/Cylinder
; Upper 2 bits of 10-bit Cylinders in upper 2 bits of CL
; Sector in lower 6 bits of CL
;
; Notes: Output registers match expectation of Int 13h/AH=2 inputs
;
lba_to_chs:
push ax ; Preserve AX
mov ax, si ; Copy LBA to AX
xor dx, dx ; Upper 16-bit of 32-bit value set to 0 for DIV
div word [sectorsPerTrack] ; 32-bit by 16-bit DIV : LBA / SPT
mov cl, dl ; CL = S = LBA mod SPT
inc cl ; CL = S = (LBA mod SPT) + 1
xor dx, dx ; Upper 16-bit of 32-bit value set to 0 for DIV
div word [numHeads] ; 32-bit by 16-bit DIV : (LBA / SPT) / HEADS
mov dh, dl ; DH = H = (LBA / SPT) mod HEADS
mov dl, [bootDevice] ; boot device, not necessary to set but convenient
mov ch, al ; CH = C(lower 8 bits) = (LBA / SPT) / HEADS
shl ah, 6 ; Store upper 2 bits of 10-bit Cylinder into
or cl, ah ; upper 2 bits of Sector (CL)
pop ax ; Restore scratch registers
ret
; Uncomment these lines if not using a BPB (via bpb.inc)
%ifndef WITH_BPB
numHeads: dw 2 ; 1.44MB Floppy has 2 heads & 18 sector per track
sectorsPerTrack: dw 18
%endif
bootDevice: db 0x00
diskErrorMsg: db "Unrecoverable disk error!", 0
; Pad boot sector to 510 bytes and add 2 byte boot signature for 512 total bytes
TIMES 510-($-$$) db 0
dw 0xaa55
section .data
msg: db "Hello, World more than 512 bytes!", 0
; base a 32 bit value describing where the segment begins
; limit a 20 bit value describing where the segment ends, can be multiplied by 4096
; if granularity = 1
; present must be 1 for the entry to be valid
; ring level an int between 0-3 indicating the kernel Ring Level
; direction:
; > 0 = segment grows up from base, 1 = segment grows down for a data segment
; > 0 = can only execute from ring level, 1 = prevent jumping to higher ring levels
; read/write if you can read/write to this segment
; accessed if the CPU has accessed this segment
; granularity 0 = limit is in 1 byte blocks, 1 = limit is multiples of 4KB blocks
; size 0 = 16 bit mode, 1 = 32 bit protected mode
gdt_start:
dq 0x0
gdt_code:
dw 0xFFFF
dw 0x0
db 0x0
db 10011010b
db 11001111b
db 0x0
gdt_data:
dw 0xFFFF
dw 0x0
db 0x0
db 10010010b
db 11001111b
db 0x0
gdt_end:
gdt_pointer:
dw gdt_end - gdt_start
dd gdt_start
disk:
db 0x0
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
bits 16
section .text.start
stage2:
cli ; Disable the interrupts
mov ax, 0x2401
int 0x15 ; Enable A20 bit
lgdt [gdt_pointer] ; Load the gdt table
mov eax, cr0 ; Init swap cr0...
or eax,0x1 ; Set the protected mode bit on special CPU reg cr0
mov cr0, eax
jmp CODE_SEG:startpm ; FAR JMP to the code segment
bits 32
startpm:
mov ax, DATA_SEG
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov esi, msg ; SI now points to our message
mov ebx, 0xb8000 ; vga memory position (0)
.loop:
lodsb ; Loads SI into AL and increments SI [next char]
or al, al ; Checks if the end of the string
jz halt ; Jump to halt if the end
or eax,0x0200 ; The top byte defines the character colour in the
; buffer as an int value from 0-15 with 0 = black,
; 1 = blue and 15 = white.
; The bottom byte defines an ASCII code point
mov word [ebx], ax
add ebx, 2
jmp .loop ; Next iteration of the loop
halt:
mov esp, kernel_stack_top
extern __start
extern __bss_start
extern __bss_sizel
; Zero the BSS section
mov ecx, __bss_sizel
mov edi, __bss_start
xor eax, eax
rep stosd
; Call C entry point
call __start
cli
hlt ; CPU command to halt the execution
section .bss
align 4
kernel_stack_bottom:
resb 16384 ; 16 KB stack
kernel_stack_top:
Run Code Online (Sandbox Code Playgroud)
这个boot.asm松散地基于我在另一个Stackoverflow 问答中提出的引导加载程序。主要区别在于链接器通过链接器脚本计算所需的大部分信息,而不是直接编码/包含在程序集文件中。此代码还将 A20 线路的启用和进入保护模式移动到第二阶段。如果您将来需要扩展引导加载程序中的功能,这可以释放空间。
如果您正在构建要在真实硬件上用作未分区媒体的引导加载程序 - 可以在bpb.inc文件中找到 1.44MiB BIOS 参数块 (BPB) 的副本。这对于使用软盘仿真 (FDD) 在 USB 介质上启动非常有用。要启用它,只需;从此行中删除:
; %include "src/init/bpb.inc"
Run Code Online (Sandbox Code Playgroud)
1有一个公式可以将基于零的逻辑块地址转换为一组 CHS 值:
Run Code Online (Sandbox Code Playgroud)C = LBA ÷ (HPC × SPT) H = (LBA ÷ SPT) mod HPC S = (LBA mod SPT) + 1
LBA 0 是引导扇区。如果内核在引导加载程序之后的连续扇区中,则内核的开始位于 LBA 1。内核的第二个 32KiB 块将位于 LBA 65(64+1)。对于 1.44MiB 软盘 HPC=2 和 SPT=18。根据计算,LBA 0=CHS(0,0,2) 和 LBA 65= CHS (1,1,12)。这些是第一个版本的boot.asm 中64 个扇区磁盘读取使用的值。
| 归档时间: |
|
| 查看次数: |
1737 次 |
| 最近记录: |