//========================================================================== // // disk.c // // RedBoot disk support // //========================================================================== //####ECOSGPLCOPYRIGHTBEGIN#### // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. // Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004 Red Hat, Inc. // Copyright (C) 2002 Gary Thomas // // eCos 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 or (at your option) any later version. // // eCos 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 eCos; if not, write to the Free Software Foundation, Inc., // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. // // As a special exception, if other files instantiate templates or use macros // or inline functions from this file, or you compile this file and link it // with other works to produce a work based on this file, this file does not // by itself cause the resulting work to be covered by the GNU General Public // License. However the source code for this file must still be made available // in accordance with section (3) of the GNU General Public License. // // This exception does not invalidate any other reasons why a work based on // this file might be covered by the GNU General Public License. // // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. // at http://sources.redhat.com/ecos/ecos-license/ // ------------------------------------------- //####ECOSGPLCOPYRIGHTEND#### //========================================================================== //#####DESCRIPTIONBEGIN#### // // Author(s): msalter // Contributors: msalter // Date: 2001-07-14 // Purpose: // Description: // // This code is part of RedBoot (tm). // //####DESCRIPTIONEND#### // //========================================================================== #include #include #ifdef CYGSEM_REDBOOT_DISK_EXT2FS #include #endif #ifdef CYGSEM_REDBOOT_DISK_ISO9660 #include #endif static void do_disks(int argc, char *argv[]); RedBoot_cmd("disks", "Display disks/partitions.", "", do_disks ); static disk_t disk_table[CYGNUM_REDBOOT_MAX_DISKS]; static int disk_count = 0; static inline cyg_uint32 u32_unaligned(void *p) { cyg_uint32 val; char *d = (char *)&val; char *s = p; int i; for (i = 0; i < 4; i++) *d++ = *s++; return val; } static int find_dos_partitions(disk_t *d, cyg_uint8 *mbr) { cyg_uint32 s, n; struct mbr_partition *p; int i, found = 0; p = (struct mbr_partition *)(mbr + MBR_PTABLE_OFFSET); // Look for primary partitions for (i = 0; i < 4 && i < CYGNUM_REDBOOT_MAX_PARTITIONS; i++) { s = SWAB_LE32(u32_unaligned(p->start_sect)); n = SWAB_LE32(u32_unaligned(p->nr_sects)); if (s && n) { ++found; d->partitions[i].disk = d; d->partitions[i].start_sector = s; d->partitions[i].nr_sectors = n; d->partitions[i].systype = p->sys_ind; d->partitions[i].bootflag = p->boot_ind; } p++; } #if CYGNUM_REDBOOT_MAX_PARTITIONS > 4 { cyg_uint32 buf[SECTOR_SIZE/sizeof(cyg_uint32)], xoffset; cyg_uint16 magic; int nextp; // Go back through and find extended partitions for (i = 0, nextp = 4; i < 4 && nextp < CYGNUM_REDBOOT_MAX_PARTITIONS; i++) { if (d->partitions[i].systype == SYSTYPE_EXTENDED) { // sector offsets in partition tables are relative to start // of extended partition. xoffset = d->partitions[i].start_sector; for ( ; nextp < CYGNUM_REDBOOT_MAX_PARTITIONS; ++nextp) { // read partition boot record (same format as mbr except // there should only be 2 entries max: a normal partition // and another extended partition if (DISK_READ(d, xoffset, buf, 1) <= 0) break; magic = *(cyg_uint16 *)((char *)buf + MBR_MAGIC_OFFSET); if (SWAB_LE16(magic) != MBR_MAGIC) break; p = (struct mbr_partition *)((char *)buf + MBR_PTABLE_OFFSET); s = SWAB_LE32(u32_unaligned(p->start_sect)); n = SWAB_LE32(u32_unaligned(p->nr_sects)); if (s && n) { ++found; d->partitions[nextp].disk = d; d->partitions[nextp].start_sector = s + xoffset; d->partitions[nextp].nr_sectors = n; d->partitions[nextp].systype = p->sys_ind; d->partitions[nextp].bootflag = p->boot_ind; } ++p; s = SWAB_LE32(u32_unaligned(p->start_sect)); n = SWAB_LE32(u32_unaligned(p->nr_sects)); // more extended partitions? if (p->sys_ind != SYSTYPE_EXTENDED || !s || !n) break; xoffset += s; } } } } #endif return found; } // Find partitions on given disk. // Return number of partitions found static int find_partitions(disk_t *d) { cyg_uint32 buf[SECTOR_SIZE/sizeof(cyg_uint32)]; cyg_uint16 magic; partition_t *p; int i, found = 0; if (d->kind == DISK_IDE_CDROM) { #ifdef CYGSEM_REDBOOT_DISK_ISO9660 // no partition table, so fake it p = d->partitions; p->disk = d; p->start_sector = 0; p->nr_sectors = d->nr_sectors; p->funs = &redboot_iso9660fs_funs; return 1; #else return 0; #endif } // read Master Boot Record if (DISK_READ(d, 0, buf, 1) <= 0) return 0; // Check for DOS MBR magic = *(cyg_uint16 *)((char *)buf + MBR_MAGIC_OFFSET); if (SWAB_LE16(magic) == MBR_MAGIC) { found = find_dos_partitions(d, (cyg_uint8 *)buf); } else { // Might want to handle other MBR types, here... } // Now go through all partitions and install the correct // funcs for supported filesystems. for (i = 0, p = d->partitions; i < CYGNUM_REDBOOT_MAX_PARTITIONS; i++, p++) { switch (p->systype) { #ifdef CYGSEM_REDBOOT_DISK_EXT2FS case SYSTYPE_LINUX: p->funs = &redboot_e2fs_funs; break; #endif #ifdef CYGSEM_REDBOOT_DISK_FAT16 case SYSTYPE_FAT16: p->funs = &redboot_fat16_funs; break; #endif #ifdef CYGSEM_REDBOOT_DISK_FAT32 case SYSTYPE_FAT32: p->funs = &redboot_fat32_funs; break; #endif default: break; // ignore unsupported filesystems } } return found; } // Add a disk to the disk table. // Return zero if no more room in table. externC int disk_register(disk_t *d) { int i; // make sure we have room for it if (disk_count >= CYGNUM_REDBOOT_MAX_DISKS) return 0; // Set the index d->index = 0; for (i = 0; i < disk_count; i++) if (disk_table[i].kind == d->kind) d->index++; // put it in the table disk_table[disk_count] = *d; // fill in partition info find_partitions(&disk_table[disk_count++]); return 1; } // Convert a filename in the form : into // a partition and path. // static int disk_parse_filename(const char *name, partition_t **part, const char **path) { int i, kind, index, pindex; kind = index = pindex = 0; if (name[0] == 'h' && name[1] == 'd') { // IDE hard drives kind = DISK_IDE_HD; if (name[2] < 'a' || name[2] > 'z') return 0; index = name[2] - 'a'; if (name[3] < '1' || name[3] >= ('1' + CYGNUM_REDBOOT_MAX_PARTITIONS)) return 0; pindex = name[3] - '1'; if (name[4] != ':') return 0; *path = &name[5]; } #ifdef CYGSEM_REDBOOT_DISK_ISO9660 else if (name[0] == 'c' && name[1] == 'd') { // CD drives kind = DISK_IDE_CDROM; if (name[2] < '0' || name[2] > '9') return 0; index = name[2] - '0'; if (name[3] != ':') return 0; *path = &name[4]; } #endif if (kind) { for (i = 0; i < CYGNUM_REDBOOT_MAX_DISKS; i++) { if (disk_table[i].kind == kind && disk_table[i].index == index) { *part = &disk_table[i].partitions[pindex]; return 1; } } } return 0; } static const struct { int kind; const char *str; } systype_names[] = { { SYSTYPE_FAT12, "FAT12" }, { SYSTYPE_FAT16_32M, "FAT16 <32M" }, { SYSTYPE_FAT16, "FAT16" }, { SYSTYPE_EXTENDED, "Extended" }, { SYSTYPE_LINUX_SWAP, "Linux Swap" }, { SYSTYPE_LINUX, "Linux" } }; static const char * systype_name(int systype) { int i; for (i = 0; i < sizeof(systype_names)/sizeof(systype_names[0]); i++) if (systype_names[i].kind == systype) return systype_names[i].str; return "Unknown"; } // List disk partitions static void do_disks(int argc, char *argv[]) { int i, j; disk_t *d; partition_t *p; char name[16]; for (i = 0, d = disk_table; i < disk_count; i++, d++) { switch (d->kind) { case DISK_IDE_HD: for (j = 0, p = d->partitions; j < CYGNUM_REDBOOT_MAX_PARTITIONS; j++, p++) { if (p->systype) { diag_sprintf(name, "hd%c%d", 'a' + d->index, j+1); diag_printf("%-8s %s\n", name, systype_name(p->systype)); } } break; case DISK_IDE_CDROM: diag_sprintf(name, "cd%d", d->index); diag_printf("%-8s ISO9660\n", name); break; } } } static void *fileptr; static partition_t *file_part; externC int disk_stream_open(connection_info_t *info, int *err) { const char *filepath; char *filename = info->filename; // The filename is in : format. // Convert to a partition and path. if (!disk_parse_filename(filename, &file_part, &filepath)) { *err = diskerr_badname; return -1; } if (file_part->disk->kind != DISK_IDE_CDROM && file_part->systype == 0) { *err = diskerr_partition; return -1; } if (file_part->funs == (fs_funs_t *)0) { *err = diskerr_partition; return -1; } fileptr = (file_part->funs->open)(file_part, filepath); if (fileptr == NULL) { *err = diskerr_open; return -1; } return 0; } externC int disk_stream_read(char *buf, int size, int *err) { int nread; if ((nread = (file_part->funs->read)(fileptr, buf, size)) < 0) { *err = diskerr_read; return -1; } return nread; } externC void disk_stream_close(int *err) { fileptr = NULL; } externC char * disk_error(int err) { switch (err) { case diskerr_badname: return "Bad filename"; break; case diskerr_partition: return "Unsupported filesystem"; break; case diskerr_open: return "Can't open file"; break; case diskerr_read: return "Can't read file"; break; default: return "Unknown error"; break; } } // // RedBoot interface // GETC_IO_FUNCS(disk_io, disk_stream_open, disk_stream_close, 0, disk_stream_read, disk_error); RedBoot_load(disk, disk_io, true, true, 0);