]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/redboot/v2_0/src/fs/disk.c
Initial revision
[karo-tx-redboot.git] / packages / redboot / v2_0 / src / fs / disk.c
1 //==========================================================================
2 //
3 //      disk.c
4 //
5 //      RedBoot disk support
6 //
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004 Red Hat, Inc.
12 // Copyright (C) 2002 Gary Thomas
13 //
14 // eCos is free software; you can redistribute it and/or modify it under
15 // the terms of the GNU General Public License as published by the Free
16 // Software Foundation; either version 2 or (at your option) any later version.
17 //
18 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
19 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
21 // for more details.
22 //
23 // You should have received a copy of the GNU General Public License along
24 // with eCos; if not, write to the Free Software Foundation, Inc.,
25 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 //
27 // As a special exception, if other files instantiate templates or use macros
28 // or inline functions from this file, or you compile this file and link it
29 // with other works to produce a work based on this file, this file does not
30 // by itself cause the resulting work to be covered by the GNU General Public
31 // License. However the source code for this file must still be made available
32 // in accordance with section (3) of the GNU General Public License.
33 //
34 // This exception does not invalidate any other reasons why a work based on
35 // this file might be covered by the GNU General Public License.
36 //
37 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
38 // at http://sources.redhat.com/ecos/ecos-license/
39 // -------------------------------------------
40 //####ECOSGPLCOPYRIGHTEND####
41 //==========================================================================
42 //#####DESCRIPTIONBEGIN####
43 //
44 // Author(s):    msalter
45 // Contributors: msalter
46 // Date:         2001-07-14
47 // Purpose:      
48 // Description:  
49 //              
50 // This code is part of RedBoot (tm).
51 //
52 //####DESCRIPTIONEND####
53 //
54 //==========================================================================
55
56 #include <redboot.h>
57 #include <fs/disk.h>
58
59 #ifdef CYGSEM_REDBOOT_DISK_EXT2FS
60 #include <fs/e2fs.h>
61 #endif
62 #ifdef CYGSEM_REDBOOT_DISK_ISO9660
63 #include <fs/iso9660fs.h>
64 #endif
65
66 static void do_disks(int argc, char *argv[]);
67
68 RedBoot_cmd("disks", 
69             "Display disks/partitions.",
70             "",
71             do_disks
72     );
73
74 static disk_t disk_table[CYGNUM_REDBOOT_MAX_DISKS];
75 static int    disk_count = 0;
76
77 static inline cyg_uint32
78 u32_unaligned(void *p)
79 {
80     cyg_uint32 val;
81     char *d = (char *)&val;
82     char *s = p;
83     int i;
84
85     for (i = 0; i < 4; i++)
86         *d++ = *s++;
87
88     return val;
89 }
90
91 static int
92 find_dos_partitions(disk_t *d, cyg_uint8 *mbr)
93 {
94     cyg_uint32 s, n;
95     struct mbr_partition *p;
96     int i, found = 0;
97
98     p = (struct mbr_partition *)(mbr + MBR_PTABLE_OFFSET);
99
100     // Look for primary partitions
101     for (i = 0; i < 4 && i < CYGNUM_REDBOOT_MAX_PARTITIONS; i++) {
102
103         s = SWAB_LE32(u32_unaligned(p->start_sect));
104         n = SWAB_LE32(u32_unaligned(p->nr_sects));
105
106         if (s && n) {
107             ++found;
108             d->partitions[i].disk = d;
109             d->partitions[i].start_sector = s;
110             d->partitions[i].nr_sectors = n;
111             d->partitions[i].systype = p->sys_ind;
112             d->partitions[i].bootflag = p->boot_ind;
113         }
114         p++;
115     }
116
117 #if CYGNUM_REDBOOT_MAX_PARTITIONS > 4
118     {
119         cyg_uint32 buf[SECTOR_SIZE/sizeof(cyg_uint32)], xoffset;
120         cyg_uint16 magic;
121         int nextp;
122
123         // Go back through and find extended partitions
124         for (i = 0, nextp = 4; i < 4 && nextp < CYGNUM_REDBOOT_MAX_PARTITIONS; i++) {
125             if (d->partitions[i].systype == SYSTYPE_EXTENDED) {
126                 // sector offsets in partition tables are relative to start
127                 // of extended partition.
128                 xoffset = d->partitions[i].start_sector;
129                 for ( ; nextp < CYGNUM_REDBOOT_MAX_PARTITIONS; ++nextp) {
130
131                     // read partition boot record (same format as mbr except
132                     // there should only be 2 entries max: a normal partition
133                     // and another extended partition
134                     if (DISK_READ(d, xoffset, buf, 1) <= 0)
135                         break;
136
137                     magic = *(cyg_uint16 *)((char *)buf + MBR_MAGIC_OFFSET);
138                     if (SWAB_LE16(magic) != MBR_MAGIC)
139                         break;
140
141                     p = (struct mbr_partition *)((char *)buf + MBR_PTABLE_OFFSET);
142
143                     s = SWAB_LE32(u32_unaligned(p->start_sect));
144                     n = SWAB_LE32(u32_unaligned(p->nr_sects));
145
146                     if (s && n) {
147                         ++found;
148                         d->partitions[nextp].disk = d;
149                         d->partitions[nextp].start_sector = s + xoffset;
150                         d->partitions[nextp].nr_sectors = n;
151                         d->partitions[nextp].systype = p->sys_ind;
152                         d->partitions[nextp].bootflag = p->boot_ind;
153                     }
154                     ++p;
155
156                     s = SWAB_LE32(u32_unaligned(p->start_sect));
157                     n = SWAB_LE32(u32_unaligned(p->nr_sects));
158
159                     // more extended partitions?
160                     if (p->sys_ind != SYSTYPE_EXTENDED || !s || !n)
161                         break;
162
163                     xoffset += s;
164                 }
165             }
166         }
167     }
168 #endif
169     return found;
170 }
171
172
173 // Find partitions on given disk.
174 // Return number of partitions found
175 static int
176 find_partitions(disk_t *d)
177 {
178     cyg_uint32 buf[SECTOR_SIZE/sizeof(cyg_uint32)];
179     cyg_uint16 magic;
180     partition_t *p;
181     int i, found = 0;
182
183
184     if (d->kind == DISK_IDE_CDROM) {
185 #ifdef CYGSEM_REDBOOT_DISK_ISO9660
186         // no partition table, so fake it
187         p = d->partitions;
188         p->disk = d;
189         p->start_sector = 0;
190         p->nr_sectors = d->nr_sectors;
191         p->funs = &redboot_iso9660fs_funs;
192         return 1;
193 #else
194         return 0;
195 #endif
196     }
197
198     // read Master Boot Record
199     if (DISK_READ(d, 0, buf, 1) <= 0)
200         return 0;
201
202     // Check for DOS MBR
203     magic = *(cyg_uint16 *)((char *)buf + MBR_MAGIC_OFFSET);
204     if (SWAB_LE16(magic) == MBR_MAGIC) {
205         found = find_dos_partitions(d, (cyg_uint8 *)buf);
206     } else {
207         // Might want to handle other MBR types, here...
208     }
209
210     // Now go through all partitions and install the correct
211     // funcs for supported filesystems.
212     for (i = 0, p = d->partitions; i < CYGNUM_REDBOOT_MAX_PARTITIONS; i++, p++) {
213         switch (p->systype) {
214 #ifdef CYGSEM_REDBOOT_DISK_EXT2FS
215           case SYSTYPE_LINUX:
216             p->funs = &redboot_e2fs_funs;
217             break;
218 #endif
219 #ifdef CYGSEM_REDBOOT_DISK_FAT16
220           case SYSTYPE_FAT16:
221             p->funs = &redboot_fat16_funs;
222             break;
223 #endif
224 #ifdef CYGSEM_REDBOOT_DISK_FAT32
225           case SYSTYPE_FAT32:
226             p->funs = &redboot_fat32_funs;
227             break;
228 #endif
229           default:
230             break;  // ignore unsupported filesystems
231         }
232     }
233
234     return found;
235 }
236
237 // Add a disk to the disk table.
238 // Return zero if no more room in table.
239 externC int
240 disk_register(disk_t *d)
241 {
242     int i;
243
244     // make sure we have room for it
245     if (disk_count >= CYGNUM_REDBOOT_MAX_DISKS)
246         return 0;
247
248     // Set the index
249     d->index = 0;
250     for (i = 0; i < disk_count; i++)
251         if (disk_table[i].kind == d->kind)
252             d->index++;
253
254     // put it in the table
255     disk_table[disk_count] = *d;
256
257     // fill in partition info
258     find_partitions(&disk_table[disk_count++]);
259
260     return 1;
261 }
262
263 // Convert a filename in the form <partition_name>:<filename> into
264 // a partition and path.
265 //
266 static int
267 disk_parse_filename(const char *name, partition_t **part, const char **path)
268 {
269     int i, kind, index, pindex;
270
271     kind = index = pindex = 0;
272
273     if (name[0] == 'h' && name[1] == 'd') {
274         // IDE hard drives
275         kind = DISK_IDE_HD;
276         if (name[2] < 'a' || name[2] > 'z')
277             return 0;
278         index = name[2] - 'a';
279         if (name[3] < '1' || name[3] >= ('1' + CYGNUM_REDBOOT_MAX_PARTITIONS))
280             return 0;
281         pindex = name[3] - '1';
282         if (name[4] != ':')
283             return 0;
284         *path = &name[5];
285     }
286 #ifdef CYGSEM_REDBOOT_DISK_ISO9660
287     else if (name[0] == 'c' && name[1] == 'd') {
288         // CD drives
289         kind = DISK_IDE_CDROM;
290         if (name[2] < '0' || name[2] > '9')
291             return 0;
292         index = name[2] - '0';
293         if (name[3] != ':')
294             return 0;
295         *path = &name[4];
296     }
297 #endif
298
299     if (kind) {
300         for (i = 0; i < CYGNUM_REDBOOT_MAX_DISKS; i++) {
301             if (disk_table[i].kind == kind && disk_table[i].index == index) {
302                 *part = &disk_table[i].partitions[pindex];
303                 return 1;
304             }
305         }
306     }
307     return 0;
308 }
309
310 static const struct {
311     int        kind;
312     const char *str;
313 } systype_names[] = {
314     { SYSTYPE_FAT12,      "FAT12" },
315     { SYSTYPE_FAT16_32M,  "FAT16 <32M" },
316     { SYSTYPE_FAT16,      "FAT16" },
317     { SYSTYPE_EXTENDED,   "Extended" },
318     { SYSTYPE_LINUX_SWAP, "Linux Swap" },
319     { SYSTYPE_LINUX,      "Linux" }
320 };
321
322 static const char *
323 systype_name(int systype)
324 {
325     int i;
326     
327     for (i = 0; i < sizeof(systype_names)/sizeof(systype_names[0]); i++)
328         if (systype_names[i].kind == systype)
329             return systype_names[i].str;
330     return "Unknown";
331 }
332
333 // List disk partitions
334 static void
335 do_disks(int argc, char *argv[])
336 {
337     int i, j;
338     disk_t *d;
339     partition_t *p;
340     char name[16];
341
342     for (i = 0, d = disk_table;  i < disk_count;  i++, d++) {
343         switch (d->kind) {
344           case DISK_IDE_HD:
345             for (j = 0, p = d->partitions;
346                  j < CYGNUM_REDBOOT_MAX_PARTITIONS;
347                  j++, p++) {
348                 if (p->systype) {
349                     diag_sprintf(name, "hd%c%d", 'a' + d->index, j+1);
350                     diag_printf("%-8s %s\n", name, systype_name(p->systype));
351                 }
352             }
353             break;
354           case DISK_IDE_CDROM:
355             diag_sprintf(name, "cd%d", d->index);
356             diag_printf("%-8s ISO9660\n", name);
357             break;
358         }
359     }
360 }
361
362 static void *fileptr;
363 static partition_t *file_part;
364
365 externC int 
366 disk_stream_open(connection_info_t *info, int *err)
367 {
368     const char *filepath;
369     char *filename = info->filename;
370
371     // The filename is in <disk>:<path> format.
372     // Convert to a partition and path.
373     if (!disk_parse_filename(filename, &file_part, &filepath)) {
374         *err = diskerr_badname;
375         return -1;
376     }
377
378     if (file_part->disk->kind != DISK_IDE_CDROM && file_part->systype == 0) {
379         *err = diskerr_partition;
380         return -1;
381     }
382
383     if (file_part->funs == (fs_funs_t *)0) {
384         *err = diskerr_partition;
385         return -1;
386     }
387
388     fileptr = (file_part->funs->open)(file_part, filepath);
389     if (fileptr == NULL) {
390         *err = diskerr_open;
391         return -1;
392     }
393     return 0;
394 }
395
396 externC int 
397 disk_stream_read(char *buf, int size, int *err)
398 {
399     int nread;
400
401     if ((nread = (file_part->funs->read)(fileptr, buf, size)) < 0) {
402         *err = diskerr_read;
403         return -1;
404     }
405     return nread;
406 }
407
408 externC void
409 disk_stream_close(int *err)
410 {
411     fileptr = NULL;
412 }
413
414 externC char *
415 disk_error(int err)
416 {
417     switch (err) {
418     case diskerr_badname:
419         return "Bad filename";
420         break;
421     case diskerr_partition:
422         return "Unsupported filesystem";
423         break;
424     case diskerr_open:
425         return "Can't open file";
426         break;
427     case diskerr_read:
428         return "Can't read file";
429         break;
430     default:
431         return "Unknown error";
432         break;
433     }
434 }
435
436 //
437 // RedBoot interface
438 //
439 GETC_IO_FUNCS(disk_io, disk_stream_open, disk_stream_close,
440               0, disk_stream_read, disk_error);
441 RedBoot_load(disk, disk_io, true, true, 0);