Problem switching to protected mode

This is a discussion on Problem switching to protected mode within the ASM x86 ASM 370 forums in Programming Languages category; This is probably a perennial question but please bear with me. I am writing a small toy "OS" which I want to run in protected mode but initially switch back to real mode to use the BIOS for I/O, until I can write some device drivers. I have written a boot loader that loads a test program to 3000H:0 and executes it. However, the machine resets itself when it hits the part to switch into protected mode. Commenting out the switch to protected mode and back and it executes fine. Can anyone tell me what the problem is? I suspect ...

Go Back   Application Development Forum > Programming Languages > ASM x86 ASM 370

Object Mix

Register FAQ Calendar Search Today's Posts Mark Forums Read
  #1  
Old 08-03-2008, 10:02 PM
Mark Brown
Guest
 
Default Problem switching to protected mode

This is probably a perennial question but please bear with me.

I am writing a small toy "OS" which I want to run in protected mode
but initially switch back to real mode to use the BIOS for I/O, until
I can write some device drivers.

I have written a boot loader that loads a test program to 3000H:0 and
executes it. However, the machine resets itself when it hits the part
to switch into protected mode. Commenting out the switch to protected
mode and back and it executes fine.

Can anyone tell me what the problem is? I suspect that the global
descriptor table is wrong, or that the global descriptor table is
getting filled with crap, but I can't tell. (I'm booting a test PC
from a memory stick containing the boot loader and the test program,
which makes debugging tricky.)

Program follows. Thanks for your patience.


; This is loaded to segment 3000H by the bootstrap loader.
;

KBD_CTRL EQU 0060H ; 8042 keyboard controller control port
KBD_STATUS EQU 0064H ; 8042 keyboard controller status port
GDT_RELOC EQU 0600H ; base of GDT
MON_STACK_TOP EQU 0FFEH ; top of monitor stack


[ORG 0H]
PUSH cs
POP ax
MOV ds, ax
XOR ax, ax
MOV es, ax
MOV ss, ax
MOV sp, MON_STACK_TOP

; Print a welcome message.
MOV si, M_BANNER
CALL Puts

; Relocate the Interrupt Descriptor Table and Global Descriptor Table
from
; our segment into the bottom segment.
;
CLD ; want things counting UP
MOV si, GDT_BASE ; interrupt descriptor table
MOV di, GDT_RELOC
MOV cx, (GDT_END - GDT_BASE) >> 2
REP MOVSD

; Set up for protected mode (segmented memory model).
; Global Descriptor Table is set up with
; one code segment and one data segment, overlapping.
;
CALL EnableA20 ; Enable A20 address line.
CLI ; Let's hope an NMI doesn't happen while we're pratting
around.
; Don't know whether it's used on the IBM PC, nor how to disable
; it in hardware.
;
; Load the GDT descriptor
LGDT [DS:GDT_DESCRIPTOR]
;
; Enter protected mode by setting the PE bit in CR0.
MOV eax, cr0
OR eax, 1
MOV cr0, eax
;
; Far jump to set CS to point to the correct segment.
; Note that segment references are now offsets in the Global
Descriptor Table.
; The code segment is therefore 08H (after the NULL segment).
JMP 08H:START_PROT

[BITS 32]
; From here we're in protected mode.
START_PROT:
MOV ax, 10H ; Save data segment identifier (offset of code segment
in GDT).
MOV ds, ax ; Initialise a valid data segment
MOV es, ax
MOV fs, ax
MOV gs, ax
MOV ss, ax ; and stack segment.
MOV esp, 090000H ; Set stack pointer.
MOV BYTE[0B8000H], 'Z' ; Put something on the screen.
MOV BYTE[0B8001H], 1BH

; Go back to real mode.
;
; Clear the PE bit.
MOV eax, cr0
AND eax, 0FFFFFFFEH
MOV cr0, eax
;
; Reset CS to real mode value by a far jump.
JMP 3000H:START_REAL

[BITS 16]
START_REAL:
PUSH cs
POP ax
MOV ds, ax
XOR ax, ax
MOV ss, ax
MOV sp, MON_STACK_TOP
CALL DisableA20

STI

; Print a welcome message.
MOV si, M_BANNER
CALL Puts
HANG: JMP HANG


;
------------------------------------------------------------------------

; A20 control routines.
;
EnableA20:
CALL Wait8042
MOV al, 0D1H ; write command
OUT KBD_STATUS, al
CALL Wait8042
MOV al, 0DFH ; enable bit 1 (A20)
OUT KBD_CTRL, al
CALL Wait8042
RET

