debuggers.hg

view xen/arch/ia64/vmx/mmio.c @ 16392:91575bb23d07

[IA64] vti save-restore: hvm domain io page clean up.

- set_hvm_param hypercall clean up.
- The reference counts of the io pages must be incremented.
- Buffered pio wasn't SMP safe.
- Clean up get_vio() parameter.

Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
author Alex Williamson <alex.williamson@hp.com>
date Wed Nov 07 10:31:09 2007 -0700 (2007-11-07)
parents cbf8224779c6
children ee935d2b8a63
line source
1 /* -*- Mode:C; c-basic-offset:4; tab-width:4; indent-tabs-mode:nil -*- */
2 /*
3 * mmio.c: MMIO emulation components.
4 * Copyright (c) 2004, Intel Corporation.
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 *
19 * Yaozu Dong (Eddie Dong) (Eddie.dong@intel.com)
20 * Kun Tian (Kevin Tian) (Kevin.tian@intel.com)
21 */
23 #include <linux/sched.h>
24 #include <xen/mm.h>
25 #include <asm/vmx_mm_def.h>
26 #include <asm/gcc_intrin.h>
27 #include <linux/interrupt.h>
28 #include <asm/vmx_vcpu.h>
29 #include <asm/bundle.h>
30 #include <asm/types.h>
31 #include <public/hvm/ioreq.h>
32 #include <asm/vmx.h>
33 #include <public/event_channel.h>
34 #include <public/xen.h>
35 #include <linux/event.h>
36 #include <xen/domain.h>
37 #include <asm/viosapic.h>
38 #include <asm/vlsapic.h>
39 #include <asm/hvm/vacpi.h>
41 #define HVM_BUFFERED_IO_RANGE_NR 1
43 struct hvm_buffered_io_range {
44 unsigned long start_addr;
45 unsigned long length;
46 };
48 static struct hvm_buffered_io_range buffered_stdvga_range = {0xA0000, 0x20000};
49 static struct hvm_buffered_io_range
50 *hvm_buffered_io_ranges[HVM_BUFFERED_IO_RANGE_NR] =
51 {
52 &buffered_stdvga_range
53 };
55 static int hvm_buffered_io_intercept(ioreq_t *p)
56 {
57 struct vcpu *v = current;
58 buffered_iopage_t *pg =
59 (buffered_iopage_t *)(v->domain->arch.hvm_domain.buf_ioreq.va);
60 buf_ioreq_t bp;
61 int i, qw = 0;
63 /* Ensure buffered_iopage fits in a page */
64 BUILD_BUG_ON(sizeof(buffered_iopage_t) > PAGE_SIZE);
66 /* ignore READ ioreq_t and anything buffered io can't deal with */
67 if (p->dir == IOREQ_READ || p->addr > 0xFFFFFUL ||
68 p->data_is_ptr || p->df || p->count != 1)
69 return 0;
71 for (i = 0; i < HVM_BUFFERED_IO_RANGE_NR; i++) {
72 if (p->addr >= hvm_buffered_io_ranges[i]->start_addr &&
73 p->addr + p->size - 1 < hvm_buffered_io_ranges[i]->start_addr +
74 hvm_buffered_io_ranges[i]->length)
75 break;
76 }
78 if (i == HVM_BUFFERED_IO_RANGE_NR)
79 return 0;
81 bp.type = p->type;
82 bp.dir = p->dir;
83 switch (p->size) {
84 case 1:
85 bp.size = 0;
86 break;
87 case 2:
88 bp.size = 1;
89 break;
90 case 4:
91 bp.size = 2;
92 break;
93 case 8:
94 bp.size = 3;
95 qw = 1;
96 break;
97 default:
98 gdprintk(XENLOG_WARNING, "unexpected ioreq size:%"PRId64"\n", p->size);
99 return 0;
100 }
101 bp.data = p->data;
102 bp.addr = p->addr;
104 spin_lock(&v->domain->arch.hvm_domain.buf_ioreq.lock);
106 if (pg->write_pointer - pg->read_pointer >= IOREQ_BUFFER_SLOT_NUM - qw) {
107 /* the queue is full.
108 * send the iopacket through the normal path.
109 * NOTE: The arithimetic operation could handle the situation for
110 * write_pointer overflow.
111 */
112 spin_unlock(&v->domain->arch.hvm_domain.buf_ioreq.lock);
113 return 0;
114 }
116 memcpy(&pg->buf_ioreq[pg->write_pointer % IOREQ_BUFFER_SLOT_NUM],
117 &bp, sizeof(bp));
119 if (qw) {
120 bp.data = p->data >> 32;
121 memcpy(&pg->buf_ioreq[(pg->write_pointer + 1) % IOREQ_BUFFER_SLOT_NUM],
122 &bp, sizeof(bp));
123 }
125 /* Make the ioreq_t visible before write_pointer */
126 wmb();
127 pg->write_pointer += qw ? 2 : 1;
129 spin_unlock(&v->domain->arch.hvm_domain.buf_ioreq.lock);
131 return 1;
132 }
134 static void low_mmio_access(VCPU *vcpu, u64 pa, u64 *val, size_t s, int dir)
135 {
136 struct vcpu *v = current;
137 vcpu_iodata_t *vio;
138 ioreq_t *p;
140 vio = get_vio(v);
141 if (!vio)
142 panic_domain(NULL, "bad shared page");
144 p = &vio->vp_ioreq;
146 p->addr = pa;
147 p->size = s;
148 p->count = 1;
149 if (dir == IOREQ_WRITE)
150 p->data = *val;
151 else
152 p->data = 0;
153 p->data_is_ptr = 0;
154 p->dir = dir;
155 p->df = 0;
156 p->type = 1;
158 p->io_count++;
160 if (hvm_buffered_io_intercept(p)) {
161 p->state = STATE_IORESP_READY;
162 vmx_io_assist(v);
163 if (dir != IOREQ_READ)
164 return;
165 }
167 vmx_send_assist_req(v);
168 if (dir == IOREQ_READ)
169 *val = p->data;
171 return;
172 }
174 static int vmx_ide_pio_intercept(ioreq_t *p, u64 *val)
175 {
176 struct buffered_piopage *pio_page =
177 (void *)(current->domain->arch.hvm_domain.buf_pioreq.va);
178 spinlock_t *pio_lock;
179 struct pio_buffer *piobuf;
180 uint32_t pointer, page_offset;
182 if (p->addr == 0x1F0)
183 piobuf = &pio_page->pio[PIO_BUFFER_IDE_PRIMARY];
184 else if (p->addr == 0x170)
185 piobuf = &pio_page->pio[PIO_BUFFER_IDE_SECONDARY];
186 else
187 return 0;
189 if (p->size != 2 && p->size != 4)
190 return 0;
192 pio_lock = &current->domain->arch.hvm_domain.buf_pioreq.lock;
193 spin_lock(pio_lock);
195 pointer = piobuf->pointer;
196 page_offset = piobuf->page_offset;
198 /* sanity check */
199 if (page_offset + pointer < offsetof(struct buffered_piopage, buffer))
200 goto unlock_out;
201 if (page_offset + piobuf->data_end > PAGE_SIZE)
202 goto unlock_out;
204 if (pointer + p->size < piobuf->data_end) {
205 uint8_t *bufp = (uint8_t *)pio_page + page_offset + pointer;
206 if (p->dir == IOREQ_WRITE) {
207 if (likely(p->size == 4 && (((long)bufp & 3) == 0)))
208 *(uint32_t *)bufp = *val;
209 else
210 memcpy(bufp, val, p->size);
211 } else {
212 if (likely(p->size == 4 && (((long)bufp & 3) == 0))) {
213 *val = *(uint32_t *)bufp;
214 } else {
215 *val = 0;
216 memcpy(val, bufp, p->size);
217 }
218 }
219 piobuf->pointer += p->size;
220 spin_unlock(pio_lock);
222 p->state = STATE_IORESP_READY;
223 vmx_io_assist(current);
224 return 1;
225 }
227 unlock_out:
228 spin_unlock(pio_lock);
229 return 0;
230 }
232 #define TO_LEGACY_IO(pa) (((pa)>>12<<2)|((pa)&0x3))
234 static const char * const guest_os_name[] = {
235 "Unknown",
236 "Windows 2003 server",
237 "Linux",
238 };
240 static inline void set_os_type(VCPU *v, u64 type)
241 {
242 if (type > OS_BASE && type < OS_END) {
243 v->domain->arch.vmx_platform.gos_type = type;
244 gdprintk(XENLOG_INFO, "Guest OS : %s\n", guest_os_name[type - OS_BASE]);
246 if (GOS_WINDOWS(v)) {
247 struct xen_ia64_opt_feature optf;
249 /* Windows identity maps regions 4 & 5 */
250 optf.cmd = XEN_IA64_OPTF_IDENT_MAP_REG4;
251 optf.on = XEN_IA64_OPTF_ON;
252 optf.pgprot = (_PAGE_P|_PAGE_A|_PAGE_D|_PAGE_MA_WB|_PAGE_AR_RW);
253 optf.key = 0;
254 domain_opt_feature(&optf);
256 optf.cmd = XEN_IA64_OPTF_IDENT_MAP_REG5;
257 optf.pgprot = (_PAGE_P|_PAGE_A|_PAGE_D|_PAGE_MA_UC|_PAGE_AR_RW);
258 domain_opt_feature(&optf);
259 }
260 }
261 }
264 static void legacy_io_access(VCPU *vcpu, u64 pa, u64 *val, size_t s, int dir)
265 {
266 struct vcpu *v = current;
267 vcpu_iodata_t *vio;
268 ioreq_t *p;
270 vio = get_vio(v);
271 if (!vio)
272 panic_domain(NULL, "bad shared page\n");
274 p = &vio->vp_ioreq;
275 p->addr = TO_LEGACY_IO(pa & 0x3ffffffUL);
276 p->size = s;
277 p->count = 1;
278 p->dir = dir;
279 if (dir == IOREQ_WRITE)
280 p->data = *val;
281 else
282 p->data = 0;
283 p->data_is_ptr = 0;
284 p->type = 0;
285 p->df = 0;
287 p->io_count++;
289 if (dir == IOREQ_WRITE && p->addr == OS_TYPE_PORT) {
290 set_os_type(v, *val);
291 return;
292 }
294 if (vmx_ide_pio_intercept(p, val))
295 return;
297 if (IS_ACPI_ADDR(p->addr) && vacpi_intercept(p, val))
298 return;
300 vmx_send_assist_req(v);
301 if (dir == IOREQ_READ) { // read
302 *val=p->data;
303 }
304 #ifdef DEBUG_PCI
305 if (dir == IOREQ_WRITE)
306 if (p->addr == 0xcf8UL)
307 printk("Write 0xcf8, with val [0x%lx]\n", p->data);
308 else
309 if (p->addr == 0xcfcUL)
310 printk("Read 0xcfc, with val [0x%lx]\n", p->data);
311 #endif //DEBUG_PCI
312 return;
313 }
315 static void mmio_access(VCPU *vcpu, u64 src_pa, u64 *dest, size_t s, int ma, int dir)
316 {
317 unsigned long iot;
318 iot = __gpfn_is_io(vcpu->domain, src_pa >> PAGE_SHIFT);
320 perfc_incra(vmx_mmio_access, iot >> 56);
321 switch (iot) {
322 case GPFN_PIB:
323 if (ma != 4)
324 panic_domain(NULL, "Access PIB not with UC attribute\n");
326 if (!dir)
327 vlsapic_write(vcpu, src_pa, s, *dest);
328 else
329 *dest = vlsapic_read(vcpu, src_pa, s);
330 break;
331 case GPFN_GFW:
332 break;
333 case GPFN_IOSAPIC:
334 if (!dir)
335 viosapic_write(vcpu, src_pa, s, *dest);
336 else
337 *dest = viosapic_read(vcpu, src_pa, s);
338 break;
339 case GPFN_FRAME_BUFFER:
340 case GPFN_LOW_MMIO:
341 low_mmio_access(vcpu, src_pa, dest, s, dir);
342 break;
343 case GPFN_LEGACY_IO:
344 legacy_io_access(vcpu, src_pa, dest, s, dir);
345 break;
346 default:
347 panic_domain(NULL,"Bad I/O access\n");
348 break;
349 }
350 return;
351 }
353 /*
354 dir 1: read 0:write
355 */
356 void emulate_io_inst(VCPU *vcpu, u64 padr, u64 ma)
357 {
358 REGS *regs;
359 IA64_BUNDLE bundle;
360 int slot, dir=0;
361 enum { SL_INTEGER, SL_FLOATING, SL_FLOATING_FP8 } inst_type;
362 size_t size;
363 u64 data, data1, temp, update_reg;
364 s32 imm;
365 INST64 inst;
367 regs = vcpu_regs(vcpu);
368 if (IA64_RETRY == __vmx_get_domain_bundle(regs->cr_iip, &bundle)) {
369 /* if fetch code fail, return and try again */
370 return;
371 }
372 slot = ((struct ia64_psr *)&(regs->cr_ipsr))->ri;
373 if (!slot)
374 inst.inst = bundle.slot0;
375 else if (slot == 1) {
376 u64 slot1b = bundle.slot1b;
377 inst.inst = bundle.slot1a + (slot1b << 18);
378 }
379 else if (slot == 2)
380 inst.inst = bundle.slot2;
383 // Integer Load/Store
384 if (inst.M1.major == 4 && inst.M1.m == 0 && inst.M1.x == 0) {
385 inst_type = SL_INTEGER;
386 size = (inst.M1.x6 & 0x3);
387 if ((inst.M1.x6 >> 2) > 0xb) {
388 dir = IOREQ_WRITE;
389 vcpu_get_gr_nat(vcpu, inst.M4.r2, &data);
390 } else if ((inst.M1.x6 >> 2) < 0xb) {
391 dir = IOREQ_READ;
392 }
393 }
394 // Integer Load + Reg update
395 else if (inst.M2.major == 4 && inst.M2.m == 1 && inst.M2.x == 0) {
396 inst_type = SL_INTEGER;
397 dir = IOREQ_READ;
398 size = (inst.M2.x6 & 0x3);
399 vcpu_get_gr_nat(vcpu, inst.M2.r3, &temp);
400 vcpu_get_gr_nat(vcpu, inst.M2.r2, &update_reg);
401 temp += update_reg;
402 vcpu_set_gr(vcpu, inst.M2.r3, temp, 0);
403 }
404 // Integer Load/Store + Imm update
405 else if (inst.M3.major == 5) {
406 inst_type = SL_INTEGER;
407 size = (inst.M3.x6 & 0x3);
408 if ((inst.M5.x6 >> 2) > 0xb) {
409 dir = IOREQ_WRITE;
410 vcpu_get_gr_nat(vcpu, inst.M5.r2, &data);
411 vcpu_get_gr_nat(vcpu, inst.M5.r3, &temp);
412 imm = (inst.M5.s << 31) | (inst.M5.i << 30) | (inst.M5.imm7 << 23);
413 temp += imm >> 23;
414 vcpu_set_gr(vcpu, inst.M5.r3, temp, 0);
415 } else if ((inst.M3.x6 >> 2) < 0xb) {
416 dir = IOREQ_READ;
417 vcpu_get_gr_nat(vcpu, inst.M3.r3, &temp);
418 imm = (inst.M3.s << 31) | (inst.M3.i << 30) | (inst.M3.imm7 << 23);
419 temp += imm >> 23;
420 vcpu_set_gr(vcpu, inst.M3.r3, temp, 0);
421 }
422 }
423 // Floating-point spill
424 else if (inst.M9.major == 6 && inst.M9.x6 == 0x3B &&
425 inst.M9.m == 0 && inst.M9.x == 0) {
426 struct ia64_fpreg v;
428 inst_type = SL_FLOATING;
429 dir = IOREQ_WRITE;
430 vcpu_get_fpreg(vcpu, inst.M9.f2, &v);
431 data1 = v.u.bits[1] & 0x3ffff;
432 data = v.u.bits[0];
433 size = 4;
434 }
435 // Floating-point spill + Imm update
436 else if (inst.M10.major == 7 && inst.M10.x6 == 0x3B) {
437 struct ia64_fpreg v;
439 inst_type = SL_FLOATING;
440 dir = IOREQ_WRITE;
441 vcpu_get_fpreg(vcpu, inst.M10.f2, &v);
442 vcpu_get_gr_nat(vcpu, inst.M10.r3, &temp);
443 imm = (inst.M10.s << 31) | (inst.M10.i << 30) | (inst.M10.imm7 << 23);
444 temp += imm >> 23;
445 vcpu_set_gr(vcpu, inst.M10.r3, temp, 0);
446 data1 = v.u.bits[1] & 0x3ffff;
447 data = v.u.bits[0];
448 size = 4;
449 }
450 // Floating-point stf8 + Imm update
451 else if (inst.M10.major == 7 && inst.M10.x6 == 0x31) {
452 struct ia64_fpreg v;
454 inst_type = SL_FLOATING;
455 dir = IOREQ_WRITE;
456 size = 3;
457 vcpu_get_fpreg(vcpu, inst.M10.f2, &v);
458 data = v.u.bits[0]; /* Significand. */
459 vcpu_get_gr_nat(vcpu, inst.M10.r3, &temp);
460 imm = (inst.M10.s << 31) | (inst.M10.i << 30) | (inst.M10.imm7 << 23);
461 temp += imm >> 23;
462 vcpu_set_gr(vcpu, inst.M10.r3, temp, 0);
463 }
464 // lfetch - do not perform accesses.
465 else if (inst.M15.major== 7 && inst.M15.x6 >=0x2c && inst.M15.x6 <= 0x2f) {
466 vcpu_get_gr_nat(vcpu, inst.M15.r3, &temp);
467 imm = (inst.M15.s << 31) | (inst.M15.i << 30) | (inst.M15.imm7 << 23);
468 temp += imm >> 23;
469 vcpu_set_gr(vcpu, inst.M15.r3, temp, 0);
471 vcpu_increment_iip(vcpu);
472 return;
473 }
474 // Floating-point Load Pair + Imm ldfp8 M12
475 else if (inst.M12.major == 6 && inst.M12.m == 1
476 && inst.M12.x == 1 && inst.M12.x6 == 1) {
477 inst_type = SL_FLOATING_FP8;
478 dir = IOREQ_READ;
479 size = 4; //ldfd
480 vcpu_set_gr(vcpu,inst.M12.r3,padr + 16, 0);
481 }
482 else {
483 panic_domain
484 (NULL, "This memory access instr can't be emulated: %lx pc=%lx\n",
485 inst.inst, regs->cr_iip);
486 }
488 if (size == 4) {
489 mmio_access(vcpu, padr + 8, &data1, 1 << 3, ma, dir);
490 size = 3;
491 }
492 mmio_access(vcpu, padr, &data, 1 << size, ma, dir);
494 if (dir == IOREQ_READ) {
495 if (inst_type == SL_INTEGER) {
496 vcpu_set_gr(vcpu, inst.M1.r1, data, 0);
497 } else if (inst_type == SL_FLOATING_FP8) {
498 struct ia64_fpreg v;
500 v.u.bits[0] = data;
501 v.u.bits[1] = 0x1003E;
502 vcpu_set_fpreg(vcpu, inst.M12.f1, &v);
503 v.u.bits[0] = data1;
504 v.u.bits[1] = 0x1003E;
505 vcpu_set_fpreg(vcpu, inst.M12.f2, &v);
506 } else {
507 panic_domain(NULL, "Don't support ldfd now !");
508 }
509 }
510 vcpu_increment_iip(vcpu);
511 }