diff options
author | Pasha <pasha@member.fsf.org> | 2024-02-20 18:49:50 +0000 |
---|---|---|
committer | Pasha <pasha@member.fsf.org> | 2024-02-20 18:49:50 +0000 |
commit | 5e0b8d508ed51004bd836384293be00950ee62c9 (patch) | |
tree | e3f16b1aa8b7177032ce3ec429fbad2b1d92a876 /xen/time.c | |
download | gnumach-riscv-5e0b8d508ed51004bd836384293be00950ee62c9.tar.gz gnumach-riscv-5e0b8d508ed51004bd836384293be00950ee62c9.tar.bz2 |
init gnumach copy
Diffstat (limited to 'xen/time.c')
-rw-r--r-- | xen/time.c | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/xen/time.c b/xen/time.c new file mode 100644 index 0000000..21791a5 --- /dev/null +++ b/xen/time.c @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2006-2009 Free Software Foundation + * + * This program is free software ; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation ; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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. + * + * You should have received a copy of the GNU General Public License + * along with the program ; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <sys/types.h> +#include <mach/mach_types.h> +#include <kern/mach_clock.h> +#include <mach/xen.h> +#include <machine/xen.h> +#include <machine/spl.h> +#include <machine/ipl.h> +#include <mach/machine/eflags.h> +#include <xen/evt.h> +#include "xen.h" +#include "time.h" +#include "store.h" + +static uint64_t lastnsec; + +/* 2^64 nanoseconds ~= 500 years */ +static uint64_t hyp_get_stime(void) { + uint32_t version; + uint64_t cpu_clock, last_cpu_clock, delta, system_time; + uint64_t delta_high, delta_low; + uint32_t mul; + int8_t shift; + volatile struct vcpu_time_info *time = &hyp_shared_info.vcpu_info[0].time; + + do { + version = time->version; + rmb(); + cpu_clock = hyp_cpu_clock(); + last_cpu_clock = time->tsc_timestamp; + system_time = time->system_time; + mul = time->tsc_to_system_mul; + shift = time->tsc_shift; + rmb(); + } while (version != time->version); + + delta = cpu_clock - last_cpu_clock; + if (shift < 0) + delta >>= -shift; + else + delta <<= shift; + delta_high = delta >> 32; + delta_low = (uint32_t) delta; + return system_time + ((delta_low * (uint64_t) mul) >> 32) + + (delta_high * (uint64_t) mul); +} + +uint64_t hyp_get_time(void) { + uint32_t version; + uint32_t sec, nsec; + + do { + version = hyp_shared_info.wc_version; + rmb(); + sec = hyp_shared_info.wc_sec; + nsec = hyp_shared_info.wc_nsec; + rmb(); + } while (version != hyp_shared_info.wc_version); + + return sec*1000000000ULL + nsec + hyp_get_stime(); +} + +static void hypclock_intr(int unit, int old_ipl, void *ret_addr, struct i386_interrupt_state *regs) { + uint64_t nsec, delta; + + if (!lastnsec) + return; + + nsec = hyp_get_stime(); + if (nsec < lastnsec) { + printf("warning: nsec 0x%08lx%08lx < lastnsec 0x%08lx%08lx\n",(unsigned long)(nsec>>32), (unsigned long)nsec, (unsigned long)(lastnsec>>32), (unsigned long)lastnsec); + nsec = lastnsec; + } + delta = nsec-lastnsec; + + lastnsec += (delta/1000)*1000; + hypclock_machine_intr(old_ipl, ret_addr, regs, delta); + /* 10ms tick rest */ + hyp_do_set_timer_op(hyp_get_stime()+10*1000*1000); + +#if 0 + char *c = hyp_store_read(0, 1, "control/shutdown"); + if (c) { + static int go_down = 0; + if (!go_down) { + printf("uh oh, shutdown: %s\n", c); + go_down = 1; + /* TODO: somehow send startup_reboot notification to init */ + if (!strcmp(c, "reboot")) { + /* this is just a reboot */ + } + } + } +#endif +} + +int +readtodc(uint64_t *tp) +{ + uint64_t t = hyp_get_time(); + uint64_t n = t / 1000000000; + + *tp = n; + + return(0); +} + +int +writetodc(void) +{ + /* Not allowed in Xen */ + return(-1); +} + +void +clkstart(void) +{ + evtchn_port_t port = hyp_event_channel_bind_virq(VIRQ_TIMER, 0); + hyp_evt_handler(port, (interrupt_handler_fn)hypclock_intr, 0, SPLHI); + + /* first clock tick */ + clock_interrupt(0, 0, 0, 0); + lastnsec = hyp_get_stime(); + + /* 10ms tick rest */ + hyp_do_set_timer_op(hyp_get_stime()+10*1000*1000); +} |