[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [optional PATCH v3 01/11] hvm/hpet: Add manual unit test code.
Add the code at tools/tests/vhpet. See comment in tools/tests/vhpet/main.c for details on running either in a xen source tree or elsewhere. A basic in source tree usage is: make -C tools/tests/vhpet run Does repro the bug: ..MP-BIOS bug: 8254 timer not connected to IO-APIC The make file includes coping hpet.c and hpet.h from the source tree. hpet.c is then modifed to remove all include file and add the emul.h include file. The manual test code has only a few automatic checks that output messages to stderr: 1) Possible ..MP-BIOS bug: 8254 timer... if 1st period is not <= the expected value 2) hpet_set_mode(%ld): T%d Error: Set ... if read of comparator != write of comparator in 3) hpet_check_stopped(%ld): T%d Error: Set ... if read != write 4) main(%ld): With clock stopped mc64 changed: ... if hpet_save returns different master clock values when called more then once. It also generates a lot of output, which is why the sugested way to use includes a redirect of stdout to a file. Signed-off-by: Don Slutz <dslutz@xxxxxxxxxxx> --- v3: Add Makefile. Better commit message. Make it optional. Add hpet_check_stopped() testing. Adjust print_error messages. tools/tests/vhpet/.gitignore | 4 + tools/tests/vhpet/Makefile | 28 ++ tools/tests/vhpet/emul.h | 416 +++++++++++++++++++++++ tools/tests/vhpet/main.c | 768 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1216 insertions(+) create mode 100644 tools/tests/vhpet/.gitignore create mode 100644 tools/tests/vhpet/Makefile create mode 100644 tools/tests/vhpet/emul.h create mode 100644 tools/tests/vhpet/main.c diff --git a/tools/tests/vhpet/.gitignore b/tools/tests/vhpet/.gitignore new file mode 100644 index 0000000..4cefa62 --- /dev/null +++ b/tools/tests/vhpet/.gitignore @@ -0,0 +1,4 @@ +test_vhpet +test_vhpet.out +hpet.h +hpet.c diff --git a/tools/tests/vhpet/Makefile b/tools/tests/vhpet/Makefile new file mode 100644 index 0000000..4a21355 --- /dev/null +++ b/tools/tests/vhpet/Makefile @@ -0,0 +1,28 @@ + +XEN_ROOT=$(CURDIR)/../../.. +include $(XEN_ROOT)/tools/Rules.mk + +TARGET := test_vhpet + +.PHONY: all +all: $(TARGET) + +.PHONY: run +run: $(TARGET) + ./$(TARGET) > $(TARGET).out + +$(TARGET): hpet.c main.c hpet.h emul.h Makefile + $(HOSTCC) -g -o $@ hpet.c main.c + +.PHONY: clean +clean: + rm -rf $(TARGET) $(TARGET).out *.o *~ core* hpet.h hpet.c + +.PHONY: install +install: + +hpet.h: $(XEN_ROOT)/xen/include/asm-x86/hpet.h + cp $(XEN_ROOT)/xen/include/asm-x86/hpet.h . + +hpet.c: $(XEN_ROOT)/xen/arch/x86/hvm/hpet.c + sed -e "/#include/d" -e "1i#include \"emul.h\"\n" <$(XEN_ROOT)/xen/arch/x86/hvm/hpet.c >hpet.c diff --git a/tools/tests/vhpet/emul.h b/tools/tests/vhpet/emul.h new file mode 100644 index 0000000..09e4611 --- /dev/null +++ b/tools/tests/vhpet/emul.h @@ -0,0 +1,416 @@ +/* + * Xen emulation for hpet + * + * Copyright (C) 2014 Verizon Corporation + * + * This file is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License Version 2 (GPLv2) + * as published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <inttypes.h> +#include <string.h> + +#define PCI_HAVE_64BIT_ADDRESS +#include <pci/types.h> + +#include "hpet.h" + +#define NR_CPUS 8 + +typedef int64_t s_time_t; +typedef int spinlock_t; +typedef int bool_t; + +#define BITS_PER_LONG __WORDSIZE +#define BITS_TO_LONGS(bits) \ + (((bits) + BITS_PER_LONG - 1) / BITS_PER_LONG) +#define DECLARE_BITMAP(name, bits) \ + unsigned long name[BITS_TO_LONGS(bits)] +typedef struct cpumask +{ + DECLARE_BITMAP(bits, NR_CPUS); +} cpumask_t; +typedef cpumask_t *cpumask_var_t; +struct msi_desc +{ + struct msi_attrib + { + u8 type : 5; /* {0: unused, 5h:MSI, 11h:MSI-X} */ + u8 maskbit : 1; /* mask-pending bit supported ? */ + u8 masked : 1; + u8 is_64 : 1; /* Address size: 0=32bit 1=64bit */ + u8 pos; /* Location of the msi capability */ + u16 entry_nr; /* specific enabled entry */ + } msi_attrib; +}; + +struct msi_msg +{ + u32 address_lo; /* low 32 bits of msi message address */ + u32 address_hi; /* high 32 bits of msi message address */ + u32 data; /* 16 bits of msi message data */ + u32 dest32; /* used when Interrupt Remapping with EIM is enabled */ +}; + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#endif + +#define X86EMUL_OKAY 100 +#define EINVAL 101 + +#define DBG_LEVEL_PIT 200 + +#define TRC_HW_VCHIP_HPET_START_TIMER 300 +#define TRC_HW_VCHIP_HPET_STOP_TIMER 301 +#define TRC_HW_VCHIP_PIT_STOP_TIMER 302 + +#define TRC_HVM_VCHIP_HPET_START_TIMER 400 +#define TRC_HVM_VCHIP_HPET_STOP_TIMER 401 +#define TRC_HVM_VCHIP_PIT_STOP_TIMER 402 + +#define TRC_HVM_EMUL_HPET_START_TIMER 400 +#define TRC_HVM_EMUL_HPET_STOP_TIMER 401 +#define TRC_HVM_EMUL_PIT_STOP_TIMER 402 + +#define __read_mostly +#define __initdata +#define __init +#define __maybe_unused +#define __cacheline_aligned +#define boolean_param(a, b) +#define fix_to_virt(a) a +#define xmalloc_array(_type, _num) (void *)(_type)(_num) +#define DEFINE_PER_CPU(_type, _name) _type _name + +#define KERN_DEBUG +#define KERN_INFO + +#define XENLOG_WARNING +#define XENLOG_INFO +#define XENLOG_ERR +#define XENLOG_GUEST + +#define MSI_TYPE_UNKNOWN 0 +#define MSI_TYPE_HPET 1 +#define MSI_TYPE_IOMMU 2 + +#define STIME_MAX ((s_time_t)((uint64_t)~0ull>>1)) + +/* Low-latency softirqs come first in the following list. */ +enum +{ + TIMER_SOFTIRQ = 0, + SCHEDULE_SOFTIRQ, + NEW_TLBFLUSH_CLOCK_PERIOD_SOFTIRQ, + RCU_SOFTIRQ, + TASKLET_SOFTIRQ, + NR_COMMON_SOFTIRQS +}; +/* + * ..and if you can't take the strict + * types, you can specify one yourself. + * + * Or not use min/max at all, of course. + */ +#define min_t(type, x, y) \ + ({ type __x = (x); type __y = (y); __x < __y ? __x : __y; }) +#define max_t(type, x, y) \ + ({ type __x = (x); type __y = (y); __x > __y ? __x : __y; }) +#define offsetof(t, m) ((unsigned long )&((t *)0)->m) +#define container_of(ptr, type, member) ({ \ + typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) ); }) + +struct domain; + +struct vcpu +{ + int vcpu_id; + struct domain *domain; +}; + +typedef void time_cb(struct vcpu *v, void *opaque); + +struct periodic_time +{ +#define PTSRC_isa 1 /* ISA time source */ +#define PTSRC_lapic 2 /* LAPIC time source */ + u8 source; /* PTSRC_ */ +}; + +void destroy_periodic_time(struct periodic_time *pt); +void create_periodic_time( + struct vcpu *v, struct periodic_time *pt, uint64_t delta, + uint64_t period, uint8_t irq, time_cb *cb, void *data); + +#define HPET_TIMER_NUM 3 + +struct hpet_registers +{ + /* Memory-mapped, software visible registers */ + uint64_t capability; /* capabilities */ + uint64_t config; /* configuration */ + uint64_t isr; /* interrupt status reg */ + uint64_t mc64; /* main counter */ + struct /* timers */ + { + uint64_t config; /* configuration/cap */ + uint64_t cmp; /* comparator */ + uint64_t fsb; /* FSB route, not supported now */ + } timers[HPET_TIMER_NUM]; + + /* Hidden register state */ + uint64_t period[HPET_TIMER_NUM]; /* Last value written to comparator */ + uint64_t comparator64[HPET_TIMER_NUM]; /* 64 bit running comparator */ + uint64_t offset64[HPET_TIMER_NUM]; /* offset so comparator calc "works" */ + uint64_t first_mc64[HPET_TIMER_NUM]; /* 1st interval main counter */ + bool_t first_enabled[HPET_TIMER_NUM]; /* In 1st interval */ +}; + +typedef struct HPETState +{ + struct hpet_registers hpet; + uint64_t stime_freq; + uint64_t hpet_to_ns_scale; /* hpet ticks to ns (multiplied by 2^10) */ + uint64_t hpet_to_ns_limit; /* max hpet ticks convertable to ns */ + uint64_t mc_offset; + struct periodic_time pt[HPET_TIMER_NUM]; + spinlock_t lock; +} HPETState; + +typedef struct PITState +{ + struct periodic_time pt0; + spinlock_t lock; +} PITState; + + +struct pl_time /* platform time */ +{ + struct HPETState vhpet; + /* guest_time = Xen sys time + stime_offset */ + int64_t stime_offset; + /* Ensures monotonicity in appropriate timer modes. */ + uint64_t last_guest_time; + spinlock_t pl_time_lock; +}; + +#define HVM_PARAM_HPET_ENABLED 11 + +struct hvm_domain +{ + struct pl_time pl_time; + long params[20]; +}; + +struct arch_domain +{ + struct hvm_domain hvm_domain; + struct PITState vpit; +}; + +struct domain +{ + int domain_id; + struct arch_domain arch; + struct vcpu *vcpu[NR_CPUS]; +}; + +typedef int (*hvm_mmio_read_t)(struct vcpu *v, + unsigned long addr, + unsigned long length, + unsigned long *val); +typedef int (*hvm_mmio_write_t)(struct vcpu *v, + unsigned long addr, + unsigned long length, + unsigned long val); +typedef int (*hvm_mmio_check_t)(struct vcpu *v, unsigned long addr); + + +struct hvm_mmio_handler +{ + hvm_mmio_check_t check_handler; + hvm_mmio_read_t read_handler; + hvm_mmio_write_t write_handler; +}; + +/* Marshalling and unmarshalling uses a buffer with size and cursor. */ +typedef struct hvm_domain_context +{ + uint32_t cur; + uint32_t size; + uint8_t *data; +} hvm_domain_context_t; + +int current_domain_id(void); +#define dprintk(_l, _f, _a...) \ + printk(_l "%s:%d: " _f, __FILE__ , __LINE__ , ## _a ) +#define gdprintk(_l, _f, _a...) \ + printk(XENLOG_GUEST _l "%s:%d:d%d " _f, __FILE__, \ + __LINE__, current_domain_id() , ## _a ) +struct vcpu *get_current(); +#define current get_current() + +#define HVM_SAVE_CODE(_x) HVM_SAVE_CODE_##_x +#define HVM_SAVE_LENGTH(_x) HVM_SAVE_LENGTH_##_x + +/* + * HPET + */ + +uint64_t hvm_get_guest_time(struct vcpu *v); + +#define HPET_TIMER_NUM 3 /* 3 timers supported now */ +struct hvm_hw_hpet +{ + /* Memory-mapped, software visible registers */ + uint64_t capability; /* capabilities */ + uint64_t res0; /* reserved */ + uint64_t config; /* configuration */ + uint64_t res1; /* reserved */ + uint64_t isr; /* interrupt status reg */ + uint64_t res2[25]; /* reserved */ + uint64_t mc64; /* main counter */ + uint64_t res3; /* reserved */ + struct /* timers */ + { + uint64_t config; /* configuration/cap */ + uint64_t cmp; /* comparator */ + uint64_t fsb; /* FSB route, not supported now */ + uint64_t res4; /* reserved */ + } timers[HPET_TIMER_NUM]; + uint64_t res5[4 * (24 - HPET_TIMER_NUM)]; /* reserved, up to 0x3ff */ + + /* Hidden register state */ + uint64_t period[HPET_TIMER_NUM]; /* Last value written to comparator */ +}; + +typedef int (*hvm_save_handler)(struct domain *d, + hvm_domain_context_t *h); +typedef int (*hvm_load_handler)(struct domain *d, + hvm_domain_context_t *h); + +struct hvm_save_descriptor +{ + uint16_t typecode; /* Used to demux the various types below */ + uint16_t instance; /* Further demux within a type */ + uint32_t length; /* In bytes, *not* including this descriptor */ +}; + +void hvm_register_savevm(uint16_t typecode, + const char *name, + hvm_save_handler save_state, + hvm_load_handler load_state, + size_t size, int kind); + +#define HVMSR_PER_DOM 1 + +#define HVM_REGISTER_SAVE_RESTORE(_x, _save, _load, _num, _k) \ + int __init __hvm_register_##_x##_save_and_restore(void) \ + { \ + hvm_register_savevm(HVM_SAVE_CODE(_x), \ + #_x, \ + &_save, \ + &_load, \ + (_num) * (HVM_SAVE_LENGTH(_x) \ + + sizeof(struct hvm_save_descriptor)), \ + _k); \ + return 0; \ + } \ + +#define HVM_SAVE_CODE_HPET 0 +#define HVM_SAVE_LENGTH_HPET sizeof(struct hvm_hw_hpet) + +#define printk printf + +#define spin_lock(a) +#define spin_unlock(a) +#define spin_lock_init(a) +#define spin_is_locked(a) 1 +#define ASSERT(a) + +#define ADDR (*(volatile long *) addr) + +static inline void __set_bit(int nr, volatile void *addr) +{ + asm volatile( + "btsl %1,%0" + : "=m"(ADDR) + : "Ir"(nr), "m"(ADDR) : "memory"); +} + +static inline void __clear_bit(int nr, volatile void *addr) +{ + asm volatile( + "btrl %1,%0" + : "=m"(ADDR) + : "Ir"(nr), "m"(ADDR) : "memory"); +} + +static inline unsigned int find_first_set_bit(unsigned long word) +{ + asm("bsf %1,%0" : "=r"(word) : "r"(word)); + return (unsigned int)word; +} + +#define HVM_DBG_LOG(level, _f, _a...) \ + do { \ + printf("[HVM:%d.%d] <%s> " _f "\n", \ + current->domain->domain_id, current->vcpu_id, __func__, \ + ## _a); \ + } while ( 0 ) + +void __domain_crash(struct domain *d); +#define domain_crash(d) do { \ + printf("domain_crash called from %s:%d\n", __FILE__, __LINE__); \ + __domain_crash(d); \ + } while ( 0 ) + +#define MICROSECS(_us) ((s_time_t)((_us) * 1000ULL)) + +#define pt_global_vcpu_target(d) \ + ((d)->vcpu ? (d)->vcpu[0] : NULL) + +#define TRACE_0D(a) +#define TRACE_1D(a, b) +#define TRACE_2D(a, b, c) +#define TRACE_3D(a, b, c, d) +#define TRACE_4D(a, b, c, d, e) +#define TRACE_5D(a, b, c, d, e, f) +#define TRACE_6D(a, b, c, d, e, f, g) + +#define TRC_PAR_LONG(par) ((par)&0xFFFFFFFF),((par)>>32) + +#define TRACE_2_LONG_2D(_e, d1, d2, ...) \ + TRACE_4D(_e, d1, d2) +#define TRACE_2_LONG_3D(_e, d1, d2, d3, ...) \ + TRACE_5D(_e, d1, d2, d3) +#define TRACE_2_LONG_4D(_e, d1, d2, d3, d4, ...) \ + TRACE_6D(_e, d1, d2, d3, d4) + +/* debug */ + +extern int __read_mostly hpet_debug; +extern uint64_t __read_mostly hpet_force_diff; +extern uint64_t __read_mostly hpet_force_mc64; +extern uint64_t __read_mostly hpet_force_cmp; +extern uint64_t __read_mostly hpet_force_period; + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/tests/vhpet/main.c b/tools/tests/vhpet/main.c new file mode 100644 index 0000000..e24f038 --- /dev/null +++ b/tools/tests/vhpet/main.c @@ -0,0 +1,768 @@ +/* + * Xen emulation for hpet + * + * Copyright (C) 2014 Verizon Corporation + * + * This file is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License Version 2 (GPLv2) + * as published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. <http://www.gnu.org/licenses/>. + */ + +/* + * http://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/software-developers-hpet-spec-1-0a.pdf + * + * xen_source is a directory that has all xen source below it. + * + * Usage: + * + + + xen_source=../../.. + sed -e "/#include/d" -e "1i#include \"emul.h\"\n" <$xen_source/xen/arch/x86/hvm/hpet.c >hpet.c + cp $xen_source/xen/include/asm-x86/hpet.h . + + gcc -g -o test_vhpet hpet.c main.c + ./test_vhpet >test_vhpet.out + + * + * + * This is almost the same as + * + + make run + + * + * Or + * + * make -C tools/tests/vhpet run + * + * From a xen source tree. The differance + * is that you need to be in a xen source tree + * and normal make rules apply. + * + */ + +#define FORCE_THOUSANDS_SEP + +#include <locale.h> +#include <langinfo.h> +#include <stdarg.h> +#include "emul.h" +#include "hpet.h" + +#define S_TO_NS 1000000000ULL /* 1s = 10^9 ns */ + +#define START_MC64 0x108a8 + +static int hpet_mult = 1; +static int hpet_add; +static int hvm_clock_cost = 1234567; +static int tick_count = 1; +static int debug = 3; + +static int skip_load; + +static char *global_thousep; + +extern const struct hvm_mmio_handler hpet_mmio_handler; + +struct domain dom1; +struct vcpu vcpu0; +struct hvm_hw_hpet hpet_save; + + +uint64_t hvm_guest_time; + +static struct +{ + hvm_save_handler save; + hvm_load_handler load; + const char *name; + size_t size; + int kind; +} hvm_sr_handlers[3] = {{NULL, NULL, "<?>"},}; + +static uint64_t new_guest_time[] = { + 0x20, + 0x2a840, + 0xf4200, + 0x10000000000ULL, + 0x0fffffffffefff00ULL, + 0x20, + 0xffffffff00000000ULL, + 0x20, +}; + +static int print_error(const char *fmt, ...) +{ + va_list args; + int i = 0; + + va_start(args, fmt); + if ( debug & 0x0001 ) + i = vfprintf(stdout, fmt, args); + va_end(args); + va_start(args, fmt); + if ( debug & 0x0002 ) + i = vfprintf(stderr, fmt, args); + va_end(args); + return i; +} + + +int current_domain_id(void) +{ + return current->domain->domain_id; +} + +struct vcpu *get_current() +{ + return &vcpu0; +} + +void __domain_crash(struct domain *d) +{ + exit(42); +} + +uint64_t hvm_get_guest_time(struct vcpu *v) +{ + uint64_t ret = hvm_guest_time; + + hvm_guest_time += hvm_clock_cost; + return ret; +} + +int _hvm_init_entry(struct hvm_domain_context *h, + uint16_t tc, uint16_t inst, uint32_t len) +{ + h->cur = 0; + h->size = sizeof(hpet_save); + h->data = (void *)&hpet_save; + + return 0; +} + +int _hvm_check_entry(struct hvm_domain_context *h, + uint16_t type, uint32_t len, bool_t strict_length) +{ + h->cur = 0; + h->size = sizeof(hpet_save); + h->data = (void *)&hpet_save; + + return 0; +} + +void __init hvm_register_savevm(uint16_t typecode, + const char *name, + hvm_save_handler save_state, + hvm_load_handler load_state, + size_t size, int kind) +{ + hvm_sr_handlers[typecode].save = save_state; + hvm_sr_handlers[typecode].load = load_state; + hvm_sr_handlers[typecode].name = name; + hvm_sr_handlers[typecode].size = size; + hvm_sr_handlers[typecode].kind = kind; +} + +int do_save(uint16_t typecode, struct domain *d, hvm_domain_context_t *h) +{ + return hvm_sr_handlers[typecode].save(d, h); +} + +int do_load(uint16_t typecode, struct domain *d, hvm_domain_context_t *h) +{ + if (skip_load & 0x1) + { + printf("skip_load=%#x\n", skip_load); + } + else + { + printf("do_load\n"); + return hvm_sr_handlers[typecode].load(d, h); + } +} + +static void dump_hpet(void) +{ + int i; + unsigned long long conf; + struct hvm_hw_hpet h = hpet_save; + conf = (unsigned long long) h.config; + printf(" HPET: capability %#llx config %#llx(%s%s)\n", + (unsigned long long) h.capability, + conf, + conf & HPET_CFG_ENABLE ? "E" : "", + conf & HPET_CFG_LEGACY ? "L" : ""); + printf(" isr %#llx counter %#llx(%'lld)\n", + (unsigned long long) h.isr, + (unsigned long long) h.mc64, + (unsigned long long) h.mc64); + for (i = 0; i < HPET_TIMER_NUM; i++) + { + conf = (unsigned long long) h.timers[i].config; + printf(" timer%i config %#llx(%s%s%s) cmp %#llx(%'lld)\n", i, + conf, + conf & HPET_TN_ENABLE ? "E" : "", + conf & HPET_TN_PERIODIC ? "P" : "", + conf & HPET_TN_32BIT ? "32" : "", + (unsigned long long) h.timers[i].cmp, + (unsigned long long) h.timers[i].cmp); + printf(" timer%i period %#llx(%'lld) fsb %#llx\n", i, + (unsigned long long) h.period[i], + (unsigned long long) h.period[i], + (unsigned long long) h.timers[i].fsb); + } +} + +void pit_stop_channel0_irq(PITState *pit) +{ + printf("pit_stop_channel0_irq: pit=%p\n", pit); + + TRACE_1D(TRC_HVM_VCHIP_PIT_STOP_TIMER, get_cycles()); + spin_lock(&pit->lock); + destroy_periodic_time(&pit->pt0); + spin_unlock(&pit->lock); +} + +void destroy_periodic_time(struct periodic_time *pt) +{ + int idx = ((long)pt) & 0x7; + + printf("destroy_periodic_time: pt=%d\n", idx); +} + +void create_periodic_time(struct vcpu *v, struct periodic_time *pt, + uint64_t delta, uint64_t period, uint8_t irq, + time_cb *cb, void *data) +{ + int idx = ((long)pt) & 0x7; + + if ( debug & 0x0010 ) + { + int i; + + printf("create_periodic_time: " + "mc64=%#lx(%'ld) mc_offset=%#lx(%'ld)\n", + dom1.arch.hvm_domain.pl_time.vhpet.hpet.mc64, + dom1.arch.hvm_domain.pl_time.vhpet.hpet.mc64, + dom1.arch.hvm_domain.pl_time.vhpet.mc_offset, + dom1.arch.hvm_domain.pl_time.vhpet.mc_offset); + for (i = 0; i < 3; i++) + { + printf(" " + "[%d] cmp64=%#lx(%'ld) cmp=%#lx(%'ld)\n", i, + dom1.arch.hvm_domain.pl_time.vhpet.hpet.comparator64[i], + dom1.arch.hvm_domain.pl_time.vhpet.hpet.comparator64[i], + dom1.arch.hvm_domain.pl_time.vhpet.hpet.timers[i].cmp, + dom1.arch.hvm_domain.pl_time.vhpet.hpet.timers[i].cmp); + } + } + if ( period ) + { + printf("create_periodic_time: pt=%d delta=%'"PRId64" period=%'"PRIu64 + " - %'"PRIu64".%02d Hz irq=%d\n", + idx, delta, period, (uint64_t)(S_TO_NS / period), + (int)((S_TO_NS / (period / 100ULL)) % 100), irq); + /* +160 is for hpet_tick_to_ns() not simple. */ + if ( delta > (period * (hpet_mult + hpet_add + 160)) ) + print_error("%s(%ld): Possible ..MP-BIOS bug: 8254 timer...: delta=%'"PRId64 + " period=%'"PRIu64"\n", __func__, __LINE__, + delta, period); + } + else + printf("create_periodic_time: pt=%d delta=%'"PRId64 + " period=%'"PRIu64" irq=%d\n", + idx, delta, period, irq); +} + +void udelay(int w) +{ +} + +unsigned int hpet_readl(unsigned long a) +{ + unsigned long ret = 0; + hpet_mmio_handler.read_handler(current, a, 4, &ret); + return ret; +} + +void hpet_writel(unsigned long d, unsigned long a) +{ + hpet_mmio_handler.write_handler(current, a, 4, d); + return; +} + +static void _hpet_print_config(const char *function, int line) +{ + u32 i, timers, l, h; + printk(KERN_INFO "hpet: %s(%d):\n", function, line); + l = hpet_readl(HPET_ID); + h = hpet_readl(HPET_PERIOD); + timers = ((l & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT) + 1; + printk(KERN_INFO "hpet: ID: 0x%x, PERIOD: 0x%x\n", l, h); + l = hpet_readl(HPET_CFG); + h = hpet_readl(HPET_STATUS); + printk(KERN_INFO "hpet: CFG: 0x%x, STATUS: 0x%x\n", l, h); + l = hpet_readl(HPET_COUNTER); + h = hpet_readl(HPET_COUNTER + 4); + printk(KERN_INFO "hpet: COUNTER_l: 0x%x, COUNTER_h: 0x%x\n", l, h); + + for (i = 0; i < timers; i++) + { + l = hpet_readl(HPET_Tn_CFG(i)); + h = hpet_readl(HPET_Tn_CFG(i) + 4); + printk(KERN_INFO "hpet: T%d: CFG_l: 0x%x, CFG_h: 0x%x\n", + i, l, h); + l = hpet_readl(HPET_Tn_CMP(i)); + h = hpet_readl(HPET_Tn_CMP(i) + 4); + printk(KERN_INFO "hpet: T%d: CMP_l: 0x%x, CMP_h: 0x%x\n", + i, l, h); + l = hpet_readl(HPET_Tn_ROUTE(i)); + h = hpet_readl(HPET_Tn_ROUTE(i) + 4); + printk(KERN_INFO "hpet: T%d ROUTE_l: 0x%x, ROUTE_h: 0x%x\n", + i, l, h); + } +} + +#define hpet_print_config() \ + do { \ + _hpet_print_config(__func__, __LINE__); \ + } while ( 0 ) + +static void hpet_stop_counter(void) +{ + unsigned long cfg = hpet_readl(HPET_CFG); + cfg &= ~HPET_CFG_ENABLE; + hpet_writel(cfg, HPET_CFG); +} + +static void hpet_reset_counter(unsigned long low, unsigned long high) +{ + hpet_writel(low, HPET_COUNTER); + hpet_writel(high, HPET_COUNTER + 4); +} + +static void hpet_start_counter(void) +{ + unsigned long cfg = hpet_readl(HPET_CFG); + cfg |= HPET_CFG_ENABLE; + hpet_writel(cfg, HPET_CFG); +} + +static void hpet_restart_counter(void) +{ + hpet_stop_counter(); + hpet_reset_counter(0, 0); + hpet_start_counter(); +} + +static void hpet_set_mode(uint64_t delta, int timer) +{ + unsigned long cfg, cmp, cmp2, now; + + hpet_stop_counter(); + now = hpet_readl(HPET_COUNTER); + cmp = now + (unsigned long)(hpet_mult * delta) + hpet_add; + cfg = hpet_readl(HPET_Tn_CFG(timer)); + /* Make sure we use edge triggered interrupts */ + cfg &= ~HPET_TN_LEVEL; + cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC | + HPET_TN_SETVAL | HPET_TN_32BIT; + /* Mask to 32 bits just like the hardware */ + cmp = (uint32_t)cmp; + delta = (uint32_t)delta; + /* Do the config */ + hpet_writel(cfg, HPET_Tn_CFG(timer)); + hpet_writel(cmp, HPET_Tn_CMP(timer)); + printf("%s(%ld): HPET_TN_SETVAL cmp=%#lx(%'ld) timer=%d\n", + __func__, __LINE__, cmp, cmp, timer); + udelay(1); + /* + * HPET on AMD 81xx needs a second write (with HPET_TN_SETVAL + * cleared) to T0_CMP to set the period. The HPET_TN_SETVAL + * bit is automatically cleared after the first write. + * (See AMD-8111 HyperTransport I/O Hub Data Sheet, + * Publication # 24674) + */ + hpet_writel((unsigned long) delta, HPET_Tn_CMP(timer)); + printf("%s(%ld): period=%#lx(%'ld) timer=%d\n", __func__, __LINE__, + (unsigned long) delta, (unsigned long) delta, timer); + cmp2 = hpet_readl(HPET_Tn_CMP(timer)); + if ( cmp2 != cmp ) + print_error("%s(%ld): T%d Error: Set %#lx(%'ld) != %#lx(%'ld)\n", + __func__, __LINE__, timer, cmp, cmp, cmp2, cmp2); + + hpet_start_counter(); + hpet_print_config(); +} + + +hpet_check_stopped(uint64_t old_delta, int timer) +{ + unsigned long mc_low, mc_high, old_cmp, now; + unsigned long cfg, cmp, delta, cmp2, cmp3; + + if (skip_load & 0x2) + { + printf("Skip hpet_check_stopped. skip_load=%#x\n", skip_load); + return; + } + hpet_stop_counter(); + mc_low = hpet_readl(HPET_COUNTER); + mc_high = hpet_readl(HPET_COUNTER + 4); + old_cmp = hpet_readl(HPET_Tn_CMP(timer)); + + hpet_reset_counter(67752, 0); + cmp = 255252; + delta = 62500; + + now = hpet_readl(HPET_COUNTER); + if ( now != 67752 ) + print_error("%s(%ld): T%d Error: Set mc %#lx(%'ld) != %#lx(%'ld)\n", + __func__, __LINE__, timer, 67752, 67752, now, now); + cfg = hpet_readl(HPET_Tn_CFG(timer)); + cfg |= HPET_TN_SETVAL; + hpet_writel(cfg, HPET_Tn_CFG(timer)); + hpet_writel(cmp, HPET_Tn_CMP(timer)); + printf("%s(%ld): HPET_TN_SETVAL cmp=%#lx(%'ld) timer=%d\n", + __func__, __LINE__, cmp, cmp, timer); + cmp2 = hpet_readl(HPET_Tn_CMP(timer)); + if ( cmp2 != cmp ) + print_error("%s(%ld): T%d Error: Set cmp %#lx(%'ld) != %#lx(%'ld)\n", + __func__, __LINE__, timer, cmp, cmp, cmp2, cmp2); + + hpet_writel((unsigned long) delta, HPET_Tn_CMP(timer)); + printf("%s(%ld): period=%#lx(%'ld) timer=%d\n", __func__, __LINE__, + (unsigned long) delta, (unsigned long) delta, timer); + cmp3 = hpet_readl(HPET_Tn_CMP(timer)); + if ( cmp3 != cmp ) + print_error("%s(%ld): T%d Error: Set period, cmp %#lx(%'ld) != %#lx(%'ld)\n", + __func__, __LINE__, timer, cmp, cmp, cmp3, cmp3); + + if ( dom1.arch.hvm_domain.pl_time.vhpet.hpet.period[timer] != delta ) + printf("%s(%ld): T%d Warning: Set period %#lx(%'ld) != %#lx(%'ld)\n", + __func__, __LINE__, timer, delta, delta, + dom1.arch.hvm_domain.pl_time.vhpet.hpet.period[timer], + dom1.arch.hvm_domain.pl_time.vhpet.hpet.period[timer]); + + hpet_reset_counter(67752, 0); + cmp = 255252; + delta = 62500; + + now = hpet_readl(HPET_COUNTER); + if ( now != 67752 ) + print_error("%s(%ld): T%d Error: Set mc %#lx(%'ld) != %#lx(%'ld)\n", + __func__, __LINE__, timer, 67752, 67752, now, now); + cfg = hpet_readl(HPET_Tn_CFG(timer)); + cfg |= HPET_TN_SETVAL; + hpet_writel(cfg, HPET_Tn_CFG(timer)); + hpet_writel(cmp, HPET_Tn_CMP(timer)); + printf("%s(%ld): HPET_TN_SETVAL cmp=%#lx(%'ld) timer=%d\n", + __func__, __LINE__, cmp, cmp, timer); + cmp2 = hpet_readl(HPET_Tn_CMP(timer)); + if ( cmp2 != cmp ) + print_error("%s(%ld): T%d Error: Set cmp %#lx(%'ld) != %#lx(%'ld)\n", + __func__, __LINE__, timer, cmp, cmp, cmp2, cmp2); + + hpet_writel((unsigned long) delta, HPET_Tn_CMP(timer)); + printf("%s(%ld): period=%#lx(%'ld) timer=%d\n", __func__, __LINE__, + (unsigned long) delta, (unsigned long) delta, timer); + cmp3 = hpet_readl(HPET_Tn_CMP(timer)); + if ( cmp3 != cmp ) + print_error("%s(%ld): T%d Error: Set period, cmp %#lx(%'ld) != %#lx(%'ld)\n", + __func__, __LINE__, timer, cmp, cmp, cmp3, cmp3); + + if ( dom1.arch.hvm_domain.pl_time.vhpet.hpet.period[timer] != delta ) + printf("%s(%ld): T%d Warning: Set period %#lx(%'ld) != %#lx(%'ld)\n", + __func__, __LINE__, timer, delta, delta, + dom1.arch.hvm_domain.pl_time.vhpet.hpet.period[timer], + dom1.arch.hvm_domain.pl_time.vhpet.hpet.period[timer]); + + hpet_reset_counter(67700, 0); + + now = hpet_readl(HPET_COUNTER); + if ( now != 67700 ) + print_error("%s(%ld): T%d Error: Set mc %#lx(%'ld) != %#lx(%'ld)\n", + __func__, __LINE__, timer, 67752, 67752, now, now); + cmp2 = hpet_readl(HPET_Tn_CMP(timer)); + if ( cmp2 != cmp ) + print_error("%s(%ld): T%d Error: Set mc, cmp %#lx(%'ld) != %#lx(%'ld)\n", + __func__, __LINE__, timer, cmp, cmp, cmp2, cmp2); + + cmp3 = hpet_readl(HPET_Tn_CMP(timer)); + if ( cmp3 != cmp ) + print_error("%s(%ld): T%d Error: Set mc, cmp %#lx(%'ld) != %#lx(%'ld)\n", + __func__, __LINE__, timer, cmp, cmp, cmp3, cmp3); + + if ( dom1.arch.hvm_domain.pl_time.vhpet.hpet.period[timer] != delta ) + printf("%s(%ld): T%d Warning: Set mc, period %#lx(%'ld) != %#lx(%'ld)\n", + __func__, __LINE__, timer, delta, delta, + dom1.arch.hvm_domain.pl_time.vhpet.hpet.period[timer], + dom1.arch.hvm_domain.pl_time.vhpet.hpet.period[timer]); + + cmp = 67701; + + now = hpet_readl(HPET_COUNTER); + if ( now != 67700 ) + print_error("%s(%ld): T%d Error: Set cmp, mc %#lx(%'ld) != %#lx(%'ld)\n", + __func__, __LINE__, timer, 67752, 67752, now, now); + cfg = hpet_readl(HPET_Tn_CFG(timer)); + cfg |= HPET_TN_SETVAL; + hpet_writel(cfg, HPET_Tn_CFG(timer)); + hpet_writel(cmp, HPET_Tn_CMP(timer)); + printf("%s(%ld): HPET_TN_SETVAL cmp=%#lx(%'ld) timer=%d\n", + __func__, __LINE__, cmp, cmp, timer); + cmp2 = hpet_readl(HPET_Tn_CMP(timer)); + if ( cmp2 != cmp ) + print_error("%s(%ld): T%d Error: Set cmp, cmp %#lx(%'ld) != %#lx(%'ld)\n", + __func__, __LINE__, timer, cmp, cmp, cmp2, cmp2); + + cmp3 = hpet_readl(HPET_Tn_CMP(timer)); + if ( cmp3 != cmp ) + print_error("%s(%ld): T%d Error: Set cmp, cmp %#lx(%'ld) != %#lx(%'ld)\n", + __func__, __LINE__, timer, cmp, cmp, cmp3, cmp3); + + if ( dom1.arch.hvm_domain.pl_time.vhpet.hpet.period[timer] != delta ) + printf("%s(%ld): T%d Warning: Set cmp, period %#lx(%'ld) != %#lx(%'ld)\n", + __func__, __LINE__, timer, delta, delta, + dom1.arch.hvm_domain.pl_time.vhpet.hpet.period[timer], + dom1.arch.hvm_domain.pl_time.vhpet.hpet.period[timer]); + + delta = 500; + + now = hpet_readl(HPET_COUNTER); + if ( now != 67700 ) + print_error("%s(%ld): T%d Error: Set period, mc %#lx(%'ld) != %#lx(%'ld)\n", + __func__, __LINE__, timer, 67752, 67752, now, now); + cmp2 = hpet_readl(HPET_Tn_CMP(timer)); + if ( cmp2 != cmp ) + print_error("%s(%ld): T%d Error: Set period, cmp %#lx(%'ld) != %#lx(%'ld)\n", + __func__, __LINE__, timer, cmp, cmp, cmp2, cmp2); + + hpet_writel((unsigned long) delta, HPET_Tn_CMP(timer)); + printf("%s(%ld): period=%#lx(%'ld) timer=%d\n", __func__, __LINE__, + (unsigned long) delta, (unsigned long) delta, timer); + cmp3 = hpet_readl(HPET_Tn_CMP(timer)); + if ( cmp3 != cmp ) + print_error("%s(%ld): T%d Error: Set period, cmp %#lx(%'ld) != %#lx(%'ld)\n", + __func__, __LINE__, timer, cmp, cmp, cmp3, cmp3); + + if ( dom1.arch.hvm_domain.pl_time.vhpet.hpet.period[timer] != delta ) + printf("%s(%ld): T%d Warning: Set period, period %#lx(%'ld) != %#lx(%'ld)\n", + __func__, __LINE__, timer, delta, delta, + dom1.arch.hvm_domain.pl_time.vhpet.hpet.period[timer], + dom1.arch.hvm_domain.pl_time.vhpet.hpet.period[timer]); + + hpet_reset_counter(mc_low, mc_high); + cfg = hpet_readl(HPET_Tn_CFG(timer)); + cfg |= HPET_TN_SETVAL; + hpet_writel(cfg, HPET_Tn_CFG(timer)); + hpet_writel(old_cmp, HPET_Tn_CMP(timer)); + hpet_writel(old_delta, HPET_Tn_CMP(timer)); + hpet_start_counter(); +} + + +int +main(int argc, char **argv) +{ + hvm_domain_context_t hdc; + struct hvm_hw_hpet hpet0; + struct hvm_hw_hpet hpet1; + struct hvm_hw_hpet hpet2; + int i, k; + + setlocale(LC_ALL, ""); + +#ifdef FORCE_THOUSANDS_SEP + setlocale(LC_NUMERIC, "en_US.utf8"); +#endif + global_thousep = nl_langinfo(THOUSEP); + + printf("test_vhpet 1.0\n"); + + if ( argc > 1 ) + hvm_clock_cost = atoi(argv[1]); + if ( argc > 2 ) + hpet_mult = atoi(argv[2]); + if ( argc > 3 ) + hpet_add = atoi(argv[3]); + if ( argc > 4 ) + tick_count = atoi(argv[4]); + if ( argc > 5 ) + debug = strtol(argv[5], NULL, 0); + + printf("hvm_clock_cost=%'d hpet_mult=%'d hpet_add=%'d tick_count=%d debug=%#x\n", + hvm_clock_cost, hpet_mult, hpet_add, tick_count, debug); + + dom1.domain_id = 1; + dom1.vcpu[0] = &vcpu0; + vcpu0.vcpu_id = 0; + vcpu0.domain = &dom1; + + __hvm_register_HPET_save_and_restore(); + + for (skip_load = 3; skip_load >= 0; skip_load--) + { + + printf("\nskip_load=%d\n", skip_load); + + hvm_guest_time = 16; + + hpet_init(&vcpu0); + + do_save(HVM_SAVE_CODE(HPET), &dom1, &hdc); + dump_hpet(); + hpet0 = hpet_save; + do_save(HVM_SAVE_CODE(HPET), &dom1, &hdc); + dump_hpet(); + hpet1 = hpet_save; + if (hpet0.mc64 != hpet1.mc64) + print_error("%s(%ld): With clock stopped mc64 changed: %'ld to %'ld\n", + __func__, __LINE__, hpet0.mc64, hpet1.mc64); + + do_load(HVM_SAVE_CODE(HPET), &dom1, &hdc); + do_save(HVM_SAVE_CODE(HPET), &dom1, &hdc); + dump_hpet(); + hpet2 = hpet_save; + if (hpet1.mc64 != hpet2.mc64) + print_error("%s(%ld): With clock stopped mc64 changed: %'ld to %'ld\n", + __func__, __LINE__, hpet1.mc64, hpet2.mc64); + + dom1.arch.hvm_domain.pl_time.vhpet.hpet.mc64 = START_MC64; + dom1.arch.hvm_domain.pl_time.vhpet.mc_offset = START_MC64 + - hvm_guest_time - hvm_clock_cost; + printf("\n" + "mc64=%#lx(%'ld) mc_offset=%#lx(%'ld)\n", + dom1.arch.hvm_domain.pl_time.vhpet.hpet.mc64, + dom1.arch.hvm_domain.pl_time.vhpet.hpet.mc64, + dom1.arch.hvm_domain.pl_time.vhpet.mc_offset, + dom1.arch.hvm_domain.pl_time.vhpet.mc_offset); + + printf("\nhvm_guest_time=%#lx(%'ld)\n", + hvm_guest_time, hvm_guest_time); + + do_save(HVM_SAVE_CODE(HPET), &dom1, &hdc); + dump_hpet(); + hpet0 = hpet_save; + do_save(HVM_SAVE_CODE(HPET), &dom1, &hdc); + dump_hpet(); + hpet1 = hpet_save; + if (hpet0.mc64 != hpet1.mc64) + print_error("%s(%ld): With clock stopped mc64 changed: %'ld to %'ld\n", + __func__, __LINE__, hpet0.mc64, hpet1.mc64); + + do_load(HVM_SAVE_CODE(HPET), &dom1, &hdc); + do_save(HVM_SAVE_CODE(HPET), &dom1, &hdc); + dump_hpet(); + hpet2 = hpet_save; + if (hpet1.mc64 != hpet2.mc64) + print_error("%s(%ld): With clock stopped mc64 changed: %'ld to %'ld\n", + __func__, __LINE__, hpet1.mc64, hpet2.mc64); + + hpet_set_mode(0xf424, 0); + hpet_check_stopped(0xf424, 0); + + do_save(HVM_SAVE_CODE(HPET), &dom1, &hdc); + dump_hpet(); + hpet0 = hpet_save; + do_save(HVM_SAVE_CODE(HPET), &dom1, &hdc); + dump_hpet(); + hpet1 = hpet_save; + do_load(HVM_SAVE_CODE(HPET), &dom1, &hdc); + do_save(HVM_SAVE_CODE(HPET), &dom1, &hdc); + dump_hpet(); + hpet2 = hpet_save; + + hpet_set_mode(0, 1); + hpet_check_stopped(0, 1); + + do_save(HVM_SAVE_CODE(HPET), &dom1, &hdc); + dump_hpet(); + hpet0 = hpet_save; + do_save(HVM_SAVE_CODE(HPET), &dom1, &hdc); + dump_hpet(); + hpet1 = hpet_save; + + do_load(HVM_SAVE_CODE(HPET), &dom1, &hdc); + do_save(HVM_SAVE_CODE(HPET), &dom1, &hdc); + dump_hpet(); + hpet2 = hpet_save; + + hpet_set_mode(~0ULL, 2); + hpet_check_stopped(~0ULL, 2); + + hpet_set_mode(0x80000000, 2); + hpet_check_stopped(0x80000000, 2); + + do_save(HVM_SAVE_CODE(HPET), &dom1, &hdc); + dump_hpet(); + hpet0 = hpet_save; + do_save(HVM_SAVE_CODE(HPET), &dom1, &hdc); + dump_hpet(); + hpet1 = hpet_save; + + do_load(HVM_SAVE_CODE(HPET), &dom1, &hdc); + do_save(HVM_SAVE_CODE(HPET), &dom1, &hdc); + dump_hpet(); + hpet2 = hpet_save; + + + for (k = 0; k < ARRAY_SIZE(new_guest_time); k++) + { + hvm_guest_time = new_guest_time[k]; + printf("\nhvm_guest_time=%#lx(%'ld)\n", + hvm_guest_time, hvm_guest_time); + + do_save(HVM_SAVE_CODE(HPET), &dom1, &hdc); + dump_hpet(); + hpet0 = hpet_save; + do_save(HVM_SAVE_CODE(HPET), &dom1, &hdc); + dump_hpet(); + hpet1 = hpet_save; + + do_load(HVM_SAVE_CODE(HPET), &dom1, &hdc); + do_save(HVM_SAVE_CODE(HPET), &dom1, &hdc); + dump_hpet(); + hpet2 = hpet_save; + + for (i = 0; i < tick_count; i++) + { + hvm_guest_time += 0x10; + printf("\nhvm_guest_time=%#lx(%'ld)\n", + hvm_guest_time, hvm_guest_time); + + do_save(HVM_SAVE_CODE(HPET), &dom1, &hdc); + dump_hpet(); + hpet0 = hpet_save; + do_save(HVM_SAVE_CODE(HPET), &dom1, &hdc); + dump_hpet(); + hpet1 = hpet_save; + + do_load(HVM_SAVE_CODE(HPET), &dom1, &hdc); + do_save(HVM_SAVE_CODE(HPET), &dom1, &hdc); + dump_hpet(); + hpet2 = hpet_save; + + } + } + } + + return 0; +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ -- 1.8.4 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |