/*
* 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 (c) 2010, Intel Corporation.
* All rights reserved.
*/
/*
* Copyright 2011 Joyent, Inc. All rights reserved.
*/
/*
* Welcome to the world of the "real mode platter".
*/
#include <sys/cpu_module.h>
#include <sys/archsystm.h>
#include <sys/machsystm.h>
#include <sys/controlregs.h>
#include <sys/x86_archext.h>
#include <sys/smp_impldefs.h>
#include <sys/sysmacros.h>
#include <sys/mach_mmu.h>
#include <sys/cpu_event.h>
extern cpuset_t cpu_ready_set;
extern void real_mode_start_cpu(void);
extern void real_mode_start_cpu_end(void);
extern void real_mode_stop_cpu_stage1(void);
extern void real_mode_stop_cpu_stage1_end(void);
extern void real_mode_stop_cpu_stage2(void);
extern void real_mode_stop_cpu_stage2_end(void);
void rmp_gdt_init(rm_platter_t *);
/*
* Fill up the real mode platter to make it easy for real mode code to
* kick it off. This area should really be one passed by boot to kernel
* and guaranteed to be below 1MB and aligned to 16 bytes. Should also
* have identical physical and virtual address in paged mode.
*/
int
mach_cpucontext_init(void)
{
return (-1);
/*
* setup secondary cpu bios boot up vector
* Write page offset to 0x467 and page frame number to 0x469.
*/
/* Map real mode platter into kas so kernel can access it. */
/* Copy CPU startup code to rm_platter if it's still during boot. */
if (!plat_dr_enabled()) {
}
return (0);
}
void
mach_cpucontext_fini(void)
{
if (warm_reset_vector)
sizeof (warm_reset_vector));
}
#if defined(__amd64)
extern void *long_mode_64(void);
#endif /* __amd64 */
/*ARGSUSED*/
void
{
#if defined(__amd64)
/* Use the kas address space for the CPU startup thread. */
panic("Cannot initialize CPUs; kernel's 64-bit page tables\n"
"located above 4G in physical memory (@ 0x%lx)",
/*
* Setup pseudo-descriptors for temporary GDT and IDT for use ONLY
* by code in real_mode_start_cpu():
*
* GDT[0]: NULL selector
* GDT[1]: 64-bit CS: Long = 1, Present = 1, bits 12, 11 = 1
*
* Clear the IDT as interrupts will be off and a limit of 0 will cause
* the CPU to triple fault and reset on an NMI, seemingly as reasonable
* a course of action as any other, though it may cause the entire
* platform to reset in some cases...
*/
rm->rm_temp_gdt[0] = 0;
rm->rm_temp_idt_lim = 0;
rm->rm_temp_idt_base = 0;
/*
* Since the CPU needs to jump to protected mode using an identity
* mapped address, we need to calculate it here.
*/
#endif /* __amd64 */
}
static void *
{
/*
* Allocate space for stack, tss, gdt and idt. We round the size
* allotted for cpu_tables up, so that the TSS is on a unique page.
* This is more efficient when running in virtual machines.
*/
panic("mach_cpucontext_alloc_tables: cpu%d misaligned tables",
#if defined(__amd64)
/*
* #DF (double fault).
*/
#endif /* __i386 */
/*
* Set I/O bit map offset equal to size of TSS segment limit
* for no I/O permission map. This will cause all user I/O
* instructions to generate #gp fault.
*/
/*
* Setup kernel tss.
*/
return (ct);
}
void *
{
static int cpu_halt_code_ready;
if (optype == MACH_CPUCONTEXT_OP_STOP) {
/*
* The WARM_RESET_VECTOR has a limitation that the physical
* address written to it must be page-aligned. To work around
* this limitation, the CPU stop code has been splitted into
* two stages.
* The stage 2 code, which implements the real logic to halt
* CPUs, is copied to the rm_cpu_halt_code field in the real
* mode platter. The stage 1 code, which simply jumps to the
* stage 2 code in the rm_cpu_halt_code field, is copied to
* rm_code field in the real mode platter and it may be
* overwritten after the CPU has been stopped.
*/
if (!cpu_halt_code_ready) {
/*
* The rm_cpu_halt_code field in the real mode platter
* is used by the CPU stop code only. So only copy the
* CPU stop stage 2 code into the rm_cpu_halt_code
* field on the first call.
*/
cpu_halt_code_ready = 1;
}
/*
* The rm_code field in the real mode platter is shared by
* the CPU start, CPU stop, CPR and fast reboot code. So copy
* the CPU stop stage 1 code into the rm_code field every time.
*/
rm->rm_cpu_halted = 0;
} else if (optype != MACH_CPUCONTEXT_OP_START) {
return (NULL);
}
/*
* Only need to allocate tables when starting CPU.
* Tables allocated when starting CPU will be reused when stopping CPU.
*/
return (NULL);
}
/* Copy CPU startup code to rm_platter for CPU hot-add operations. */
if (plat_dr_enabled()) {
}
/*
* Now copy all that we've set up onto the real mode platter
* for the real mode code to digest as part of starting the cpu.
*/
/*
* CPU needs to access kernel address space after powering on.
* When hot-adding CPU at runtime, directly use top level page table
* of kas other than the return value of getcr3(). getcr3() returns
* current process's top level page table, which may be different from
* the one of kas.
*/
/*
* For hot-adding CPU at runtime, Machine Check and Performance Counter
* should be disabled. They will be enabled on demand after CPU powers
* on successfully
*/
return (ct);
}
void
{
if (optype == MACH_CPUCONTEXT_OP_START) {
switch (err) {
case 0:
/*
* Save pointer for reuse when stopping CPU.
*/
break;
case ETIMEDOUT:
/*
* The processor was poked, but failed to start before
* we gave up waiting for it. In case it starts later,
* don't free anything.
*/
break;
default:
/*
* Some other, passive, error occurred.
*/
break;
}
} else if (optype == MACH_CPUCONTEXT_OP_STOP) {
switch (err) {
case 0:
/*
* Free resources allocated when starting CPU.
*/
break;
default:
/*
* Don't touch table pointer in case of failure.
*/
break;
}
} else {
ASSERT(0);
}
}
void *
{
}
void
{
}
/*
* "Enter monitor." Called via cross-call from stop_other_cpus().
*/
void
{
if (msg)
/*CONSTANTCONDITION*/
while (1)
;
}
void
mach_cpu_idle(void)
{
i86_halt();
}
void
{
/*
* This cpu is now safe.
*/
*safe = PAUSE_WAIT;
membar_enter(); /* make sure stores are flushed */
/*
* Now we wait. When we are allowed to continue, safe
* will be set to PAUSE_IDLE.
*/
while (*safe != PAUSE_IDLE)
SMT_PAUSE();
}
/*
* Power on the target CPU.
*/
int
{
int error;
if (use_mp == 0 || plat_dr_support_cpu() == 0) {
return (ENOTSUP);
return (EINVAL);
}
/*
* The currrent x86 implementaiton of mp_cpu_configure() and
* mp_cpu_poweron() have a limitation that mp_cpu_poweron() could only
* be called once after calling mp_cpu_configure() for a specific CPU.
* It's because mp_cpu_poweron() will destroy data structure created
* by mp_cpu_configure(). So reject the request if the CPU has already
* been powered on once after calling mp_cpu_configure().
* This limitaiton only affects the p_online syscall and the DR driver
* won't be affected because the DR driver always invoke public CPU
* management interfaces in the predefined order:
* cpu_configure()->cpu_poweron()...->cpu_poweroff()->cpu_unconfigure()
*/
return (ENOTSUP);
}
/*
* Check if there's at least a Mbyte of kmem available
* before attempting to start the cpu.
*/
/*
* Kick off a reap in case that helps us with
* later attempts ..
*/
kmem_reap();
return (ENOMEM);
}
/*
* Start the target CPU. No need to call mach_cpucontext_fini()
* if mach_cpucontext_init() fails.
*/
if ((error = mach_cpucontext_init()) == 0) {
}
if (error != 0) {
return (error);
}
/* Wait for the target cpu to reach READY state. */
delay(1);
}
/* Mark the target CPU as available for mp operation. */
/* Free the space allocated to hold the microcode file */
return (0);
}
static int
{
int i;
/*
* Check if caller holds pdip busy - can cause deadlocks in
* e_ddi_branch_unconfigure(), which calls devfs_clean().
*/
if (DEVI_BUSY_OWNED(pdip)) {
return (EDEADLOCK);
}
for (i = 0; i < MP_CPU_DETACH_MAX_TRIES; i++) {
rv = 0;
break;
}
}
return (rv);
}
/*
* Power off the target CPU.
* Note: cpu_lock will be released and then reacquired.
*/
int
{
int rv = 0;
void *ctx;
extern void cpupm_start(cpu_t *);
extern void cpupm_stop(cpu_t *);
if (use_mp == 0 || plat_dr_support_cpu() == 0) {
return (ENOTSUP);
}
/*
* There is no support for powering off cpu0 yet.
* There are many pieces of code which have a hard dependency on cpu0.
*/
return (ENOTSUP);
};
return (ENXIO);
}
if (mp_cpu_detach_driver(dip) != 0) {
goto out_online;
}
/* Allocate CPU context for stopping */
if (mach_cpucontext_init() != 0) {
goto out_online;
}
goto out_context_fini;
}
cpupm_stop(cp);
}
if (rv != 0) {
goto out_enable_cmi;
}
/* Wait until the target CPU has been halted. */
delay(1);
}
/* CPU_READY has been cleared by mach_cpu_stop. */
return (0);
{
}
}
}
return (rv);
}
/*
* Return vcpu state, since this could be a virtual environment that we
* are unaware of, return "unknown".
*/
/* ARGSUSED */
int
{
return (VCPU_STATE_UNKNOWN);
}