Page 1 of 1

simple asm protected mode switch help!

Posted: Wed May 11, 2005 4:17 pm
by Ben_3D
Hi all,
well I've been fiddling around with some asm code, trying to go into protected mode...I wanted to go into 32bit pm from dos instead of doing a boot disk...just simple stuff.

I've been messing around with this for ages...I mean its not long...kept it very simple...but I know its something to do with where the code is in memory or something stupid.

Its not much code...and when we get into pm it just seems to reboot the computer...tried all sorts of hacks and checks but think it has something to do with where the code is put in memory...or possibly offsets or something.

Thanks for any feedback on this :)

Ben


<div class='indent'>
; Run in dos (not under windows) and it will take us to 32 bit protected mode

[ORG 0x100] ; Reserve 256 bytes for dos


[BITS 16] ; Dos is 16 bits


; assemble using 'nasm' assembler

; C:>nasm asmtest.asm -o test.exe

jmp entry ; Jump to the start of our code

msg1 db 'Where good to go..$';


entry:

; Display a message showing where alive!

mov dx, msg1 ; register dx=msg1
mov ah, 9 ; register ah=9 -- the print string function
int 21h ; dos service interrupt .. looks at register ah to figure out what to do

; Where in dos, and we've done a simple text message to the screen to show
; our program is running... so now where going to break out of this real 16 bit
; world and get into 32 protected mode. So lets set things up and go go go..

cli ; Clear or disable interrupts
lgdt[gdtr] ; Load GDT
mov eax,cr0 ; The lsb of cr0 is the protected mode bit
or al,0x01 ; Set protected mode bit
mov cr0,eax ; Mov modified word to the control register
jmp go_pm


; Once we reach here where in protected mode! 32 Bit! Where not in
; the real world (mode) anymore :)
[BITS 32]
go_pm :

mov ax, 0; ; Just poke something into the graphics memory so that we
mov es, ax; ; know its all okay! *SEEMS TO CRASH HERE!!!*
mov word [es: 0xb8000],0x740

spin : jmp spin ; Loop forever


; We use 16 bit alignment here - as you'll notice we use dw and dd only,
; and out data will be packed together nice and tight.

[BITS 16]

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Our GDTR register value
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

gdtr :
dw gdt_end-gdt-1 ; Length of the gdt
dd gdt ; physical address of gdt

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; This is the start of our gdt - its actual value
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

gdt:
nullsel equ $-gdt
gdt0
dd 0
dd 0
codesel equ $-gdt
code_gdt:
dw 0x0ffff
dw 0x0000
db 0x00
db 0x09a
db 0x0cf
db 0x00
datasel equ $-gdt
data_gdt:
dw 0x0ffff ; Limit 4Gb
dw 0x0000 ; Base 0000:0000h
db 0x00 ; Descriptor format same as above
db 0x092
db 0x0cf
db 0x00

gdt_end:



TIMES 0x500-($-$$) DB 0x90 ; And of course, this will make our file size
; equal to 0x500 a nice round number -
; 0x500 ... so if you assemble the file
; you should find its that size exactly.
</div>

Re:simple asm protected mode switch help!

Posted: Wed May 11, 2005 4:25 pm
by B.E
try using a far jump when jumping to 32bit mode(e.g. jmp 08:go_pm). Part from that the rest of the code seems fine.

Re:simple asm protected mode switch help!

Posted: Wed May 11, 2005 7:31 pm
by smiddy
The FAR jump is one thing, the other thing I noticed is you need to ensure your GDT offset for ES, DS, isn't 0. Meaning, the GDT is your selector and the 0 selector is usually 0, which will cause your reboot or rather triple fault, which reboots the box. Most people use the second selector 10 as their data selector, so putting ES,DS, etc. to 10h is what you need to do also (that is if it is your intended data selector for reading/writing information).

[edited]

DAH, yes, your selector should be 10h, or datasel, which is your EQUate. Then it will write to video RAM.

Also

Code: Select all

    JMP $
works too for continuous loop to self.

Re:simple asm protected mode switch help!

