summaryrefslogtreecommitdiff
path: root/cesar/ecos/packages/redboot/current/src/fs/disk.c
diff options
context:
space:
mode:
Diffstat (limited to 'cesar/ecos/packages/redboot/current/src/fs/disk.c')
-rw-r--r--cesar/ecos/packages/redboot/current/src/fs/disk.c441
1 files changed, 441 insertions, 0 deletions
diff --git a/cesar/ecos/packages/redboot/current/src/fs/disk.c b/cesar/ecos/packages/redboot/current/src/fs/disk.c
new file mode 100644
index 0000000000..96a9e82acf
--- /dev/null
+++ b/cesar/ecos/packages/redboot/current/src/fs/disk.c
@@ -0,0 +1,441 @@
+//==========================================================================
+//
+// 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 <redboot.h>
+#include <fs/disk.h>
+
+#ifdef CYGSEM_REDBOOT_DISK_EXT2FS
+#include <fs/e2fs.h>
+#endif
+#ifdef CYGSEM_REDBOOT_DISK_ISO9660
+#include <fs/iso9660fs.h>
+#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 <partition_name>:<filename> 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 <disk>:<path> 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);