DisableA20:
CALL Wait8042
MOV al, 0D1H ; write command
OUT KBD_STATUS, al
CALL Wait8042
MOV al, 0DDH ; disable bit 1 (A20)
OUT KBD_CTRL, al
CALL Wait8042
RET

Wait8042: ; wait for the keyboard controller to finish handling
input
;CALL Delay
IN al, KBD_STATUS
TEST al, 2
JNZ Wait8042
RET


; Put string
;
Puts: LODSB
OR al, al
JZ .done
MOV ah, 0EH
MOV bx, 7
INT 10H
JMP Puts
..done: RET

M_BANNER: DB 'Wallo, chaps!', 13, 10, 0


.ALIGN32

; Interrupt Descriptor Table
IDT_BASE: TIMES 256 DB 0

; Global Descriptor Table.
; Must be contiguous with IDT for relocation purposes.
GDT_BASE: ; base of table
; null segment - must be present
DW 0, 0, 0, 0
; code segment: base address 0, 4GB limit, read-only
; kernel privilege
DW 0FFFFh
DW 0
DW 9a00H
DW 00CFH
; DQ 00CF9A000000FFFFH
; data segment: base address 0, 4GB limit, read-write
DW 0FFFFH
DW 0
DW 9200H
DW 00CFH
; DQ 00CF92000000FFFFH
GDT_END:

; GDT descriptor table. This is a 6-byte pseudo-descriptor to the GDT.
GDT_DESCRIPTOR: DW GDT_END - GDT_BASE ; GDT size in bytes
DD GDT_RELOC ; base of the GDT


; end

Reply With Quote
  #2  
Old 08-04-2008, 04:03 AM
Alexei A. Frounze
Guest
 
Default Re: Problem switching to protected mode

On Aug 3, 7:02 pm, Mark Brown <spamt...@crayne.org> wrote:
> This is probably a perennial question but please bear with me.
>
> I am writing a small toy "OS" which I want to run in protected mode
> but initially switch back to real mode to use the BIOS for I/O, until
> I can write some device drivers.
>
> I have written a boot loader that loads a test program to 3000H:0 and
> executes it. However, the machine resets itself when it hits the part
> to switch into protected mode. Commenting out the switch to protected
> mode and back and it executes fine.
>
> Can anyone tell me what the problem is? I suspect that the global
> descriptor table is wrong, or that the global descriptor table is
> getting filled with crap, but I can't tell. (I'm booting a test PC
> from a memory stick containing the boot loader and the test program,
> which makes debugging tricky.)
>
> Program follows. Thanks for your patience.
>
> ; This is loaded to segment 3000H by the bootstrap loader.
> ;
>
> KBD_CTRL EQU 0060H ; 8042 keyboard controller control port
> KBD_STATUS EQU 0064H ; 8042 keyboard controller status port
> GDT_RELOC EQU 0600H ; base of GDT
> MON_STACK_TOP EQU 0FFEH ; top of monitor stack
>
> [ORG 0H]
> PUSH cs
> POP ax
> MOV ds, ax
> XOR ax, ax
> MOV es, ax
> MOV ss, ax
> MOV sp, MON_STACK_TOP
>
> ; Print a welcome message.
> MOV si, M_BANNER
> CALL Puts
>
> ; Relocate the Interrupt Descriptor Table and Global Descriptor Table
> from
> ; our segment into the bottom segment.
> ;
> CLD ; want things counting UP
> MOV si, GDT_BASE ; interrupt descriptor table
> MOV di, GDT_RELOC
> MOV cx, (GDT_END - GDT_BASE) >> 2
> REP MOVSD
>
> ; Set up for protected mode (segmented memory model).
> ; Global Descriptor Table is set up with
> ; one code segment and one data segment, overlapping.
> ;
> CALL EnableA20 ; Enable A20 address line.
> CLI ; Let's hope an NMI doesn't happen while we're pratting
> around.
> ; Don't know whether it's used on the IBM PC, nor how to disable
> ; it in hardware.
> ;
> ; Load the GDT descriptor
> LGDT [DS:GDT_DESCRIPTOR]
> ;
> ; Enter protected mode by setting the PE bit in CR0.
> MOV eax, cr0
> OR eax, 1
> MOV cr0, eax
> ;
> ; Far jump to set CS to point to the correct segment.
> ; Note that segment references are now offsets in the Global
> Descriptor Table.
> ; The code segment is therefore 08H (after the NULL segment).
> JMP 08H:START_PROT
>
> [BITS 32]
> ; From here we're in protected mode.
> START_PROT:
> MOV ax, 10H ; Save data segment identifier (offset of code segment
> in GDT).
> MOV ds, ax ; Initialise a valid data segment
> MOV es, ax
> MOV fs, ax
> MOV gs, ax
> MOV ss, ax ; and stack segment.
> MOV esp, 090000H ; Set stack pointer.
> MOV BYTE[0B8000H], 'Z' ; Put something on the screen.
> MOV BYTE[0B8001H], 1BH
>
> ; Go back to real mode.
> ;
> ; Clear the PE bit.
> MOV eax, cr0
> AND eax, 0FFFFFFFEH
> MOV cr0, eax
> ;
> ; Reset CS to real mode value by a far jump.
> JMP 3000H:START_REAL
>
> [BITS 16]
> START_REAL:
> PUSH cs
> POP ax
> MOV ds, ax
> XOR ax, ax
> MOV ss, ax
> MOV sp, MON_STACK_TOP
> CALL DisableA20
>
> STI
>
> ; Print a welcome message.
> MOV si, M_BANNER
> CALL Puts
> HANG: JMP HANG
>
> ;
> ------------------------------------------------------------------------
>
> ; A20 control routines.
> ;
> EnableA20:
> CALL Wait8042
> MOV al, 0D1H ; write command
> OUT KBD_STATUS, al
> CALL Wait8042
> MOV al, 0DFH ; enable bit 1 (A20)
> OUT KBD_CTRL, al
> CALL Wait8042
> RET
>
> DisableA20:
> CALL Wait8042
> MOV al, 0D1H ; write command
> OUT KBD_STATUS, al
> CALL Wait8042
> MOV al, 0DDH ; disable bit 1 (A20)
> OUT KBD_CTRL, al
> CALL Wait8042
> RET
>
> Wait8042: ; wait for the keyboard controller to finish handling
> input
> ;CALL Delay
> IN al, KBD_STATUS
> TEST al, 2
> JNZ Wait8042
> RET
>
> ; Put string
> ;
> Puts: LODSB
> OR al, al
> JZ .done
> MOV ah, 0EH
> MOV bx, 7
> INT 10H
> JMP Puts
> .done: RET
>
> M_BANNER: DB 'Wallo, chaps!', 13, 10, 0
>
> .ALIGN32
>
> ; Interrupt Descriptor Table
> IDT_BASE: TIMES 256 DB 0
>
> ; Global Descriptor Table.
> ; Must be contiguous with IDT for relocation purposes.
> GDT_BASE: ; base of table
> ; null segment - must be present
> DW 0, 0, 0, 0
> ; code segment: base address 0, 4GB limit, read-only
> ; kernel privilege
> DW 0FFFFh
> DW 0
> DW 9a00H
> DW 00CFH
> ; DQ 00CF9A000000FFFFH
> ; data segment: base address 0, 4GB limit, read-write
> DW 0FFFFH
> DW 0
> DW 9200H
> DW 00CFH
> ; DQ 00CF92000000FFFFH
> GDT_END:
>
> ; GDT descriptor table. This is a 6-byte pseudo-descriptor to the GDT.
> GDT_DESCRIPTOR: DW GDT_END - GDT_BASE ; GDT size in bytes
> DD GDT_RELOC ; base of the GDT
>
> ; end


I can immediately see a few problems:

1. the instructions in the code are compiled with the assumption of
the segment base = 3000H * 16, where (E)IP is 0. However your GDT
describes the segments as having base = 0. Guess to what physical
location JMP 08H:START_PROT is jumping?

You either need to redo the GDT (change or add more segments) or
change the initial instructions in the above code and introduce ORG
START_PROT + 3000H * 16 right after START_PROT and also move all(!)
real mode code before START_PROT... Actually, in the latter case you
need to carefully separate all 16-bit and 32-bit code (and data used
by them) or you'll have the same problem as now but in a different
place.

Don't forget that the compiled x86 code is generally not position-
independent and therefore its your responsibility to take care of all
segment bases and offsets (know what, where and when).

2. Clearing CR0.PE doesn't switch to a 16-bit mode (not supposed to
just like setting CR0.PE). Nor does JMP 3000H:START_REAL. The 16-
bit mode must be set prior to clearing CR0.PE.

