Page 1 of 2

SOLVED Crash when entering protected mode BUT in Bochs/QEMU

Posted: Tue Apr 30, 2013 6:50 pm
by cnlohr
My problem was that I did not put a $ sign in front of the constant I was oring with eax when entering protected mode. I was not properly setting the CR0 register. Much thanks to everyone for helping me learn everything else, and bringing myself down to copying and pasting the Intel code.

I have need to make a very small bootable x86 program (needs to fit in ~12kB). I faired very well in the emulators, both QEMU and BOCHS had no problem with anything I was doing. However, when I went and put it on a pen drive, the code chirps along happily until it enters protected mode on CR0. I can verify that by the characters it prints to the screen.

It does this on two different systems I've checked. All real systems don't work, QEMU and BOCHS don't even give me warnings.

About the only thing I figure I might be doing wrong is I only set up a GDT, and it's flat.

This doesn't seem to be a problem I can find anyone else having :-/

Code: Select all

/*
	Copyright 2009 Charles Lohr under the MIT/X11 License.
	This code is a bootloader and switches to flat 4G code, 4G data protected mode.
	It then calls C code. 
*/

//Useful article: http://thiscouldbebetter.wordpress.com/2011/03/17/entering-protected-mode-from-assembly

	.code16
	.global loader

//Code runs from 0x7c00
.align 4
.org 0x00
bootstart:

	//Configure the stack
	mov $0xc000, %sp

	//Configure the segments
	mov $0x0000, %ax
	mov %ax, %ds
	mov %ax, %es
	mov %ax, %fs
	mov %ax, %gs
	mov %ax, %ss

	//First, preserve drive letter.
	push %dx

	//Print out the letter 1, so we know it's working.
	mov $0x0e31, %ax
	mov $0x00, %bx
	int $0x10

**EDIT (but no change)**
	//Enable a20
	mov $0x2401, %ax
	int $0x15

	//Verify a20 was set.
	mov %ah, %al
	mov $0x0e, %ah
	add $0x30, %al
	mov $0x00, %ebx
	int $0x10
**END EDIT**

	//Just in case we have a dumb bios that set our code segment.
	ljmp $0x00, $continuestart
continuestart:

	//Copy over first few sectors of hard disk into ram.
	mov %al, %dl
	mov $0x0208, %ax //# of sectors to read.
	mov $0x0002, %cx //Track = 0, sector = 2
	pop %dx
	mov $0x00, %dh //Head = 0, Drive = (whatever drive we started with)
	mov $0x0000, %bx
	mov %bx, %es //Segment of output
	mov $0x7e00, %bx //Pointer of output
	int $0x13

	mov %ah, %al
	mov $0x0e, %ah
	add $0x30, %al
	mov $0x00, %ebx
	int $0x10

	//Setup our real big GDT
	cli
	pusha
	lgdt (GDT_ptr)
	popa

	//Enter protected mode
	mov %cr0, %eax
	or 1, %eax   [b]NEEDS TO BE %1[/B]
	mov %eax, %cr0

	//Jump to correct data selector, and enter 32-bit mode

*edit* If I put a hlt here, it halts.

	jmpl $0x08, $later2 
later2:

*edit* If I put a hlt here, it does not halt.


Here's my GDT:

Code: Select all


//GDT, Flat 4GiB
//http://wiki.osdev.org/GDT_Tutorial
GDT:

	.long 0x00000000
	.long 0x00000000

	//Descriptor (0x9a, RW, Execute) (Code32)
	.long 0x0000ffff
	.long 0x00CF9A00

	//Descriptor  (0x92, RW)  (Data32)
	.long 0x0000ffff
	.long 0x00cf9200

	//Code16
	.long 0x0000ffff
	.long 0x000f9a00

	//Data16
	.long 0x0000ffff
	.long 0x000f9200

GDT_end:

GDT_ptr:
	.word GDT_end - GDT // For limit storage
	.long GDT // For base storage
Any ideas? Any way I can make bochs do super verbose output? Do I /need/ an IDT? Why would the behavior be so different on a real system?

