diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:55:34 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:55:34 +0000 |
commit | 7f1d6c8fec531fa1762d6d65576aecbee837982c (patch) | |
tree | b37177c380fa30d0336aad7cac9c72035523206a /probe_roms.c | |
parent | Initial commit. (diff) | |
download | mdadm-7f1d6c8fec531fa1762d6d65576aecbee837982c.tar.xz mdadm-7f1d6c8fec531fa1762d6d65576aecbee837982c.zip |
Adding upstream version 4.3.upstream/4.3
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | probe_roms.c | 331 |
1 files changed, 331 insertions, 0 deletions
diff --git a/probe_roms.c b/probe_roms.c new file mode 100644 index 0000000..94c80c2 --- /dev/null +++ b/probe_roms.c @@ -0,0 +1,331 @@ +/* + * 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 "mdadm.h" +#include <unistd.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; +static const int rom_len = 0xf0000 - 0xc0000; /* option-rom memory region */ +static int _sigbus; +static unsigned long rom_align; + +static void roms_deinit(void); +static int roms_init(void); + +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_s(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; + } + roms_deinit(); +} + +int probe_roms_init(unsigned long align) +{ + int fd = -1; + int rc = 0; + + /* valid values are 2048 and 512. 512 is for PCI-3.0 compliant + * systems, or systems that do not have dangerous/legacy ISA + * devices. 2048 should always be safe + */ + if (align == 512 || align == 2048) + rom_align = align; + else + return -1; + + if (roms_init()) + return -1; + + if (signal_s(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 { + if (fd >= 0) + close(fd); + 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; + unsigned long data; + const char *name; + struct resource *next; +}; + +static struct resource system_rom_resource = { + .name = "System ROM", + .start = 0xf0000, + .data = 0, + .end = 0xfffff, +}; + +static struct resource extension_rom_resource = { + .name = "Extension ROM", + .start = 0xe0000, + .data = 0, + .end = 0xeffff, +}; + +static struct resource *adapter_rom_resources; + +static struct resource video_rom_resource = { + .name = "Video ROM", + .start = 0xc0000, + .data = 0, + .end = 0xc7fff, +}; + +static int roms_init(void) +{ + adapter_rom_resources = malloc(sizeof(struct resource)); + if (adapter_rom_resources == NULL) + return 1; + adapter_rom_resources->name = "Adapter ROM"; + adapter_rom_resources->start = 0xc8000; + adapter_rom_resources->data = 0; + adapter_rom_resources->end = 0; + adapter_rom_resources->next = NULL; + return 0; +} + +static void roms_deinit(void) +{ + struct resource *res; + + res = adapter_rom_resources; + while (res) { + struct resource *tmp = res; + + res = res->next; + free(tmp); + } +} + +#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 */ + struct resource *res = adapter_rom_resources; + int found; + + if (rom_fd < 0) + return 0; + + found = 0; + while (res) { + if (res->start) { + found = fn(isa_bus_to_virt(res->start), + isa_bus_to_virt(res->end), + isa_bus_to_virt(res->data)); + if (found) + break; + } else + break; + res = res->next; + } + + return found; +} + +static unsigned long align(unsigned long addr, unsigned long alignment) +{ + return (addr + alignment - 1) & ~(alignment - 1); +} + +void probe_roms(void) +{ + const void *rom; + unsigned long start, length, upper; + unsigned char c; + struct resource *res = adapter_rom_resources; + __u16 val=0; + + if (rom_fd < 0) + return; + + /* video rom */ + upper = res->start; + for (start = video_rom_resource.start; start < upper; start += rom_align) { + 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 = align(video_rom_resource.end + 1, rom_align); + 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; + } + + struct resource *prev_res = res; + /* check for adapter roms on 2k boundaries */ + for (; start < upper; start += rom_align) { + 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; + + /* Retrieve 16-bit pointer to PCI Data Structure (offset 18h-19h) + * The data can be within 64KB forward of the first location + * of this code image. The pointer is in little-endian order + */ + + if (probe_address16(rom + 0x18, &val) != 0) + continue; + val = __le16_to_cpu(val); + + /* but accept any length that fits if checksum okay */ + if (!length || start + length > upper || !romchecksum(rom, length)) + continue; + + if (res == NULL) { + res = calloc(1, sizeof(struct resource)); + if (res == NULL) + return; + prev_res->next = res; + } + + res->start = start; + res->data = start + (unsigned long)val; + res->end = start + length - 1; + + start = res->end & ~(rom_align - 1); + prev_res = res; + res = res->next; + } +} |