#include <sys/xpv_user.h>
#include <xen/public/arch-x86/xen-mca.h>
#include <sys/ctype.h>
#include <sys/types.h>
#include <sys/cmn_err.h>
#include <sys/trap.h>
#include <sys/segments.h>
#include <sys/hypervisor.h>
#include <sys/xen_mmu.h>
#include <sys/machsystm.h>
#include <sys/promif.h>
#include <sys/bootconf.h>
#include <sys/bootinfo.h>
#include <sys/cpr.h>
#include <sys/taskq.h>
#include <sys/uadmin.h>
#include <sys/evtchn_impl.h>
#include <sys/archsystm.h>
#include <xen/sys/xenbus_impl.h>
#include <sys/mach_mmu.h>
#include <vm/hat_i86.h>
#include <sys/gnttab.h>
#include <sys/reboot.h>
#include <sys/stack.h>
#include <sys/clock.h>
#include <sys/bitmap.h>
#include <sys/processor.h>
#include <sys/xen_errno.h>
#include <sys/xpv_panic.h>
#include <sys/smp_impldefs.h>
#include <sys/cpu.h>
#include <sys/balloon_impl.h>
#include <sys/ddi.h>
#ifdef DEBUG
#define SUSPEND_DEBUG if (xen_suspend_debug) xen_printf
#else
#define SUSPEND_DEBUG(...)
#endif
int cpr_debug;
cpuset_t cpu_suspend_lost_set;
static int xen_suspend_debug;
uint_t xen_phys_ncpus;
xen_mc_logical_cpu_t *xen_phys_cpus;
int xen_physinfo_debug = 0;
typedef enum xen_version {
XENVER_BOOT_IDX,
XENVER_CURRENT_IDX
} xen_version_t;
struct xenver {
ulong_t xv_major;
ulong_t xv_minor;
ulong_t xv_revision;
xen_extraversion_t xv_ver;
ulong_t xv_is_xvm;
xen_changeset_info_t xv_chgset;
xen_compile_info_t xv_build;
xen_capabilities_info_t xv_caps;
} xenver[2];
#define XENVER_BOOT(m) (xenver[XENVER_BOOT_IDX].m)
#define XENVER_CURRENT(m) (xenver[XENVER_CURRENT_IDX].m)
static void
xen_set_version(xen_version_t idx)
{
ulong_t ver;
bzero(&xenver[idx], sizeof (xenver[idx]));
ver = HYPERVISOR_xen_version(XENVER_version, 0);
xenver[idx].xv_major = BITX(ver, 31, 16);
xenver[idx].xv_minor = BITX(ver, 15, 0);
(void) HYPERVISOR_xen_version(XENVER_extraversion, &xenver[idx].xv_ver);
if (strlen(xenver[idx].xv_ver) > 1 && isdigit(xenver[idx].xv_ver[1]))
xenver[idx].xv_revision = xenver[idx].xv_ver[1] - '0';
else
cmn_err(CE_WARN, "Cannot extract revision on this hypervisor "
"version: v%s, unexpected version format",
xenver[idx].xv_ver);
xenver[idx].xv_is_xvm = 0;
if (strstr(xenver[idx].xv_ver, "-xvm") != NULL)
xenver[idx].xv_is_xvm = 1;
(void) HYPERVISOR_xen_version(XENVER_changeset,
&xenver[idx].xv_chgset);
(void) HYPERVISOR_xen_version(XENVER_compile_info,
&xenver[idx].xv_build);
(void) HYPERVISOR_xen_version(XENVER_capabilities,
&xenver[idx].xv_caps);
cmn_err(CE_CONT, "?v%lu.%lu%s chgset '%s'\n", xenver[idx].xv_major,
xenver[idx].xv_minor, xenver[idx].xv_ver, xenver[idx].xv_chgset);
if (idx == XENVER_BOOT_IDX)
bcopy(&xenver[XENVER_BOOT_IDX], &xenver[XENVER_CURRENT_IDX],
sizeof (xenver[XENVER_BOOT_IDX]));
}
typedef enum xen_hypervisor_check {
XEN_RUN_CHECK,
XEN_SUSPEND_CHECK
} xen_hypervisor_check_t;
static int
xen_hypervisor_supports_solaris(xen_hypervisor_check_t check)
{
if (xen_suspend_debug == 1)
return (1);
if (XENVER_CURRENT(xv_major) < 3)
return (0);
if (XENVER_CURRENT(xv_major) > 3)
return (1);
if (XENVER_CURRENT(xv_minor) > 0)
return (1);
if (XENVER_CURRENT(xv_revision) < 4)
return (0);
if (check == XEN_SUSPEND_CHECK && XENVER_CURRENT(xv_revision) == 4 &&
!XENVER_CURRENT(xv_is_xvm))
return (0);
return (1);
}
static void
xen_pte_workaround(void)
{
extern int pt_kern;
if (XENVER_CURRENT(xv_major) != 3)
return;
if (XENVER_CURRENT(xv_minor) > 1)
return;
if (XENVER_CURRENT(xv_minor) == 1 &&
XENVER_CURRENT(xv_revision) > 1)
return;
if (XENVER_CURRENT(xv_is_xvm))
return;
pt_kern = PT_USER;
}
void
xen_set_callback(void (*func)(void), uint_t type, uint_t flags)
{
struct callback_register cb;
bzero(&cb, sizeof (cb));
cb.address = (ulong_t)func;
cb.type = type;
cb.flags = flags;
if (HYPERVISOR_callback_op(CALLBACKOP_register, &cb) != 0 &&
type != CALLBACKTYPE_nmi)
panic("HYPERVISOR_callback_op failed");
}
void
xen_init_callbacks(void)
{
xen_set_callback(xen_callback, CALLBACKTYPE_event, 0);
xen_set_callback(xen_failsafe_callback, CALLBACKTYPE_failsafe,
CALLBACKF_mask_events);
xen_set_callback(nmiint, CALLBACKTYPE_nmi, 0);
xen_set_callback(sys_syscall, CALLBACKTYPE_syscall,
CALLBACKF_mask_events);
}
void
cpr_err(int ce, const char *fmt, ...)
{
va_list adx;
va_start(adx, fmt);
vcmn_err(ce, fmt, adx);
va_end(adx);
drv_usecwait(MICROSEC >> 2);
}
void
xen_suspend_devices(void)
{
int rc;
SUSPEND_DEBUG("xen_suspend_devices\n");
if ((rc = cpr_suspend_devices(ddi_root_node())) != 0)
panic("failed to suspend devices: %d", rc);
}
void
xen_resume_devices(void)
{
int rc;
SUSPEND_DEBUG("xen_resume_devices\n");
if ((rc = cpr_resume_devices(ddi_root_node(), 0)) != 0)
panic("failed to resume devices: %d", rc);
}
static void
rebuild_mfn_list(void)
{
int i = 0;
size_t sz;
size_t off;
pfn_t pfn;
SUSPEND_DEBUG("rebuild_mfn_list\n");
sz = ((mfn_count * sizeof (mfn_t)) + MMU_PAGEOFFSET) & MMU_PAGEMASK;
for (off = 0; off < sz; off += MMU_PAGESIZE) {
size_t j = mmu_btop(off);
if (((j * sizeof (mfn_t)) & MMU_PAGEOFFSET) == 0) {
pfn = hat_getpfnum(kas.a_hat,
(caddr_t)&mfn_list_pages[j]);
mfn_list_pages_page[i++] = pfn_to_mfn(pfn);
}
pfn = hat_getpfnum(kas.a_hat, (caddr_t)mfn_list + off);
mfn_list_pages[j] = pfn_to_mfn(pfn);
}
pfn = hat_getpfnum(kas.a_hat, (caddr_t)mfn_list_pages_page);
HYPERVISOR_shared_info->arch.pfn_to_mfn_frame_list_list
= pfn_to_mfn(pfn);
}
static void
suspend_cpus(void)
{
int i;
SUSPEND_DEBUG("suspend_cpus\n");
mp_enter_barrier();
for (i = 1; i < ncpus; i++) {
if (!CPU_IN_SET(cpu_suspend_lost_set, i)) {
SUSPEND_DEBUG("xen_vcpu_down %d\n", i);
(void) xen_vcpu_down(i);
}
mach_cpucontext_reset(cpu[i]);
}
}
static void
resume_cpus(void)
{
int i;
for (i = 1; i < ncpus; i++) {
if (cpu[i] == NULL)
continue;
if (!CPU_IN_SET(cpu_suspend_lost_set, i)) {
SUSPEND_DEBUG("xen_vcpu_up %d\n", i);
mach_cpucontext_restore(cpu[i]);
(void) xen_vcpu_up(i);
}
}
mp_leave_barrier();
}
void
xen_suspend_domain(void)
{
extern void rtcsync(void);
extern hrtime_t hres_last_tick;
mfn_t start_info_mfn;
ulong_t flags;
pfn_t pfn;
int i;
if (xen_hypervisor_supports_solaris(XEN_SUSPEND_CHECK) == 0) {
cpr_err(CE_WARN, "Cannot suspend on this hypervisor "
"version: v%lu.%lu%s, need at least version v3.0.4 or "
"-xvm based hypervisor", XENVER_CURRENT(xv_major),
XENVER_CURRENT(xv_minor), XENVER_CURRENT(xv_ver));
return;
}
cpr_err(CE_NOTE, "Domain suspending for save/migrate");
SUSPEND_DEBUG("xen_suspend_domain\n");
xen_suspend_devices();
SUSPEND_DEBUG("xenbus_suspend\n");
xenbus_suspend();
pfn = hat_getpfnum(kas.a_hat, (caddr_t)xen_info);
start_info_mfn = pfn_to_mfn(pfn);
mutex_enter(&cpu_lock);
thread_affinity_set(curthread, 0);
kpreempt_disable();
SUSPEND_DEBUG("xen_start_migrate\n");
xen_start_migrate();
if (ncpus > 1)
suspend_cpus();
mutex_enter(&ec_lock);
SUSPEND_DEBUG("ec_suspend\n");
ec_suspend();
SUSPEND_DEBUG("gnttab_suspend\n");
gnttab_suspend();
flags = intr_clear();
xpv_time_suspend();
for (i = 1; i < ncpus; i++) {
if (cpu[i] == NULL)
continue;
if (cpu_get_state(cpu[i]) == P_POWEROFF)
CPUSET_ATOMIC_ADD(cpu_suspend_lost_set, i);
}
xen_info->store_mfn = mfn_to_pfn_mapping[xen_info->store_mfn];
xen_info->console.domU.mfn =
mfn_to_pfn_mapping[xen_info->console.domU.mfn];
if (CPU->cpu_m.mcpu_vcpu_info->evtchn_upcall_mask == 0) {
prom_printf("xen_suspend_domain(): "
"CPU->cpu_m.mcpu_vcpu_info->evtchn_upcall_mask not set\n");
(void) HYPERVISOR_shutdown(SHUTDOWN_crash);
}
if (HYPERVISOR_update_va_mapping((uintptr_t)HYPERVISOR_shared_info,
0, UVMF_INVLPG)) {
prom_printf("xen_suspend_domain(): "
"HYPERVISOR_update_va_mapping() failed\n");
(void) HYPERVISOR_shutdown(SHUTDOWN_crash);
}
SUSPEND_DEBUG("HYPERVISOR_suspend\n");
if (HYPERVISOR_suspend(start_info_mfn)) {
prom_printf("xen_suspend_domain(): "
"HYPERVISOR_suspend() failed\n");
(void) HYPERVISOR_shutdown(SHUTDOWN_crash);
}
if (HYPERVISOR_update_va_mapping((uintptr_t)HYPERVISOR_shared_info,
xen_info->shared_info | PT_NOCONSIST | PT_VALID | PT_WRITABLE,
UVMF_INVLPG))
(void) HYPERVISOR_shutdown(SHUTDOWN_crash);
if (xen_info->nr_pages != mfn_count) {
prom_printf("xen_suspend_domain(): number of pages"
" changed, was 0x%lx, now 0x%lx\n", mfn_count,
xen_info->nr_pages);
(void) HYPERVISOR_shutdown(SHUTDOWN_crash);
}
xpv_time_resume();
cached_max_mfn = 0;
SUSPEND_DEBUG("gnttab_resume\n");
gnttab_resume();
SUSPEND_DEBUG("ec_resume\n");
ec_resume();
intr_restore(flags);
if (ncpus > 1)
resume_cpus();
mutex_exit(&ec_lock);
xen_end_migrate();
mutex_exit(&cpu_lock);
hres_last_tick = xpv_gethrtime();
rtcsync();
rebuild_mfn_list();
SUSPEND_DEBUG("xenbus_resume\n");
xenbus_resume();
SUSPEND_DEBUG("xenbus_resume_devices\n");
xen_resume_devices();
thread_affinity_clear(curthread);
kpreempt_enable();
SUSPEND_DEBUG("finished xen_suspend_domain\n");
xen_set_version(XENVER_CURRENT_IDX);
if (xen_hypervisor_supports_solaris(XEN_SUSPEND_CHECK) == 0)
cmn_err(CE_WARN, "Found hypervisor version: v%lu.%lu%s "
"but need at least version v3.0.4",
XENVER_CURRENT(xv_major), XENVER_CURRENT(xv_minor),
XENVER_CURRENT(xv_ver));
cmn_err(CE_NOTE, "domain restore/migrate completed");
}
uint_t
xen_debug_handler(caddr_t arg __unused, caddr_t arg1 __unused)
{
debug_enter("External debug event received");
if (!(boothowto & RB_DEBUG)) {
shared_info_t *si = HYPERVISOR_shared_info;
int i;
prom_printf("evtchn_pending [ ");
for (i = 0; i < 8; i++)
prom_printf("%lx ", si->evtchn_pending[i]);
prom_printf("]\nevtchn_mask [ ");
for (i = 0; i < 8; i++)
prom_printf("%lx ", si->evtchn_mask[i]);
prom_printf("]\n");
for (i = 0; i < ncpus; i++) {
vcpu_info_t *vcpu = &si->vcpu_info[i];
if (cpu[i] == NULL)
continue;
prom_printf("CPU%d pending %d mask %d sel %lx\n",
i, vcpu->evtchn_upcall_pending,
vcpu->evtchn_upcall_mask,
vcpu->evtchn_pending_sel);
}
}
return (0);
}
static void
xen_sysrq_handler(struct xenbus_watch *watch, const char **vec,
unsigned int len)
{
xenbus_transaction_t xbt;
char key = '\0';
int ret;
retry:
if (xenbus_transaction_start(&xbt)) {
cmn_err(CE_WARN, "failed to start sysrq transaction");
return;
}
if ((ret = xenbus_scanf(xbt, "control", "sysrq", "%c", &key)) != 0) {
if (ret != ENOENT)
cmn_err(CE_WARN, "failed to read sysrq: %d", ret);
goto out;
}
if ((ret = xenbus_rm(xbt, "control", "sysrq")) != 0) {
cmn_err(CE_WARN, "failed to reset sysrq: %d", ret);
goto out;
}
if (xenbus_transaction_end(xbt, 0) == EAGAIN)
goto retry;
if (key == 'b')
(void) xen_debug_handler(NULL, NULL);
else
cmn_err(CE_WARN, "Ignored sysrq %c", key);
return;
out:
(void) xenbus_transaction_end(xbt, 1);
}
taskq_t *xen_shutdown_tq;
#define SHUTDOWN_INVALID -1
#define SHUTDOWN_POWEROFF 0
#define SHUTDOWN_REBOOT 1
#define SHUTDOWN_SUSPEND 2
#define SHUTDOWN_HALT 3
#define SHUTDOWN_MAX 4
#define SHUTDOWN_TIMEOUT_SECS (60 * 5)
static const char *cmd_strings[SHUTDOWN_MAX] = {
"poweroff",
"reboot",
"suspend",
"halt"
};
static void
xen_dirty_shutdown(void *arg)
{
int cmd = (uintptr_t)arg;
cmn_err(CE_WARN, "Externally requested shutdown failed or "
"timed out.\nShutting down.\n");
switch (cmd) {
case SHUTDOWN_HALT:
case SHUTDOWN_POWEROFF:
(void) kadmin(A_SHUTDOWN, AD_POWEROFF, NULL, kcred);
break;
case SHUTDOWN_REBOOT:
(void) kadmin(A_REBOOT, AD_BOOT, NULL, kcred);
break;
}
}
static void
xen_shutdown(void *arg)
{
int cmd = (uintptr_t)arg;
proc_t *initpp;
ASSERT(cmd > SHUTDOWN_INVALID && cmd < SHUTDOWN_MAX);
if (cmd == SHUTDOWN_SUSPEND) {
xen_suspend_domain();
return;
}
switch (cmd) {
case SHUTDOWN_POWEROFF:
force_shutdown_method = AD_POWEROFF;
break;
case SHUTDOWN_HALT:
force_shutdown_method = AD_HALT;
break;
case SHUTDOWN_REBOOT:
force_shutdown_method = AD_BOOT;
break;
}
mutex_enter(&pidlock);
initpp = prfind(P_INITPID);
mutex_exit(&pidlock);
if (initpp == NULL) {
extern void halt(char *);
halt("Power off the System");
}
psignal(initpp, SIGPWR);
(void) timeout(xen_dirty_shutdown, arg,
SHUTDOWN_TIMEOUT_SECS * drv_usectohz(MICROSEC));
}
static void
xen_shutdown_handler(struct xenbus_watch *watch, const char **vec,
unsigned int len)
{
char *str;
xenbus_transaction_t xbt;
int err, shutdown_code = SHUTDOWN_INVALID;
unsigned int slen;
again:
err = xenbus_transaction_start(&xbt);
if (err)
return;
if (xenbus_read(xbt, "control", "shutdown", (void *)&str, &slen)) {
(void) xenbus_transaction_end(xbt, 1);
return;
}
SUSPEND_DEBUG("%d: xen_shutdown_handler: \"%s\"\n", CPU->cpu_id, str);
if (strcmp(str, "") == 0) {
(void) xenbus_transaction_end(xbt, 0);
kmem_free(str, slen);
return;
} else if (strcmp(str, "poweroff") == 0) {
shutdown_code = SHUTDOWN_POWEROFF;
} else if (strcmp(str, "reboot") == 0) {
shutdown_code = SHUTDOWN_REBOOT;
} else if (strcmp(str, "suspend") == 0) {
shutdown_code = SHUTDOWN_SUSPEND;
} else if (strcmp(str, "halt") == 0) {
shutdown_code = SHUTDOWN_HALT;
} else {
printf("Ignoring shutdown request: %s\n", str);
}
(void) xenbus_write(xbt, "control", "shutdown", "");
err = xenbus_transaction_end(xbt, 0);
if (err == EAGAIN) {
SUSPEND_DEBUG("%d: trying again\n", CPU->cpu_id);
kmem_free(str, slen);
goto again;
}
kmem_free(str, slen);
if (shutdown_code != SHUTDOWN_INVALID) {
(void) taskq_dispatch(xen_shutdown_tq, xen_shutdown,
(void *)(intptr_t)shutdown_code, 0);
}
}
static struct xenbus_watch shutdown_watch;
static struct xenbus_watch sysrq_watch;
void
xen_late_startup(void)
{
if (!DOMAIN_IS_INITDOMAIN(xen_info)) {
xen_shutdown_tq = taskq_create("shutdown_taskq", 1,
maxclsyspri - 1, 1, 1, TASKQ_PREPOPULATE);
shutdown_watch.node = "control/shutdown";
shutdown_watch.callback = xen_shutdown_handler;
if (register_xenbus_watch(&shutdown_watch))
cmn_err(CE_WARN, "Failed to set shutdown watcher");
sysrq_watch.node = "control/sysrq";
sysrq_watch.callback = xen_sysrq_handler;
if (register_xenbus_watch(&sysrq_watch))
cmn_err(CE_WARN, "Failed to set sysrq watcher");
}
balloon_init(xen_info->nr_pages);
}
#ifdef DEBUG
#define XEN_PRINTF_BUFSIZE 1024
char xen_printf_buffer[XEN_PRINTF_BUFSIZE];
void
xen_printf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
(void) vsnprintf(xen_printf_buffer, XEN_PRINTF_BUFSIZE, fmt, ap);
va_end(ap);
(void) HYPERVISOR_console_io(CONSOLEIO_write,
strlen(xen_printf_buffer), xen_printf_buffer);
}
#else
void
xen_printf(const char *fmt, ...)
{
}
#endif
void
startup_xen_version(void)
{
xen_set_version(XENVER_BOOT_IDX);
if (xen_hypervisor_supports_solaris(XEN_RUN_CHECK) == 0)
cmn_err(CE_WARN, "Found hypervisor version: v%lu.%lu%s "
"but need at least version v3.0.4",
XENVER_CURRENT(xv_major), XENVER_CURRENT(xv_minor),
XENVER_CURRENT(xv_ver));
xen_pte_workaround();
}
int xen_mca_simulate_mc_physinfo_failure = 0;
void
startup_xen_mca(void)
{
if (!DOMAIN_IS_INITDOMAIN(xen_info))
return;
xen_phys_ncpus = 0;
xen_phys_cpus = NULL;
if (xen_mca_simulate_mc_physinfo_failure ||
xen_get_mc_physcpuinfo(NULL, &xen_phys_ncpus) != 0) {
cmn_err(CE_WARN,
"%sxen_get_mc_physinfo failure during xen MCA startup: "
"there will be no machine check support",
xen_mca_simulate_mc_physinfo_failure ? "(simulated) " : "");
return;
}
xen_phys_cpus = kmem_alloc(xen_phys_ncpus *
sizeof (xen_mc_logical_cpu_t), KM_NOSLEEP);
if (xen_phys_cpus == NULL) {
cmn_err(CE_WARN,
"xen_get_mc_physinfo failure: can't allocate CPU array");
return;
}
if (xen_get_mc_physcpuinfo(xen_phys_cpus, &xen_phys_ncpus) != 0) {
cmn_err(CE_WARN, "xen_get_mc_physinfo failure: no "
"physical CPU info");
kmem_free(xen_phys_cpus,
xen_phys_ncpus * sizeof (xen_mc_logical_cpu_t));
xen_phys_ncpus = 0;
xen_phys_cpus = NULL;
}
if (xen_physinfo_debug) {
xen_mc_logical_cpu_t *xcp;
unsigned i;
cmn_err(CE_NOTE, "xvm mca: %u physical cpus:\n",
xen_phys_ncpus);
for (i = 0; i < xen_phys_ncpus; i++) {
xcp = &xen_phys_cpus[i];
cmn_err(CE_NOTE, "cpu%u: (%u, %u, %u) apid %u",
xcp->mc_cpunr, xcp->mc_chipid, xcp->mc_coreid,
xcp->mc_threadid, xcp->mc_apicid);
}
}
}
void
xen_set_gdt(ulong_t *frame_list, int entries)
{
int err;
if ((err = HYPERVISOR_set_gdt(frame_list, entries)) != 0) {
panic("xen_set_gdt(%p, %d): error %d",
(void *)frame_list, entries, -(int)err);
}
}
void
xen_set_ldt(user_desc_t *ldt, uint_t nsels)
{
struct mmuext_op op;
long err;
op.cmd = MMUEXT_SET_LDT;
op.arg1.linear_addr = (uintptr_t)ldt;
op.arg2.nr_ents = nsels;
if ((err = HYPERVISOR_mmuext_op(&op, 1, NULL, DOMID_SELF)) != 0) {
panic("xen_set_ldt(%p, %d): error %d",
(void *)ldt, nsels, -(int)err);
}
}
void
xen_stack_switch(ulong_t ss, ulong_t esp)
{
long err;
if ((err = HYPERVISOR_stack_switch(ss, esp)) != 0) {
panic("xen_stack_switch(%lx, %lx): error %d", ss, esp,
-(int)err);
}
}
long
xen_set_trap_table(trap_info_t *table)
{
long err;
if ((err = HYPERVISOR_set_trap_table(table)) != 0) {
panic("xen_set_trap_table(%p): error %d", (void *)table,
-(int)err);
}
return (err);
}
void
xen_set_segment_base(int reg, ulong_t value)
{
long err;
if ((err = HYPERVISOR_set_segment_base(reg, value)) != 0) {
panic("xen_set_segment_base(%d, %lx): error %d",
reg, value, -(int)err);
}
}
int
xen_xlate_errcode(int error)
{
switch (-error) {
#define CASE(num) case X_##num: error = num; break
CASE(EPERM); CASE(ENOENT); CASE(ESRCH);
CASE(EINTR); CASE(EIO); CASE(ENXIO);
CASE(E2BIG); CASE(ENOMEM); CASE(EACCES);
CASE(EFAULT); CASE(EBUSY); CASE(EEXIST);
CASE(ENODEV); CASE(EISDIR); CASE(EINVAL);
CASE(ENOSPC); CASE(ESPIPE); CASE(EROFS);
CASE(ENOSYS); CASE(ENOTEMPTY); CASE(EISCONN);
CASE(ENODATA); CASE(EAGAIN);
#undef CASE
default:
panic("xen_xlate_errcode: unknown error %d", error);
}
return (error);
}
void
xen_enable_user_iopl(void *arg __unused)
{
physdev_set_iopl_t set_iopl;
set_iopl.iopl = 3;
(void) HYPERVISOR_physdev_op(PHYSDEVOP_set_iopl, &set_iopl);
}
void
xen_disable_user_iopl(void *arg __unused)
{
physdev_set_iopl_t set_iopl;
set_iopl.iopl = 1;
(void) HYPERVISOR_physdev_op(PHYSDEVOP_set_iopl, &set_iopl);
}
int
xen_gdt_setprot(cpu_t *cp, uint_t prot)
{
int err;
int pt_bits = PT_VALID;
if (prot & PROT_WRITE)
pt_bits |= PT_WRITABLE;
if ((err = as_setprot(&kas, (caddr_t)cp->cpu_gdt,
MMU_PAGESIZE, prot)) != 0)
goto done;
err = xen_kpm_page(mmu_btop(cp->cpu_m.mcpu_gdtpa), pt_bits);
done:
if (err) {
cmn_err(CE_WARN, "cpu%d: xen_gdt_setprot(%s) failed: error %d",
cp->cpu_id, (prot & PROT_WRITE) ? "writable" : "read-only",
err);
}
return (err);
}
int
xen_ldt_setprot(user_desc_t *ldt, size_t lsize, uint_t prot)
{
int err;
caddr_t lva = (caddr_t)ldt;
int pt_bits = PT_VALID;
pgcnt_t npgs;
if (prot & PROT_WRITE)
pt_bits |= PT_WRITABLE;
if ((err = as_setprot(&kas, (caddr_t)ldt, lsize, prot)) != 0)
goto done;
ASSERT(IS_P2ALIGNED(lsize, PAGESIZE));
npgs = mmu_btop(lsize);
while (npgs--) {
if ((err = xen_kpm_page(hat_getpfnum(kas.a_hat, lva),
pt_bits)) != 0)
break;
lva += PAGESIZE;
}
done:
if (err) {
cmn_err(CE_WARN, "xen_ldt_setprot(%p, %s) failed: error %d",
(void *)lva,
(prot & PROT_WRITE) ? "writable" : "read-only", err);
}
return (err);
}
int
xen_get_mc_physcpuinfo(xen_mc_logical_cpu_t *log_cpus, uint_t *ncpus)
{
xen_mc_t xmc;
struct xen_mc_physcpuinfo *cpi = &xmc.u.mc_physcpuinfo;
cpi->ncpus = *ncpus;
set_xen_guest_handle(cpi->info, log_cpus);
if (HYPERVISOR_mca(XEN_MC_physcpuinfo, &xmc) != 0)
return (-1);
*ncpus = cpi->ncpus;
return (0);
}
void
print_panic(const char *str)
{
xen_printf(str);
}
xen_mc_lcpu_cookie_t
xen_physcpu_next(xen_mc_lcpu_cookie_t cookie)
{
xen_mc_logical_cpu_t *xcp = (xen_mc_logical_cpu_t *)cookie;
if (!DOMAIN_IS_INITDOMAIN(xen_info))
return (NULL);
if (cookie == NULL)
return ((xen_mc_lcpu_cookie_t)xen_phys_cpus);
if (xcp == xen_phys_cpus + xen_phys_ncpus - 1)
return (NULL);
else
return ((xen_mc_lcpu_cookie_t)++xcp);
}
#define COOKIE2XCP(c) ((xen_mc_logical_cpu_t *)(c))
const char *
xen_physcpu_vendorstr(xen_mc_lcpu_cookie_t cookie)
{
xen_mc_logical_cpu_t *xcp = COOKIE2XCP(cookie);
return ((const char *)&xcp->mc_vendorid[0]);
}
int
xen_physcpu_family(xen_mc_lcpu_cookie_t cookie)
{
return (COOKIE2XCP(cookie)->mc_family);
}
int
xen_physcpu_model(xen_mc_lcpu_cookie_t cookie)
{
return (COOKIE2XCP(cookie)->mc_model);
}
int
xen_physcpu_stepping(xen_mc_lcpu_cookie_t cookie)
{
return (COOKIE2XCP(cookie)->mc_step);
}
id_t
xen_physcpu_chipid(xen_mc_lcpu_cookie_t cookie)
{
return (COOKIE2XCP(cookie)->mc_chipid);
}
id_t
xen_physcpu_coreid(xen_mc_lcpu_cookie_t cookie)
{
return (COOKIE2XCP(cookie)->mc_coreid);
}
id_t
xen_physcpu_strandid(xen_mc_lcpu_cookie_t cookie)
{
return (COOKIE2XCP(cookie)->mc_threadid);
}
id_t
xen_physcpu_initial_apicid(xen_mc_lcpu_cookie_t cookie)
{
return (COOKIE2XCP(cookie)->mc_clusterid);
}
id_t
xen_physcpu_logical_id(xen_mc_lcpu_cookie_t cookie)
{
return (COOKIE2XCP(cookie)->mc_cpunr);
}
boolean_t
xen_physcpu_is_cmt(xen_mc_lcpu_cookie_t cookie)
{
return (COOKIE2XCP(cookie)->mc_nthreads > 1);
}
uint64_t
xen_physcpu_mcg_cap(xen_mc_lcpu_cookie_t cookie)
{
xen_mc_logical_cpu_t *xcp = COOKIE2XCP(cookie);
return (xcp->mc_msrvalues[0].value);
}
int
xen_map_gref(uint_t cmd, gnttab_map_grant_ref_t *mapop, uint_t count,
boolean_t uvaddr)
{
long rc;
uint_t i;
ASSERT(cmd == GNTTABOP_map_grant_ref);
#if !defined(_BOOT)
if (uvaddr == B_FALSE) {
for (i = 0; i < count; ++i) {
mapop[i].flags |= (PT_FOREIGN <<_GNTMAP_guest_avail0);
}
}
#endif
rc = HYPERVISOR_grant_table_op(cmd, mapop, count);
return (rc);
}
static int
xpv_get_physinfo(xen_sysctl_physinfo_t *pi)
{
xen_sysctl_t op;
struct sp { void *p; } *sp = (struct sp *)&op.u.physinfo.cpu_to_node;
int ret;
bzero(&op, sizeof (op));
op.cmd = XEN_SYSCTL_physinfo;
op.interface_version = XEN_SYSCTL_INTERFACE_VERSION;
set_xen_guest_handle(*sp, NULL);
ret = HYPERVISOR_sysctl(&op);
if (ret != 0)
return (xen_xlate_errcode(ret));
bcopy(&op.u.physinfo, pi, sizeof (op.u.physinfo));
return (0);
}
uint_t
xpv_nr_phys_cpus(void)
{
static uint_t nphyscpus = 0;
ASSERT(DOMAIN_IS_INITDOMAIN(xen_info));
if (nphyscpus == 0) {
xen_sysctl_physinfo_t pi;
int ret;
if ((ret = xpv_get_physinfo(&pi)) != 0)
panic("xpv_get_physinfo() failed: %d\n", ret);
nphyscpus = pi.nr_cpus;
}
return (nphyscpus);
}
pgcnt_t
xpv_nr_phys_pages(void)
{
xen_sysctl_physinfo_t pi;
int ret;
ASSERT(DOMAIN_IS_INITDOMAIN(xen_info));
if ((ret = xpv_get_physinfo(&pi)) != 0)
panic("xpv_get_physinfo() failed: %d\n", ret);
return ((pgcnt_t)pi.total_pages);
}
uint64_t
xpv_cpu_khz(void)
{
xen_sysctl_physinfo_t pi;
int ret;
ASSERT(DOMAIN_IS_INITDOMAIN(xen_info));
if ((ret = xpv_get_physinfo(&pi)) != 0)
panic("xpv_get_physinfo() failed: %d\n", ret);
return ((uint64_t)pi.cpu_khz);
}