Re: Crash when entering protected mode (But NOT in Bochs/QEM

Posted: Tue Apr 30, 2013 10:57 pm
by xenos
This does not really make sense:

Code: Select all

//Code runs from 0x7c00
.align 4
.org 0x00
Indeed your code runs from 0x7c00 - but you set your .org to 0x00... It should be 0x7c00. Otherwise the jump into protected mode will get you to some bogus address, and very likely cause a crash / triple fault / reboot.

Re: Crash when entering protected mode (But NOT in Bochs/QEM

Posted: Wed May 01, 2013 3:54 am
by osdog
XenOS wrote:Otherwise the jump into protected mode will get you to some bogus address, and very likely cause a crash / triple fault / reboot.
In fact even the first jump to reload CS should go bad ... unless of course the .org is overridden by some magic linker option my crystal ball has not detected.

Re: Crash when entering protected mode (But NOT in Bochs/QEM

Posted: Wed May 01, 2013 9:15 am
by cnlohr
It is overridden. The linker script gives the absolute base address. It is 0x7c00. If I alter the .org here, it will alter it absolutely in the binary. Jumps, pointers, etc. definitely work.

Re: Crash when entering protected mode (But NOT in Bochs/QEM

Posted: Wed May 01, 2013 11:40 am
by osdog
cnlohr wrote:

Code: Select all

[...]
GDT_end:

GDT_ptr:
	.word GDT_end - GDT - 1 // For limit storage
	.long GDT // For base storage
That should do it. The size of the GDTD is the size of the GDT in octets minus 1.

Re: Crash when entering protected mode (But NOT in Bochs/QEM

Posted: Wed May 01, 2013 12:17 pm
by cnlohr
Already tried, no change in behavior. It now reads:

Code: Select all

.align 8

//GDT, Flat 4GiB
//http://wiki.osdev.org/GDT_Tutorial
GDT:
	.long 0x00000000
	.long 0x00000000

	//Descriptor (0x9a, RW, Execute) (Code32)
	.long 0x0000ffff
	.long 0x00CF9800

	//Descriptor  (0x92, RW)  (Data32)
	.long 0x0000ffff
	.long 0x00cf9200

	//Code16
	.long 0x0000ffff
	.long 0x000f9a00

	//Data16
	.long 0x0000ffff
	.long 0x000f9200

GDT_end:

.align 8
GDT_ptr:
     .word GDT_end - GDT - 1// For limit storage
     .long GDT // For base storage
Could it be that the BIOS expects some other information to be on the disk? That I am using the area at the beginning for something other than descriptors?

Re: Crash when entering protected mode (But NOT in Bochs/QEM

Posted: Wed May 01, 2013 1:23 pm
by cnlohr
Here's the updated code, same behavior, though.

Code: Select all

/*
	Copyright 2009-2013 Charles Lohr under the MIT/X11 License.
	This code is a bootloader and switches to flat 4G code, 4G data protected mode.
	It then calls C code. 
*/

//Useful article: http://thiscouldbebetter.wordpress.com/2011/03/17/entering-protected-mode-from-assembly

	.code16
	.global loader

//Code runs from 0x7c00
.align 4
.org 0x00
bootstart:
	//Configure the stack
	mov $0xc000, %sp

	//Configure the segments
	mov $0x0000, %ax
	mov %ax, %ds
	mov %ax, %es
	mov %ax, %fs
	mov %ax, %gs
	mov %ax, %ss

	//First, preserve drive letter.
	push %dx

	//Print out the letter 1, so we know it's working.
	mov $0x0e31, %ax
	mov $0x00, %bx
	int $0x10


	//Just in case we have a dumb bios that set our code segment.
	ljmp $0x00, $continuestart
continuestart:

	//Copy over first few sectors of hard disk into ram.
	mov %al, %dl
	mov $0x0208, %ax //# of sectors to read.
	mov $0x0002, %cx //Track = 0, sector = 2
	pop %dx
	mov $0x00, %dh //Head = 0, Drive = (whatever drive we started with)
	mov $0x0000, %bx
	mov %bx, %es //Segment of output
	mov $0x7e00, %bx //Pointer of output
	int $0x13

	push %ax

	mov %ah, %al
	mov $0x0e, %ah  //Should read 0 to say everything was fine with the read call.
	add $0x30, %al
	mov $0x00, %ebx
	int $0x10

	pop %ax
	mov $0x0e, %ah
	add $0x30, %al
	mov $0x00, %ebx //should be the number of bytes read
	int $0x10

	mov $0x0e, %ah
	mov (0x7e02), %al //Should be lower case x
	mov $0x00, %ebx
	int $0x10

	//Setup our real big GDT
	lgdt (GDT_ptr)

	//I am certain this gets called. I can see the on-screen text.

	mov $0xb800, %ax
	mov %ax, %ds
	movb $0x91, (32)
	movb $0xf1, (33)  //prints a small 0x91 a couple of columns in.
	mov $0x0000, %ax
	mov %ax, %ds

	cli


	//CODE HERE WILL EXECUTE


	//Enter protected mode
	mov %cr0, %eax
	or 0x00000001, %eax
	mov %eax, %cr0

	//Jump to correct data selector, and enter 32-bit mode
	jmpl $0x08, $later2 
later2:
.code32

	//CODE HERE WILL NOT EXECUTE ON REAL SYSTEMS.

loopback:
	jmpl $0x08, $loopback

	//Setup data selectors
	mov $0x10, %ax
	mov %ax, %ds
	mov %ax, %es
	mov %ax, %fs
	mov %ax, %gs
	mov %ax, %ss

//This does not get called in real life, but does in the emulator.
	mov $0x3131, %ax
	mov $0xb8004, %edx
	movw %ax, (%edx)
cli

	//Enable the FPU
	mov %cr0, %eax
	and $0xfffffff3,%eax
	mov %eax, %cr0
	fninit

	jmpl $0x08, $main //$0x7e00 //Start of main program
.align 8

//GDT, Flat 4GiB
//http://wiki.osdev.org/GDT_Tutorial
GDT:
	.long 0x00000000
	.long 0x00000000

	//Descriptor (0x9a, RW, Execute) (Code32)
	.long 0x0000ffff
	.long 0x00CF9800

	//Descriptor  (0x92, RW)  (Data32)
	.long 0x0000ffff
	.long 0x00cf9200

	//Code16
	.long 0x0000ffff
	.long 0x000f9a00

	//Data16
	.long 0x0000ffff
	.long 0x000f9200

GDT_end:

.align 8
GDT_ptr:
	 .word GDT_end - GDT - 1// For limit storage
     .long GDT // For base storage

//Disk Signature
.org 0x1b8
	.long 0xefcdab89
	.short 0x0000
	//Disk entry 1.
	.byte 0x80 //Active partition 1 (and bootable)
	.byte 0x00
	.byte 0x02 //CHS address
	.byte 0x00
	.byte 0x56 //Partition type
	.byte 0x03 
	.byte 0x0b //CHS adderss of last sector
	.byte 0x00
	.long 0x01
	.long 0xc7 //Number of sectors in partition.



//Area for setting variables to call the interrupt
//We are putting this here since the values must be all zeroes anyway.
//In fact, we can put anything that's all zeroes on boot here.
.global CI32Params, ci32ax, ci32bx, ci32cx, ci32dx, ci32ss
.global ci32sp, ci32bp, ci32di, ci32si, ci32gs, ci32fs, ci32es, ci32ds


CI32Params:
ci32ax: .word 0x0000
ci32bx: .word 0x0000
ci32cx: .word 0x0000
ci32dx: .word 0x0000
ci32bp: .word 0x0000
ci32di: .word 0x0000
ci32si: .word 0x0000

ci32gs: .word 0x0000
ci32fs: .word 0x0000
ci32es: .word 0x0000
ci32ds: .word 0x0000


idt_prot:
	.word 0x0000
	.long 0x00000000

savecr0:
	.long 0x00000000

//still have about 70 bytes left between zero-init values and 0x55aa signature
//.long 0xCCCCCCCC

//MBR Signature
.org 0x1fe
	.byte 0x55
	.byte 0xaa

Re: Crash when entering protected mode (But NOT in Bochs/QEM

Posted: Wed May 01, 2013 1:38 pm
by osdog
We'll here is the procedure for switching to protected mode as per the Intel specification - your's looks a little different, so maybe try to follow the Intel Manual by the letter:

Code: Select all

[...]

/***********************
 * enter protected mode
 ***********************/
debug_enter_protected_mode:
	/* 9.9.1 Switching to Protected Mode [3] */
	/* 1. Disable interrupts. A CLI instruction disables maskable
	 * hardware interrupts. NMI interrupts can be disabled with external
	 * circuitry. (Software must guarantee that no exceptions or
	 * interrupts are generated during the mode switching operation.)
	 */
	cli
	inb $0x70, %al
	or $0x80, %al
	outb %al, $0x70
	
	/* 2. Execute the LGDT instruction to load the GDTR register
	 * with the base address of the GDT.
	 */
	lgdtl (gdtd)
	
	/* 3. Execute a MOV CR0 instruction that sets the PE flag (and
	 * optionally the PG flag) in control register CR0.
	 */
	mov %cr0, %eax
	or $0x1, %eax
	mov %eax, %cr0
	
	/* 4. Immediately following the MOV CR0 instruction, execute a far
	 * JMP or far CALL instruction. (This operation is typically a far
	 * jump or call to the next instruction in the instruction stream.)
	 */
	ljmpl $0x08, $(1f)
1:
	.code32
	/* 5. The JMP or CALL instruction immediately after the MOV CR0
	 * instruction changes the flow of execution and serializes the
	 * processor.
	 */
	// NOTE: pray change of flow of execution and serialization worked?
	
	/* 6. If paging is enabled, the code for the MOV CR0 instruction
	 * and the JMP or CALL instruction must come from a page that is
	 * identity mappe (that is, the linear address before the jump
	 * is the same as the physical address after paging and protected
	 * mode is enabled). The target instruction for the JMP or CALL
	 * instruction does not need to be identity mapped.
	 */
	// NOTE: we don't use paging
	
	/* 7. If a local descriptor table is going to be used, execute the
	 * LLDT instruction to load the segment selector for the LDT in the
	 * LDTR register.
	 */
	// NOTE: we don't use a ldt
	
	/* 8. Execute the LTR instruction to load the task register with a
	 * segment selector to the initial protected-mode task or to a
	 * writable area of memory that can be used to store TSS information
	 * on a task switch.
	 */
	// NOTE: we don't use tss
	
	/* 9. After entering protected mode, the segment registers continue
	 * to hold the contents they had in real-address mode. The JMP or
	 * CALL instruction in step 4 resets the CS register. Perform one
	 * of the following operations to update the contents of the remaining
	 * segment registers.
	 * - Reload segment registers DS, SS, ES, FS, and GS. If the ES,
	 *   FS, and/or GS registers are not going to be used, load them
	 *   with a null selector.
	 * - Perform a JMP or CALL instruction to a new task, which
	 *   automatically resets the values of the segment registers and
	 *   branches to a new code segment.
	 */
	mov $0x10, %ax
	mov %ax, %ds
	mov %ax, %es
	mov %ax, %fs
	mov %ax, %gs
	mov %ax, %ss
	
	/* 10. Execute the LIDT instruction to load the IDTR register with
	 * the address and limit of the protected-mode IDT.
	 */
	// NOTE: don't set an idt ... we are just a boot loader!
	
	/* 11. Execute the STI instruction to enable maskable hardware
	 * interrupts and perform the necessary hardware operation to
	 * enable NMI interrupts.
	 */
	// NOTE: don't enable interrupts ... kernel must do so after
	//       setting up an IDT
//	sti
//	inb $0x70, %al
//	and $~0x80, %al
//	outb %al, $0x70

[...]

/******
 * GTD
 ******/
. = 408
gdt:
	/* Figure 3-8. Segment Descriptor [3] */
	/* .byte limit[0:7],limit[8:15],base[0:7],base[8:15],
	 *       base[16:32], present[0]<<7|DPL[0:1]<<5|iscodedataseg[0]<<4|
	 *       type[0:3],
	 *       granularity[0]<<7|32bit[0]<<6|64bit[0]<<5|avail[0]<<4|
	 *       limit[16:19], base[24:31]
	 */
	/* null */
	.byte 0, 0, 0, 0, 0, 0, 0, 0
	
	/* protected mode */
	/* code 0x08 */
	.byte 0xff, 0xff, 0, 0, 0, 1<<7|1<<4|0xa, 1<<7|1<<6|0xf, 0x00
	/* data 0x10 */
	.byte 0xff, 0xff, 0, 0, 0, 1<<7|1<<4|0x2, 1<<7|1<<6|0xf, 0x00
	
gdt_end:

wasted_space:
	.word 0xdead

gdtd:
	.word gdt_end - gdt - 1
	.long gdt

/* References:
 * [3] Intel(R) 64 and IA-32 Architectures Software Developer’s Manual:
 *     Volume 3 (3A, 3B & 3C): System Programming Guide,
 *     Order Number: 325384-044US, August 2012
 */
Anyway I would also try to reduce your code to the bare minimum (no copying of sectors) to narrow down the problem.

Good luck.

Re: Crash when entering protected mode (But NOT in Bochs/QEM

Posted: Thu May 02, 2013 7:20 am
by JAAman
one thing looked funny to me (though not likely the problem) -- you appear to be setting SP before setting SS (quite a bit before, actually) -- since you cannot be certain where BIOS had your stack, you shouldn't mess with SP without also changing SS at the same time

and since interrupts don't appear to be disabled at this point (disabling interrupts is not necessary if you load stack correctly), you are actually likely to get an interrupt during this time, which would cause unpredictable behavior (meaning it may work on some computers, and crash unpredictably on others, and cause various random conditions which may cause untraceable errors and crashes at a later point in your code, or, more likely, during BIOS calls)

simply moving your set SP down immediately below where you set SS should fix this

Re: Crash when entering protected mode (But NOT in Bochs/QEM

Posted: Thu May 02, 2013 1:24 pm
by cnlohr
Ok, I've stripped it down - same behavior as always. On VM's it works as expected, on my PC it reboots when entering protected mode. @JAAman, I've tried swapping them. @osdog, I am now using the Intel table, and to no avail.

Here's my code:

Code: Select all

	.code16
	.global loader

//Code runs from 0x7c00
.align 4
.org 0x00
bootstart:

	//Configure the segments
	mov $0x0000, %ax
	mov %ax, %ds
	mov %ax, %es
	mov %ax, %fs
	mov %ax, %gs
	mov %ax, %ss
	//Configure the stack
	mov $0xc000, %sp

	mov $0x0e, %ah
	mov (0x7e02), %al //Should be lower case x
	mov $0x00, %ebx
	int $0x10

	cli
	//Disable NMI
	inb $0x70, %al
	or $0x80, %al
	outb %al, $0x70

	//Setup our real big GDT
	lgdt (GDT_ptr)

/*
	//I am certain this gets called. I can see the on-screen text. Both real and emulated.
	mov $0xb800, %ax
	mov %ax, %ds
	movb $0x91, (32)
	movb $0xf1, (33)  //prints a small 0x91 a couple of columns in.
	mov $0x0000, %ax
	mov %ax, %ds
*/

	//Enter protected mode
	mov %cr0, %eax
	or 0x00000001, %eax
	mov %eax, %cr0

	//Jump to correct data selector, and enter 32-bit mode
	jmpl $0x08, $(1f)
.code32
1:

	//On the VM this pauses the system.  On real machines, this causes a reboot.

	xor %eax, %eax
	test %eax, %eax
test:
	jz test

.org 408
.align 8

//GDT, Flat 4GiB
//http://wiki.osdev.org/GDT_Tutorial
GDT:

   //Copied from the Intel one.
   .byte 0, 0, 0, 0, 0, 0, 0, 0
   
   /* protected mode */
   /* code 0x08 */
   .byte 0xff, 0xff, 0, 0, 0, 1<<7|1<<4|0xa, 1<<7|1<<6|0xf, 0x00
   /* data 0x10 */
   .byte 0xff, 0xff, 0, 0, 0, 1<<7|1<<4|0x2, 1<<7|1<<6|0xf, 0x00

GDT_end:

.align 8
GDT_ptr:
	 .word GDT_end - GDT - 1// For limit storage
     .long GDT // For base storage

//Disk Signature
.org 0x1b8
	.long 0xefcdab89
	.short 0x0000
	//Disk entry 1.
	.byte 0x80 //Active partition 1 (and bootable)
	.byte 0x00
	.byte 0x02 //CHS address
	.byte 0x00
	.byte 0x56 //Partition type
	.byte 0x03 
	.byte 0x0b //CHS adderss of last sector
	.byte 0x00
	.long 0x01
	.long 0xc7 //Number of sectors in partition.

//MBR Signature
.org 0x1fe
	.byte 0x55
	.byte 0xaa
Here's my hex:

Code: Select all

00000000  b8 00 00 8e d8 8e c0 8e  e0 8e e8 8e d0 bc 00 c0  |................|
00000010  b4 0e a0 02 7e 66 bb 00  00 00 00 cd 10 fa e4 70  |....~f.........p|
00000020  0c 80 e6 70 0f 01 16 b0  7d 0f 20 c0 66 0b 06 01  |...p....}. .f...|
00000030  00 0f 22 c0 66 ea 3c 7c  00 00 08 00 31 c0 85 c0  |..".f.<|....1...|
00000040  74 fe 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |t...............|
00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000001a0  ff ff 00 00 00 9a cf 00  ff ff 00 00 00 92 cf 00  |................|
000001b0  17 00 98 7d 00 00 00 00  89 ab cd ef 00 00 80 00  |...}............|
000001c0  02 00 56 03 0b 00 01 00  00 00 c7 00 00 00 00 00  |..V.............|
000001d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000001f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 55 aa  |..............U.|
My small concern is with the lgdt command, is this correct? The opcode generated for it looks bizarre.

Code: Select all

  38                            //Setup our real big GDT
  39 0024 0F0116B0              lgdt (GDT_ptr)
  39      01
I can't seem to find a good description of what the opcodes for this command should look like.

Regardless... it works in an emulator and not on real PCs.

Re: Crash when entering protected mode (But NOT in Bochs/QEM

Posted: Thu May 02, 2013 8:41 pm
by JAAman
My small concern is with the lgdt command, is this correct? The opcode generated for it looks bizarre.

I can't seem to find a good description of what the opcodes for this command should look like.
should be: LGDT = 0F 01 mod 010 r/m
so yours appears to be encoded as 0F 01 16 B0 7D:
0F 01 sub-code=010 = LGDT opcode

mod = 00, r/m = 110 -- this combination means immediate 16-bit address follows the instruction
B0 7D == 7DB0 == (beginning of sector)+1B0 (0x40 short of end-of-sector)



the best place for information on encoding would be intel manuals in volume 2B: appendix B (specifically B2, for a table of all non-64bit encodings) and volume 2A: chapter2 (specifically 2.1.5) for mod and r/m encodings (note according to the table in 2B, mod=11 is reserved)

Re: Crash when entering protected mode (But NOT in Bochs/QEM

Posted: Fri May 03, 2013 5:01 am
by osdog
cnlohr wrote:@osdog, I am now using the Intel table, and to no avail.
Careful the code I posted isn't straight from Intel. They haven't published any code afaik. I only posted code (I found) that I use for my bootup process and the comments seem to be taken straight from the Intel manual and the code seems to follow them by the letter.
cnlohr wrote: My small concern is with the lgdt command, is this correct? The opcode generated for it looks bizarre.
Good question. That really depends on your assembler and possible also disassembler you are investigating it with ... most tools can't handle 16bit code let alone 16bit code mixed with 32bit code well.

Anyway the code I posted uses lgdtl so maybe try that. lgdt will use default op size (in this case 16bit, which should be fine regardless) while lgdtl explicitly overrides the op size to be 32bit. Maybe that's the hardwares problem.
Also the jmp. Try making it a ljmpl. I've has problems with using simply jmpl before.

But other than that I have no idea.

Re: Crash when entering protected mode (But NOT in Bochs/QEM

Posted: Fri May 03, 2013 6:35 am
by JAAman
as for your particular problem, usually these sort of problems (working on emulators but not certain real machines) come from making assumptions about the start-up state, however i don't see that here... your code looks right to me, but i didn't spend much time with it and I'm not used to AT&T syntax, so i could be missing something

Re: Crash when entering protected mode (But NOT in Bochs/QEM

Posted: Fri May 03, 2013 9:28 am
by Combuster
Well, then this would be a good moment to post all the code, scripts, and build instructions you used to perform the test on a real machine. It'd at least give us the opportunity to test it ourselves.

Re: Crash when entering protected mode (But NOT in Bochs/QEM

Posted: Fri May 03, 2013 10:22 am
by cnlohr
OH MY GOSH I am dumb. This is what was wrong:

Code: Select all

   mov %cr0, %eax
   or 0x00000001, %eax
   mov %eax, %cr0
It needed to be $0x00000001, not 0x00000001

I learned a lot, though. Thanks, everyone! Everything is cooking along!