Okay, I changed my jump code to this:
;jmp dword CODE_SEL:main32
db 67h, 0eah
dd main32
dw CODE_SEL
In Bochs, it gives the error "jump protected: cs == 0". It gives the same error without the 67h, but it triple-faults with the 66h. The only thing I can see could be wrong is the GDT or GDT init code... Plz help!
Jump changed... still not working
RE:Jump changed... still not working
Hmmm, I played around with the code, and changed the "DD MAIN32" to "DW MAIN32", and got it working! Yay!
RE:Jump changed... still not working
Uhm... that's strange. If you're entering pmode correctly, the jump should, in fact, use a 32-bit (dd) offset... it's pmode, afterall, a far pointer is 48-bits (16-bit segment, 32-bit offset)
For example:
db 0x66 ; jump to the kernel
db 0xea
dd 0x00010000
dw 0x10
will jump to 10:0x00010000 (boot stub->kernel)
Jeff
For example:
db 0x66 ; jump to the kernel
db 0xea
dd 0x00010000
dw 0x10
will jump to 10:0x00010000 (boot stub->kernel)
Jeff
Kernel code
Okay, right now I have a bootloader that loads this kernel, then this kernel changes to pmode. But it's crashing on the jump, because if I put a JMP $ right before it, it hangs. The bootloader loads the code at 0x1000:0x0000 (0x10000 linear).
... I wish the x86 came with NEW and DELETE keywords...
[SEGMENT .text]
[BITS 16]
[ORG 0]
jmp start
_print:
mov ah, 0eh
xor bh, bh
.loop:
lodsb
or al, al
jz .done
int 10h
jmp .loop
.done:
ret
start:
cli
mov ax, cs
mov ds, ax
mov [bootdrive], dl
; Tell the user
mov si, initpmodemsg
call _print
; Enable A20
xor ah, ah
mov al, 0xd1
out 0x64, al
mov al, 0xdf
out 0x60, al
; Load the GDT & (blank) IDT
lgdt [gdtPtr]
lidt [idtPtr]
; Enable the PE bit
mov eax, cr0
or al, 1
mov cr0, eax
; Jump to the 32-bit main32
db 66h, 0eah
dd main32
dw 8
jmp $
[BITS 32]
main32:
mov ax, 0x10
mov ds, ax
mov ax, 0x18
mov es, ax
mov [es:0], byte 205
jmp $
[SEGMENT .data]
initpmodemsg db "Switching to 32-bit protected mode...",0
donemsg db " DONE",13,10,0
errormsg db " ERROR",13,10,0
bootdrive db 0
gdt dd 0, 0
CODE_SEL equ $-gdt
code32Dscr dw 0xffff, 0
db 1, 10011010b, 01000000b, 0
DATA_SEL equ $-gdt
data32Dscr dw 0xffff, 0
db 1, 10010010b, 01000000b, 0
TEXT_SEL equ $-gdt
textDscr dw 0xfa0, 0x8000
db 0xb, 10010010b, 01000000b, 0
gdtEnd:
gdtSize equ gdtEnd - gdt
gdtPtr dw gdtSize - 1
dd gdt
idtPtr dw 0, 0, 0
... I wish the x86 came with NEW and DELETE keywords...
[SEGMENT .text]
[BITS 16]
[ORG 0]
jmp start
_print:
mov ah, 0eh
xor bh, bh
.loop:
lodsb
or al, al
jz .done
int 10h
jmp .loop
.done:
ret
start:
cli
mov ax, cs
mov ds, ax
mov [bootdrive], dl
; Tell the user
mov si, initpmodemsg
call _print
; Enable A20
xor ah, ah
mov al, 0xd1
out 0x64, al
mov al, 0xdf
out 0x60, al
; Load the GDT & (blank) IDT
lgdt [gdtPtr]
lidt [idtPtr]
; Enable the PE bit
mov eax, cr0
or al, 1
mov cr0, eax
; Jump to the 32-bit main32
db 66h, 0eah
dd main32
dw 8
jmp $
[BITS 32]
main32:
mov ax, 0x10
mov ds, ax
mov ax, 0x18
mov es, ax
mov [es:0], byte 205
jmp $
[SEGMENT .data]
initpmodemsg db "Switching to 32-bit protected mode...",0
donemsg db " DONE",13,10,0
errormsg db " ERROR",13,10,0
bootdrive db 0
gdt dd 0, 0
CODE_SEL equ $-gdt
code32Dscr dw 0xffff, 0
db 1, 10011010b, 01000000b, 0
DATA_SEL equ $-gdt
data32Dscr dw 0xffff, 0
db 1, 10010010b, 01000000b, 0
TEXT_SEL equ $-gdt
textDscr dw 0xfa0, 0x8000
db 0xb, 10010010b, 01000000b, 0
gdtEnd:
gdtSize equ gdtEnd - gdt
gdtPtr dw gdtSize - 1
dd gdt
idtPtr dw 0, 0, 0
RE:Kernel code
>> ... I wish the x86 came with NEW and DELETE keywords...
LOL! But that takes all the fun out of memory management routines...
You're code looks alright, but there are chunks missing. For example, after setting the PE bit
; Enable the PE bit
mov eax, cr0
or al, 1
mov cr0, eax
You should load in all registers with valid protected mode segments
mov ax, 0x10 ; 0x10 -kernel data segment
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ax, 0x18 ; 0x18 -kernel stack segment? (0x10 also works)
mov ss, ax
mov sp, 0x0
And then try your jump:
; Jump to the 32-bit main32
db 66h, 0eah
dd main32
dw 8
jmp $
Also, you say you load your code at physical address 0x00010000, right? And yet your org statement is [org 0], so... the above jump will only work if the base address for your code descriptor (and data descriptor, actually) is 0x00010000 (I can't interpret ASM GDTs on the fly
If you're using descriptors with a base of 0, however, then you'll have to do something like the following:
; Jump to the 32-bit main32
db 66h, 0eah
dd main32 + 0x00010000
dw 8
jmp $
Hopefully something up there gets things goin'
Jeff
LOL! But that takes all the fun out of memory management routines...
You're code looks alright, but there are chunks missing. For example, after setting the PE bit
; Enable the PE bit
mov eax, cr0
or al, 1
mov cr0, eax
You should load in all registers with valid protected mode segments
mov ax, 0x10 ; 0x10 -kernel data segment
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ax, 0x18 ; 0x18 -kernel stack segment? (0x10 also works)
mov ss, ax
mov sp, 0x0
And then try your jump:
; Jump to the 32-bit main32
db 66h, 0eah
dd main32
dw 8
jmp $
Also, you say you load your code at physical address 0x00010000, right? And yet your org statement is [org 0], so... the above jump will only work if the base address for your code descriptor (and data descriptor, actually) is 0x00010000 (I can't interpret ASM GDTs on the fly
If you're using descriptors with a base of 0, however, then you'll have to do something like the following:
; Jump to the 32-bit main32
db 66h, 0eah
dd main32 + 0x00010000
dw 8
jmp $
Hopefully something up there gets things goin'
Jeff
RE:Kernel code (A20)
Oh, and btw, your A20 may not work on all hardware. You should check to make sure the keyboard buffer is empty before sending anything to its ports (and wait ~25uS for the commands to actually be processed.
Try this:
enablea20:
;cli ; no more interuptions!
;xor cx, cx
mov si, a20message
call message
clear_buf:
in al, 64h ; get input from keyboard status port
test al, 02h ; test the buffer full flag
loopnz clear_buf ; loop until buffer is empty
mov al, 0D1h ; keyboard: write to output port
out 64h, al ; output command to keyboard
clear_buf2:
in al, 64h ; wait 'till buffer is empty again
test al, 02h
loopnz clear_buf2
mov al, 0dfh ; keyboard: set A20
out 60h, al ; send it to the keyboard controller
;mov cx, 14h
wait_kbc: ; this is approx. a 25uS delay to wait
out 0edh, ax ; for the kb controler to execute our
loop wait_kbc ; command.
;sti
ret
Cheers,
Jeff
Try this:
enablea20:
;cli ; no more interuptions!
;xor cx, cx
mov si, a20message
call message
clear_buf:
in al, 64h ; get input from keyboard status port
test al, 02h ; test the buffer full flag
loopnz clear_buf ; loop until buffer is empty
mov al, 0D1h ; keyboard: write to output port
out 64h, al ; output command to keyboard
clear_buf2:
in al, 64h ; wait 'till buffer is empty again
test al, 02h
loopnz clear_buf2
mov al, 0dfh ; keyboard: set A20
out 60h, al ; send it to the keyboard controller
;mov cx, 14h
wait_kbc: ; this is approx. a 25uS delay to wait
out 0edh, ax ; for the kb controler to execute our
loop wait_kbc ; command.
;sti
ret
Cheers,
Jeff
RE: Kernel code
I added the A20 code (thx!) and the base of my GDT code entry is 0x10000. Entry 0x18 is a pointer to the text screen, but I'd forgotten about the stack, and created an entry (base = 0x20000, limit = 0xffff). I tried setting the registers before the jmp, but then it crashed on the mov ds, ax. Should I try a different assembler? Thanks for your help so far!
; Load the GDT & (blank) IDT
lgdt [gdtPtr]
lidt [idtPtr]
; Enable the PE bit
mov eax, cr0
or al, 1
mov cr0, eax
; Jump to the 32-bit main32
db 66h, 0eah
dd main32
dw CODE_SEL ; CODE_SEL == 8
jmp $
[BITS 32]
main32:
; Load registers
jmp $ ; Get the jump working first
mov ax, 0x10
mov ds, ax
mov ax, 0x18
mov es, ax
mov fs, ax
mov gs, ax
mov ax, 0x20
mov ss, ax
mov sp, 0xffff
; Load the GDT & (blank) IDT
lgdt [gdtPtr]
lidt [idtPtr]
; Enable the PE bit
mov eax, cr0
or al, 1
mov cr0, eax
; Jump to the 32-bit main32
db 66h, 0eah
dd main32
dw CODE_SEL ; CODE_SEL == 8
jmp $
[BITS 32]
main32:
; Load registers
jmp $ ; Get the jump working first
mov ax, 0x10
mov ds, ax
mov ax, 0x18
mov es, ax
mov fs, ax
mov gs, ax
mov ax, 0x20
mov ss, ax
mov sp, 0xffff
RE: Kernel code
I'm pretty sure you want to load your registers with proper descriptors before to do you far jump... that's how I was always taught, anyway, and it will work (with nasm, as well).
Are you sure your idtPtr and (more specifically) gdtPtr are correct? You've defined the offset as "gdt", which is relative to the beginning of your boot loader (and consequently, also relative to the real mode data segment). So, unless your boot loader is being loaded at 0x0 (very unlikely), then "gdt" is an offset, and not a linear address, which lidt and lgdt expect.
In other words, if this program is, indeed, a boot sector, and loaded at real mode segment 0x07c0, then you'll want your gdtPtr to be defined as:
gdtPtr
dw equ gdtSize - 1
dd gdt + 0x00007c00
Or, more generally, your base address supplied to lgdt would be "gdt + DS*16"
Hope that makes sense,
Jeff
Are you sure your idtPtr and (more specifically) gdtPtr are correct? You've defined the offset as "gdt", which is relative to the beginning of your boot loader (and consequently, also relative to the real mode data segment). So, unless your boot loader is being loaded at 0x0 (very unlikely), then "gdt" is an offset, and not a linear address, which lidt and lgdt expect.
In other words, if this program is, indeed, a boot sector, and loaded at real mode segment 0x07c0, then you'll want your gdtPtr to be defined as:
gdtPtr
dw equ gdtSize - 1
dd gdt + 0x00007c00
Or, more generally, your base address supplied to lgdt would be "gdt + DS*16"
Hope that makes sense,
Jeff
Problem solved!
Thanks a lot for your help!!! I changed my gdtPtr to a linear address (adding 0x10000) and it worked fine!