Bipman
Bipman

Reputation: 73

X86 switch to 32 bit protected mode

Another switching to 32 bit protected mode question. I'm currently writing a secure OS (text only) and have researched tutorials on switching modes but either I'm missing something or the code listed did not have all the details. I have a simple boot-loader which loads this code and then runs it. This code will then load the kernel and maybe switch to 64 bit long mode. The code faults at the mov cr0, eax line. I'm really stumped now so would really appreciate someone taking a look. In case it helps, I'm running it as a VM in VirtualBox and in the debugger it faults with 'u32: error: DBGCCmdHlpVarToDbgfAddr failed on '0000:00009362 L a': VERR_INVALID_SELECTOR' when I try to unassemble. The code has some sections borrowed from others so I've tried to acknowledge this where I can.

;;
;; enableA20.s (adapted from Visopsys OS-loader)
;;
;; Copyright (c) 2000, J. Andrew McLaughlin
;; You're free to use this code in any manner you like, as long as this
;; notice is included (and you give credit where it is due), and as long
;; as you understand and accept that it comes with NO WARRANTY OF ANY KIND.
;; Contact me at [email protected] about any bugs or problems.
;;
org 0x9210

enableA20:
;; This subroutine will enable the A20 address line in the keyboard
;; controller.  Takes no arguments.  Returns 0 in EAX on success, 
;; -1 on failure.  Written for use in 16-bit code, see lines marked
;; with 32-BIT for use in 32-bit code.u 9210

[BITS 16]

mov bx,jumper
call prstr

pusha

;; Make sure interrupts are disabled
cli

;; Keep a counter so that we can make up to 5 attempts to turn
;; on A20 if necessary
mov CX, 5

.startAttempt1:     
;; Wait for the controller to be ready for a command
.commandWait1:
xor AX, AX
in AL, 64h
bt AX, 1
jc .commandWait1

;; Tell the controller we want to read the current status.
;; Send the command D0h: read output port.
mov AL, 0D0h
out 64h, AL

;; Wait for the controller to be ready with a byte of data
.dataWait1:
xor AX, AX
in AL, 64h
bt AX, 0
jnc .dataWait1

;; Read the current port status from port 60h
xor AX, AX
in AL, 60h

;; Save the current value of (E)AX
push AX         ; 16-BIT
;; push EAX     ; 32-BIT

;; Wait for the controller to be ready for a command
.commandWait2:
in AL, 64h
bt AX, 1
jc .commandWait2

;; Tell the controller we want to write the status byte again
mov AL, 0D1h
out 64h, AL 

;; Wait for the controller to be ready for the data
.commandWait3:
xor AX, AX
in AL, 64h
bt AX, 1
jc .commandWait3

;; Write the new value to port 60h.  Remember we saved the old
;; value on the stack
pop AX          ; 16-BIT
;; pop EAX      ; 32-BIT

;; Turn on the A20 enable bit
or AL, 00000010b
out 60h, AL

;; Finally, we will attempt to read back the A20 status
;; to ensure it was enabled.

;; Wait for the controller to be ready for a command
.commandWait4:
xor AX, AX
in AL, 64h
bt AX, 1
jc .commandWait4

;; Send the command D0h: read output port.
mov AL, 0D0h
out 64h, AL 

;; Wait for the controller to be ready with a byte of data
.dataWait2:
xor AX, AX
in AL, 64h
bt AX, 0
jnc .dataWait2

;; Read the current port status from port 60h
xor AX, AX
in AL, 60h

;; Is A20 enabled?
bt AX, 1

;; Check the result.  If carry is on, A20 is on.
jc .success

;; Should we retry the operation?  If the counter value in ECX
;; has not reached zero, we will retry
loop .startAttempt1


;; Well, our initial attempt to set A20 has failed.  Now we will
;; try a backup method (which is supposedly not supported on many
;; chipsets, but which seems to be the only method that works on
;; other chipsets).


;; Keep a counter so that we can make up to 5 attempts to turn
;; on A20 if necessary
mov CX, 5

.startAttempt2:
;; Wait for the keyboard to be ready for another command
.commandWait6:
xor AX, AX
in AL, 64h
bt AX, 1
jc .commandWait6

;; Tell the controller we want to turn on A20
mov AL, 0DFh
out 64h, AL

;; Again, we will attempt to read back the A20 status
;; to ensure it was enabled.

;; Wait for the controller to be ready for a command
.commandWait7:
xor AX, AX
in AL, 64h
bt AX, 1
jc .commandWait7

;; Send the command D0h: read output port.
mov AL, 0D0h
out 64h, AL 

;; Wait for the controller to be ready with a byte of data
.dataWait3:
xor AX, AX
in AL, 64h
bt AX, 0
jnc .dataWait3

;; Read the current port status from port 60h
xor AX, AX
in AL, 60h

;; Is A20 enabled?
bt AX, 1

;; Check the result.  If carry is on, A20 is on, but we might warn
;; that we had to use this alternate method
jc .warn

;; Should we retry the operation?  If the counter value in ECX
;; has not reached zero, we will retry
loop .startAttempt2


;; OK, we weren't able to set the A20 address line.  Do you want
;; to put an error message here?
jmp .fail


.warn:
;; Here you may or may not want to print a warning message about
;; the fact that we had to use the nonstandard alternate enabling
;; method
mov bx,a20alt
call prstr
.success:
sti
popa
xor AX, AX
mov bx,a20norm
call prstr
jmp loader

.fail:
sti
popa
mov AX, -1
mov bx,a20err
call prstr
hlt
ret


;
;   Prints string to screen
;
prstr:
push bx
mov ah,0x0E
ploop:
mov al,[bx]             ;load byte of string
cmp al,0                ;if al=0 then string has ended so return
jz fin
mov bh,0
mov bl,7
int 0x10                ;print chr in string
pop bx
inc bx
push bx
jmp ploop               ;back to print next chr
fin:
pop bx                  ;get bx off stack
ret

section .data

a20err:             db 13,10,13,10,"A20 initialisation failed, halting",0
a20alt:             db 13,10,13,10,"Alternate A20 switch",0
a20norm:            db 13,10,13,10,"Normal A20 switch",0
jumper:             db 13,10,13,10,"Jumped to code",0



loader:

;
;jump to 32bit mode next
;
;32 bit mode code and gdt with help from Broken Thorn Website
;OS tutorial by Mike
;

cli             ; clear interrupts
xor ax,ax
mov ds,ax
[BITS 32]

lgdt [toc]          ; load GDT into GDTR
mov eax, cr0        ; set bit 0 in cr0--enter pmode
or  eax, 1
mov cr0, eax
jmp $   
mov ax, DATA_SEG
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax    
mov    esp, 0x090000  ; Set stack to grown downwards from 0x30000
jmp 0x08:clear_prefetch
nop
nop
clear_prefetch:

;;mov ax,cs     ;
;;push ax           ;
;;mov eax,Really32  ;
;;push eax          ;
;;retf          ;

jmp 0x08:Really32


Really32:

;
;test 32 bit mode
;

mov eax,0xb8000
mov word [Eax],0x41
jmp $








;
;GDT table  
; Offset 0 in GDT: Descriptor code=0
;
;First 1gb for code and then the rest for Data
;


gdt_data: 
dd 0                ; null descriptor
dd 0 

; Offset 0x8 bytes from start of GDT: Descriptor code therfore is 8

; gdt code:             ; code descriptor
gdt_code:
dw 0FFFFh           ; limit low
dw 0                ; base low
db 0                    ; base middle
db 10011010b            ; access
db 11001111b            ; granularity = Limit = 2GB-1
db 00               ; base high = Base =1GB

; Offset 16 bytes (0x10) from start of GDT. Descriptor code therfore is 0x10.

; gdt data:             ; data descriptor
gdt_dat:
dw 0FFFFh           ; limit low 
dw 0                ; base low
db 0                ; base middle
db 10010010b            ; access
db 11001111b            ; granularity = limit=rest of memry for data
db 00               ; base high

;...Other descriptors begin at offset 0x18. Remember that each descriptor is 8 bytes in size?
; Add other descriptors for Ring 3 applications, stack, whatever here...

end_of_gdt:
toc: 
dw end_of_gdt - gdt_data    ; limit (Size of GDT)
dd gdt_data             ; base of GDT

CODE_SEG equ gdt_code - gdt_data
DATA_SEG equ gdt_dat - gdt_data

The last couple of lines of code should write 'A' to the screen. Regards

Bipman

Upvotes: 3

Views: 2303

Answers (1)

Bipman
Bipman

Reputation: 73

Thanks to Margaret I have the answer in moving the [BITS 32] and also generating the far jump as a series of bytes instead of using the 32 bit far jump which of course did not work. Hope 64 bit long mode isn't this confusing! Code below.

lgdt [toc]          ; load GDT into GDTR
mov eax, cr0        ; set bit 0 in cr0--enter pmode
or  eax, 1
mov cr0, eax
jmp clear_prefetch
nop
nop
clear_prefetch:
mov ax, DATA_SEG
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax 
mov    esp, 0x090000  ; Set stack to grown downwards from 0x30000

;;mov ax,cs     ;
;;push ax           ;
;;mov eax,Really32  ;
;;push eax          ;
;;retf          ;

db 0x66
db 0xEA
dd Really32
dw 0x0008


Really32:

;
;test 32 bit mode
;
[BITS 32]

mov eax,0xb8000
mov word [Eax],0x4141
jmp $

Upvotes: 1

Related Questions