mach_i86mmu.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/vm_machparam.h>
#include <vm/seg_kmem.h>
#include <sys/x86_archext.h>
#include <sys/archsystm.h>
#include <sys/ddidmareq.h>
#include <sys/controlregs.h>
{
} else {
}
}
return (addr1);
}
/*
* This routine is like page_numtopp, but accepts only free pages, which
* it allocates (unfrees) and returns with the exclusive lock held.
*/
page_t *
{
return (NULL);
}
return (NULL);
}
goto retry;
}
return (NULL);
}
goto retry;
}
/* If associated with a vnode, destroy mappings */
return (NULL);
}
goto retry;
}
}
return (NULL);
}
return (pp);
}
/*
* The boot loader doesn't use PAE page tables for 32 bit platforms
* so the definitions in hat_pte for LEVEL_SHIFT, etc. don't apply.
*/
#if defined(__i386) /* 32 bit boot loader */
#define BOOT_TOP_LEVEL 1
#define BOOT_PTES_PER_TABLE 1024
#define BOOT_PADDR 0xfffff000
#define BOOT_TOP_LEVEL 3
#define BOOT_PTES_PER_TABLE 512
#endif /* __amd64 */
#define BOOT_SHIFT(l) (boot_shift[l])
#define BOOT_MASK(l) (~BOOT_OFFSET(l))
/*
* Flag is not set early in boot. Once it is set we are no longer
* using boot's page tables.
*/
uint_t khat_running = 0;
/*
* Probe the boot loader's page tables to find the first mapping
* including va (or higher) and return non-zero if one is found.
* va is updated to the starting address and len to the pagesize.
* pp will be set to point to the 1st page_t of the mapped page(s).
*
* Note that if va is in the middle of a large page, the returned va
* will be less than what was asked for.
*
* This works by walking the actual page table's currently in use
* and rooted at control register 3. This code has the following fundamental
* assumptions:
* of boot pte_t is compatibile with uintptr_t
* - The 64 bit mode boot loader has enabled NX bit usage
* - The pagetables allocated by boot have identity mappings, ie.
* Virtual address == Physical address
*/
int
{
level_t l = BOOT_TOP_LEVEL;
*len = 0;
*pfn = PFN_INVALID;
*prot = 0;
l = BOOT_TOP_LEVEL;
ptable = top_ptable;
for (;;) {
if (IN_VA_HOLE(probe_va))
/*
* then we can bump VA by this level's pagesize and try again.
* When the probe_va wraps around, we are done.
*/
return (0);
goto restart_new_va;
}
/*
* If this entry is a pointer to a lower level page table
* go down to it.
*/
ASSERT(l > 0);
--l;
continue;
}
/*
* We found a boot level page table entry
*/
*prot |= PROT_WRITE;
/*
* pt_nx is cleared if processor doesn't support NX bit
*/
return (1);
}
}
/*
* Destroy a boot loader page table 4K mapping.
* See hat_boot_probe() for assumptions.
*/
void
{
/*
* Walk down the page tables, which are 1 to 1 mapped, to the
* desired mapping.
*/
panic("hat_boot_demap(): no pte at desired addr");
if (level == 0)
break;
panic("hat_boot_demap(): large page at va");
}
/*
* We found a boot level page table entry, invalidate it
*/
}
/*
* Change a boot loader page table 4K mapping.
* Returns the pfn of the old mapping.
* See hat_boot_probe() for assumptions.
*/
{
/*
* Walk down the page tables, which are 1 to 1 mapped, to the
* desired mapping.
*/
if (level == 0)
break;
panic("hat_boot_remap(): no pte at desired addr");
panic("hat_boot_remap(): large page at va");
}
/*
* We found a boot level page table entry, change it and return
* the old pfn. Assume full permissions.
*/
return (old_pfn);
}
/*
* This procedure is callable only while the boot loader is in charge
* of the MMU. It assumes that PA == VA for page table pointers.
*/
{
if (khat_running)
panic("va_to_pfn(): called too late\n");
return (PFN_INVALID);
return (PFN_INVALID);
return (pfn);
}
/*
* Routine to pre-allocate any htable's and hments that should be needed in
* hat_kern_setup(). It computes how many pagetables it needs by walking the
* boot loader's page tables.
*/
void
{
level_t l;
extern pgcnt_t boot_npages;
/*
* Walk the boot loader's page tables and figure out
* how many tables and page mappings there will be.
*/
/*
* At each level, if the last_va falls into a new htable,
* increment table_cnt. We can stop at the 1st level where
* they are in the same htable.
*/
if (size == MMU_PAGESIZE)
start_level = 0;
else
start_level = 1;
break;
++table_cnt;
}
}
/*
* Besides the boot loader mappings, we're going to fill in
* the entire top level page table for the kernel. Make sure there's
* enough reserve for that too.
*/
/*
* If we still have pages that need page_t's created for them, then
* make sure we create the pagetables needed to map them in.
*
* (yes. We need pagetables to map the page_t's for the unmapped
* pages. We also need pagetables to map the vmem structures
* allocated to support the VA range into which they are mapped.
* Does your head hurt yet?)
*/
if (boot_npages < npages) {
/*
* Number of pages needed for all the new pages_ts. This
* assumes that they will all be mapped consecutively.
*/
/*
* Number of level 0 pagetables needed to map these pages.
* The '+1' is to handle the likely case that the address
* range doesn't align with a pagetable boundary.
*/
/*
* We also add in some extra to account for the higher level
* pagetables and for the vmem structures that get
* allocated along the way.
*/
}
#if defined(__i386)
/*
* The 32 bit PAE hat allocates tables one level below the top when
* kernelbase isn't 1 Gig aligned. We'll just be sloppy and allocate
* a bunch more to the reserve. Any unused will be returned later.
* Note we've already counted these mappings, just not the extra
* pagetables.
*/
#endif
/*
* Add 1/4 more into table_cnt for extra slop. The unused
* slop is freed back when we htable_adjust_reserve() later.
*/
/*
* We only need mapping entries (hments) for shared pages.
* This should be far, far fewer than the total possible,
* We'll allocate enough for 1/16 of all possible PTEs.
*/
/*
*/
}
extern void enable_pae(uintptr_t);
extern void setup_121_andcall();
/*
* We need to setup a 1:1 (virtual to physical) mapping for the
* page containing enable_pae() in the new kernel hat.
*/
void
activate_pae(void *pages)
{
#if defined(__amd64)
int turning_on_pae = 0; /* it's already on */
#endif
if (!turning_on_pae) {
/*
* Finish setup for x86pte_access_pagetable()
*/
/*
* switch off of boot's page tables onto the ones we've built
*/
khat_running = 1;
return;
}
#if defined(__i386)
panic("cr3 value would be > 4G on 32 bit PAE");
/*
* find the htable containing the physical address that would be
* an identity mapping for enable_pae, save the current pte,
* then fill in the identity mapping
*/
if (pfn == PFN_INVALID)
panic("activate_pae(): va_to_pfn(enable_pae) failed");
/*
* Finish setup for x86pte_access_pagetable(), this has to be
* done after the last reference to a newly built page table and
* before switching to the newly built pagetables.
*/
/*
* now switch to kernel hat activating PAE
*/
khat_running = 1;
/*
* release the mapping we used for the kernel hat
*/
#endif /* __i386 */
}
/*
* Function to set the EFER.NXE bit if we want to use No eXecute.
* Note that since this is called from manually relocated code from
* mpcore.s, we have to use an indirect call with set_nxe_func.
* This is due to the "call" instruction always being PC relative,
* unless you go through an indirect pointer in memory.
*/
static void
set_nxe(void)
{
return;
/*
* AMD64 EFER is model specific register #0xc0000080 and NXE is bit 11
*/
efer |= AMD_EFER_NXE;
}
void (*set_nxe_func)(void) = set_nxe;
/*
* This routine handles the work of creating the kernel's initial mappings
* by deciphering the mappings in the page tables created by the boot program.
*
* We maintain large page mappings, but only to a level 1 pagesize.
* The boot loader can only add new mappings once this function starts.
* In particular it can not change the pagesize used for any existing
* mappings or this code breaks!
*/
void
hat_kern_setup(void)
{
void *pages;
/*
* activate AMD processor NX bit support
*/
set_nxe();
/*
* Allocate 3 initial page addresses for x86pte_cpu_init().
*/
/*
* next allocate the kernel hat's top level
*/
/*
* Now walk through the address space copying all the page mappings.
*/
va = 0;
last_size = 0;
cnt = 0;
#ifdef DEBUG
HKS_DBG(" Start VA / PERM / PFN / # Mappings\n");
#endif
++cnt;
} else {
if (cnt) {
#ifdef DEBUG
" / L" : " / -");
HKS_DBG("----skip----\n");
#endif /* DEBUG */
}
cnt = 1;
}
}
if (cnt != 0) {
#ifdef DEBUG
#endif
}
#if defined(__i386)
#endif /* __i386 */
/*
* Now switch cr3 to the newly built page tables. This includes
* turning on PAE for 32 bit if necessary.
*/
}