Reputation: 91
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
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