lpad.c revision 1ae0874509b6811fdde1dfd46f0d93fd09867a3f
/*
* 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
* or http://www.opensolaris.org/os/licensing.
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/types.h>
#include <sys/machsystm.h>
#include <sys/machparam.h>
#include <sys/cmn_err.h>
#include <sys/cpuvar.h>
#include <sys/note.h>
#include <sys/hypervisor_api.h>
#include <sys/lpad.h>
typedef struct {
uint64_t inuse;
uint64_t buf[LPAD_SIZE / sizeof (uint64_t)];
} lpad_t;
/*
* A global pool of landing pad memory. Currently, CPUs are only
* brought into the system one at a time, so the pool is only a
* single landing pad. In the future, it may be desirable to bring
* CPUs into the systems in parallel. At that time, the size of
* the pool can be increased by changing the pool size constant.
*/
#define LPAD_POOL_SIZE 1
static lpad_t lpad_pool[LPAD_POOL_SIZE];
#ifdef DEBUG
static int lpad_dbg = 0;
#define LPAD_DBG if (lpad_dbg) printf
#define LPAD_DUMP_DATA lpad_dump_data
static void lpad_dump_data(uint64_t *lpd_start, uint64_t *lpd_end);
#else /* DEBUG */
#define LPAD_DBG _NOTE(CONSTCOND) if (0) printf
#define LPAD_DUMP_DATA
#endif /* DEBUG */
extern void mach_cpu_startup(uint64_t rabase, uint64_t memsize);
extern void mach_cpu_startup_end(void);
extern int promif_in_cif(void);
static lpad_t *lpad_alloc(void);
uint64_t *
lpad_setup(int cpuid, uint64_t pc, uint64_t arg)
{
lpad_t *lpp;
uint64_t textsz;
uint64_t datasz;
lpad_data_t *lpd;
lpad_map_t *lpm;
/* external parameters */
extern caddr_t textva;
extern caddr_t datava;
extern tte_t ktext_tte;
extern tte_t kdata_tte;
extern caddr_t mmu_fault_status_area;
LPAD_DBG("lpad_setup...\n");
if ((cpuid < 0) || (cpuid > NCPU)) {
cmn_err(CE_PANIC, "lpad_setup: invalid cpuid");
}
/* allocate our landing pad */
if ((lpp = lpad_alloc()) == NULL) {
cmn_err(CE_PANIC, "lpad_setup: unable to allocate lpad");
}
/* calculate the size of our text */
textsz = (uint64_t)mach_cpu_startup_end - (uint64_t)mach_cpu_startup;
LPAD_DBG("lpad textsz=%ld\n", textsz);
ASSERT(textsz <= LPAD_TEXT_SIZE);
/* copy over text section */
bcopy((void *)mach_cpu_startup, lpp->buf, textsz);
lpd = (lpad_data_t *)(((caddr_t)lpp->buf) + LPAD_TEXT_SIZE);
lpm = (lpad_map_t *)lpd->map;
ASSERT(mmu_fault_status_area);
bzero(lpd, LPAD_TEXT_SIZE);
lpd->magic = LPAD_MAGIC_VAL;
lpd->inuse = &(lpp->inuse);
lpd->mmfsa_ra = va_to_pa(mmu_fault_status_area) + (MMFSA_SIZE * cpuid);
lpd->pc = pc;
lpd->arg = arg;
/*
* List of mappings:
*
* - permanent inst/data mapping for kernel text
* - permanent data mapping for kernel data
* - non-permanent inst mapping for kernel data,
* required for landing pad text
*/
lpd->nmap = 3;
/* verify the lpad has enough room for the data */
datasz = sizeof (lpad_data_t);
datasz += (lpd->nmap - 1) * sizeof (lpad_map_t);
ASSERT(datasz <= LPAD_DATA_SIZE);
/*
* Kernel Text Mapping
*/
lpm->va = (uint64_t)textva;
lpm->tte = ktext_tte;
lpm->flag_mmuflags = (MAP_ITLB | MAP_DTLB);
lpm->flag_perm = 1;
lpm++;
/*
* Kernel Data Mapping
*/
lpm->va = (uint64_t)datava;
lpm->tte = kdata_tte;
lpm->flag_mmuflags = MAP_DTLB;
lpm->flag_perm = 1;
lpm++;
/*
* Landing Pad Text Mapping
*
* Because this mapping should not be permanent,
* the permanent mapping above cannot be used.
*/
lpm->va = (uint64_t)datava;
lpm->tte = kdata_tte;
lpm->flag_mmuflags = MAP_ITLB;
lpm->flag_perm = 0;
lpm++;
ASSERT(((uint64_t)lpm - (uint64_t)lpd) == datasz);
LPAD_DBG("copied %ld bytes of data into lpad\n", datasz);
LPAD_DUMP_DATA((uint64_t *)lpd, (uint64_t *)lpm);
return (lpp->buf);
}
static lpad_t *
lpad_alloc(void)
{
int idx;
/*
* No locking is required for the global lpad pool since
* it should only be accessed while in the CIF which is
* single threaded. If this assumption changes, locking
* would be required.
*/
ASSERT(promif_in_cif());
/*
* Wait until an lpad buffer becomes available.
*/
for (;;) {
LPAD_DBG("checking lpad pool:\n");
/* walk the lpad buffer array */
for (idx = 0; idx < LPAD_POOL_SIZE; idx++) {
LPAD_DBG("\tchecking lpad_pool[%d]\n", idx);
if (lpad_pool[idx].inuse == 0) {
LPAD_DBG("found empty lpad (%d)\n", idx);
/* mark the buffer as busy */
lpad_pool[idx].inuse = 1;
return (&lpad_pool[idx]);
}
}
}
}
#ifdef DEBUG
static void
lpad_dump_data(uint64_t *lpd_start, uint64_t *lpd_end)
{
uint64_t *lp;
uint_t offset = 0;
if (lpad_dbg == 0)
return;
printf("lpad data:\n");
for (lp = lpd_start; lp < lpd_end; lp++) {
printf("\t0x%02x 0x%016lx\n", offset, *lp);
offset += sizeof (uint64_t);
}
}
#endif /* DEBUG */