xref: /openbmc/linux/arch/x86/kernel/probe_roms.c (revision 4338e40d)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
25d94e81fSDan Williams #include <linux/sched.h>
35d94e81fSDan Williams #include <linux/mm.h>
45d94e81fSDan Williams #include <linux/uaccess.h>
55d94e81fSDan Williams #include <linux/mmzone.h>
65d94e81fSDan Williams #include <linux/ioport.h>
75d94e81fSDan Williams #include <linux/seq_file.h>
85d94e81fSDan Williams #include <linux/console.h>
95d94e81fSDan Williams #include <linux/init.h>
105d94e81fSDan Williams #include <linux/edd.h>
115d94e81fSDan Williams #include <linux/dmi.h>
125d94e81fSDan Williams #include <linux/pfn.h>
135d94e81fSDan Williams #include <linux/pci.h>
1469c60c88SPaul Gortmaker #include <linux/export.h>
1569c60c88SPaul Gortmaker 
16a240ada2SJan Beulich #include <asm/probe_roms.h>
175d94e81fSDan Williams #include <asm/pci-direct.h>
1866441bd3SIngo Molnar #include <asm/e820/api.h>
195d94e81fSDan Williams #include <asm/mmzone.h>
205d94e81fSDan Williams #include <asm/setup.h>
215d94e81fSDan Williams #include <asm/sections.h>
225d94e81fSDan Williams #include <asm/io.h>
235d94e81fSDan Williams #include <asm/setup_arch.h>
24*9704c07bSBrijesh Singh #include <asm/sev.h>
255d94e81fSDan Williams 
265d94e81fSDan Williams static struct resource system_rom_resource = {
275d94e81fSDan Williams 	.name	= "System ROM",
285d94e81fSDan Williams 	.start	= 0xf0000,
295d94e81fSDan Williams 	.end	= 0xfffff,
305d94e81fSDan Williams 	.flags	= IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM
315d94e81fSDan Williams };
325d94e81fSDan Williams 
335d94e81fSDan Williams static struct resource extension_rom_resource = {
345d94e81fSDan Williams 	.name	= "Extension ROM",
355d94e81fSDan Williams 	.start	= 0xe0000,
365d94e81fSDan Williams 	.end	= 0xeffff,
375d94e81fSDan Williams 	.flags	= IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM
385d94e81fSDan Williams };
395d94e81fSDan Williams 
405d94e81fSDan Williams static struct resource adapter_rom_resources[] = { {
415d94e81fSDan Williams 	.name 	= "Adapter ROM",
425d94e81fSDan Williams 	.start	= 0xc8000,
435d94e81fSDan Williams 	.end	= 0,
445d94e81fSDan Williams 	.flags	= IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM
455d94e81fSDan Williams }, {
465d94e81fSDan Williams 	.name 	= "Adapter ROM",
475d94e81fSDan Williams 	.start	= 0,
485d94e81fSDan Williams 	.end	= 0,
495d94e81fSDan Williams 	.flags	= IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM
505d94e81fSDan Williams }, {
515d94e81fSDan Williams 	.name 	= "Adapter ROM",
525d94e81fSDan Williams 	.start	= 0,
535d94e81fSDan Williams 	.end	= 0,
545d94e81fSDan Williams 	.flags	= IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM
555d94e81fSDan Williams }, {
565d94e81fSDan Williams 	.name 	= "Adapter ROM",
575d94e81fSDan Williams 	.start	= 0,
585d94e81fSDan Williams 	.end	= 0,
595d94e81fSDan Williams 	.flags	= IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM
605d94e81fSDan Williams }, {
615d94e81fSDan Williams 	.name 	= "Adapter ROM",
625d94e81fSDan Williams 	.start	= 0,
635d94e81fSDan Williams 	.end	= 0,
645d94e81fSDan Williams 	.flags	= IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM
655d94e81fSDan Williams }, {
665d94e81fSDan Williams 	.name 	= "Adapter ROM",
675d94e81fSDan Williams 	.start	= 0,
685d94e81fSDan Williams 	.end	= 0,
695d94e81fSDan Williams 	.flags	= IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM
705d94e81fSDan Williams } };
715d94e81fSDan Williams 
725d94e81fSDan Williams static struct resource video_rom_resource = {
735d94e81fSDan Williams 	.name 	= "Video ROM",
745d94e81fSDan Williams 	.start	= 0xc0000,
755d94e81fSDan Williams 	.end	= 0xc7fff,
765d94e81fSDan Williams 	.flags	= IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM
775d94e81fSDan Williams };
785d94e81fSDan Williams 
795d94e81fSDan Williams /* does this oprom support the given pci device, or any of the devices
805d94e81fSDan Williams  * that the driver supports?
815d94e81fSDan Williams  */
match_id(struct pci_dev * pdev,unsigned short vendor,unsigned short device)825d94e81fSDan Williams static bool match_id(struct pci_dev *pdev, unsigned short vendor, unsigned short device)
835d94e81fSDan Williams {
84d98d5333SUwe Kleine-König 	struct pci_driver *drv = to_pci_driver(pdev->dev.driver);
855d94e81fSDan Williams 	const struct pci_device_id *id;
865d94e81fSDan Williams 
875d94e81fSDan Williams 	if (pdev->vendor == vendor && pdev->device == device)
885d94e81fSDan Williams 		return true;
895d94e81fSDan Williams 
905d94e81fSDan Williams 	for (id = drv ? drv->id_table : NULL; id && id->vendor; id++)
915d94e81fSDan Williams 		if (id->vendor == vendor && id->device == device)
925d94e81fSDan Williams 			break;
935d94e81fSDan Williams 
945d94e81fSDan Williams 	return id && id->vendor;
955d94e81fSDan Williams }
965d94e81fSDan Williams 
probe_list(struct pci_dev * pdev,unsigned short vendor,const void * rom_list)975d94e81fSDan Williams static bool probe_list(struct pci_dev *pdev, unsigned short vendor,
980c389d89SLinus Torvalds 		       const void *rom_list)
995d94e81fSDan Williams {
1005d94e81fSDan Williams 	unsigned short device;
1015d94e81fSDan Williams 
1025d94e81fSDan Williams 	do {
10325f12ae4SChristoph Hellwig 		if (get_kernel_nofault(device, rom_list) != 0)
1045d94e81fSDan Williams 			device = 0;
1055d94e81fSDan Williams 
1065d94e81fSDan Williams 		if (device && match_id(pdev, vendor, device))
1075d94e81fSDan Williams 			break;
1085d94e81fSDan Williams 
1095d94e81fSDan Williams 		rom_list += 2;
1105d94e81fSDan Williams 	} while (device);
1115d94e81fSDan Williams 
1125d94e81fSDan Williams 	return !!device;
1135d94e81fSDan Williams }
1145d94e81fSDan Williams 
find_oprom(struct pci_dev * pdev)1155d94e81fSDan Williams static struct resource *find_oprom(struct pci_dev *pdev)
1165d94e81fSDan Williams {
1175d94e81fSDan Williams 	struct resource *oprom = NULL;
1185d94e81fSDan Williams 	int i;
1195d94e81fSDan Williams 
1205d94e81fSDan Williams 	for (i = 0; i < ARRAY_SIZE(adapter_rom_resources); i++) {
1215d94e81fSDan Williams 		struct resource *res = &adapter_rom_resources[i];
1225d94e81fSDan Williams 		unsigned short offset, vendor, device, list, rev;
1230c389d89SLinus Torvalds 		const void *rom;
1245d94e81fSDan Williams 
1255d94e81fSDan Williams 		if (res->end == 0)
1265d94e81fSDan Williams 			break;
1275d94e81fSDan Williams 
1285d94e81fSDan Williams 		rom = isa_bus_to_virt(res->start);
12925f12ae4SChristoph Hellwig 		if (get_kernel_nofault(offset, rom + 0x18) != 0)
1305d94e81fSDan Williams 			continue;
1315d94e81fSDan Williams 
13225f12ae4SChristoph Hellwig 		if (get_kernel_nofault(vendor, rom + offset + 0x4) != 0)
1335d94e81fSDan Williams 			continue;
1345d94e81fSDan Williams 
13525f12ae4SChristoph Hellwig 		if (get_kernel_nofault(device, rom + offset + 0x6) != 0)
1365d94e81fSDan Williams 			continue;
1375d94e81fSDan Williams 
1385d94e81fSDan Williams 		if (match_id(pdev, vendor, device)) {
1395d94e81fSDan Williams 			oprom = res;
1405d94e81fSDan Williams 			break;
1415d94e81fSDan Williams 		}
1425d94e81fSDan Williams 
14325f12ae4SChristoph Hellwig 		if (get_kernel_nofault(list, rom + offset + 0x8) == 0 &&
14425f12ae4SChristoph Hellwig 		    get_kernel_nofault(rev, rom + offset + 0xc) == 0 &&
1455d94e81fSDan Williams 		    rev >= 3 && list &&
1465d94e81fSDan Williams 		    probe_list(pdev, vendor, rom + offset + list)) {
1475d94e81fSDan Williams 			oprom = res;
1485d94e81fSDan Williams 			break;
1495d94e81fSDan Williams 		}
1505d94e81fSDan Williams 	}
1515d94e81fSDan Williams 
1525d94e81fSDan Williams 	return oprom;
1535d94e81fSDan Williams }
1545d94e81fSDan Williams 
pci_map_biosrom(struct pci_dev * pdev)15504d695a6SMathias Krause void __iomem *pci_map_biosrom(struct pci_dev *pdev)
1565d94e81fSDan Williams {
1575d94e81fSDan Williams 	struct resource *oprom = find_oprom(pdev);
1585d94e81fSDan Williams 
1595d94e81fSDan Williams 	if (!oprom)
1605d94e81fSDan Williams 		return NULL;
1615d94e81fSDan Williams 
1625d94e81fSDan Williams 	return ioremap(oprom->start, resource_size(oprom));
1635d94e81fSDan Williams }
1645d94e81fSDan Williams EXPORT_SYMBOL(pci_map_biosrom);
1655d94e81fSDan Williams 
pci_unmap_biosrom(void __iomem * image)1665d94e81fSDan Williams void pci_unmap_biosrom(void __iomem *image)
1675d94e81fSDan Williams {
1685d94e81fSDan Williams 	iounmap(image);
1695d94e81fSDan Williams }
1705d94e81fSDan Williams EXPORT_SYMBOL(pci_unmap_biosrom);
1715d94e81fSDan Williams 
pci_biosrom_size(struct pci_dev * pdev)1725d94e81fSDan Williams size_t pci_biosrom_size(struct pci_dev *pdev)
1735d94e81fSDan Williams {
1745d94e81fSDan Williams 	struct resource *oprom = find_oprom(pdev);
1755d94e81fSDan Williams 
1765d94e81fSDan Williams 	return oprom ? resource_size(oprom) : 0;
1775d94e81fSDan Williams }
1785d94e81fSDan Williams EXPORT_SYMBOL(pci_biosrom_size);
1795d94e81fSDan Williams 
1805d94e81fSDan Williams #define ROMSIGNATURE 0xaa55
1815d94e81fSDan Williams 
romsignature(const unsigned char * rom)1825d94e81fSDan Williams static int __init romsignature(const unsigned char *rom)
1835d94e81fSDan Williams {
1845d94e81fSDan Williams 	const unsigned short * const ptr = (const unsigned short *)rom;
1855d94e81fSDan Williams 	unsigned short sig;
1865d94e81fSDan Williams 
18725f12ae4SChristoph Hellwig 	return get_kernel_nofault(sig, ptr) == 0 && sig == ROMSIGNATURE;
1885d94e81fSDan Williams }
1895d94e81fSDan Williams 
romchecksum(const unsigned char * rom,unsigned long length)1905d94e81fSDan Williams static int __init romchecksum(const unsigned char *rom, unsigned long length)
1915d94e81fSDan Williams {
1925d94e81fSDan Williams 	unsigned char sum, c;
1935d94e81fSDan Williams 
19425f12ae4SChristoph Hellwig 	for (sum = 0; length && get_kernel_nofault(c, rom++) == 0; length--)
1955d94e81fSDan Williams 		sum += c;
1965d94e81fSDan Williams 	return !length && !sum;
1975d94e81fSDan Williams }
1985d94e81fSDan Williams 
probe_roms(void)1995d94e81fSDan Williams void __init probe_roms(void)
2005d94e81fSDan Williams {
2015d94e81fSDan Williams 	unsigned long start, length, upper;
202*9704c07bSBrijesh Singh 	const unsigned char *rom;
2035d94e81fSDan Williams 	unsigned char c;
2045d94e81fSDan Williams 	int i;
2055d94e81fSDan Williams 
2065d94e81fSDan Williams 	/* video rom */
2075d94e81fSDan Williams 	upper = adapter_rom_resources[0].start;
2085d94e81fSDan Williams 	for (start = video_rom_resource.start; start < upper; start += 2048) {
2095d94e81fSDan Williams 		rom = isa_bus_to_virt(start);
2105d94e81fSDan Williams 		if (!romsignature(rom))
2115d94e81fSDan Williams 			continue;
2125d94e81fSDan Williams 
2135d94e81fSDan Williams 		video_rom_resource.start = start;
2145d94e81fSDan Williams 
21525f12ae4SChristoph Hellwig 		if (get_kernel_nofault(c, rom + 2) != 0)
2165d94e81fSDan Williams 			continue;
2175d94e81fSDan Williams 
2185d94e81fSDan Williams 		/* 0 < length <= 0x7f * 512, historically */
2195d94e81fSDan Williams 		length = c * 512;
2205d94e81fSDan Williams 
2215d94e81fSDan Williams 		/* if checksum okay, trust length byte */
2225d94e81fSDan Williams 		if (length && romchecksum(rom, length))
2235d94e81fSDan Williams 			video_rom_resource.end = start + length - 1;
2245d94e81fSDan Williams 
2255d94e81fSDan Williams 		request_resource(&iomem_resource, &video_rom_resource);
2265d94e81fSDan Williams 		break;
2275d94e81fSDan Williams 	}
2285d94e81fSDan Williams 
2295d94e81fSDan Williams 	start = (video_rom_resource.end + 1 + 2047) & ~2047UL;
2305d94e81fSDan Williams 	if (start < upper)
2315d94e81fSDan Williams 		start = upper;
2325d94e81fSDan Williams 
2335d94e81fSDan Williams 	/* system rom */
2345d94e81fSDan Williams 	request_resource(&iomem_resource, &system_rom_resource);
2355d94e81fSDan Williams 	upper = system_rom_resource.start;
2365d94e81fSDan Williams 
2375d94e81fSDan Williams 	/* check for extension rom (ignore length byte!) */
2385d94e81fSDan Williams 	rom = isa_bus_to_virt(extension_rom_resource.start);
2395d94e81fSDan Williams 	if (romsignature(rom)) {
24028f65c11SJoe Perches 		length = resource_size(&extension_rom_resource);
2415d94e81fSDan Williams 		if (romchecksum(rom, length)) {
2425d94e81fSDan Williams 			request_resource(&iomem_resource, &extension_rom_resource);
2435d94e81fSDan Williams 			upper = extension_rom_resource.start;
2445d94e81fSDan Williams 		}
2455d94e81fSDan Williams 	}
2465d94e81fSDan Williams 
2475d94e81fSDan Williams 	/* check for adapter roms on 2k boundaries */
2485d94e81fSDan Williams 	for (i = 0; i < ARRAY_SIZE(adapter_rom_resources) && start < upper; start += 2048) {
2495d94e81fSDan Williams 		rom = isa_bus_to_virt(start);
2505d94e81fSDan Williams 		if (!romsignature(rom))
2515d94e81fSDan Williams 			continue;
2525d94e81fSDan Williams 
25325f12ae4SChristoph Hellwig 		if (get_kernel_nofault(c, rom + 2) != 0)
2545d94e81fSDan Williams 			continue;
2555d94e81fSDan Williams 
2565d94e81fSDan Williams 		/* 0 < length <= 0x7f * 512, historically */
2575d94e81fSDan Williams 		length = c * 512;
2585d94e81fSDan Williams 
2595d94e81fSDan Williams 		/* but accept any length that fits if checksum okay */
2605d94e81fSDan Williams 		if (!length || start + length > upper || !romchecksum(rom, length))
2615d94e81fSDan Williams 			continue;
2625d94e81fSDan Williams 
2635d94e81fSDan Williams 		adapter_rom_resources[i].start = start;
2645d94e81fSDan Williams 		adapter_rom_resources[i].end = start + length - 1;
2655d94e81fSDan Williams 		request_resource(&iomem_resource, &adapter_rom_resources[i]);
2665d94e81fSDan Williams 
2675d94e81fSDan Williams 		start = adapter_rom_resources[i++].end & ~2047UL;
2685d94e81fSDan Williams 	}
2695d94e81fSDan Williams }
2705d94e81fSDan Williams 
271