diff options
| author | Rusty Russell <rusty@rustcorp.com.au> | 2015-02-10 23:56:01 -0500 |
|---|---|---|
| committer | Rusty Russell <rusty@rustcorp.com.au> | 2015-02-11 01:17:44 -0500 |
| commit | a561adfaecc9eb6fb66941b450458801f3f60ca0 (patch) | |
| tree | fc4bb1870df410e832adb8e321b6a2417706b33d | |
| parent | 713e3f72244cb67fe1ad5c82a061c0b1be2f2fc5 (diff) | |
lguest: use the PCI console device's emerg_wr for early boot messages.
This involves manually checking the console device (which is always in
slot 1 of bus 0) and using the window in VIRTIO_PCI_CAP_PCI_CFG to
program it (as we can't map the BAR yet).
We could in fact do this much earlier, but we wait for the first
write from the virtio_cons_early_init() facility.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
| -rw-r--r-- | arch/x86/lguest/boot.c | 146 |
1 files changed, 134 insertions, 12 deletions
diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c index 2943ab931671..531b844cb48d 100644 --- a/arch/x86/lguest/boot.c +++ b/arch/x86/lguest/boot.c | |||
| @@ -57,6 +57,7 @@ | |||
| 57 | #include <linux/pm.h> | 57 | #include <linux/pm.h> |
| 58 | #include <linux/export.h> | 58 | #include <linux/export.h> |
| 59 | #include <linux/pci.h> | 59 | #include <linux/pci.h> |
| 60 | #include <linux/virtio_pci.h> | ||
| 60 | #include <asm/acpi.h> | 61 | #include <asm/acpi.h> |
| 61 | #include <asm/apic.h> | 62 | #include <asm/apic.h> |
| 62 | #include <asm/lguest.h> | 63 | #include <asm/lguest.h> |
| @@ -74,6 +75,7 @@ | |||
| 74 | #include <asm/reboot.h> /* for struct machine_ops */ | 75 | #include <asm/reboot.h> /* for struct machine_ops */ |
| 75 | #include <asm/kvm_para.h> | 76 | #include <asm/kvm_para.h> |
| 76 | #include <asm/pci_x86.h> | 77 | #include <asm/pci_x86.h> |
| 78 | #include <asm/pci-direct.h> | ||
| 77 | 79 | ||
| 78 | /*G:010 | 80 | /*G:010 |
| 79 | * Welcome to the Guest! | 81 | * Welcome to the Guest! |
| @@ -1202,25 +1204,145 @@ static __init char *lguest_memory_setup(void) | |||
| 1202 | return "LGUEST"; | 1204 | return "LGUEST"; |
| 1203 | } | 1205 | } |
| 1204 | 1206 | ||
| 1207 | /* Offset within PCI config space of BAR access capability. */ | ||
| 1208 | static int console_cfg_offset = 0; | ||
| 1209 | static int console_access_cap; | ||
| 1210 | |||
| 1211 | /* Set up so that we access off in bar0 (on bus 0, device 1, function 0) */ | ||
| 1212 | static void set_cfg_window(u32 cfg_offset, u32 off) | ||
| 1213 | { | ||
| 1214 | write_pci_config_byte(0, 1, 0, | ||
| 1215 | cfg_offset + offsetof(struct virtio_pci_cap, bar), | ||
| 1216 | 0); | ||
| 1217 | write_pci_config(0, 1, 0, | ||
| 1218 | cfg_offset + offsetof(struct virtio_pci_cap, length), | ||
| 1219 | 4); | ||
| 1220 | write_pci_config(0, 1, 0, | ||
| 1221 | cfg_offset + offsetof(struct virtio_pci_cap, offset), | ||
| 1222 | off); | ||
| 1223 | } | ||
| 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) | ||
| 1233 | { | ||
| 1234 | set_cfg_window(cfg_offset, off); | ||
| 1235 | write_pci_config(0, 1, 0, | ||
| 1236 | cfg_offset + sizeof(struct virtio_pci_cap), val); | ||
| 1237 | } | ||
| 1238 | |||
| 1239 | static void probe_pci_console(void) | ||
| 1240 | { | ||
| 1241 | u8 cap, common_cap = 0, device_cap = 0; | ||
| 1242 | /* Offsets within BAR0 */ | ||
| 1243 | u32 common_offset, device_offset; | ||
| 1244 | |||
| 1245 | /* Avoid recursive printk into here. */ | ||
| 1246 | console_cfg_offset = -1; | ||
| 1247 | |||
| 1248 | if (!early_pci_allowed()) { | ||
| 1249 | printk(KERN_ERR "lguest: early PCI access not allowed!\n"); | ||
| 1250 | return; | ||
| 1251 | } | ||
| 1252 | |||
| 1253 | /* We expect a console PCI device at BUS0, slot 1. */ | ||
| 1254 | if (read_pci_config(0, 1, 0, 0) != 0x10431AF4) { | ||
| 1255 | printk(KERN_ERR "lguest: PCI device is %#x!\n", | ||
| 1256 | read_pci_config(0, 1, 0, 0)); | ||
| 1257 | return; | ||
| 1258 | } | ||
| 1259 | |||
| 1260 | /* Find the capabilities we need (must be in bar0) */ | ||
| 1261 | cap = read_pci_config_byte(0, 1, 0, PCI_CAPABILITY_LIST); | ||
| 1262 | while (cap) { | ||
| 1263 | u8 vndr = read_pci_config_byte(0, 1, 0, cap); | ||
| 1264 | if (vndr == PCI_CAP_ID_VNDR) { | ||
| 1265 | u8 type, bar; | ||
| 1266 | u32 offset; | ||
| 1267 | |||
| 1268 | type = read_pci_config_byte(0, 1, 0, | ||
| 1269 | cap + offsetof(struct virtio_pci_cap, cfg_type)); | ||
| 1270 | bar = read_pci_config_byte(0, 1, 0, | ||
| 1271 | cap + offsetof(struct virtio_pci_cap, bar)); | ||
| 1272 | offset = read_pci_config(0, 1, 0, | ||
| 1273 | cap + offsetof(struct virtio_pci_cap, offset)); | ||
| 1274 | |||
| 1275 | 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: | ||
| 1283 | if (bar == 0) { | ||
| 1284 | device_cap = cap; | ||
| 1285 | device_offset = offset; | ||
| 1286 | } | ||
| 1287 | break; | ||
| 1288 | case VIRTIO_PCI_CAP_PCI_CFG: | ||
| 1289 | console_access_cap = cap; | ||
| 1290 | break; | ||
| 1291 | } | ||
| 1292 | } | ||
| 1293 | cap = read_pci_config_byte(0, 1, 0, cap + PCI_CAP_LIST_NEXT); | ||
| 1294 | } | ||
| 1295 | if (!common_cap || !device_cap || !console_access_cap) { | ||
| 1296 | printk(KERN_ERR "lguest: No caps (%u/%u/%u) in console!\n", | ||
| 1297 | common_cap, device_cap, console_access_cap); | ||
| 1298 | return; | ||
| 1299 | } | ||
| 1300 | |||
| 1301 | |||
| 1302 | #define write_common_config(reg, val) \ | ||
| 1303 | write_bar_via_cfg(console_access_cap, \ | ||
| 1304 | common_offset+offsetof(struct virtio_pci_common_cfg,reg),\ | ||
| 1305 | val) | ||
| 1306 | |||
| 1307 | #define read_common_config(reg) \ | ||
| 1308 | read_bar_via_cfg(console_access_cap, \ | ||
| 1309 | common_offset+offsetof(struct virtio_pci_common_cfg,reg)) | ||
| 1310 | |||
| 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; | ||
| 1318 | } | ||
| 1319 | |||
| 1320 | console_cfg_offset = device_offset; | ||
| 1321 | } | ||
| 1322 | |||
| 1205 | /* | 1323 | /* |
| 1206 | * We will eventually use the virtio console device to produce console output, | 1324 | * We will eventually use the virtio console device to produce console output, |
| 1207 | * but before that is set up we use LHCALL_NOTIFY on normal memory to produce | 1325 | * but before that is set up we use the virtio PCI console's backdoor mmio |
| 1208 | * console output. | 1326 | * access and the "emergency" write facility (which is legal even before the |
| 1327 | * device is configured). | ||
| 1209 | */ | 1328 | */ |
| 1210 | static __init int early_put_chars(u32 vtermno, const char *buf, int count) | 1329 | static __init int early_put_chars(u32 vtermno, const char *buf, int count) |
| 1211 | { | 1330 | { |
| 1212 | char scratch[17]; | 1331 | /* If we couldn't find PCI console, forget it. */ |
| 1213 | unsigned int len = count; | 1332 | if (console_cfg_offset < 0) |
| 1333 | return count; | ||
| 1214 | 1334 | ||
| 1215 | /* We use a nul-terminated string, so we make a copy. Icky, huh? */ | 1335 | if (unlikely(!console_cfg_offset)) { |
| 1216 | if (len > sizeof(scratch) - 1) | 1336 | probe_pci_console(); |
| 1217 | len = sizeof(scratch) - 1; | 1337 | if (console_cfg_offset < 0) |
| 1218 | scratch[len] = '\0'; | 1338 | return count; |
| 1219 | memcpy(scratch, buf, len); | 1339 | } |
| 1220 | hcall(LHCALL_NOTIFY, __pa(scratch), 0, 0, 0); | ||
| 1221 | 1340 | ||
| 1222 | /* This routine returns the number of bytes actually written. */ | 1341 | write_bar_via_cfg(console_access_cap, |
| 1223 | return len; | 1342 | console_cfg_offset |
| 1343 | + offsetof(struct virtio_console_config, emerg_wr), | ||
| 1344 | buf[0]); | ||
| 1345 | return 1; | ||
| 1224 | } | 1346 | } |
| 1225 | 1347 | ||
| 1226 | /* | 1348 | /* |
