OSDev.org https://forum.osdev.org/ |
|
Trouble moving into protected mode. https://forum.osdev.org/viewtopic.php?f=1&t=31434 |
Page 1 of 1 |
Author: | Izzette [ Fri Mar 17, 2017 10:07 pm ] |
Post subject: | Trouble moving into protected mode. |
I've written (most) of this simple MBR bootstrap/bootloader. I've gotten up to the point where it load's up to 127 sectors of the first DOS partition with the boot flag set into memory. I've double-triple checked using the QEMU monitor command "pmemsave" that it loads the partition into memory. I've just "dd"ed my kernel right onto the raw partition. Now I'm looking to enter protected mode, and exec my kernel. However, when I far jump to my kernels "_start" address at 0x9000 at a 4K offset from the start of my ELF (which I load into 0x8000), it doesn't seem to be jumping to the correct location, just executing some random somewhere else in memory. I'm using GDB to debug, and I switch architectures to i386 just after I call "kexec". Here is a link to my full bootloader code, https://github.com/Izzette/izixboot/blob/master/boot.s (GAS syntax). At the very end of the file I've got my protected mode code: Code: // More code up here ... // The GDT registry .set gdtr, 0x0500 // Kernel entry point .set kentryseg, 0x08 .set kentryoffset, 0x8f80 // We're moving into 32-bit protected mode. .code32 // Enter protected mode and execute the kernel. // void kexec () { kexec: // There's no coming back from here, // so forget about saving the base pointer. // push %bp mov %sp, %bp // Load the GDT registry. lgdt gdtr // set PE (Protection Enable) bit in CR0 (Control Register 0) mov %cr0, %eax or $1, %eax mov %eax, %cr0 ljmp $kentryseg, $kentryoffset // This function should never return, // so forget about restoring the stack and base pointers. // mov %bp, %sp // pop %bp // ret // } Right at the "ljmp" instruction, things go haywire: Code: The target architecture is assumed to be i8086 Breakpoint 1 at 0x7c00 Breakpoint 1, 0x00007c00 in ?? () => 0x00007c00: fa cli (gdb) b *(0x7e00 + 0x160) Breakpoint 2 at 0x7f60 (gdb) c Continuing. Breakpoint 2, 0x00007f60 in ?? () => 0x00007f60: 66 89 e5 mov %esp,%ebp (gdb) si 0x00007f63 in ?? () => 0x00007f63: 0f 01 15 lgdtw (%di) (gdb) 0x00007f66 in ?? () => 0x00007f66: 00 05 add %al,(%di) (gdb) 0x00007f68 in ?? () => 0x00007f68: 00 00 add %al,(%bx,%si) (gdb) 0x00007f6a in ?? () => 0x00007f6a: 0f 20 c0 mov %cr0,%eax (gdb) set architecture i386 The target architecture is assumed to be i386 (gdb) si 0x00007f6d in ?? () => 0x00007f6d: 83 c8 01 or $0x1,%eax (gdb) si 0x00007f70 in ?? () => 0x00007f70: 0f 22 c0 mov %eax,%cr0 (gdb) si 0x00007f73 in ?? () => 0x00007f73: ea 80 8f 00 00 08 00 ljmp $0x8,$0x8f80 (gdb) si 0x0000e05b in ?? () => 0x0000e05b: 12 16 adc (%esi),%dl (gdb) si 0x0000e05b in ?? () => 0x0000e05b: 12 16 adc (%esi),%dl (gdb) info registers eax 0x0 0 ecx 0x0 0 edx 0x663 1635 ebx 0x0 0 esp 0x0 0x0 ebp 0x0 0x0 esi 0x0 0 edi 0x0 0 eip 0xe05b 0xe05b eflags 0x2 [ ] cs 0xf000 61440 ss 0x0 0 ds 0x0 0 es 0x0 0 fs 0x0 0 gs 0x0 0 (gdb) QEMU shows that "cr0" does not have the lowest order bit set. Code: QEMU 2.8.0 monitor - type 'help' for more information (qemu) info registers EAX=00000000 EBX=00000000 ECX=00000000 EDX=00000663 ESI=00000000 EDI=00000000 EBP=00000000 ESP=00000000 EIP=0000e05b EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0 ES =0000 00000000 0000ffff 00009300 CS =f000 000f0000 0000ffff 00009b00 SS =0000 00000000 0000ffff 00009300 DS =0000 00000000 0000ffff 00009300 FS =0000 00000000 0000ffff 00009300 GS =0000 00000000 0000ffff 00009300 LDT=0000 00000000 0000ffff 00008200 TR =0000 00000000 0000ffff 00008b00 GDT= 00000000 0000ffff IDT= 00000000 0000ffff CR0=60000010 CR2=00000000 CR3=00000000 CR4=00000000 DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 DR6=00000000ffff0ff0 DR7=0000000000000400 EFER=0000000000000000 FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80 FPR0=0000000000000000 0000 FPR1=0000000000000000 0000 FPR2=0000000000000000 0000 FPR3=0000000000000000 0000 FPR4=0000000000000000 0000 FPR5=0000000000000000 0000 FPR6=0000000000000000 0000 FPR7=0000000000000000 0000 XMM00=00000000000000000000000000000000 XMM01=00000000000000000000000000000000 XMM02=00000000000000000000000000000000 XMM03=00000000000000000000000000000000 XMM04=00000000000000000000000000000000 XMM05=00000000000000000000000000000000 XMM06=00000000000000000000000000000000 XMM07=00000000000000000000000000000000 (qemu) Here is my kernel "_start", not that I think it matters. Code: .code32 .section .text .global _start .type _start, @function _start: mov $0x10, %ax mov %ax, %ds mov %ax, %es mov %ax, %fs mov %ax, %gs mov %ax, %ss mov $0x8000, %esp mov %esp, %ebp call kernel_main cli freeze: hlt jmp freeze .size _start, . - _start Clearly, I'm not understanding segmentation correctly? Can anyone kindly point me in the right direction? |
Author: | SpyderTL [ Fri Mar 17, 2017 10:19 pm ] |
Post subject: | Re: Trouble moving into protected mode. |
It looks like you are telling the compiler that you want it to write out 32-bit code BEFORE you are setting the CR0 register to enable 32-bit mode on the CPU. Setting up the GDT and setting the CR0 register needs to happen while still in 16-bit mode. Then, jump to a label that is after your .code32 line. Let us know if it solves your problem. |
Author: | Izzette [ Fri Mar 17, 2017 10:24 pm ] |
Post subject: | Re: Trouble moving into protected mode. |
I applied this diff, but nada, I still end up at "0x0000e05b", and my registers haven't changed. Code: diff --git a/boot.s b/boot.s
index 3796d0c..faac226 100644 --- a/boot.s +++ b/boot.s @@ -680,9 +680,6 @@ boot: // ret // } -// We're moving into 32-bit protected mode. -.code32 - // Enter protected mode and execute the kernel. // void kexec () { kexec: @@ -699,6 +696,12 @@ kexec: or $1, %eax mov %eax, %cr0 +// Do I really need to jump? + jmp .Lkexec_ljmp + +// We're moving into 32-bit protected mode. +.code32 +.Lkexec_ljmp: ljmp $kentryseg, $kentryoffset // This function should never return, |
Author: | SpyderTL [ Fri Mar 17, 2017 10:42 pm ] |
Post subject: | Re: Trouble moving into protected mode. |
Yes, you must jump into 32-bit code. And it must be a far jump into a segment/selector that is defined as a code segment in your GDT. (Most likely, 0x08 or 0x10). |
Author: | Izzette [ Fri Mar 17, 2017 10:55 pm ] |
Post subject: | Re: Trouble moving into protected mode. |
Oh, my GDT is all null. I need to set up my kernel and my MBR as a code segment (or something like that), before I use "lgdt", don't I? |
Author: | Izzette [ Sat Mar 18, 2017 1:49 am ] |
Post subject: | Re: Trouble moving into protected mode. |
Thanks to your help, I was able to able to execute my "Bare Bones" kernel successfully using a flat memory model. In case anyone is at a similar point and struggling, hopefully, this diff will help, I know it would have helped me. (Edited for brevarity.) Code: diff --git a/Makefile b/Makefile
new file mode 100644 index 0000000..a2598bb --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +# Makefile + +CC := gcc +CFLAGS := -ggdb -Wl,-T,linker.ld -Wl,--oformat,binary -nostdlib + +all: boot + +boot.s: +pmode.s: +linker.ld: + +boot.o: + $(CC) $(CFLAGS) -m16 -c boot.s -o boot.o +pmode.o: + $(CC) $(CFLAGS) -m32 -c pmode.s -o pmode.o + +boot: boot.o pmode.o linker.ld + $(CC) $(CFLAGS) -m16 boot.o pmode.o -o boot + +# vim: set ts=4 sw=4 noet syn=make: diff --git a/boot.s b/boot.s index 3796d0c..a3577f5 100644 --- a/boot.s +++ b/boot.s @@ -40,12 +40,13 @@ // Safe LBA max to read with bios .set lbasafemax, 0x7f -// Kernel entry point -.set kentryseg, 0x08 -.set kentryoffset, 0x8f80 +.set heapstart, 0x0500 -// The GDT registry -.set gdtr, 0x0500 +// The GDT registry. +.set gdtr, heapstart +// The GDT. The GDT registry is only 6 bytes long, +// but lets just make sure it's aligned to 4. +.set gdt, heapstart+8 // Get DOS partition address. // Addess will be left in %ax. @@ -329,6 +382,21 @@ blkstarthigh: // Upper 32-bits, which we won't be using. .long 0x00000000 +// GDT prototype +gdtproto: +// NULL descriptor + .long 0x00000000 + .long 0x00000000 +// Code descriptor + .long 0x0000FFFF + .long 0x00CF9A00 +// Data descriptor + .long 0x0000FFFF + .long 0x00CF9200 +gdtprotoend: +// Make sure there's something here and gdtend oesn't extend into stage2. + .byte 0x00 +.set gdtlen, gdtprotoend-gdtproto /* ******************************* */ /* *********** STAGE 2 *********** */ @@ -636,6 +704,54 @@ load_kernel: ret // } +// Initialize the GDT +// void init_gdt () { +init_gdt: + push %bp + mov %sp, %bp + +// Copy the GDT prototype. + push $gdtlen + push $gdtproto + push $gdt + call memcpy + add $0x6, %sp + +// Create the descriptor registry. + movw $gdtlen-1, gdtr + movl $gdt, gdtr+2 + + mov %bp, %sp + pop %bp + ret +// } + +// Enter protected mode and execute the kernel. +// void kexec () { +kexec: +// There's no coming back from here, +// so forget about saving the base pointer. +// push %bp + mov %sp, %bp + +// Load the GDT registry. + lgdt gdtr + +// set PE (Protection Enable) bit in CR0 (Control Register 0) + mov %cr0, %eax + or $1, %eax + mov %eax, %cr0 + +// Jump to 32-bit protected mode code + ljmp $0x08, $pmode + +// This function should never return, +// so forget about restoring the stack and base pointers. +// mov %bp, %sp +// pop %bp +// ret +// } + // Do the thing, Julie. // void boot (uint16_t *driveindex) { boot: @@ -670,36 +786,11 @@ boot: cmpw $ENOERR, errno jne readerr -// TODO: do something. - call kexec - -// This function should never return, -// so forget about restoring the stack and base pointers. -// mov %bp, %sp -// pop %bp -// ret -// } - -// We're moving into 32-bit protected mode. -.code32 +// Create the GDT, but don't load it yet. + call init_gdt -// Enter protected mode and execute the kernel. -// void kexec () { -kexec: -// There's no coming back from here, -// so forget about saving the base pointer. -// push %bp - mov %sp, %bp - -// Load the GDT registry. - lgdt gdtr - -// set PE (Protection Enable) bit in CR0 (Control Register 0) - mov %cr0, %eax - or $1, %eax - mov %eax, %cr0 - - ljmp $kentryseg, $kentryoffset +// Switch to 32-bit protected mode and execute the kernel. + call kexec // This function should never return, // so forget about restoring the stack and base pointers. diff --git a/pmode.s b/pmode.s new file mode 100644 index 0000000..a1005f7 --- /dev/null +++ b/pmode.s @@ -0,0 +1,22 @@ +// pmode.s +// Protected mode bootloader code. + +// Kernel entry point +.set kentryseg, 0x08 +.set kentryoffset, 0x9000 + +// We're moving into 32-bit protected mode. +.code32 +.section .stage2 +.global pmode +pmode: + mov $0x10, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + mov %ax, %ss + + ljmp $kentryseg, $kentryoffset + +// vim: set ts=8 sw=8 noet syn=asm: |
Author: | SpyderTL [ Sat Mar 18, 2017 5:00 am ] |
Post subject: | Re: Trouble moving into protected mode. |
Congrats. And welcome to the site. Let us know if there is anything else we can help with. |
Author: | starmanz [ Sat Mar 25, 2017 5:21 am ] |
Post subject: | Re: Trouble moving into protected mode. |
If the compiler/assembler thinks you're running 32-bit mode, then it probably won't let you go into 32-bit mode. I thought it was just a "tell the assembler" instruction |
Page 1 of 1 | All times are UTC - 6 hours |
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |