diff options
author | Rusty Russell <rusty@rustcorp.com.au> | 2015-02-13 01:43:43 -0500 |
---|---|---|
committer | Rusty Russell <rusty@rustcorp.com.au> | 2015-02-13 01:45:51 -0500 |
commit | 55c2d7884e9a97c2f2d46d5818f783bf3dcc5314 (patch) | |
tree | 6291c81f9c5456dff1a1c284f8aa14a851f7e5b1 /arch/x86/lguest/boot.c | |
parent | d761b0329108c73020a7c95b6fa0d7e82e35fe8b (diff) |
lguest: don't look in console features to find emerg_wr.
The 1.0 spec clearly states that you must set the ACKNOWLEDGE and
DRIVER status bits before accessing the feature bits. This is a
problem for the early console code, which doesn't really want to
acknowledge the device (the spec specifically excepts writing to the
console's emerg_wr from the usual ordering constrains).
Instead, we check that the *size* of the device configuration is
sufficient to hold emerg_wr: at worst (if the device doesn't support
the VIRTIO_CONSOLE_F_EMERG_WRITE feature), it will ignore the
writes.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Diffstat (limited to 'arch/x86/lguest/boot.c')
-rw-r--r-- | arch/x86/lguest/boot.c | 57 |
1 files changed, 24 insertions, 33 deletions
diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c index 531b844cb48d..ac4453d8520e 100644 --- a/arch/x86/lguest/boot.c +++ b/arch/x86/lguest/boot.c | |||
@@ -1222,15 +1222,13 @@ static void set_cfg_window(u32 cfg_offset, u32 off) | |||
1222 | off); | 1222 | off); |
1223 | } | 1223 | } |
1224 | 1224 | ||
1225 | static u32 read_bar_via_cfg(u32 cfg_offset, u32 off) | ||
1226 | { | ||
1227 | set_cfg_window(cfg_offset, off); | ||
1228 | return read_pci_config(0, 1, 0, | ||
1229 | cfg_offset + sizeof(struct virtio_pci_cap)); | ||
1230 | } | ||
1231 | |||
1232 | static void write_bar_via_cfg(u32 cfg_offset, u32 off, u32 val) | 1225 | static void write_bar_via_cfg(u32 cfg_offset, u32 off, u32 val) |
1233 | { | 1226 | { |
1227 | /* | ||
1228 | * We could set this up once, then leave it; nothing else in the * | ||
1229 | * kernel should touch these registers. But if it went wrong, that | ||
1230 | * would be a horrible bug to find. | ||
1231 | */ | ||
1234 | set_cfg_window(cfg_offset, off); | 1232 | set_cfg_window(cfg_offset, off); |
1235 | write_pci_config(0, 1, 0, | 1233 | write_pci_config(0, 1, 0, |
1236 | cfg_offset + sizeof(struct virtio_pci_cap), val); | 1234 | cfg_offset + sizeof(struct virtio_pci_cap), val); |
@@ -1239,8 +1237,9 @@ static void write_bar_via_cfg(u32 cfg_offset, u32 off, u32 val) | |||
1239 | static void probe_pci_console(void) | 1237 | static void probe_pci_console(void) |
1240 | { | 1238 | { |
1241 | u8 cap, common_cap = 0, device_cap = 0; | 1239 | u8 cap, common_cap = 0, device_cap = 0; |
1242 | /* Offsets within BAR0 */ | 1240 | /* Offset within BAR0 */ |
1243 | u32 common_offset, device_offset; | 1241 | u32 device_offset; |
1242 | u32 device_len; | ||
1244 | 1243 | ||
1245 | /* Avoid recursive printk into here. */ | 1244 | /* Avoid recursive printk into here. */ |
1246 | console_cfg_offset = -1; | 1245 | console_cfg_offset = -1; |
@@ -1263,7 +1262,7 @@ static void probe_pci_console(void) | |||
1263 | u8 vndr = read_pci_config_byte(0, 1, 0, cap); | 1262 | u8 vndr = read_pci_config_byte(0, 1, 0, cap); |
1264 | if (vndr == PCI_CAP_ID_VNDR) { | 1263 | if (vndr == PCI_CAP_ID_VNDR) { |
1265 | u8 type, bar; | 1264 | u8 type, bar; |
1266 | u32 offset; | 1265 | u32 offset, length; |
1267 | 1266 | ||
1268 | type = read_pci_config_byte(0, 1, 0, | 1267 | type = read_pci_config_byte(0, 1, 0, |
1269 | cap + offsetof(struct virtio_pci_cap, cfg_type)); | 1268 | cap + offsetof(struct virtio_pci_cap, cfg_type)); |
@@ -1271,18 +1270,15 @@ static void probe_pci_console(void) | |||
1271 | cap + offsetof(struct virtio_pci_cap, bar)); | 1270 | cap + offsetof(struct virtio_pci_cap, bar)); |
1272 | offset = read_pci_config(0, 1, 0, | 1271 | offset = read_pci_config(0, 1, 0, |
1273 | cap + offsetof(struct virtio_pci_cap, offset)); | 1272 | cap + offsetof(struct virtio_pci_cap, offset)); |
1273 | length = read_pci_config(0, 1, 0, | ||
1274 | cap + offsetof(struct virtio_pci_cap, length)); | ||
1274 | 1275 | ||
1275 | switch (type) { | 1276 | switch (type) { |
1276 | case VIRTIO_PCI_CAP_COMMON_CFG: | ||
1277 | if (bar == 0) { | ||
1278 | common_cap = cap; | ||
1279 | common_offset = offset; | ||
1280 | } | ||
1281 | break; | ||
1282 | case VIRTIO_PCI_CAP_DEVICE_CFG: | 1277 | case VIRTIO_PCI_CAP_DEVICE_CFG: |
1283 | if (bar == 0) { | 1278 | if (bar == 0) { |
1284 | device_cap = cap; | 1279 | device_cap = cap; |
1285 | device_offset = offset; | 1280 | device_offset = offset; |
1281 | device_len = length; | ||
1286 | } | 1282 | } |
1287 | break; | 1283 | break; |
1288 | case VIRTIO_PCI_CAP_PCI_CFG: | 1284 | case VIRTIO_PCI_CAP_PCI_CFG: |
@@ -1292,32 +1288,27 @@ static void probe_pci_console(void) | |||
1292 | } | 1288 | } |
1293 | cap = read_pci_config_byte(0, 1, 0, cap + PCI_CAP_LIST_NEXT); | 1289 | cap = read_pci_config_byte(0, 1, 0, cap + PCI_CAP_LIST_NEXT); |
1294 | } | 1290 | } |
1295 | if (!common_cap || !device_cap || !console_access_cap) { | 1291 | if (!device_cap || !console_access_cap) { |
1296 | printk(KERN_ERR "lguest: No caps (%u/%u/%u) in console!\n", | 1292 | printk(KERN_ERR "lguest: No caps (%u/%u/%u) in console!\n", |
1297 | common_cap, device_cap, console_access_cap); | 1293 | common_cap, device_cap, console_access_cap); |
1298 | return; | 1294 | return; |
1299 | } | 1295 | } |
1300 | 1296 | ||
1301 | 1297 | /* | |
1302 | #define write_common_config(reg, val) \ | 1298 | * Note that we can't check features, until we've set the DRIVER |
1303 | write_bar_via_cfg(console_access_cap, \ | 1299 | * status bit. We don't want to do that until we have a real driver, |
1304 | common_offset+offsetof(struct virtio_pci_common_cfg,reg),\ | 1300 | * so we just check that the device-specific config has room for |
1305 | val) | 1301 | * emerg_wr. If it doesn't support VIRTIO_CONSOLE_F_EMERG_WRITE |
1306 | 1302 | * it should ignore the access. | |
1307 | #define read_common_config(reg) \ | 1303 | */ |
1308 | read_bar_via_cfg(console_access_cap, \ | 1304 | if (device_len < (offsetof(struct virtio_console_config, emerg_wr) |
1309 | common_offset+offsetof(struct virtio_pci_common_cfg,reg)) | 1305 | + sizeof(u32))) { |
1310 | 1306 | printk(KERN_ERR "lguest: console missing emerg_wr field\n"); | |
1311 | /* Check features: they must offer EMERG_WRITE */ | ||
1312 | write_common_config(device_feature_select, 0); | ||
1313 | |||
1314 | if (!(read_common_config(device_feature) | ||
1315 | & (1 << VIRTIO_CONSOLE_F_EMERG_WRITE))) { | ||
1316 | printk(KERN_ERR "lguest: console missing EMERG_WRITE\n"); | ||
1317 | return; | 1307 | return; |
1318 | } | 1308 | } |
1319 | 1309 | ||
1320 | console_cfg_offset = device_offset; | 1310 | console_cfg_offset = device_offset; |
1311 | printk(KERN_INFO "lguest: Console via virtio-pci emerg_wr\n"); | ||
1321 | } | 1312 | } |
1322 | 1313 | ||
1323 | /* | 1314 | /* |