3. Before clearing CR0.PE all segments must be loaded with selectors
of segments that are setup appropriately for operation in real mode
(effective limit (>= 65535), type (present, DPL=0, not expand-down,
not conforming, 16-bit for CS, not big for SS).

I'd also advise to comment out the A20 manipulation code for now. If
there's a bug in it, it can also cause a reset (in the same port
there's a bit setting (or is it resetting?) which will reset the CPU).

Alex

Reply With Quote
  #3  
Old 08-04-2008, 04:42 AM
Dirk Wolfgang Glomp
Guest
 
Default Re: Problem switching to protected mode

Am Sun, 3 Aug 2008 19:02:10 -0700 (PDT) schrieb Mark Brown:
> CLI ; Let's hope an NMI doesn't happen while we're pratting
> around.
> ; Don't know whether it's used on the IBM PC, nor how to disable
> ; it in hardware.


RBIL->inter61d.zip->Ports.a
----------P0070007F--------------------------
PORT 0070-007F - CMOS RAM/RTC (REAL TIME CLOCK)
Note: the real-time clock may be either a discrete MC146814, MC146818, or
an emulation thereof built into the motherboard chipset
SeeAlso: PORT 00A0h"XT"

0070 -W CMOS RAM index register port (ISA, EISA)
bit 7 = 1 NMI disabled from reaching CPU
= 0 NMI enabled

Ralf Browns Interrupt List(RBIL):
http://www.pobox.com/~ralf
http://www.pobox.com/~ralf/files.html
ftp://ftp.cs.cmu.edu/afs/cs.cmu.edu/user/ralf/pub/

Dirk

Reply With Quote
  #4  
Old 08-04-2008, 06:03 AM
Rod Pemberton
Guest
 
Default Re: Problem switching to protected mode

"Mark Brown" <spamtrap@crayne.org> wrote in message
news:f53f616f-ecf0-4b9f-b75e-85eb612035f6@p31g2000prf.googlegroups.com...
> This is probably a perennial question but please bear with me.
>
> I am writing a small toy "OS" which I want to run in protected mode
> but initially switch back to real mode to use the BIOS for I/O, until
> I can write some device drivers.
>
> I have written a boot loader that loads a test program to 3000H:0 and
> executes it. However, the machine resets itself when it hits the part
> to switch into protected mode. Commenting out the switch to protected
> mode and back and it executes fine.
>
> Can anyone tell me what the problem is? I suspect that the global
> descriptor table is wrong, or that the global descriptor table is
> getting filled with crap, but I can't tell. (I'm booting a test PC
> from a memory stick containing the boot loader and the test program,
> which makes debugging tricky.)
>
> Program follows. Thanks for your patience.
>
>
> ; This is loaded to segment 3000H by the bootstrap loader.
> ;
>
> KBD_CTRL EQU 0060H ; 8042 keyboard controller control port
> KBD_STATUS EQU 0064H ; 8042 keyboard controller status port
> GDT_RELOC EQU 0600H ; base of GDT
> MON_STACK_TOP EQU 0FFEH ; top of monitor stack
>
>
> [ORG 0H]
> PUSH cs
> POP ax
> MOV ds, ax
> XOR ax, ax
> MOV es, ax
> MOV ss, ax
> MOV sp, MON_STACK_TOP
>
> ; Print a welcome message.
> MOV si, M_BANNER
> CALL Puts
>
> ; Relocate the Interrupt Descriptor Table and Global Descriptor Table
> from
> ; our segment into the bottom segment.
> ;
> CLD ; want things counting UP
> MOV si, GDT_BASE ; interrupt descriptor table
> MOV di, GDT_RELOC
> MOV cx, (GDT_END - GDT_BASE) >> 2
> REP MOVSD
>


1) Have you tried to see if it works without relocating the GDT?
2) And, have you confirmed that you don't have an off by one error with
'cx'?

> ; Set up for protected mode (segmented memory model).
> ; Global Descriptor Table is set up with
> ; one code segment and one data segment, overlapping.
> ;
> CALL EnableA20 ; Enable A20 address line.


I would probably comment out the A20 and GDT relocation until I found the
reset problem.

