X86 在引导加载程序中加载内核之前检测内存
在我的引导加载程序中加载内核之前15小时,我尝试检测eax=0xe820 int的内存。 所以我做了一些调查,发现问题出在EDX寄存器上。 我想我已经为加载内核准备好了DL和BX,然后通过调用do_e820将它们销毁了 下面是我的引导加载程序文件bootloader.asm:X86 在引导加载程序中加载内核之前检测内存,x86,osdev,X86,Osdev,在我的引导加载程序中加载内核之前15小时,我尝试检测eax=0xe820 int的内存。 所以我做了一些调查,发现问题出在EDX寄存器上。 我想我已经为加载内核准备好了DL和BX,然后通过调用do_e820将它们销毁了 下面是我的引导加载程序文件bootloader.asm: [bits 16] global _start number_sector db 0 _start: cli xor ax, ax mov ds, ax mov es, ax m
[bits 16]
global _start
number_sector db 0
_start:
cli
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x8000 ; Stack pointer at SS:SP = 0x0000:0x8000
mov [BOOT_DRIVE], dl; Boot drive passed to us by the BIOS
mov dh, 17 ; Number of sectors (kernel.bin) to read from disk
; 17*512 allows for a kernel.bin up to 8704 bytes
mov bx, 0x9000 ; Load Kernel to ES:BX = 0x0000:0x9000
call do_E820
call load_kernel
call enable_A20
; call graphics_mode ; Uncomment if you want to switch to graphics mode 0x13
lgdt [gdtr]
mov eax, cr0
or al, 1
mov cr0, eax
jmp CODE_SEG:init_pm
graphics_mode:
mov ax, 0013h
int 10h
ret
load_kernel:
; load DH sectors to ES:BX from drive DL
push dx ; Store DX on stack so later we can recall
; how many sectors were request to be read ,
; even if it is altered in the meantime
mov ah , 0x02 ; BIOS read sector function
mov al , dh ; Read DH sectors
mov ch , 0x00 ; Select cylinder 0
mov dh , 0x00 ; Select head 0
mov cl , 0x02 ; Start reading from second sector ( i.e.
; after the boot sector )
int 0x13 ; BIOS interrupt
jc disk_error ; Jump if error ( i.e. carry flag set )
pop dx ; Restore DX from the stack
cmp dh , al ; if AL ( sectors read ) != DH ( sectors expected )
jne disk_error ; display error message
ret
disk_error:
mov bx , ERROR_MSG
call print_string
hlt
; prints a null - terminated string pointed to by EDX
print_string:
pusha
push es ;Save ES on stack and restore when we finish
push VIDEO_MEMORY_SEG ;Video mem segment 0xb800
pop es
xor di, di ;Video mem offset (start at 0)
print_string_loop:
mov al , [ bx ] ; Store the char at BX in AL
mov ah , WHITE_ON_BLACK ; Store the attributes in AH
cmp al , 0 ; if (al == 0) , at end of string , so
je print_string_done ; jump to done
mov word [es:di], ax ; Store char and attributes at current
; character cell.
add bx , 1 ; Increment BX to the next char in string.
add di , 2 ; Move to next character cell in vid mem.
jmp print_string_loop ; loop around to print the next char.
print_string_done:
pop es ;Restore ES that was saved on entry
popa
ret ; Return from the function
%include "BOOT/a20.inc"
%include "BOOT/gdt.inc"
%include "BOOT/detect_mem.inc"
[bits 32]
init_pm:
mov ax, DATA_SEG
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ebp, 0x90000
mov esp, ebp
call 0x9000
cli
loopend: ;Infinite loop when finished
jmp loopend
[bits 16]
; Variables
ERROR db "A20 Error!" , 0
ERROR_MSG db "Error!" , 0
BOOT_DRIVE: db 0
VIDEO_MEMORY_SEG equ 0xb800
WHITE_ON_BLACK equ 0x0f
times 510-($-$$) db 0
db 0x55
db 0xAA
我的功能是检测内存detect_mem.inc:
entries equ 0
buffer equ 1
do_E820:
pushf
pusha
mov edi , buffer ;destination buffer
mov byte [edi+20] , 1 ;Force a vaalid ACPI
.begin:
xor ebx , ebx
mov edx , 0x534D4150
mov eax , 0xE820
mov ecx , 24
int 0x15
jc .failed
mov edx , 0x534D4150
cmp eax,edx ;voir si eax est different de 0x534D4150
jne .failed
test ebx , ebx ;EBX will be set to some non-zero value
je .failed
jmp .verify
.do_E820_loop:
mov eax, 0xe820 ; eax, ecx get trashed on every int 0x15 call
mov [es:di+20], dword 1 ; force a valid ACPI 3.X entry
mov ecx, 24 ; ask for 24 bytes again
int 0x15
jc .e820_failed
.verify:
jcxz .skip_entries ;Length of "region" (if this value is 0, ignore the entry)
cmp cl , 20
jbe .extension
cmp byte [es:di+20] , 0
je .skip_entries
.extension: ;go to the next buffer if cl equal 24
push eax
mov eax , dword [entries]
inc eax
mov dword [entries] , eax
pop eax
mov ecx , [es:di+8]
jcxz .skip_entries
add di ,24
.skip_entries:
test ebx , ebx ;if ebx resets to 0, list is complete
jne .do_E820_loop
.e820_failed:
clc
popa
popf
ret
.failed:
stc
popa
popf
ret
请,你能给我一些建议吗?如果我犯了别人的错误,也请给我一些建议
我想我已经为加载内核准备好了DL和BX,然后通过调用do_e820将它们销毁了
不,您没有-do_e820中的pusha和popa将保存并恢复dh和dl组合的dx
还有许多其他问题。例如:
1在cli启动后,让中断处于禁用/延迟状态没有任何意义;而且不仅仅是因为BIOS功能只会启用IRQ,特别是处理磁盘,这可能需要IRQ来指示传输已完成
2 do_e820代码使用进位标志返回成功/失败;但它设置或清除进位标志clc或stc,并从堆栈popf中弹出调用者的标志,覆盖进位标志以返回成功/失败
3在do_e820循环中没有缓冲区完全保护,因此至少在理论上,如果内存映射中有大量条目,那么di可能会卷起并丢弃所有内容
4 do_e820中的jcxz.skip_项是错误的,它只检查该项64位大小字段的32位,因此如果BIOS想说某个地方有4 GiB的可用RAM的倍数,您会错误地忽略它,并且在增加条目的数量后会发生错位,因此,您可以增加条目的数量,然后忽略/跳过该条目,最终在[entries]中得到错误的值。注意,您可以使用inc dword[entries]使其更干净,而无需保存/恢复eax
5 hlt仅在中断发生之前停止CPU。这包括暂停,直到发生非屏蔽中断/NMI,其中使用cli禁用/延迟IRQ,这是错误的/愚蠢的,不会阻止NMI;允许CPU在hlt之后继续执行代码。出于这个原因,您应该始终使用类似.die:hlt然后是jmp.die的循环;但在这种情况下,您没有理由禁用IRQ。请注意,对于真正的计算机来说,在引导加载程序决定永久停止后按control+alt+delete非常方便,但在禁用IRQ时将无法工作
6对于软盘,你不应该在第一次出错后就放弃,因为软盘是相对不可靠的。标准做法是至少重试3次,并在重试之间重置软盘
还要注意的是,只要稍微重新安排一下,代码就更容易阅读,也不容易出错。具体来说,您可以先调用do_e820,然后加载调用load_内核所需的参数,例如mov dh,17,mov bx,0x9000。在这种情况下,您还可以使用mov dl、[BOOT_DRIVE]重新加载dl,并从do_e820中删除pusha和popa以及pushf和popf
我想我已经为加载内核准备好了DL和BX,然后通过调用do_e820将它们销毁了
不,您没有-do_e820中的pusha和popa将保存并恢复dh和dl组合的dx
还有许多其他问题。例如:
1在cli启动后,让中断处于禁用/延迟状态没有任何意义;而且不仅仅是因为BIOS功能只会启用IRQ,特别是处理磁盘,这可能需要IRQ来指示传输已完成
2 do_e820代码使用进位标志返回成功/失败;但它设置或清除进位标志clc或stc,并从堆栈popf中弹出调用者的标志,覆盖进位标志以返回成功/失败
3在do_e820循环中没有缓冲区完全保护,因此至少在理论上,如果内存映射中有大量条目,那么di可能会卷起并丢弃所有内容
4 do_e820中的jcxz.skip_项是错误的,它只检查该项64位大小字段的32位,因此如果BIOS想说某个地方有4 GiB的可用RAM的倍数,您会错误地忽略它,并且在增加条目的数量后会发生错位,因此,您可以增加条目的数量,然后忽略/跳过该条目,最终在[entries]中得到错误的值。注意,您可以使用inc dword[entries]使其更干净,而无需保存/恢复eax
5 hlt仅在中断发生之前停止CPU。这包括暂停,直到发生非屏蔽中断/NMI,其中使用cli禁用/延迟IRQ,这是错误的/愚蠢的,不会阻止NMI;允许CPU在hlt之后继续执行代码。出于这个原因,您应该始终使用类似.die:hlt然后是jmp.die的循环;但是那样的话你
没有理由禁用IRQ。请注意,对于真正的计算机来说,在引导加载程序决定永久停止后按control+alt+delete非常方便,但在禁用IRQ时将无法工作
6对于软盘,你不应该在第一次出错后就放弃,因为软盘是相对不可靠的。标准做法是至少重试3次,并在重试之间重置软盘
还要注意的是,只要稍微重新安排一下,代码就更容易阅读,也不容易出错。具体来说,您可以先调用do_e820,然后加载调用load_内核所需的参数,例如mov dh,17,mov bx,0x9000。在这种情况下,您还可以使用mov dl、[BOOT_DRIVE]重新加载dl,并从do_e820中删除pusha和popa以及pushf和popf。汇编中edx寄存器的特殊性是什么???@ledoux:我不太明白。edx就像任何其他通用寄存器eax、ebx。。除了一些使用暗示mul、div、cwd的说明之外。所有32位通用寄存器的最低16位ax、bx、…、最低8位al、bl、。。。和他们中间的8位啊,bh。。。;主要是因为历史原因,比如说,在英特尔将其扩展到32位之前,你就已经有了这个功能。好的,我已经准备好了。但是我收到了这个错误BOOT/bootloader.asm:109:error:TIMES值-38是负数,当我包含另一个时,我想我的BOOT没有第109行超过512字节,是真的吗??我如何修复这个错误?@ledoux:是的,这个错误意味着引导加载程序太大了38字节,无法安装。要修复它,您必须减小代码的大小;或者找到更节省空间的方法来做同样的事情,或者将代码从引导扇区转移到其他地方。我做的一个技巧是有多个扇区,其中第一个扇区包含要打印到屏幕的代码,第二个扇区包含要加载的代码,然后这两个扇区中的代码加载引导加载程序的其余扇区。汇编中edx寄存器的特殊性是什么???@ledoux:我不确定是否理解。edx就像任何其他通用寄存器eax、ebx。。除了一些使用暗示mul、div、cwd的说明之外。所有32位通用寄存器的最低16位ax、bx、…、最低8位al、bl、。。。和他们中间的8位啊,bh。。。;主要是因为历史原因,比如说,在英特尔将其扩展到32位之前,你就已经有了这个功能。好的,我已经准备好了。但是我收到了这个错误BOOT/bootloader.asm:109:error:TIMES值-38是负数,当我包含另一个时,我想我的BOOT没有第109行超过512字节,是真的吗??我如何修复这个错误?@ledoux:是的,这个错误意味着引导加载程序太大了38字节,无法安装。要修复它,您必须减小代码的大小;或者找到更节省空间的方法来做同样的事情,或者将代码从引导扇区转移到其他地方。我做的一个技巧是有多个扇区,其中第一个扇区包含要打印到屏幕的代码和要加载第二个扇区的代码,然后这两个扇区中的代码加载引导加载程序的其余扇区。