aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRusty Russell <rusty@rustcorp.com.au>2015-02-10 23:54:01 -0500
committerRusty Russell <rusty@rustcorp.com.au>2015-02-11 01:17:43 -0500
commit59eba788db298c3597728774dc3d0f16bdc8a1a4 (patch)
tree90013065e783dade54db4877efef5e2ad68a7644
parente8330d9bc1f7af7737500aebd3fc1f488e3dbb71 (diff)
lguest: support backdoor window.
The VIRTIO_PCI_CAP_PCI_CFG in the PCI virtio 1.0 spec allows access to the BAR registers without mapping them. This is a compulsory feature, and we implement it here. There are some subtleties involving access widths which we should note: 4.1.4.7.1 Device Requirements: PCI configuration access capability ... Upon detecting driver write access to pci_cfg_data, the device MUST execute a write access at offset cap.offset at BAR selected by cap.bar using the first cap.length bytes from pci_cfg_data. Upon detecting driver read access to pci_cfg_data, the device MUST execute a read access of length cap.length at offset cap.offset at BAR selected by cap.bar and store the first cap.length bytes in pci_cfg_data. So, for a write, we copy into the pci_cfg_data window, then write from there out to the BAR. This works correctly if cap.length != width of write. Similarly, for a read, we read into window from the BAR then read the value from there. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
-rw-r--r--tools/lguest/lguest.c101
1 files changed, 100 insertions, 1 deletions
diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c
index 8959ac246668..e3c4d3d7dc2a 100644
--- a/tools/lguest/lguest.c
+++ b/tools/lguest/lguest.c
@@ -156,7 +156,6 @@ struct pci_config {
156 struct virtio_pci_notify_cap notify; 156 struct virtio_pci_notify_cap notify;
157 struct virtio_pci_cap isr; 157 struct virtio_pci_cap isr;
158 struct virtio_pci_cap device; 158 struct virtio_pci_cap device;
159 /* FIXME: Implement this! */
160 struct virtio_pci_cfg_cap cfg_access; 159 struct virtio_pci_cfg_cap cfg_access;
161}; 160};
162 161
@@ -1184,6 +1183,36 @@ static struct device *dev_and_reg(u32 *reg)
1184 return find_pci_device(pci_config_addr.bits.devnum); 1183 return find_pci_device(pci_config_addr.bits.devnum);
1185} 1184}
1186 1185
1186/*
1187 * We can get invalid combinations of values while they're writing, so we
1188 * only fault if they try to write with some invalid bar/offset/length.
1189 */
1190static bool valid_bar_access(struct device *d,
1191 struct virtio_pci_cfg_cap *cfg_access)
1192{
1193 /* We only have 1 bar (BAR0) */
1194 if (cfg_access->cap.bar != 0)
1195 return false;
1196
1197 /* Check it's within BAR0. */
1198 if (cfg_access->cap.offset >= d->mmio_size
1199 || cfg_access->cap.offset + cfg_access->cap.length > d->mmio_size)
1200 return false;
1201
1202 /* Check length is 1, 2 or 4. */
1203 if (cfg_access->cap.length != 1
1204 && cfg_access->cap.length != 2
1205 && cfg_access->cap.length != 4)
1206 return false;
1207
1208 /* Offset must be multiple of length */
1209 if (cfg_access->cap.offset % cfg_access->cap.length != 0)
1210 return false;
1211
1212 /* Return pointer into word in BAR0. */
1213 return true;
1214}
1215
1187/* Is this accessing the PCI config address port?. */ 1216/* Is this accessing the PCI config address port?. */
1188static bool is_pci_addr_port(u16 port) 1217static bool is_pci_addr_port(u16 port)
1189{ 1218{
@@ -1215,6 +1244,8 @@ static bool is_pci_data_port(u16 port)
1215 return port >= PCI_CONFIG_DATA && port < PCI_CONFIG_DATA + 4; 1244 return port >= PCI_CONFIG_DATA && port < PCI_CONFIG_DATA + 4;
1216} 1245}
1217 1246
1247static void emulate_mmio_write(struct device *d, u32 off, u32 val, u32 mask);
1248
1218static bool pci_data_iowrite(u16 port, u32 mask, u32 val) 1249static bool pci_data_iowrite(u16 port, u32 mask, u32 val)
1219{ 1250{
1220 u32 reg, portoff; 1251 u32 reg, portoff;
@@ -1255,12 +1286,53 @@ static bool pci_data_iowrite(u16 port, u32 mask, u32 val)
1255 && mask == 0xFFFF) { 1286 && mask == 0xFFFF) {
1256 /* Ignore command writes. */ 1287 /* Ignore command writes. */
1257 return true; 1288 return true;
1289 } else if (&d->config_words[reg]
1290 == (void *)&d->config.cfg_access.cap.bar
1291 || &d->config_words[reg]
1292 == &d->config.cfg_access.cap.length
1293 || &d->config_words[reg]
1294 == &d->config.cfg_access.cap.offset) {
1295
1296 /*
1297 * The VIRTIO_PCI_CAP_PCI_CFG capability
1298 * provides a backdoor to access the MMIO
1299 * regions without mapping them. Weird, but
1300 * useful.
1301 */
1302 iowrite(portoff, val, mask, &d->config_words[reg]);
1303 return true;
1304 } else if (&d->config_words[reg] == &d->config.cfg_access.window) {
1305 u32 write_mask;
1306
1307 /* Must be bar 0 */
1308 if (!valid_bar_access(d, &d->config.cfg_access))
1309 return false;
1310
1311 /* First copy what they wrote into the window */
1312 iowrite(portoff, val, mask, &d->config.cfg_access.window);
1313
1314 /*
1315 * Now emulate a write. The mask we use is set by
1316 * len, *not* this write!
1317 */
1318 write_mask = (1ULL<<(8*d->config.cfg_access.cap.length)) - 1;
1319 verbose("Window writing %#x/%#x to bar %u, offset %u len %u\n",
1320 d->config.cfg_access.window, write_mask,
1321 d->config.cfg_access.cap.bar,
1322 d->config.cfg_access.cap.offset,
1323 d->config.cfg_access.cap.length);
1324
1325 emulate_mmio_write(d, d->config.cfg_access.cap.offset,
1326 d->config.cfg_access.window, write_mask);
1327 return true;
1258 } 1328 }
1259 1329
1260 /* Complain about other writes. */ 1330 /* Complain about other writes. */
1261 return false; 1331 return false;
1262} 1332}
1263 1333
1334static u32 emulate_mmio_read(struct device *d, u32 off, u32 mask);
1335
1264static void pci_data_ioread(u16 port, u32 mask, u32 *val) 1336static void pci_data_ioread(u16 port, u32 mask, u32 *val)
1265{ 1337{
1266 u32 reg; 1338 u32 reg;
@@ -1268,6 +1340,33 @@ static void pci_data_ioread(u16 port, u32 mask, u32 *val)
1268 1340
1269 if (!d) 1341 if (!d)
1270 return; 1342 return;
1343
1344 /* Read through the PCI MMIO access window is special */
1345 if (&d->config_words[reg] == &d->config.cfg_access.window) {
1346 u32 read_mask;
1347
1348 /* Must be bar 0 */
1349 if (!valid_bar_access(d, &d->config.cfg_access))
1350 errx(1, "Invalid cfg_access to bar%u, offset %u len %u",
1351 d->config.cfg_access.cap.bar,
1352 d->config.cfg_access.cap.offset,
1353 d->config.cfg_access.cap.length);
1354
1355 /*
1356 * Read into the window. The mask we use is set by
1357 * len, *not* this read!
1358 */
1359 read_mask = (1ULL<<(8*d->config.cfg_access.cap.length))-1;
1360 d->config.cfg_access.window
1361 = emulate_mmio_read(d,
1362 d->config.cfg_access.cap.offset,
1363 read_mask);
1364 verbose("Window read %#x/%#x from bar %u, offset %u len %u\n",
1365 d->config.cfg_access.window, read_mask,
1366 d->config.cfg_access.cap.bar,
1367 d->config.cfg_access.cap.offset,
1368 d->config.cfg_access.cap.length);
1369 }
1271 ioread(port - PCI_CONFIG_DATA, d->config_words[reg], mask, val); 1370 ioread(port - PCI_CONFIG_DATA, d->config_words[reg], mask, val);
1272} 1371}
1273 1372