OpenSolaris_b135/uts/sun4v/os/lpad.c

/*
 * 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 */