Page 1 of 1
Help with Higher Half Bootloader
Posted: Wed Aug 06, 2008 10:57 am
by enigma
Okay, I tried using the GRUB/GDT trick, but GRUB wouldn't load my OS image no matter what I tried. So instead, I tried creating a bootloader that could load the kernel, enable A20 and Pmode, and set up paging to map the kernel at 0xC0010000. The problem is that the bootloader loads the kernel, and does everything else fine, but then it just hangs.
Code: Select all
[BITS 16]
[ORG 0x7C00]
reset_drive:
mov ah, 0 ; RESET-command
int 13h ; Call interrupt 13h
or ah, ah ; Check for error code
jnz reset_drive ; Try again if ah != 0
mov ax, 0x1000
mov es, ax
mov bx, 0x0 ; Destination address = 0x10000
mov ah, 02h ; READ SECTOR-command
mov al, 36 ; Number of sectors to read = 36
mov ch, 0 ; Cylinder = 0
mov cl, 02h ; Sector = 2
mov dh, 0 ; Head = 0
int 13h ; Call interrupt 13h
or ah, ah ; Check for error code
jnz reset_drive ; Try again if ah != 0
cli ; Disable interrupts, we want to be alone
xor ax, ax
mov ds, ax ; Set DS-register to 0 - used by lgdt
lgdt [gdt_desc]
call enable_A20
mov ax, 0x9C00
mov es, ax
xor di, di
mov ax, 0
mov bx, 3
init_1st_page:
mov cx, 0x100
stosw
xchg bx, ax
stosw
add bx, 0x1000
xchg ax, bx
loop init_1st_page
mov cx, 0x200
xor ax, ax
rep stosd
init_2nd_page:
mov cx, 0x10
rep stosd
mov cx, 0x3F0
mov eax, 0x10000
mov ebx, 3
stosw
xchg ebx, eax
stosw
xchg eax, ebx
add eax, 0x1000
loop init_2nd_page
mov eax, 0x9C003
stosd
mov ax, 767
xchg ax, cx
rep stosd
mov eax, 0x9D003
stosd
mov eax, 0x9E000
mov cr3, eax
mov eax, cr0
or eax, 0x80000001 ; enables paging and Pmode
mov cr0, eax
jmp 08h:clear_pipe ; Jump to code segment, offset clear_pipe
enable_A20:
in al, 0x64
test al, 2
jnz enable_A20
mov al, 0xD1
out 0x64, al
.6:
in al, 0x64
and ax, byte 2
jnz .6
mov al, 0xDF
out 0x60, al
[BITS 32]
clear_pipe:
mov eax, 0x10
mov ds, eax
mov es, eax
mov fs, eax
mov gs, eax
mov ss, eax
jmp 8:0x10000
gdt: ; Address for the GDT
gdt_null: ; Null Segment
dd 0
dd 0
gdt_code: ; Code segment, read/execute, nonconforming
dw 0FFFFh
dw 0
db 0
db 10011010b
db 11001111b
db 0
gdt_data: ; Data segment, read/write, expand down
dw 0FFFFh
dw 0
db 0
db 10010010b
db 11001111b
db 0
gdt_end: ; Used to calculate the size of the GDT
gdt_desc: ; The GDT descriptor
dw gdt_end - gdt - 1 ; Limit (size)
dd gdt ; Address of the GDT
times 510-($-$$) db 0
dw 0AA55h ; Boot sector identifyer
Re: Help with Higher Half Bootloader
Posted: Wed Aug 06, 2008 12:01 pm
by codemastersnake
Have you tried your kernel in other emulators or on a real PC?
Re: Help with Higher Half Bootloader
Posted: Wed Aug 06, 2008 3:06 pm
by enigma
no, but i figure that i should try to make it work successfully on the emulator i have (qemu 0.9.0) before tackling other emulators
Re: Help with Higher Half Bootloader
Posted: Thu Aug 07, 2008 1:48 am
by AJ
Hi,
Whan your bootloader just hangs, where is EIP? Where is ESP? Are they where you would expect? What is the instruction that it is hanging on (in other words, do you have a JMP $ anywhere - sorry to be patronising but I've done this myself before
)
I'm not familiar with Qemu, just Bochs, but if Qemu gives you a Bochs-style final register dump, could you post that register dump here, please?
Cheers,
Adam
Re: Help with Higher Half Bootloader
Posted: Thu Aug 07, 2008 1:26 pm
by xyzzy
AJ wrote:Hi,
Whan your bootloader just hangs, where is EIP? Where is ESP? Are they where you would expect? What is the instruction that it is hanging on (in other words, do you have a JMP $ anywhere - sorry to be patronising but I've done this myself before
)
I'm not familiar with Qemu, just Bochs, but if Qemu gives you a Bochs-style final register dump, could you post that register dump here, please?
Cheers,
Adam
Its not as easy to get a register dump from QEMU. Only way I know of while its running is to switch into the monitor (Ctrl+Alt+2) and type "info registers", but AFAIK you can't copy and paste that.
Re: Help with Higher Half Bootloader
Posted: Thu Aug 07, 2008 10:10 pm
by enigma
AlexExtreme wrote:AJ wrote:Hi,
Whan your bootloader just hangs, where is EIP? Where is ESP? Are they where you would expect? What is the instruction that it is hanging on (in other words, do you have a JMP $ anywhere - sorry to be patronising but I've done this myself before
)
I'm not familiar with Qemu, just Bochs, but if Qemu gives you a Bochs-style final register dump, could you post that register dump here, please?
Cheers,
Adam
Its not as easy to get a register dump from QEMU. Only way I know of while its running is to switch into the monitor (Ctrl+Alt+2) and type "info registers", but AFAIK you can't copy and paste that.
Thanks for that, I have a screenshot of the register dump now.
Based on the values, it seems that the loader enables the A20, but the values for cr0 seem wrong as does EIP.
Now I'm even more confused as there doesn't seem to be any sort of jump instruction to 0x3c, and protected mode and paging should be initialized.
Linker Map:
Code: Select all
Allocating common symbols
Common symbol size file
idtp 0x6 idt.o
lower_mem 0x400 mm.o
idt 0x800 idt.o
upper_mem 0x1fc00 mm.o
Memory Configuration
Name Origin Length Attributes
*default* 0x00000000 0xffffffff
Linker script and memory map
0xc0010000 . = 0xc0010000
.text 0xc0010000 0x155a load address 0x00010000
*(.text)
.text 0xc0010000 0x27b start.o
0xc001004c isr4
0xc0010147 isr27
0xc001009e isr13
0xc0010227 irq12
0xc0010115 isr22
0xc00100cf isr15
0xc0010213 irq10
0xc001023b irq14
0xc001007c isr9
0xc00101ff irq8
0xc001011f isr23
0xc001015b isr29
0xc001016f isr31
0xc001010b isr21
0xc001021d irq11
0xc0010151 isr28
0xc0010074 isr8
0xc00101e1 irq5
0xc0010101 isr20
0xc00100a6 isr14
0xc0010056 isr5
0xc00101eb irq6
0xc00101b9 irq1
0xc001002e isr1
0xc0010209 irq9
0xc001013d isr26
0xc001008e isr11
0xc0010231 irq13
0xc0010096 isr12
0xc001001c idt_load
0xc0010024 isr0
0xc00101c3 irq2
0xc0010129 isr24
0xc00100d9 isr16
0xc0010042 isr3
0xc0010060 isr6
0xc00101f5 irq7
0xc00100ed isr18
0xc00101af irq0
0xc0010086 isr10
0xc0010000 start
0xc00100e3 isr17
0xc0010038 isr2
0xc00100f7 isr19
0xc0010165 isr30
0xc0010245 irq15
0xc001006a isr7
0xc0010133 isr25
0xc00101d7 irq4
0xc00101cd irq3
*fill* 0xc001027b 0x1 00
.text 0xc001027c 0x66f kernel.o
0xc00106b9 printf
0xc0010283 sys_halt
0xc0010625 inportb
0xc00104c2 putch
0xc0010280 debug_int
0xc0010645 move_csr
0xc0010604 outportb
0xc0010381 out_ok
0xc00102b1 k_main
0xc001028a panic
0xc0010405 scroll
0xc001027c enable_ints
0xc001061a outportw
0xc001027e disable_ints
0xc0010286 halt
0xc0010288 nop
0xc00103b0 clear_screen
*fill* 0xc00108eb 0x5 00
.text 0xc00108f0 0x1e paging.o
0xc00108ff read_cr3
0xc00108f4 write_cr0
0xc0010903 write_cr3
0xc00108f0 read_cr0
*fill* 0xc001090e 0x2 00
.text 0xc0010910 0xce memory.o
0xc0010980 memsetw
0xc0010910 memcpy
0xc001094c memset
0xc00109b7 strlen
*fill* 0xc00109de 0x2 00
.text 0xc00109e0 0x178 mm.o
0xc0010a12 upper_alloc
0xc0010a44 init_paging
0xc0010aff mem_init
0xc0010a27 upper_free
0xc00109e0 lower_alloc
0xc00109f5 lower_free
.text 0xc0010b58 0xbc idt.o
0xc0010bdd idt_install
0xc0010b58 idt_set_gate
.text 0xc0010c14 0x3ba isrs.o
0xc0010efb fault_handler
0xc0010c14 isrs_install
*fill* 0xc0010fce 0x2 00
.text 0xc0010fd0 0x2a8 irq.o
0xc0010fe0 irq_uninstall_handler
0xc001109c irq_install
0xc0010fd0 irq_install_handler
0xc0011218 irq_handler
0xc0010ff0 irq_remap
.text 0xc0011278 0xa7 pit.o
0xc00112d1 timer_handler
0xc00112e6 sleep
0xc0011278 timer_phase
0xc00112f9 timer_install
*fill* 0xc001131f 0x1 00
.text 0xc0011320 0x23a kb.o
0xc0011541 keyboard_install
0xc0011320 keyboard_handler
.data 0xc0012000 0x59e load address 0x00012000
*(.data)
.data 0xc0012000 0x26 start.o
*fill* 0xc0012026 0x2 00
.data 0xc0012028 0xc kernel.o
0xc0012030 page_table
0xc001202c attrib
0xc0012028 textmemptr
.data 0xc0012034 0x0 memory.o
.data 0xc0012034 0x18 mm.o
0xc0012034 top_lower_stack
0xc0012044 page_table_k_start
0xc0012048 page_table_page_directory
0xc0012038 top_upper_stack
0xc0012040 page_table0
0xc001203c page_directory
.data 0xc001204c 0x0 idt.o
*fill* 0xc001204c 0x14 00
.data 0xc0012060 0x7c isrs.o
0xc0012060 exception_messages
.data 0xc00120dc 0x0 irq.o
.data 0xc00120dc 0x0 pit.o
*fill* 0xc00120dc 0x4 00
.data 0xc00120e0 0x200 kb.o
0xc0012160 shift_kbdus
0xc00120e0 kbdus
0xc00121e0 caps_kbdus
0xc0012260 shift_caps_kbdus
*(.rodata*)
.rodata 0xc00122e0 0xf0 kernel.o
.rodata 0xc00123d0 0x1ce isrs.o
.bss 0xc0013000 0x24886 load address 0x00013000
*(COMMON*)
COMMON 0xc0013000 0x20000 mm.o
0xc0013000 lower_mem
0xc0013400 upper_mem
COMMON 0xc0033000 0x820 idt.o
0xc0033000 idtp
0xc0033020 idt
*(.bss*)
.bss 0xc0033820 0x4000 start.o
.bss 0xc0037820 0x8 kernel.o
0xc0037820 csr_x
0xc0037824 csr_y
.bss 0xc0037828 0x0 memory.o
.bss 0xc0037828 0x0 mm.o
.bss 0xc0037828 0x0 idt.o
.bss 0xc0037828 0x0 isrs.o
*fill* 0xc0037828 0x18 00
.bss 0xc0037840 0x40 irq.o
0xc0037840 irq_routines
.bss 0xc0037880 0x4 pit.o
0xc0037880 count_down
.bss 0xc0037884 0x2 kb.o
0xc0037885 temp_char
0xc0037884 keystate
.rel.dyn 0xc0037888 0x0 load address 0x00037888
.rel.text 0x00000000 0x0 start.o
LOAD start.o
LOAD kernel.o
LOAD paging.o
LOAD memory.o
LOAD mm.o
LOAD idt.o
LOAD isrs.o
LOAD irq.o
LOAD pit.o
LOAD kb.o
OUTPUT(a.out elf32-i386)
Re: Help with Higher Half Bootloader
Posted: Fri Aug 08, 2008 3:39 am
by AJ
Hmmm...I think you need to step through your binary instruction-by-instruction to sort this one out. Again, if you can't do this in qemu, try Bochs (the user WindowsNT has made an excelent frontend for the Windows Bochs debugger).
Just one thing - could you start your boot loader with a far jump to reset_drive in order to ensure you have a valid CS:IP?
If all else fails, write a JMP $ at the start of your code and see if Qemu gives you the expected CS:IP result on termination. If so, move that JMP $ down through your code until you hit the problem point. Tedious, but it works!
Cheers,
Adam
Re: Help with Higher Half Bootloader
Posted: Fri Aug 08, 2008 12:14 pm
by Brynet-Inc
AlexExtreme wrote:Its not as easy to get a register dump from QEMU. Only way I know of while its running is to switch into the monitor (Ctrl+Alt+2) and type "info registers", but AFAIK you can't copy and paste that.
It most definitely is easy to get a register dump.. -monitor stdio.
Re: Help with Higher Half Bootloader
Posted: Sat Aug 09, 2008 12:19 am
by xyzzy
Brynet-Inc wrote:AlexExtreme wrote:Its not as easy to get a register dump from QEMU. Only way I know of while its running is to switch into the monitor (Ctrl+Alt+2) and type "info registers", but AFAIK you can't copy and paste that.
It most definitely is easy to get a register dump.. -monitor stdio.
Well you learn something new every day, never knew about that! Thanks!
Re: Help with Higher Half Bootloader
Posted: Sun Aug 10, 2008 12:39 am
by enigma
Alright, I've tried using bochs to emulate my bootloader/OS, but I can't even get it to boot properly. I'm wondering if this may be due to the way I create a disk image, by just putting the bootloader and kernel together back to back without padding it to a proper floppy size. Qemu tackled this just fine, but I haven't used bochs at all really. If anyone could help out with this, or knows how to implement breakpoints in qemu, I'd be much obliged.
Re: Help with Higher Half Bootloader
Posted: Sun Aug 10, 2008 2:02 am
by enigma
AJ wrote:Hmmm...I think you need to step through your binary instruction-by-instruction to sort this one out. Again, if you can't do this in qemu, try Bochs (the user WindowsNT has made an excelent frontend for the Windows Bochs debugger).
Just one thing - could you start your boot loader with a far jump to reset_drive in order to ensure you have a valid CS:IP?
If all else fails, write a JMP $ at the start of your code and see if Qemu gives you the expected CS:IP result on termination. If so, move that JMP $ down through your code until you hit the problem point. Tedious, but it works!
Cheers,
Adam
Thanks so much for telling me about that. I got bochs working, but if not for that debugger, I never would have seen that the A20 enabling code was missing it's "ret"
Now to work out the tedious little logic errors in the pages.
Re: Help with Higher Half Bootloader
Posted: Sat Sep 13, 2008 4:17 am
by ivannz
Hello,
Maybe this topic is a bit outdated, but here is where your problems stems from, besides missing ret:
you use 'loop' instruction, which uses cx/ecx as a counter, but inside your loop you Reinitialize cx/ecx counter each iteration.
So each time counter is reinitialized and the cycle goes on and on forever.
Here is what you have got in your code:
this part, as far as i see, is responsible for filling 2 page tables id mapping 1st Mb and mapping first 4Mb excpet for first 64Kb.
Code: Select all
mov ax, 0x9C00
mov es, ax
xor di, di
mov ax, 0
mov bx, 3
init_1st_page:
mov cx, 0x100 ; this is the problem
stosw
xchg bx, ax
stosw
add bx, 0x1000
xchg ax, bx
loop init_1st_page
mov cx, 0x200
xor ax, ax
rep stosd
init_2nd_page:
mov cx, 0x10
rep stosd
mov cx, 0x3F0 ; this is the problem
mov eax, 0x10000
mov ebx, 3
stosw
xchg ebx, eax
stosw
xchg eax, ebx
add eax, 0x1000
loop init_2nd_page
After some analysis I might suggest this solution, but note that this solution is not the best and the most size eficient and somehow else optimal:
Code: Select all
mov ax, 0x9C00
mov es, ax
xor di, di
xor dx, dx
mov ax, 3 ; dx:ax = page table entry. AX bits 0-11 are flags!!
mov cx, 0x100 ; ID map first Mb
call fill_pt
xor ax, ax
mov cx, (0xC00)*2 ; unmap everything else
rep stosw
mov cx, (0x10)*2 ; unmap first 64Kb
rep stosw
mov dx, 1
mov ax, 3 ; dx:ax = 00010003
mov cx, 0x3F0 ; map the rest of first 4 Mb
call fill_pt
;; put this function somewhere at the end of your code
;; In: dx:ax - base address with page table entry flags
;; es:di - page table (4Kb aligned)
;; cx - count of entires to fill
fill_pt:
stosw ; store low word
xchg dx, ax
stosw ; store high word
xchg ax, dx
add ax, 0x1000
adc dx, 0
loop fill_pt
ret