rock.c revision 125be069be21bcb7b94bd4525c19eb424bc56276
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/archsystm.h>
#include <sys/machparam.h>
#include <sys/machsystm.h>
#include <sys/elf_SPARC.h>
#include <sys/dditypes.h>
#include <sys/cpu_module.h>
#include <sys/prom_debug.h>
#include <sys/prom_plat.h>
#include <sys/sysmacros.h>
#include <sys/machtrap.h>
#include <sys/hypervisor_api.h>
#include <sys/rock_hypervisor_api.h>
#include <vm/hat_sfmmu.h>
char cpu_module_name[] = "SUNW,UltraSPARC-AT10";
static hsvc_info_t rock_tm_hsvc = {
HSVC_REV_1, /* HSVC rev num */
NULL, /* Private */
HSVC_GROUP_TM, /* Requested API Group */
ROCK_HSVC_MAJOR, /* Requested Major */
ROCK_HSVC_MINOR, /* Requested Minor */
cpu_module_name /* Module name */
};
static hsvc_info_t rock_mmu_ext_hsvc = {
HSVC_REV_1, /* HSVC rev num */
NULL, /* Private */
HSVC_GROUP_RKMMU_EXT, /* Requested API Group */
ROCK_HSVC_MAJOR, /* Requested Major */
ROCK_HSVC_MINOR, /* Requested Minor */
cpu_module_name /* Module name */
};
sfmmu_t *);
/*
* come first in the pagesize order register.
*/
int pgsz_order_shared_first = 1;
#define MCOREID_MASK 0x1E
#define MCOREID_SHIFT 1
void
cpu_setup(void)
{
extern int cpc_has_overflow_intr;
int status;
/*
* The setup common to all CPU modules is done in cpu_setup_common
* routine.
*/
/*
* Rock's max nctxs is 64K. Set it accordingly.
*/
/*
* Rock I$ is non-coherent.
*/
#ifdef DEBUG
/*
* These should always be present on Rock
*/
if (cpu_hwcap_flags == 0)
#endif
if (use_page_coloring) {
do_pg_coloring = 1;
}
/*
* Rock generates hpriv performance event trap instead of pic overflow
* trap. To get the attention of the guest hv in-turn generates pic
* overflow trap. Therefore enable support for that.
*/
/*
* Enable 4M pages for OOB.
*/
/*
* hv_tm_enable is a part of TM group. We need to
* negotiate that API group before we can use it.
*/
"major: 0x%lx minor: 0x%lx group: 0x%x errno: %d",
}
/*
* Negotiate API group for rock mmu extensions.
*/
(uint64_t)ROCK_HSVC_MINOR)) {
"major: 0x%lx minor: 0x%lx group: 0x%x errno: %d",
status);
}
}
/*
* Set the magic constants of the implementation.
*/
void
{
/*
* The Cache node is optional in MD. Therefore in case it
* does not exist, use hardcoded values.
*/
#ifdef DEBUG
/*
* ...that said, we do want this info to come from the MD.
*/
cpunode->ecache_associativity == 0) {
}
#endif
if (cpunode->ecache_size == 0)
if (cpunode->ecache_linesize == 0)
if (cpunode->ecache_associativity == 0)
}
void
{
if (pfn != -1) {
/* sparc needs 8-byte align */
}
}
}
void
{
/*
* The cpu_ipipe and cpu_fpu fields are initialized based on
* the execution unit sharing information from the MD. They
* default to the CPU id in the absence of such information.
*/
/*
* The cpu_chip field is initialized based on the information
* in the MD and assume that all cpus within a chip
* share the same L2 cache. If no such info is available, we
* set the cpu to CPU_CHIPID_INVALID.
*/
}
void
{
}
/*ARGSUSED*/
void
{
}
/*
* cpu_feature_init
*
* This function is called once per strand.
*/
void
cpu_feature_init(void)
{
/*
* Enable or disable for each cpu if hypervisor API is negotiated.
*/
if (hsvc_tm_available == B_TRUE)
}
/*
* Flush specified address range from I$ via hv_mem_iflush interface
* Note that the hypervisor interface expects physical address range
* and can flush less than the requested size.
*/
void
{
/*
* Do not clear the I-cache after bcopy.
* The default value is 0. This flag made be
*/
return;
if (!tba_taken_over)
/*
* Very early in boot, va_to_pa() will try to call back
* into OBP. Very *very* early in boot, this will fail
* because we haven't set up the OBP callback handler.
* (Without this check, kmdb boot will fail.)
*/
return;
/*
* Only flush the required length up to a PAGESIZE.
*/
/*
* Flush I$ up to the page bounday. This call should never
* fail. If it does, we panic the system as I$ may contain
* stale instructions, which can result in silent data
* corruption.
*/
}
}
}
/*
* There are no Hypervisor trapstat(1m) interfaces for Rock
* If trapstat(1m) wants to do its thing, it will have to
* take over all TLB miss handling.
*/
int
cpu_trapstat_conf(int cmd)
{
int status;
switch (cmd) {
case CPU_TSTATCONF_INIT:
case CPU_TSTATCONF_FINI:
case CPU_TSTATCONF_ENABLE:
case CPU_TSTATCONF_DISABLE:
break;
default:
break;
}
return (status);
}
/*ARGSUSED*/
void
{
}
#define MAX_PAGE_COLORS_SHIFT (5)
/*ARGSUSED*/
{
}
/*
* this macro rotates value "x" n steps to the right
* mask consists of "n + m" bits
* ASSERT(x < (1 << (n + m));
*/
/*
* on Rock, the hash cache index is calculated as follows:
* pa[47:43]^pa[42:38]^pa[37:33]^pa[32:28]^
* pa[27:23]^pa[22:18]^pa[17:13].pa[12:6]
* That is, every 5 bits is folded and XORd together. Page sizes
* differ by 3 bits, which is a factor of 8. This function computes
* the next sequential color by rotating by 3 steps within a field of 5 bits
* for every page size.
*/
void
{
(MAX_PAGE_COLORS_SHIFT - rot));
}
}
}
{
}
#if MMU_PAGE_SIZES > 8
#endif
{
}
/*ARGSUSED*/
{
return (color);
}
{
return (0);
}
/*ARGSUSED*/
{
do {
ceq_mask) == 0);
if (nsqn_color != base_sqn_color)
continue;
break;
wrap++;
return (cpfn);
}
void
{
int i;
for (i = 0; i < mmu_page_sizes; i++) {
}
/*
* initialise conversion table between page colors and
* sequential colors
*/
}
/*
* group colorequiv colors on Rock by low order bits of the color first
*/
void
{
if (colorequiv > 1) {
int i;
if (sv_a > 15)
sv_a = 15;
for (i = 0; i < MMU_PAGE_SIZES; i++) {
continue;
while ((colors >> a) == 0)
a--;
if (a > (colorequivszc[i] & 0xf) +
(colorequivszc[i] >> 4)) {
if (a <= nequiv_shades_log2[i]) {
colorequivszc[i] = (uchar_t)a;
} else {
colorequivszc[i] =
((a - nequiv_shades_log2[i]) << 4) |
}
}
}
}
}
/*
* Calculate the page sizes needed to program Rock TLB page size register.
* The invctx parameter is a flag which indicates that it will be necessary to
* synchronize by invalidating contexts if the sfmmu pagesize register is
* updated.
*/
void
{
uint64_t pgsz_order = 0;
int private_pgsz_num = 0;
int shared_pgsz_num = 0;
int tot_pgsz_num;
int ret;
int i;
/*
* The hatlock must be held in all cases except when the sfmmu is
* being initialized by hat_alloc() or we are calling hat_dup(), in
* these cases no other thread will be using the sfmmu yet.
*/
if (pgsz_search_on == 0)
return;
/* Always enable 8K private mappings */
/* Enable 64K private mappings unless specifically disabled */
}
/*
* First check for ISM segments not in an SCD. The algorithm for
* creating an SCD is to create one when an (D)ISM segment is attached
* unless the process's shared segments are a subset of an SCD which
* already exists.
*
* This situation also arises when we attach to more than the maximum
* number of (D)ISM segments defined in the region bit map
* (currently 64).
*
* We have set mmu_disable_ism_large_pages to force ISM segments to use
* only 4M and 256M pages.
*/
}
}
/* Now check for regions not included in the SCD. */
if (!ret) {
}
} else {
}
/*
* If the process is part of an SCD then enable 4M and 256M shared
* page sizes - unless these are specifically disabled. If the 4M
* shared page size is specifically disabled and the process has (D)ISM
* segments attached or 4M regions then enable the private 4M page size.
* If the 256M shared page size is disabled and the process has a 256M
* page size region then enable the 256M private page size. The trap
* handler looks at the shared page sizes enabled and if a shared
* mapping does not correspond to one these sizes then it is treated
* as a private mapping.
*
* The SCD includes the process's main text segment and (D)ISM segments
* but we only enable the 4M shared page size so an 8K main text
* segment will be treated as private due to the trap handler support.
*
* Note that for simplicity the ordering of the shared page sizes is
* hard coded.
*/
shared_pgsz_mask = 0;
(1 << TTE4M))) {
}
} else {
}
}
}
for (i = 0; i < tot_pgsz_num; i++) {
break;
}
/*
* If either we've reached the maximum number of page sizes or the
* next element is 0, indicating the end of the list, then both the
* entries and their number in both arrays is the same and we return.
*/
if ((i == tot_pgsz_num) && (i == MAX_PGSZ_SEARCH_ORDER ||
sfmmup->sfmmu_pgsz_order_hv[i] == 0)) {
return;
}
/* Otherwise update the sw page size register setting */
if (invctx) {
}
for (i = 0; i < tot_pgsz_num; i++) {
}
/* Disable next entry in search list to mark the end */
if (i < MAX_PGSZ_SEARCH_ORDER) {
sfmmup->sfmmu_pgsz_order_hv[i] = 0;
}
}
/*
* Encode the Rock TLB page size register.
*
* Input:
* pgsz_order, ordered list of page sizes, private and shared, the order
* between these depends on the pgsz_order_shared_first config variable.
* private_pgsz_num, number of private page sizes.
* shared_pgsz_num, number of shared page sizes.
* Output:
* pgsz_order_hv contains the encoded pagesize search order for the hv
* pgsz_map field contains the page size bit map used by the trap
* handler to prevent unauthorized shared page sizes being used.
*/
static void
{
int i;
int tot_pgsz_num;
int first_pgsz_num;
ASSERT(private_pgsz_num > 0);
if (pgsz_order_shared_first) {
} else {
}
for (i = 0; i < tot_pgsz_num; i++) {
if (i < first_pgsz_num) {
if (pgsz_order_shared_first) {
}
} else {
if (!pgsz_order_shared_first) {
}
}
pgsz_order >>= 4;
pgsz_order_hv[i] = pgsz_entry;
}
}
/*
* The function returns the mmu-specific values for the
* hat's disable_large_pages, disable_ism_large_pages, and
* disable_auto_data_large_pages and
* disable_text_data_large_pages variables.
*/
{
uint_t pages_disable = 0;
} else if (flag == HAT_LOAD_SHARE) {
} else if (flag == HAT_AUTO_DATA) {
} else if (flag == HAT_AUTO_TEXT) {
}
return (pages_disable);
}
/*
* Uses private and shared page size bitmaps to produce an ordered list
* of page sizes and counts to be passed to encode_pgsz_order().
*
* Input:
* private_pgsz_mask, bit map of private page sizes.
* shared_pgsz_mask, bit map of private page sizes.
* sfmmup, pointer to hat structure.
*
* Output:
* pgsz_order, ordered list of page sizes.
* private_pgsz_num, number of private page sizes in pgsz_order.
* shared_pgsz_num, number of shared page sizes in pgsz_order.
*/
static void
{
*private_pgsz_num = 0;
*shared_pgsz_num = 0;
*pgsz_order = 0;
/* Sort pages by area mapped */
for (i = 0; i < mmu_page_sizes; i++) {
}
for (j = 0; j < mmu_page_sizes; j++) {
max = i;
}
}
/* Add shared page sizes to page order if these come first */
if (pgsz_order_shared_first) {
*pgsz_order = TTE256M;
(*shared_pgsz_num)++;
}
(*shared_pgsz_num)++;
}
}
/* Add private page sizes to page order */
for (i = 0; i < mmu_page_sizes; i++) {
*pgsz_order |= (tmp_pgsz[i] <<
(*private_pgsz_num)++;
}
}
/* Add shared page sizes to page order if these come last */
if (!pgsz_order_shared_first) {
*pgsz_order |= (TTE256M <<
(*shared_pgsz_num)++;
}
*pgsz_order |= (TTE4M <<
(*shared_pgsz_num)++;
}
}
ASSERT(*pgsz_order);
}
/*
* This routine is called without holding the hat lock to determine
* whether the process's optimal page size order has changed significantly
* since the page size register was last set. If it has changed we get the
* hat lock and call mmu_set_pgsz_order() to update the effective pagesize
* order.
*/
void
{
int page_order_changed;
int pgsz_count = 0;
if (pgsz_search_on == 0)
return;
/*
* Check if ttecnt has changed significantly, since the last time we
* were called. If the shared page sizes have changed then this is
* handled by mmu_set_pgsz_order() being called directly when we join
* the SCD.
*/
for (i = 0; i < mmu_page_sizes; i++) {
break;
}
if (i == mmu_page_sizes) {
return;
}
/* Sort pages by area mapped */
for (i = 0; i < mmu_page_sizes; i++) {
}
for (j = 0; j < mmu_page_sizes; j++) {
max = i;
}
}
/*
* Check if the order of the private page sizes has changed. We call
* mmu_set_pgsz_order() directly if additional page sizes are used,
* so we can assume that the number of entries is unchanged.
*/
if (pgsz_order_shared_first) {
/* skip over shared pgsz entries */
pgsz_count++;
}
}
i = 0;
page_order_changed = 0;
(pgsz_count < MAX_PGSZ_SEARCH_ORDER)) {
page_order_changed = 1;
break;
}
pgsz_count++;
i++;
}
if (page_order_changed) {
/* Save old values of ttecnt */
for (i = 0; i < mmu_page_sizes; i++) {
}
}
}
/*
* If the mmu extension API is supported and pgsz_search_on is set,
* patch out the instruction to branch over the hypervisor call in
* sfmmu_load_mmustate().
*/
void
{
/* patch in hcall to set pgsz order */
}
}