> CLI ; Let's hope an NMI doesn't happen while we're pratting
> around.
> ; Don't know whether it's used on the IBM PC, nor how to disable
> ; it in hardware.
> ;
> ; Load the GDT descriptor
> LGDT [DS:GDT_DESCRIPTOR]
> ;
> ; Enter protected mode by setting the PE bit in CR0.
> MOV eax, cr0
> OR eax, 1
> MOV cr0, eax
> ;
> ; Far jump to set CS to point to the correct segment.
> ; Note that segment references are now offsets in the Global
> Descriptor Table.
> ; The code segment is therefore 08H (after the NULL segment).


If you place a "JMP $" here, does it hang instead of reboot? If yes, good
until here.

> JMP 08H:START_PROT
>
> [BITS 32]
> ; From here we're in protected mode.
> START_PROT:


If you place a "JMP $" here, does it hang instead of reboot? If yes, good
until here. If yes to other "JMP $", and no to this one, then there is a
problem with your selector, descriptor, or LGDT. Which is why I suggested
you try to get it working without relocating the GDT.

> MOV ax, 10H ; Save data segment identifier (offset of code segment
> in GDT).
> MOV ds, ax ; Initialise a valid data segment
> MOV es, ax
> MOV fs, ax
> MOV gs, ax
> MOV ss, ax ; and stack segment.
> MOV esp, 090000H ; Set stack pointer.
> MOV BYTE[0B8000H], 'Z' ; Put something on the screen.
> MOV BYTE[0B8001H], 1BH
>


If you place a "JMP $" here, does it hang instead of reboot? If yes, good
until here.

> ; Go back to real mode.
> ;
> ; Clear the PE bit.
> MOV eax, cr0
> AND eax, 0FFFFFFFEH


(save a byte or few, AND with 0xFE sign extended to 0xFFFFFFFE. E.g., for
NASM: AND eax, byte -0x02)

> MOV cr0, eax
> ;
> ; Reset CS to real mode value by a far jump.
> JMP 3000H:START_REAL


I'm not sure this is correct.

Someone else will need to clarify, but I thought you needed to do PM far
jump to a PM selector setup to match RM address segments, and then jump
again via a RM far jump to go to RM... I.e., you need two more selectors
(code,data) in your GDT for RM, don't you?

>
> [BITS 16]
> START_REAL:
> PUSH cs
> POP ax
> MOV ds, ax
> XOR ax, ax
> MOV ss, ax
> MOV sp, MON_STACK_TOP
> CALL DisableA20
>
> STI
>
> ; Print a welcome message.
> MOV si, M_BANNER
> CALL Puts
> HANG: JMP HANG
>
>
> ;
> ------------------------------------------------------------------------
>
> ; A20 control routines.
> ;
> EnableA20:
> CALL Wait8042
> MOV al, 0D1H ; write command
> OUT KBD_STATUS, al
> CALL Wait8042
> MOV al, 0DFH ; enable bit 1 (A20)
> OUT KBD_CTRL, al
> CALL Wait8042
> RET
>
> DisableA20:
> CALL Wait8042
> MOV al, 0D1H ; write command
> OUT KBD_STATUS, al
> CALL Wait8042
> MOV al, 0DDH ; disable bit 1 (A20)
> OUT KBD_CTRL, al
> CALL Wait8042
> RET
>
> Wait8042: ; wait for the keyboard controller to finish handling
> input
> ;CALL Delay
> IN al, KBD_STATUS
> TEST al, 2
> JNZ Wait8042
> RET
>
>
> ; Put string
> ;
> Puts: LODSB
> OR al, al
> JZ .done
> MOV ah, 0EH
> MOV bx, 7
> INT 10H
> JMP Puts
> .done: RET
>
> M_BANNER: DB 'Wallo, chaps!', 13, 10, 0
>
>
> .ALIGN32
>
> ; Interrupt Descriptor Table
> IDT_BASE: TIMES 256 DB 0
>
> ; Global Descriptor Table.
> ; Must be contiguous with IDT for relocation purposes.
> GDT_BASE: ; base of table
> ; null segment - must be present
> DW 0, 0, 0, 0
> ; code segment: base address 0, 4GB limit, read-only
> ; kernel privilege
> DW 0FFFFh
> DW 0
> DW 9a00H
> DW 00CFH
> ; DQ 00CF9A000000FFFFH
> ; data segment: base address 0, 4GB limit, read-write
> DW 0FFFFH
> DW 0
> DW 9200H
> DW 00CFH
> ; DQ 00CF92000000FFFFH
> GDT_END:
>
> ; GDT descriptor table. This is a 6-byte pseudo-descriptor to the GDT.
> GDT_DESCRIPTOR: DW GDT_END - GDT_BASE ; GDT size in bytes


