/root/src/xen/xen/arch/x86/hvm/hpet.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * hpet.c: HPET emulation for HVM guests. |
3 | | * Copyright (c) 2006, Intel Corporation. |
4 | | * Copyright (c) 2006, Keir Fraser <keir@xensource.com> |
5 | | * |
6 | | * This program is free software; you can redistribute it and/or modify it |
7 | | * under the terms and conditions of the GNU General Public License, |
8 | | * version 2, as published by the Free Software Foundation. |
9 | | * |
10 | | * This program is distributed in the hope it will be useful, but WITHOUT |
11 | | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
12 | | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
13 | | * more details. |
14 | | * |
15 | | * You should have received a copy of the GNU General Public License along with |
16 | | * this program; If not, see <http://www.gnu.org/licenses/>. |
17 | | */ |
18 | | |
19 | | #include <asm/hvm/vpt.h> |
20 | | #include <asm/hvm/io.h> |
21 | | #include <asm/hvm/support.h> |
22 | | #include <asm/hvm/trace.h> |
23 | | #include <asm/current.h> |
24 | | #include <asm/hpet.h> |
25 | | #include <xen/sched.h> |
26 | | #include <xen/event.h> |
27 | | #include <xen/trace.h> |
28 | | |
29 | 1 | #define domain_vhpet(x) (&(x)->arch.hvm_domain.pl_time->vhpet) |
30 | 0 | #define vcpu_vhpet(x) (domain_vhpet((x)->domain)) |
31 | 0 | #define vhpet_domain(x) (container_of(x, struct pl_time, vhpet)->domain) |
32 | 0 | #define vhpet_vcpu(x) (pt_global_vcpu_target(vhpet_domain(x))) |
33 | | |
34 | 0 | #define HPET_BASE_ADDRESS 0xfed00000ULL |
35 | 0 | #define HPET_MMAP_SIZE 1024 |
36 | 0 | #define S_TO_NS 1000000000ULL /* 1s = 10^9 ns */ |
37 | 0 | #define S_TO_FS 1000000000000000ULL /* 1s = 10^15 fs */ |
38 | | |
39 | | /* Frequency_of_Xen_systeme_time / frequency_of_HPET = 16 */ |
40 | 0 | #define STIME_PER_HPET_TICK 16 |
41 | | #define guest_time_hpet(hpet) \ |
42 | 0 | (hvm_get_guest_time(vhpet_vcpu(hpet)) / STIME_PER_HPET_TICK) |
43 | | |
44 | 0 | #define HPET_TN_INT_ROUTE_CAP_SHIFT 32 |
45 | | #define HPET_TN_CFG_BITS_READONLY_OR_RESERVED (HPET_TN_RESERVED | \ |
46 | | HPET_TN_PERIODIC_CAP | HPET_TN_64BIT_CAP | HPET_TN_FSB_CAP) |
47 | | |
48 | | /* can be routed to IOAPIC.redirect_table[23..20] */ |
49 | 0 | #define HPET_TN_INT_ROUTE_CAP (0x00f00000ULL \ |
50 | 0 | << HPET_TN_INT_ROUTE_CAP_SHIFT) |
51 | | |
52 | | #define HPET_TN_INT_ROUTE_CAP_MASK (0xffffffffULL \ |
53 | | << HPET_TN_INT_ROUTE_CAP_SHIFT) |
54 | | |
55 | 0 | #define HPET_TN(reg, addr) (((addr) - HPET_Tn_##reg(0)) / \ |
56 | 0 | (HPET_Tn_##reg(1) - HPET_Tn_##reg(0))) |
57 | | |
58 | | #define hpet_tick_to_ns(h, tick) \ |
59 | 0 | ((s_time_t)((((tick) > (h)->hpet_to_ns_limit) ? \ |
60 | 0 | ~0ULL : (tick) * (h)->hpet_to_ns_scale) >> 10)) |
61 | | |
62 | 0 | #define timer_config(h, n) (h->hpet.timers[n].config) |
63 | 0 | #define timer_enabled(h, n) (timer_config(h, n) & HPET_TN_ENABLE) |
64 | 0 | #define timer_is_periodic(h, n) (timer_config(h, n) & HPET_TN_PERIODIC) |
65 | 0 | #define timer_is_32bit(h, n) (timer_config(h, n) & HPET_TN_32BIT) |
66 | 0 | #define hpet_enabled(h) (h->hpet.config & HPET_CFG_ENABLE) |
67 | 0 | #define timer_level(h, n) (timer_config(h, n) & HPET_TN_LEVEL) |
68 | | |
69 | | #define timer_int_route(h, n) \ |
70 | 0 | ((timer_config(h, n) & HPET_TN_ROUTE) >> HPET_TN_ROUTE_SHIFT) |
71 | | |
72 | | #define timer_int_route_cap(h, n) \ |
73 | | ((timer_config(h, n) & HPET_TN_INT_ROUTE_CAP_MASK) \ |
74 | | >> HPET_TN_INT_ROUTE_CAP_SHIFT) |
75 | | |
76 | | static inline uint64_t hpet_read_maincounter(HPETState *h, uint64_t guest_time) |
77 | 0 | { |
78 | 0 | ASSERT(rw_is_locked(&h->lock)); |
79 | 0 |
|
80 | 0 | if ( hpet_enabled(h) ) |
81 | 0 | return guest_time + h->mc_offset; |
82 | 0 | else |
83 | 0 | return h->hpet.mc64; |
84 | 0 | } |
85 | | |
86 | | static uint64_t hpet_get_comparator(HPETState *h, unsigned int tn, |
87 | | uint64_t guest_time) |
88 | 0 | { |
89 | 0 | uint64_t comparator; |
90 | 0 | uint64_t elapsed; |
91 | 0 |
|
92 | 0 | ASSERT(rw_is_write_locked(&h->lock)); |
93 | 0 |
|
94 | 0 | comparator = h->hpet.comparator64[tn]; |
95 | 0 | if ( hpet_enabled(h) && timer_is_periodic(h, tn) ) |
96 | 0 | { |
97 | 0 | /* update comparator by number of periods elapsed since last update */ |
98 | 0 | uint64_t period = h->hpet.period[tn]; |
99 | 0 | if (period) |
100 | 0 | { |
101 | 0 | elapsed = hpet_read_maincounter(h, guest_time) - comparator; |
102 | 0 | if ( (int64_t)elapsed >= 0 ) |
103 | 0 | { |
104 | 0 | comparator += ((elapsed + period) / period) * period; |
105 | 0 | h->hpet.comparator64[tn] = comparator; |
106 | 0 | } |
107 | 0 | } |
108 | 0 | } |
109 | 0 |
|
110 | 0 | /* truncate if timer is in 32 bit mode */ |
111 | 0 | if ( timer_is_32bit(h, tn) ) |
112 | 0 | comparator = (uint32_t)comparator; |
113 | 0 | h->hpet.timers[tn].cmp = comparator; |
114 | 0 | return comparator; |
115 | 0 | } |
116 | | static inline uint64_t hpet_read64(HPETState *h, unsigned long addr, |
117 | | uint64_t guest_time) |
118 | 0 | { |
119 | 0 | addr &= ~7; |
120 | 0 |
|
121 | 0 | switch ( addr ) |
122 | 0 | { |
123 | 0 | case HPET_ID: |
124 | 0 | return h->hpet.capability; |
125 | 0 | case HPET_CFG: |
126 | 0 | return h->hpet.config; |
127 | 0 | case HPET_STATUS: |
128 | 0 | return h->hpet.isr; |
129 | 0 | case HPET_COUNTER: |
130 | 0 | return hpet_read_maincounter(h, guest_time); |
131 | 0 | case HPET_Tn_CFG(0): |
132 | 0 | case HPET_Tn_CFG(1): |
133 | 0 | case HPET_Tn_CFG(2): |
134 | 0 | return h->hpet.timers[HPET_TN(CFG, addr)].config; |
135 | 0 | case HPET_Tn_CMP(0): |
136 | 0 | case HPET_Tn_CMP(1): |
137 | 0 | case HPET_Tn_CMP(2): |
138 | 0 | return hpet_get_comparator(h, HPET_TN(CMP, addr), guest_time); |
139 | 0 | case HPET_Tn_ROUTE(0): |
140 | 0 | case HPET_Tn_ROUTE(1): |
141 | 0 | case HPET_Tn_ROUTE(2): |
142 | 0 | return h->hpet.timers[HPET_TN(ROUTE, addr)].fsb; |
143 | 0 | } |
144 | 0 |
|
145 | 0 | return 0; |
146 | 0 | } |
147 | | |
148 | | static inline int hpet_check_access_length( |
149 | | unsigned long addr, unsigned long len) |
150 | 0 | { |
151 | 0 | if ( (addr & (len - 1)) || (len > 8) ) |
152 | 0 | { |
153 | 0 | /* |
154 | 0 | * According to ICH9 specification, unaligned accesses may result |
155 | 0 | * in unexpected behaviour or master abort, but should not crash/hang. |
156 | 0 | * Hence we read all-ones, drop writes, and log a warning. |
157 | 0 | */ |
158 | 0 | gdprintk(XENLOG_WARNING, "HPET: access across register boundary: " |
159 | 0 | "%lx %lx\n", addr, len); |
160 | 0 | return -EINVAL; |
161 | 0 | } |
162 | 0 |
|
163 | 0 | return 0; |
164 | 0 | } |
165 | | |
166 | | static int hpet_read( |
167 | | struct vcpu *v, unsigned long addr, unsigned int length, |
168 | | unsigned long *pval) |
169 | 0 | { |
170 | 0 | HPETState *h = vcpu_vhpet(v); |
171 | 0 | unsigned long result; |
172 | 0 | uint64_t val; |
173 | 0 |
|
174 | 0 | if ( !v->domain->arch.hvm_domain.params[HVM_PARAM_HPET_ENABLED] ) |
175 | 0 | { |
176 | 0 | result = ~0ul; |
177 | 0 | goto out; |
178 | 0 | } |
179 | 0 |
|
180 | 0 | addr &= HPET_MMAP_SIZE-1; |
181 | 0 |
|
182 | 0 | if ( hpet_check_access_length(addr, length) != 0 ) |
183 | 0 | { |
184 | 0 | result = ~0ul; |
185 | 0 | goto out; |
186 | 0 | } |
187 | 0 |
|
188 | 0 | result = addr < HPET_Tn_CMP(0) || |
189 | 0 | ((addr - HPET_Tn_CMP(0)) % (HPET_Tn_CMP(1) - HPET_Tn_CMP(0))) > 7; |
190 | 0 | if ( result ) |
191 | 0 | read_lock(&h->lock); |
192 | 0 | else |
193 | 0 | write_lock(&h->lock); |
194 | 0 |
|
195 | 0 | val = hpet_read64(h, addr, guest_time_hpet(h)); |
196 | 0 |
|
197 | 0 | if ( result ) |
198 | 0 | read_unlock(&h->lock); |
199 | 0 | else |
200 | 0 | write_unlock(&h->lock); |
201 | 0 |
|
202 | 0 | result = val; |
203 | 0 | if ( length != 8 ) |
204 | 0 | result = (val >> ((addr & 7) * 8)) & ((1ULL << (length * 8)) - 1); |
205 | 0 |
|
206 | 0 | out: |
207 | 0 | *pval = result; |
208 | 0 | return X86EMUL_OKAY; |
209 | 0 | } |
210 | | |
211 | | static void hpet_stop_timer(HPETState *h, unsigned int tn, |
212 | | uint64_t guest_time) |
213 | 0 | { |
214 | 0 | ASSERT(tn < HPET_TIMER_NUM); |
215 | 0 | ASSERT(rw_is_write_locked(&h->lock)); |
216 | 0 | TRACE_1D(TRC_HVM_EMUL_HPET_STOP_TIMER, tn); |
217 | 0 | destroy_periodic_time(&h->pt[tn]); |
218 | 0 | /* read the comparator to get it updated so a read while stopped will |
219 | 0 | * return the expected value. */ |
220 | 0 | hpet_get_comparator(h, tn, guest_time); |
221 | 0 | } |
222 | | |
223 | | /* the number of HPET tick that stands for |
224 | | * 1/(2^10) second, namely, 0.9765625 milliseconds */ |
225 | 0 | #define HPET_TINY_TIME_SPAN ((h->stime_freq >> 10) / STIME_PER_HPET_TICK) |
226 | | |
227 | | static void hpet_set_timer(HPETState *h, unsigned int tn, |
228 | | uint64_t guest_time) |
229 | 0 | { |
230 | 0 | uint64_t tn_cmp, cur_tick, diff; |
231 | 0 | unsigned int irq; |
232 | 0 | unsigned int oneshot; |
233 | 0 |
|
234 | 0 | ASSERT(tn < HPET_TIMER_NUM); |
235 | 0 | ASSERT(rw_is_write_locked(&h->lock)); |
236 | 0 |
|
237 | 0 | if ( (tn == 0) && (h->hpet.config & HPET_CFG_LEGACY) ) |
238 | 0 | { |
239 | 0 | /* HPET specification requires PIT shouldn't generate |
240 | 0 | * interrupts if LegacyReplacementRoute is set for timer0 */ |
241 | 0 | pit_stop_channel0_irq(&vhpet_domain(h)->arch.vpit); |
242 | 0 | } |
243 | 0 |
|
244 | 0 | if ( !timer_enabled(h, tn) ) |
245 | 0 | return; |
246 | 0 |
|
247 | 0 | tn_cmp = hpet_get_comparator(h, tn, guest_time); |
248 | 0 | cur_tick = hpet_read_maincounter(h, guest_time); |
249 | 0 | if ( timer_is_32bit(h, tn) ) |
250 | 0 | { |
251 | 0 | tn_cmp = (uint32_t)tn_cmp; |
252 | 0 | cur_tick = (uint32_t)cur_tick; |
253 | 0 | } |
254 | 0 |
|
255 | 0 | diff = tn_cmp - cur_tick; |
256 | 0 |
|
257 | 0 | /* |
258 | 0 | * Detect time values set in the past. This is hard to do for 32-bit |
259 | 0 | * comparators as the timer does not have to be set that far in the future |
260 | 0 | * for the counter difference to wrap a 32-bit signed integer. We fudge |
261 | 0 | * by looking for a 'small' time value in the past. |
262 | 0 | */ |
263 | 0 | if ( (int64_t)diff < 0 ) |
264 | 0 | diff = (timer_is_32bit(h, tn) && (-diff > HPET_TINY_TIME_SPAN)) |
265 | 0 | ? (uint32_t)diff : 0; |
266 | 0 |
|
267 | 0 | if ( (tn <= 1) && (h->hpet.config & HPET_CFG_LEGACY) ) |
268 | 0 | /* if LegacyReplacementRoute bit is set, HPET specification requires |
269 | 0 | timer0 be routed to IRQ0 in NON-APIC or IRQ2 in the I/O APIC, |
270 | 0 | timer1 be routed to IRQ8 in NON-APIC or IRQ8 in the I/O APIC. */ |
271 | 0 | irq = (tn == 0) ? 0 : 8; |
272 | 0 | else |
273 | 0 | irq = timer_int_route(h, tn); |
274 | 0 |
|
275 | 0 | /* |
276 | 0 | * diff is the time from now when the timer should fire, for a periodic |
277 | 0 | * timer we also need the period which may be different because time may |
278 | 0 | * have elapsed between the time the comparator was written and the timer |
279 | 0 | * being enabled (now). |
280 | 0 | */ |
281 | 0 | oneshot = !timer_is_periodic(h, tn); |
282 | 0 | TRACE_2_LONG_4D(TRC_HVM_EMUL_HPET_START_TIMER, tn, irq, |
283 | 0 | TRC_PAR_LONG(hpet_tick_to_ns(h, diff)), |
284 | 0 | TRC_PAR_LONG(oneshot ? 0LL : |
285 | 0 | hpet_tick_to_ns(h, h->hpet.period[tn]))); |
286 | 0 | create_periodic_time(vhpet_vcpu(h), &h->pt[tn], |
287 | 0 | hpet_tick_to_ns(h, diff), |
288 | 0 | oneshot ? 0 : hpet_tick_to_ns(h, h->hpet.period[tn]), |
289 | 0 | irq, NULL, NULL); |
290 | 0 | } |
291 | | |
292 | | static inline uint64_t hpet_fixup_reg( |
293 | | uint64_t new, uint64_t old, uint64_t mask) |
294 | 0 | { |
295 | 0 | new &= mask; |
296 | 0 | new |= old & ~mask; |
297 | 0 | return new; |
298 | 0 | } |
299 | | |
300 | | static int hpet_write( |
301 | | struct vcpu *v, unsigned long addr, |
302 | | unsigned int length, unsigned long val) |
303 | 0 | { |
304 | 0 | HPETState *h = vcpu_vhpet(v); |
305 | 0 | uint64_t old_val, new_val; |
306 | 0 | uint64_t guest_time; |
307 | 0 | int tn, i; |
308 | 0 |
|
309 | 0 | /* Acculumate a bit mask of timers whos state is changed by this write. */ |
310 | 0 | unsigned long start_timers = 0; |
311 | 0 | unsigned long stop_timers = 0; |
312 | 0 | #define set_stop_timer(n) (__set_bit((n), &stop_timers)) |
313 | 0 | #define set_start_timer(n) (__set_bit((n), &start_timers)) |
314 | 0 | #define set_restart_timer(n) (set_stop_timer(n),set_start_timer(n)) |
315 | 0 |
|
316 | 0 | if ( !v->domain->arch.hvm_domain.params[HVM_PARAM_HPET_ENABLED] ) |
317 | 0 | goto out; |
318 | 0 |
|
319 | 0 | addr &= HPET_MMAP_SIZE-1; |
320 | 0 |
|
321 | 0 | if ( hpet_check_access_length(addr, length) != 0 ) |
322 | 0 | goto out; |
323 | 0 |
|
324 | 0 | write_lock(&h->lock); |
325 | 0 |
|
326 | 0 | guest_time = guest_time_hpet(h); |
327 | 0 | old_val = hpet_read64(h, addr, guest_time); |
328 | 0 | new_val = val; |
329 | 0 | if ( length != 8 ) |
330 | 0 | new_val = hpet_fixup_reg( |
331 | 0 | new_val << (addr & 7) * 8, old_val, |
332 | 0 | ((1ULL << (length*8)) - 1) << ((addr & 7) * 8)); |
333 | 0 |
|
334 | 0 | switch ( addr & ~7 ) |
335 | 0 | { |
336 | 0 | case HPET_CFG: |
337 | 0 | h->hpet.config = hpet_fixup_reg(new_val, old_val, 0x3); |
338 | 0 |
|
339 | 0 | if ( !(old_val & HPET_CFG_ENABLE) && (new_val & HPET_CFG_ENABLE) ) |
340 | 0 | { |
341 | 0 | /* Enable main counter and interrupt generation. */ |
342 | 0 | h->mc_offset = h->hpet.mc64 - guest_time; |
343 | 0 | for ( i = 0; i < HPET_TIMER_NUM; i++ ) |
344 | 0 | { |
345 | 0 | h->hpet.comparator64[i] = |
346 | 0 | h->hpet.timers[i].config & HPET_TN_32BIT ? |
347 | 0 | (uint32_t)h->hpet.timers[i].cmp : |
348 | 0 | h->hpet.timers[i].cmp; |
349 | 0 | if ( timer_enabled(h, i) ) |
350 | 0 | set_start_timer(i); |
351 | 0 | } |
352 | 0 | } |
353 | 0 | else if ( (old_val & HPET_CFG_ENABLE) && !(new_val & HPET_CFG_ENABLE) ) |
354 | 0 | { |
355 | 0 | /* Halt main counter and disable interrupt generation. */ |
356 | 0 | h->hpet.mc64 = h->mc_offset + guest_time; |
357 | 0 | for ( i = 0; i < HPET_TIMER_NUM; i++ ) |
358 | 0 | if ( timer_enabled(h, i) ) |
359 | 0 | set_stop_timer(i); |
360 | 0 | } |
361 | 0 | break; |
362 | 0 |
|
363 | 0 | case HPET_COUNTER: |
364 | 0 | h->hpet.mc64 = new_val; |
365 | 0 | if ( hpet_enabled(h) ) |
366 | 0 | { |
367 | 0 | gdprintk(XENLOG_WARNING, |
368 | 0 | "HPET: writing main counter but it's not halted!\n"); |
369 | 0 | for ( i = 0; i < HPET_TIMER_NUM; i++ ) |
370 | 0 | if ( timer_enabled(h, i) ) |
371 | 0 | set_restart_timer(i); |
372 | 0 | } |
373 | 0 | break; |
374 | 0 |
|
375 | 0 | case HPET_Tn_CFG(0): |
376 | 0 | case HPET_Tn_CFG(1): |
377 | 0 | case HPET_Tn_CFG(2): |
378 | 0 | tn = HPET_TN(CFG, addr); |
379 | 0 |
|
380 | 0 | h->hpet.timers[tn].config = hpet_fixup_reg(new_val, old_val, 0x3f4e); |
381 | 0 |
|
382 | 0 | if ( timer_level(h, tn) ) |
383 | 0 | { |
384 | 0 | gdprintk(XENLOG_ERR, |
385 | 0 | "HPET: level triggered interrupt not supported now\n"); |
386 | 0 | domain_crash(current->domain); |
387 | 0 | break; |
388 | 0 | } |
389 | 0 |
|
390 | 0 | if ( new_val & HPET_TN_32BIT ) |
391 | 0 | { |
392 | 0 | h->hpet.timers[tn].cmp = (uint32_t)h->hpet.timers[tn].cmp; |
393 | 0 | h->hpet.period[tn] = (uint32_t)h->hpet.period[tn]; |
394 | 0 | } |
395 | 0 | if ( hpet_enabled(h) ) |
396 | 0 | { |
397 | 0 | if ( new_val & HPET_TN_ENABLE ) |
398 | 0 | { |
399 | 0 | if ( (new_val ^ old_val) & HPET_TN_PERIODIC ) |
400 | 0 | /* timer is enabled but switching mode to/from periodic/ |
401 | 0 | * one-shot, stop and restart the vpt timer to get it in |
402 | 0 | * the right mode. */ |
403 | 0 | set_restart_timer(tn); |
404 | 0 | else if ( (new_val & HPET_TN_32BIT) && |
405 | 0 | !(old_val & HPET_TN_32BIT) ) |
406 | 0 | /* switching from 64 bit to 32 bit mode could cause timer |
407 | 0 | * next fire time, or period, to change. */ |
408 | 0 | set_restart_timer(tn); |
409 | 0 | else if ( !(old_val & HPET_TN_ENABLE) ) |
410 | 0 | /* transition from timer disabled to timer enabled. */ |
411 | 0 | set_start_timer(tn); |
412 | 0 | } |
413 | 0 | else if ( old_val & HPET_TN_ENABLE ) |
414 | 0 | /* transition from timer enabled to timer disabled. */ |
415 | 0 | set_stop_timer(tn); |
416 | 0 | } |
417 | 0 | break; |
418 | 0 |
|
419 | 0 | case HPET_Tn_CMP(0): |
420 | 0 | case HPET_Tn_CMP(1): |
421 | 0 | case HPET_Tn_CMP(2): |
422 | 0 | tn = HPET_TN(CMP, addr); |
423 | 0 | if ( timer_is_periodic(h, tn) && |
424 | 0 | !(h->hpet.timers[tn].config & HPET_TN_SETVAL) ) |
425 | 0 | { |
426 | 0 | uint64_t max_period = (timer_is_32bit(h, tn) ? ~0u : ~0ull) >> 1; |
427 | 0 |
|
428 | 0 | /* |
429 | 0 | * Clamp period to reasonable min/max values: |
430 | 0 | * - minimum is 100us, same as timers controlled by vpt.c |
431 | 0 | * - maximum is to prevent overflow in time_after() calculations |
432 | 0 | */ |
433 | 0 | if ( hpet_tick_to_ns(h, new_val) < MICROSECS(100) ) |
434 | 0 | new_val = (MICROSECS(100) << 10) / h->hpet_to_ns_scale; |
435 | 0 | if ( new_val > max_period ) |
436 | 0 | new_val = max_period; |
437 | 0 | h->hpet.period[tn] = new_val; |
438 | 0 | } |
439 | 0 | else |
440 | 0 | { |
441 | 0 | /* |
442 | 0 | * When SETVAL is one, software is able to "directly set |
443 | 0 | * a periodic timer's accumulator." That is, set the |
444 | 0 | * comparator without adjusting the period. Much the |
445 | 0 | * same as just setting the comparator on an enabled |
446 | 0 | * one-shot timer. |
447 | 0 | * |
448 | 0 | * This configuration bit clears when the comparator is |
449 | 0 | * written. |
450 | 0 | */ |
451 | 0 | h->hpet.timers[tn].config &= ~HPET_TN_SETVAL; |
452 | 0 | h->hpet.comparator64[tn] = new_val; |
453 | 0 | /* truncate if timer is in 32 bit mode */ |
454 | 0 | if ( timer_is_32bit(h, tn) ) |
455 | 0 | new_val = (uint32_t)new_val; |
456 | 0 | h->hpet.timers[tn].cmp = new_val; |
457 | 0 | } |
458 | 0 | if ( hpet_enabled(h) && timer_enabled(h, tn) ) |
459 | 0 | set_restart_timer(tn); |
460 | 0 | break; |
461 | 0 |
|
462 | 0 | case HPET_Tn_ROUTE(0): |
463 | 0 | case HPET_Tn_ROUTE(1): |
464 | 0 | case HPET_Tn_ROUTE(2): |
465 | 0 | tn = HPET_TN(ROUTE, addr); |
466 | 0 | h->hpet.timers[tn].fsb = new_val; |
467 | 0 | break; |
468 | 0 |
|
469 | 0 | default: |
470 | 0 | /* Ignore writes to unsupported and reserved registers. */ |
471 | 0 | break; |
472 | 0 | } |
473 | 0 |
|
474 | 0 | /* stop/start timers whos state was changed by this write. */ |
475 | 0 | while (stop_timers) |
476 | 0 | { |
477 | 0 | i = find_first_set_bit(stop_timers); |
478 | 0 | __clear_bit(i, &stop_timers); |
479 | 0 | hpet_stop_timer(h, i, guest_time); |
480 | 0 | } |
481 | 0 |
|
482 | 0 | while (start_timers) |
483 | 0 | { |
484 | 0 | i = find_first_set_bit(start_timers); |
485 | 0 | __clear_bit(i, &start_timers); |
486 | 0 | hpet_set_timer(h, i, guest_time); |
487 | 0 | } |
488 | 0 |
|
489 | 0 | #undef set_stop_timer |
490 | 0 | #undef set_start_timer |
491 | 0 | #undef set_restart_timer |
492 | 0 |
|
493 | 0 | write_unlock(&h->lock); |
494 | 0 |
|
495 | 0 | out: |
496 | 0 | return X86EMUL_OKAY; |
497 | 0 | } |
498 | | |
499 | | static int hpet_range(struct vcpu *v, unsigned long addr) |
500 | 0 | { |
501 | 0 | return ( (addr >= HPET_BASE_ADDRESS) && |
502 | 0 | (addr < (HPET_BASE_ADDRESS + HPET_MMAP_SIZE)) ); |
503 | 0 | } |
504 | | |
505 | | static const struct hvm_mmio_ops hpet_mmio_ops = { |
506 | | .check = hpet_range, |
507 | | .read = hpet_read, |
508 | | .write = hpet_write |
509 | | }; |
510 | | |
511 | | |
512 | | static int hpet_save(struct domain *d, hvm_domain_context_t *h) |
513 | 0 | { |
514 | 0 | HPETState *hp = domain_vhpet(d); |
515 | 0 | struct vcpu *v = pt_global_vcpu_target(d); |
516 | 0 | int rc; |
517 | 0 | uint64_t guest_time; |
518 | 0 |
|
519 | 0 | if ( !has_vhpet(d) ) |
520 | 0 | return 0; |
521 | 0 |
|
522 | 0 | write_lock(&hp->lock); |
523 | 0 | guest_time = (v->arch.hvm_vcpu.guest_time ?: hvm_get_guest_time(v)) / |
524 | 0 | STIME_PER_HPET_TICK; |
525 | 0 |
|
526 | 0 | /* Write the proper value into the main counter */ |
527 | 0 | if ( hpet_enabled(hp) ) |
528 | 0 | hp->hpet.mc64 = hp->mc_offset + guest_time; |
529 | 0 |
|
530 | 0 | /* Save the HPET registers */ |
531 | 0 | rc = _hvm_init_entry(h, HVM_SAVE_CODE(HPET), 0, HVM_SAVE_LENGTH(HPET)); |
532 | 0 | if ( rc == 0 ) |
533 | 0 | { |
534 | 0 | struct hvm_hw_hpet *rec = (struct hvm_hw_hpet *)&h->data[h->cur]; |
535 | 0 | h->cur += HVM_SAVE_LENGTH(HPET); |
536 | 0 | memset(rec, 0, HVM_SAVE_LENGTH(HPET)); |
537 | 0 | #define C(x) rec->x = hp->hpet.x |
538 | 0 | C(capability); |
539 | 0 | C(config); |
540 | 0 | C(isr); |
541 | 0 | C(mc64); |
542 | 0 | C(timers[0].config); |
543 | 0 | C(timers[0].fsb); |
544 | 0 | C(timers[1].config); |
545 | 0 | C(timers[1].fsb); |
546 | 0 | C(timers[2].config); |
547 | 0 | C(timers[2].fsb); |
548 | 0 | C(period[0]); |
549 | 0 | C(period[1]); |
550 | 0 | C(period[2]); |
551 | 0 | #undef C |
552 | 0 | /* |
553 | 0 | * read the comparator to get it updated so hpet_save will |
554 | 0 | * return the expected value. |
555 | 0 | */ |
556 | 0 | hpet_get_comparator(hp, 0, guest_time); |
557 | 0 | hpet_get_comparator(hp, 1, guest_time); |
558 | 0 | hpet_get_comparator(hp, 2, guest_time); |
559 | 0 | /* |
560 | 0 | * save the 64 bit comparator in the 64 bit timer[n].cmp |
561 | 0 | * field regardless of whether or not the timer is in 32 bit |
562 | 0 | * mode. |
563 | 0 | */ |
564 | 0 | rec->timers[0].cmp = hp->hpet.comparator64[0]; |
565 | 0 | rec->timers[1].cmp = hp->hpet.comparator64[1]; |
566 | 0 | rec->timers[2].cmp = hp->hpet.comparator64[2]; |
567 | 0 | } |
568 | 0 |
|
569 | 0 | write_unlock(&hp->lock); |
570 | 0 |
|
571 | 0 | return rc; |
572 | 0 | } |
573 | | |
574 | | static int hpet_load(struct domain *d, hvm_domain_context_t *h) |
575 | 0 | { |
576 | 0 | HPETState *hp = domain_vhpet(d); |
577 | 0 | struct hvm_hw_hpet *rec; |
578 | 0 | uint64_t cmp; |
579 | 0 | uint64_t guest_time; |
580 | 0 | int i; |
581 | 0 |
|
582 | 0 | if ( !has_vhpet(d) ) |
583 | 0 | return -ENODEV; |
584 | 0 |
|
585 | 0 | write_lock(&hp->lock); |
586 | 0 |
|
587 | 0 | /* Reload the HPET registers */ |
588 | 0 | if ( _hvm_check_entry(h, HVM_SAVE_CODE(HPET), HVM_SAVE_LENGTH(HPET), 1) ) |
589 | 0 | { |
590 | 0 | write_unlock(&hp->lock); |
591 | 0 | return -EINVAL; |
592 | 0 | } |
593 | 0 |
|
594 | 0 | rec = (struct hvm_hw_hpet *)&h->data[h->cur]; |
595 | 0 | h->cur += HVM_SAVE_LENGTH(HPET); |
596 | 0 |
|
597 | 0 | #define C(x) hp->hpet.x = rec->x |
598 | 0 | C(capability); |
599 | 0 | C(config); |
600 | 0 | C(isr); |
601 | 0 | C(mc64); |
602 | 0 | /* The following define will generate a compiler error if HPET_TIMER_NUM |
603 | 0 | * changes. This indicates an incompatability with previous saved state. */ |
604 | 0 | #define HPET_TIMER_NUM 3 |
605 | 0 | for ( i = 0; i < HPET_TIMER_NUM; i++ ) |
606 | 0 | { |
607 | 0 | C(timers[i].config); |
608 | 0 | C(timers[i].fsb); |
609 | 0 | C(period[i]); |
610 | 0 | /* restore the hidden 64 bit comparator and truncate the timer's |
611 | 0 | * visible comparator field if in 32 bit mode. */ |
612 | 0 | cmp = rec->timers[i].cmp; |
613 | 0 | hp->hpet.comparator64[i] = cmp; |
614 | 0 | if ( timer_is_32bit(hp, i) ) |
615 | 0 | cmp = (uint32_t)cmp; |
616 | 0 | hp->hpet.timers[i].cmp = cmp; |
617 | 0 | } |
618 | 0 | #undef C |
619 | 0 |
|
620 | 0 | /* Recalculate the offset between the main counter and guest time */ |
621 | 0 | guest_time = guest_time_hpet(hp); |
622 | 0 | hp->mc_offset = hp->hpet.mc64 - guest_time; |
623 | 0 |
|
624 | 0 | /* restart all timers */ |
625 | 0 |
|
626 | 0 | if ( hpet_enabled(hp) ) |
627 | 0 | for ( i = 0; i < HPET_TIMER_NUM; i++ ) |
628 | 0 | if ( timer_enabled(hp, i) ) |
629 | 0 | hpet_set_timer(hp, i, guest_time); |
630 | 0 |
|
631 | 0 | write_unlock(&hp->lock); |
632 | 0 |
|
633 | 0 | return 0; |
634 | 0 | } |
635 | | |
636 | | HVM_REGISTER_SAVE_RESTORE(HPET, hpet_save, hpet_load, 1, HVMSR_PER_DOM); |
637 | | |
638 | | void hpet_init(struct domain *d) |
639 | 1 | { |
640 | 1 | HPETState *h = domain_vhpet(d); |
641 | 1 | int i; |
642 | 1 | |
643 | 1 | if ( !has_vhpet(d) ) |
644 | 1 | return; |
645 | 1 | |
646 | 0 | memset(h, 0, sizeof(HPETState)); |
647 | 0 |
|
648 | 0 | rwlock_init(&h->lock); |
649 | 0 |
|
650 | 0 | h->stime_freq = S_TO_NS; |
651 | 0 |
|
652 | 0 | h->hpet_to_ns_scale = ((S_TO_NS * STIME_PER_HPET_TICK) << 10) / h->stime_freq; |
653 | 0 | h->hpet_to_ns_limit = ~0ULL / h->hpet_to_ns_scale; |
654 | 0 |
|
655 | 0 | h->hpet.capability = 0x80860001ULL | |
656 | 0 | ((HPET_TIMER_NUM - 1) << HPET_ID_NUMBER_SHIFT) | |
657 | 0 | HPET_ID_64BIT | HPET_ID_LEGSUP; |
658 | 0 |
|
659 | 0 | /* This is the number of femptoseconds per HPET tick. */ |
660 | 0 | /* Here we define HPET's frequency to be 1/16 of Xen system time */ |
661 | 0 | h->hpet.capability |= ((S_TO_FS*STIME_PER_HPET_TICK/h->stime_freq) << 32); |
662 | 0 |
|
663 | 0 | for ( i = 0; i < HPET_TIMER_NUM; i++ ) |
664 | 0 | { |
665 | 0 | h->hpet.timers[i].config = |
666 | 0 | HPET_TN_INT_ROUTE_CAP | HPET_TN_64BIT_CAP | HPET_TN_PERIODIC_CAP; |
667 | 0 | h->hpet.timers[i].cmp = ~0ULL; |
668 | 0 | h->hpet.comparator64[i] = ~0ULL; |
669 | 0 | h->pt[i].source = PTSRC_isa; |
670 | 0 | } |
671 | 0 |
|
672 | 0 | register_mmio_handler(d, &hpet_mmio_ops); |
673 | 0 | d->arch.hvm_domain.params[HVM_PARAM_HPET_ENABLED] = 1; |
674 | 0 | } |
675 | | |
676 | | void hpet_deinit(struct domain *d) |
677 | 0 | { |
678 | 0 | int i; |
679 | 0 | HPETState *h = domain_vhpet(d); |
680 | 0 |
|
681 | 0 | if ( !has_vhpet(d) ) |
682 | 0 | return; |
683 | 0 |
|
684 | 0 | write_lock(&h->lock); |
685 | 0 |
|
686 | 0 | if ( hpet_enabled(h) ) |
687 | 0 | { |
688 | 0 | uint64_t guest_time = guest_time_hpet(h); |
689 | 0 |
|
690 | 0 | for ( i = 0; i < HPET_TIMER_NUM; i++ ) |
691 | 0 | if ( timer_enabled(h, i) ) |
692 | 0 | hpet_stop_timer(h, i, guest_time); |
693 | 0 | } |
694 | 0 |
|
695 | 0 | write_unlock(&h->lock); |
696 | 0 | } |
697 | | |
698 | | void hpet_reset(struct domain *d) |
699 | 0 | { |
700 | 0 | hpet_deinit(d); |
701 | 0 | hpet_init(d); |
702 | 0 | } |
703 | | |
704 | | /* |
705 | | * Local variables: |
706 | | * mode: C |
707 | | * c-file-style: "BSD" |
708 | | * c-basic-offset: 4 |
709 | | * indent-tabs-mode: nil |
710 | | * End: |
711 | | */ |