linux/arch/x86/kernel/probe_roms.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <linux/sched.h>
   3#include <linux/mm.h>
   4#include <linux/uaccess.h>
   5#include <linux/mmzone.h>
   6#include <linux/ioport.h>
   7#include <linux/seq_file.h>
   8#include <linux/console.h>
   9#include <linux/init.h>
  10#include <linux/edd.h>
  11#include <linux/dmi.h>
  12#include <linux/pfn.h>
  13#include <linux/pci.h>
  14#include <linux/export.h>
  15
  16#include <asm/probe_roms.h>
  17#include <asm/pci-direct.h>
  18#include <asm/e820/api.h>
  19#include <asm/mmzone.h>
  20#include <asm/setup.h>
  21#include <asm/sections.h>
  22#include <asm/io.h>
  23#include <asm/setup_arch.h>
  24
  25static struct resource system_rom_resource = {
  26        .name   = "System ROM",
  27        .start  = 0xf0000,
  28        .end    = 0xfffff,
  29        .flags  = IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM
  30};
  31
  32static struct resource extension_rom_resource = {
  33        .name   = "Extension ROM",
  34        .start  = 0xe0000,
  35        .end    = 0xeffff,
  36        .flags  = IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM
  37};
  38
  39static struct resource adapter_rom_resources[] = { {
  40        .name   = "Adapter ROM",
  41        .start  = 0xc8000,
  42        .end    = 0,
  43        .flags  = IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM
  44}, {
  45        .name   = "Adapter ROM",
  46        .start  = 0,
  47        .end    = 0,
  48        .flags  = IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM
  49}, {
  50        .name   = "Adapter ROM",
  51        .start  = 0,
  52        .end    = 0,
  53        .flags  = IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM
  54}, {
  55        .name   = "Adapter ROM",
  56        .start  = 0,
  57        .end    = 0,
  58        .flags  = IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM
  59}, {
  60        .name   = "Adapter ROM",
  61        .start  = 0,
  62        .end    = 0,
  63        .flags  = IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM
  64}, {
  65        .name   = "Adapter ROM",
  66        .start  = 0,
  67        .end    = 0,
  68        .flags  = IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM
  69} };
  70
  71static struct resource video_rom_resource = {
  72        .name   = "Video ROM",
  73        .start  = 0xc0000,
  74        .end    = 0xc7fff,
  75        .flags  = IORESOURCE_BUSY | IORESOURCE_READONLY | IORESOURCE_MEM
  76};
  77
  78/* does this oprom support the given pci device, or any of the devices
  79 * that the driver supports?
  80 */
  81static bool match_id(struct pci_dev *pdev, unsigned short vendor, unsigned short device)
  82{
  83        struct pci_driver *drv = pdev->driver;
  84        const struct pci_device_id *id;
  85
  86        if (pdev->vendor == vendor && pdev->device == device)
  87                return true;
  88
  89        for (id = drv ? drv->id_table : NULL; id && id->vendor; id++)
  90                if (id->vendor == vendor && id->device == device)
  91                        break;
  92
  93        return id && id->vendor;
  94}
  95
  96static bool probe_list(struct pci_dev *pdev, unsigned short vendor,
  97                       const unsigned char *rom_list)
  98{
  99        unsigned short device;
 100
 101        do {
 102                if (probe_kernel_address(rom_list, device) != 0)
 103                        device = 0;
 104
 105                if (device && match_id(pdev, vendor, device))
 106                        break;
 107
 108                rom_list += 2;
 109        } while (device);
 110
 111        return !!device;
 112}
 113
 114static struct resource *find_oprom(struct pci_dev *pdev)
 115{
 116        struct resource *oprom = NULL;
 117        int i;
 118
 119        for (i = 0; i < ARRAY_SIZE(adapter_rom_resources); i++) {
 120                struct resource *res = &adapter_rom_resources[i];
 121                unsigned short offset, vendor, device, list, rev;
 122                const unsigned char *rom;
 123
 124                if (res->end == 0)
 125                        break;
 126
 127                rom = isa_bus_to_virt(res->start);
 128                if (probe_kernel_address(rom + 0x18, offset) != 0)
 129                        continue;
 130
 131                if (probe_kernel_address(rom + offset + 0x4, vendor) != 0)
 132                        continue;
 133
 134                if (probe_kernel_address(rom + offset + 0x6, device) != 0)
 135                        continue;
 136
 137                if (match_id(pdev, vendor, device)) {
 138                        oprom = res;
 139                        break;
 140                }
 141
 142                if (probe_kernel_address(rom + offset + 0x8, list) == 0 &&
 143                    probe_kernel_address(rom + offset + 0xc, rev) == 0 &&
 144                    rev >= 3 && list &&
 145                    probe_list(pdev, vendor, rom + offset + list)) {
 146                        oprom = res;
 147                        break;
 148                }
 149        }
 150
 151        return oprom;
 152}
 153
 154void __iomem *pci_map_biosrom(struct pci_dev *pdev)
 155{
 156        struct resource *oprom = find_oprom(pdev);
 157
 158        if (!oprom)
 159                return NULL;
 160
 161        return ioremap(oprom->start, resource_size(oprom));
 162}
 163EXPORT_SYMBOL(pci_map_biosrom);
 164
 165void pci_unmap_biosrom(void __iomem *image)
 166{
 167        iounmap(image);
 168}
 169EXPORT_SYMBOL(pci_unmap_biosrom);
 170
 171size_t pci_biosrom_size(struct pci_dev *pdev)
 172{
 173        struct resource *oprom = find_oprom(pdev);
 174
 175        return oprom ? resource_size(oprom) : 0;
 176}
 177EXPORT_SYMBOL(pci_biosrom_size);
 178
 179#define ROMSIGNATURE 0xaa55
 180
 181static int __init romsignature(const unsigned char *rom)
 182{
 183        const unsigned short * const ptr = (const unsigned short *)rom;
 184        unsigned short sig;
 185
 186        return probe_kernel_address(ptr, sig) == 0 && sig == ROMSIGNATURE;
 187}
 188
 189static int __init romchecksum(const unsigned char *rom, unsigned long length)
 190{
 191        unsigned char sum, c;
 192
 193        for (sum = 0; length && probe_kernel_address(rom++, c) == 0; length--)
 194                sum += c;
 195        return !length && !sum;
 196}
 197
 198void __init probe_roms(void)
 199{
 200        const unsigned char *rom;
 201        unsigned long start, length, upper;
 202        unsigned char c;
 203        int i;
 204
 205        /* video rom */
 206        upper = adapter_rom_resources[0].start;
 207        for (start = video_rom_resource.start; start < upper; start += 2048) {
 208                rom = isa_bus_to_virt(start);
 209                if (!romsignature(rom))
 210                        continue;
 211
 212                video_rom_resource.start = start;
 213
 214                if (probe_kernel_address(rom + 2, c) != 0)
 215                        continue;
 216
 217                /* 0 < length <= 0x7f * 512, historically */
 218                length = c * 512;
 219
 220                /* if checksum okay, trust length byte */
 221                if (length && romchecksum(rom, length))
 222                        video_rom_resource.end = start + length - 1;
 223
 224                request_resource(&iomem_resource, &video_rom_resource);
 225                break;
 226        }
 227
 228        start = (video_rom_resource.end + 1 + 2047) & ~2047UL;
 229        if (start < upper)
 230                start = upper;
 231
 232        /* system rom */
 233        request_resource(&iomem_resource, &system_rom_resource);
 234        upper = system_rom_resource.start;
 235
 236        /* check for extension rom (ignore length byte!) */
 237        rom = isa_bus_to_virt(extension_rom_resource.start);
 238        if (romsignature(rom)) {
 239                length = resource_size(&extension_rom_resource);
 240                if (romchecksum(rom, length)) {
 241                        request_resource(&iomem_resource, &extension_rom_resource);
 242                        upper = extension_rom_resource.start;
 243                }
 244        }
 245
 246        /* check for adapter roms on 2k boundaries */
 247        for (i = 0; i < ARRAY_SIZE(adapter_rom_resources) && start < upper; start += 2048) {
 248                rom = isa_bus_to_virt(start);
 249                if (!romsignature(rom))
 250                        continue;
 251
 252                if (probe_kernel_address(rom + 2, c) != 0)
 253                        continue;
 254
 255                /* 0 < length <= 0x7f * 512, historically */
 256                length = c * 512;
 257
 258                /* but accept any length that fits if checksum okay */
 259                if (!length || start + length > upper || !romchecksum(rom, length))
 260                        continue;
 261
 262                adapter_rom_resources[i].start = start;
 263                adapter_rom_resources[i].end = start + length - 1;
 264                request_resource(&iomem_resource, &adapter_rom_resources[i]);
 265
 266                start = adapter_rom_resources[i++].end & ~2047UL;
 267        }
 268}
 269
 270