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