Posted: Thu May 12, 2005 4:15 am
by Ben_3D
:(

Thanks guys, I can see the errors that could have caused it... as I was making the silly mistake of assuming es and ds to be offsets in memory when there really offsets into the gdt table.

I modified the jump into protected mode (thanks to B.E):

jmp 0x08:go_pm ; uses codesel selector and jumps into 32 bit pm

Then I modified the code in the 32bit section, so it became (thanks to smiddy):

mov ax, 0x10
mov ds, ax
mov es, ax

mov word[0xb8000], 0x740

jmp $

I mean it looks simple...but it still reboots :(
It couldn't have anything to do with the code being offset to 0x100?...I didn't think it did as where not doing any memory stuff...just simple opcodes.

Hmmmmm

As I boot up a dos floppy, and then launch my binary from the disk....everything seems to work until protected mode comes into action...something inside go_pm just seems to make it reboot the computer :(

Any ideas or hacks to fix this would be great...just been changing lots of things today and have run out of ideas.

Thanks

Ben

Re:simple asm protected mode switch help!

Posted: Thu May 12, 2005 4:19 am
by Ben_3D
:( Thanks guys, I changed the jmp and I changed it so that the ds and es are set to 0x10 (datasel selector)...but it still seems to reboot...hmmmm

I just boot up a dos floppy and lauch the program from the disk...everything seems okay until it goes into protected mode...just seems to reboot...thought it would just display a changed char on the screen ....hmmmm.

Tried changing all sorts of things this morning but can't seem to get it!...bet its something really silly.

Thanks for your feedback guys...I can see where I was going wrong before...and really thought it would have worked, but any other ideas would be great.

Thanks

Ben

Re:simple asm protected mode switch help!

Posted: Thu May 12, 2005 5:09 am
by Brendan
Hi,

Code: Select all

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Our GDTR register value
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

gdtr:
    dw gdt_end-gdt-1 ; Length of the gdt
    dd gdt ; physical address of gdt
This last line should contain the actual address of the GDT, not the offset of the GDT relative to where DOS loaded it...

To fix this you'll need to work out where in memory DOS loaded your binary and adjust accordingly - for example:

Code: Select all

    mov eax,0
    mov ax,cs
    shl eax,4
    add [gdtr+2],eax

    cli ; Clear or disable interrupts
    lgdt[gdtr] ; Load GDT
    mov eax,cr0 ; The lsb of cr0 is the protected mode bit
    or al,0x01 ; Set protected mode bit
    mov cr0,eax ; Mov modified word to the control register
    jmp go_pm 
I didn't see any other bugs... :)


I'd also recommend using some alignment, for e.g. "align 4" before the GDT, and perhaps some before "gdtr:". To save a little space you can also recycle the first GDT entry (the NULL descriptor), because the CPU never uses it.

Code: Select all

   align 4

gdt:
    nullsel equ $-gdt
    dw 0
gdtr:
    dw gdt_end-gdt-1 ; Length of the gdt
    dd gdt ; physical address of gdt

    codesel equ $-gdt
code_gdt:
    dw 0x0ffff
    dw 0x0000

<--more GDT in here-->


Cheers,

Brendan

Re:simple asm protected mode switch help!

Posted: Fri May 13, 2005 3:58 am
by Ben_3D
Hi Brendon,
thanks for that info...I think thats the problem with the rebooting, it was the physical address of gdt as you said.

I added that code and it doesn't reboot anymore!..yeahh!...but it just seems to hang in pm...wont let me poke anything on the screen at 0xb8000...I mean I thought I could just write a few char's onto the graphics memory to show the code is okay.

I've pasted the code here...just seems to lock up after its gone into pm... I mean its using the dataselector which has a base address of 0000:0000 so if I access memory ds:0xb8000 it should show up on the screen in theory :)

Thanks again for all your help.

Ben

Code: Select all


    mov eax,0
    mov ax,cs
    shl eax,4
    add [gdtr+2],eax


cli          ; Clear or disable interrupts
lgdt[gdtr]       ; Load GDT
   mov eax,cr0       ; The lsb of cr0 is the protected mode bit
   or al,0x01       ; Set protected mode bit
   mov cr0,eax       ; Mov modified word to the control register
   jmp go_pm

nop                 ; ignore - no operation opcodes :)
nop

; Once we reach here where in protected mode!  32 Bit!  Where not in
; the real world (mode) anymore :)
[BITS 32]
go_pm :

mov ax, 0x10        ; use our datasel selector ( alternatively mov ax, datasel )
mov ds, ax,
mov es, ax

mov word [es: 0xb8000],0x740

     ; Write '- 32 bits OK   -' at [ds:0B84AAh].
     ;---------------------------------------
          mov     byte [ds:0B84CAh], '-'              ; char '-'
          mov     byte [ds:0B84CBh], 02h              ; Assign a color code
          mov     byte [ds:0B84CCh], ' '              ; char ' '
          mov     byte [ds:0B84CDh], 02h              ; Assign a color code
          mov     byte [ds:0B84CEh], '3'              ; char '3'
          mov     byte [ds:0B84CFh], 02h              ; Assign a color code
          mov     byte [ds:0B84D0h], '2'
          mov     byte [ds:0B84D1h], 02h              ; Assign a color code
          mov     byte [ds:0B84D2h], ' '
          mov     byte [ds:0B84D3h], 02h              ; Assign a color code
          mov     byte [ds:0B84D4h], 'b'
          mov     byte [ds:0B84D5h], 02h              ; Assign a color code
          mov     byte [ds:0B84D6h], 'i'
          mov     byte [ds:0B84D7h], 02h              ; Assign a color code
          mov     byte [ds:0B84D8h], 't'
          mov     byte [ds:0B84D9h], 02h              ; Assign a color code
          mov     byte [ds:0B84DAh], 's'
          mov     byte [ds:0B84DBh], 02h              ; Assign a color code
          mov     byte [ds:0B84DCh], ' '
          mov     byte [ds:0B84DDh], 02h              ; Assign a color code
          mov     byte [ds:0B84DEh], 'O'
          mov     byte [ds:0B84DFh], 02h              ; Assign a color code
          mov     byte [ds:0B84E0h], 'K'
          mov     byte [ds:0B84E1h], 02h              ; Assign a color code
          mov     byte [ds:0B84E2h], ' '
          mov     byte [ds:0B84E3h], 02h              ; Assign a color code
          mov     byte [ds:0B84E4h], ' '
          mov     byte [ds:0B84E5h], 02h              ; Assign a color code
          mov     byte [ds:0B84E6h], ' '
          mov     byte [ds:0B84E7h], 02h              ; Assign a color code
          mov     byte [ds:0B84E8h], '-'
          mov     byte [ds:0B84E9h], 02h              ; Assign a color code

lp: jmp lp  ; loops here forever and ever...


Re:simple asm protected mode switch help!

Posted: Fri May 13, 2005 5:34 am
by bubach
I would change "jmp go_pm" to "jmp 0x08:go_pm", it just feels safer (no idea if it helps).. ;)

/ Christoffer

Re:simple asm protected mode switch help!

Posted: Fri May 13, 2005 6:13 am
by smiddy
Try this code:

Code: Select all

MyMessage    db '- 32 bits OK  -',0

DisplayMyMessage:          ; Messag displaying algo

   mov esi,MyMessage
   mov eax,0b8000   ; assumes color linear framebuffer

.Loop:

   mov al,byte [esi]  ; copy byte
   cmp al,0
   je .Done
   mov byte [eax],al ; swap this if I have them reversed (char attribute)
   inc eax
   mov byte [eax],7 ; or what ever attribute you want
   inc eax
   inc esi
   jmp .Loop

.Done:

   ret

This should write your message at the top of the screen.

OH, I just noticed something, you'll need to establish a stack too, so you can do CALLs to routines. So set your SS when you set you DS, ES, FS, GS...then set ESP to a place where you can have some room to play, like 0ffffffh (1 byte under 16MB).

Re:simple asm protected mode switch help!

Posted: Fri May 13, 2005 6:13 am
by Brendan
Hi,
bubach wrote:I would change "jmp go_pm" to "jmp 0x08:go_pm", it just feels safer (no idea if it helps).. ;)
It'd definately help, but it still won't be correct! :)

