обнаружить память перед загрузкой ядра в загрузчик - PullRequest
1 голос
/ 15 февраля 2020

Я пытаюсь обнаружить память с помощью eax=0xe820 int 15h перед загрузкой ядра в мой загрузчик. Итак, я провел некоторое исследование и увидел, что проблема в EDX registers. Я думаю, что я подготовил 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
    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.in c):

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

Пожалуйста, вы можете мне что-то посоветовать, если я сделал чужие ошибки, пожалуйста, посоветуйте мне тоже ,

1 Ответ

4 голосов
/ 16 февраля 2020

Я думаю, что я подготовил DL и BX для загрузки ядра, а затем уничтожил их, вызвав do_e820.

Нет, у вас нет - pusha и popa в do_e820 сохранит и восстановит dx (что вместе составляет dh и dl).

Существует множество других проблем. Например:

1) Нет смысла оставлять прерывания отключенными / отложенными (после cli в начале); и не только потому, что функции B IOS будут в любом случае просто включать IRQ (особенно для обработки диска, что может потребовать IRQ для указания того, что передача завершена).

2) Код do_e820 использует перенос флаг для возврата успеха / неудачи; но он устанавливает или очищает флаг переноса (clc или stc) и извлекает флаги вызывающего из стека (popf), который перезаписывает «флаг переноса для возврата успеха / сбоя».

3) На do_e820 l oop нет защиты "буфер заполнен", поэтому (по крайней мере, в теории), если в карте памяти очень большое количество записей, тогда di может обернуться и перевести sh все.

4) jcxz .skip_entriesdo_e820) неверно (он проверяет только 32 бита 64-битного поля «размер» записи, поэтому, если B IOS хочет сказать, что есть кратное 4 ГБ используемой оперативной памяти где-то, что вы неправильно проигнорируете) и неуместно (происходит после увеличения числа записей, так что вы можете увеличивать количество записей, а затем игнорировать / пропускать запись и в итоге получить неправильную значение в [entries]). Обратите внимание, что вы можете сделать inc dword [entries], чтобы сделать его чище (нет необходимости сохранять / восстанавливать eax).

5) hlt только останавливает процессор, пока не произойдет прерывание. Это включает «остановку до тех пор, пока не произойдет прерывание без маскирования / NMI», где отключение / откладывание IRQ (с cli, что неправильно / глупо) не помешает NMI; что позволяет процессору продолжать выполнение кода после hlt. По этой причине вы всегда должны использовать al oop (например, «.die: hlt затем jmp .die); но в этом случае у вас нет причин отключать IRQ. Обратите внимание, что (для реальных компьютеров) можно нажать« control + alt ». + delete "после того, как загрузчик решил остановить навсегда, очень удобно (но не будет работать, если IRQ отключены).

6) Для дискет, вы не должны сдаваться после первой ошибки (потому что дискеты относительно ненадежны.) Стандартная практика состоит в том, чтобы сделать по крайней мере 3 попытки и сбросить дискету между (некоторыми) попытками.

Также обратите внимание, что с небольшой перестановкой код может быть легче читать и меньше ошибок. В частности, вы можете сначала вызвать do_e820, а затем загрузить параметры, необходимые для call load_kernel после (например, mov dh, 17, mov bx, 0x9000). В этом случае вы также можете перезагрузить dlmov dl,[BOOT_DRIVE] ) и удалите pusha и popapushf и popf) из do_e820.

...