Off by one? DW GDT_END - GDT_BASE -1 ;

> DD GDT_RELOC ; base of the GDT
>
>
> ; end
>


Just a quick skim, I'm not sure any was your issue...


Rod Pemberton

Reply With Quote
  #5  
Old 08-04-2008, 04:17 PM
James Harris
Guest
 
Default Re: Problem switching to protected mode

On 4 Aug, 03:02, Mark Brown <spamt...@crayne.org> wrote:
> This is probably a perennial question but please bear with me.
>
> I am writing a small toy "OS" which I want to run in protected mode
> but initially switch back to real mode to use the BIOS for I/O, until
> I can write some device drivers.
>
> I have written a boot loader that loads a test program to 3000H:0 and
> executes it. However, the machine resets itself when it hits the part
> to switch into protected mode. Commenting out the switch to protected
> mode and back and it executes fine.
>
> Can anyone tell me what the problem is? I suspect that the global
> descriptor table is wrong, or that the global descriptor table is
> getting filled with crap, but I can't tell. (I'm booting a test PC
> from a memory stick containing the boot loader and the test program,
> which makes debugging tricky.)
>
> Program follows. Thanks for your patience.


<code snipped>

It took me a while to get the PM transition right. Like you every time
I ran the code it reset the machine because something wasn't right. My
last find was that it wasn't enough to identity-map the code (in my
case code is in range 0x0001_0000 to 0x0001_ffff was only mapped as
part). I had to have a 64k code segment for exactly this address
range. Below is some minimalistic code that transfers to PM bearing
this in mind. Maybe it will help.

By the way, booting always on real hardware is very time consuming.
You may want to try some virtualisation software. I found Vmware
Server (free) and Microsoft Virtual PC (also free) worked well. With
these, and using a floppy to boot, to test a new version of code all
you need to do is compile, copy to floppy and reboot the virtual
machine. You could maybe do similar with USB flash rather than floppy
if you prefer that.


;************************************************* *********************
;*
;* 386 code from here though we are still running 16-bit real mode
;*
;************************************************* *********************

cpu 386
bits 16
;
;We can now use 32-bit registers for values but addresses need to
;remain within a 64k offset from a segment register.
;

call initial_enable_a20 ;First attempt at enabling A20
call get_memory_map ;Get standard form memory map

call set_up_gdt_32r


call nmi_disable ;Prevent NMIs
call a20_required_32r ;Ensure A20 enabled or abort


cli ;Prevent other interrupts
lgdt [gdt.limit]
mov eax, cr0 ;Change
or eax, IA32_CR0_PE ; to
mov cr0, eax ; protected
jmp 32mode_start ; mode

cpu 386
bits 32

pmode_start:


;************************************************* *********************
;*
;* Protected mode from here
;*
;************************************************* *********************


The GDT pointer referred to above is defined as

align 8
gdt:
dw 0
..limit dw 256 * 8 - 1 ;Initial GDT limit
..base dd 0x8000 ; and address



The GDT contents are set up with


;************************************************* *********************
;*
;* Set up initial GDT
;*
;************************************************* *********************

set_up_gdt_32r:

push eax
push ecx
push edx
push es
push di

mov di, [gdt.base]
xor eax, eax
mov es, ax
mov cx, 256

..clear_gdt:
mov [es:di], eax
mov [es:di + 4], eax
add di, 8
loop .clear_gdt

;Set up a data descriptor for the entire address space
mov di, [gdt.base]
add di, 8 ;GDT.(1)
xor edx, edx ;Base = 0
mov ecx, -1 ;Limit = max
mov bh, 0x80 + 0x40 ;G + D/B
mov bl, 0x80 + 0x10 + 2 ;P + S + type 2 (r/w data)
call descriptor_32r.create ;Write the values

;Set up a code descriptor for the entire address space
mov di, [gdt.base]
add di, 16 ;GDT.(2)
xor edx, edx ;Base = 0
mov ecx, -1 ;Limit = max
mov bh, 0x80 + 0x40 ;G + D/B
mov bl, 0x80 + 0x10 + 8 ;P + S + type 8 (exe only)
call descriptor_32r.create ;Write the values


;Set up a descriptor for the data in this code
mov di, [gdt.base]
add di, 24 ;GDT.(3)
mov edx, 0x10000 ;Base = 0x0001_0000
mov ecx, 65535 ;Limit
mov bh, 0x40 ;D/B
mov bl, 0x80 + 0x10 + 2 ;P + S + type 2 (r/w data)
call descriptor_32r.create ;Write the values

