mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-15 10:17:20 +00:00
Merge ^/head r274961 through r276472.
This commit is contained in:
commit
ccd2f3b61a
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/projects/clang350-import/; revision=276473
@ -4,7 +4,7 @@
|
||||
The compilation of software known as FreeBSD is distributed under the
|
||||
following terms:
|
||||
|
||||
Copyright (c) 1992-2014 The FreeBSD Project. All rights reserved.
|
||||
Copyright (c) 1992-2015 The FreeBSD Project. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
|
@ -256,7 +256,8 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf, size_t len,
|
||||
break;
|
||||
|
||||
case ZPOOL_PROP_HEALTH:
|
||||
(void) strlcpy(buf, "FAULTED", len);
|
||||
(void) strlcpy(buf,
|
||||
zpool_pool_state_to_name(POOL_STATE_UNAVAIL), len);
|
||||
break;
|
||||
|
||||
case ZPOOL_PROP_GUID:
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include <gelf.h>
|
||||
#include <libelf.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "_libelf.h"
|
||||
@ -50,7 +51,6 @@ _libelf_load_section_headers(Elf *e, void *ehdr)
|
||||
Elf64_Ehdr *eh64;
|
||||
int ec, swapbytes;
|
||||
unsigned char *src;
|
||||
unsigned char *rawend;
|
||||
size_t fsz, i, shnum;
|
||||
int (*xlator)(unsigned char *_d, size_t _dsz, unsigned char *_s,
|
||||
size_t _c, int _swap);
|
||||
@ -61,7 +61,8 @@ _libelf_load_section_headers(Elf *e, void *ehdr)
|
||||
|
||||
#define CHECK_EHDR(E,EH) do { \
|
||||
if (fsz != (EH)->e_shentsize || \
|
||||
shoff + fsz * shnum > e->e_rawsize) { \
|
||||
shnum > SIZE_MAX / fsz || \
|
||||
fsz * shnum > e->e_rawsize - shoff) { \
|
||||
LIBELF_SET_ERROR(HEADER, 0); \
|
||||
return (0); \
|
||||
} \
|
||||
@ -87,7 +88,6 @@ _libelf_load_section_headers(Elf *e, void *ehdr)
|
||||
|
||||
swapbytes = e->e_byteorder != LIBELF_PRIVATE(byteorder);
|
||||
src = e->e_rawfile + shoff;
|
||||
rawend = e->e_rawfile + e->e_rawsize;
|
||||
|
||||
/*
|
||||
* If the file is using extended numbering then section #0
|
||||
@ -104,8 +104,6 @@ _libelf_load_section_headers(Elf *e, void *ehdr)
|
||||
}
|
||||
|
||||
for (; i < shnum; i++, src += fsz) {
|
||||
if (src + sizeof(scn->s_shdr) > rawend)
|
||||
return (0);
|
||||
if ((scn = _libelf_allocate_scn(e, i)) == NULL)
|
||||
return (0);
|
||||
|
||||
|
@ -41,6 +41,7 @@ __RCSID("$NetBSD: t_access.c,v 1.1 2011/07/07 06:57:53 jruoho Exp $");
|
||||
#include <atf-c.h>
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
@ -116,6 +117,10 @@ ATF_TC_HEAD(access_inval, tc)
|
||||
ATF_TC_BODY(access_inval, tc)
|
||||
{
|
||||
|
||||
#if defined(__FreeBSD__) && __FreeBSD_version < 1100033
|
||||
atf_tc_expect_fail("arguments to access aren't validated; see "
|
||||
"bug # 181155 for more details");
|
||||
#endif
|
||||
errno = 0;
|
||||
|
||||
ATF_REQUIRE(access("/usr", -1) != 0);
|
||||
|
@ -1146,3 +1146,55 @@ vm_set_intinfo(struct vmctx *ctx, int vcpu, uint64_t info1)
|
||||
error = ioctl(ctx->fd, VM_SET_INTINFO, &vmii);
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
vm_rtc_write(struct vmctx *ctx, int offset, uint8_t value)
|
||||
{
|
||||
struct vm_rtc_data rtcdata;
|
||||
int error;
|
||||
|
||||
bzero(&rtcdata, sizeof(struct vm_rtc_data));
|
||||
rtcdata.offset = offset;
|
||||
rtcdata.value = value;
|
||||
error = ioctl(ctx->fd, VM_RTC_WRITE, &rtcdata);
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
vm_rtc_read(struct vmctx *ctx, int offset, uint8_t *retval)
|
||||
{
|
||||
struct vm_rtc_data rtcdata;
|
||||
int error;
|
||||
|
||||
bzero(&rtcdata, sizeof(struct vm_rtc_data));
|
||||
rtcdata.offset = offset;
|
||||
error = ioctl(ctx->fd, VM_RTC_READ, &rtcdata);
|
||||
if (error == 0)
|
||||
*retval = rtcdata.value;
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
vm_rtc_settime(struct vmctx *ctx, time_t secs)
|
||||
{
|
||||
struct vm_rtc_time rtctime;
|
||||
int error;
|
||||
|
||||
bzero(&rtctime, sizeof(struct vm_rtc_time));
|
||||
rtctime.secs = secs;
|
||||
error = ioctl(ctx->fd, VM_RTC_SETTIME, &rtctime);
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
vm_rtc_gettime(struct vmctx *ctx, time_t *secs)
|
||||
{
|
||||
struct vm_rtc_time rtctime;
|
||||
int error;
|
||||
|
||||
bzero(&rtctime, sizeof(struct vm_rtc_time));
|
||||
error = ioctl(ctx->fd, VM_RTC_GETTIME, &rtctime);
|
||||
if (error == 0)
|
||||
*secs = rtctime.secs;
|
||||
return (error);
|
||||
}
|
||||
|
@ -133,6 +133,12 @@ void vm_copyin(struct vmctx *ctx, int vcpu, struct iovec *guest_iov,
|
||||
void vm_copyout(struct vmctx *ctx, int vcpu, const void *host_src,
|
||||
struct iovec *guest_iov, size_t len);
|
||||
|
||||
/* RTC */
|
||||
int vm_rtc_write(struct vmctx *ctx, int offset, uint8_t value);
|
||||
int vm_rtc_read(struct vmctx *ctx, int offset, uint8_t *retval);
|
||||
int vm_rtc_settime(struct vmctx *ctx, time_t secs);
|
||||
int vm_rtc_gettime(struct vmctx *ctx, time_t *secs);
|
||||
|
||||
/* Reset vcpu register state */
|
||||
int vcpu_reset(struct vmctx *ctx, int vcpu);
|
||||
|
||||
|
0
share/man/man4/iscsi.4
Executable file → Normal file
0
share/man/man4/iscsi.4
Executable file → Normal file
@ -286,6 +286,7 @@ int vm_unassign_pptdev(struct vm *vm, int bus, int slot, int func);
|
||||
struct vatpic *vm_atpic(struct vm *vm);
|
||||
struct vatpit *vm_atpit(struct vm *vm);
|
||||
struct vpmtmr *vm_pmtmr(struct vm *vm);
|
||||
struct vrtc *vm_rtc(struct vm *vm);
|
||||
|
||||
/*
|
||||
* Inject exception 'vme' into the guest vcpu. This function returns 0 on
|
||||
|
@ -195,6 +195,15 @@ struct vm_intinfo {
|
||||
uint64_t info2;
|
||||
};
|
||||
|
||||
struct vm_rtc_time {
|
||||
time_t secs;
|
||||
};
|
||||
|
||||
struct vm_rtc_data {
|
||||
int offset;
|
||||
uint8_t value;
|
||||
};
|
||||
|
||||
enum {
|
||||
/* general routines */
|
||||
IOCNUM_ABIVERS = 0,
|
||||
@ -254,6 +263,12 @@ enum {
|
||||
/* vm_cpuset */
|
||||
IOCNUM_ACTIVATE_CPU = 90,
|
||||
IOCNUM_GET_CPUSET = 91,
|
||||
|
||||
/* RTC */
|
||||
IOCNUM_RTC_READ = 100,
|
||||
IOCNUM_RTC_WRITE = 101,
|
||||
IOCNUM_RTC_SETTIME = 102,
|
||||
IOCNUM_RTC_GETTIME = 103,
|
||||
};
|
||||
|
||||
#define VM_RUN \
|
||||
@ -336,4 +351,12 @@ enum {
|
||||
_IOW('v', IOCNUM_SET_INTINFO, struct vm_intinfo)
|
||||
#define VM_GET_INTINFO \
|
||||
_IOWR('v', IOCNUM_GET_INTINFO, struct vm_intinfo)
|
||||
#define VM_RTC_WRITE \
|
||||
_IOW('v', IOCNUM_RTC_WRITE, struct vm_rtc_data)
|
||||
#define VM_RTC_READ \
|
||||
_IOWR('v', IOCNUM_RTC_READ, struct vm_rtc_data)
|
||||
#define VM_RTC_SETTIME \
|
||||
_IOW('v', IOCNUM_RTC_SETTIME, struct vm_rtc_time)
|
||||
#define VM_RTC_GETTIME \
|
||||
_IOR('v', IOCNUM_RTC_GETTIME, struct vm_rtc_time)
|
||||
#endif
|
||||
|
@ -101,14 +101,22 @@
|
||||
#define VM_FREEPOOL_DIRECT 1
|
||||
|
||||
/*
|
||||
* Create two free page lists: VM_FREELIST_DEFAULT is for physical
|
||||
* pages that are above the largest physical address that is
|
||||
* accessible by ISA DMA and VM_FREELIST_ISADMA is for physical pages
|
||||
* that are below that address.
|
||||
* Create up to three free page lists: VM_FREELIST_DMA32 is for physical pages
|
||||
* that have physical addresses below 4G but are not accessible by ISA DMA,
|
||||
* and VM_FREELIST_ISADMA is for physical pages that are accessible by ISA
|
||||
* DMA.
|
||||
*/
|
||||
#define VM_NFREELIST 2
|
||||
#define VM_NFREELIST 3
|
||||
#define VM_FREELIST_DEFAULT 0
|
||||
#define VM_FREELIST_ISADMA 1
|
||||
#define VM_FREELIST_DMA32 1
|
||||
#define VM_FREELIST_ISADMA 2
|
||||
|
||||
/*
|
||||
* Create the DMA32 free list only if the number of physical pages above
|
||||
* physical address 4G is at least 16M, which amounts to 64GB of physical
|
||||
* memory.
|
||||
*/
|
||||
#define VM_DMA32_NPAGES_THRESHOLD 16777216
|
||||
|
||||
/*
|
||||
* An allocation size of 16MB is supported in order to optimize the
|
||||
|
@ -1322,9 +1322,12 @@ svm_vmexit(struct svm_softc *svm_sc, int vcpu, struct vm_exit *vmexit)
|
||||
|
||||
if (reflect) {
|
||||
/* Reflect the exception back into the guest */
|
||||
bzero(&exception, sizeof(struct vm_exception));
|
||||
exception.vector = idtvec;
|
||||
exception.error_code_valid = errcode_valid;
|
||||
exception.error_code = errcode_valid ? info1 : 0;
|
||||
if (errcode_valid) {
|
||||
exception.error_code = info1;
|
||||
exception.error_code_valid = 1;
|
||||
}
|
||||
VCPU_CTR2(svm_sc->vm, vcpu, "Reflecting exception "
|
||||
"%d/%#x into the guest", exception.vector,
|
||||
exception.error_code);
|
||||
|
@ -104,7 +104,6 @@ vhpet_capabilities(void)
|
||||
uint64_t cap = 0;
|
||||
|
||||
cap |= 0x8086 << 16; /* vendor id */
|
||||
cap |= HPET_CAP_LEG_RT; /* legacy routing capable */
|
||||
cap |= (VHPET_NUM_TIMERS - 1) << 8; /* number of timers */
|
||||
cap |= 1; /* revision */
|
||||
cap &= ~HPET_CAP_COUNT_SIZE; /* 32-bit timer */
|
||||
@ -127,15 +126,6 @@ vhpet_timer_msi_enabled(struct vhpet *vhpet, int n)
|
||||
{
|
||||
const uint64_t msi_enable = HPET_TCAP_FSB_INT_DEL | HPET_TCNF_FSB_EN;
|
||||
|
||||
/*
|
||||
* LegacyReplacement Route configuration takes precedence over MSI
|
||||
* for timers 0 and 1.
|
||||
*/
|
||||
if (n == 0 || n == 1) {
|
||||
if (vhpet->config & HPET_CNF_LEG_RT)
|
||||
return (false);
|
||||
}
|
||||
|
||||
if ((vhpet->timer[n].cap_config & msi_enable) == msi_enable)
|
||||
return (true);
|
||||
else
|
||||
@ -152,41 +142,9 @@ vhpet_timer_ioapic_pin(struct vhpet *vhpet, int n)
|
||||
if (vhpet_timer_msi_enabled(vhpet, n))
|
||||
return (0);
|
||||
|
||||
if (vhpet->config & HPET_CNF_LEG_RT) {
|
||||
/*
|
||||
* In "legacy routing" timers 0 and 1 are connected to
|
||||
* ioapic pins 2 and 8 respectively.
|
||||
*/
|
||||
switch (n) {
|
||||
case 0:
|
||||
return (2);
|
||||
case 1:
|
||||
return (8);
|
||||
}
|
||||
}
|
||||
|
||||
return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ROUTE) >> 9);
|
||||
}
|
||||
|
||||
static __inline int
|
||||
vhpet_timer_atpic_pin(struct vhpet *vhpet, int n)
|
||||
{
|
||||
if (vhpet->config & HPET_CNF_LEG_RT) {
|
||||
/*
|
||||
* In "legacy routing" timers 0 and 1 are connected to
|
||||
* 8259 master pin 0 and slave pin 0 respectively.
|
||||
*/
|
||||
switch (n) {
|
||||
case 0:
|
||||
return (0);
|
||||
case 1:
|
||||
return (8);
|
||||
}
|
||||
}
|
||||
|
||||
return (-1);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
vhpet_counter(struct vhpet *vhpet, sbintime_t *nowptr)
|
||||
{
|
||||
@ -216,17 +174,12 @@ vhpet_counter(struct vhpet *vhpet, sbintime_t *nowptr)
|
||||
static void
|
||||
vhpet_timer_clear_isr(struct vhpet *vhpet, int n)
|
||||
{
|
||||
int pin, legacy_pin;
|
||||
int pin;
|
||||
|
||||
if (vhpet->isr & (1 << n)) {
|
||||
pin = vhpet_timer_ioapic_pin(vhpet, n);
|
||||
KASSERT(pin != 0, ("vhpet timer %d irq incorrectly routed", n));
|
||||
vioapic_deassert_irq(vhpet->vm, pin);
|
||||
|
||||
legacy_pin = vhpet_timer_atpic_pin(vhpet, n);
|
||||
if (legacy_pin != -1)
|
||||
vatpic_deassert_irq(vhpet->vm, legacy_pin);
|
||||
|
||||
vhpet->isr &= ~(1 << n);
|
||||
}
|
||||
}
|
||||
@ -252,12 +205,6 @@ vhpet_timer_edge_trig(struct vhpet *vhpet, int n)
|
||||
KASSERT(!vhpet_timer_msi_enabled(vhpet, n), ("vhpet_timer_edge_trig: "
|
||||
"timer %d is using MSI", n));
|
||||
|
||||
/* The legacy replacement interrupts are always edge triggered */
|
||||
if (vhpet->config & HPET_CNF_LEG_RT) {
|
||||
if (n == 0 || n == 1)
|
||||
return (true);
|
||||
}
|
||||
|
||||
if ((vhpet->timer[n].cap_config & HPET_TCNF_INT_TYPE) == 0)
|
||||
return (true);
|
||||
else
|
||||
@ -267,7 +214,7 @@ vhpet_timer_edge_trig(struct vhpet *vhpet, int n)
|
||||
static void
|
||||
vhpet_timer_interrupt(struct vhpet *vhpet, int n)
|
||||
{
|
||||
int pin, legacy_pin;
|
||||
int pin;
|
||||
|
||||
/* If interrupts are not enabled for this timer then just return. */
|
||||
if (!vhpet_timer_interrupt_enabled(vhpet, n))
|
||||
@ -293,17 +240,11 @@ vhpet_timer_interrupt(struct vhpet *vhpet, int n)
|
||||
return;
|
||||
}
|
||||
|
||||
legacy_pin = vhpet_timer_atpic_pin(vhpet, n);
|
||||
|
||||
if (vhpet_timer_edge_trig(vhpet, n)) {
|
||||
vioapic_pulse_irq(vhpet->vm, pin);
|
||||
if (legacy_pin != -1)
|
||||
vatpic_pulse_irq(vhpet->vm, legacy_pin);
|
||||
} else {
|
||||
vhpet->isr |= 1 << n;
|
||||
vioapic_assert_irq(vhpet->vm, pin);
|
||||
if (legacy_pin != -1)
|
||||
vatpic_assert_irq(vhpet->vm, legacy_pin);
|
||||
}
|
||||
}
|
||||
|
||||
@ -579,6 +520,13 @@ vhpet_mmio_write(void *vm, int vcpuid, uint64_t gpa, uint64_t val, int size,
|
||||
counter = vhpet_counter(vhpet, nowptr);
|
||||
oldval = vhpet->config;
|
||||
update_register(&vhpet->config, data, mask);
|
||||
|
||||
/*
|
||||
* LegacyReplacement Routing is not supported so clear the
|
||||
* bit explicitly.
|
||||
*/
|
||||
vhpet->config &= ~HPET_CNF_LEG_RT;
|
||||
|
||||
if ((oldval ^ vhpet->config) & HPET_CNF_ENABLE) {
|
||||
if (vhpet_counter_enabled(vhpet)) {
|
||||
vhpet_start_counting(vhpet);
|
||||
|
952
sys/amd64/vmm/io/vrtc.c
Normal file
952
sys/amd64/vmm/io/vrtc.c
Normal file
@ -0,0 +1,952 @@
|
||||
/*-
|
||||
* Copyright (c) 2014, Neel Natu (neel@freebsd.org)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice unmodified, this list of conditions, and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/cpuset.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/clock.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <machine/vmm.h>
|
||||
|
||||
#include <isa/rtc.h>
|
||||
|
||||
#include "vmm_ktr.h"
|
||||
#include "vatpic.h"
|
||||
#include "vioapic.h"
|
||||
#include "vrtc.h"
|
||||
|
||||
/* Register layout of the RTC */
|
||||
struct rtcdev {
|
||||
uint8_t sec;
|
||||
uint8_t alarm_sec;
|
||||
uint8_t min;
|
||||
uint8_t alarm_min;
|
||||
uint8_t hour;
|
||||
uint8_t alarm_hour;
|
||||
uint8_t day_of_week;
|
||||
uint8_t day_of_month;
|
||||
uint8_t month;
|
||||
uint8_t year;
|
||||
uint8_t reg_a;
|
||||
uint8_t reg_b;
|
||||
uint8_t reg_c;
|
||||
uint8_t reg_d;
|
||||
uint8_t nvram[128 - 14];
|
||||
} __packed;
|
||||
CTASSERT(sizeof(struct rtcdev) == 128);
|
||||
|
||||
struct vrtc {
|
||||
struct vm *vm;
|
||||
struct mtx mtx;
|
||||
struct callout callout;
|
||||
u_int addr; /* RTC register to read or write */
|
||||
sbintime_t base_uptime;
|
||||
time_t base_rtctime;
|
||||
struct rtcdev rtcdev;
|
||||
};
|
||||
|
||||
#define VRTC_LOCK(vrtc) mtx_lock(&((vrtc)->mtx))
|
||||
#define VRTC_UNLOCK(vrtc) mtx_unlock(&((vrtc)->mtx))
|
||||
#define VRTC_LOCKED(vrtc) mtx_owned(&((vrtc)->mtx))
|
||||
|
||||
/*
|
||||
* RTC time is considered "broken" if:
|
||||
* - RTC updates are halted by the guest
|
||||
* - RTC date/time fields have invalid values
|
||||
*/
|
||||
#define VRTC_BROKEN_TIME ((time_t)-1)
|
||||
|
||||
#define RTC_IRQ 8
|
||||
#define RTCSB_BIN 0x04
|
||||
#define RTCSB_ALL_INTRS (RTCSB_UINTR | RTCSB_AINTR | RTCSB_PINTR)
|
||||
#define rtc_halted(vrtc) ((vrtc->rtcdev.reg_b & RTCSB_HALT) != 0)
|
||||
#define aintr_enabled(vrtc) (((vrtc)->rtcdev.reg_b & RTCSB_AINTR) != 0)
|
||||
#define pintr_enabled(vrtc) (((vrtc)->rtcdev.reg_b & RTCSB_PINTR) != 0)
|
||||
#define uintr_enabled(vrtc) (((vrtc)->rtcdev.reg_b & RTCSB_UINTR) != 0)
|
||||
|
||||
static void vrtc_callout_handler(void *arg);
|
||||
static void vrtc_set_reg_c(struct vrtc *vrtc, uint8_t newval);
|
||||
|
||||
static MALLOC_DEFINE(M_VRTC, "vrtc", "bhyve virtual rtc");
|
||||
|
||||
SYSCTL_DECL(_hw_vmm);
|
||||
SYSCTL_NODE(_hw_vmm, OID_AUTO, vrtc, CTLFLAG_RW, NULL, NULL);
|
||||
|
||||
static int rtc_flag_broken_time = 1;
|
||||
SYSCTL_INT(_hw_vmm_vrtc, OID_AUTO, flag_broken_time, CTLFLAG_RDTUN,
|
||||
&rtc_flag_broken_time, 0, "Stop guest when invalid RTC time is detected");
|
||||
|
||||
static __inline bool
|
||||
divider_enabled(int reg_a)
|
||||
{
|
||||
/*
|
||||
* The RTC is counting only when dividers are not held in reset.
|
||||
*/
|
||||
return ((reg_a & 0x70) == 0x20);
|
||||
}
|
||||
|
||||
static __inline bool
|
||||
update_enabled(struct vrtc *vrtc)
|
||||
{
|
||||
/*
|
||||
* RTC date/time can be updated only if:
|
||||
* - divider is not held in reset
|
||||
* - guest has not disabled updates
|
||||
* - the date/time fields have valid contents
|
||||
*/
|
||||
if (!divider_enabled(vrtc->rtcdev.reg_a))
|
||||
return (false);
|
||||
|
||||
if (rtc_halted(vrtc))
|
||||
return (false);
|
||||
|
||||
if (vrtc->base_rtctime == VRTC_BROKEN_TIME)
|
||||
return (false);
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
static time_t
|
||||
vrtc_curtime(struct vrtc *vrtc)
|
||||
{
|
||||
sbintime_t now, delta;
|
||||
time_t t;
|
||||
|
||||
KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__));
|
||||
|
||||
t = vrtc->base_rtctime;
|
||||
if (update_enabled(vrtc)) {
|
||||
now = sbinuptime();
|
||||
delta = now - vrtc->base_uptime;
|
||||
KASSERT(delta >= 0, ("vrtc_curtime: uptime went backwards: "
|
||||
"%#lx to %#lx", vrtc->base_uptime, now));
|
||||
t += delta / SBT_1S;
|
||||
}
|
||||
return (t);
|
||||
}
|
||||
|
||||
static __inline uint8_t
|
||||
rtcset(struct rtcdev *rtc, int val)
|
||||
{
|
||||
|
||||
KASSERT(val >= 0 && val < 100, ("%s: invalid bin2bcd index %d",
|
||||
__func__, val));
|
||||
|
||||
return ((rtc->reg_b & RTCSB_BIN) ? val : bin2bcd_data[val]);
|
||||
}
|
||||
|
||||
static void
|
||||
secs_to_rtc(time_t rtctime, struct vrtc *vrtc, int force_update)
|
||||
{
|
||||
struct clocktime ct;
|
||||
struct timespec ts;
|
||||
struct rtcdev *rtc;
|
||||
int hour;
|
||||
|
||||
KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__));
|
||||
|
||||
if (rtctime < 0) {
|
||||
KASSERT(rtctime == VRTC_BROKEN_TIME,
|
||||
("%s: invalid vrtc time %#lx", __func__, rtctime));
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the RTC is halted then the guest has "ownership" of the
|
||||
* date/time fields. Don't update the RTC date/time fields in
|
||||
* this case (unless forced).
|
||||
*/
|
||||
if (rtc_halted(vrtc) && !force_update)
|
||||
return;
|
||||
|
||||
ts.tv_sec = rtctime;
|
||||
ts.tv_nsec = 0;
|
||||
clock_ts_to_ct(&ts, &ct);
|
||||
|
||||
KASSERT(ct.sec >= 0 && ct.sec <= 59, ("invalid clocktime sec %d",
|
||||
ct.sec));
|
||||
KASSERT(ct.min >= 0 && ct.min <= 59, ("invalid clocktime min %d",
|
||||
ct.min));
|
||||
KASSERT(ct.hour >= 0 && ct.hour <= 23, ("invalid clocktime hour %d",
|
||||
ct.hour));
|
||||
KASSERT(ct.dow >= 0 && ct.dow <= 6, ("invalid clocktime wday %d",
|
||||
ct.dow));
|
||||
KASSERT(ct.day >= 1 && ct.day <= 31, ("invalid clocktime mday %d",
|
||||
ct.day));
|
||||
KASSERT(ct.mon >= 1 && ct.mon <= 12, ("invalid clocktime month %d",
|
||||
ct.mon));
|
||||
KASSERT(ct.year >= POSIX_BASE_YEAR, ("invalid clocktime year %d",
|
||||
ct.year));
|
||||
|
||||
rtc = &vrtc->rtcdev;
|
||||
rtc->sec = rtcset(rtc, ct.sec);
|
||||
rtc->min = rtcset(rtc, ct.min);
|
||||
|
||||
hour = ct.hour;
|
||||
if ((rtc->reg_b & RTCSB_24HR) == 0)
|
||||
hour = (hour % 12) + 1; /* convert to a 12-hour format */
|
||||
|
||||
rtc->hour = rtcset(rtc, hour);
|
||||
|
||||
if ((rtc->reg_b & RTCSB_24HR) == 0 && ct.hour >= 12)
|
||||
rtc->hour |= 0x80; /* set MSB to indicate PM */
|
||||
|
||||
rtc->day_of_week = rtcset(rtc, ct.dow + 1);
|
||||
rtc->day_of_month = rtcset(rtc, ct.day);
|
||||
rtc->month = rtcset(rtc, ct.mon);
|
||||
rtc->year = rtcset(rtc, ct.year % 100);
|
||||
}
|
||||
|
||||
static int
|
||||
rtcget(struct rtcdev *rtc, int val, int *retval)
|
||||
{
|
||||
uint8_t upper, lower;
|
||||
|
||||
if (rtc->reg_b & RTCSB_BIN) {
|
||||
*retval = val;
|
||||
return (0);
|
||||
}
|
||||
|
||||
lower = val & 0xf;
|
||||
upper = (val >> 4) & 0xf;
|
||||
|
||||
if (lower > 9 || upper > 9)
|
||||
return (-1);
|
||||
|
||||
*retval = upper * 10 + lower;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static time_t
|
||||
rtc_to_secs(struct vrtc *vrtc)
|
||||
{
|
||||
struct clocktime ct;
|
||||
struct timespec ts;
|
||||
struct rtcdev *rtc;
|
||||
struct vm *vm;
|
||||
int error, hour, pm, year;
|
||||
|
||||
KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__));
|
||||
|
||||
vm = vrtc->vm;
|
||||
rtc = &vrtc->rtcdev;
|
||||
|
||||
bzero(&ct, sizeof(struct clocktime));
|
||||
|
||||
error = rtcget(rtc, rtc->sec, &ct.sec);
|
||||
if (error || ct.sec < 0 || ct.sec > 59) {
|
||||
VM_CTR2(vm, "Invalid RTC sec %#x/%d", rtc->sec, ct.sec);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
error = rtcget(rtc, rtc->min, &ct.min);
|
||||
if (error || ct.min < 0 || ct.min > 59) {
|
||||
VM_CTR2(vm, "Invalid RTC min %#x/%d", rtc->min, ct.min);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pm = 0;
|
||||
hour = rtc->hour;
|
||||
if ((rtc->reg_b & RTCSB_24HR) == 0) {
|
||||
if (hour & 0x80) {
|
||||
hour &= ~0x80;
|
||||
pm = 1;
|
||||
}
|
||||
}
|
||||
error = rtcget(rtc, hour, &ct.hour);
|
||||
if ((rtc->reg_b & RTCSB_24HR) == 0) {
|
||||
ct.hour -= 1;
|
||||
if (pm)
|
||||
ct.hour += 12;
|
||||
}
|
||||
|
||||
if (error || ct.hour < 0 || ct.hour > 23) {
|
||||
VM_CTR2(vm, "Invalid RTC hour %#x/%d", rtc->hour, ct.hour);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ignore 'rtc->dow' because some guests like Linux don't bother
|
||||
* setting it at all while others like OpenBSD/i386 set it incorrectly.
|
||||
*
|
||||
* clock_ct_to_ts() does not depend on 'ct.dow' anyways so ignore it.
|
||||
*/
|
||||
ct.dow = -1;
|
||||
|
||||
error = rtcget(rtc, rtc->day_of_month, &ct.day);
|
||||
if (error || ct.day < 1 || ct.day > 31) {
|
||||
VM_CTR2(vm, "Invalid RTC mday %#x/%d", rtc->day_of_month,
|
||||
ct.day);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
error = rtcget(rtc, rtc->month, &ct.mon);
|
||||
if (error || ct.mon < 1 || ct.mon > 12) {
|
||||
VM_CTR2(vm, "Invalid RTC month %#x/%d", rtc->month, ct.mon);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
error = rtcget(rtc, rtc->year, &year);
|
||||
if (error || year < 0 || year > 99) {
|
||||
VM_CTR2(vm, "Invalid RTC year %#x/%d", rtc->year, year);
|
||||
goto fail;
|
||||
}
|
||||
if (year >= 70)
|
||||
ct.year = 1900 + year;
|
||||
else
|
||||
ct.year = 2000 + year;
|
||||
|
||||
error = clock_ct_to_ts(&ct, &ts);
|
||||
if (error || ts.tv_sec < 0) {
|
||||
VM_CTR3(vm, "Invalid RTC clocktime.date %04d-%02d-%02d",
|
||||
ct.year, ct.mon, ct.day);
|
||||
VM_CTR3(vm, "Invalid RTC clocktime.time %02d:%02d:%02d",
|
||||
ct.hour, ct.min, ct.sec);
|
||||
goto fail;
|
||||
}
|
||||
return (ts.tv_sec); /* success */
|
||||
fail:
|
||||
return (VRTC_BROKEN_TIME); /* failure */
|
||||
}
|
||||
|
||||
static int
|
||||
vrtc_time_update(struct vrtc *vrtc, time_t newtime)
|
||||
{
|
||||
struct rtcdev *rtc;
|
||||
time_t oldtime;
|
||||
uint8_t alarm_sec, alarm_min, alarm_hour;
|
||||
|
||||
KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__));
|
||||
|
||||
rtc = &vrtc->rtcdev;
|
||||
alarm_sec = rtc->alarm_sec;
|
||||
alarm_min = rtc->alarm_min;
|
||||
alarm_hour = rtc->alarm_hour;
|
||||
|
||||
oldtime = vrtc->base_rtctime;
|
||||
VM_CTR2(vrtc->vm, "Updating RTC time from %#lx to %#lx",
|
||||
oldtime, newtime);
|
||||
|
||||
if (newtime == oldtime)
|
||||
return (0);
|
||||
|
||||
/*
|
||||
* If 'newtime' indicates that RTC updates are disabled then just
|
||||
* record that and return. There is no need to do alarm interrupt
|
||||
* processing or update 'base_uptime' in this case.
|
||||
*/
|
||||
if (newtime == VRTC_BROKEN_TIME) {
|
||||
vrtc->base_rtctime = VRTC_BROKEN_TIME;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return an error if RTC updates are halted by the guest.
|
||||
*/
|
||||
if (rtc_halted(vrtc)) {
|
||||
VM_CTR0(vrtc->vm, "RTC update halted by guest");
|
||||
return (EBUSY);
|
||||
}
|
||||
|
||||
do {
|
||||
/*
|
||||
* If the alarm interrupt is enabled and 'oldtime' is valid
|
||||
* then visit all the seconds between 'oldtime' and 'newtime'
|
||||
* to check for the alarm condition.
|
||||
*
|
||||
* Otherwise move the RTC time forward directly to 'newtime'.
|
||||
*/
|
||||
if (aintr_enabled(vrtc) && oldtime != VRTC_BROKEN_TIME)
|
||||
vrtc->base_rtctime++;
|
||||
else
|
||||
vrtc->base_rtctime = newtime;
|
||||
|
||||
if (aintr_enabled(vrtc)) {
|
||||
/*
|
||||
* Update the RTC date/time fields before checking
|
||||
* if the alarm conditions are satisfied.
|
||||
*/
|
||||
secs_to_rtc(vrtc->base_rtctime, vrtc, 0);
|
||||
|
||||
if ((alarm_sec >= 0xC0 || alarm_sec == rtc->sec) &&
|
||||
(alarm_min >= 0xC0 || alarm_min == rtc->min) &&
|
||||
(alarm_hour >= 0xC0 || alarm_hour == rtc->hour)) {
|
||||
vrtc_set_reg_c(vrtc, rtc->reg_c | RTCIR_ALARM);
|
||||
}
|
||||
}
|
||||
} while (vrtc->base_rtctime != newtime);
|
||||
|
||||
if (uintr_enabled(vrtc))
|
||||
vrtc_set_reg_c(vrtc, rtc->reg_c | RTCIR_UPDATE);
|
||||
|
||||
vrtc->base_uptime = sbinuptime();
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static sbintime_t
|
||||
vrtc_freq(struct vrtc *vrtc)
|
||||
{
|
||||
int ratesel;
|
||||
|
||||
static sbintime_t pf[16] = {
|
||||
0,
|
||||
SBT_1S / 256,
|
||||
SBT_1S / 128,
|
||||
SBT_1S / 8192,
|
||||
SBT_1S / 4096,
|
||||
SBT_1S / 2048,
|
||||
SBT_1S / 1024,
|
||||
SBT_1S / 512,
|
||||
SBT_1S / 256,
|
||||
SBT_1S / 128,
|
||||
SBT_1S / 64,
|
||||
SBT_1S / 32,
|
||||
SBT_1S / 16,
|
||||
SBT_1S / 8,
|
||||
SBT_1S / 4,
|
||||
SBT_1S / 2,
|
||||
};
|
||||
|
||||
KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__));
|
||||
|
||||
/*
|
||||
* If both periodic and alarm interrupts are enabled then use the
|
||||
* periodic frequency to drive the callout. The minimum periodic
|
||||
* frequency (2 Hz) is higher than the alarm frequency (1 Hz) so
|
||||
* piggyback the alarm on top of it. The same argument applies to
|
||||
* the update interrupt.
|
||||
*/
|
||||
if (pintr_enabled(vrtc) && divider_enabled(vrtc->rtcdev.reg_a)) {
|
||||
ratesel = vrtc->rtcdev.reg_a & 0xf;
|
||||
return (pf[ratesel]);
|
||||
} else if (aintr_enabled(vrtc) && update_enabled(vrtc)) {
|
||||
return (SBT_1S);
|
||||
} else if (uintr_enabled(vrtc) && update_enabled(vrtc)) {
|
||||
return (SBT_1S);
|
||||
} else {
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
vrtc_callout_reset(struct vrtc *vrtc, sbintime_t freqsbt)
|
||||
{
|
||||
|
||||
KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__));
|
||||
|
||||
if (freqsbt == 0) {
|
||||
if (callout_active(&vrtc->callout)) {
|
||||
VM_CTR0(vrtc->vm, "RTC callout stopped");
|
||||
callout_stop(&vrtc->callout);
|
||||
}
|
||||
return;
|
||||
}
|
||||
VM_CTR1(vrtc->vm, "RTC callout frequency %d hz", SBT_1S / freqsbt);
|
||||
callout_reset_sbt(&vrtc->callout, freqsbt, 0, vrtc_callout_handler,
|
||||
vrtc, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
vrtc_callout_handler(void *arg)
|
||||
{
|
||||
struct vrtc *vrtc = arg;
|
||||
sbintime_t freqsbt;
|
||||
time_t rtctime;
|
||||
int error;
|
||||
|
||||
VM_CTR0(vrtc->vm, "vrtc callout fired");
|
||||
|
||||
VRTC_LOCK(vrtc);
|
||||
if (callout_pending(&vrtc->callout)) /* callout was reset */
|
||||
goto done;
|
||||
|
||||
if (!callout_active(&vrtc->callout)) /* callout was stopped */
|
||||
goto done;
|
||||
|
||||
callout_deactivate(&vrtc->callout);
|
||||
|
||||
KASSERT((vrtc->rtcdev.reg_b & RTCSB_ALL_INTRS) != 0,
|
||||
("gratuitous vrtc callout"));
|
||||
|
||||
if (pintr_enabled(vrtc))
|
||||
vrtc_set_reg_c(vrtc, vrtc->rtcdev.reg_c | RTCIR_PERIOD);
|
||||
|
||||
if (aintr_enabled(vrtc) || uintr_enabled(vrtc)) {
|
||||
rtctime = vrtc_curtime(vrtc);
|
||||
error = vrtc_time_update(vrtc, rtctime);
|
||||
KASSERT(error == 0, ("%s: vrtc_time_update error %d",
|
||||
__func__, error));
|
||||
}
|
||||
|
||||
freqsbt = vrtc_freq(vrtc);
|
||||
KASSERT(freqsbt != 0, ("%s: vrtc frequency cannot be zero", __func__));
|
||||
vrtc_callout_reset(vrtc, freqsbt);
|
||||
done:
|
||||
VRTC_UNLOCK(vrtc);
|
||||
}
|
||||
|
||||
static __inline void
|
||||
vrtc_callout_check(struct vrtc *vrtc, sbintime_t freq)
|
||||
{
|
||||
int active;
|
||||
|
||||
active = callout_active(&vrtc->callout) ? 1 : 0;
|
||||
KASSERT((freq == 0 && !active) || (freq != 0 && active),
|
||||
("vrtc callout %s with frequency %#lx",
|
||||
active ? "active" : "inactive", freq));
|
||||
}
|
||||
|
||||
static void
|
||||
vrtc_set_reg_c(struct vrtc *vrtc, uint8_t newval)
|
||||
{
|
||||
struct rtcdev *rtc;
|
||||
int oldirqf, newirqf;
|
||||
uint8_t oldval, changed;
|
||||
|
||||
KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__));
|
||||
|
||||
rtc = &vrtc->rtcdev;
|
||||
newval &= RTCIR_ALARM | RTCIR_PERIOD | RTCIR_UPDATE;
|
||||
|
||||
oldirqf = rtc->reg_c & RTCIR_INT;
|
||||
if ((aintr_enabled(vrtc) && (newval & RTCIR_ALARM) != 0) ||
|
||||
(pintr_enabled(vrtc) && (newval & RTCIR_PERIOD) != 0) ||
|
||||
(uintr_enabled(vrtc) && (newval & RTCIR_UPDATE) != 0)) {
|
||||
newirqf = RTCIR_INT;
|
||||
} else {
|
||||
newirqf = 0;
|
||||
}
|
||||
|
||||
oldval = rtc->reg_c;
|
||||
rtc->reg_c = newirqf | newval;
|
||||
changed = oldval ^ rtc->reg_c;
|
||||
if (changed) {
|
||||
VM_CTR2(vrtc->vm, "RTC reg_c changed from %#x to %#x",
|
||||
oldval, rtc->reg_c);
|
||||
}
|
||||
|
||||
if (!oldirqf && newirqf) {
|
||||
VM_CTR1(vrtc->vm, "RTC irq %d asserted", RTC_IRQ);
|
||||
vatpic_pulse_irq(vrtc->vm, RTC_IRQ);
|
||||
vioapic_pulse_irq(vrtc->vm, RTC_IRQ);
|
||||
} else if (oldirqf && !newirqf) {
|
||||
VM_CTR1(vrtc->vm, "RTC irq %d deasserted", RTC_IRQ);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
vrtc_set_reg_b(struct vrtc *vrtc, uint8_t newval)
|
||||
{
|
||||
struct rtcdev *rtc;
|
||||
sbintime_t oldfreq, newfreq;
|
||||
time_t curtime, rtctime;
|
||||
int error;
|
||||
uint8_t oldval, changed;
|
||||
|
||||
KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__));
|
||||
|
||||
rtc = &vrtc->rtcdev;
|
||||
oldval = rtc->reg_b;
|
||||
oldfreq = vrtc_freq(vrtc);
|
||||
|
||||
rtc->reg_b = newval;
|
||||
changed = oldval ^ newval;
|
||||
if (changed) {
|
||||
VM_CTR2(vrtc->vm, "RTC reg_b changed from %#x to %#x",
|
||||
oldval, newval);
|
||||
}
|
||||
|
||||
if (changed & RTCSB_HALT) {
|
||||
if ((newval & RTCSB_HALT) == 0) {
|
||||
rtctime = rtc_to_secs(vrtc);
|
||||
if (rtctime == VRTC_BROKEN_TIME) {
|
||||
/*
|
||||
* Stop updating the RTC if the date/time
|
||||
* programmed by the guest is not correct.
|
||||
*/
|
||||
VM_CTR0(vrtc->vm, "Invalid RTC date/time "
|
||||
"programming detected");
|
||||
|
||||
if (rtc_flag_broken_time)
|
||||
return (-1);
|
||||
}
|
||||
} else {
|
||||
curtime = vrtc_curtime(vrtc);
|
||||
KASSERT(curtime == vrtc->base_rtctime, ("%s: mismatch "
|
||||
"between vrtc basetime (%#lx) and curtime (%#lx)",
|
||||
__func__, vrtc->base_rtctime, curtime));
|
||||
|
||||
/*
|
||||
* Force a refresh of the RTC date/time fields so
|
||||
* they reflect the time right before the guest set
|
||||
* the HALT bit.
|
||||
*/
|
||||
secs_to_rtc(curtime, vrtc, 1);
|
||||
|
||||
/*
|
||||
* Updates are halted so mark 'base_rtctime' to denote
|
||||
* that the RTC date/time is in flux.
|
||||
*/
|
||||
rtctime = VRTC_BROKEN_TIME;
|
||||
rtc->reg_b &= ~RTCSB_UINTR;
|
||||
}
|
||||
error = vrtc_time_update(vrtc, rtctime);
|
||||
KASSERT(error == 0, ("vrtc_time_update error %d", error));
|
||||
}
|
||||
|
||||
/*
|
||||
* Side effect of changes to the interrupt enable bits.
|
||||
*/
|
||||
if (changed & RTCSB_ALL_INTRS)
|
||||
vrtc_set_reg_c(vrtc, vrtc->rtcdev.reg_c);
|
||||
|
||||
/*
|
||||
* Change the callout frequency if it has changed.
|
||||
*/
|
||||
newfreq = vrtc_freq(vrtc);
|
||||
if (newfreq != oldfreq)
|
||||
vrtc_callout_reset(vrtc, newfreq);
|
||||
else
|
||||
vrtc_callout_check(vrtc, newfreq);
|
||||
|
||||
/*
|
||||
* The side effect of bits that control the RTC date/time format
|
||||
* is handled lazily when those fields are actually read.
|
||||
*/
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
vrtc_set_reg_a(struct vrtc *vrtc, uint8_t newval)
|
||||
{
|
||||
sbintime_t oldfreq, newfreq;
|
||||
uint8_t oldval, changed;
|
||||
|
||||
KASSERT(VRTC_LOCKED(vrtc), ("%s: vrtc not locked", __func__));
|
||||
|
||||
newval &= ~RTCSA_TUP;
|
||||
oldval = vrtc->rtcdev.reg_a;
|
||||
oldfreq = vrtc_freq(vrtc);
|
||||
|
||||
if (divider_enabled(oldval) && !divider_enabled(newval)) {
|
||||
VM_CTR2(vrtc->vm, "RTC divider held in reset at %#lx/%#lx",
|
||||
vrtc->base_rtctime, vrtc->base_uptime);
|
||||
} else if (!divider_enabled(oldval) && divider_enabled(newval)) {
|
||||
/*
|
||||
* If the dividers are coming out of reset then update
|
||||
* 'base_uptime' before this happens. This is done to
|
||||
* maintain the illusion that the RTC date/time was frozen
|
||||
* while the dividers were disabled.
|
||||
*/
|
||||
vrtc->base_uptime = sbinuptime();
|
||||
VM_CTR2(vrtc->vm, "RTC divider out of reset at %#lx/%#lx",
|
||||
vrtc->base_rtctime, vrtc->base_uptime);
|
||||
} else {
|
||||
/* NOTHING */
|
||||
}
|
||||
|
||||
vrtc->rtcdev.reg_a = newval;
|
||||
changed = oldval ^ newval;
|
||||
if (changed) {
|
||||
VM_CTR2(vrtc->vm, "RTC reg_a changed from %#x to %#x",
|
||||
oldval, newval);
|
||||
}
|
||||
|
||||
/*
|
||||
* Side effect of changes to rate select and divider enable bits.
|
||||
*/
|
||||
newfreq = vrtc_freq(vrtc);
|
||||
if (newfreq != oldfreq)
|
||||
vrtc_callout_reset(vrtc, newfreq);
|
||||
else
|
||||
vrtc_callout_check(vrtc, newfreq);
|
||||
}
|
||||
|
||||
int
|
||||
vrtc_set_time(struct vm *vm, time_t secs)
|
||||
{
|
||||
struct vrtc *vrtc;
|
||||
int error;
|
||||
|
||||
vrtc = vm_rtc(vm);
|
||||
VRTC_LOCK(vrtc);
|
||||
error = vrtc_time_update(vrtc, secs);
|
||||
VRTC_UNLOCK(vrtc);
|
||||
|
||||
if (error) {
|
||||
VM_CTR2(vrtc->vm, "Error %d setting RTC time to %#lx", error,
|
||||
secs);
|
||||
} else {
|
||||
VM_CTR1(vrtc->vm, "RTC time set to %#lx", secs);
|
||||
}
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
time_t
|
||||
vrtc_get_time(struct vm *vm)
|
||||
{
|
||||
struct vrtc *vrtc;
|
||||
time_t t;
|
||||
|
||||
vrtc = vm_rtc(vm);
|
||||
VRTC_LOCK(vrtc);
|
||||
t = vrtc_curtime(vrtc);
|
||||
VRTC_UNLOCK(vrtc);
|
||||
|
||||
return (t);
|
||||
}
|
||||
|
||||
int
|
||||
vrtc_nvram_write(struct vm *vm, int offset, uint8_t value)
|
||||
{
|
||||
struct vrtc *vrtc;
|
||||
uint8_t *ptr;
|
||||
|
||||
vrtc = vm_rtc(vm);
|
||||
|
||||
/*
|
||||
* Don't allow writes to RTC control registers or the date/time fields.
|
||||
*/
|
||||
if (offset < offsetof(struct rtcdev, nvram[0]) ||
|
||||
offset >= sizeof(struct rtcdev)) {
|
||||
VM_CTR1(vrtc->vm, "RTC nvram write to invalid offset %d",
|
||||
offset);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
VRTC_LOCK(vrtc);
|
||||
ptr = (uint8_t *)(&vrtc->rtcdev);
|
||||
ptr[offset] = value;
|
||||
VM_CTR2(vrtc->vm, "RTC nvram write %#x to offset %#x", value, offset);
|
||||
VRTC_UNLOCK(vrtc);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
vrtc_nvram_read(struct vm *vm, int offset, uint8_t *retval)
|
||||
{
|
||||
struct vrtc *vrtc;
|
||||
time_t curtime;
|
||||
uint8_t *ptr;
|
||||
|
||||
/*
|
||||
* Allow all offsets in the RTC to be read.
|
||||
*/
|
||||
if (offset < 0 || offset >= sizeof(struct rtcdev))
|
||||
return (EINVAL);
|
||||
|
||||
vrtc = vm_rtc(vm);
|
||||
VRTC_LOCK(vrtc);
|
||||
|
||||
/*
|
||||
* Update RTC date/time fields if necessary.
|
||||
*/
|
||||
if (offset < 10) {
|
||||
curtime = vrtc_curtime(vrtc);
|
||||
secs_to_rtc(curtime, vrtc, 0);
|
||||
}
|
||||
|
||||
ptr = (uint8_t *)(&vrtc->rtcdev);
|
||||
*retval = ptr[offset];
|
||||
|
||||
VRTC_UNLOCK(vrtc);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
vrtc_addr_handler(struct vm *vm, int vcpuid, bool in, int port, int bytes,
|
||||
uint32_t *val)
|
||||
{
|
||||
struct vrtc *vrtc;
|
||||
|
||||
vrtc = vm_rtc(vm);
|
||||
|
||||
if (bytes != 1)
|
||||
return (-1);
|
||||
|
||||
if (in) {
|
||||
*val = 0xff;
|
||||
return (0);
|
||||
}
|
||||
|
||||
VRTC_LOCK(vrtc);
|
||||
vrtc->addr = *val & 0x7f;
|
||||
VRTC_UNLOCK(vrtc);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
vrtc_data_handler(struct vm *vm, int vcpuid, bool in, int port, int bytes,
|
||||
uint32_t *val)
|
||||
{
|
||||
struct vrtc *vrtc;
|
||||
struct rtcdev *rtc;
|
||||
time_t curtime;
|
||||
int error, offset;
|
||||
|
||||
vrtc = vm_rtc(vm);
|
||||
rtc = &vrtc->rtcdev;
|
||||
|
||||
if (bytes != 1)
|
||||
return (-1);
|
||||
|
||||
VRTC_LOCK(vrtc);
|
||||
offset = vrtc->addr;
|
||||
if (offset >= sizeof(struct rtcdev)) {
|
||||
VRTC_UNLOCK(vrtc);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
error = 0;
|
||||
curtime = vrtc_curtime(vrtc);
|
||||
vrtc_time_update(vrtc, curtime);
|
||||
|
||||
if (in) {
|
||||
/*
|
||||
* Update RTC date/time fields if necessary.
|
||||
*/
|
||||
if (offset < 10)
|
||||
secs_to_rtc(curtime, vrtc, 0);
|
||||
|
||||
if (offset == 12) {
|
||||
/*
|
||||
* XXX
|
||||
* reg_c interrupt flags are updated only if the
|
||||
* corresponding interrupt enable bit in reg_b is set.
|
||||
*/
|
||||
*val = vrtc->rtcdev.reg_c;
|
||||
vrtc_set_reg_c(vrtc, 0);
|
||||
} else {
|
||||
*val = *((uint8_t *)rtc + offset);
|
||||
}
|
||||
VCPU_CTR2(vm, vcpuid, "Read value %#x from RTC offset %#x",
|
||||
*val, offset);
|
||||
} else {
|
||||
switch (offset) {
|
||||
case 10:
|
||||
VCPU_CTR1(vm, vcpuid, "RTC reg_a set to %#x", *val);
|
||||
vrtc_set_reg_a(vrtc, *val);
|
||||
break;
|
||||
case 11:
|
||||
VCPU_CTR1(vm, vcpuid, "RTC reg_b set to %#x", *val);
|
||||
error = vrtc_set_reg_b(vrtc, *val);
|
||||
break;
|
||||
case 12:
|
||||
VCPU_CTR1(vm, vcpuid, "RTC reg_c set to %#x (ignored)",
|
||||
*val);
|
||||
break;
|
||||
case 13:
|
||||
VCPU_CTR1(vm, vcpuid, "RTC reg_d set to %#x (ignored)",
|
||||
*val);
|
||||
break;
|
||||
case 0:
|
||||
/*
|
||||
* High order bit of 'seconds' is readonly.
|
||||
*/
|
||||
*val &= 0x7f;
|
||||
/* FALLTHRU */
|
||||
default:
|
||||
VCPU_CTR2(vm, vcpuid, "RTC offset %#x set to %#x",
|
||||
offset, *val);
|
||||
*((uint8_t *)rtc + offset) = *val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
VRTC_UNLOCK(vrtc);
|
||||
return (error);
|
||||
}
|
||||
|
||||
void
|
||||
vrtc_reset(struct vrtc *vrtc)
|
||||
{
|
||||
struct rtcdev *rtc;
|
||||
|
||||
VRTC_LOCK(vrtc);
|
||||
|
||||
rtc = &vrtc->rtcdev;
|
||||
vrtc_set_reg_b(vrtc, rtc->reg_b & ~(RTCSB_ALL_INTRS | RTCSB_SQWE));
|
||||
vrtc_set_reg_c(vrtc, 0);
|
||||
KASSERT(!callout_active(&vrtc->callout), ("rtc callout still active"));
|
||||
|
||||
VRTC_UNLOCK(vrtc);
|
||||
}
|
||||
|
||||
struct vrtc *
|
||||
vrtc_init(struct vm *vm)
|
||||
{
|
||||
struct vrtc *vrtc;
|
||||
struct rtcdev *rtc;
|
||||
time_t curtime;
|
||||
|
||||
vrtc = malloc(sizeof(struct vrtc), M_VRTC, M_WAITOK | M_ZERO);
|
||||
vrtc->vm = vm;
|
||||
mtx_init(&vrtc->mtx, "vrtc lock", NULL, MTX_DEF);
|
||||
callout_init(&vrtc->callout, 1);
|
||||
|
||||
/* Allow dividers to keep time but disable everything else */
|
||||
rtc = &vrtc->rtcdev;
|
||||
rtc->reg_a = 0x20;
|
||||
rtc->reg_b = RTCSB_24HR;
|
||||
rtc->reg_c = 0;
|
||||
rtc->reg_d = RTCSD_PWR;
|
||||
|
||||
/* Reset the index register to a safe value. */
|
||||
vrtc->addr = RTC_STATUSD;
|
||||
|
||||
/*
|
||||
* Initialize RTC time to 00:00:00 Jan 1, 1970.
|
||||
*/
|
||||
curtime = 0;
|
||||
|
||||
VRTC_LOCK(vrtc);
|
||||
vrtc->base_rtctime = VRTC_BROKEN_TIME;
|
||||
vrtc_time_update(vrtc, curtime);
|
||||
secs_to_rtc(curtime, vrtc, 0);
|
||||
VRTC_UNLOCK(vrtc);
|
||||
|
||||
return (vrtc);
|
||||
}
|
||||
|
||||
void
|
||||
vrtc_cleanup(struct vrtc *vrtc)
|
||||
{
|
||||
|
||||
callout_drain(&vrtc->callout);
|
||||
free(vrtc, M_VRTC);
|
||||
}
|
50
sys/amd64/vmm/io/vrtc.h
Normal file
50
sys/amd64/vmm/io/vrtc.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*-
|
||||
* Copyright (c) 2014 Neel Natu (neel@freebsd.org)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice unmodified, this list of conditions, and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _VRTC_H_
|
||||
#define _VRTC_H_
|
||||
|
||||
#include <isa/isareg.h>
|
||||
|
||||
struct vrtc;
|
||||
|
||||
struct vrtc *vrtc_init(struct vm *vm);
|
||||
void vrtc_cleanup(struct vrtc *vrtc);
|
||||
void vrtc_reset(struct vrtc *vrtc);
|
||||
|
||||
time_t vrtc_get_time(struct vm *vm);
|
||||
int vrtc_set_time(struct vm *vm, time_t secs);
|
||||
int vrtc_nvram_write(struct vm *vm, int offset, uint8_t value);
|
||||
int vrtc_nvram_read(struct vm *vm, int offset, uint8_t *retval);
|
||||
|
||||
int vrtc_addr_handler(struct vm *vm, int vcpuid, bool in, int port, int bytes,
|
||||
uint32_t *val);
|
||||
int vrtc_data_handler(struct vm *vm, int vcpuid, bool in, int port, int bytes,
|
||||
uint32_t *val);
|
||||
|
||||
#endif
|
@ -75,6 +75,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include "vioapic.h"
|
||||
#include "vlapic.h"
|
||||
#include "vpmtmr.h"
|
||||
#include "vrtc.h"
|
||||
#include "vmm_ipi.h"
|
||||
#include "vmm_stat.h"
|
||||
#include "vmm_lapic.h"
|
||||
@ -136,6 +137,7 @@ struct vm {
|
||||
struct vatpic *vatpic; /* (i) virtual atpic */
|
||||
struct vatpit *vatpit; /* (i) virtual atpit */
|
||||
struct vpmtmr *vpmtmr; /* (i) virtual ACPI PM timer */
|
||||
struct vrtc *vrtc; /* (o) virtual RTC */
|
||||
volatile cpuset_t active_cpus; /* (i) active vcpus */
|
||||
int suspend; /* (i) stop VM execution */
|
||||
volatile cpuset_t suspended_cpus; /* (i) suspended vcpus */
|
||||
@ -375,6 +377,8 @@ vm_init(struct vm *vm, bool create)
|
||||
vm->vatpic = vatpic_init(vm);
|
||||
vm->vatpit = vatpit_init(vm);
|
||||
vm->vpmtmr = vpmtmr_init(vm);
|
||||
if (create)
|
||||
vm->vrtc = vrtc_init(vm);
|
||||
|
||||
CPU_ZERO(&vm->active_cpus);
|
||||
|
||||
@ -437,6 +441,10 @@ vm_cleanup(struct vm *vm, bool destroy)
|
||||
if (vm->iommu != NULL)
|
||||
iommu_destroy_domain(vm->iommu);
|
||||
|
||||
if (destroy)
|
||||
vrtc_cleanup(vm->vrtc);
|
||||
else
|
||||
vrtc_reset(vm->vrtc);
|
||||
vpmtmr_cleanup(vm->vpmtmr);
|
||||
vatpit_cleanup(vm->vatpit);
|
||||
vhpet_cleanup(vm->vhpet);
|
||||
@ -2222,6 +2230,13 @@ vm_pmtmr(struct vm *vm)
|
||||
return (vm->vpmtmr);
|
||||
}
|
||||
|
||||
struct vrtc *
|
||||
vm_rtc(struct vm *vm)
|
||||
{
|
||||
|
||||
return (vm->vrtc);
|
||||
}
|
||||
|
||||
enum vm_reg_name
|
||||
vm_segment_name(int seg)
|
||||
{
|
||||
|
@ -58,6 +58,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include "io/vatpic.h"
|
||||
#include "io/vioapic.h"
|
||||
#include "io/vhpet.h"
|
||||
#include "io/vrtc.h"
|
||||
|
||||
struct vmmdev_softc {
|
||||
struct vm *vm; /* vm instance cookie */
|
||||
@ -174,6 +175,8 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
|
||||
struct vm_activate_cpu *vac;
|
||||
struct vm_cpuset *vm_cpuset;
|
||||
struct vm_intinfo *vmii;
|
||||
struct vm_rtc_time *rtctime;
|
||||
struct vm_rtc_data *rtcdata;
|
||||
|
||||
sc = vmmdev_lookup2(cdev);
|
||||
if (sc == NULL)
|
||||
@ -482,6 +485,25 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
|
||||
error = vm_get_intinfo(sc->vm, vmii->vcpuid, &vmii->info1,
|
||||
&vmii->info2);
|
||||
break;
|
||||
case VM_RTC_WRITE:
|
||||
rtcdata = (struct vm_rtc_data *)data;
|
||||
error = vrtc_nvram_write(sc->vm, rtcdata->offset,
|
||||
rtcdata->value);
|
||||
break;
|
||||
case VM_RTC_READ:
|
||||
rtcdata = (struct vm_rtc_data *)data;
|
||||
error = vrtc_nvram_read(sc->vm, rtcdata->offset,
|
||||
&rtcdata->value);
|
||||
break;
|
||||
case VM_RTC_SETTIME:
|
||||
rtctime = (struct vm_rtc_time *)data;
|
||||
error = vrtc_set_time(sc->vm, rtctime->secs);
|
||||
break;
|
||||
case VM_RTC_GETTIME:
|
||||
error = 0;
|
||||
rtctime = (struct vm_rtc_time *)data;
|
||||
rtctime->secs = vrtc_get_time(sc->vm);
|
||||
break;
|
||||
default:
|
||||
error = ENOTTY;
|
||||
break;
|
||||
|
@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include "vatpic.h"
|
||||
#include "vatpit.h"
|
||||
#include "vpmtmr.h"
|
||||
#include "vrtc.h"
|
||||
#include "vmm_ioport.h"
|
||||
#include "vmm_ktr.h"
|
||||
|
||||
@ -60,6 +61,8 @@ ioport_handler_func_t ioport_handler[MAX_IOPORTS] = {
|
||||
[IO_ELCR1] = vatpic_elc_handler,
|
||||
[IO_ELCR2] = vatpic_elc_handler,
|
||||
[IO_PMTMR] = vpmtmr_handler,
|
||||
[IO_RTC] = vrtc_addr_handler,
|
||||
[IO_RTC + 1] = vrtc_data_handler,
|
||||
};
|
||||
|
||||
#ifdef KTR
|
||||
|
@ -106,7 +106,7 @@ ASENTRY_NP(dcache_inv_pou_all)
|
||||
bx lr
|
||||
#else
|
||||
mrc CP15_CLIDR(r0)
|
||||
ands r0, r0, #0x07000000
|
||||
ands r0, r0, #0x38000000
|
||||
mov r0, r0, lsr #26 /* Get LoUU (naturally aligned) */
|
||||
beq 4f
|
||||
|
||||
|
@ -84,11 +84,9 @@ ASENTRY_NP(_start)
|
||||
*/
|
||||
mrc CP15_SCTLR(r7)
|
||||
tst r7, #CPU_CONTROL_DC_ENABLE
|
||||
beq 1f
|
||||
bic r7, #CPU_CONTROL_DC_ENABLE
|
||||
mcr CP15_SCTLR(r7)
|
||||
ISB
|
||||
bl dcache_wbinv_poc_all
|
||||
blne dcache_wbinv_poc_all
|
||||
|
||||
/* ! Do not write to memory between wbinv and disabling cache ! */
|
||||
|
||||
/*
|
||||
* Now there are no dirty lines, but there may still be lines marked
|
||||
@ -96,6 +94,7 @@ ASENTRY_NP(_start)
|
||||
* before setting up new page tables and re-enabling the mmu.
|
||||
*/
|
||||
1:
|
||||
bic r7, #CPU_CONTROL_DC_ENABLE
|
||||
bic r7, #CPU_CONTROL_MMU_ENABLE
|
||||
bic r7, #CPU_CONTROL_IC_ENABLE
|
||||
bic r7, #CPU_CONTROL_UNAL_ENABLE
|
||||
@ -340,7 +339,7 @@ END(reinit_mmu)
|
||||
*
|
||||
* Addresses must be 1MiB aligned
|
||||
*/
|
||||
ASENTRY_NP(build_pagetables)
|
||||
build_pagetables:
|
||||
/* Set the required page attributed */
|
||||
#if defined(ARM_NEW_PMAP)
|
||||
ldr r4, =PTE1_V|PTE1_A|PTE1_AP_KRW|TEX1_CLASS_0
|
||||
@ -521,6 +520,7 @@ ENTRY_NP(sigcode)
|
||||
|
||||
/* Branch back to retry SYS_sigreturn */
|
||||
b . - 16
|
||||
END(sigcode)
|
||||
|
||||
.word SYS_sigreturn
|
||||
.word SYS_exit
|
||||
@ -533,5 +533,5 @@ ENTRY_NP(sigcode)
|
||||
.global szsigcode
|
||||
szsigcode:
|
||||
.long esigcode-sigcode
|
||||
END(sigcode)
|
||||
|
||||
/* End of locore.S */
|
||||
|
@ -2776,7 +2776,8 @@ vdev_get_stats(vdev_t *vd, vdev_stat_t *vs)
|
||||
vs->vs_rsize = vdev_get_min_asize(vd);
|
||||
if (vd->vdev_ops->vdev_op_leaf)
|
||||
vs->vs_rsize += VDEV_LABEL_START_SIZE + VDEV_LABEL_END_SIZE;
|
||||
vs->vs_esize = vd->vdev_max_asize - vd->vdev_asize;
|
||||
if (vd->vdev_max_asize != 0)
|
||||
vs->vs_esize = vd->vdev_max_asize - vd->vdev_asize;
|
||||
vs->vs_configured_ashift = vd->vdev_top != NULL
|
||||
? vd->vdev_top->vdev_ashift : vd->vdev_ashift;
|
||||
vs->vs_logical_ashift = vd->vdev_logical_ashift;
|
||||
|
@ -71,7 +71,7 @@ __FBSDID("$FreeBSD$");
|
||||
*/
|
||||
#define SDHC_PCI_MODE_KEY 0xf9
|
||||
#define SDHC_PCI_MODE 0x150
|
||||
#define SDHC_PCI_MODE_SD20 0x10
|
||||
#define SDHC_PCI_MODE_SD20 0x10
|
||||
#define SDHC_PCI_BASE_FREQ_KEY 0xfc
|
||||
#define SDHC_PCI_BASE_FREQ 0xe1
|
||||
|
||||
@ -83,8 +83,9 @@ static const struct sdhci_device {
|
||||
} sdhci_devices[] = {
|
||||
{ 0x08221180, 0xffff, "RICOH R5C822 SD",
|
||||
SDHCI_QUIRK_FORCE_DMA },
|
||||
{ 0xe8221180, 0xffff, "RICOH SD",
|
||||
SDHCI_QUIRK_FORCE_DMA },
|
||||
{ 0xe8221180, 0xffff, "RICOH R5CE822 SD",
|
||||
SDHCI_QUIRK_FORCE_DMA |
|
||||
SDHCI_QUIRK_LOWER_FREQUENCY },
|
||||
{ 0xe8231180, 0xffff, "RICOH R5CE823 SD",
|
||||
SDHCI_QUIRK_LOWER_FREQUENCY },
|
||||
{ 0x8034104c, 0xffff, "TI XX21/XX11 SD",
|
||||
@ -109,7 +110,6 @@ static const struct sdhci_device {
|
||||
};
|
||||
|
||||
struct sdhci_pci_softc {
|
||||
device_t dev; /* Controller device */
|
||||
u_int quirks; /* Chip specific quirks */
|
||||
struct resource *irq_res; /* IRQ resource */
|
||||
void *intrhand; /* Interrupt handle */
|
||||
@ -117,6 +117,8 @@ struct sdhci_pci_softc {
|
||||
int num_slots; /* Number of slots on this controller */
|
||||
struct sdhci_slot slots[6];
|
||||
struct resource *mem_res[6]; /* Memory resource */
|
||||
uint8_t cfg_freq; /* Saved mode */
|
||||
uint8_t cfg_mode; /* Saved frequency */
|
||||
};
|
||||
|
||||
static int sdhci_enable_msi = 1;
|
||||
@ -206,21 +208,43 @@ static void sdhci_pci_intr(void *arg);
|
||||
static void
|
||||
sdhci_lower_frequency(device_t dev)
|
||||
{
|
||||
struct sdhci_pci_softc *sc = device_get_softc(dev);
|
||||
|
||||
/* Enable SD2.0 mode. */
|
||||
/*
|
||||
* Enable SD2.0 mode.
|
||||
* NB: for RICOH R5CE823, this changes the PCI device ID to 0xe822.
|
||||
*/
|
||||
pci_write_config(dev, SDHC_PCI_MODE_KEY, 0xfc, 1);
|
||||
sc->cfg_mode = pci_read_config(dev, SDHC_PCI_MODE, 1);
|
||||
pci_write_config(dev, SDHC_PCI_MODE, SDHC_PCI_MODE_SD20, 1);
|
||||
pci_write_config(dev, SDHC_PCI_MODE_KEY, 0x00, 1);
|
||||
|
||||
/*
|
||||
* Some SD/MMC cards don't work with the default base
|
||||
* clock frequency of 200MHz. Lower it to 50Hz.
|
||||
* clock frequency of 200 MHz. Lower it to 50 MHz.
|
||||
*/
|
||||
pci_write_config(dev, SDHC_PCI_BASE_FREQ_KEY, 0x01, 1);
|
||||
sc->cfg_freq = pci_read_config(dev, SDHC_PCI_BASE_FREQ, 1);
|
||||
pci_write_config(dev, SDHC_PCI_BASE_FREQ, 50, 1);
|
||||
pci_write_config(dev, SDHC_PCI_BASE_FREQ_KEY, 0x00, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
sdhci_restore_frequency(device_t dev)
|
||||
{
|
||||
struct sdhci_pci_softc *sc = device_get_softc(dev);
|
||||
|
||||
/* Restore mode. */
|
||||
pci_write_config(dev, SDHC_PCI_MODE_KEY, 0xfc, 1);
|
||||
pci_write_config(dev, SDHC_PCI_MODE, sc->cfg_mode, 1);
|
||||
pci_write_config(dev, SDHC_PCI_MODE_KEY, 0x00, 1);
|
||||
|
||||
/* Restore frequency. */
|
||||
pci_write_config(dev, SDHC_PCI_BASE_FREQ_KEY, 0x01, 1);
|
||||
pci_write_config(dev, SDHC_PCI_BASE_FREQ, sc->cfg_freq, 1);
|
||||
pci_write_config(dev, SDHC_PCI_BASE_FREQ_KEY, 0x00, 1);
|
||||
}
|
||||
|
||||
static int
|
||||
sdhci_pci_probe(device_t dev)
|
||||
{
|
||||
@ -262,7 +286,6 @@ sdhci_pci_attach(device_t dev)
|
||||
uint16_t subvendor;
|
||||
int bar, err, rid, slots, i;
|
||||
|
||||
sc->dev = dev;
|
||||
model = (uint32_t)pci_get_device(dev) << 16;
|
||||
model |= (uint32_t)pci_get_vendor(dev) & 0x0000ffff;
|
||||
subvendor = pci_get_subvendor(dev);
|
||||
@ -352,6 +375,18 @@ sdhci_pci_detach(device_t dev)
|
||||
bus_release_resource(dev, SYS_RES_MEMORY,
|
||||
rman_get_rid(sc->mem_res[i]), sc->mem_res[i]);
|
||||
}
|
||||
if (sc->quirks & SDHCI_QUIRK_LOWER_FREQUENCY)
|
||||
sdhci_restore_frequency(dev);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
sdhci_pci_shutdown(device_t dev)
|
||||
{
|
||||
struct sdhci_pci_softc *sc = device_get_softc(dev);
|
||||
|
||||
if (sc->quirks & SDHCI_QUIRK_LOWER_FREQUENCY)
|
||||
sdhci_restore_frequency(dev);
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -397,6 +432,7 @@ static device_method_t sdhci_methods[] = {
|
||||
DEVMETHOD(device_probe, sdhci_pci_probe),
|
||||
DEVMETHOD(device_attach, sdhci_pci_attach),
|
||||
DEVMETHOD(device_detach, sdhci_pci_detach),
|
||||
DEVMETHOD(device_shutdown, sdhci_pci_shutdown),
|
||||
DEVMETHOD(device_suspend, sdhci_pci_suspend),
|
||||
DEVMETHOD(device_resume, sdhci_pci_resume),
|
||||
|
||||
|
@ -38,7 +38,7 @@
|
||||
#include <sys/systm.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/rwlock.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/proc.h>
|
||||
@ -57,7 +57,7 @@
|
||||
#define NULL_NHASH(vp) (&null_node_hashtbl[vfs_hash_index(vp) & null_hash_mask])
|
||||
|
||||
static LIST_HEAD(null_node_hashhead, null_node) *null_node_hashtbl;
|
||||
static struct mtx null_hashmtx;
|
||||
static struct rwlock null_hash_lock;
|
||||
static u_long null_hash_mask;
|
||||
|
||||
static MALLOC_DEFINE(M_NULLFSHASH, "nullfs_hash", "NULLFS hash table");
|
||||
@ -75,7 +75,7 @@ nullfs_init(vfsp)
|
||||
|
||||
null_node_hashtbl = hashinit(desiredvnodes, M_NULLFSHASH,
|
||||
&null_hash_mask);
|
||||
mtx_init(&null_hashmtx, "nullhs", NULL, MTX_DEF);
|
||||
rw_init(&null_hash_lock, "nullhs");
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -84,7 +84,7 @@ nullfs_uninit(vfsp)
|
||||
struct vfsconf *vfsp;
|
||||
{
|
||||
|
||||
mtx_destroy(&null_hashmtx);
|
||||
rw_destroy(&null_hash_lock);
|
||||
hashdestroy(null_node_hashtbl, M_NULLFSHASH, null_hash_mask);
|
||||
return (0);
|
||||
}
|
||||
@ -111,7 +111,7 @@ null_hashget(mp, lowervp)
|
||||
* reference count (but NOT the lower vnode's VREF counter).
|
||||
*/
|
||||
hd = NULL_NHASH(lowervp);
|
||||
mtx_lock(&null_hashmtx);
|
||||
rw_rlock(&null_hash_lock);
|
||||
LIST_FOREACH(a, hd, null_hash) {
|
||||
if (a->null_lowervp == lowervp && NULLTOV(a)->v_mount == mp) {
|
||||
/*
|
||||
@ -122,11 +122,11 @@ null_hashget(mp, lowervp)
|
||||
*/
|
||||
vp = NULLTOV(a);
|
||||
vref(vp);
|
||||
mtx_unlock(&null_hashmtx);
|
||||
rw_runlock(&null_hash_lock);
|
||||
return (vp);
|
||||
}
|
||||
}
|
||||
mtx_unlock(&null_hashmtx);
|
||||
rw_runlock(&null_hash_lock);
|
||||
return (NULLVP);
|
||||
}
|
||||
|
||||
@ -144,7 +144,7 @@ null_hashins(mp, xp)
|
||||
struct vnode *ovp;
|
||||
|
||||
hd = NULL_NHASH(xp->null_lowervp);
|
||||
mtx_lock(&null_hashmtx);
|
||||
rw_wlock(&null_hash_lock);
|
||||
LIST_FOREACH(oxp, hd, null_hash) {
|
||||
if (oxp->null_lowervp == xp->null_lowervp &&
|
||||
NULLTOV(oxp)->v_mount == mp) {
|
||||
@ -154,12 +154,12 @@ null_hashins(mp, xp)
|
||||
*/
|
||||
ovp = NULLTOV(oxp);
|
||||
vref(ovp);
|
||||
mtx_unlock(&null_hashmtx);
|
||||
rw_wunlock(&null_hash_lock);
|
||||
return (ovp);
|
||||
}
|
||||
}
|
||||
LIST_INSERT_HEAD(hd, xp, null_hash);
|
||||
mtx_unlock(&null_hashmtx);
|
||||
rw_wunlock(&null_hash_lock);
|
||||
return (NULLVP);
|
||||
}
|
||||
|
||||
@ -277,9 +277,9 @@ null_hashrem(xp)
|
||||
struct null_node *xp;
|
||||
{
|
||||
|
||||
mtx_lock(&null_hashmtx);
|
||||
rw_wlock(&null_hash_lock);
|
||||
LIST_REMOVE(xp, null_hash);
|
||||
mtx_unlock(&null_hashmtx);
|
||||
rw_wunlock(&null_hash_lock);
|
||||
}
|
||||
|
||||
#ifdef DIAGNOSTIC
|
||||
|
@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/rwlock.h>
|
||||
#include <sys/vnode.h>
|
||||
|
||||
static MALLOC_DEFINE(M_VFS_HASH, "vfs_hash", "VFS hash table");
|
||||
@ -40,14 +41,14 @@ static MALLOC_DEFINE(M_VFS_HASH, "vfs_hash", "VFS hash table");
|
||||
static LIST_HEAD(vfs_hash_head, vnode) *vfs_hash_tbl;
|
||||
static LIST_HEAD(,vnode) vfs_hash_side;
|
||||
static u_long vfs_hash_mask;
|
||||
static struct mtx vfs_hash_mtx;
|
||||
static struct rwlock vfs_hash_lock;
|
||||
|
||||
static void
|
||||
vfs_hashinit(void *dummy __unused)
|
||||
{
|
||||
|
||||
vfs_hash_tbl = hashinit(desiredvnodes, M_VFS_HASH, &vfs_hash_mask);
|
||||
mtx_init(&vfs_hash_mtx, "vfs hash", NULL, MTX_DEF);
|
||||
rw_init(&vfs_hash_lock, "vfs hash");
|
||||
LIST_INIT(&vfs_hash_side);
|
||||
}
|
||||
|
||||
@ -75,7 +76,7 @@ vfs_hash_get(const struct mount *mp, u_int hash, int flags, struct thread *td, s
|
||||
int error;
|
||||
|
||||
while (1) {
|
||||
mtx_lock(&vfs_hash_mtx);
|
||||
rw_rlock(&vfs_hash_lock);
|
||||
LIST_FOREACH(vp, vfs_hash_bucket(mp, hash), v_hashlist) {
|
||||
if (vp->v_hash != hash)
|
||||
continue;
|
||||
@ -84,7 +85,7 @@ vfs_hash_get(const struct mount *mp, u_int hash, int flags, struct thread *td, s
|
||||
if (fn != NULL && fn(vp, arg))
|
||||
continue;
|
||||
VI_LOCK(vp);
|
||||
mtx_unlock(&vfs_hash_mtx);
|
||||
rw_runlock(&vfs_hash_lock);
|
||||
error = vget(vp, flags | LK_INTERLOCK, td);
|
||||
if (error == ENOENT && (flags & LK_NOWAIT) == 0)
|
||||
break;
|
||||
@ -94,7 +95,7 @@ vfs_hash_get(const struct mount *mp, u_int hash, int flags, struct thread *td, s
|
||||
return (0);
|
||||
}
|
||||
if (vp == NULL) {
|
||||
mtx_unlock(&vfs_hash_mtx);
|
||||
rw_runlock(&vfs_hash_lock);
|
||||
*vpp = NULL;
|
||||
return (0);
|
||||
}
|
||||
@ -105,9 +106,9 @@ void
|
||||
vfs_hash_remove(struct vnode *vp)
|
||||
{
|
||||
|
||||
mtx_lock(&vfs_hash_mtx);
|
||||
rw_wlock(&vfs_hash_lock);
|
||||
LIST_REMOVE(vp, v_hashlist);
|
||||
mtx_unlock(&vfs_hash_mtx);
|
||||
rw_wunlock(&vfs_hash_lock);
|
||||
}
|
||||
|
||||
int
|
||||
@ -118,7 +119,7 @@ vfs_hash_insert(struct vnode *vp, u_int hash, int flags, struct thread *td, stru
|
||||
|
||||
*vpp = NULL;
|
||||
while (1) {
|
||||
mtx_lock(&vfs_hash_mtx);
|
||||
rw_wlock(&vfs_hash_lock);
|
||||
LIST_FOREACH(vp2,
|
||||
vfs_hash_bucket(vp->v_mount, hash), v_hashlist) {
|
||||
if (vp2->v_hash != hash)
|
||||
@ -128,13 +129,13 @@ vfs_hash_insert(struct vnode *vp, u_int hash, int flags, struct thread *td, stru
|
||||
if (fn != NULL && fn(vp2, arg))
|
||||
continue;
|
||||
VI_LOCK(vp2);
|
||||
mtx_unlock(&vfs_hash_mtx);
|
||||
rw_wunlock(&vfs_hash_lock);
|
||||
error = vget(vp2, flags | LK_INTERLOCK, td);
|
||||
if (error == ENOENT && (flags & LK_NOWAIT) == 0)
|
||||
break;
|
||||
mtx_lock(&vfs_hash_mtx);
|
||||
rw_wlock(&vfs_hash_lock);
|
||||
LIST_INSERT_HEAD(&vfs_hash_side, vp, v_hashlist);
|
||||
mtx_unlock(&vfs_hash_mtx);
|
||||
rw_wunlock(&vfs_hash_lock);
|
||||
vput(vp);
|
||||
if (!error)
|
||||
*vpp = vp2;
|
||||
@ -146,7 +147,7 @@ vfs_hash_insert(struct vnode *vp, u_int hash, int flags, struct thread *td, stru
|
||||
}
|
||||
vp->v_hash = hash;
|
||||
LIST_INSERT_HEAD(vfs_hash_bucket(vp->v_mount, hash), vp, v_hashlist);
|
||||
mtx_unlock(&vfs_hash_mtx);
|
||||
rw_wunlock(&vfs_hash_lock);
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -154,9 +155,9 @@ void
|
||||
vfs_hash_rehash(struct vnode *vp, u_int hash)
|
||||
{
|
||||
|
||||
mtx_lock(&vfs_hash_mtx);
|
||||
rw_wlock(&vfs_hash_lock);
|
||||
LIST_REMOVE(vp, v_hashlist);
|
||||
LIST_INSERT_HEAD(vfs_hash_bucket(vp->v_mount, hash), vp, v_hashlist);
|
||||
vp->v_hash = hash;
|
||||
mtx_unlock(&vfs_hash_mtx);
|
||||
rw_wunlock(&vfs_hash_lock);
|
||||
}
|
||||
|
@ -160,13 +160,11 @@
|
||||
#define VM_FREEPOOL_DIRECT 1
|
||||
|
||||
/*
|
||||
* we support 2 free lists:
|
||||
*
|
||||
* - DEFAULT for direct mapped (KSEG0) pages.
|
||||
* Note: This usage of DEFAULT may be misleading because we use
|
||||
* DEFAULT for allocating direct mapped pages. The normal page
|
||||
* allocations use HIGHMEM if available, and then DEFAULT.
|
||||
* - HIGHMEM for other pages
|
||||
* Create up to two free lists on !__mips_n64: VM_FREELIST_DEFAULT is for
|
||||
* physical pages that are above the largest physical address that is
|
||||
* accessible through the direct map (KSEG0) and VM_FREELIST_LOWMEM is for
|
||||
* physical pages that are below that address. VM_LOWMEM_BOUNDARY is the
|
||||
* physical address for the end of the direct map (KSEG0).
|
||||
*/
|
||||
#ifdef __mips_n64
|
||||
#define VM_NFREELIST 1
|
||||
@ -174,10 +172,10 @@
|
||||
#define VM_FREELIST_DIRECT VM_FREELIST_DEFAULT
|
||||
#else
|
||||
#define VM_NFREELIST 2
|
||||
#define VM_FREELIST_DEFAULT 1
|
||||
#define VM_FREELIST_HIGHMEM 0
|
||||
#define VM_FREELIST_DIRECT VM_FREELIST_DEFAULT
|
||||
#define VM_HIGHMEM_ADDRESS ((vm_paddr_t)0x20000000)
|
||||
#define VM_FREELIST_DEFAULT 0
|
||||
#define VM_FREELIST_LOWMEM 1
|
||||
#define VM_FREELIST_DIRECT VM_FREELIST_LOWMEM
|
||||
#define VM_LOWMEM_BOUNDARY ((vm_paddr_t)0x20000000)
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -35,7 +35,8 @@ SRCS+= iommu.c \
|
||||
vhpet.c \
|
||||
vioapic.c \
|
||||
vlapic.c \
|
||||
vpmtmr.c
|
||||
vpmtmr.c \
|
||||
vrtc.c
|
||||
|
||||
# intel-specific files
|
||||
.PATH: ${.CURDIR}/../../amd64/vmm/intel
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*-
|
||||
* Copyright (C) 1992-2014 The FreeBSD Project. All rights reserved.
|
||||
* Copyright (C) 1992-2015 The FreeBSD Project. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@ -30,7 +30,7 @@
|
||||
|
||||
/* FreeBSD */
|
||||
#define COPYRIGHT_FreeBSD \
|
||||
"Copyright (c) 1992-2014 The FreeBSD Project.\n"
|
||||
"Copyright (c) 1992-2015 The FreeBSD Project.\n"
|
||||
|
||||
/* Foundation */
|
||||
#define TRADEMARK_Foundation \
|
||||
|
207
sys/vm/vm_phys.c
207
sys/vm/vm_phys.c
@ -101,7 +101,32 @@ MALLOC_DEFINE(M_FICT_PAGES, "vm_fictitious", "Fictitious VM pages");
|
||||
static struct vm_freelist
|
||||
vm_phys_free_queues[MAXMEMDOM][VM_NFREELIST][VM_NFREEPOOL][VM_NFREEORDER];
|
||||
|
||||
static int vm_nfreelists = VM_FREELIST_DEFAULT + 1;
|
||||
static int vm_nfreelists;
|
||||
|
||||
/*
|
||||
* Provides the mapping from VM_FREELIST_* to free list indices (flind).
|
||||
*/
|
||||
static int vm_freelist_to_flind[VM_NFREELIST];
|
||||
|
||||
CTASSERT(VM_FREELIST_DEFAULT == 0);
|
||||
|
||||
#ifdef VM_FREELIST_ISADMA
|
||||
#define VM_ISADMA_BOUNDARY 16777216
|
||||
#endif
|
||||
#ifdef VM_FREELIST_DMA32
|
||||
#define VM_DMA32_BOUNDARY ((vm_paddr_t)1 << 32)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Enforce the assumptions made by vm_phys_add_seg() and vm_phys_init() about
|
||||
* the ordering of the free list boundaries.
|
||||
*/
|
||||
#if defined(VM_ISADMA_BOUNDARY) && defined(VM_LOWMEM_BOUNDARY)
|
||||
CTASSERT(VM_ISADMA_BOUNDARY < VM_LOWMEM_BOUNDARY);
|
||||
#endif
|
||||
#if defined(VM_LOWMEM_BOUNDARY) && defined(VM_DMA32_BOUNDARY)
|
||||
CTASSERT(VM_LOWMEM_BOUNDARY < VM_DMA32_BOUNDARY);
|
||||
#endif
|
||||
|
||||
static int cnt_prezero;
|
||||
SYSCTL_INT(_vm_stats_misc, OID_AUTO, cnt_prezero, CTLFLAG_RD,
|
||||
@ -120,9 +145,8 @@ SYSCTL_INT(_vm, OID_AUTO, ndomains, CTLFLAG_RD,
|
||||
|
||||
static vm_page_t vm_phys_alloc_domain_pages(int domain, int flind, int pool,
|
||||
int order);
|
||||
static void _vm_phys_create_seg(vm_paddr_t start, vm_paddr_t end, int flind,
|
||||
int domain);
|
||||
static void vm_phys_create_seg(vm_paddr_t start, vm_paddr_t end, int flind);
|
||||
static void _vm_phys_create_seg(vm_paddr_t start, vm_paddr_t end, int domain);
|
||||
static void vm_phys_create_seg(vm_paddr_t start, vm_paddr_t end);
|
||||
static int vm_phys_paddr_to_segind(vm_paddr_t pa);
|
||||
static void vm_phys_split_pages(vm_page_t m, int oind, struct vm_freelist *fl,
|
||||
int order);
|
||||
@ -298,7 +322,7 @@ vm_freelist_rem(struct vm_freelist *fl, vm_page_t m, int order)
|
||||
* Create a physical memory segment.
|
||||
*/
|
||||
static void
|
||||
_vm_phys_create_seg(vm_paddr_t start, vm_paddr_t end, int flind, int domain)
|
||||
_vm_phys_create_seg(vm_paddr_t start, vm_paddr_t end, int domain)
|
||||
{
|
||||
struct vm_phys_seg *seg;
|
||||
|
||||
@ -314,16 +338,15 @@ _vm_phys_create_seg(vm_paddr_t start, vm_paddr_t end, int flind, int domain)
|
||||
seg->start = start;
|
||||
seg->end = end;
|
||||
seg->domain = domain;
|
||||
seg->free_queues = &vm_phys_free_queues[domain][flind];
|
||||
}
|
||||
|
||||
static void
|
||||
vm_phys_create_seg(vm_paddr_t start, vm_paddr_t end, int flind)
|
||||
vm_phys_create_seg(vm_paddr_t start, vm_paddr_t end)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (mem_affinity == NULL) {
|
||||
_vm_phys_create_seg(start, end, flind, 0);
|
||||
_vm_phys_create_seg(start, end, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -336,11 +359,11 @@ vm_phys_create_seg(vm_paddr_t start, vm_paddr_t end, int flind)
|
||||
panic("No affinity info for start %jx",
|
||||
(uintmax_t)start);
|
||||
if (mem_affinity[i].end >= end) {
|
||||
_vm_phys_create_seg(start, end, flind,
|
||||
_vm_phys_create_seg(start, end,
|
||||
mem_affinity[i].domain);
|
||||
break;
|
||||
}
|
||||
_vm_phys_create_seg(start, mem_affinity[i].end, flind,
|
||||
_vm_phys_create_seg(start, mem_affinity[i].end,
|
||||
mem_affinity[i].domain);
|
||||
start = mem_affinity[i].end;
|
||||
}
|
||||
@ -352,64 +375,149 @@ vm_phys_create_seg(vm_paddr_t start, vm_paddr_t end, int flind)
|
||||
void
|
||||
vm_phys_add_seg(vm_paddr_t start, vm_paddr_t end)
|
||||
{
|
||||
vm_paddr_t paddr;
|
||||
|
||||
KASSERT((start & PAGE_MASK) == 0,
|
||||
("vm_phys_define_seg: start is not page aligned"));
|
||||
KASSERT((end & PAGE_MASK) == 0,
|
||||
("vm_phys_define_seg: end is not page aligned"));
|
||||
|
||||
/*
|
||||
* Split the physical memory segment if it spans two or more free
|
||||
* list boundaries.
|
||||
*/
|
||||
paddr = start;
|
||||
#ifdef VM_FREELIST_ISADMA
|
||||
if (start < 16777216) {
|
||||
if (end > 16777216) {
|
||||
vm_phys_create_seg(start, 16777216,
|
||||
VM_FREELIST_ISADMA);
|
||||
vm_phys_create_seg(16777216, end, VM_FREELIST_DEFAULT);
|
||||
} else
|
||||
vm_phys_create_seg(start, end, VM_FREELIST_ISADMA);
|
||||
if (VM_FREELIST_ISADMA >= vm_nfreelists)
|
||||
vm_nfreelists = VM_FREELIST_ISADMA + 1;
|
||||
} else
|
||||
if (paddr < VM_ISADMA_BOUNDARY && end > VM_ISADMA_BOUNDARY) {
|
||||
vm_phys_create_seg(paddr, VM_ISADMA_BOUNDARY);
|
||||
paddr = VM_ISADMA_BOUNDARY;
|
||||
}
|
||||
#endif
|
||||
#ifdef VM_FREELIST_HIGHMEM
|
||||
if (end > VM_HIGHMEM_ADDRESS) {
|
||||
if (start < VM_HIGHMEM_ADDRESS) {
|
||||
vm_phys_create_seg(start, VM_HIGHMEM_ADDRESS,
|
||||
VM_FREELIST_DEFAULT);
|
||||
vm_phys_create_seg(VM_HIGHMEM_ADDRESS, end,
|
||||
VM_FREELIST_HIGHMEM);
|
||||
} else
|
||||
vm_phys_create_seg(start, end, VM_FREELIST_HIGHMEM);
|
||||
if (VM_FREELIST_HIGHMEM >= vm_nfreelists)
|
||||
vm_nfreelists = VM_FREELIST_HIGHMEM + 1;
|
||||
} else
|
||||
#ifdef VM_FREELIST_LOWMEM
|
||||
if (paddr < VM_LOWMEM_BOUNDARY && end > VM_LOWMEM_BOUNDARY) {
|
||||
vm_phys_create_seg(paddr, VM_LOWMEM_BOUNDARY);
|
||||
paddr = VM_LOWMEM_BOUNDARY;
|
||||
}
|
||||
#endif
|
||||
vm_phys_create_seg(start, end, VM_FREELIST_DEFAULT);
|
||||
#ifdef VM_FREELIST_DMA32
|
||||
if (paddr < VM_DMA32_BOUNDARY && end > VM_DMA32_BOUNDARY) {
|
||||
vm_phys_create_seg(paddr, VM_DMA32_BOUNDARY);
|
||||
paddr = VM_DMA32_BOUNDARY;
|
||||
}
|
||||
#endif
|
||||
vm_phys_create_seg(paddr, end);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the physical memory allocator.
|
||||
*
|
||||
* Requires that vm_page_array is initialized!
|
||||
*/
|
||||
void
|
||||
vm_phys_init(void)
|
||||
{
|
||||
struct vm_freelist *fl;
|
||||
struct vm_phys_seg *seg;
|
||||
#ifdef VM_PHYSSEG_SPARSE
|
||||
long pages;
|
||||
#endif
|
||||
int dom, flind, oind, pind, segind;
|
||||
u_long npages;
|
||||
int dom, flind, freelist, oind, pind, segind;
|
||||
|
||||
/*
|
||||
* Compute the number of free lists, and generate the mapping from the
|
||||
* manifest constants VM_FREELIST_* to the free list indices.
|
||||
*
|
||||
* Initially, the entries of vm_freelist_to_flind[] are set to either
|
||||
* 0 or 1 to indicate which free lists should be created.
|
||||
*/
|
||||
npages = 0;
|
||||
for (segind = vm_phys_nsegs - 1; segind >= 0; segind--) {
|
||||
seg = &vm_phys_segs[segind];
|
||||
#ifdef VM_FREELIST_ISADMA
|
||||
if (seg->end <= VM_ISADMA_BOUNDARY)
|
||||
vm_freelist_to_flind[VM_FREELIST_ISADMA] = 1;
|
||||
else
|
||||
#endif
|
||||
#ifdef VM_FREELIST_LOWMEM
|
||||
if (seg->end <= VM_LOWMEM_BOUNDARY)
|
||||
vm_freelist_to_flind[VM_FREELIST_LOWMEM] = 1;
|
||||
else
|
||||
#endif
|
||||
#ifdef VM_FREELIST_DMA32
|
||||
if (
|
||||
#ifdef VM_DMA32_NPAGES_THRESHOLD
|
||||
/*
|
||||
* Create the DMA32 free list only if the amount of
|
||||
* physical memory above physical address 4G exceeds the
|
||||
* given threshold.
|
||||
*/
|
||||
npages > VM_DMA32_NPAGES_THRESHOLD &&
|
||||
#endif
|
||||
seg->end <= VM_DMA32_BOUNDARY)
|
||||
vm_freelist_to_flind[VM_FREELIST_DMA32] = 1;
|
||||
else
|
||||
#endif
|
||||
{
|
||||
npages += atop(seg->end - seg->start);
|
||||
vm_freelist_to_flind[VM_FREELIST_DEFAULT] = 1;
|
||||
}
|
||||
}
|
||||
/* Change each entry into a running total of the free lists. */
|
||||
for (freelist = 1; freelist < VM_NFREELIST; freelist++) {
|
||||
vm_freelist_to_flind[freelist] +=
|
||||
vm_freelist_to_flind[freelist - 1];
|
||||
}
|
||||
vm_nfreelists = vm_freelist_to_flind[VM_NFREELIST - 1];
|
||||
KASSERT(vm_nfreelists > 0, ("vm_phys_init: no free lists"));
|
||||
/* Change each entry into a free list index. */
|
||||
for (freelist = 0; freelist < VM_NFREELIST; freelist++)
|
||||
vm_freelist_to_flind[freelist]--;
|
||||
|
||||
/*
|
||||
* Initialize the first_page and free_queues fields of each physical
|
||||
* memory segment.
|
||||
*/
|
||||
#ifdef VM_PHYSSEG_SPARSE
|
||||
pages = 0;
|
||||
npages = 0;
|
||||
#endif
|
||||
for (segind = 0; segind < vm_phys_nsegs; segind++) {
|
||||
seg = &vm_phys_segs[segind];
|
||||
#ifdef VM_PHYSSEG_SPARSE
|
||||
seg->first_page = &vm_page_array[pages];
|
||||
pages += atop(seg->end - seg->start);
|
||||
seg->first_page = &vm_page_array[npages];
|
||||
npages += atop(seg->end - seg->start);
|
||||
#else
|
||||
seg->first_page = PHYS_TO_VM_PAGE(seg->start);
|
||||
#endif
|
||||
#ifdef VM_FREELIST_ISADMA
|
||||
if (seg->end <= VM_ISADMA_BOUNDARY) {
|
||||
flind = vm_freelist_to_flind[VM_FREELIST_ISADMA];
|
||||
KASSERT(flind >= 0,
|
||||
("vm_phys_init: ISADMA flind < 0"));
|
||||
} else
|
||||
#endif
|
||||
#ifdef VM_FREELIST_LOWMEM
|
||||
if (seg->end <= VM_LOWMEM_BOUNDARY) {
|
||||
flind = vm_freelist_to_flind[VM_FREELIST_LOWMEM];
|
||||
KASSERT(flind >= 0,
|
||||
("vm_phys_init: LOWMEM flind < 0"));
|
||||
} else
|
||||
#endif
|
||||
#ifdef VM_FREELIST_DMA32
|
||||
if (seg->end <= VM_DMA32_BOUNDARY) {
|
||||
flind = vm_freelist_to_flind[VM_FREELIST_DMA32];
|
||||
KASSERT(flind >= 0,
|
||||
("vm_phys_init: DMA32 flind < 0"));
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
flind = vm_freelist_to_flind[VM_FREELIST_DEFAULT];
|
||||
KASSERT(flind >= 0,
|
||||
("vm_phys_init: DEFAULT flind < 0"));
|
||||
}
|
||||
seg->free_queues = &vm_phys_free_queues[seg->domain][flind];
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the free queues.
|
||||
*/
|
||||
for (dom = 0; dom < vm_ndomains; dom++) {
|
||||
for (flind = 0; flind < vm_nfreelists; flind++) {
|
||||
for (pind = 0; pind < VM_NFREEPOOL; pind++) {
|
||||
@ -419,6 +527,7 @@ vm_phys_init(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rw_init(&vm_phys_fictitious_reg_lock, "vmfctr");
|
||||
}
|
||||
|
||||
@ -498,25 +607,29 @@ vm_phys_alloc_pages(int pool, int order)
|
||||
}
|
||||
|
||||
/*
|
||||
* Find and dequeue a free page on the given free list, with the
|
||||
* specified pool and order
|
||||
* Allocate a contiguous, power of two-sized set of physical pages from the
|
||||
* specified free list. The free list must be specified using one of the
|
||||
* manifest constants VM_FREELIST_*.
|
||||
*
|
||||
* The free page queues must be locked.
|
||||
*/
|
||||
vm_page_t
|
||||
vm_phys_alloc_freelist_pages(int flind, int pool, int order)
|
||||
vm_phys_alloc_freelist_pages(int freelist, int pool, int order)
|
||||
{
|
||||
vm_page_t m;
|
||||
int dom, domain;
|
||||
|
||||
KASSERT(flind < VM_NFREELIST,
|
||||
("vm_phys_alloc_freelist_pages: freelist %d is out of range", flind));
|
||||
KASSERT(freelist < VM_NFREELIST,
|
||||
("vm_phys_alloc_freelist_pages: freelist %d is out of range",
|
||||
freelist));
|
||||
KASSERT(pool < VM_NFREEPOOL,
|
||||
("vm_phys_alloc_freelist_pages: pool %d is out of range", pool));
|
||||
KASSERT(order < VM_NFREEORDER,
|
||||
("vm_phys_alloc_freelist_pages: order %d is out of range", order));
|
||||
|
||||
for (dom = 0; dom < vm_ndomains; dom++) {
|
||||
domain = vm_rr_selectdomain();
|
||||
m = vm_phys_alloc_domain_pages(domain, flind, pool, order);
|
||||
m = vm_phys_alloc_domain_pages(domain,
|
||||
vm_freelist_to_flind[freelist], pool, order);
|
||||
if (m != NULL)
|
||||
return (m);
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ void vm_phys_add_page(vm_paddr_t pa);
|
||||
void vm_phys_add_seg(vm_paddr_t start, vm_paddr_t end);
|
||||
vm_page_t vm_phys_alloc_contig(u_long npages, vm_paddr_t low, vm_paddr_t high,
|
||||
u_long alignment, vm_paddr_t boundary);
|
||||
vm_page_t vm_phys_alloc_freelist_pages(int flind, int pool, int order);
|
||||
vm_page_t vm_phys_alloc_freelist_pages(int freelist, int pool, int order);
|
||||
vm_page_t vm_phys_alloc_pages(int pool, int order);
|
||||
boolean_t vm_phys_domain_intersects(long mask, vm_paddr_t low, vm_paddr_t high);
|
||||
int vm_phys_fictitious_reg_range(vm_paddr_t start, vm_paddr_t end,
|
||||
|
@ -634,7 +634,7 @@ usage(void)
|
||||
" [-r rej-name] [-V t | nil | never] [-x number] [-z backup-ext]\n"
|
||||
" [--posix] [origfile [patchfile]]\n"
|
||||
" patch <patchfile\n");
|
||||
my_exit(EXIT_SUCCESS);
|
||||
my_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -428,7 +428,7 @@ checked_in(char *file)
|
||||
void
|
||||
version(void)
|
||||
{
|
||||
fprintf(stderr, "patch 2.0-12u10 FreeBSD\n");
|
||||
printf("patch 2.0-12u10 FreeBSD\n");
|
||||
my_exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
|
@ -30,10 +30,7 @@
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
|
||||
@ -41,47 +38,11 @@ __FBSDID("$FreeBSD$");
|
||||
#include <vmmapi.h>
|
||||
|
||||
#include "acpi.h"
|
||||
#include "inout.h"
|
||||
#include "pci_lpc.h"
|
||||
#include "rtc.h"
|
||||
|
||||
#define IO_RTC 0x70
|
||||
#define IO_RTC 0x70
|
||||
|
||||
#define RTC_SEC 0x00 /* seconds */
|
||||
#define RTC_SEC_ALARM 0x01
|
||||
#define RTC_MIN 0x02
|
||||
#define RTC_MIN_ALARM 0x03
|
||||
#define RTC_HRS 0x04
|
||||
#define RTC_HRS_ALARM 0x05
|
||||
#define RTC_WDAY 0x06
|
||||
#define RTC_DAY 0x07
|
||||
#define RTC_MONTH 0x08
|
||||
#define RTC_YEAR 0x09
|
||||
#define RTC_CENTURY 0x32 /* current century */
|
||||
|
||||
#define RTC_STATUSA 0xA
|
||||
#define RTCSA_TUP 0x80 /* time update, don't look now */
|
||||
|
||||
#define RTC_STATUSB 0xB
|
||||
#define RTCSB_DST 0x01
|
||||
#define RTCSB_24HR 0x02
|
||||
#define RTCSB_BIN 0x04 /* 0 = BCD, 1 = Binary */
|
||||
#define RTCSB_PINTR 0x40 /* 1 = enable periodic clock interrupt */
|
||||
#define RTCSB_HALT 0x80 /* stop clock updates */
|
||||
|
||||
#define RTC_INTR 0x0c /* status register C (R) interrupt source */
|
||||
|
||||
#define RTC_STATUSD 0x0d /* status register D (R) Lost Power */
|
||||
#define RTCSD_PWR 0x80 /* clock power OK */
|
||||
|
||||
#define RTC_NVRAM_START 0x0e
|
||||
#define RTC_NVRAM_END 0x7f
|
||||
#define RTC_NVRAM_SZ (128 - RTC_NVRAM_START)
|
||||
#define nvoff(x) ((x) - RTC_NVRAM_START)
|
||||
|
||||
#define RTC_DIAG 0x0e
|
||||
#define RTC_RSTCODE 0x0f
|
||||
#define RTC_EQUIPMENT 0x14
|
||||
#define RTC_LMEM_LSB 0x34
|
||||
#define RTC_LMEM_MSB 0x35
|
||||
#define RTC_HMEM_LSB 0x5b
|
||||
@ -92,249 +53,30 @@ __FBSDID("$FreeBSD$");
|
||||
#define m_16MB (16*1024*1024)
|
||||
#define m_4GB (4ULL*1024*1024*1024)
|
||||
|
||||
static int addr;
|
||||
|
||||
static uint8_t rtc_nvram[RTC_NVRAM_SZ];
|
||||
|
||||
/* XXX initialize these to default values as they would be from BIOS */
|
||||
static uint8_t status_a, status_b;
|
||||
|
||||
static struct {
|
||||
uint8_t hours;
|
||||
uint8_t mins;
|
||||
uint8_t secs;
|
||||
} rtc_alarm;
|
||||
|
||||
static u_char const bin2bcd_data[] = {
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
|
||||
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
|
||||
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
|
||||
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
|
||||
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
|
||||
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
|
||||
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99
|
||||
};
|
||||
#define bin2bcd(bin) (bin2bcd_data[bin])
|
||||
|
||||
#define rtcout(val) ((status_b & RTCSB_BIN) ? (val) : bin2bcd((val)))
|
||||
|
||||
static void
|
||||
timevalfix(struct timeval *t1)
|
||||
/*
|
||||
* Returns the current RTC time as number of seconds since 00:00:00 Jan 1, 1970
|
||||
*
|
||||
* XXX this always returns localtime to maintain compatibility with the
|
||||
* original device model.
|
||||
*/
|
||||
static time_t
|
||||
rtc_time(struct vmctx *ctx)
|
||||
{
|
||||
|
||||
if (t1->tv_usec < 0) {
|
||||
t1->tv_sec--;
|
||||
t1->tv_usec += 1000000;
|
||||
}
|
||||
if (t1->tv_usec >= 1000000) {
|
||||
t1->tv_sec++;
|
||||
t1->tv_usec -= 1000000;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
timevalsub(struct timeval *t1, const struct timeval *t2)
|
||||
{
|
||||
|
||||
t1->tv_sec -= t2->tv_sec;
|
||||
t1->tv_usec -= t2->tv_usec;
|
||||
timevalfix(t1);
|
||||
}
|
||||
|
||||
static int
|
||||
rtc_addr_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
|
||||
uint32_t *eax, void *arg)
|
||||
{
|
||||
if (bytes != 1)
|
||||
return (-1);
|
||||
|
||||
if (in) {
|
||||
/* straight read of this register will return 0xFF */
|
||||
*eax = 0xff;
|
||||
return (0);
|
||||
}
|
||||
|
||||
switch (*eax & 0x7f) {
|
||||
case RTC_SEC:
|
||||
case RTC_SEC_ALARM:
|
||||
case RTC_MIN:
|
||||
case RTC_MIN_ALARM:
|
||||
case RTC_HRS:
|
||||
case RTC_HRS_ALARM:
|
||||
case RTC_WDAY:
|
||||
case RTC_DAY:
|
||||
case RTC_MONTH:
|
||||
case RTC_YEAR:
|
||||
case RTC_STATUSA:
|
||||
case RTC_STATUSB:
|
||||
case RTC_INTR:
|
||||
case RTC_STATUSD:
|
||||
case RTC_NVRAM_START ... RTC_NVRAM_END:
|
||||
break;
|
||||
default:
|
||||
return (-1);
|
||||
}
|
||||
|
||||
addr = *eax & 0x7f;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
rtc_data_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
|
||||
uint32_t *eax, void *arg)
|
||||
{
|
||||
int hour;
|
||||
struct tm tm;
|
||||
time_t t;
|
||||
struct timeval cur, delta;
|
||||
|
||||
static struct timeval last;
|
||||
static struct tm tm;
|
||||
|
||||
if (bytes != 1)
|
||||
return (-1);
|
||||
|
||||
gettimeofday(&cur, NULL);
|
||||
|
||||
/*
|
||||
* Increment the cached time only once per second so we can guarantee
|
||||
* that the guest has at least one second to read the hour:min:sec
|
||||
* separately and still get a coherent view of the time.
|
||||
*/
|
||||
delta = cur;
|
||||
timevalsub(&delta, &last);
|
||||
if (delta.tv_sec >= 1 && (status_b & RTCSB_HALT) == 0) {
|
||||
t = cur.tv_sec;
|
||||
localtime_r(&t, &tm);
|
||||
last = cur;
|
||||
}
|
||||
|
||||
if (in) {
|
||||
switch (addr) {
|
||||
case RTC_SEC_ALARM:
|
||||
*eax = rtc_alarm.secs;
|
||||
break;
|
||||
case RTC_MIN_ALARM:
|
||||
*eax = rtc_alarm.mins;
|
||||
break;
|
||||
case RTC_HRS_ALARM:
|
||||
*eax = rtc_alarm.hours;
|
||||
break;
|
||||
case RTC_SEC:
|
||||
*eax = rtcout(tm.tm_sec);
|
||||
return (0);
|
||||
case RTC_MIN:
|
||||
*eax = rtcout(tm.tm_min);
|
||||
return (0);
|
||||
case RTC_HRS:
|
||||
if (status_b & RTCSB_24HR)
|
||||
hour = tm.tm_hour;
|
||||
else
|
||||
hour = (tm.tm_hour % 12) + 1;
|
||||
|
||||
*eax = rtcout(hour);
|
||||
|
||||
/*
|
||||
* If we are representing time in the 12-hour format
|
||||
* then set the MSB to indicate PM.
|
||||
*/
|
||||
if ((status_b & RTCSB_24HR) == 0 && tm.tm_hour >= 12)
|
||||
*eax |= 0x80;
|
||||
|
||||
return (0);
|
||||
case RTC_WDAY:
|
||||
*eax = rtcout(tm.tm_wday + 1);
|
||||
return (0);
|
||||
case RTC_DAY:
|
||||
*eax = rtcout(tm.tm_mday);
|
||||
return (0);
|
||||
case RTC_MONTH:
|
||||
*eax = rtcout(tm.tm_mon + 1);
|
||||
return (0);
|
||||
case RTC_YEAR:
|
||||
*eax = rtcout(tm.tm_year % 100);
|
||||
return (0);
|
||||
case RTC_STATUSA:
|
||||
*eax = status_a;
|
||||
return (0);
|
||||
case RTC_STATUSB:
|
||||
*eax = status_b;
|
||||
return (0);
|
||||
case RTC_INTR:
|
||||
*eax = 0;
|
||||
return (0);
|
||||
case RTC_STATUSD:
|
||||
*eax = RTCSD_PWR;
|
||||
return (0);
|
||||
case RTC_NVRAM_START ... RTC_NVRAM_END:
|
||||
*eax = rtc_nvram[addr - RTC_NVRAM_START];
|
||||
return (0);
|
||||
default:
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
switch (addr) {
|
||||
case RTC_STATUSA:
|
||||
status_a = *eax & ~RTCSA_TUP;
|
||||
break;
|
||||
case RTC_STATUSB:
|
||||
/* XXX not implemented yet XXX */
|
||||
if (*eax & RTCSB_PINTR)
|
||||
return (-1);
|
||||
status_b = *eax;
|
||||
break;
|
||||
case RTC_STATUSD:
|
||||
/* ignore write */
|
||||
break;
|
||||
case RTC_SEC_ALARM:
|
||||
rtc_alarm.secs = *eax;
|
||||
break;
|
||||
case RTC_MIN_ALARM:
|
||||
rtc_alarm.mins = *eax;
|
||||
break;
|
||||
case RTC_HRS_ALARM:
|
||||
rtc_alarm.hours = *eax;
|
||||
break;
|
||||
case RTC_SEC:
|
||||
case RTC_MIN:
|
||||
case RTC_HRS:
|
||||
case RTC_WDAY:
|
||||
case RTC_DAY:
|
||||
case RTC_MONTH:
|
||||
case RTC_YEAR:
|
||||
/*
|
||||
* Ignore writes to the time of day registers
|
||||
*/
|
||||
break;
|
||||
case RTC_NVRAM_START ... RTC_NVRAM_END:
|
||||
rtc_nvram[addr - RTC_NVRAM_START] = *eax;
|
||||
break;
|
||||
default:
|
||||
return (-1);
|
||||
}
|
||||
return (0);
|
||||
time(&t);
|
||||
localtime_r(&t, &tm);
|
||||
return (timegm(&tm));
|
||||
}
|
||||
|
||||
void
|
||||
rtc_init(struct vmctx *ctx)
|
||||
{
|
||||
struct timeval cur;
|
||||
struct tm tm;
|
||||
size_t himem;
|
||||
size_t lomem;
|
||||
int err;
|
||||
|
||||
err = gettimeofday(&cur, NULL);
|
||||
assert(err == 0);
|
||||
(void) localtime_r(&cur.tv_sec, &tm);
|
||||
|
||||
memset(rtc_nvram, 0, sizeof(rtc_nvram));
|
||||
|
||||
rtc_nvram[nvoff(RTC_CENTURY)] = bin2bcd((tm.tm_year + 1900) / 100);
|
||||
|
||||
/* XXX init diag/reset code/equipment/checksum ? */
|
||||
|
||||
/*
|
||||
@ -344,17 +86,22 @@ rtc_init(struct vmctx *ctx)
|
||||
* 0x5b/0x5c/0x5d - 64KB chunks above 4GB
|
||||
*/
|
||||
lomem = (vm_get_lowmem_size(ctx) - m_16MB) / m_64KB;
|
||||
rtc_nvram[nvoff(RTC_LMEM_LSB)] = lomem;
|
||||
rtc_nvram[nvoff(RTC_LMEM_MSB)] = lomem >> 8;
|
||||
err = vm_rtc_write(ctx, RTC_LMEM_LSB, lomem);
|
||||
assert(err == 0);
|
||||
err = vm_rtc_write(ctx, RTC_LMEM_MSB, lomem >> 8);
|
||||
assert(err == 0);
|
||||
|
||||
himem = vm_get_highmem_size(ctx) / m_64KB;
|
||||
rtc_nvram[nvoff(RTC_HMEM_LSB)] = himem;
|
||||
rtc_nvram[nvoff(RTC_HMEM_SB)] = himem >> 8;
|
||||
rtc_nvram[nvoff(RTC_HMEM_MSB)] = himem >> 16;
|
||||
}
|
||||
err = vm_rtc_write(ctx, RTC_HMEM_LSB, himem);
|
||||
assert(err == 0);
|
||||
err = vm_rtc_write(ctx, RTC_HMEM_SB, himem >> 8);
|
||||
assert(err == 0);
|
||||
err = vm_rtc_write(ctx, RTC_HMEM_MSB, himem >> 16);
|
||||
assert(err == 0);
|
||||
|
||||
INOUT_PORT(rtc, IO_RTC, IOPORT_F_INOUT, rtc_addr_handler);
|
||||
INOUT_PORT(rtc, IO_RTC + 1, IOPORT_F_INOUT, rtc_data_handler);
|
||||
err = vm_rtc_settime(ctx, rtc_time(ctx));
|
||||
assert(err == 0);
|
||||
}
|
||||
|
||||
static void
|
||||
rtc_dsdt(void)
|
||||
|
@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <machine/cpufunc.h>
|
||||
@ -157,6 +158,11 @@ usage(bool cpu_intel)
|
||||
" [--inject-nmi]\n"
|
||||
" [--force-reset]\n"
|
||||
" [--force-poweroff]\n"
|
||||
" [--get-rtc-time]\n"
|
||||
" [--set-rtc-time=<secs>]\n"
|
||||
" [--get-rtc-nvram]\n"
|
||||
" [--set-rtc-nvram=<val>]\n"
|
||||
" [--rtc-nvram-offset=<offset>]\n"
|
||||
" [--get-active-cpus]\n"
|
||||
" [--get-suspended-cpus]\n"
|
||||
" [--get-intinfo]\n"
|
||||
@ -220,6 +226,12 @@ usage(bool cpu_intel)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int get_rtc_time, set_rtc_time;
|
||||
static int get_rtc_nvram, set_rtc_nvram;
|
||||
static int rtc_nvram_offset;
|
||||
static uint8_t rtc_nvram_value;
|
||||
static time_t rtc_secs;
|
||||
|
||||
static int get_stats, getcap, setcap, capval, get_gpa_pmap;
|
||||
static int inject_nmi, assert_lapic_lvt;
|
||||
static int force_reset, force_poweroff;
|
||||
@ -545,6 +557,9 @@ enum {
|
||||
UNASSIGN_PPTDEV,
|
||||
GET_GPA_PMAP,
|
||||
ASSERT_LAPIC_LVT,
|
||||
SET_RTC_TIME,
|
||||
SET_RTC_NVRAM,
|
||||
RTC_NVRAM_OFFSET,
|
||||
};
|
||||
|
||||
static void
|
||||
@ -1269,6 +1284,11 @@ setup_options(bool cpu_intel)
|
||||
{ "setcap", REQ_ARG, 0, SET_CAP },
|
||||
{ "get-gpa-pmap", REQ_ARG, 0, GET_GPA_PMAP },
|
||||
{ "assert-lapic-lvt", REQ_ARG, 0, ASSERT_LAPIC_LVT },
|
||||
{ "get-rtc-time", NO_ARG, &get_rtc_time, 1 },
|
||||
{ "set-rtc-time", REQ_ARG, 0, SET_RTC_TIME },
|
||||
{ "rtc-nvram-offset", REQ_ARG, 0, RTC_NVRAM_OFFSET },
|
||||
{ "get-rtc-nvram", NO_ARG, &get_rtc_nvram, 1 },
|
||||
{ "set-rtc-nvram", REQ_ARG, 0, SET_RTC_NVRAM },
|
||||
{ "getcap", NO_ARG, &getcap, 1 },
|
||||
{ "get-stats", NO_ARG, &get_stats, 1 },
|
||||
{ "get-desc-ds",NO_ARG, &get_desc_ds, 1 },
|
||||
@ -1462,6 +1482,33 @@ setup_options(bool cpu_intel)
|
||||
return (all_opts);
|
||||
}
|
||||
|
||||
static const char *
|
||||
wday_str(int idx)
|
||||
{
|
||||
static const char *weekdays[] = {
|
||||
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
|
||||
};
|
||||
|
||||
if (idx >= 0 && idx < 7)
|
||||
return (weekdays[idx]);
|
||||
else
|
||||
return ("UNK");
|
||||
}
|
||||
|
||||
static const char *
|
||||
mon_str(int idx)
|
||||
{
|
||||
static const char *months[] = {
|
||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
||||
};
|
||||
|
||||
if (idx >= 0 && idx < 12)
|
||||
return (months[idx]);
|
||||
else
|
||||
return ("UNK");
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
@ -1477,6 +1524,7 @@ main(int argc, char *argv[])
|
||||
cpuset_t cpus;
|
||||
bool cpu_intel;
|
||||
uint64_t cs, ds, es, fs, gs, ss, tr, ldtr;
|
||||
struct tm tm;
|
||||
struct option *opts;
|
||||
|
||||
cpu_intel = cpu_vendor_intel();
|
||||
@ -1594,6 +1642,17 @@ main(int argc, char *argv[])
|
||||
capval = strtoul(optarg, NULL, 0);
|
||||
setcap = 1;
|
||||
break;
|
||||
case SET_RTC_TIME:
|
||||
rtc_secs = strtoul(optarg, NULL, 0);
|
||||
set_rtc_time = 1;
|
||||
break;
|
||||
case SET_RTC_NVRAM:
|
||||
rtc_nvram_value = (uint8_t)strtoul(optarg, NULL, 0);
|
||||
set_rtc_nvram = 1;
|
||||
break;
|
||||
case RTC_NVRAM_OFFSET:
|
||||
rtc_nvram_offset = strtoul(optarg, NULL, 0);
|
||||
break;
|
||||
case GET_GPA_PMAP:
|
||||
gpa_pmap = strtoul(optarg, NULL, 0);
|
||||
get_gpa_pmap = 1;
|
||||
@ -1971,6 +2030,31 @@ main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
if (!error && set_rtc_nvram)
|
||||
error = vm_rtc_write(ctx, rtc_nvram_offset, rtc_nvram_value);
|
||||
|
||||
if (!error && (get_rtc_nvram || get_all)) {
|
||||
error = vm_rtc_read(ctx, rtc_nvram_offset, &rtc_nvram_value);
|
||||
if (error == 0) {
|
||||
printf("rtc nvram[%03d]: 0x%02x\n", rtc_nvram_offset,
|
||||
rtc_nvram_value);
|
||||
}
|
||||
}
|
||||
|
||||
if (!error && set_rtc_time)
|
||||
error = vm_rtc_settime(ctx, rtc_secs);
|
||||
|
||||
if (!error && (get_rtc_time || get_all)) {
|
||||
error = vm_rtc_gettime(ctx, &rtc_secs);
|
||||
if (error == 0) {
|
||||
gmtime_r(&rtc_secs, &tm);
|
||||
printf("rtc time %#lx: %s %s %02d %02d:%02d:%02d %d\n",
|
||||
rtc_secs, wday_str(tm.tm_wday), mon_str(tm.tm_mon),
|
||||
tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
|
||||
1900 + tm.tm_year);
|
||||
}
|
||||
}
|
||||
|
||||
if (!error && (getcap || get_all)) {
|
||||
int captype, val, getcaptype;
|
||||
|
||||
|
@ -27,7 +27,7 @@
|
||||
.\"
|
||||
.\" Support for miscellaneous binary image activators
|
||||
.\"
|
||||
.Dd April 10, 2014
|
||||
.Dd December 30, 2014
|
||||
.Dt BINMISCCTL 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -146,46 +146,148 @@ Look up and print out the activator entry identified with
|
||||
Take a snapshot and print all the activator entries currently configured.
|
||||
.El
|
||||
.Sh EXAMPLES
|
||||
Add an image activator to run the LLVM interpreter (lli) on bitcode
|
||||
compiled files:
|
||||
.Bd -ragged -offset indent
|
||||
# binmiscctl add llvmbc --interpreter ''/usr/bin/lli --fake-argv0=#a''
|
||||
--magic ''BC\\xc0\\xde'' --size 4 --set-enabled
|
||||
.Ed
|
||||
.Pp
|
||||
Add an image activator to run the LLVM interpreter (lli) on bitcode
|
||||
compiled files.
|
||||
.Ar #a
|
||||
gets replaced with the old
|
||||
is replaced with the old
|
||||
.Dv argv0
|
||||
value so that 'lli' can fake its
|
||||
.Dv argv0 .
|
||||
Set its state to enabled.
|
||||
.Pp
|
||||
Set the state of the
|
||||
.Ar llvmbc
|
||||
image activator to disabled:
|
||||
.Dl # binmiscctl disable llvmbc
|
||||
.Pp
|
||||
Set the state of the
|
||||
.Ar llvmbc
|
||||
image activator to disabled.
|
||||
.Pp
|
||||
image activator to enabled:
|
||||
.Dl # binmiscctl enable llvmbc
|
||||
.Pp
|
||||
Set the state of the
|
||||
.Ar llvmbc
|
||||
image activator to enabled.
|
||||
.Pp
|
||||
.Dl # binmiscctl remove llvmbc
|
||||
.Pp
|
||||
Delete the
|
||||
.Ar llvmbc
|
||||
image activator.
|
||||
.Pp
|
||||
.Dl # binmiscctl lookup llvmbc
|
||||
image activator:
|
||||
.Dl # binmiscctl remove llvmbc
|
||||
.Pp
|
||||
Look up and list the record for the
|
||||
.Ar llvmbc
|
||||
image activator.
|
||||
image activator:
|
||||
.Dl # binmiscctl lookup llvmbc
|
||||
.Pp
|
||||
Add QEMU bsd-user program as an image activator for ARM little-endian binaries:
|
||||
.Bd -literal -offset indent
|
||||
# binmiscctl add armelf \e
|
||||
--interpreter "/usr/local/bin/qemu-arm-static" \e
|
||||
--magic "\ex7f\ex45\ex4c\ex46\ex01\ex01\ex01\ex00\ex00\ex00\e
|
||||
\ex00\ex00\ex00\ex00\ex00\ex00\ex02\ex00\ex28\ex00" \e
|
||||
--mask "\exff\exff\exff\exff\exff\exff\exff\ex00\exff\exff\e
|
||||
\exff\exff\exff\exff\exff\exff\exfe\exff\exff\exff" \e
|
||||
--size 20 --set-enabled
|
||||
.Ed
|
||||
.Pp
|
||||
Add QEMU bsd-user program as an image activator for ARM big-endian binaries:
|
||||
.Bd -literal -offset indent
|
||||
# binmiscctl add armebelf \e
|
||||
--interpreter "/usr/local/bin/qemu-arm-static" \e
|
||||
--magic "\ex7f\ex45\ex4c\ex46\ex01\ex02\ex01\ex00\ex00\ex00\e
|
||||
\ex00\ex00\ex00\ex00\ex00\ex00\ex00\ex02\ex00\ex28" \e
|
||||
--mask "\exff\exff\exff\exff\exff\exff\exff\ex00\exff\exff\e
|
||||
\exff\exff\exff\exff\exff\exff\exff\exfe\exff\exff" \e
|
||||
--size 20 --set-enabled
|
||||
.Ed
|
||||
.Pp
|
||||
Add QEMU bsd-user program as an image activator for MIPS32 binaries:
|
||||
.Bd -literal -offset indent
|
||||
# binmiscctl add mips32 \e
|
||||
--interpreter "/usr/local/bin/qemu-mips-static" \e
|
||||
--magic "\ex7f\ex45\ex4c\ex46\ex01\ex02\ex01\ex00\ex00\ex00\e
|
||||
\ex00\ex00\ex00\ex00\ex00\ex00\ex00\ex02\ex00\ex08" \e
|
||||
--mask "\exff\exff\exff\exff\exff\exff\exff\ex00\exff\exff\e
|
||||
\exff\exff\exff\exff\exff\exff\exff\exfe\exff\exff" \e
|
||||
--size 20 --set-enabled
|
||||
.Ed
|
||||
.Pp
|
||||
Add QEMU bsd-user program as an image activator for MIPS64 binaries:
|
||||
.Bd -literal -offset indent
|
||||
# binmiscctl add mips64 \e
|
||||
--interpreter "/usr/local/bin/qemu-mips64-static" \e
|
||||
--magic "\ex7f\ex45\ex4c\ex46\ex02\ex02\ex01\ex00\ex00\ex00\e
|
||||
\ex00\ex00\ex00\ex00\ex00\ex00\ex00\ex02\ex00\ex08" \e
|
||||
--mask "\exff\exff\exff\exff\exff\exff\exff\ex00\exff\exff\e
|
||||
\exff\exff\exff\exff\exff\exff\exff\exfe\exff\exff" \e
|
||||
--size 20 --set-enabled
|
||||
.Ed
|
||||
.Pp
|
||||
Add QEMU bsd-user program as an image activator for PowerPC binaries:
|
||||
.Bd -literal -offset indent
|
||||
# binmiscctl add powerpc \e
|
||||
--interpreter "/usr/local/bin/qemu-ppc-static" \e
|
||||
--magic "\ex7f\ex45\ex4c\ex46\ex01\ex02\ex01\ex00\ex00\ex00\e
|
||||
\ex00\ex00\ex00\ex00\ex00\ex00\ex00\ex02\ex00\ex14" \e
|
||||
--mask "\exff\exff\exff\exff\exff\exff\exff\ex00\exff\exff\e
|
||||
\exff\exff\exff\exff\exff\exff\exff\exfe\exff\exff" \e
|
||||
--size 20 --set-enabled
|
||||
.Ed
|
||||
.Pp
|
||||
Add QEMU bsd-user program as an image activator for PowerPC64 binaries:
|
||||
.Bd -literal -offset indent
|
||||
# binmiscctl add powerpc64 \e
|
||||
--interpreter "/usr/local/bin/qemu-ppc64-static" \e
|
||||
--magic "\ex7f\ex45\ex4c\ex46\ex01\ex02\ex01\ex00\ex00\ex00\e
|
||||
\ex00\ex00\ex00\ex00\ex00\ex00\ex00\ex02\ex00\ex15" \e
|
||||
--mask "\exff\exff\exff\exff\exff\exff\exff\ex00\exff\exff\e
|
||||
\exff\exff\exff\exff\exff\exff\exff\exfe\exff\exff" \e
|
||||
--size 20 --set-enabled
|
||||
.Ed
|
||||
.Pp
|
||||
Add QEMU bsd-user program as an image activator for SPARC64 binaries:
|
||||
.Bd -literal -offset indent
|
||||
# binmiscctl add sparc64 \e
|
||||
--interpreter "/usr/local/bin/qemu-sparc64-static" \e
|
||||
--magic "\ex7f\ex45\ex4c\ex46\ex02\ex02\ex01\ex00\ex00\ex00\e
|
||||
\ex00\ex00\ex00\ex00\ex00\ex00\ex00\ex02\ex00\ex2b" \e
|
||||
--mask "\exff\exff\exff\exff\exff\exff\exff\ex00\exff\exff\e
|
||||
\exff\exff\exff\exff\exff\exff\exff\exfe\exff\exff" \e
|
||||
--size 20 --set-enabled
|
||||
.Ed
|
||||
.Pp
|
||||
.Ss "Create and use an ARMv6 chroot on an AMD64 host"
|
||||
Use an existing source tree to build a chroot host with architecture
|
||||
overrides:
|
||||
.Bd -literal
|
||||
D=/path/to/chroot
|
||||
cd /usr/src
|
||||
mkdir -p $D
|
||||
make world TARGET=arm TARGET_ARCH=armv6 DESTDIR=$D
|
||||
make distribution TARGET=arm TARGET_ARCH=armv6 DESTDIR=$D
|
||||
.Ed
|
||||
.Pp
|
||||
With
|
||||
.Pa emulators/qemu-user-static
|
||||
from the
|
||||
.Fx
|
||||
Ports Collection, the emulator must be copied into the jail path
|
||||
specified in the binmiscctl command.
|
||||
Using the example above:
|
||||
.Bd -literal
|
||||
mkdir $D/usr/local/bin
|
||||
cp /usr/local/bin/qemu-arm-static $D/usr/local/bin
|
||||
.Ed
|
||||
.Pp
|
||||
Now the user can chroot into the environment normally, as root:
|
||||
.Bd -literal
|
||||
chroot $D
|
||||
.Ed
|
||||
.Sh SEE ALSO
|
||||
.Xr lli 1 ,
|
||||
.Xr execve 2
|
||||
.Xr execve 2 ,
|
||||
.Xr jail 8
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Cm binmiscctl
|
||||
|
Loading…
Reference in New Issue
Block a user