mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
[hvm] Keep track of time offset between domain time and dom0 time.
On each rtc time update from the hvm domain, we send an ioreq request to qemu which then updates the time offset xenstore entry. The time offset is preserved across reboot and can be set on domain creation by setting the rtc_timeoffset variable in a config file. From: Andrei Petrov <andrei.petrov@xensource.com> Signed-off-by: Christian Limpach <Christian.Limpach@xensource.com>
This commit is contained in:
1 parent
9423a78d7f
commit
2bdbd3e193
16 files changed
+191
-20
No files matched your search
@@ -179,6 +179,10 @@ serial='pty'
|
||||
#localtime=1
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# set the real time clock offset in seconds [default=0 i.e. same as dom0]
|
||||
#rtc_timeoffset=3600
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# start in full screen
|
||||
#full-screen=1
|
||||
|
||||
@@ -74,6 +74,8 @@ int vcpus = 1;
|
||||
|
||||
int xc_handle;
|
||||
|
||||
long time_offset = 0;
|
||||
|
||||
shared_iopage_t *shared_page = NULL;
|
||||
|
||||
#define BUFFER_IO_MAX_DELAY 100
|
||||
@@ -439,6 +441,34 @@ void cpu_ioreq_xor(CPUState *env, ioreq_t *req)
|
||||
req->data = tmp1;
|
||||
}
|
||||
|
||||
void timeoffset_get()
|
||||
{
|
||||
char *p;
|
||||
|
||||
p = xenstore_vm_read(domid, "rtc/timeoffset", NULL);
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
if (sscanf(p, "%ld", &time_offset) == 1)
|
||||
fprintf(logfile, "Time offset set %ld\n", time_offset);
|
||||
else
|
||||
time_offset = 0;
|
||||
|
||||
xc_domain_set_time_offset(xc_handle, domid, time_offset);
|
||||
|
||||
free(p);
|
||||
}
|
||||
|
||||
void cpu_ioreq_timeoffset(CPUState *env, ioreq_t *req)
|
||||
{
|
||||
char b[64];
|
||||
|
||||
time_offset += (ulong)req->data;
|
||||
|
||||
sprintf(b, "%ld", time_offset);
|
||||
xenstore_vm_write(domid, "rtc/timeoffset", b);
|
||||
}
|
||||
|
||||
void cpu_ioreq_xchg(CPUState *env, ioreq_t *req)
|
||||
{
|
||||
unsigned long tmp1;
|
||||
@@ -478,6 +508,9 @@ void __handle_ioreq(CPUState *env, ioreq_t *req)
|
||||
case IOREQ_TYPE_XCHG:
|
||||
cpu_ioreq_xchg(env, req);
|
||||
break;
|
||||
case IOREQ_TYPE_TIMEOFFSET:
|
||||
cpu_ioreq_timeoffset(env, req);
|
||||
break;
|
||||
default:
|
||||
hw_error("Invalid ioreq type 0x%x\n", req->type);
|
||||
}
|
||||
|
||||
@@ -6670,6 +6670,9 @@ int main(int argc, char **argv)
|
||||
}
|
||||
free(page_array);
|
||||
#endif
|
||||
|
||||
timeoffset_get();
|
||||
|
||||
#else /* !CONFIG_DM */
|
||||
|
||||
phys_ram_base = qemu_vmalloc(phys_ram_size);
|
||||
|
||||
@@ -1276,6 +1276,12 @@ int xenstore_unsubscribe_from_hotplug_status(struct xs_handle *handle,
|
||||
const char *inst,
|
||||
const char *token);
|
||||
|
||||
int xenstore_vm_write(int domid, char *key, char *val);
|
||||
char *xenstore_vm_read(int domid, char *key, int *len);
|
||||
|
||||
/* helper2.c */
|
||||
extern long time_offset;
|
||||
void timeoffset_get(void);
|
||||
|
||||
/* xen_platform.c */
|
||||
void pci_xen_platform_init(PCIBus *bus);
|
||||
|
||||
@@ -567,3 +567,72 @@ int xenstore_unsubscribe_from_hotplug_status(struct xs_handle *handle,
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
char *xenstore_vm_read(int domid, char *key, int *len)
|
||||
{
|
||||
char *buf = NULL, *path = NULL, *value = NULL;
|
||||
|
||||
if (xsh == NULL)
|
||||
goto out;
|
||||
|
||||
path = xs_get_domain_path(xsh, domid);
|
||||
if (path == NULL) {
|
||||
fprintf(logfile, "xs_get_domain_path(%d): error\n", domid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
pasprintf(&buf, "%s/vm", path);
|
||||
free(path);
|
||||
path = xs_read(xsh, XBT_NULL, buf, NULL);
|
||||
if (path == NULL) {
|
||||
fprintf(logfile, "xs_read(%s): read error\n", buf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
pasprintf(&buf, "%s/%s", path, key);
|
||||
value = xs_read(xsh, XBT_NULL, buf, len);
|
||||
if (value == NULL) {
|
||||
fprintf(logfile, "xs_read(%s): read error\n", buf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
free(path);
|
||||
free(buf);
|
||||
return value;
|
||||
}
|
||||
|
||||
int xenstore_vm_write(int domid, char *key, char *value)
|
||||
{
|
||||
char *buf = NULL, *path = NULL;
|
||||
int rc = -1;
|
||||
|
||||
if (xsh == NULL)
|
||||
goto out;
|
||||
|
||||
path = xs_get_domain_path(xsh, domid);
|
||||
if (path == NULL) {
|
||||
fprintf(logfile, "xs_get_domain_path(%d): error\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
pasprintf(&buf, "%s/vm", path);
|
||||
free(path);
|
||||
path = xs_read(xsh, XBT_NULL, buf, NULL);
|
||||
if (path == NULL) {
|
||||
fprintf(logfile, "xs_read(%s): read error\n", buf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
pasprintf(&buf, "%s/%s", path, key);
|
||||
rc = xs_write(xsh, XBT_NULL, buf, value, strlen(value));
|
||||
if (rc) {
|
||||
fprintf(logfile, "xs_write(%s, %s): write error\n", buf, key);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
free(path);
|
||||
free(buf);
|
||||
return rc;
|
||||
}
|
||||
@@ -118,7 +118,7 @@ LEGACY_CFG_TO_XENAPI_CFG = reverse_dict(XENAPI_CFG_TO_LEGACY_CFG)
|
||||
# Platform configuration keys.
|
||||
XENAPI_PLATFORM_CFG = [ 'acpi', 'apic', 'boot', 'device_model', 'display',
|
||||
'fda', 'fdb', 'keymap', 'isa', 'localtime',
|
||||
'nographic', 'pae', 'serial', 'sdl',
|
||||
'nographic', 'pae', 'rtc_timeoffset', 'serial', 'sdl',
|
||||
'soundhw','stdvga', 'usb', 'usbdevice', 'vnc',
|
||||
'vncconsole', 'vncdisplay', 'vnclisten',
|
||||
'vncpasswd', 'vncunused', 'xauthority']
|
||||
@@ -203,6 +203,7 @@ LEGACY_CFG_TYPES = {
|
||||
'on_xend_stop': str,
|
||||
'on_xend_start': str,
|
||||
'online_vcpus': int,
|
||||
'rtc/timeoffset': str,
|
||||
}
|
||||
|
||||
# Values that should be stored in xenstore's /vm/<uuid> that is used
|
||||
|
||||
@@ -859,7 +859,8 @@ class XendDomainInfo:
|
||||
# Check whether values in the configuration have
|
||||
# changed in Xenstore.
|
||||
|
||||
cfg_vm = ['name', 'on_poweroff', 'on_reboot', 'on_crash']
|
||||
cfg_vm = ['name', 'on_poweroff', 'on_reboot', 'on_crash',
|
||||
'rtc/timeoffset']
|
||||
|
||||
vm_details = self._readVMDetails([(k,XendConfig.LEGACY_CFG_TYPES[k])
|
||||
for k in cfg_vm])
|
||||
@@ -888,6 +889,11 @@ class XendDomainInfo:
|
||||
self.info.update_with_image_sxp(sxp.from_string(image_sxp))
|
||||
changed = True
|
||||
|
||||
# Check if the rtc offset has changes
|
||||
if vm_details.get("rtc/timeoffset", 0) != self.info["platform"].get("rtc_timeoffset", 0):
|
||||
self.info["platform"]["rtc_timeoffset"] = vm_details.get("rtc/timeoffset", 0)
|
||||
changed = True
|
||||
|
||||
if changed:
|
||||
# Update the domain section of the store, as this contains some
|
||||
# parameters derived from the VM configuration.
|
||||
|
||||
@@ -256,9 +256,12 @@ class HVMImageHandler(ImageHandler):
|
||||
self.xauthority = vmConfig['platform'].get('xauthority')
|
||||
self.vncconsole = vmConfig['platform'].get('vncconsole')
|
||||
|
||||
rtc_timeoffset = vmConfig['platform'].get('rtc_timeoffset')
|
||||
|
||||
self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)),
|
||||
("image/device-model", self.device_model),
|
||||
("image/display", self.display))
|
||||
self.vm.storeVm(("rtc/timeoffset", rtc_timeoffset))
|
||||
|
||||
self.pid = None
|
||||
|
||||
|
||||
@@ -186,6 +186,10 @@ gopts.var('cpus', val='CPUS',
|
||||
fn=set_value, default=None,
|
||||
use="CPUS to run the domain on.")
|
||||
|
||||
gopts.var('rtc_timeoffset', val='RTC_TIMEOFFSET',
|
||||
fn=set_value, default="0",
|
||||
use="Set RTC offset.")
|
||||
|
||||
gopts.var('pae', val='PAE',
|
||||
fn=set_int, default=1,
|
||||
use="Disable or enable PAE of HVM domain.")
|
||||
@@ -717,7 +721,7 @@ def configure_hvm(config_image, vals):
|
||||
args = [ 'device_model', 'pae', 'vcpus', 'boot', 'fda', 'fdb',
|
||||
'localtime', 'serial', 'stdvga', 'isa', 'nographic', 'soundhw',
|
||||
'vnc', 'vncdisplay', 'vncunused', 'vncconsole', 'vnclisten',
|
||||
'sdl', 'display', 'xauthority',
|
||||
'sdl', 'display', 'xauthority', 'rtc_timeoffset',
|
||||
'acpi', 'apic', 'usb', 'usbdevice', 'keymap' ]
|
||||
for a in args:
|
||||
if a in vals.__dict__ and vals.__dict__[a] is not None:
|
||||
|
||||
@@ -155,28 +155,13 @@ static inline void hvm_mmio_access(struct vcpu *v,
|
||||
}
|
||||
}
|
||||
|
||||
int hvm_buffered_io_intercept(ioreq_t *p)
|
||||
int hvm_buffered_io_send(ioreq_t *p)
|
||||
{
|
||||
struct vcpu *v = current;
|
||||
spinlock_t *buffered_io_lock;
|
||||
buffered_iopage_t *buffered_iopage =
|
||||
(buffered_iopage_t *)(v->domain->arch.hvm_domain.buffered_io_va);
|
||||
unsigned long tmp_write_pointer = 0;
|
||||
int i;
|
||||
|
||||
/* ignore READ ioreq_t! */
|
||||
if ( p->dir == IOREQ_READ )
|
||||
return 0;
|
||||
|
||||
for ( i = 0; i < HVM_BUFFERED_IO_RANGE_NR; i++ ) {
|
||||
if ( p->addr >= hvm_buffered_io_ranges[i]->start_addr &&
|
||||
p->addr + p->size - 1 < hvm_buffered_io_ranges[i]->start_addr +
|
||||
hvm_buffered_io_ranges[i]->length )
|
||||
break;
|
||||
}
|
||||
|
||||
if ( i == HVM_BUFFERED_IO_RANGE_NR )
|
||||
return 0;
|
||||
|
||||
buffered_io_lock = &v->domain->arch.hvm_domain.buffered_io_lock;
|
||||
spin_lock(buffered_io_lock);
|
||||
@@ -205,6 +190,27 @@ int hvm_buffered_io_intercept(ioreq_t *p)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int hvm_buffered_io_intercept(ioreq_t *p)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* ignore READ ioreq_t! */
|
||||
if ( p->dir == IOREQ_READ )
|
||||
return 0;
|
||||
|
||||
for ( i = 0; i < HVM_BUFFERED_IO_RANGE_NR; i++ ) {
|
||||
if ( p->addr >= hvm_buffered_io_ranges[i]->start_addr &&
|
||||
p->addr + p->size - 1 < hvm_buffered_io_ranges[i]->start_addr +
|
||||
hvm_buffered_io_ranges[i]->length )
|
||||
break;
|
||||
}
|
||||
|
||||
if ( i == HVM_BUFFERED_IO_RANGE_NR )
|
||||
return 0;
|
||||
|
||||
return hvm_buffered_io_send(p);
|
||||
}
|
||||
|
||||
int hvm_mmio_intercept(ioreq_t *p)
|
||||
{
|
||||
struct vcpu *v = current;
|
||||
|
||||
@@ -921,6 +921,26 @@ static void send_mmio_req(unsigned char type, unsigned long gpa,
|
||||
hvm_send_assist_req(v);
|
||||
}
|
||||
|
||||
void send_timeoffset_req(unsigned long timeoff)
|
||||
{
|
||||
ioreq_t p[1];
|
||||
|
||||
if ( timeoff == 0 )
|
||||
return;
|
||||
|
||||
memset(p, 0, sizeof(*p));
|
||||
|
||||
p->type = IOREQ_TYPE_TIMEOFFSET;
|
||||
p->size = 4;
|
||||
p->dir = IOREQ_WRITE;
|
||||
p->data = timeoff;
|
||||
|
||||
p->state = STATE_IOREQ_READY;
|
||||
|
||||
if ( !hvm_buffered_io_send(p) )
|
||||
printk("Unsuccessful timeoffset update\n");
|
||||
}
|
||||
|
||||
static void mmio_operands(int type, unsigned long gpa,
|
||||
struct hvm_io_op *mmio_op,
|
||||
unsigned char op_size)
|
||||
|
||||
@@ -157,6 +157,10 @@ static inline int from_bcd(RTCState *s, int a)
|
||||
static void rtc_set_time(RTCState *s)
|
||||
{
|
||||
struct tm *tm = &s->current_tm;
|
||||
unsigned long before, after; /* XXX s_time_t */
|
||||
|
||||
before = mktime(tm->tm_year, tm->tm_mon, tm->tm_mday,
|
||||
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||
|
||||
tm->tm_sec = from_bcd(s, s->hw.cmos_data[RTC_SECONDS]);
|
||||
tm->tm_min = from_bcd(s, s->hw.cmos_data[RTC_MINUTES]);
|
||||
@@ -168,6 +172,10 @@ static void rtc_set_time(RTCState *s)
|
||||
tm->tm_mday = from_bcd(s, s->hw.cmos_data[RTC_DAY_OF_MONTH]);
|
||||
tm->tm_mon = from_bcd(s, s->hw.cmos_data[RTC_MONTH]) - 1;
|
||||
tm->tm_year = from_bcd(s, s->hw.cmos_data[RTC_YEAR]) + 100;
|
||||
|
||||
after = mktime(tm->tm_year, tm->tm_mon, tm->tm_mday,
|
||||
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||
send_timeoffset_req(after - before);
|
||||
}
|
||||
|
||||
static void rtc_copy_date(RTCState *s)
|
||||
|
||||
+1
-1
@@ -573,7 +573,7 @@ static void init_platform_timer(void)
|
||||
* machines were long is 32-bit! (However, as time_t is signed, we
|
||||
* will already get problems at other places on 2038-01-19 03:14:08)
|
||||
*/
|
||||
static inline unsigned long
|
||||
unsigned long
|
||||
mktime (unsigned int year, unsigned int mon,
|
||||
unsigned int day, unsigned int hour,
|
||||
unsigned int min, unsigned int sec)
|
||||
|
||||
@@ -127,6 +127,7 @@ static inline int hvm_portio_intercept(ioreq_t *p)
|
||||
}
|
||||
|
||||
extern int hvm_mmio_intercept(ioreq_t *p);
|
||||
extern int hvm_buffered_io_send(ioreq_t *p);
|
||||
extern int hvm_buffered_io_intercept(ioreq_t *p);
|
||||
|
||||
static inline int register_portio_handler(
|
||||
@@ -145,6 +146,7 @@ static inline int irq_masked(unsigned long eflags)
|
||||
|
||||
extern void send_pio_req(unsigned long port, unsigned long count, int size,
|
||||
paddr_t value, int dir, int df, int value_is_ptr);
|
||||
void send_timeoffset_req(unsigned long timeoff);
|
||||
extern void handle_mmio(unsigned long gpa);
|
||||
extern void hvm_interrupt_post(struct vcpu *v, int vector, int type);
|
||||
extern void hvm_io_assist(struct vcpu *v);
|
||||
|
||||
@@ -16,4 +16,9 @@ static inline cycles_t get_cycles(void)
|
||||
return c;
|
||||
}
|
||||
|
||||
unsigned long
|
||||
mktime (unsigned int year, unsigned int mon,
|
||||
unsigned int day, unsigned int hour,
|
||||
unsigned int min, unsigned int sec);
|
||||
|
||||
#endif /* __X86_TIME_H__ */
|
||||
@@ -39,6 +39,7 @@
|
||||
#define IOREQ_TYPE_XOR 4
|
||||
#define IOREQ_TYPE_XCHG 5
|
||||
#define IOREQ_TYPE_ADD 6
|
||||
#define IOREQ_TYPE_TIMEOFFSET 7
|
||||
|
||||
/*
|
||||
* VMExit dispatcher should cooperate with instruction decoder to
|
||||
|
||||