| /* Copyright (c) 2009 The Regents of the University of California |
| * Barret Rhoden <brho@cs.berkeley.edu> |
| * See LICENSE for details. |
| * |
| * Physical memory managment, common to 32 and 64 bit */ |
| |
| #include <arch/x86.h> |
| #include <arch/arch.h> |
| #include <arch/mmu.h> |
| #include <arch/apic.h> |
| |
| #include <error.h> |
| #include <sys/queue.h> |
| |
| #include <atomic.h> |
| #include <string.h> |
| #include <assert.h> |
| #include <pmap.h> |
| #include <env.h> |
| #include <stdio.h> |
| #include <kmalloc.h> |
| #include <page_alloc.h> |
| |
| bool enable_pse(void) |
| { |
| uint32_t edx, cr4; |
| |
| cpuid(0x1, 0x0, 0, 0, 0, &edx); |
| if (edx & CPUID_PSE_SUPPORT) { |
| cr4 = rcr4(); |
| cr4 |= CR4_PSE; |
| lcr4(cr4); |
| return 1; |
| } else |
| return 0; |
| } |
| |
| #define PAT_UC 0x00 |
| #define PAT_WC 0x01 |
| #define PAT_WT 0x04 |
| #define PAT_WP 0x05 |
| #define PAT_WB 0x06 |
| #define PAT_UCm 0x07 |
| |
| static inline uint64_t mk_pat(int pat_idx, int type) |
| { |
| return (uint64_t)type << (8 * pat_idx); |
| } |
| |
| static void pat_init(void) |
| { |
| uint64_t pat = 0; |
| |
| /* Default PAT at boot: |
| * 0: WB, 1: WT, 2: UC-, 3: UC, 4: WB, 5: WT, 6: UC-, 7: UC |
| * |
| * We won't use PATs 4-7, but we'll at least enforce that they are set |
| * up the way we think they are. I'd like to avoid using the PAT flag, |
| * since that is also the PTE_PS (jumbo) flag. That means we can't use |
| * __PTE_PAT on jumbo pages, and we'd need to be careful whenever using |
| * any unorthodox types. We're better off just not using it. |
| * |
| * We want WB, WT, WC, and either UC or UC- for our memory types. (WT |
| * is actually optional at this point). We'll use UC- instead of UC, |
| * since Linux uses that for their pgprot_noncached. The UC- type is UC |
| * with the ability to override to WC via MTRR. We don't use the MTRRs |
| * much yet, and hopefully won't. The UC- will only matter if we do. |
| * |
| * No one should be using the __PTE_{PAT,PCD,PWT} bits directly, and |
| * everyone should use things like PTE_NOCACHE. */ |
| pat |= mk_pat(0, PAT_WB); /* | | */ |
| pat |= mk_pat(1, PAT_WT); /* | | __PTE_PWT */ |
| pat |= mk_pat(2, PAT_WC); /* | __PTE_PCD | */ |
| pat |= mk_pat(3, PAT_UCm); /* | __PTE_PCD | __PTE_PWT */ |
| pat |= mk_pat(4, PAT_WB); /* __PTE_PAT | | */ |
| pat |= mk_pat(5, PAT_WT); /* __PTE_PAT | | __PTE_PWT */ |
| pat |= mk_pat(6, PAT_UCm); /* __PTE_PAT | __PTE_PCD | */ |
| pat |= mk_pat(7, PAT_UC); /* __PTE_PAT | __PTE_PCD | __PTE_PWT */ |
| write_msr(MSR_IA32_CR_PAT, pat); |
| } |
| |
| // could consider having an API to allow these to dynamically change |
| // MTRRs are for physical, static ranges. PAT are linear, more granular, and |
| // more dynamic |
| void setup_default_mtrrs(barrier_t* smp_barrier) |
| { |
| // disable interrupts |
| int8_t state = 0; |
| disable_irqsave(&state); |
| // barrier - if we're meant to do this for all cores, we'll be |
| // passed a pointer to an initialized barrier |
| if (smp_barrier) |
| waiton_barrier(smp_barrier); |
| |
| // disable caching cr0: set CD and clear NW |
| lcr0((rcr0() | CR0_CD) & ~CR0_NW); |
| // flush caches |
| cache_flush(); |
| // flush tlb |
| tlb_flush_global(); |
| // disable MTRRs, and sets default type to WB (06) |
| #ifndef CONFIG_NOMTRRS |
| write_msr(IA32_MTRR_DEF_TYPE, 0x00000006); |
| |
| // Now we can actually safely adjust the MTRRs |
| // MTRR for IO Holes (note these are 64 bit values we are writing) |
| // 0x000a0000 - 0x000c0000 : VGA - WC 0x01 |
| write_msr(IA32_MTRR_PHYSBASE0, PTE_ADDR(VGAPHYSMEM) | 0x01); |
| // if we need to have a full 64bit val, use the UINT64 macro |
| write_msr(IA32_MTRR_PHYSMASK0, 0x0000000ffffe0800); |
| // 0x000c0000 - 0x00100000 : IO devices (and ROM BIOS) - UC 0x00 |
| write_msr(IA32_MTRR_PHYSBASE1, PTE_ADDR(DEVPHYSMEM) | 0x00); |
| write_msr(IA32_MTRR_PHYSMASK1, 0x0000000ffffc0800); |
| // APIC/IOAPIC holes |
| /* Going to skip them, since we set their mode using PAT when we |
| * map them in |
| */ |
| // make sure all other MTRR ranges are disabled (should be unnecessary) |
| write_msr(IA32_MTRR_PHYSMASK2, 0); |
| write_msr(IA32_MTRR_PHYSMASK3, 0); |
| write_msr(IA32_MTRR_PHYSMASK4, 0); |
| write_msr(IA32_MTRR_PHYSMASK5, 0); |
| write_msr(IA32_MTRR_PHYSMASK6, 0); |
| write_msr(IA32_MTRR_PHYSMASK7, 0); |
| |
| // keeps default type to WB (06), turns MTRRs on, and turns off fixed |
| // ranges |
| write_msr(IA32_MTRR_DEF_TYPE, 0x00000806); |
| #endif |
| pat_init(); |
| // reflush caches and TLB |
| cache_flush(); |
| tlb_flush_global(); |
| // turn on caching |
| lcr0(rcr0() & ~(CR0_CD | CR0_NW)); |
| // barrier |
| if (smp_barrier) |
| waiton_barrier(smp_barrier); |
| // enable interrupts |
| enable_irqsave(&state); |
| } |
| |
| void invlpg(void *addr) |
| { |
| asm volatile("invlpg (%0)" : : "r" (addr) : "memory"); |
| if (per_cpu_info[core_id()].vmx_enabled) |
| ept_inval_addr((uintptr_t)addr); |
| } |
| |
| void tlbflush(void) |
| { |
| unsigned long cr3; |
| asm volatile("mov %%cr3,%0" : "=r" (cr3)); |
| asm volatile("mov %0,%%cr3" : : "r" (cr3)); |
| if (per_cpu_info[core_id()].vmx_enabled) |
| ept_inval_context(); |
| } |
| |
| /* Flushes a TLB, including global pages. We should always have the CR4_PGE |
| * flag set, but just in case, we'll check. Toggling this bit flushes the TLB. |
| */ |
| void tlb_flush_global(void) |
| { |
| uint32_t cr4 = rcr4(); |
| if (cr4 & CR4_PGE) { |
| lcr4(cr4 & ~CR4_PGE); |
| lcr4(cr4); |
| } else { |
| lcr3(rcr3()); |
| } |
| if (per_cpu_info[core_id_early()].vmx_enabled) |
| ept_inval_global(); |
| } |