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 /arch/x86/lguest/boot.c | |
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>
Diffstat (limited to 'arch/x86/lguest/boot.c')
-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 | /* |