Commit 55c2d788 authored by Rusty Russell's avatar Rusty Russell

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: default avatarRusty Russell <rusty@rustcorp.com.au>
parent d761b032
...@@ -1222,15 +1222,13 @@ static void set_cfg_window(u32 cfg_offset, u32 off) ...@@ -1222,15 +1222,13 @@ static void set_cfg_window(u32 cfg_offset, u32 off)
off); off);
} }
static u32 read_bar_via_cfg(u32 cfg_offset, u32 off)
{
set_cfg_window(cfg_offset, off);
return read_pci_config(0, 1, 0,
cfg_offset + sizeof(struct virtio_pci_cap));
}
static void write_bar_via_cfg(u32 cfg_offset, u32 off, u32 val) static void write_bar_via_cfg(u32 cfg_offset, u32 off, u32 val)
{ {
/*
* We could set this up once, then leave it; nothing else in the *
* kernel should touch these registers. But if it went wrong, that
* would be a horrible bug to find.
*/
set_cfg_window(cfg_offset, off); set_cfg_window(cfg_offset, off);
write_pci_config(0, 1, 0, write_pci_config(0, 1, 0,
cfg_offset + sizeof(struct virtio_pci_cap), val); 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,8 +1237,9 @@ static void write_bar_via_cfg(u32 cfg_offset, u32 off, u32 val)
static void probe_pci_console(void) static void probe_pci_console(void)
{ {
u8 cap, common_cap = 0, device_cap = 0; u8 cap, common_cap = 0, device_cap = 0;
/* Offsets within BAR0 */ /* Offset within BAR0 */
u32 common_offset, device_offset; u32 device_offset;
u32 device_len;
/* Avoid recursive printk into here. */ /* Avoid recursive printk into here. */
console_cfg_offset = -1; console_cfg_offset = -1;
...@@ -1263,7 +1262,7 @@ static void probe_pci_console(void) ...@@ -1263,7 +1262,7 @@ static void probe_pci_console(void)
u8 vndr = read_pci_config_byte(0, 1, 0, cap); u8 vndr = read_pci_config_byte(0, 1, 0, cap);
if (vndr == PCI_CAP_ID_VNDR) { if (vndr == PCI_CAP_ID_VNDR) {
u8 type, bar; u8 type, bar;
u32 offset; u32 offset, length;
type = read_pci_config_byte(0, 1, 0, type = read_pci_config_byte(0, 1, 0,
cap + offsetof(struct virtio_pci_cap, cfg_type)); cap + offsetof(struct virtio_pci_cap, cfg_type));
...@@ -1271,18 +1270,15 @@ static void probe_pci_console(void) ...@@ -1271,18 +1270,15 @@ static void probe_pci_console(void)
cap + offsetof(struct virtio_pci_cap, bar)); cap + offsetof(struct virtio_pci_cap, bar));
offset = read_pci_config(0, 1, 0, offset = read_pci_config(0, 1, 0,
cap + offsetof(struct virtio_pci_cap, offset)); cap + offsetof(struct virtio_pci_cap, offset));
length = read_pci_config(0, 1, 0,
cap + offsetof(struct virtio_pci_cap, length));
switch (type) { switch (type) {
case VIRTIO_PCI_CAP_COMMON_CFG:
if (bar == 0) {
common_cap = cap;
common_offset = offset;
}
break;
case VIRTIO_PCI_CAP_DEVICE_CFG: case VIRTIO_PCI_CAP_DEVICE_CFG:
if (bar == 0) { if (bar == 0) {
device_cap = cap; device_cap = cap;
device_offset = offset; device_offset = offset;
device_len = length;
} }
break; break;
case VIRTIO_PCI_CAP_PCI_CFG: case VIRTIO_PCI_CAP_PCI_CFG:
...@@ -1292,32 +1288,27 @@ static void probe_pci_console(void) ...@@ -1292,32 +1288,27 @@ static void probe_pci_console(void)
} }
cap = read_pci_config_byte(0, 1, 0, cap + PCI_CAP_LIST_NEXT); cap = read_pci_config_byte(0, 1, 0, cap + PCI_CAP_LIST_NEXT);
} }
if (!common_cap || !device_cap || !console_access_cap) { if (!device_cap || !console_access_cap) {
printk(KERN_ERR "lguest: No caps (%u/%u/%u) in console!\n", printk(KERN_ERR "lguest: No caps (%u/%u/%u) in console!\n",
common_cap, device_cap, console_access_cap); common_cap, device_cap, console_access_cap);
return; return;
} }
/*
#define write_common_config(reg, val) \ * Note that we can't check features, until we've set the DRIVER
write_bar_via_cfg(console_access_cap, \ * status bit. We don't want to do that until we have a real driver,
common_offset+offsetof(struct virtio_pci_common_cfg,reg),\ * so we just check that the device-specific config has room for
val) * emerg_wr. If it doesn't support VIRTIO_CONSOLE_F_EMERG_WRITE
* it should ignore the access.
#define read_common_config(reg) \ */
read_bar_via_cfg(console_access_cap, \ if (device_len < (offsetof(struct virtio_console_config, emerg_wr)
common_offset+offsetof(struct virtio_pci_common_cfg,reg)) + sizeof(u32))) {
printk(KERN_ERR "lguest: console missing emerg_wr field\n");
/* Check features: they must offer EMERG_WRITE */
write_common_config(device_feature_select, 0);
if (!(read_common_config(device_feature)
& (1 << VIRTIO_CONSOLE_F_EMERG_WRITE))) {
printk(KERN_ERR "lguest: console missing EMERG_WRITE\n");
return; return;
} }
console_cfg_offset = device_offset; console_cfg_offset = device_offset;
printk(KERN_INFO "lguest: Console via virtio-pci emerg_wr\n");
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment