OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Mar 28, 2024 1:41 pm

All times are UTC - 6 hours




Post new topic Reply to topic  [ 8 posts ] 
Author Message
 Post subject: Trouble moving into protected mode.
PostPosted: Fri Mar 17, 2017 10:07 pm 
Offline

Joined: Fri Mar 17, 2017 9:37 pm
Posts: 22
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?


Top
 Profile  
 
 Post subject: Re: Trouble moving into protected mode.
PostPosted: Fri Mar 17, 2017 10:19 pm 
Offline
Member
Member
User avatar

Joined: Sun Sep 19, 2010 10:05 pm
Posts: 1074
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.

_________________
Project: OZone
Source: GitHub
Current Task: LIB/OBJ file support
"The more they overthink the plumbing, the easier it is to stop up the drain." - Montgomery Scott


Top
 Profile  
 
 Post subject: Re: Trouble moving into protected mode.
PostPosted: Fri Mar 17, 2017 10:24 pm 
Offline

Joined: Fri Mar 17, 2017 9:37 pm
Posts: 22
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,


Top
 Profile  
 
 Post subject: Re: Trouble moving into protected mode.
PostPosted: Fri Mar 17, 2017 10:42 pm 
Offline
Member
Member
User avatar

Joined: Sun Sep 19, 2010 10:05 pm
Posts: 1074
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).

_________________
Project: OZone
Source: GitHub
Current Task: LIB/OBJ file support
"The more they overthink the plumbing, the easier it is to stop up the drain." - Montgomery Scott


Top
 Profile  
 
 Post subject: Re: Trouble moving into protected mode.
PostPosted: Fri Mar 17, 2017 10:55 pm 
Offline

Joined: Fri Mar 17, 2017 9:37 pm
Posts: 22
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?


Top
 Profile  
 
 Post subject: Re: Trouble moving into protected mode.
PostPosted: Sat Mar 18, 2017 1:49 am 
Offline

Joined: Fri Mar 17, 2017 9:37 pm
Posts: 22
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:


Top
 Profile  
 
 Post subject: Re: Trouble moving into protected mode.
PostPosted: Sat Mar 18, 2017 5:00 am 
Offline
Member
Member
User avatar

Joined: Sun Sep 19, 2010 10:05 pm
Posts: 1074
Congrats. And welcome to the site.

Let us know if there is anything else we can help with.

_________________
Project: OZone
Source: GitHub
Current Task: LIB/OBJ file support
"The more they overthink the plumbing, the easier it is to stop up the drain." - Montgomery Scott


Top
 Profile  
 
 Post subject: Re: Trouble moving into protected mode.
PostPosted: Sat Mar 25, 2017 5:21 am 
Offline

Joined: Sat Mar 18, 2017 4:52 am
Posts: 17
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 8)

_________________
AX BX CX DX BP SP SI DI. The registers that never die. 16-bit... will always sit... in a book where people look. My signature is terrible! I think we all know that.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 8 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: Google [Bot] and 65 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group