;Set up a descriptor for this code
mov di, [gdt.base]
add di, 32 ;GDT.(4)
mov edx, 0x10000 ;Base = 0x0001_0000
mov ecx, 65535 ;Limit
mov bh, 0x40 ;D/B
mov bl, 0x80 + 0x10 + 10 ;P + S + type 10 (exe, read)
call descriptor_32r.create ;Write the values


pop di
pop es
pop edx
pop ecx
pop eax
ret


;************************************************* *********************
;*
;* Descriptor maintenance
;*
;************************************************* *********************

%include "ia32_defs.inc"

descriptor_32r:

;All these use es:di pointing at the descriptor address


..create:

;Enter with
;
; edx = base address
; ecx = limit in bytes (even where granularity bit set)
; ebx
; bh = g, d/b, l, avl in top 4 bits
; bl = p, dpl, s, type
;

push ebx
push ecx
push edx

;Set the base from edx
mov [es:di + descript.base_15_0], dx ;Base bytes 1 and 0
shr edx, 16
mov [es:di + descript.base_23_16], dl ;Base byte 2
mov [es:di + descript.base_31_24], dh ;Base byte 3

;Set the type byte - P, DPL, S, Type
mov [es:di + descript.pddstttt], bl ;Set type byte flags

test bh, 0x80 ;Granularity bit set?
jz .set_limit ;Skip if no

shr ecx, 12 ;Div 32-bit by 4096

..set_limit:
and bh, 0xf0 ;Mask out limit bits
mov [es:di + descript.lim_15_0], cx ;Limit bits 15 :- 0
shr ecx, 16
and cl, 0xf ;Limit bits 19 :- 16
or cl, bh ;Merge g, d/b, l, avl
mov [es:di + descript.gdlallll], cl ;Set these 8 bits

pop edx
pop ecx
pop ebx
ret

Reply With Quote
  #6  
Old 08-04-2008, 04:42 PM
Wolfgang Kern
Guest
 
Default Re: Problem switching to protected mode


"ark Brown asked:

{about returning to RM16]

the manuals (Intel and AMD) states that it is required to switch
from PM32 to RM16 by using an intermediate PM16 state:

:use32
jmp far PM16
PM16:
:use16
.... ;restore RM16 segment limits or keep it 'Unreal'
jmp far RM16
RM16:
....

__
wolfgang
I checked it as not required on a few 486-CPUs,
but who will rely on the undocumented ...?

btw: I missed a check for A20 is actually enabled in your code


Reply With Quote
  #7  
Old 08-05-2008, 05:15 AM
Mark Brown
Guest
 
Default Re: Problem switching to protected mode

I got it working finally, at least as far as the switch to PM. The
switch back to RM is the next challenge. Many thanks to those who took
the time to respond. I'm very grateful and it probably saved me a
week ;-)

The main problem was highlighted by Alexei - the segments were all
over the place. I hadn't realised the far jump didn't take a 32-bit
address as an argument, but only a 16-bit address referenced to a base
pointer. (I come from a 68000 assembler background, and before that
Z80, so this segmentation stuff is totally alien and is causing no end
of grief.)

So, I re-wrote the code to relocate itself. The extract of the
pertinent section is as follows.


; This is loaded to segment 3000H by the bootstrap loader.
;
ORIGSEG EQU 3000H

IDT_RELOC EQU 0500H ; base of IVT (256 bytes)
GDT_RELOC EQU 0600H ; base of GDT
MON_RELOC EQU 0700H ; base of relocated monitor program
MON_STACK_TOP EQU 0FFEH ; top of (16-bit) monitor stack

[BITS 16]
[ORG ORIGSEG*16]

RELOC_START: ; Relocate from here.
CLI
PUSH cs
POP ax
MOV ds, ax
XOR ax, ax ; initialise stack pointer
MOV ss, ax ; lives in the bottom segment
MOV sp, MON_STACK_TOP
MOV es, ax ; es also points to bottom segment
STI

; Print a welcome message.
MOV si, M_BANNER
CALL Puts

; Relocate the IDT and GDT.
;
CLD ; want things counting UP
MOV si, IDT_BASE ; interrupt descriptor table
MOV di, IDT_RELOC
MOV cx, (GDT_END - GDT_BASE + 256) >> 2
REP MOVSD

