home bbs files messages ]

Forums before death by AOL, social media and spammers... "We can't have nice things"

   alt.os.development      Operating system development chatter      4,255 messages   

[   << oldest   |   < older   |   list   |   newer >   |   newest >>   ]

   Message 4,201 of 4,255   
   Ar Rakin to Dan Cross   
   Re: [OSDev] How to switch to long mode i   
   02 Mar 25 17:35:01   
   
   From: rakinar2@onesoftnet.eu.org   
      
   On 3/1/25 7:15 PM, Dan Cross wrote:   
   > [Note: Followup-To: set to alt.os.development]   
   >   
   > In article <871pvje5yq.fsf@onesoftnet.eu.org>,   
   > Ar Rakin   wrote:   
   >> Hello there,   
   >>   
   >> I am trying to develop my own, simple operating system to learn more   
   >> about how kernels work and low level stuff like that.  However, I am   
   >> stuck at setting up paging while switching long mode (64-bit protected   
   >> mode) in x86 processors.   
   >   
   > As has been mentioned, comp.lang.c is not the appropriate place   
   > to ask this.  I have set the `Followup-To:` header to   
   > alt.os.development, and am cross-posting this post to that   
   > newsgroup.   
   >   
   >> The assembly code I currently have:   
   >>   
   >> #define PG_START 0x000000000   
   >   
   > Just to be clear, this means that you have decide to make your   
   > _virtual_ address space starts at absolute address 0?  What   
   > address do you link your kernel at?   
   >   
      
   The page start address was originally 0x1000000, due to the triple fault   
   I was trying to use different addresses.  Sorry for the confusion.  And   
   the kernel is loaded by GRUB, and apparently that means the kernel gets   
   loaded at 0x100000.   
      
   >> #define MSR_EFER 0xc0000080   
   >>   
   >> .section .bss, "aw", @nobits   
   >> .align 4096   
   >> pml4_tbl:   
   >>   .skip 4096   
   >> pdpt_tbl:   
   >>   .skip 4096   
   >   
   > This is fine, but note that, instead of using code to fill in   
   > your page tables, you could simply define them here with the   
   > expected entries, as they are very simple.   
      
   I tried doing something like this:   
      
   pml4_tbl:   
      .quad pdpt_tbl | 0x3   
      
   But the assembler complained that I can't use the '|' operator.  Is   
   there any other way I could define entries directly here?   
      
   >> .text   
   >> .globl _mboot_start   
   >> _mboot_start:   
   >>   /* GRUB executes this code in 32-bit protected mode. */   
   >>   
   >>   /* Write (pdpt_tbl | 0x3) to the first 8 bytes of pml4_tbl */   
   >>   movl $pdpt_tbl, %eax   
   >>   orl $0x3, %eax   
   >>   movl $pml4_tbl, %edi   
   >>   movl %eax, (%edi)   
   >   
   > Note that this sequence implicitly assumes that you are starting   
   > with an identity mapping between between the physical and   
   > virtual address spaces.  In particular, when you   
   > `movl $pdpt_tbl, %eax` you are copying whatever address the   
   > linker assigns to `$pdpt_tbl` into %eax (the low 32-bits of it   
   > anyway, though the assembler would probably sqwuak at you if   
   > didn't fit into a 32 bit immediate).  Page table entries must   
   > refer to physical addresses, so if you've arranged for the   
   > linker to use some base address other than 0 for your kernel,   
   > you've got to take care to account for an offset here.   
      
   Yes, I have the following linker script:   
      
   ENTRY(_start)   
      
   SECTIONS {   
        . = 1M;  /* Load the kernel at 1MB (GRUB loads it here) */   
      
        .multiboot2_header : {   
            *(.multiboot2_header)   
        } :headers   
      
        .text ALIGN(4K) : {   
            *(.text)   
        } :text   
      
        .rodata ALIGN(4K) : {   
            *(.rodata)   
        } :rodata   
      
        .data ALIGN(4K) : {   
            *(.data)   
        } :data   
      
        .bss ALIGN(4K) : {   
            *(.bss)   
        } :bss   
   }   
      
   PHDRS {   
        headers PT_LOAD;   
        text PT_LOAD FLAGS(5);   
        rodata PT_LOAD;   
        data PT_LOAD;   
        bss PT_LOAD;   
   }   
      
   >>   xorl %eax, %eax   
   >>   movl %eax, 4(%edi)   
   >   
   > Note that, as you're doing this in assembly, the upper bits in   
   > the table are already filled with zeros, so there's no need for   
   > the `xorl %eax, %eax; movl %eax 4(%edi)` sequence.   
      
   Makes sense!   
      
   >>   movl $pdpt_tbl, %edi   
   >>   movl $PG_START, %eax   
   >>   /* 0x83 = 0b10000011; flags: present, writable, upervisor-only,   
   >>      1GB huge page */   
   >>   movl $0x83, (%edi)   
   >>   movl %eax, 4(%edi)   
   >   
   > This looks correct.  Your page tables will now map a single   
   > gigabyte of address space starting at (virtual) address zero to   
   > physical address 0, and nothing else.  To be clear, is that what   
   > you want?  When coming out of protected mode, I generally try to   
   > map the whole 32-bit address space; that is, all 4 GiB.   
      
   Now that I have mentioned the page start address (0x1000000), yeah, I   
   wanted to create 1:1 identity page mapping for simplicity.   
      
   >>   /* Enable Physical Address Extension (PAE) */   
   >>   movl %cr4, %eax   
   >>   btsl $5, %eax   
   >>   movl %eax, %cr4   
   >>   
   >>   /* Load the address of the PML4 table into %cr3 */   
   >>   movl $pml4_tbl, %edi   
   >>   movl %edi, %cr3   
   >   
   > Note that the same caveat about physical addresses of the PML4   
   > apply here as applied to the PDPT above.   
      
   Noted.   
      
   >>   /* Enable long mode */   
   >>   movl $MSR_EFER, %ecx   
   >>   rdmsr   
   >>   btsl $8, %eax   
   >>   wrmsr   
   >>   
   >>   /* Enable paging */   
   >>   movl %cr0, %eax   
   >>   btsl $31, %eax   
   >>   movl %eax, %cr0   
   >   
   > So immediately after executing this instruction, the processor   
   > will be executing with paging enabled.  This means that the very   
   > next instruction (the ljmp below here) _must_ be mapped at the   
   > address in %rip.  If not, you will fault.   
   >   
   > Your post suggests that you fault on the ljmp, but this may not   
   > be why.  Any easy test would be to add a `jmp .` here and see if   
   > that faults in the QEMU monitor.   
   >   
   > If you do not fault here, then your page tables are ok (at least   
   > so far), and your problem lies elsewhere.  See below.   
   >   
   >>   /* Jump to 64-bit code */   
   >>   ljmpl $0x08, $long_mode_entry   
   >   
   > Have you set up a GDT with an entry for a 64-bit code segment   
   > by this point?  It doesn't look like it.  My guess is that that   
   > is the source of your fault; note that the multiboot1 spec says   
   > that you must set up a GDT and should not rely on the one that   
   > it set up to get you into 32-bit protected mode.  Certainly   
   > there is no guarantee that there's a 64-bit code segment at   
   > offset 0x8 in whatever table it set up.   
   >   
   > My guess is that this is the source of your problem.   
      
   Correct, thanks!  I forgot to load a GDT.   
      
   >> .loop:   
   >>   hlt   
   >>   jmp .loop   
   >   
   > I would delete this loop; you can't ever really hit it: either   
   > the long jump will succeed and skip over it, or it will fault.   
      
   Noted.   
      
   >> long_mode_entry:   
   >>   .code64   
   >>   xorw %ax, %ax   
   >>   movw %ax, %ds   
   >>   movw %ax, %es   
   >>   movw %ax, %fs   
   >>   movw %ax, %gs   
   >>   movw %ax, %ss   
   >>   
   >>   callq kmain   
   >   
   > You should probably give yourself a stack before calling C.   
   > What's in `%rsp` here?  My guess is that this would fault if you   
   > got here: the `callq` will push the address of the next   
   > instruction onto the stack, but since you haven't set one up,   
   > %rsp is whatever it is (either it's reset value, 0, or something   
   > random set up by multiboot).  Suppose it's 0; then the call will   
   > attempt to push %rip to -8; that's fine (the processor will   
   > happily wrap around to 0xfffffffffffffff8) but you definitely   
   > don't have anything mapped there, so you'll get a fault.   
   >   
      
   [continued in next message]   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   

[   << oldest   |   < older   |   list   |   newer >   |   newest >>   ]


(c) 1994,  bbs@darkrealms.ca