#include <sys/types.h>
#include <sys/systm.h>
#include <sys/cpuvar.h>
#include <sys/cpu_module.h>
#include <sys/kmem.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/promif.h>
#include <sys/cpu.h>
#include <sys/cpu_event.h>
#include <sys/sunndi.h>
#include <sys/fs/dv_node.h>
#include <vm/hat_i86.h>
#include <vm/as.h>
extern cpuset_t cpu_ready_set;
extern int mp_start_cpu_common(cpu_t *cp, boolean_t boot);
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 *);
static ushort_t *warm_reset_vector = NULL;
int
mach_cpucontext_init(void)
{
ushort_t *vec;
ulong_t addr;
struct rm_platter *rm = (struct rm_platter *)rm_platter_va;
if (!(vec = (ushort_t *)psm_map_phys(WARM_RESET_VECTOR,
sizeof (vec), PROT_READ | PROT_WRITE)))
return (-1);
addr = (ulong_t)((caddr_t)rm->rm_code - (caddr_t)rm) + rm_platter_pa;
vec[0] = (ushort_t)(addr & PAGEOFFSET);
vec[1] = (ushort_t)((addr & (0xfffff & PAGEMASK)) >> 4);
warm_reset_vector = vec;
hat_devload(kas.a_hat,
(caddr_t)(uintptr_t)rm_platter_pa, MMU_PAGESIZE,
btop(rm_platter_pa), PROT_READ | PROT_WRITE | PROT_EXEC,
HAT_LOAD_NOCONSIST);
if (!plat_dr_enabled()) {
ASSERT((size_t)real_mode_start_cpu_end -
(size_t)real_mode_start_cpu <= RM_PLATTER_CODE_SIZE);
bcopy((caddr_t)real_mode_start_cpu, (caddr_t)rm->rm_code,
(size_t)real_mode_start_cpu_end -
(size_t)real_mode_start_cpu);
}
return (0);
}
void
mach_cpucontext_fini(void)
{
if (warm_reset_vector)
psm_unmap_phys((caddr_t)warm_reset_vector,
sizeof (warm_reset_vector));
hat_unload(kas.a_hat, (caddr_t)(uintptr_t)rm_platter_pa, MMU_PAGESIZE,
HAT_UNLOAD);
}
extern void *long_mode_64(void);
void
rmp_gdt_init(rm_platter_t *rm)
{
if (mmu_ptob(kas.a_hat->hat_htable->ht_pfn) > 0xffffffffUL) {
panic("Cannot initialize CPUs; kernel's 64-bit page tables\n"
"located above 4G in physical memory (@ 0x%lx)",
mmu_ptob(kas.a_hat->hat_htable->ht_pfn));
}
rm->rm_temp_gdt[0] = 0;
rm->rm_temp_gdt[TEMPGDT_KCODE64] = 0x20980000000000ULL;
rm->rm_temp_gdt_lim = (ushort_t)(sizeof (rm->rm_temp_gdt) - 1);
rm->rm_temp_gdt_base = rm_platter_pa +
(uint32_t)offsetof(rm_platter_t, rm_temp_gdt);
rm->rm_temp_idt_lim = 0;
rm->rm_temp_idt_base = 0;
rm->rm_longmode64_addr = rm_platter_pa +
(uint32_t)((uintptr_t)long_mode_64 -
(uintptr_t)real_mode_start_cpu);
}
static void *
mach_cpucontext_alloc_tables(struct cpu *cp)
{
tss_t *ntss;
struct cpu_tables *ct;
size_t ctsize;
ctsize = P2ROUNDUP(sizeof (*ct), PAGESIZE);
ct = kmem_zalloc(ctsize, KM_SLEEP);
if ((uintptr_t)ct & PAGEOFFSET)
panic("mach_cpucontext_alloc_tables: cpu%d misaligned tables",
cp->cpu_id);
ntss = cp->cpu_tss = &ct->ct_tss;
uintptr_t va;
size_t len;
ntss->tss_ist1 = (uintptr_t)&ct->ct_stack1[sizeof (ct->ct_stack1)];
ntss->tss_ist2 = (uintptr_t)&ct->ct_stack2[sizeof (ct->ct_stack2)];
ntss->tss_ist3 = (uintptr_t)&ct->ct_stack3[sizeof (ct->ct_stack3)];
ntss->tss_ist4 = (uintptr_t)&cp->cpu_m.mcpu_kpti_dbg.kf_tr_rsp;
if (kpti_enable == 1) {
ntss->tss_ist5 = (uintptr_t)&cp->cpu_m.mcpu_kpti_flt.kf_tr_rsp;
ntss->tss_ist6 = (uint64_t)&cp->cpu_m.mcpu_kpti.kf_tr_rsp;
for (va = (uintptr_t)ct, len = ctsize - MMU_PAGESIZE;
len >= MMU_PAGESIZE;
len -= MMU_PAGESIZE, va += MMU_PAGESIZE) {
hati_cpu_punchin(cp, va, PROT_READ | PROT_WRITE);
}
ASSERT3U((uintptr_t)ntss, ==, va);
hati_cpu_punchin(cp, (uintptr_t)ntss, PROT_READ);
}
ntss->tss_bitmapbase = sizeof (*ntss);
set_syssegd((system_desc_t *)&cp->cpu_gdt[GDT_KTSS], cp->cpu_tss,
sizeof (*cp->cpu_tss) - 1, SDT_SYSTSS, SEL_KPL);
return (ct);
}
void *
mach_cpucontext_xalloc(struct cpu *cp, int optype)
{
size_t len;
struct cpu_tables *ct;
rm_platter_t *rm = (rm_platter_t *)rm_platter_va;
static int cpu_halt_code_ready;
if (optype == MACH_CPUCONTEXT_OP_STOP) {
ASSERT(plat_dr_enabled());
if (!cpu_halt_code_ready) {
len = (size_t)real_mode_stop_cpu_stage2_end -
(size_t)real_mode_stop_cpu_stage2;
ASSERT(len <= RM_PLATTER_CPU_HALT_CODE_SIZE);
bcopy((caddr_t)real_mode_stop_cpu_stage2,
(caddr_t)rm->rm_cpu_halt_code, len);
cpu_halt_code_ready = 1;
}
len = (size_t)real_mode_stop_cpu_stage1_end -
(size_t)real_mode_stop_cpu_stage1;
ASSERT(len <= RM_PLATTER_CODE_SIZE);
bcopy((caddr_t)real_mode_stop_cpu_stage1,
(caddr_t)rm->rm_code, len);
rm->rm_cpu_halted = 0;
return (cp->cpu_m.mcpu_mach_ctx_ptr);
} else if (optype != MACH_CPUCONTEXT_OP_START) {
return (NULL);
}
ct = mach_cpucontext_alloc_tables(cp);
if (ct == NULL) {
return (NULL);
}
if (plat_dr_enabled()) {
bcopy((caddr_t)real_mode_start_cpu, (caddr_t)rm->rm_code,
(size_t)real_mode_start_cpu_end -
(size_t)real_mode_start_cpu);
}
rm->rm_idt_base = cp->cpu_idt;
rm->rm_idt_lim = sizeof (*cp->cpu_idt) * NIDT - 1;
rm->rm_gdt_base = cp->cpu_gdt;
rm->rm_gdt_lim = sizeof (*cp->cpu_gdt) * NGDT - 1;
rm->rm_pdbr = MAKECR3(kas.a_hat->hat_htable->ht_pfn, PCID_NONE);
rm->rm_cpu = cp->cpu_id;
rm->rm_cr4 = getcr4();
rm->rm_cr4 &= ~(CR4_MCE | CR4_PCE | CR4_PCIDE);
rmp_gdt_init(rm);
return (ct);
}
void
mach_cpucontext_xfree(struct cpu *cp, void *arg, int err, int optype)
{
struct cpu_tables *ct = arg;
ASSERT(&ct->ct_tss == cp->cpu_tss);
if (optype == MACH_CPUCONTEXT_OP_START) {
switch (err) {
case 0:
cp->cpu_m.mcpu_mach_ctx_ptr = arg;
break;
case ETIMEDOUT:
cp->cpu_m.mcpu_mach_ctx_ptr = arg;
break;
default:
kmem_free(ct, P2ROUNDUP(sizeof (*ct), PAGESIZE));
cp->cpu_tss = NULL;
break;
}
} else if (optype == MACH_CPUCONTEXT_OP_STOP) {
switch (err) {
case 0:
kmem_free(ct, P2ROUNDUP(sizeof (*ct), PAGESIZE));
cp->cpu_tss = NULL;
cp->cpu_m.mcpu_mach_ctx_ptr = NULL;
break;
default:
break;
}
} else {
ASSERT(0);
}
}
void *
mach_cpucontext_alloc(struct cpu *cp)
{
return (mach_cpucontext_xalloc(cp, MACH_CPUCONTEXT_OP_START));
}
void
mach_cpucontext_free(struct cpu *cp, void *arg, int err)
{
mach_cpucontext_xfree(cp, arg, err, MACH_CPUCONTEXT_OP_START);
}
int
mach_cpu_halt(xc_arg_t arg1, xc_arg_t arg2 __unused, xc_arg_t arg3 __unused)
{
char *msg = (char *)arg1;
if (msg)
prom_printf("%s\n", msg);
while (1)
;
return (0);
}
void
mach_cpu_idle(void)
{
x86_md_clear();
i86_halt();
}
void
mach_cpu_pause(volatile char *safe)
{
*safe = PAUSE_WAIT;
membar_enter();
while (*safe != PAUSE_IDLE)
SMT_PAUSE();
}
int
mp_cpu_poweron(struct cpu *cp)
{
int error;
cpuset_t tempset;
processorid_t cpuid;
ASSERT(cp != NULL);
cpuid = cp->cpu_id;
if (use_mp == 0 || plat_dr_support_cpu() == 0) {
return (ENOTSUP);
} else if (cpuid < 0 || cpuid >= max_ncpus) {
return (EINVAL);
}
if (cpuid_checkpass(cp, 4) || cp->cpu_thread == cp->cpu_idle_thread) {
return (ENOTSUP);
}
if (kmem_avail() < 1024 * 1024) {
kmem_reap();
return (ENOMEM);
}
affinity_set(CPU->cpu_id);
if ((error = mach_cpucontext_init()) == 0) {
error = mp_start_cpu_common(cp, B_FALSE);
mach_cpucontext_fini();
}
if (error != 0) {
affinity_clear();
return (error);
}
tempset = cpu_ready_set;
while (!CPU_IN_SET(tempset, cpuid)) {
delay(1);
tempset = *((volatile cpuset_t *)&cpu_ready_set);
}
CPUSET_ATOMIC_ADD(mp_cpus, cpuid);
ucode_cleanup();
affinity_clear();
return (0);
}
#define MP_CPU_DETACH_MAX_TRIES 5
#define MP_CPU_DETACH_DELAY 100
static int
mp_cpu_detach_driver(dev_info_t *dip)
{
int i;
int rv = EBUSY;
dev_info_t *pdip;
pdip = ddi_get_parent(dip);
ASSERT(pdip != NULL);
if (DEVI_BUSY_OWNED(pdip)) {
return (EDEADLOCK);
}
for (i = 0; i < MP_CPU_DETACH_MAX_TRIES; i++) {
if (e_ddi_branch_unconfigure(dip, NULL, 0) == 0) {
rv = 0;
break;
}
DELAY(MP_CPU_DETACH_DELAY);
}
return (rv);
}
int
mp_cpu_poweroff(struct cpu *cp)
{
int rv = 0;
void *ctx;
dev_info_t *dip = NULL;
rm_platter_t *rm = (rm_platter_t *)rm_platter_va;
extern void cpupm_start(cpu_t *);
extern void cpupm_stop(cpu_t *);
ASSERT(cp != NULL);
ASSERT((cp->cpu_flags & CPU_OFFLINE) != 0);
ASSERT((cp->cpu_flags & CPU_QUIESCED) != 0);
if (use_mp == 0 || plat_dr_support_cpu() == 0) {
return (ENOTSUP);
}
if (cp->cpu_id == 0) {
return (ENOTSUP);
};
if (mach_cpu_get_device_node(cp, &dip) != PSM_SUCCESS) {
return (ENXIO);
}
ASSERT(dip != NULL);
if (mp_cpu_detach_driver(dip) != 0) {
rv = EBUSY;
goto out_online;
}
if (mach_cpucontext_init() != 0) {
rv = ENXIO;
goto out_online;
}
ctx = mach_cpucontext_xalloc(cp, MACH_CPUCONTEXT_OP_STOP);
if (ctx == NULL) {
rv = ENXIO;
goto out_context_fini;
}
cpupm_stop(cp);
cpu_event_fini_cpu(cp);
if (cp->cpu_m.mcpu_cmi_hdl != NULL) {
cmi_fini(cp->cpu_m.mcpu_cmi_hdl);
cp->cpu_m.mcpu_cmi_hdl = NULL;
}
rv = mach_cpu_stop(cp, ctx);
if (rv != 0) {
goto out_enable_cmi;
}
while (*(volatile ushort_t *)&(rm->rm_cpu_halted) != 0xdead) {
delay(1);
}
rm->rm_cpu_halted = 0xffff;
ASSERT((cp->cpu_flags & CPU_READY) == 0);
ASSERT((cp->cpu_flags & CPU_RUNNING) == 0);
cp->cpu_flags = CPU_OFFLINE | CPU_QUIESCED | CPU_POWEROFF;
CPUSET_ATOMIC_DEL(mp_cpus, cp->cpu_id);
mach_cpucontext_xfree(cp, ctx, 0, MACH_CPUCONTEXT_OP_STOP);
mach_cpucontext_fini();
return (0);
out_enable_cmi:
{
cmi_hdl_t hdl;
if ((hdl = cmi_init(CMI_HDL_NATIVE, cmi_ntv_hwchipid(cp),
cmi_ntv_hwcoreid(cp), cmi_ntv_hwstrandid(cp))) != NULL) {
if (is_x86_feature(x86_featureset, X86FSET_MCA))
cmi_mca_init(hdl);
cp->cpu_m.mcpu_cmi_hdl = hdl;
}
}
cpu_event_init_cpu(cp);
cpupm_start(cp);
mach_cpucontext_xfree(cp, ctx, rv, MACH_CPUCONTEXT_OP_STOP);
out_context_fini:
mach_cpucontext_fini();
out_online:
(void) e_ddi_branch_configure(dip, NULL, 0);
if (rv != EAGAIN && rv != ETIME) {
rv = ENXIO;
}
return (rv);
}
int
vcpu_on_pcpu(processorid_t cpu)
{
return (VCPU_STATE_UNKNOWN);
}