/* * eddsupport.c - handling of mapping disk drives in Linux to disk drives * according to the BIOS using the edd kernel module * * Copyright (C) 2004 Dell, Inc. All rights reserved. * Copyright (C) 2004 Red Hat, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that 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, see . * * Author(s): Rezwanul_Kabir@Dell.com * Jeremy Katz */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "eddsupport.h" #include "devices.h" #include "isys.h" #define EDD_DIR "/sys/firmware/edd" #define SIG_FILE "mbr_signature" #define MBRSIG_OFFSET 0x1b8 #define HASH_TABLE_SIZE 17 struct diskMapEntry{ uint32_t key; char *diskname; struct diskMapEntry *next; }; struct diskMapTable { struct diskMapEntry **table; int tableSize; }; static struct diskMapTable *mbrSigToName = NULL; static int diskHashInit = 0; static struct diskMapTable* initializeHashTable(int); static int insertHashItem(struct diskMapTable *, struct diskMapEntry *); static struct diskMapEntry* lookupHashItem(struct diskMapTable *, uint32_t); static int addToHashTable(struct diskMapTable *, uint32_t , char *); static struct device ** createDiskList(); static int mapBiosDisks(struct device ** , const char *); static int readDiskSig(char *, uint32_t *); static int readMbrSig(char *, uint32_t *); /* This is the top level function that creates a disk list present in the * system, checks to see if unique signatures exist on the disks at offset * 0x1b8. If a unique signature exists then it will map BIOS disks to their * corresponding hd/sd device names. Otherwise, we'll avoid mapping drives. */ int probeBiosDisks() { struct device ** devices = NULL; devices = createDiskList(); if(!devices){ #ifdef STANDALONE fprintf(stderr, "No disks!\n"); #endif return -1; } if(!mapBiosDisks(devices, EDD_DIR)){ #ifdef STANDALONE fprintf(stderr, "WARNING: couldn't map BIOS disks\n"); #endif return -1; } return 0; } static struct device ** createDiskList(){ return getDevices (DEVICE_DISK); } static int readDiskSig(char *device, uint32_t *disksig) { int fd, rc; fd = open(device, O_RDONLY); if (fd < 0) { #ifdef STANDALONE fprintf(stderr, "Error opening device %s: %s\n ", device, strerror(errno)); #endif return -errno; } rc = lseek(fd, MBRSIG_OFFSET, SEEK_SET); if (rc < 0){ close(fd); #ifdef STANDALONE fprintf(stderr, "Error seeking to MBRSIG_OFFSET in %s: %s\n", device, strerror(errno)); #endif return -1; } rc = read(fd, disksig, sizeof(uint32_t)); if (rc < sizeof(uint32_t)) { close(fd); #ifdef STANDALONE fprintf(stderr, "Failed to read signature from %s\n", device); #endif return -1; } close(fd); return 0; } static int mapBiosDisks(struct device** devices,const char *path) { DIR *dirHandle; struct dirent *entry; char * sigFileName; uint32_t mbrSig, biosNum, currentSig; struct device **currentDev, **foundDisk; int i, rc, ret; dirHandle = opendir(path); if(!dirHandle){ #ifdef STANDALONE fprintf(stderr, "Failed to open directory %s: %s\n", path, strerror(errno)); #endif return 0; } mbrSigToName = initializeHashTable(HASH_TABLE_SIZE); if(!mbrSigToName){ #ifdef STANDALONE fprintf(stderr, "Error initializing mbrSigToName table\n"); #endif closedir(dirHandle); return 0; } while ((entry = readdir(dirHandle)) != NULL) { if(!strncmp(entry->d_name,".",1) || !strncmp(entry->d_name,"..",2)) { continue; } ret = sscanf((entry->d_name+9), "%x", &biosNum); sigFileName = malloc(strlen(path) + strlen(entry->d_name) + 20); sprintf(sigFileName, "%s/%s/%s", path, entry->d_name, SIG_FILE); if (readMbrSig(sigFileName, &mbrSig) == 0) { for (currentDev = devices, i = 0, foundDisk=NULL; (*currentDev) != NULL && i<2; currentDev++) { if (!(*currentDev)->device) continue; if ((rc=readDiskSig((*currentDev)->device, ¤tSig)) < 0) { if (rc == -ENOMEDIUM) continue; closedir(dirHandle); return 0; } if (mbrSig == currentSig) { foundDisk=currentDev; i++; } } if (i==1) { if(!addToHashTable(mbrSigToName, (uint32_t)biosNum, (*foundDisk)->device)) { closedir(dirHandle); return 0; } } } } closedir(dirHandle); return 1; } static int readMbrSig(char *filename, uint32_t *int_sig){ FILE* fh; fh = fopen(filename,"r"); if(fh == NULL) { #ifdef STANDALONE fprintf(stderr, "Error opening mbr_signature file %s: %s\n", filename, strerror(errno)); #endif return -1; } fseek(fh, 0, SEEK_SET); if (fscanf(fh, "%x", int_sig) != 1) { #ifdef STANDALONE fprintf(stderr, "Error reading %s\n", filename); #endif fclose(fh); return -1; } fclose(fh); return 0; } static struct diskMapTable* initializeHashTable(int size) { struct diskMapTable *hashTable; hashTable = malloc(sizeof(struct diskMapTable)); hashTable->tableSize = size; hashTable->table = malloc(sizeof(struct diskMapEntry *) * size); memset(hashTable->table,0,(sizeof(struct diskMapEntry *) * size)); return hashTable; } static int insertHashItem(struct diskMapTable *hashTable, struct diskMapEntry *hashItem) { int index; index = (hashItem->key) % (hashTable->tableSize); if(hashTable->table[index] == NULL){ hashTable->table[index] = hashItem; return index; } else { hashItem->next = hashTable->table[index]; hashTable->table[index] = hashItem; return index; } } static struct diskMapEntry * lookupHashItem(struct diskMapTable *hashTable, uint32_t itemKey) { int index; struct diskMapEntry *hashItem; index = itemKey % (hashTable->tableSize); for (hashItem = hashTable->table[index]; (hashItem != NULL) && (hashItem->key != itemKey); hashItem = hashItem->next) { ; } return hashItem; } static int addToHashTable(struct diskMapTable *hashTable, uint32_t itemKey, char *diskName) { int index; struct diskMapEntry *diskSigToNameEntry; diskSigToNameEntry = malloc(sizeof(struct diskMapEntry)); diskSigToNameEntry->next = NULL; diskSigToNameEntry->key = itemKey; diskSigToNameEntry->diskname = diskName; if ((index = insertHashItem(hashTable, diskSigToNameEntry)) < 0){ #ifdef STANDALONE fprintf(stderr, "Unable to insert item\n"); #endif return 0; } else { return 1; } } char * getBiosDisk(char *biosStr) { uint32_t biosNum; struct diskMapEntry * disk; int ret; if (diskHashInit == 0) { probeBiosDisks(); diskHashInit = 1; } if (mbrSigToName == NULL) return NULL; ret = sscanf(biosStr,"%x",&biosNum); disk = lookupHashItem(mbrSigToName, biosNum); if (disk) return disk->diskname; return NULL; }