Submit #1742 ยป PAT-1.patch
| sys/cpu/i386/include/cpufunc.h | ||
|---|---|---|
|
#endif
|
||
|
#ifndef _CPU_WBINVL_DEFINED
|
||
|
static __inline void
|
||
|
cpu_wbinvl(void)
|
||
|
{
|
||
|
__asm __volatile("wbinvd");
|
||
|
}
|
||
|
#endif
|
||
|
static __inline void
|
||
|
cpu_nop(void)
|
||
|
{
|
||
| sys/cpu/i386/include/pmap.h | ||
|---|---|---|
|
#define PG_A 0x020 /* A Accessed */
|
||
|
#define PG_M 0x040 /* D Dirty */
|
||
|
#define PG_PS 0x080 /* PS Page size (0=4k,1=4M) */
|
||
|
#define PG_PTE_PAT 0x080 /* PAT PAT index */
|
||
|
#define PG_G 0x100 /* G Global */
|
||
|
#define PG_AVAIL1 0x200 /* / Available for system */
|
||
|
#define PG_AVAIL2 0x400 /* < programmers use */
|
||
|
#define PG_AVAIL3 0x800 /* \ */
|
||
|
#define PG_PDE_PAT 0x1000 /* PAT PAT index */
|
||
|
/* Our various interpretations of the above */
|
||
| sys/cpu/i386/include/specialreg.h | ||
|---|---|---|
|
#define MSR_MTRR64kBase 0x250
|
||
|
#define MSR_MTRR16kBase 0x258
|
||
|
#define MSR_MTRR4kBase 0x268
|
||
|
#define MSR_PAT 0x277
|
||
|
#define MSR_MTRRdefType 0x2ff
|
||
|
#define MSR_MC0_CTL 0x400
|
||
|
#define MSR_MC0_STATUS 0x401
|
||
| ... | ... | |
|
#define MSR_THERM_INTERRUPT 0x19b
|
||
|
#define MSR_THERM_STATUS 0x19c
|
||
|
/*
|
||
|
* PAT modes.
|
||
|
*/
|
||
|
#define PAT_UNCACHEABLE 0x00
|
||
|
#define PAT_WRITE_COMBINING 0x01
|
||
|
#define PAT_WRITE_THROUGH 0x04
|
||
|
#define PAT_WRITE_PROTECTED 0x05
|
||
|
#define PAT_WRITE_BACK 0x06
|
||
|
#define PAT_UNCACHED 0x07
|
||
|
#define PAT_VALUE(i, m) ((long long)(m) << (8 * (i)))
|
||
|
#define PAT_MASK(i) PAT_VALUE(i, 0xff)
|
||
|
/*
|
||
|
* Constants related to MTRRs
|
||
| sys/platform/pc32/i386/mp_machdep.c | ||
|---|---|---|
|
load_cr0(cr0);
|
||
|
pmap_set_opt(); /* PSE/4MB pages, etc */
|
||
|
pmap_init_pat(); /* Page Attribute Table */
|
||
|
/* set up CPU registers and state */
|
||
|
cpu_setregs();
|
||
| sys/platform/pc32/i386/pmap.c | ||
|---|---|---|
|
static boolean_t pmap_initialized = FALSE; /* Has pmap_init completed? */
|
||
|
static int pgeflag; /* PG_G or-in */
|
||
|
static int pseflag; /* PG_PS or-in */
|
||
|
static int pat_works; /* Is page attribute table sane? */
|
||
|
static vm_object_t kptobj;
|
||
| ... | ... | |
|
static vm_page_t pmap_page_lookup (vm_object_t object, vm_pindex_t pindex);
|
||
|
static int pmap_unuse_pt (pmap_t, vm_offset_t, vm_page_t, pmap_inval_info_t);
|
||
|
static vm_offset_t pmap_kmem_choose(vm_offset_t addr);
|
||
|
static int pmap_cache_bits(int, boolean_t);
|
||
|
static unsigned pdir4mb;
|
||
| ... | ... | |
|
return (0);
|
||
|
}
|
||
|
/*
|
||
|
* Setup the PAT MSR.
|
||
|
*/
|
||
|
void
|
||
|
pmap_init_pat(void)
|
||
|
{
|
||
|
uint64_t pat_msr;
|
||
|
/* Bail if this CPU doesn't implement PAT. */
|
||
|
if (!(cpu_feature & CPUID_PAT))
|
||
|
return;
|
||
|
#ifdef notyet
|
||
|
if (cpu_vendor_id != CPU_VENDOR_INTEL ||
|
||
|
(I386_CPU_FAMILY(cpu_id) == 6 && I386_CPU_MODEL(cpu_id) >= 0xe)) {
|
||
|
#else
|
||
|
if (!0) {
|
||
|
#endif
|
||
|
/*
|
||
|
* Leave the indices 0-3 at the default of WB, WT, UC, and UC-.
|
||
|
* Program 4 and 5 as WP and WC.
|
||
|
* Leave 6 and 7 as UC and UC-.
|
||
|
*/
|
||
|
pat_msr = rdmsr(MSR_PAT);
|
||
|
pat_msr &= ~(PAT_MASK(4) | PAT_MASK(5));
|
||
|
pat_msr |= PAT_VALUE(4, PAT_WRITE_PROTECTED) |
|
||
|
PAT_VALUE(5, PAT_WRITE_COMBINING);
|
||
|
pat_works = 1;
|
||
|
} else {
|
||
|
/*
|
||
|
* Due to some Intel errata, we can only safely use the lower 4
|
||
|
* PAT entries. Thus, just replace PAT Index 2 with WC instead
|
||
|
* of UC-.
|
||
|
*
|
||
|
* Intel Pentium III Processor Specification Update
|
||
|
* Errata E.27 (Upper Four PAT Entries Not Usable With Mode B
|
||
|
* or Mode C Paging)
|
||
|
*
|
||
|
* Intel Pentium IV Processor Specification Update
|
||
|
* Errata N46 (PAT Index MSB May Be Calculated Incorrectly)
|
||
|
*/
|
||
|
pat_msr = rdmsr(MSR_PAT);
|
||
|
pat_msr &= ~PAT_MASK(2);
|
||
|
pat_msr |= PAT_VALUE(2, PAT_WRITE_COMBINING);
|
||
|
pat_works = 0;
|
||
|
}
|
||
|
wrmsr(MSR_PAT, pat_msr);
|
||
|
}
|
||
|
/*
|
||
|
* Bootstrap the system enough to run with virtual memory.
|
||
| ... | ... | |
|
}
|
||
|
#endif
|
||
|
pmap_init_pat();
|
||
|
/*
|
||
|
* We need to finish setting up the globaldata page for the BSP.
|
||
|
* locore has already populated the page table for the mdglobaldata
|
||
| ... | ... | |
|
* Low level helper routines.....
|
||
|
***************************************************/
|
||
|
/*
|
||
|
* Determine the appropriate bits to set in a PTE or PDE for a specified
|
||
|
* caching mode.
|
||
|
*/
|
||
|
static int
|
||
|
pmap_cache_bits(int mode, boolean_t is_pde)
|
||
|
{
|
||
|
int pat_flag, pat_index, cache_bits;
|
||
|
/* The PAT bit is different for PTE's and PDE's. */
|
||
|
pat_flag = is_pde ? PG_PDE_PAT : PG_PTE_PAT;
|
||
|
/* If we don't support PAT, map extended modes to older ones. */
|
||
|
if (!(cpu_feature & CPUID_PAT)) {
|
||
|
switch (mode) {
|
||
|
case PAT_UNCACHEABLE:
|
||
|
case PAT_WRITE_THROUGH:
|
||
|
case PAT_WRITE_BACK:
|
||
|
break;
|
||
|
case PAT_UNCACHED:
|
||
|
case PAT_WRITE_COMBINING:
|
||
|
case PAT_WRITE_PROTECTED:
|
||
|
mode = PAT_UNCACHEABLE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Map the caching mode to a PAT index. */
|
||
|
if (pat_works) {
|
||
|
switch (mode) {
|
||
|
case PAT_UNCACHEABLE:
|
||
|
pat_index = 3;
|
||
|
break;
|
||
|
case PAT_WRITE_THROUGH:
|
||
|
pat_index = 1;
|
||
|
break;
|
||
|
case PAT_WRITE_BACK:
|
||
|
pat_index = 0;
|
||
|
break;
|
||
|
case PAT_UNCACHED:
|
||
|
pat_index = 2;
|
||
|
break;
|
||
|
case PAT_WRITE_COMBINING:
|
||
|
pat_index = 5;
|
||
|
break;
|
||
|
case PAT_WRITE_PROTECTED:
|
||
|
pat_index = 4;
|
||
|
break;
|
||
|
default:
|
||
|
panic("Unknown caching mode %d\n", mode);
|
||
|
}
|
||
|
} else {
|
||
|
switch (mode) {
|
||
|
case PAT_UNCACHED:
|
||
|
case PAT_UNCACHEABLE:
|
||
|
case PAT_WRITE_PROTECTED:
|
||
|
pat_index = 3;
|
||
|
break;
|
||
|
case PAT_WRITE_THROUGH:
|
||
|
pat_index = 1;
|
||
|
break;
|
||
|
case PAT_WRITE_BACK:
|
||
|
pat_index = 0;
|
||
|
break;
|
||
|
case PAT_WRITE_COMBINING:
|
||
|
pat_index = 2;
|
||
|
break;
|
||
|
default:
|
||
|
panic("Unknown caching mode %d\n", mode);
|
||
|
}
|
||
|
}
|
||
|
/* Map the 3-bit index value into the PAT, PCD, and PWT bits. */
|
||
|
cache_bits = 0;
|
||
|
if (pat_index & 0x4)
|
||
|
cache_bits |= pat_flag;
|
||
|
if (pat_index & 0x2)
|
||
|
cache_bits |= PG_NC_PCD;
|
||
|
if (pat_index & 0x1)
|
||
|
cache_bits |= PG_NC_PWT;
|
||
|
return (cache_bits);
|
||
|
}
|
||
|
#if defined(PMAP_DIAGNOSTIC)
|
||
|
/*
|
||
| ... | ... | |
|
kmem_free(&kernel_map, base, size);
|
||
|
}
|
||
|
int
|
||
|
pmap_change_attr(vm_offset_t va, vm_size_t size, int mode)
|
||
|
{
|
||
|
vm_offset_t base, offset, tmpva;
|
||
|
pt_entry_t *pte;
|
||
|
u_int opte, npte;
|
||
|
pd_entry_t *pde;
|
||
|
pmap_inval_info info;
|
||
|
base = trunc_page(va);
|
||
|
offset = va & PAGE_MASK;
|
||
|
size = roundup(offset + size, PAGE_SIZE);
|
||
|
/*
|
||
|
* Only supported on kernel virtual addresses
|
||
|
*/
|
||
|
if (base < KvaStart)
|
||
|
return (EINVAL);
|
||
|
/* 4MB pages and pages that aren't mapped aren't supported. */
|
||
|
for (tmpva = base; tmpva < (base + size); tmpva += PAGE_SIZE) {
|
||
|
pde = pmap_pde(&kernel_pmap, tmpva);
|
||
|
if (*pde & PG_PS)
|
||
|
return (EINVAL);
|
||
|
if (*pde == 0)
|
||
|
return (EINVAL);
|
||
|
pte = vtopte(tmpva);
|
||
|
if (*pte == 0)
|
||
|
return (EINVAL);
|
||
|
}
|
||
|
pmap_inval_init(&info);
|
||
|
/*
|
||
|
* Ok, all the pages exist and are 4k, so run through them updating
|
||
|
* their cache mode.
|
||
|
*/
|
||
|
for (tmpva = base; size > 0; ) {
|
||
|
pte = vtopte(tmpva);
|
||
|
/*
|
||
|
* The cache mode bits are all in the low 32-bits of the
|
||
|
* PTE, so we can just spin on updating the low 32-bits.
|
||
|
*/
|
||
|
do {
|
||
|
opte = *(u_int *)pte;
|
||
|
npte = opte & ~(PG_PTE_PAT | PG_NC_PCD | PG_NC_PWT);
|
||
|
npte |= pmap_cache_bits(mode, 0);
|
||
|
} while (npte != opte &&
|
||
|
!atomic_cmpset_int((u_int *)pte, opte, npte));
|
||
|
pmap_inval_add(&info, &kernel_pmap, tmpva);
|
||
|
tmpva += PAGE_SIZE;
|
||
|
size -= PAGE_SIZE;
|
||
|
}
|
||
|
/*
|
||
|
* Flush CPU caches to make sure any data isn't cached that shouldn't
|
||
|
* be, etc.
|
||
|
*/
|
||
|
pmap_inval_cache_add(&info, &kernel_pmap, -1);
|
||
|
pmap_inval_flush(&info);
|
||
|
return (0);
|
||
|
}
|
||
|
/*
|
||
|
* perform the pmap work for mincore
|
||
|
*/
|
||
| sys/platform/pc32/i386/pmap_inval.c | ||
|---|---|---|
|
#ifdef SMP
|
||
|
static void
|
||
|
_cpu_invltlb(void *dummy)
|
||
|
_cpu_invltlb(void *dummy __unused)
|
||
|
{
|
||
|
cpu_invltlb();
|
||
|
}
|
||
| ... | ... | |
|
cpu_invlpg(data);
|
||
|
}
|
||
|
static void
|
||
|
_cpu_wbinvl(void *dummy __unused)
|
||
|
{
|
||
|
cpu_wbinvl();
|
||
|
}
|
||
|
#endif
|
||
|
/*
|
||
| ... | ... | |
|
info->pir_flags = 0;
|
||
|
}
|
||
|
#ifdef SMP
|
||
|
/*
|
||
|
* Add a (pmap, va) pair to the invalidation list and protect access
|
||
|
* as appropriate.
|
||
| ... | ... | |
|
void
|
||
|
pmap_inval_add(pmap_inval_info_t info, pmap_t pmap, vm_offset_t va)
|
||
|
{
|
||
|
#ifdef SMP
|
||
|
if ((info->pir_flags & PIRF_CPUSYNC) == 0) {
|
||
|
info->pir_flags |= PIRF_CPUSYNC;
|
||
|
info->pir_cpusync.cs_run_func = NULL;
|
||
| ... | ... | |
|
} else if (pmap->pm_active & ~info->pir_cpusync.cs_mask) {
|
||
|
lwkt_cpusync_add(pmap->pm_active, &info->pir_cpusync);
|
||
|
}
|
||
|
#else
|
||
|
if (pmap->pm_active == 0)
|
||
|
return;
|
||
|
#endif
|
||
|
if ((info->pir_flags & (PIRF_INVLTLB|PIRF_INVL1PG)) == 0) {
|
||
|
if (va == (vm_offset_t)-1) {
|
||
|
info->pir_flags |= PIRF_INVLTLB;
|
||
|
#ifdef SMP
|
||
|
info->pir_cpusync.cs_fin2_func = _cpu_invltlb;
|
||
|
#endif
|
||
|
} else {
|
||
|
info->pir_flags |= PIRF_INVL1PG;
|
||
|
info->pir_cpusync.cs_data = (void *)va;
|
||
|
#ifdef SMP
|
||
|
info->pir_cpusync.cs_fin2_func = _cpu_invl1pg;
|
||
|
#endif
|
||
|
}
|
||
|
} else {
|
||
|
info->pir_flags |= PIRF_INVLTLB;
|
||
|
#ifdef SMP
|
||
|
info->pir_cpusync.cs_fin2_func = _cpu_invltlb;
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
void
|
||
|
pmap_inval_cache_add(pmap_inval_info_t info, pmap_t pmap,
|
||
|
vm_offset_t va __unused)
|
||
|
{
|
||
|
if ((info->pir_flags & PIRF_CPUSYNC) == 0) {
|
||
|
info->pir_flags |= PIRF_CPUSYNC;
|
||
|
info->pir_cpusync.cs_run_func = NULL;
|
||
|
info->pir_cpusync.cs_fin1_func = NULL;
|
||
|
info->pir_cpusync.cs_fin2_func = NULL;
|
||
|
lwkt_cpusync_start(pmap->pm_active, &info->pir_cpusync);
|
||
|
} else if (pmap->pm_active & ~info->pir_cpusync.cs_mask) {
|
||
|
lwkt_cpusync_add(pmap->pm_active, &info->pir_cpusync);
|
||
|
}
|
||
|
info->pir_flags |= PIRF_WBINVL;
|
||
|
info->pir_cpusync.cs_fin2_func = _cpu_wbinvl;
|
||
|
}
|
||
|
/*
|
||
|
* Synchronize changes with target cpus.
|
||
|
*/
|
||
|
void
|
||
|
pmap_inval_flush(pmap_inval_info_t info)
|
||
|
{
|
||
|
#ifdef SMP
|
||
|
if (info->pir_flags & PIRF_CPUSYNC)
|
||
|
lwkt_cpusync_finish(&info->pir_cpusync);
|
||
|
#else
|
||
|
info->pir_flags = 0;
|
||
|
}
|
||
|
#else /* !SMP */
|
||
|
void
|
||
|
pmap_inval_add(pmap_inval_info_t info, pmap_t pmap, vm_offset_t va)
|
||
|
{
|
||
|
if (pmap->pm_active == 0)
|
||
|
return;
|
||
|
if ((info->pir_flags & (PIRF_INVLTLB|PIRF_INVL1PG)) == 0) {
|
||
|
if (va == (vm_offset_t)-1) {
|
||
|
info->pir_flags |= PIRF_INVLTLB;
|
||
|
} else {
|
||
|
info->pir_flags |= PIRF_INVL1PG;
|
||
|
info->pir_cpusync.cs_data = (void *)va;
|
||
|
}
|
||
|
} else {
|
||
|
info->pir_flags |= PIRF_INVLTLB;
|
||
|
}
|
||
|
}
|
||
|
void
|
||
|
pmap_inval_cache_add(pmap_inval_info_t info, pmap_t pmap, vm_offset_t va)
|
||
|
{
|
||
|
if (pmap->pm_active == 0)
|
||
|
return;
|
||
|
info->pir_flags |= PIRF_WBINVL;
|
||
|
}
|
||
|
void
|
||
|
pmap_inval_flush(pmap_inval_info_t info)
|
||
|
{
|
||
|
if (info->pir_flags & PIRF_INVLTLB)
|
||
|
cpu_invltlb();
|
||
|
else if (info->pir_flags & PIRF_INVL1PG)
|
||
|
cpu_invlpg(info->pir_cpusync.cs_data);
|
||
|
#endif
|
||
|
if (info->pir_flags & PIRF_WBINVL)
|
||
|
cpu_wbinvl();
|
||
|
info->pir_flags = 0;
|
||
|
}
|
||
|
#endif /* SMP */
|
||
| sys/platform/pc32/include/pmap.h | ||
|---|---|---|
|
#ifdef SMP
|
||
|
void pmap_set_opt (void);
|
||
|
#endif
|
||
|
void pmap_init_pat(void);
|
||
|
int pmap_change_attr(vm_offset_t, vm_size_t, int);
|
||
|
#endif /* _KERNEL */
|
||
| sys/platform/pc32/include/pmap_inval.h | ||
|---|---|---|
|
#define PIRF_INVLTLB 0x0001 /* request invalidation of whole table */
|
||
|
#define PIRF_INVL1PG 0x0002 /* else request invalidation of one page */
|
||
|
#define PIRF_CPUSYNC 0x0004 /* cpusync is currently active */
|
||
|
#define PIRF_WBINVL 0x0008 /* request cache invalidation */
|
||
|
#ifdef _KERNEL
|
||
| ... | ... | |
|
void pmap_inval_init(pmap_inval_info_t);
|
||
|
void pmap_inval_add(pmap_inval_info_t, pmap_t, vm_offset_t);
|
||
|
void pmap_inval_cache_add(pmap_inval_info_t, pmap_t, vm_offset_t);
|
||
|
void pmap_inval_flush(pmap_inval_info_t);
|
||
|
#endif
|
||