aboutsummaryrefslogtreecommitdiffstats
path: root/tools/lguest
diff options
context:
space:
mode:
authorRusty Russell <rusty@rustcorp.com.au>2015-02-10 23:45:11 -0500
committerRusty Russell <rusty@rustcorp.com.au>2015-02-11 01:17:35 -0500
commit6a54f9ab0d65a2095de50160b8ca7ce6469aaac0 (patch)
tree20ed18c8bcc4d92b235642b94466eec1729de4fc /tools/lguest
parent0a6bcc183f5377eca07cbf0cf6f4b6cb00e4c1ec (diff)
lguest: decode mmio accesses for PCI in example launcher.
We don't do anything with them yet (emulate_mmio_write and emulate_mmio_read are stubs), but we decode the instructions and search for the device they're hitting. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Diffstat (limited to 'tools/lguest')
-rw-r--r--tools/lguest/lguest.c167
1 files changed, 167 insertions, 0 deletions
diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c
index 35d7aa90aa24..e52a3571076a 100644
--- a/tools/lguest/lguest.c
+++ b/tools/lguest/lguest.c
@@ -99,6 +99,9 @@ static int lguest_fd;
99/* a per-cpu variable indicating whose vcpu is currently running */ 99/* a per-cpu variable indicating whose vcpu is currently running */
100static unsigned int __thread cpu_id; 100static unsigned int __thread cpu_id;
101 101
102/* 5 bit device number in the PCI_CONFIG_ADDR => 32 only */
103#define MAX_PCI_DEVICES 32
104
102/* This is our list of devices. */ 105/* This is our list of devices. */
103struct device_list { 106struct device_list {
104 /* Counter to assign interrupt numbers. */ 107 /* Counter to assign interrupt numbers. */
@@ -114,6 +117,9 @@ struct device_list {
114 struct device *dev; 117 struct device *dev;
115 /* And a pointer to the last device for easy append. */ 118 /* And a pointer to the last device for easy append. */
116 struct device *lastdev; 119 struct device *lastdev;
120
121 /* PCI devices. */
122 struct device *pci[MAX_PCI_DEVICES];
117}; 123};
118 124
119/* The list of Guest devices, based on command line arguments. */ 125/* The list of Guest devices, based on command line arguments. */
@@ -140,6 +146,10 @@ struct device {
140 /* Is it operational */ 146 /* Is it operational */
141 bool running; 147 bool running;
142 148
149 /* PCI MMIO resources (all in BAR0) */
150 size_t mmio_size;
151 u32 mmio_addr;
152
143 /* Device-specific data. */ 153 /* Device-specific data. */
144 void *priv; 154 void *priv;
145}; 155};
@@ -1197,6 +1207,77 @@ static void setreg_off(size_t offset, u32 val)
1197 err(1, "Setting register %u", offset); 1207 err(1, "Setting register %u", offset);
1198} 1208}
1199 1209
1210/* Get register by instruction encoding */
1211static u32 getreg_num(unsigned regnum, u32 mask)
1212{
1213 /* 8 bit ops use regnums 4-7 for high parts of word */
1214 if (mask == 0xFF && (regnum & 0x4))
1215 return getreg_num(regnum & 0x3, 0xFFFF) >> 8;
1216
1217 switch (regnum) {
1218 case 0: return getreg(eax) & mask;
1219 case 1: return getreg(ecx) & mask;
1220 case 2: return getreg(edx) & mask;
1221 case 3: return getreg(ebx) & mask;
1222 case 4: return getreg(esp) & mask;
1223 case 5: return getreg(ebp) & mask;
1224 case 6: return getreg(esi) & mask;
1225 case 7: return getreg(edi) & mask;
1226 }
1227 abort();
1228}
1229
1230/* Set register by instruction encoding */
1231static void setreg_num(unsigned regnum, u32 val, u32 mask)
1232{
1233 /* Don't try to set bits out of range */
1234 assert(~(val & ~mask));
1235
1236 /* 8 bit ops use regnums 4-7 for high parts of word */
1237 if (mask == 0xFF && (regnum & 0x4)) {
1238 /* Construct the 16 bits we want. */
1239 val = (val << 8) | getreg_num(regnum & 0x3, 0xFF);
1240 setreg_num(regnum & 0x3, val, 0xFFFF);
1241 return;
1242 }
1243
1244 switch (regnum) {
1245 case 0: setreg(eax, val | (getreg(eax) & ~mask)); return;
1246 case 1: setreg(ecx, val | (getreg(ecx) & ~mask)); return;
1247 case 2: setreg(edx, val | (getreg(edx) & ~mask)); return;
1248 case 3: setreg(ebx, val | (getreg(ebx) & ~mask)); return;
1249 case 4: setreg(esp, val | (getreg(esp) & ~mask)); return;
1250 case 5: setreg(ebp, val | (getreg(ebp) & ~mask)); return;
1251 case 6: setreg(esi, val | (getreg(esi) & ~mask)); return;
1252 case 7: setreg(edi, val | (getreg(edi) & ~mask)); return;
1253 }
1254 abort();
1255}
1256
1257/* Get bytes of displacement appended to instruction, from r/m encoding */
1258static u32 insn_displacement_len(u8 mod_reg_rm)
1259{
1260 /* Switch on the mod bits */
1261 switch (mod_reg_rm >> 6) {
1262 case 0:
1263 /* If mod == 0, and r/m == 101, 16-bit displacement follows */
1264 if ((mod_reg_rm & 0x7) == 0x5)
1265 return 2;
1266 /* Normally, mod == 0 means no literal displacement */
1267 return 0;
1268 case 1:
1269 /* One byte displacement */
1270 return 1;
1271 case 2:
1272 /* Four byte displacement */
1273 return 4;
1274 case 3:
1275 /* Register mode */
1276 return 0;
1277 }
1278 abort();
1279}
1280
1200static void emulate_insn(const u8 insn[]) 1281static void emulate_insn(const u8 insn[])
1201{ 1282{
1202 unsigned long args[] = { LHREQ_TRAP, 13 }; 1283 unsigned long args[] = { LHREQ_TRAP, 13 };
@@ -1310,6 +1391,88 @@ no_emulate:
1310 err(1, "Reinjecting trap 13 for fault at %#x", getreg(eip)); 1391 err(1, "Reinjecting trap 13 for fault at %#x", getreg(eip));
1311} 1392}
1312 1393
1394static struct device *find_mmio_region(unsigned long paddr, u32 *off)
1395{
1396 unsigned int i;
1397
1398 for (i = 1; i < MAX_PCI_DEVICES; i++) {
1399 struct device *d = devices.pci[i];
1400
1401 if (!d)
1402 continue;
1403 if (paddr < d->mmio_addr)
1404 continue;
1405 if (paddr >= d->mmio_addr + d->mmio_size)
1406 continue;
1407 *off = paddr - d->mmio_addr;
1408 return d;
1409 }
1410 return NULL;
1411}
1412
1413static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask)
1414{
1415}
1416
1417static u32 emulate_mmio_read(struct device *d, u32 off, u32 mask)
1418{
1419 return 0xFFFFFFFF;
1420}
1421
1422static void emulate_mmio(unsigned long paddr, const u8 *insn)
1423{
1424 u32 val, off, mask = 0xFFFFFFFF, insnlen = 0;
1425 struct device *d = find_mmio_region(paddr, &off);
1426 unsigned long args[] = { LHREQ_TRAP, 14 };
1427
1428 if (!d) {
1429 warnx("MMIO touching %#08lx (not a device)", paddr);
1430 goto reinject;
1431 }
1432
1433 /* Prefix makes it a 16 bit op */
1434 if (insn[0] == 0x66) {
1435 mask = 0xFFFF;
1436 insnlen++;
1437 }
1438
1439 /* iowrite */
1440 if (insn[insnlen] == 0x89) {
1441 /* Next byte is r/m byte: bits 3-5 are register. */
1442 val = getreg_num((insn[insnlen+1] >> 3) & 0x7, mask);
1443 emulate_mmio_write(d, off, val, mask);
1444 insnlen += 2 + insn_displacement_len(insn[insnlen+1]);
1445 } else if (insn[insnlen] == 0x8b) { /* ioread */
1446 /* Next byte is r/m byte: bits 3-5 are register. */
1447 val = emulate_mmio_read(d, off, mask);
1448 setreg_num((insn[insnlen+1] >> 3) & 0x7, val, mask);
1449 insnlen += 2 + insn_displacement_len(insn[insnlen+1]);
1450 } else if (insn[0] == 0x88) { /* 8-bit iowrite */
1451 mask = 0xff;
1452 /* Next byte is r/m byte: bits 3-5 are register. */
1453 val = getreg_num((insn[1] >> 3) & 0x7, mask);
1454 emulate_mmio_write(d, off, val, mask);
1455 insnlen = 2 + insn_displacement_len(insn[1]);
1456 } else if (insn[0] == 0x8a) { /* 8-bit ioread */
1457 mask = 0xff;
1458 val = emulate_mmio_read(d, off, mask);
1459 setreg_num((insn[1] >> 3) & 0x7, val, mask);
1460 insnlen = 2 + insn_displacement_len(insn[1]);
1461 } else {
1462 warnx("Unknown MMIO instruction touching %#08lx:"
1463 " %02x %02x %02x %02x at %u",
1464 paddr, insn[0], insn[1], insn[2], insn[3], getreg(eip));
1465 reinject:
1466 /* Inject trap into Guest. */
1467 if (write(lguest_fd, args, sizeof(args)) < 0)
1468 err(1, "Reinjecting trap 14 for fault at %#x",
1469 getreg(eip));
1470 return;
1471 }
1472
1473 /* Finally, we've "done" the instruction, so move past it. */
1474 setreg(eip, getreg(eip) + insnlen);
1475}
1313 1476
1314/*L:190 1477/*L:190
1315 * Device Setup 1478 * Device Setup
@@ -2004,6 +2167,10 @@ static void __attribute__((noreturn)) run_guest(void)
2004 verbose("Emulating instruction at %#x\n", 2167 verbose("Emulating instruction at %#x\n",
2005 getreg(eip)); 2168 getreg(eip));
2006 emulate_insn(notify.insn); 2169 emulate_insn(notify.insn);
2170 } else if (notify.trap == 14) {
2171 verbose("Emulating MMIO at %#x\n",
2172 getreg(eip));
2173 emulate_mmio(notify.addr, notify.insn);
2007 } else 2174 } else
2008 errx(1, "Unknown trap %i addr %#08x\n", 2175 errx(1, "Unknown trap %i addr %#08x\n",
2009 notify.trap, notify.addr); 2176 notify.trap, notify.addr);