【问题标题】:detect memory before load kernel in bootloader在引导加载程序中加载内核之前检测内存
【发布时间】:2020-05-31 04:44:05
【问题描述】:

在我的引导加载程序中加载内核之前,我尝试使用eax=0xe820 int 15h 检测内存。 所以我做了一些调查,我发现问题出在 EDX registers 上。 我认为我已经准备好 DLBX 来加载内核,然后我通过调用 do_e820 将它们丢弃。

所以有我的引导加载程序文件(bootloader.asm):

[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

拜托,你能给我一些建议吗,如果我犯了其他错误,请也给我建议。

【问题讨论】:

    标签: x86 osdev


    【解决方案1】:

    我认为我已经为加载内核准备了 DL 和 BX,然后我通过调用 do_e820 将它们丢弃了。

    不,你没有 - do_e820 中的 pushapopa 将保存和恢复 dx(这是 dhdl 的组合)。

    还有许多其他问题。例如:

    1) 禁用/推迟中断没有任何意义(在开始时cli 之后);而不仅仅是因为 BIOS 功能无论如何都会启用 IRQ(尤其是处理磁盘,这可能需要 IRQ 来指示传输已完成)。

    2) do_e820 代码使用进位标志返回成功/失败;但它会设置或清除进位标志(clcstc)并从堆栈中弹出调用者的标志(popf),从而覆盖“返回成功/失败的进位标志”。

    3) do_e820 循环上没有“缓冲区已满”保护,因此(至少在理论上)如果内存映射中有大量条目,那么 di 可以环绕并丢弃所有内容。

    4) jcxz .skip_entries (in do_e820) 是错误的(它只检查条目的 64 位“大小”字段的 32 位,所以如果 BIOS 想说某处有 4 GiB 的可用 RAM 的倍数您将错误地忽略它)并且放错了位置(在您增加条目数之后发生,因此您可以增加条目数然后忽略/跳过该条目,并以[entries] 中的错误值结束)。请注意,您可以使用inc dword [entries] 使其更干净(无需保存/恢复eax)。

    5) hlt 只会暂停 CPU,直到发生中断。这包括“暂停直到发生非屏蔽中断/NMI”,其中禁用/推迟 IRQ(使用cli,这是错误的/愚蠢的)不会阻止 NMI;允许 CPU 在hlt 之后继续执行代码。出于这个原因,您应该始终使用循环(例如“.die: hlt 然后jmp .die”);但在这种情况下,您没有理由禁用 IRQ。请注意(对于真实计算机)能够按“control+alt+delete” " 在引导加载程序决定永远停止之后非常方便(但在禁用 IRQ 时将不起作用)。

    6) 对于软盘,你不应该在第一次出错后就放弃(因为软盘相对不可靠)。标准做法是至少重试 3 次,并在(一些)重试之间重置软盘。

    另外请注意,稍作重新安排,代码可能更易于阅读且不易出错。具体来说,您可以先调用do_e820,然后再加载call load_kernel 所需的参数(例如mov dh, 17mov bx, 0x9000)。在这种情况下,您还可以重新加载dl(带有mov dl,[BOOT_DRIVE])并从do_e820 中删除pushapopa(以及pushfpopf)。

    【讨论】:

    • 汇编中edx寄存器的特殊性是什么??
    • @ledoux:我不确定我是否理解。 edx 与任何其他通用寄存器(eaxebx、..)一样,除了一些隐含使用的指令(muldivcwd)。所有 32 位通用寄存器都为其最低 16 位(axbx、...)、最低 8 位(albl、...)命名以及它们的“中间 8 位”(ahbh,...);主要是出于历史原因(例如,因为这是英特尔将其扩展到 32 位之前所拥有的)。
    • 好的,我已经安排好了。但是我收到了这个错误BOOT/bootloader.asm:109: error: TIMES value -38 is negative,当我包含另一个时,我认为没有第 109 行的引导超过 512 个字节,这是真的吗?我该如何解决这个错误?
    • @ledoux:是的,这个错误意味着引导加载程序有 38 个字节太大而无法容纳。要修复它,您必须减小代码的大小;要么找到更节省空间的方法来做同样的事情,要么将代码从引导扇区转移到其他地方。我做的一个技巧是有多个扇区,其中第一个扇区包含要打印到屏幕的代码和加载第二个扇区的代码,然后这两个扇区中的代码会加载引导加载程序的剩余扇区。
    猜你喜欢
    • 1970-01-01
    • 2020-09-10
    • 2021-10-12
    • 1970-01-01
    • 2015-06-02
    • 1970-01-01
    • 2011-02-01
    • 2016-11-24
    • 1970-01-01
    相关资源
    最近更新 更多