Setting protected mode fails on some PCs

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
DrYap
Posts: 4
Joined: Thu Jul 29, 2010 1:54 am

Setting protected mode fails on some PCs

Post by DrYap »

I've created a bootloader that can successfully load a kernel with A20, protected mode and all the rest. On VirtualBox and my laptop there is no problem, but when I try it on my PC it triple faults (at least it restarts) after setting the protected mode bit in CR0. I have followed the execution to confirm this is the source of the problem.

Code: Select all

;------------------------------------------------------------------------------
; Load GDT.
;------------------------------------------------------------------------------
	lgdt	[GDT_DESC]		; Load GDT

; GETS TO HERE FINE

;------------------------------------------------------------------------------
; Protected mode.
;------------------------------------------------------------------------------
	mov	eax, cr0		; Copy CR0 into EAX
	or	eax, 1			; Set bit 0
	mov	cr0, eax		; Update CR0

;!!!!!!!!!!!!!!HERE IS THE PROBLEM!!!!!!!!!!!!!!!!!!!!!!!!!!!!
; IT NEVER REACHES HERE

	jmp	0x08:ClearPipe		; Setup registers
Please let me know if you need to see any more code.
Hangin10
Member
Member
Posts: 162
Joined: Wed Feb 27, 2008 12:40 am

Re: Setting protected mode fails on some PCs

Post by Hangin10 »

Maybe it is failing to load a valid GDT. What's the value of DS on the system that fails?
User avatar
quanganht
Member
Member
Posts: 301
Joined: Fri May 16, 2008 7:13 pm
Location: Hanoi, Vietnam

Re: Setting protected mode fails on some PCs

Post by quanganht »

How do you know that the code never reach that specific point? My suggestion is to insert a piece of printing code, to find out exactly where your code stops functioning properly.
"Programmers are tools for converting caffeine into code."
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Setting protected mode fails on some PCs

Post by Brendan »

Hi,
DrYap wrote:Please let me know if you need to see any more code.
I'd need to see more code.

There's lots of reasons for code to behave differently on different computers/emulators - different initial values in registers (e.g. DS, SS and even CS), different memory maps, different memory contents (e.g. accidentally using uninitialised memory), etc. It could even be something like assuming sectors were loaded when they weren't (e.g. not checking for errors).


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
DrYap
Posts: 4
Joined: Thu Jul 29, 2010 1:54 am

Re: Setting protected mode fails on some PCs

Post by DrYap »

As far as I know, the DS is 0 when it crashes set from the "Setup registers and environment" section of the code below.

I have done the stepping through printing to the screen to confirm where the problem arises.

Here is the code before hand, there are probably too many comments but it should make sense.

Code: Select all

[BITS 16]
[ORG 0x7C00]

;==============================================================================
;===========================|-Setup Environment-|==============================
;==============================================================================

;------------------------------------------------------------------------------
; Setup registers and environment.
;------------------------------------------------------------------------------
Start:
	cli				; Stop interupts
	cld				; Clear direction flag

	xor	bx, bx			; Zero BX
	mov	ds, bx			; Zero data segment
	mov	ss, bx			; Zero stack segment
	mov	sp, 0x7000		; Temporary stack pointer

;------------------------------------------------------------------------------
; Read kernel into memory. RELIES ON BX BEING 0 FROM ABOVE.
;------------------------------------------------------------------------------
ReadKernel:
	xor	si, si			; Reset attempts counter
	mov	ax, 0xFFFF		; Set where to load kernel
	mov	es, ax			; At 1MB
	mov	bx, 0x10		; Offset 0x10 to get to 1MB
	mov	cx, 0x0002		; Cylinder 0, Sector 1
	xor	dh, dh			; Head 0
ReadKernel_read:
	cmp	si, 3			; Check if 3 attemps have been used
	je	ReadKernelFailed	; Go to failure message
	inc	si			; Increment attempts
	mov	ax, 0x0210		; Function 2, 16 sectors
	int	0x13			; Call interrupt
	cmp	ah, 0			; See if it worked
	jne	ReadKernel_read		; Loop if it didn't

;------------------------------------------------------------------------------
; Move GDT to correct location in RAM.
;------------------------------------------------------------------------------
MoveGDT:
	xor	ax, ax
	mov	ds, ax			; Set to source segment
	mov	si, GDT			; Offset to GDT
	mov	es, ax			; Set destination segment
	mov	di, 0x500		; Offset to new GDT
	mov	cx, 0x30>>2		; GDT size / 4
	rep				; Repeat next instruction cx times
	movsd

;------------------------------------------------------------------------------
; Enable A20-Line.
;------------------------------------------------------------------------------
	xor	si, si			; Reset attempt counter
EnableA20:
	cmp	si, 3			; Cheack for 3 failed attempts
	je	EnableA20Failed		; Show error message on failure
	inc	si			; Increment attempt counter
	call	CheckA20		; Get A20 status
	jnz	A20Enabled		; Done if already set
	call	WaitA20			; Wait for controller
	mov	al, 0xD1		; Write command
	out	0x64, al		; Send to port
	call	WaitA20			; Wait for controller
	mov	al, bl			; Get status
	or	al, 2			; Set bit 1
	out	0x60, al		; Send to port
	call	CheckA20		; Get A20 status
	jz	EnableA20		; Try again if failed
A20Enabled:

;------------------------------------------------------------------------------
; Load GDT.
;------------------------------------------------------------------------------
	lgdt	[GDT_DESC]		; Load GDT

;------------------------------------------------------------------------------
; Protected mode.
;------------------------------------------------------------------------------
	mov	eax, cr0		; Copy CR0 into EAX
	or	eax, 1			; Set bit 0
	mov	cr0, eax		; Update CR0

	jmp	0x08:ClearPipe		; Setup registers
