mirror of
https://github.com/torvalds/linux.git
synced 2024-11-06 12:11:59 +00:00
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>
This commit is contained in:
parent
d761b03291
commit
55c2d7884e
@ -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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
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");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Loading…
Reference in New Issue
Block a user