diff options
author | Dan Williams <dan.j.williams@intel.com> | 2008-12-09 00:59:18 +0100 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2008-12-09 00:59:18 +0100 |
commit | 39795f9cda7ae4cf57e1743d61a909507de9eef8 (patch) | |
tree | 923b723cd633d02d4de9c165f2ee8f44e63e9d3e /probe_roms.c | |
parent | imsm: pass disk info in create message (diff) | |
download | mdadm-39795f9cda7ae4cf57e1743d61a909507de9eef8.tar.xz mdadm-39795f9cda7ae4cf57e1743d61a909507de9eef8.zip |
port arch/x86/kernel/probe_roms_32.c for use in 'platform' support
This provides at least a kernel bug compatible method of scanning for an
adapter-rom.
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Diffstat (limited to 'probe_roms.c')
-rw-r--r-- | probe_roms.c | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/probe_roms.c b/probe_roms.c new file mode 100644 index 00000000..06ec3f5a --- /dev/null +++ b/probe_roms.c @@ -0,0 +1,279 @@ +/* + * probe_roms - scan for Adapter ROMS + * + * (based on linux-2.6:arch/x86/kernel/probe_roms_32.c) + * + * Copyright (C) 2008 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "probe_roms.h" +#include <unistd.h> +#include <signal.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <asm/types.h> + +static void *rom_mem = MAP_FAILED; +static int rom_fd = -1; +const static int rom_len = 0xf0000 - 0xc0000; /* option-rom memory region */ +static int _sigbus; +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) + +static void sigbus(int sig) +{ + _sigbus = 1; +} + +static int probe_address8(const __u8 *ptr, __u8 *val) +{ + int rc = 0; + + *val = *ptr; + if (_sigbus) + rc = -1; + _sigbus = 0; + + return rc; +} + +static int probe_address16(const __u16 *ptr, __u16 *val) +{ + int rc = 0; + + *val = *ptr; + if (_sigbus) + rc = -1; + _sigbus = 0; + + return rc; +} + +void probe_roms_exit(void) +{ + signal(SIGBUS, SIG_DFL); + if (rom_fd >= 0) { + close(rom_fd); + rom_fd = -1; + } + if (rom_mem != MAP_FAILED) { + munmap(rom_mem, rom_len); + rom_mem = MAP_FAILED; + } +} + +int probe_roms_init(void) +{ + int fd; + int rc = 0; + + if (signal(SIGBUS, sigbus) == SIG_ERR) + rc = -1; + if (rc == 0) { + fd = open("/dev/mem", O_RDONLY); + if (fd < 0) + rc = -1; + } + if (rc == 0) { + rom_mem = mmap(NULL, rom_len, PROT_READ, MAP_PRIVATE, fd, 0xc0000); + if (rom_mem == MAP_FAILED) + rc = -1; + } + + if (rc == 0) + rom_fd = fd; + else + probe_roms_exit(); + + return rc; +} + +/** + * isa_bus_to_virt - convert physical address to mmap'd region + * @addr - address to convert + * + * Only valid between a successful call to probe_roms_init and the + * corresponding probe_roms_exit + */ +static void *isa_bus_to_virt(unsigned long addr) +{ + return rom_mem + (addr - 0xc0000); +} + +struct resource { + unsigned long start; + unsigned long end; + const char *name; +}; + +static struct resource system_rom_resource = { + .name = "System ROM", + .start = 0xf0000, + .end = 0xfffff, +}; + +static struct resource extension_rom_resource = { + .name = "Extension ROM", + .start = 0xe0000, + .end = 0xeffff, +}; + +static struct resource adapter_rom_resources[] = { { + .name = "Adapter ROM", + .start = 0xc8000, + .end = 0, +}, { + .name = "Adapter ROM", + .start = 0, + .end = 0, +}, { + .name = "Adapter ROM", + .start = 0, + .end = 0, +}, { + .name = "Adapter ROM", + .start = 0, + .end = 0, +}, { + .name = "Adapter ROM", + .start = 0, + .end = 0, +}, { + .name = "Adapter ROM", + .start = 0, + .end = 0, +} }; + +static struct resource video_rom_resource = { + .name = "Video ROM", + .start = 0xc0000, + .end = 0xc7fff, +}; + +#define ROMSIGNATURE 0xaa55 + +static int romsignature(const unsigned char *rom) +{ + const unsigned short * const ptr = (const unsigned short *)rom; + unsigned short sig = 0; + + return probe_address16(ptr, &sig) == 0 && sig == ROMSIGNATURE; +} + +static int romchecksum(const unsigned char *rom, unsigned long length) +{ + unsigned char sum, c; + + for (sum = 0; length && probe_address8(rom++, &c) == 0; length--) + sum += c; + return !length && !sum; +} + +int scan_adapter_roms(scan_fn fn) +{ + /* let scan_fn examing each of the adapter roms found by probe_roms */ + int i; + int found; + + if (rom_fd < 0) + return 0; + + found = 0; + for (i = 0; i < ARRAY_SIZE(adapter_rom_resources); i++) { + struct resource *res = &adapter_rom_resources[i]; + + if (res->start) { + found = fn(isa_bus_to_virt(res->start), + isa_bus_to_virt(res->end)); + if (found) + break; + } else + break; + } + + return found; +} + +void probe_roms(void) +{ + const void *rom; + unsigned long start, length, upper; + unsigned char c; + int i; + + if (rom_fd < 0) + return; + + /* video rom */ + upper = adapter_rom_resources[0].start; + for (start = video_rom_resource.start; start < upper; start += 2048) { + rom = isa_bus_to_virt(start); + if (!romsignature(rom)) + continue; + + video_rom_resource.start = start; + + if (probe_address8(rom + 2, &c) != 0) + continue; + + /* 0 < length <= 0x7f * 512, historically */ + length = c * 512; + + /* if checksum okay, trust length byte */ + if (length && romchecksum(rom, length)) + video_rom_resource.end = start + length - 1; + break; + } + + start = (video_rom_resource.end + 1 + 2047) & ~2047UL; + if (start < upper) + start = upper; + + /* system rom */ + upper = system_rom_resource.start; + + /* check for extension rom (ignore length byte!) */ + rom = isa_bus_to_virt(extension_rom_resource.start); + if (romsignature(rom)) { + length = extension_rom_resource.end - extension_rom_resource.start + 1; + if (romchecksum(rom, length)) + upper = extension_rom_resource.start; + } + + /* check for adapter roms on 2k boundaries */ + for (i = 0; i < ARRAY_SIZE(adapter_rom_resources) && start < upper; start += 2048) { + rom = isa_bus_to_virt(start); + if (!romsignature(rom)) + continue; + + if (probe_address8(rom + 2, &c) != 0) + continue; + + /* 0 < length <= 0x7f * 512, historically */ + length = c * 512; + + /* but accept any length that fits if checksum okay */ + if (!length || start + length > upper || !romchecksum(rom, length)) + continue; + + adapter_rom_resources[i].start = start; + adapter_rom_resources[i].end = start + length - 1; + + start = adapter_rom_resources[i++].end & ~2047UL; + } +} + |