This jump instruction has the same problem that GDTR had, but it's not so easy to fix. The problem is that "JMP 0x08:go_pm" will jump to "0 + go_pm" rather than "where_DOS_loaded_the_code + go_pm".

You could do something like:

Code: Select all

    section .data
jumpOffset:
    dd go_pm
    dw 0x08
    section .text
And then:

Code: Select all

    clr eax
    mov ax,cs
    shl eax,4
    mov eax,0
    add [gdtr+2],eax
    add [jumpOffset],eax

    cli            ; Clear or disable interrupts
    lgdt[gdtr]        ; Load GDT
    mov eax,cr0        ; The lsb of cr0 is the protected mode bit
    or al,0x01           ; Set protected mode bit
    mov cr0,eax          ; Mov modified word to the control register
    jmp far dword [jumpOffset]
This would jump to the right address, but you'd need to adjust everything that accesses anything within your code. The main problem is that you're using "ORG 0x100", which is correct in real mode but becomes completely wrong when you change to protected mode.

You could change the code segment base in the GDT so that it works the same as in real mode, and then use "JMP 0x08:go_pm" - that'd work too. Then you'd still have to be careful with accessing data that's within your binary, for example something like:

Code: Select all

    mov esi,address_of_string
    call print
Wouldn't work - you'd need to use something like:

Code: Select all

    mov esi,address_of_string
    add esi,[base_address_of_CS]
    call print
Another solution would be to create a seperate binary for 32 bit code, which is transferred to a fixed address. In this case you can use a correct ORG and you'd have no problems.

For example, the 32 bit code could be something like:

Code: Select all

    BITS 32
    org 0x10000

    jmp start

start:
    inc dword [0x000B8000]
    jmp start
In this case the 16 bit code would need something like:

Code: Select all

    section .data
kernelCodeAddress:
    %incbin "kernel.bin"     ;Include the first binary in this binary
kernelCodeEndAddress:
    section .text
You'd also need to copy the protected mode code to the right fixed address, and then jump to it (e.g. "JMP DWORD 0x08:0x10000"). The problem here is that you'd need to avoid overwriting the real mode code when you copy the protected mode code to it's correct address. This can be tricky because you can't predict which address DOS is going to load the real mode code at.

Which method you use is going to depend on how much protected mode code you need. If it's your entire kernel then use the last method (seperate binary at fixed address). Otherwise, if the amount of code is small it'd be easier to use one of the other methods..


Cheers,

Brendan

Re:simple asm protected mode switch help!

Posted: Sat May 14, 2005 2:37 am
by Ben_3D
Hey Brendan,
thanks...as you said it was the offsets for gdtr and the jmp to clear the pipeline and into protected mode.

Now that I know what it is I can work on playing around with the code some more :)

Thanks again everyone.

Ben