ledoux
ledoux

Reputation: 91

detect memory before load kernel in bootloader

i try to detect memory with eax=0xe820 int 15h before load kernel in my bootloader. So i have done some investigation and i have seen that the problem is the EDX registers . I think that i have prepared DL and BX for loading kernel and then i have trashed them by calling do_e820 .

So there is my bootloader file (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

and there is my function to detect memory (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

Please , can you advice me something , if i have done anothers mistakes , please advice me too.

Upvotes: 1

Views: 244

Answers (1)

Brendan
Brendan

Reputation: 37214

I think that i have prepared DL and BX for loading kernel and then i have trashed them by calling do_e820 .

No, you haven't - the pusha and popa in do_e820 will save and restore dx (which is dh and dl combined).

There are multiple other problems. For examples:

1) It doesn't make any sense to leave interrupts disabled/postponed (after the cli at the start); and not just because BIOS functions will just enable IRQs anyway (especially to handle disk, which may require an IRQ to indicate that a transfer completed).

2) The do_e820 code is using the carry flag to return success/failure; but it sets or clears the carry flag (clc or stc) and the pops the caller's flags from the stack (popf) which overwrites the "carry flag to return success/failure".

3) There's no "buffer full" guard on the do_e820 loop, so (at least in theory) if there's a very large number of entries in the memory map then di could wrap around and trash everything.

4) the jcxz .skip_entries (in do_e820) is wrong (it's only checking 32 bits of the entry's 64 bit "size" field, so if the BIOS wants to say there's a multiple of 4 GiB of usable RAM somewhere you'll incorrectly ignore that) and is misplaced (happens after you increment the number of entries, so you could increment the number of entries and then ignore/skip the entry, and end up with the wrong value in [entries]). Note that you could do inc dword [entries] to make it cleaner (no need to save/restore eax).

5) hlt only halts the CPU until an interrupt occurs. This includes "halt until non-masking interrupt/NMI occurs" where disabling/postponing IRQs (with cli, which is wrong/silly) will not prevent the NMI; allowing the CPU to continue executing code after the hlt. For this reason you should always use a loop (like ".die: hlt then jmp .die); but in that case you have no reason to disable IRQs. Note that (for real computers) being able to press "control+alt+delete" after a boot loader decided to halt forever is very convenient (but won't work when IRQs are disabled).

6) For floppy disks, you shouldn't give up after the first error (because floppies are relatively unreliable). Standard practice is to have at least 3 retries, and to reset the floppy between (some) retries.

Also note that with a little re-arrangement the code could be easier to read and less error prone. Specifically, you could call do_e820 first, then load parameters needed for call load_kernel after (e.g. mov dh, 17, mov bx, 0x9000). In this case you could also reload dl (with a mov dl,[BOOT_DRIVE]) and delete the pusha and popa (and pushf and popf) from do_e820.

Upvotes: 4

Related Questions