commit 5826ce53b464fdfaef35308d1a2b6b97401abc79
parent c081fa81205eadb0821b9eba09eada2a4e371305
Author: Brian Swetland <swetland@frotz.net>
Date: Fri, 3 Jan 2014 20:33:14 -0800
64bit vm/mmu and trap code
Diffstat:
3 files changed, 201 insertions(+), 3 deletions(-)
diff --git a/kernel/trap.c b/kernel/trap.c
@@ -14,6 +14,7 @@ extern uintp vectors[]; // in vectors.S: array of 256 entry pointers
struct spinlock tickslock;
uint ticks;
+#ifndef X64
void
tvinit(void)
{
@@ -31,6 +32,7 @@ idtinit(void)
{
lidt(idt, sizeof(idt));
}
+#endif
//PAGEBREAK: 41
void
diff --git a/kernel/vm.c b/kernel/vm.c
@@ -11,6 +11,7 @@ extern char data[]; // defined by kernel.ld
pde_t *kpgdir; // for use in scheduler()
struct segdesc gdt[NSEGS];
+#ifndef X64
// Set up CPU's kernel segment descriptors.
// Run once on entry on each CPU.
void
@@ -38,6 +39,7 @@ seginit(void)
cpu = c;
proc = 0;
}
+#endif
// Return the address of the PTE in page table pgdir
// that corresponds to virtual address va. If alloc!=0,
@@ -89,6 +91,7 @@ mappages(pde_t *pgdir, void *va, uintp size, uintp pa, int perm)
return 0;
}
+#ifndef X64
// There is one page table per process, plus one that's used when
// a CPU is not running any process (kpgdir). The kernel uses the
// current process's page table during system calls and interrupts;
@@ -175,6 +178,7 @@ switchuvm(struct proc *p)
lcr3(v2p(p->pgdir)); // switch to new address space
popcli();
}
+#endif
// Load the initcode into address 0 of pgdir.
// sz must be less than a page.
@@ -278,11 +282,10 @@ void
freevm(pde_t *pgdir)
{
uint i;
-
if(pgdir == 0)
panic("freevm: no pgdir");
- deallocuvm(pgdir, KERNBASE, 0);
- for(i = 0; i < NPDENTRIES; i++){
+ deallocuvm(pgdir, 0x3fa00000, 0);
+ for(i = 0; i < NPDENTRIES-2; i++){
if(pgdir[i] & PTE_P){
char * v = p2v(PTE_ADDR(pgdir[i]));
kfree(v);
diff --git a/kernel/vm64.c b/kernel/vm64.c
@@ -0,0 +1,193 @@
+/* vm64.c
+ *
+ * Copyright (c) 2013 Brian Swetland
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "param.h"
+#include "types.h"
+#include "defs.h"
+#include "x86.h"
+#include "memlayout.h"
+#include "mmu.h"
+#include "proc.h"
+#include "elf.h"
+
+__thread struct cpu *cpu;
+__thread struct proc *proc;
+
+static pde_t *kpml4;
+static pde_t *kpdpt;
+static pde_t *iopgdir;
+extern pde_t *kpgdir; // for use in scheduler()
+
+void wrmsr(uint msr, uint64 val);
+
+void tvinit(void) {}
+void idtinit(void) {}
+
+static void mkgate(uint *idt, uint n, void *kva, uint pl, uint trap) {
+ uint64 addr = (uint64) kva;
+ n *= 4;
+ trap = trap ? 0x8F00 : 0x8E00; // TRAP vs INTERRUPT gate;
+ idt[n+0] = (addr & 0xFFFF) | ((SEG_KCODE << 3) << 16);
+ idt[n+1] = (addr & 0xFFFF0000) | trap | ((pl & 3) << 13); // P=1 DPL=pl
+ idt[n+2] = addr >> 32;
+ idt[n+3] = 0;
+}
+
+static void tss_set_rsp(uint *tss, uint n, uint64 rsp) {
+ tss[n*2 + 1] = rsp;
+ tss[n*2 + 2] = rsp >> 32;
+}
+
+static void tss_set_ist(uint *tss, uint n, uint64 ist) {
+ tss[n*2 + 7] = ist;
+ tss[n*2 + 8] = ist >> 32;
+}
+
+extern void* vectors[];
+
+// Set up CPU's kernel segment descriptors.
+// Run once on entry on each CPU.
+void
+seginit(void)
+{
+ uint64 *gdt;
+ uint *tss;
+ uint64 addr;
+ void *local;
+ struct cpu *c;
+ uint *idt = (uint*) kalloc();
+ int n;
+ memset(idt, 0, PGSIZE);
+
+ for (n = 0; n < 256; n++)
+ mkgate(idt, n, vectors[n], 0, 0);
+ mkgate(idt, 64, vectors[64], 3, 1);
+
+ lidt((void*) idt, PGSIZE);
+
+ // create a page for cpu local storage
+ local = kalloc();
+ memset(local, 0, PGSIZE);
+
+ gdt = (uint64*) local;
+ tss = (uint*) (((char*) local) + 1024);
+ tss[16] = 0x00680000; // IO Map Base = End of TSS
+
+ // point FS smack in the middle of our local storage page
+ wrmsr(0xC0000100, ((uint64) local) + (PGSIZE / 2));
+
+ c = &cpus[cpunum()];
+ c->local = local;
+
+ cpu = c;
+ proc = 0;
+
+ addr = (uint64) tss;
+ gdt[0] = 0x0000000000000000;
+ gdt[SEG_KCODE] = 0x0020980000000000; // Code, DPL=0, R/X
+ gdt[SEG_UCODE] = 0x0020F80000000000; // Code, DPL=3, R/X
+ gdt[SEG_KDATA] = 0x0000920000000000; // Data, DPL=0, W
+ gdt[SEG_KCPU] = 0x0000000000000000; // unused
+ gdt[SEG_UDATA] = 0x0000F20000000000; // Data, DPL=3, W
+ gdt[SEG_TSS+0] = (0x0067) | ((addr & 0xFFFFFF) << 16) |
+ (0x00E9LL << 40) | (((addr >> 24) & 0xFF) << 56);
+ gdt[SEG_TSS+1] = (addr >> 32);
+
+ lgdt((void*) gdt, 8 * sizeof(uint64));
+
+ ltr(SEG_TSS << 3);
+};
+
+// The core xv6 code only knows about two levels of page tables,
+// so we will create all four, but only return the second level.
+// because we need to find the other levels later, we'll stash
+// backpointers to them in the top two entries of the level two
+// table.
+pde_t*
+setupkvm(void)
+{
+ pde_t *pml4 = (pde_t*) kalloc();
+ pde_t *pdpt = (pde_t*) kalloc();
+ pde_t *pgdir = (pde_t*) kalloc();
+
+ memset(pml4, 0, PGSIZE);
+ memset(pdpt, 0, PGSIZE);
+ memset(pgdir, 0, PGSIZE);
+ pml4[511] = v2p(kpdpt) | PTE_P | PTE_W | PTE_U;
+ pml4[0] = v2p(pdpt) | PTE_P | PTE_W | PTE_U;
+ pdpt[0] = v2p(pgdir) | PTE_P | PTE_W | PTE_U;
+
+ // virtual backpointers
+ pgdir[511] = ((uintp) pml4) | PTE_P;
+ pgdir[510] = ((uintp) pdpt) | PTE_P;
+
+ return pgdir;
+};
+
+// Allocate one page table for the machine for the kernel address
+// space for scheduler processes.
+void
+kvmalloc(void)
+{
+ int n;
+ kpml4 = (pde_t*) kalloc();
+ kpdpt = (pde_t*) kalloc();
+ kpgdir = (pde_t*) kalloc();
+ iopgdir = (pde_t*) kalloc();
+ memset(kpml4, 0, PGSIZE);
+ memset(kpdpt, 0, PGSIZE);
+ memset(kpgdir, 0, PGSIZE);
+ memset(iopgdir, 0, PGSIZE);
+ kpml4[511] = v2p(kpdpt) | PTE_P | PTE_W;
+ kpdpt[510] = v2p(kpgdir) | PTE_P | PTE_W;
+ kpdpt[509] = v2p(iopgdir) | PTE_P | PTE_W;
+ for (n = 0; n < NPDENTRIES; n++)
+ kpgdir[n] = (n << PDXSHIFT) | PTE_PS | PTE_P | PTE_W;
+ for (n = 0; n < 16; n++)
+ iopgdir[n] = (DEVSPACE + (n << PDXSHIFT)) | PTE_PS | PTE_P | PTE_W | PTE_PWT | PTE_PCD;
+ switchkvm();
+}
+
+void
+switchkvm(void)
+{
+ lcr3(v2p(kpml4));
+}
+
+void
+switchuvm(struct proc *p)
+{
+ void *pml4;
+ uint *tss;
+ pushcli();
+ if(p->pgdir == 0)
+ panic("switchuvm: no pgdir");
+ tss = (uint*) (((char*) cpu->local) + 1024);
+ tss_set_rsp(tss, 0, (uintp)proc->kstack + KSTACKSIZE);
+ pml4 = (void*) PTE_ADDR(p->pgdir[511]);
+ lcr3(v2p(pml4));
+ popcli();
+}
+