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