#include <sys/types.h>
#include <sys/archsystm.h>
#include <sys/cpuset.h>
#include <sys/fp.h>
#include <sys/kmem.h>
#include <sys/queue.h>
#include <sys/spl.h>
#include <sys/systm.h>
#include <sys/ddidmareq.h>
#include <sys/id_space.h>
#include <sys/psm_defs.h>
#include <sys/smp_impldefs.h>
#include <sys/modhash.h>
#include <sys/hma.h>
#include <sys/x86_archext.h>
#include <machine/cpufunc.h>
#include <machine/md_var.h>
#include <machine/specialreg.h>
#include <machine/vmm.h>
#include <machine/vmparam.h>
#include <sys/vmm_impl.h>
#include <sys/kernel.h>
#include <vm/as.h>
#include <vm/seg_kmem.h>
static void vmm_tsc_init(void);
SET_DECLARE(sysinit_set, struct sysinit);
void
sysinit(void)
{
struct sysinit **si;
SET_FOREACH(si, sysinit_set)
(*si)->func((*si)->data);
}
void
invalidate_cache_all(void)
{
cpuset_t cpuset;
kpreempt_disable();
cpuset_all_but(&cpuset, CPU->cpu_id);
xc_call((xc_arg_t)NULL, (xc_arg_t)NULL, (xc_arg_t)NULL,
CPUSET2BV(cpuset), (xc_func_t)invalidate_cache);
invalidate_cache();
kpreempt_enable();
}
vm_paddr_t
vtophys(void *va)
{
pfn_t pfn;
ASSERT(curthread->t_preempt == 0);
pfn = hat_getpfnum(kas.a_hat, (caddr_t)va);
ASSERT(pfn != PFN_INVALID);
return (pfn << PAGE_SHIFT) | ((uintptr_t)va & PAGE_MASK);
}
int
cpusetobj_ffs(const cpuset_t *set)
{
uint_t large, small;
cpuset_bounds(set, &small, &large);
if (small == CPUSET_NOTINSET) {
return (0);
}
ASSERT3U(small, <=, INT_MAX);
return (small + 1);
}
struct vmm_ptp_item {
void *vpi_vaddr;
};
static kmutex_t vmm_ptp_lock;
static mod_hash_t *vmm_ptp_hash;
uint_t vmm_ptp_hash_nchains = 16381;
uint_t vmm_ptp_hash_size = PAGESIZE;
static void
vmm_ptp_hash_valdtor(mod_hash_val_t val)
{
struct vmm_ptp_item *i = (struct vmm_ptp_item *)val;
kmem_free(i->vpi_vaddr, PAGE_SIZE);
kmem_free(i, sizeof (*i));
}
static void
vmm_ptp_init(void)
{
vmm_ptp_hash = mod_hash_create_ptrhash("vmm_ptp_hash",
vmm_ptp_hash_nchains, vmm_ptp_hash_valdtor, vmm_ptp_hash_size);
VERIFY(vmm_ptp_hash != NULL);
}
static uint_t
vmm_ptp_check(mod_hash_key_t key, mod_hash_val_t *val, void *unused)
{
struct vmm_ptp_item *i = (struct vmm_ptp_item *)val;
cmn_err(CE_PANIC, "!vmm_ptp_check: hash not empty: %p", i->vpi_vaddr);
return (MH_WALK_TERMINATE);
}
static void
vmm_ptp_cleanup(void)
{
mod_hash_walk(vmm_ptp_hash, vmm_ptp_check, NULL);
mod_hash_destroy_ptrhash(vmm_ptp_hash);
}
void *
vmm_ptp_alloc(void)
{
void *p;
struct vmm_ptp_item *i;
p = kmem_zalloc(PAGE_SIZE, KM_SLEEP);
i = kmem_alloc(sizeof (struct vmm_ptp_item), KM_SLEEP);
i->vpi_vaddr = p;
mutex_enter(&vmm_ptp_lock);
VERIFY(mod_hash_insert(vmm_ptp_hash,
(mod_hash_key_t)PHYS_TO_DMAP(vtophys(p)), (mod_hash_val_t)i) == 0);
mutex_exit(&vmm_ptp_lock);
return (p);
}
void
vmm_ptp_free(void *addr)
{
mutex_enter(&vmm_ptp_lock);
VERIFY(mod_hash_destroy(vmm_ptp_hash,
(mod_hash_key_t)PHYS_TO_DMAP(vtophys(addr))) == 0);
mutex_exit(&vmm_ptp_lock);
}
extern void *contig_alloc(size_t, ddi_dma_attr_t *, uintptr_t, int);
extern void contig_free(void *, size_t);
void *
vmm_contig_alloc(size_t size)
{
ddi_dma_attr_t attr = {
.dma_attr_version = DMA_ATTR_V0,
.dma_attr_addr_lo = 0,
.dma_attr_addr_hi = ~0UL,
.dma_attr_count_max = 0x00000000FFFFFFFFULL,
.dma_attr_align = PAGE_SIZE,
.dma_attr_burstsizes = 1,
.dma_attr_minxfer = 1,
.dma_attr_maxxfer = 0x00000000FFFFFFFFULL,
.dma_attr_seg = 0x00000000FFFFFFFFULL,
.dma_attr_sgllen = 1,
.dma_attr_granular = PAGE_SIZE,
.dma_attr_flags = 0,
};
void *res;
res = contig_alloc(size, &attr, PAGE_SIZE, 1);
if (res != NULL) {
bzero(res, size);
}
return (res);
}
void
vmm_contig_free(void *addr, size_t size)
{
contig_free(addr, size);
}
void
critical_enter(void)
{
kpreempt_disable();
}
void
critical_exit(void)
{
kpreempt_enable();
}
static void
vmm_glue_callout_handler(void *arg)
{
struct callout *c = arg;
if (callout_active(c)) {
c->c_fired = gethrtime();
(c->c_func)(c->c_arg);
}
}
void
vmm_glue_callout_init(struct callout *c, int mpsafe)
{
cyc_handler_t hdlr;
cyc_time_t when;
hdlr.cyh_level = CY_LOW_LEVEL;
hdlr.cyh_func = vmm_glue_callout_handler;
hdlr.cyh_arg = c;
when.cyt_when = CY_INFINITY;
when.cyt_interval = CY_INFINITY;
bzero(c, sizeof (*c));
mutex_enter(&cpu_lock);
c->c_cyc_id = cyclic_add(&hdlr, &when);
mutex_exit(&cpu_lock);
}
void
callout_reset_hrtime(struct callout *c, hrtime_t target, void (*func)(void *),
void *arg, int flags)
{
ASSERT(c->c_cyc_id != CYCLIC_NONE);
if ((flags & C_ABSOLUTE) == 0) {
target += gethrtime();
}
c->c_func = func;
c->c_arg = arg;
c->c_target = target;
(void) cyclic_reprogram(c->c_cyc_id, target);
}
void
vmm_glue_callout_stop(struct callout *c)
{
ASSERT(c->c_cyc_id != CYCLIC_NONE);
c->c_target = 0;
(void) cyclic_reprogram(c->c_cyc_id, CY_INFINITY);
}
void
vmm_glue_callout_drain(struct callout *c)
{
ASSERT(c->c_cyc_id != CYCLIC_NONE);
c->c_target = 0;
mutex_enter(&cpu_lock);
cyclic_remove(c->c_cyc_id);
c->c_cyc_id = CYCLIC_NONE;
mutex_exit(&cpu_lock);
}
void
vmm_glue_callout_localize(struct callout *c)
{
mutex_enter(&cpu_lock);
cyclic_move_here(c->c_cyc_id);
mutex_exit(&cpu_lock);
}
uint64_t
hrt_freq_count(hrtime_t interval, uint32_t freq)
{
ASSERT3S(interval, >=, 0);
const uint64_t sec = interval / NANOSEC;
const uint64_t nsec = interval % NANOSEC;
return ((sec * freq) + ((nsec * freq) / NANOSEC));
}
hrtime_t
hrt_freq_interval(uint32_t freq, uint64_t count)
{
const uint64_t sec = count / freq;
const uint64_t frac = count % freq;
return ((NANOSEC * sec) + ((frac * NANOSEC) / freq));
}
uint_t cpu_high;
uint_t cpu_exthigh;
uint_t cpu_id;
char cpu_vendor[20];
static void
vmm_cpuid_init(void)
{
uint_t regs[4];
do_cpuid(0, regs);
cpu_high = regs[0];
((uint_t *)&cpu_vendor)[0] = regs[1];
((uint_t *)&cpu_vendor)[1] = regs[3];
((uint_t *)&cpu_vendor)[2] = regs[2];
cpu_vendor[12] = '\0';
do_cpuid(1, regs);
cpu_id = regs[0];
do_cpuid(0x80000000, regs);
cpu_exthigh = regs[0];
}
void
vmm_sol_glue_init(void)
{
vmm_ptp_init();
vmm_cpuid_init();
vmm_tsc_init();
}
void
vmm_sol_glue_cleanup(void)
{
vmm_ptp_cleanup();
}
#include <sys/clock.h>
#define FEBRUARY 2
#define days_in_year(y) (leapyear(y) ? 366 : 365)
#define days_in_month(y, m) \
(month_days[(m) - 1] + (m == FEBRUARY ? leapyear(y) : 0))
#define day_of_week(days) (((days) + 4) % 7)
static const int month_days[12] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
static int
leapyear(int year)
{
int rv = 0;
if ((year & 3) == 0) {
rv = 1;
if ((year % 100) == 0) {
rv = 0;
if ((year % 400) == 0)
rv = 1;
}
}
return (rv);
}
int
clock_ct_to_ts(struct clocktime *ct, struct timespec *ts)
{
int i, year, days;
year = ct->year;
#ifdef __FreeBSD__
if (ct_debug) {
printf("ct_to_ts(");
print_ct(ct);
printf(")");
}
#endif
if (ct->mon < 1 || ct->mon > 12 || ct->day < 1 ||
ct->day > days_in_month(year, ct->mon) ||
ct->hour > 23 || ct->min > 59 || ct->sec > 59 ||
(sizeof (time_t) == 4 && year > 2037)) {
#ifdef __FreeBSD__
if (ct_debug)
printf(" = EINVAL\n");
#endif
return (EINVAL);
}
days = 0;
for (i = POSIX_BASE_YEAR; i < year; i++)
days += days_in_year(i);
for (i = 1; i < ct->mon; i++)
days += days_in_month(year, i);
days += (ct->day - 1);
ts->tv_sec = (((time_t)days * 24 + ct->hour) * 60 + ct->min) * 60 +
ct->sec;
ts->tv_nsec = ct->nsec;
#ifdef __FreeBSD__
if (ct_debug)
printf(" = %ld.%09ld\n", (long)ts->tv_sec, (long)ts->tv_nsec);
#endif
return (0);
}
void
clock_ts_to_ct(struct timespec *ts, struct clocktime *ct)
{
int i, year, days;
time_t rsec;
time_t secs;
secs = ts->tv_sec;
days = secs / SECDAY;
rsec = secs % SECDAY;
ct->dow = day_of_week(days);
for (year = POSIX_BASE_YEAR; days >= days_in_year(year); year++)
days -= days_in_year(year);
ct->year = year;
for (i = 1; days >= days_in_month(year, i); i++)
days -= days_in_month(year, i);
ct->mon = i;
ct->day = days + 1;
ct->hour = rsec / 3600;
rsec = rsec % 3600;
ct->min = rsec / 60;
rsec = rsec % 60;
ct->sec = rsec;
ct->nsec = ts->tv_nsec;
#ifdef __FreeBSD__
if (ct_debug) {
printf("ts_to_ct(%ld.%09ld) = ",
(long)ts->tv_sec, (long)ts->tv_nsec);
print_ct(ct);
printf("\n");
}
#endif
}
static bool vmm_host_tsc_offset;
static void
vmm_tsc_init(void)
{
extern hrtime_t (*gethrtimeunscaledf)(void);
extern hrtime_t tsc_gethrtimeunscaled(void);
extern hrtime_t tsc_gethrtimeunscaled_delta(void);
VERIFY(*gethrtimeunscaledf == tsc_gethrtimeunscaled ||
*gethrtimeunscaledf == tsc_gethrtimeunscaled_delta);
vmm_host_tsc_offset =
(*gethrtimeunscaledf == tsc_gethrtimeunscaled_delta);
}
uint64_t
rdtsc_offset(void)
{
return ((uint64_t)gethrtimeunscaledf());
}
uint64_t
vmm_host_tsc_delta(void)
{
if (vmm_host_tsc_offset) {
extern hrtime_t tsc_gethrtime_tick_delta(void);
return (tsc_gethrtime_tick_delta());
} else {
return (0);
}
}