Thanks for your help.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Setting protected mode fails on some PCs

Post by Brendan »

Hi,

Some notes....

Some BIOSs (typically in old Compaq machines) don't use the "bootable marker" (the 0xAA55 at offset 0x1FE) and instead check to make sure the first instruction is a JMP to determine if the sector is bootable or not.

For floppy disks you should have a BPB to describe the format the floppy is in. Without this it will work fine, until someone attempts to use a crappy OS (like Windows) to re-format the disk later (and gets an error from Windows about a corrupt disk it can't format). The first instruction would need to be a JMP to jump over the BPB.

You forgot to do a STI after setting up the temporary stack (the "mov sp, 0x7000" near the start). The BIOS probably enables interrupts during "int 0x13" (how else can the BIOS expect to get the "sectors loaded" IRQ from a hard disk controller; or determine if there's been a time-out when the PIT IRQ is disabled?).

DO NOT ask the BIOS to load data above 0x00100000. With A20 still disabled the BIOS will happily trash the IVT and trash its own data (in the BDA), then maybe even trash your GDT then trash your boot code. Even if you enabled A20 before this, it'd still be highly dodgy. Read the data into a buffer below 0x00100000, then copy it above 0x00100000 if you need to. For example, I use a buffer that's large enough for one track, and (in a loop) read a track, switch to protected mode, copy that track above 0x00100000, switch back to real mode, etc (until all data is loaded). This also works when the data being loaded (kernel?) is larger than 64 KiB.

You don't have a way to determine how large the data being loaded (kernel?) is. This is bad design - better to have a header or something that the boot code uses to determine exactly how many sectors it needs to read (so you can increase the size of the kernel without also needing to modify the boot code to suit).

I'd be "more thorough" when attempting to enable A20. First, check if it's already enabled. Then attempt to use the BIOS function. Only do it manually if you must (because doing it manually is much less reliable).

I don't know why you need to copy the GDT (and can't just use it where it is). I also don't know what the GDT contains or what "GDT_DESC" actually is either.

You forgot to disable interrupts before switching to protected mode. If the BIOS did the right thing and enabled interrupts earlier, then you can expect it to crash as soon as the first IRQ occurs.

As far as I can tell, there's very few error messages. If you fail to read the sectors then it'd be nice if you could tell the user why (e.g. use the BIOS error code to find an error message, saying if there's was a time-out or a read error or whatever). I'd also check to see if the CPU is an 80386 or later before attempting to use 32-bit instructions (e.g. "rep movsd"), and display a "This CPU is too old" error message if it's not. I also put a CRC in the header of all my files and check that the CRC is correct before relying on the file too (e.g. "Kernel CRC check failed. Boot aborted.").

It's very likely that after you fix the problems (and maybe after you decide to setup a video mode while you're still in real mode) that it won't all fit in 512 bytes. The first 512 bytes of the boot loader should probably load the remaining sectors of the boot loader, and those remaining sectors should contain the code to setup video, load the kernel, check the kernel, enable protected mode, etc.


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Re: Setting protected mode fails on some PCs

Post by Solar »

DrYap wrote:...there are probably too many comments but it should make sense.
No, and yes.

There can be stupid comments, wrong comments, or undecipherable comments.

There are never "too many" comments.

Congrats for one of the few pieces of ASM I've seen so far that didn't feel like a migraine attack. =D>

As for your original problem, I'm afraid I have nothing to add to Brendan's analysis.
Every good solution is obvious once you've found it.
Casm
Member
Member
Posts: 221
Joined: Sun Oct 17, 2010 2:21 pm
Location: United Kingdom

Re: Setting protected mode fails on some PCs

Post by Casm »

Brendan wrote:You forgot to do a STI after setting up the temporary stack (the "mov sp, 0x7000" near the start). The BIOS probably enables interrupts during "int 0x13" (how else can the BIOS expect to get the "sectors loaded" IRQ from a hard disk controller; or determine if there's been a time-out when the PIT IRQ is disabled?).
Even if the BIOS enabled interrupts, the flag would be returned to its original state when an IRET popped the flags pushed by an INT.

I can't see an LIDT anywhere, so there is a good chance it would crash anyway, as soon as you enabled interrupts after switching to protected mode.

Normally the processor can't address memory above 1mb when it is in real mode. If you want to read data directly into memory above 1mb, google "unreal mode" (not officially documented by Intel or AMD) - and make sure A20 is enabled before trying to address memory above 1mb.
DrYap
Posts: 4
Joined: Thu Jul 29, 2010 1:54 am

Re: Setting protected mode fails on some PCs

Post by DrYap »

Thanks for the help.

Do you have any suggestion for keeping track of the kernel size automatically? I'm using gcc and NASM.
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Re: Setting protected mode fails on some PCs

Post by Solar »

DrYap wrote:Do you have any suggestion for keeping track of the kernel size automatically?
The size of the kernel binary can be evaluated rather easily with the help of the linker script.
Every good solution is obvious once you've found it.
CelestialMechanic
Member
Member
Posts: 52
Joined: Mon Oct 11, 2010 11:37 pm
Location: Milwaukee, Wisconsin

Re: Setting protected mode fails on some PCs

Post by CelestialMechanic »

You are loading the 16 sectors of kernel to the 1 MB mark before you enable A20. Big mistake. It is still wrapping around and the 16 sectors you load are sufficient to write over the IVT and the BDA as Brendan mentioned. Enable A20 before reading from the disk and it should work. (Maybe.)
Microsoft is over if you want it.
Post Reply