; Relocate code from RELOC_START through RELOC_END.
;
MOV si, RELOC_START
MOV di, MON_RELOC
MOV cx, (RELOC_END - RELOC_START) >> 2
REP MOVSD

; Restart at the relocated code.
;
JMP MON_RELOC>>4:RELOC_GO

; Come here after relocation.
RELOC_GO:
PUSH cs ; load new data segment base
POP ax
MOV ds, ax

; Set up for protected mode (segmented, pseudo-flat memory
; model). GDT is set up with one code segment and one data
; segment, overlapping.
;
; CALL EnableA20 ; Enable A20 address line.
; CALL InhibitNMI ; Inhibit NMI (in hardware!)
CLI
;
; Load the GDT descriptor
LGDT [GDT_DESCRIPTOR]

;
; Load the Interrupt Descriptor Table.
; LIDT [IDT_DESCRIPTOR]
;
; Enter protected mode by setting the PE bit in CR0.
MOV eax, cr0
OR eax, 1
MOV cr0, eax
;
; Far jump to set CS to point to the correct segment.
; Note that segment references are now offsets in the GDT.
; The code segment is therefore at offset 08H
; (immediately after the NULL segment).
;
JMP 08H:MON_RELOC+START_PROT

[BITS 32]
; From here we're in protected mode.
START_PROT:
MOV ax, 10H ; Save data segment identifier (offset of code segment
in GDT).
MOV ds, ax ; Initialise a valid data segment
MOV es, ax
MOV fs, ax
MOV gs, ax
MOV ss, ax ; and stack segment.
MOV esp, 090000H ; Set the stack pointer.
MOV BYTE[0B8000H], 'Z' ; Put something on the screen.
MOV BYTE[0B8001H], 1BH
JMP $

; Go back to real mode.
; Need to preload segment selectors as appropriate.
;
; Clear the PE bit.
MOV eax, cr0
AND eax, 0FFFFFFFEH
MOV cr0, eax
;
; Do a far jump back into the monitor code.
JMP MON_RELOC>>4:START_REAL

[BITS 16]
START_REAL:
PUSH cs
POP ax
MOV ds, ax
XOR ax, ax
MOV ss, ax
MOV sp, MON_STACK_TOP
;CALL DisableA20

STI

; Print a welcome message.
MOV si, M_BANNER
CALL Puts
HANG: JMP HANG


I also tried Virtual PC and VMware server as suggested, but they don't
boot off of USB, so the next job is creating a boot floppy I suppose.

Thanks very much again.

Mark.

Reply With Quote
  #8  
Old 08-05-2008, 09:07 AM
Frank Kotler
Guest
 
Default Re: Problem switching to protected mode

Mark Brown wrote:
> I got it working finally, at least as far as the switch to PM. The
> switch back to RM is the next challenge.


You might find this info from Bob Smith helpful...

http://www.sudleyplace.com/pmtorm.html

Best,
Frank

Reply With Quote
  #9  
Old 08-07-2008, 07:32 AM
Mark Brown
Guest
 
Default Re: Problem switching to protected mode

Sorry, new problem. I manage to switch back to real mode (setting a 16-
bit segment for code and data). However, after switching back to RM,
even though the segment selector should be 16-bit, the CPU appears to
take 32-bit operands as default, and I have to put an 0x66H before
some instructions to get them to be interpreted as 16-bit.

This I discovered with by using Bochs in debug mode and single-
stepping the program.

Any ideas?

Cheers,
Mark.

Reply With Quote
  #10  
Old 08-07-2008, 06:22 PM
H. Peter Anvin
Guest
 
Default Re: Problem switching to protected mode

Mark Brown wrote:
> ;
> ; Far jump to set CS to point to the correct segment.
> ; Note that segment references are now offsets in the GDT.
> ; The code segment is therefore at offset 08H
> ; (immediately after the NULL segment).
> ;
> JMP 08H:MON_RELOC+START_PROT
>


You need a DWORD qualifier here:

JMP 08HWORD MON_RELOC+START_PROT

Otherwise your address (which will look like 0x0003XXXX) will be
truncated to 16 bits.

-hpa

Reply With Quote
Reply


Thread Tools
Display Modes


All times are GMT -5. The time now is 02:30 AM.


Powered by vBulletin® Version 3.7.2
Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO 3.2.0
vB Ad Management by =RedTyger=

In an effort to better serve ads to our visitors, cookies are used on objectmix.com. For more information, check out our Privacy Policy.