direct-io.hg

view xen/arch/x86/hvm/hpet.c @ 13320:36fd53b2e3b4

[HVM] Remove unneeded vcpu_kick() from HPET device model.
Signed-off-by: Keir Fraser <keir@xensource.com>
author kfraser@localhost.localdomain
date Wed Jan 10 14:14:30 2007 +0000 (2007-01-10)
parents a578c9703416
children e2539ab3580a
line source
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, write to the Free Software Foundation, Inc., 59 Temple
17 * Place - Suite 330, Boston, MA 02111-1307 USA.
18 */
20 #include <asm/hvm/vpt.h>
21 #include <asm/hvm/io.h>
22 #include <asm/hvm/support.h>
23 #include <asm/current.h>
24 #include <xen/sched.h>
25 #include <xen/event.h>
27 #define HPET_BASE_ADDRESS 0xfed00000ULL
28 #define HPET_MMAP_SIZE 1024
29 #define S_TO_NS 1000000000ULL /* 1s = 10^9 ns */
30 #define S_TO_FS 1000000000000000ULL /* 1s = 10^15 fs */
32 #define HPET_ID 0x000
33 #define HPET_PERIOD 0x004
34 #define HPET_CFG 0x010
35 #define HPET_STATUS 0x020
36 #define HPET_COUNTER 0x0f0
37 #define HPET_T0_CFG 0x100
38 #define HPET_T0_CMP 0x108
39 #define HPET_T0_ROUTE 0x110
40 #define HPET_T1_CFG 0x120
41 #define HPET_T1_CMP 0x128
42 #define HPET_T1_ROUTE 0x130
43 #define HPET_T2_CFG 0x140
44 #define HPET_T2_CMP 0x148
45 #define HPET_T2_ROUTE 0x150
46 #define HPET_T3_CFG 0x160
48 #define HPET_CFG_ENABLE 0x001
49 #define HPET_CFG_LEGACY 0x002
51 #define HPET_TN_INT_TYPE_LEVEL 0x002
52 #define HPET_TN_ENABLE 0x004
53 #define HPET_TN_PERIODIC 0x008
54 #define HPET_TN_PERIODIC_CAP 0x010
55 #define HPET_TN_SIZE_CAP 0x020
56 #define HPET_TN_SETVAL 0x040
57 #define HPET_TN_32BIT 0x100
58 #define HPET_TN_INT_ROUTE_MASK 0x3e00
59 #define HPET_TN_INT_ROUTE_SHIFT 9
60 #define HPET_TN_INT_ROUTE_CAP_SHIFT 32
61 #define HPET_TN_CFG_BITS_READONLY_OR_RESERVED 0xffff80b1U
63 /* can be routed to IOAPIC.redirect_table[23..20] */
64 #define HPET_TN_INT_ROUTE_CAP (0x00f00000ULL \
65 << HPET_TN_INT_ROUTE_CAP_SHIFT)
67 #define HPET_TN_INT_ROUTE_CAP_MASK (0xffffffffULL \
68 << HPET_TN_INT_ROUTE_CAP_SHIFT)
70 #define hpet_tick_to_ns(h, tick) ((s_time_t)(tick)*S_TO_NS/h->tsc_freq)
71 #define timer_config(h, n) (h->hpet.timers[n].config)
72 #define timer_enabled(h, n) (timer_config(h, n) & HPET_TN_ENABLE)
73 #define timer_is_periodic(h, n) (timer_config(h, n) & HPET_TN_PERIODIC)
74 #define timer_is_32bit(h, n) (timer_config(h, n) & HPET_TN_32BIT)
75 #define hpet_enabled(h) (h->hpet.config & HPET_CFG_ENABLE)
76 #define timer_level(h, n) (timer_config(h, n) & HPET_TN_INT_TYPE_LEVEL)
78 #define timer_int_route(h, n) \
79 ((timer_config(h, n) & HPET_TN_INT_ROUTE_MASK) >> HPET_TN_INT_ROUTE_SHIFT)
81 #define timer_int_route_cap(h, n) \
82 ((timer_config(h, n) & HPET_TN_INT_ROUTE_CAP_MASK) \
83 >> HPET_TN_INT_ROUTE_CAP_SHIFT)
85 #define hpet_time_after(a, b) ((int32_t)(b) - (int32_t)(a) < 0)
86 #define hpet_time_after64(a, b) ((int64_t)(b) - (int64_t)(a) < 0)
88 static inline uint64_t hpet_read64(HPETState *h, unsigned long addr)
89 {
90 uint64_t *p = (uint64_t *)(((unsigned long)&h->hpet) + addr);
91 return (addr >= HPET_T3_CFG) ? 0 : *p;
92 }
94 static inline int hpet_check_access_length(
95 unsigned long addr, unsigned long len)
96 {
97 if ( (addr & (len - 1)) || (len > 8) )
98 {
99 gdprintk(XENLOG_ERR, "HPET: access across register boundary: "
100 "%lx %lx\n", addr, len);
101 domain_crash(current->domain);
102 return -EINVAL;
103 }
105 return 0;
106 }
108 static inline uint64_t hpet_read_maincounter(HPETState *h)
109 {
110 if ( hpet_enabled(h) )
111 return hvm_get_guest_time(h->vcpu) + h->mc_offset;
112 else
113 return h->hpet.mc64;
114 }
116 static unsigned long hpet_read(
117 struct vcpu *v, unsigned long addr, unsigned long length)
118 {
119 HPETState *h = &v->domain->arch.hvm_domain.pl_time.vhpet;
120 unsigned long result;
121 uint64_t val;
123 addr &= HPET_MMAP_SIZE-1;
125 if ( hpet_check_access_length(addr, length) != 0 )
126 return ~0UL;
128 val = hpet_read64(h, addr & ~7);
129 if ( (addr & ~7) == HPET_COUNTER )
130 val = hpet_read_maincounter(h);
132 result = val;
133 if ( length != 8 )
134 result = (val >> ((addr & 7) * 8)) & ((1UL << (length * 8)) - 1);
136 return result;
137 }
139 static void hpet_stop_timer(HPETState *h, unsigned int tn)
140 {
141 ASSERT(tn < HPET_TIMER_NUM);
142 stop_timer(&h->timers[tn]);
143 }
145 static void hpet_set_timer(HPETState *h, unsigned int tn)
146 {
147 uint64_t tn_cmp, cur_tick;
149 ASSERT(tn < HPET_TIMER_NUM);
151 if ( !hpet_enabled(h) || !timer_enabled(h, tn) )
152 return;
154 if ( (tn == 0) && (h->hpet.config & HPET_CFG_LEGACY) )
155 {
156 /* HPET specification requires PIT shouldn't generate
157 * interrupts if LegacyReplacementRoute is set for timer0 */
158 PITState *pit = &h->vcpu->domain->arch.hvm_domain.pl_time.vpit;
159 pit_stop_channel0_irq(pit);
160 }
162 tn_cmp = h->hpet.timers[tn].cmp;
163 cur_tick = hpet_read_maincounter(h);
164 if ( timer_is_32bit(h, tn) )
165 {
166 tn_cmp = (uint32_t)tn_cmp;
167 cur_tick = (uint32_t)cur_tick;
168 }
170 if ( (int64_t)(tn_cmp - cur_tick) > 0 )
171 set_timer(&h->timers[tn], NOW() +
172 hpet_tick_to_ns(h, tn_cmp-cur_tick));
173 else
174 set_timer(&h->timers[tn], NOW());
175 }
177 static inline uint64_t hpet_fixup_reg(
178 uint64_t new, uint64_t old, uint64_t mask)
179 {
180 new &= mask;
181 new |= old & ~mask;
182 return new;
183 }
185 static void hpet_write(
186 struct vcpu *v, unsigned long addr,
187 unsigned long length, unsigned long val)
188 {
189 HPETState *h = &v->domain->arch.hvm_domain.pl_time.vhpet;
190 uint64_t old_val, new_val;
191 int tn, i;
193 addr &= HPET_MMAP_SIZE-1;
195 if ( hpet_check_access_length(addr, length) != 0 )
196 return;
198 old_val = hpet_read64(h, addr & ~7);
199 if ( (addr & ~7) == HPET_COUNTER )
200 old_val = hpet_read_maincounter(h);
202 new_val = val;
203 if ( length != 8 )
204 new_val = hpet_fixup_reg(
205 new_val << (addr & 7) * 8, old_val,
206 ((1ULL << (length*8)) - 1) << ((addr & 7) * 8));
208 switch ( addr & ~7 )
209 {
210 case HPET_CFG:
211 h->hpet.config = hpet_fixup_reg(new_val, old_val, 0x3);
213 if ( !(old_val & HPET_CFG_ENABLE) && (new_val & HPET_CFG_ENABLE) )
214 {
215 /* Enable main counter and interrupt generation. */
216 h->mc_offset = h->hpet.mc64 - hvm_get_guest_time(h->vcpu);
217 for ( i = 0; i < HPET_TIMER_NUM; i++ )
218 hpet_set_timer(h, i);
219 }
220 else if ( (old_val & HPET_CFG_ENABLE) && !(new_val & HPET_CFG_ENABLE) )
221 {
222 /* Halt main counter and disable interrupt generation. */
223 h->hpet.mc64 = h->mc_offset + hvm_get_guest_time(h->vcpu);
224 for ( i = 0; i < HPET_TIMER_NUM; i++ )
225 hpet_stop_timer(h, i);
226 }
227 break;
229 case HPET_COUNTER:
230 if ( hpet_enabled(h) )
231 gdprintk(XENLOG_WARNING,
232 "HPET: writing main counter but it's not halted!\n");
233 h->hpet.mc64 = new_val;
234 break;
236 case HPET_T0_CFG:
237 case HPET_T1_CFG:
238 case HPET_T2_CFG:
239 tn = (addr - HPET_T0_CFG) >> 5;
241 h->hpet.timers[tn].config = hpet_fixup_reg(new_val, old_val, 0x3f4e);
243 if ( timer_level(h, tn) )
244 {
245 gdprintk(XENLOG_ERR,
246 "HPET: level triggered interrupt not supported now\n");
247 domain_crash(current->domain);
248 break;
249 }
251 if ( new_val & HPET_TN_32BIT )
252 h->hpet.timers[tn].cmp = (uint32_t)h->hpet.timers[tn].cmp;
254 if ( !(old_val & HPET_TN_ENABLE) && (new_val & HPET_TN_ENABLE) )
255 hpet_set_timer(h, tn);
256 else if ( (old_val & HPET_TN_ENABLE) && !(new_val & HPET_TN_ENABLE) )
257 hpet_stop_timer(h, tn);
258 break;
260 case HPET_T0_CMP:
261 case HPET_T1_CMP:
262 case HPET_T2_CMP:
263 tn = (addr - HPET_T0_CMP) >> 5;
264 if ( timer_is_32bit(h, tn) )
265 new_val = (uint32_t)new_val;
266 if ( !timer_is_periodic(h, tn) ||
267 (h->hpet.timers[tn].config & HPET_TN_SETVAL) )
268 h->hpet.timers[tn].cmp = new_val;
269 else
270 h->period[tn] = new_val;
271 h->hpet.timers[tn].config &= ~HPET_TN_SETVAL;
272 if ( hpet_enabled(h) && timer_enabled(h, tn) )
273 hpet_set_timer(h, tn);
274 break;
276 case HPET_T0_ROUTE:
277 case HPET_T1_ROUTE:
278 case HPET_T2_ROUTE:
279 tn = (addr - HPET_T0_ROUTE) >> 5;
280 h->hpet.timers[tn].hpet_fsb[0] = new_val;
281 break;
283 default:
284 /* Ignore writes to unsupported and reserved registers. */
285 break;
286 }
287 }
289 static int hpet_range(struct vcpu *v, unsigned long addr)
290 {
291 return ((addr >= HPET_BASE_ADDRESS) &&
292 (addr < (HPET_BASE_ADDRESS + HPET_MMAP_SIZE)));
293 }
295 struct hvm_mmio_handler hpet_mmio_handler = {
296 .check_handler = hpet_range,
297 .read_handler = hpet_read,
298 .write_handler = hpet_write
299 };
301 static void hpet_route_interrupt(HPETState *h, unsigned int tn)
302 {
303 unsigned int tn_int_route = timer_int_route(h, tn);
304 struct domain *d = h->vcpu->domain;
305 struct hvm_irq *hvm_irq = &d->arch.hvm_domain.irq;
307 if ( (tn <= 1) && (h->hpet.config & HPET_CFG_LEGACY) )
308 {
309 /* if LegacyReplacementRoute bit is set, HPET specification requires
310 timer0 be routed to IRQ0 in NON-APIC or IRQ2 in the I/O APIC,
311 timer1 be routed to IRQ8 in NON-APIC or IRQ8 in the I/O APIC. */
312 int isa_irq = (tn == 0) ? 0 : 8;
313 hvm_isa_irq_deassert(d, isa_irq);
314 hvm_isa_irq_assert(d, isa_irq);
315 return;
316 }
318 if ( !(timer_int_route_cap(h, tn) & (1U << tn_int_route)) )
319 {
320 gdprintk(XENLOG_ERR,
321 "HPET: timer%u: invalid interrupt route config\n", tn);
322 domain_crash(d);
323 return;
324 }
326 /* We only support edge-triggered interrupt now */
327 spin_lock(&hvm_irq->lock);
328 vioapic_irq_positive_edge(d, tn_int_route);
329 spin_unlock(&hvm_irq->lock);
330 }
332 static void hpet_timer_fn(void *opaque)
333 {
334 struct HPET_timer_fn_info *htfi = opaque;
335 HPETState *h = htfi->hs;
336 unsigned int tn = htfi->tn;
338 if ( !hpet_enabled(h) || !timer_enabled(h, tn) )
339 return;
341 hpet_route_interrupt(h, tn);
343 if ( timer_is_periodic(h, tn) && (h->period[tn] != 0) )
344 {
345 uint64_t mc = hpet_read_maincounter(h);
346 if ( timer_is_32bit(h, tn) )
347 {
348 while ( hpet_time_after(mc, h->hpet.timers[tn].cmp) )
349 h->hpet.timers[tn].cmp = (uint32_t)(
350 h->hpet.timers[tn].cmp + h->period[tn]);
351 }
352 else
353 {
354 while ( hpet_time_after64(mc, h->hpet.timers[tn].cmp) )
355 h->hpet.timers[tn].cmp += h->period[tn];
356 }
357 set_timer(&h->timers[tn], NOW() + hpet_tick_to_ns(h, h->period[tn]));
358 }
359 }
361 void hpet_migrate_timers(struct vcpu *v)
362 {
363 struct HPETState *h = &v->domain->arch.hvm_domain.pl_time.vhpet;
364 int i;
366 for ( i = 0; i < HPET_TIMER_NUM; i++ )
367 migrate_timer(&h->timers[i], v->processor);
368 }
370 void hpet_init(struct vcpu *v)
371 {
372 HPETState *h = &v->domain->arch.hvm_domain.pl_time.vhpet;
373 int i;
375 memset(h, 0, sizeof(HPETState));
377 h->vcpu = v;
378 h->tsc_freq = ticks_per_sec(v);
380 /* 64-bit main counter; 3 timers supported; LegacyReplacementRoute. */
381 h->hpet.capability = 0x8086A201ULL;
383 /* This is the number of femptoseconds per HPET tick. */
384 /* Here we define HPET's frequency to be the same as the TSC's. */
385 h->hpet.capability |= ((S_TO_FS/h->tsc_freq) << 32);
387 for ( i = 0; i < HPET_TIMER_NUM; i++ )
388 {
389 h->hpet.timers[i].config =
390 HPET_TN_INT_ROUTE_CAP | HPET_TN_SIZE_CAP | HPET_TN_PERIODIC_CAP;
391 h->hpet.timers[i].cmp = ~0ULL;
392 h->timer_fn_info[i].hs = h;
393 h->timer_fn_info[i].tn = i;
394 init_timer(&h->timers[i], hpet_timer_fn, &h->timer_fn_info[i],
395 v->processor);
396 }
397 }
399 void hpet_deinit(struct domain *d)
400 {
401 int i;
402 HPETState *h = &d->arch.hvm_domain.pl_time.vhpet;
404 for ( i = 0; i < HPET_TIMER_NUM; i++ )
405 kill_timer(&h->timers[i]);
406 }