diff options
author | Rusty Russell <rusty@rustcorp.com.au> | 2015-02-10 23:45:11 -0500 |
---|---|---|
committer | Rusty Russell <rusty@rustcorp.com.au> | 2015-02-11 01:17:35 -0500 |
commit | 6a54f9ab0d65a2095de50160b8ca7ce6469aaac0 (patch) | |
tree | 20ed18c8bcc4d92b235642b94466eec1729de4fc /tools/lguest | |
parent | 0a6bcc183f5377eca07cbf0cf6f4b6cb00e4c1ec (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.c | 167 |
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 */ |
100 | static unsigned int __thread cpu_id; | 100 | static 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. */ |
103 | struct device_list { | 106 | struct 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 */ | ||
1211 | static 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 */ | ||
1231 | static 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 */ | ||
1258 | static 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 | |||
1200 | static void emulate_insn(const u8 insn[]) | 1281 | static 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 | ||
1394 | static 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 | |||
1413 | static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask) | ||
1414 | { | ||
1415 | } | ||
1416 | |||
1417 | static u32 emulate_mmio_read(struct device *d, u32 off, u32 mask) | ||
1418 | { | ||
1419 | return 0xFFFFFFFF; | ||
1420 | } | ||
1421 | |||
1422 | static 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); |