summary refs log tree commit diff
path: root/arch/x86/lguest
diff options
context:
space:
mode:
authorRusty Russell <rusty@rustcorp.com.au>2015-02-13 17:13:43 +1030
committerRusty Russell <rusty@rustcorp.com.au>2015-02-13 17:15:51 +1030
commit55c2d7884e9a97c2f2d46d5818f783bf3dcc5314 (patch)
tree6291c81f9c5456dff1a1c284f8aa14a851f7e5b1 /arch/x86/lguest
parentd761b0329108c73020a7c95b6fa0d7e82e35fe8b (diff)
downloadlinux-55c2d7884e9a97c2f2d46d5818f783bf3dcc5314.tar.gz
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')
-rw-r--r--arch/x86/lguest/boot.c57
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)
 			 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)
 {
+	/*
+	 * 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);
 	write_pci_config(0, 1, 0,
 			 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)
 {
 	u8 cap, common_cap = 0, device_cap = 0;
-	/* Offsets within BAR0 */
-	u32 common_offset, device_offset;
+	/* Offset within BAR0 */
+	u32 device_offset;
+	u32 device_len;
 
 	/* Avoid recursive printk into here. */
 	console_cfg_offset = -1;
@@ -1263,7 +1262,7 @@ static void probe_pci_console(void)
 		u8 vndr = read_pci_config_byte(0, 1, 0, cap);
 		if (vndr == PCI_CAP_ID_VNDR) {
 			u8 type, bar;
-			u32 offset;
+			u32 offset, length;
 
 			type = read_pci_config_byte(0, 1, 0,
 			    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));
 			offset = read_pci_config(0, 1, 0,
 			    cap + offsetof(struct virtio_pci_cap, offset));
+			length = read_pci_config(0, 1, 0,
+			    cap + offsetof(struct virtio_pci_cap, length));
 
 			switch (type) {
-			case VIRTIO_PCI_CAP_COMMON_CFG:
-				if (bar == 0) {
-					common_cap = cap;
-					common_offset = offset;
-				}
-				break;
 			case VIRTIO_PCI_CAP_DEVICE_CFG:
 				if (bar == 0) {
 					device_cap = cap;
 					device_offset = offset;
+					device_len = length;
 				}
 				break;
 			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);
 	}
-	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",
 		       common_cap, device_cap, console_access_cap);
 		return;
 	}
 
-
-#define write_common_config(reg, val)					\
-	write_bar_via_cfg(console_access_cap,				\
-			  common_offset+offsetof(struct virtio_pci_common_cfg,reg),\
-			  val)
-
-#define read_common_config(reg)						\
-	read_bar_via_cfg(console_access_cap,				\
-			 common_offset+offsetof(struct virtio_pci_common_cfg,reg))
-
-	/* 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");
+	/*
+	 * Note that we can't check features, until we've set the DRIVER
+	 * status bit.  We don't want to do that until we have a real driver,
+	 * so we just check that the device-specific config has room for
+	 * emerg_wr.  If it doesn't support VIRTIO_CONSOLE_F_EMERG_WRITE
+	 * it should ignore the access.
+	 */
+	if (device_len < (offsetof(struct virtio_console_config, emerg_wr)
+			  + sizeof(u32))) {
+		printk(KERN_ERR "lguest: console missing emerg_wr field\n");
 		return;
 	}
 
 	console_cfg_offset = device_offset;
+	printk(KERN_INFO "lguest: Console via virtio-pci emerg_wr\n");
 }
 
 /*