Jump changed... still not working

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
Khumba

Jump changed... still not working

Post by Khumba »

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!
Khumba

RE:Jump changed... still not working

Post by Khumba »

Hmmm, I played around with the code, and changed the "DD MAIN32" to "DW MAIN32", and got it working! Yay!
carbonBased

RE:Jump changed... still not working

Post by carbonBased »

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
Khumba

Kernel code

Post by Khumba »

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
carbonBased

RE:Kernel code

Post by carbonBased »

>> ... 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
carbonBased

RE:Kernel code (A20)

Post by carbonBased »

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
Khumba

RE: Kernel code

Post by Khumba »

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
carbonBased

RE: Kernel code

Post by carbonBased »

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
Khumba

Problem solved!

Post by Khumba »

Thanks a lot for your help!!! I changed my gdtPtr to a linear address (adding 0x10000) and it worked fine!
Post Reply