Deutsch English Français Italiano |
<vpv19u$b1b$1@reader1.panix.com> View for Bookmarking (what is this?) Look up another Usenet article |
Path: ...!weretis.net!feeder9.news.weretis.net!panix!.POSTED.spitfire.i.gajendra.net!not-for-mail From: cross@spitfire.i.gajendra.net (Dan Cross) Newsgroups: comp.lang.c,alt.os.development Subject: Re: [OSDev] How to switch to long mode in x86 CPUs? Followup-To: alt.os.development Date: Sat, 1 Mar 2025 13:15:42 -0000 (UTC) Organization: PANIX Public Access Internet and UNIX, NYC Message-ID: <vpv19u$b1b$1@reader1.panix.com> References: <871pvje5yq.fsf@onesoftnet.eu.org> Injection-Date: Sat, 1 Mar 2025 13:15:42 -0000 (UTC) Injection-Info: reader1.panix.com; posting-host="spitfire.i.gajendra.net:166.84.136.80"; logging-data="11307"; mail-complaints-to="abuse@panix.com" X-Newsreader: trn 4.0-test77 (Sep 1, 2010) Originator: cross@spitfire.i.gajendra.net (Dan Cross) Bytes: 7783 Lines: 204 [Note: Followup-To: set to alt.os.development] In article <871pvje5yq.fsf@onesoftnet.eu.org>, Ar Rakin <rakinar2@onesoftnet.eu.org> 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? >#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. >.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. > 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. > 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. > /* 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. > /* 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. >.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. >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. In 32-bit mode, this will wrap around to 0xfffffff8, which is in the active stack segment; there may be RAM there, which is why it doesn't fault in protected mode. Since you haven't set up an IDT yet, any fault this early will be a triple fault. Or perhaps multiboot left you a stack by chance. The multiboot1 specification explicitly says that the OS should set up it's own stack, though, so I wouldn't rely on that. Try adding a page for more for stack space in your BSS, and set %rsp to point to the top of that region, e.g., back in bss: stack: .skip 4096 .... movabsq $(stack + 4096), %rsp callq kmain > callq kabort I take it you never get here. :-) >I am not sure what is wrong, but when I run my kernel in >qemu-system-x86_64, it causes a triple fault when trying to jump to the ========== REMAINDER OF ARTICLE TRUNCATED ==========