URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
Compare Revisions
- This comparison shows the changes necessary to convert path
/or1k/trunk/ecos-2.0/packages/redboot/v2_0/src/fs
- from Rev 1254 to Rev 1765
- ↔ Reverse comparison
Rev 1254 → Rev 1765
/e2fs.c
0,0 → 1,613
//========================================================================== |
// |
// e2fs.c |
// |
// RedBoot support for second extended filesystem |
// |
//========================================================================== |
//####ECOSGPLCOPYRIGHTBEGIN#### |
// ------------------------------------------- |
// This file is part of eCos, the Embedded Configurable Operating System. |
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. |
// Copyright (C) 2003 Gary Thomas <gary@mind.be> |
// |
// 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> |
#include <fs/e2fs.h> |
|
#define DEBUG_E2FS 0 |
|
#if DEBUG_E2FS > 4 |
static void dump_sb(struct e2fs_super_block *s); |
static void dump_inode(struct e2fs_inode *i); |
#endif |
|
static void *e2fs_open(partition_t *p, const char *path); |
static int e2fs_read(void *fp, char *buf, cyg_uint32 nbytes); |
|
// This structure is the only thing exported by this module. |
// These filesystem function pointers are attached to disk |
// partitions in the generic disk handling code. |
// |
fs_funs_t redboot_e2fs_funs = { |
e2fs_open, |
e2fs_read |
}; |
|
// A single block buffer to be shared carefully. |
static cyg_uint32 blockbuf[E2FS_MAX_BLOCK_SIZE/sizeof(cyg_uint32)]; |
|
#define __READ_BLOCK(n) \ |
PARTITION_READ(e2fs->part, E2FS_BLOCK_TO_SECTOR(e2fs, (n)), \ |
blockbuf, e2fs->blocksize/SECTOR_SIZE) |
|
// Get a group descriptor. Returns non-zero for success. |
// |
static int |
e2fs_get_gdesc(e2fs_desc_t *e2fs, cyg_uint32 group_nr, e2fs_group_t *gdesc) |
{ |
cyg_uint32 sec_nr; |
|
if (group_nr < e2fs->gdesc_first || |
group_nr >= (e2fs->gdesc_first + E2FS_GDESC_CACHE_SIZE)) { |
|
// cache miss |
sec_nr = E2FS_BLOCK_TO_SECTOR(e2fs, e2fs->gdesc_block); |
sec_nr += (group_nr / E2FS_GDESC_PER_SECTOR); |
|
#if DEBUG_E2FS > 2 |
diag_printf("%s: group[%d] cache miss, sec_nr[%d]\n", |
__FUNCTION__, group_nr, sec_nr); |
#endif |
if (!PARTITION_READ(e2fs->part, sec_nr, (cyg_uint32 *)e2fs->gdesc_cache, |
sizeof(e2fs->gdesc_cache)/SECTOR_SIZE)) |
return 0; |
|
e2fs->gdesc_first = (group_nr / E2FS_GDESC_CACHE_SIZE) * E2FS_GDESC_CACHE_SIZE; |
} |
*gdesc = e2fs->gdesc_cache[group_nr - e2fs->gdesc_first]; |
|
return 1; |
} |
|
// Read the requested inode from disk. Return non-zero if successful |
// |
static int |
e2fs_get_inode(e2fs_desc_t *e2fs, int ino, e2fs_inode_t *ip) |
{ |
cyg_uint32 offset, sec_nr, buf[SECTOR_SIZE/sizeof(cyg_uint32)]; |
e2fs_group_t gdesc; |
|
// get descriptor for group which this inode belongs to |
if (!e2fs_get_gdesc(e2fs, (ino - 1) / e2fs->inodes_per_group, &gdesc)) |
return 0; |
if (gdesc.inode_table == 0) |
return 0; |
|
// byte offset within group inode table |
offset = ((ino - 1) % e2fs->inodes_per_group) * sizeof(struct e2fs_inode); |
|
// figure out which sector holds the inode |
sec_nr = E2FS_BLOCK_TO_SECTOR(e2fs, SWAB_LE32(gdesc.inode_table)); |
sec_nr += offset / SECTOR_SIZE; |
|
// and the offset within that sector. |
offset %= SECTOR_SIZE; |
|
#if DEBUG_E2FS > 0x08 |
diag_printf("%s: ino[%d], sec_nr[%d] offset[%d]\n", __FUNCTION__, |
ino, sec_nr, offset); |
#endif |
|
if (!PARTITION_READ(e2fs->part, sec_nr, buf, 1)) |
return 0; |
|
*ip = *(e2fs_inode_t *)((char *)buf + offset); |
|
#if DEBUG_E2FS > 0 |
diag_printf("%s: inode size[%d]\n", __FUNCTION__, SWAB_LE32(ip->size)); |
#endif |
|
return 1; |
} |
|
// Mount an e2fs filesystem on the given partition. |
// Return 0 if successful. |
// |
static int |
e2fs_mount(partition_t *part, e2fs_desc_t *e2fs) |
{ |
int sb_block = 1; |
cyg_uint32 sb_buf[E2FS_MIN_BLOCK_SIZE/sizeof(cyg_uint32)]; |
struct e2fs_super_block *sb = (struct e2fs_super_block *)sb_buf; |
|
e2fs->part = part; |
|
if (!PARTITION_READ(part, sb_block*(E2FS_MIN_BLOCK_SIZE/SECTOR_SIZE), |
(cyg_uint32 *)sb, E2FS_MIN_BLOCK_SIZE/SECTOR_SIZE)) |
return -1; |
|
if (SWAB_LE16(sb->magic) != E2FS_SUPER_MAGIC) { |
diag_printf("ext2_mount: bad magic 0x%x\n", SWAB_LE16(sb->magic)); |
return -1; |
} |
|
// save some stuff for easy access |
e2fs->blocksize = E2FS_BLOCK_SIZE(sb); |
e2fs->nr_ind_blocks = (e2fs)->blocksize / sizeof(cyg_uint32); |
e2fs->nr_dind_blocks = e2fs->nr_ind_blocks * ((e2fs)->blocksize / sizeof(cyg_uint32)); |
e2fs->nr_tind_blocks = e2fs->nr_dind_blocks * ((e2fs)->blocksize / sizeof(cyg_uint32)); |
e2fs->blocks_per_group = SWAB_LE32(sb->blocks_per_group); |
e2fs->ngroups = (SWAB_LE32(sb->blocks_count) + e2fs->blocks_per_group - 1) / |
e2fs->blocks_per_group; |
e2fs->inodes_per_group = SWAB_LE32(sb->inodes_per_group); |
|
// Find the group descriptors which follow superblock |
e2fs->gdesc_block = ((sb_block * E2FS_MIN_BLOCK_SIZE) / e2fs->blocksize) + 1; |
e2fs->gdesc_first = 0; // cache group 0 initially |
|
if (!PARTITION_READ(part, E2FS_BLOCK_TO_SECTOR(e2fs,e2fs->gdesc_block), |
(cyg_uint32 *)e2fs->gdesc_cache, 1)) |
return -1; |
|
#if DEBUG_E2FS > 1 |
diag_printf("E2FS superblock:\n"); |
diag_printf(" [%d] inodes\n", SWAB_LE32(sb->inodes_count)); |
diag_printf(" [%d] blocks\n", SWAB_LE32(sb->blocks_count)); |
diag_printf(" [%d] blocksize\n", e2fs->blocksize); |
diag_printf(" [%d] blocks per group\n", e2fs->blocks_per_group); |
diag_printf(" [%d] ngroups\n", e2fs->ngroups); |
#endif |
|
#if DEBUG_E2FS > 4 |
dump_sb(sb); |
#endif |
|
return 0; |
} |
|
// Convert a block index into inode data into a block_nr. |
// If successful, store block number in pblknr and return non-zero. |
// |
// NB: This needs some block/sector caching to be speedier. But |
// that takes memory and speed is not too bad now for files |
// small enough to avoid double and triple indirection. |
// |
static int |
e2fs_inode_block(e2fs_desc_t *e2fs, e2fs_inode_t *inode, |
cyg_uint32 bindex, cyg_uint32 *pblknr) |
{ |
if (bindex < E2FS_NR_DIR_BLOCKS) { |
*pblknr = SWAB_LE32(inode->block[bindex]); |
return 1; |
} |
bindex -= E2FS_NR_DIR_BLOCKS; |
|
if (bindex < e2fs->nr_ind_blocks) { |
// Indirect block |
if (!__READ_BLOCK(SWAB_LE32(inode->block[E2FS_IND_BLOCK]))) |
return 0; |
*pblknr = SWAB_LE32(blockbuf[bindex]); |
return 1; |
} |
bindex -= e2fs->nr_ind_blocks; |
|
if (bindex < e2fs->nr_dind_blocks) { |
// Double indirect block |
if (!__READ_BLOCK(SWAB_LE32(inode->block[E2FS_DIND_BLOCK]))) |
return 0; |
if (!__READ_BLOCK(SWAB_LE32(blockbuf[bindex / e2fs->nr_ind_blocks]))) |
return 0; |
*pblknr = SWAB_LE32(blockbuf[bindex % e2fs->nr_ind_blocks]); |
return 1; |
} |
bindex -= e2fs->nr_dind_blocks; |
|
// Triple indirect block |
if (!__READ_BLOCK(SWAB_LE32(inode->block[E2FS_TIND_BLOCK]))) |
return 0; |
if (!__READ_BLOCK(SWAB_LE32(blockbuf[bindex / e2fs->nr_dind_blocks]))) |
return 0; |
bindex %= e2fs->nr_dind_blocks; |
if (!__READ_BLOCK(SWAB_LE32(blockbuf[bindex / e2fs->nr_ind_blocks]))) |
return 0; |
*pblknr = SWAB_LE32(blockbuf[bindex % e2fs->nr_ind_blocks]); |
return 1; |
} |
|
|
// search a single directory block in memory looking for an |
// entry with the given name. Return pointer to entry if |
// found, NULL if not. |
// |
static e2fs_dir_entry_t * |
search_dir_block(e2fs_desc_t *e2fs, cyg_uint32 *blkbuf, |
const char *name, int namelen) |
{ |
e2fs_dir_entry_t *dir; |
cyg_uint16 reclen, len; |
cyg_uint32 offset; |
|
#if DEBUG_E2FS > 0 |
diag_dump_buf(blkbuf, e2fs->blocksize); |
#endif |
offset = 0; |
while (offset < e2fs->blocksize) { |
dir = (e2fs_dir_entry_t *)((char *)blkbuf + offset); |
reclen = SWAB_LE16(dir->reclen); |
offset += reclen; |
len = dir->namelen; |
|
// terminate on anything which doesn't make sense |
if (reclen < 8 || (len + 8) > reclen || offset > (e2fs->blocksize + 1)) |
return NULL; |
|
if (dir->inode && len == namelen && !strncmp(dir->name, name, len)) |
return dir; |
} |
return NULL; |
} |
|
|
// Look in the given directory for an entry with the given name. |
// If found, return a pointer to that entry. Return NULL if not |
// found. |
// |
static e2fs_dir_entry_t * |
e2fs_dir_lookup(e2fs_desc_t *e2fs, cyg_uint32 dir_ino, |
const char *name, int namelen) |
{ |
e2fs_inode_t inode; |
e2fs_dir_entry_t *dir; |
cyg_uint32 nblocks, last_block_size, i, block_nr, nbytes; |
|
#if DEBUG_E2FS > 0 |
diag_printf("%s: looking for %s [%d] in ino[%d]\n", |
__FUNCTION__, name, namelen, dir_ino); |
#endif |
|
if (!e2fs_get_inode(e2fs, dir_ino, &inode)) { |
#if DEBUG_E2FS > 0 |
diag_printf("%s: e2fs_get_inode [%d] failed\n", __FUNCTION__, dir_ino); |
#endif |
return NULL; |
} |
|
nbytes = SWAB_LE32(inode.size); |
nblocks = (nbytes + e2fs->blocksize - 1) / e2fs->blocksize; |
|
last_block_size = nbytes % e2fs->blocksize; |
if (last_block_size == 0) |
last_block_size = e2fs->blocksize; |
|
for (i = 0; i < nblocks; i++) { |
if (!e2fs_inode_block(e2fs, &inode, i, &block_nr)) |
return NULL; |
|
if (block_nr) { |
if (!__READ_BLOCK(block_nr)) |
return NULL; |
} else |
memset(blockbuf, 0, e2fs->blocksize); |
|
dir = search_dir_block(e2fs, blockbuf, name, namelen); |
|
if (dir != NULL) |
return dir; |
} |
return NULL; |
} |
|
typedef struct ino_info { |
cyg_uint32 ino; |
cyg_uint32 parent_ino; |
cyg_uint8 filetype; |
} ino_info_t; |
|
static int e2fs_inode_lookup(e2fs_desc_t *e2fs, cyg_uint32 dir_ino, |
const char *pathname, ino_info_t *info); |
|
// Starting from the given directory, find the inode number, filetype, and |
// parent inode for the file pointed to by the given symbolic link inode. |
// If successful, fills out ino_info_t and return true. |
// |
static int |
e2fs_follow_symlink(e2fs_desc_t *e2fs, cyg_uint32 dir_ino, cyg_uint32 sym_ino, ino_info_t *info) |
{ |
#define MAX_SYMLINK_NAME 255 |
char symlink[MAX_SYMLINK_NAME+1]; |
int pathlen; |
cyg_uint32 block_nr; |
e2fs_inode_t inode; |
|
if (!e2fs_get_inode(e2fs, sym_ino, &inode)) { |
#if DEBUG_E2FS > 0 |
diag_printf("%s: e2fs_get_inode [%d] failed\n", __FUNCTION__, sym_ino); |
#endif |
return 0; |
} |
|
pathlen = SWAB_LE32(inode.size); |
if (pathlen > MAX_SYMLINK_NAME) |
return 0; |
|
if (inode.blocks) { |
if (!e2fs_inode_block(e2fs, &inode, 0, &block_nr)) |
return 0; |
if (block_nr) { |
if (!PARTITION_READ(e2fs->part, E2FS_BLOCK_TO_SECTOR(e2fs, block_nr), |
blockbuf, e2fs->blocksize/SECTOR_SIZE)) |
return 0; |
memcpy(symlink, blockbuf, pathlen); |
} else |
return 0; |
} else { |
// small enough path to fit in inode struct |
memcpy(symlink, (char *)&inode.block[0], pathlen); |
} |
symlink[pathlen] = 0; |
|
return e2fs_inode_lookup(e2fs, dir_ino, symlink, info); |
} |
|
|
// Starting from the given directory, find the inode number, filetype, and |
// parent inode for the given file pathname. |
// If successful, fills out ino_info_t and return true. |
// |
static int |
e2fs_inode_lookup(e2fs_desc_t *e2fs, cyg_uint32 dir_ino, const char *pathname, ino_info_t *info) |
{ |
int len, pathlen; |
const char *p; |
e2fs_dir_entry_t *dir = NULL; |
|
if (!pathname || (pathlen = strlen(pathname)) == 0) |
return 0; |
|
if (*pathname == '/') { |
if (--pathlen == 0) { |
info->ino = info->parent_ino = E2FS_ROOT_INO; |
info->filetype = E2FS_FTYPE_DIR; |
return 1; |
} |
++pathname; |
dir_ino = E2FS_ROOT_INO; |
} |
|
while (pathlen) { |
// find next delimiter in path. |
for (p = pathname, len = 0; len < pathlen; len++, p++) { |
// skip delimiter if found. |
if (*p == '/') { |
++p; |
--pathlen; |
break; |
} |
} |
dir = e2fs_dir_lookup(e2fs, dir_ino, pathname, len); |
if (dir == NULL) |
return 0; |
|
pathlen -= len; |
pathname = p; |
|
switch (dir->filetype) { |
case E2FS_FTYPE_SYMLINK: |
// follow the symbolic link (this will cause recursion) |
if (!e2fs_follow_symlink(e2fs, dir_ino, SWAB_LE32(dir->inode), info)) |
return 0; |
if (pathlen == 0) |
return 1; |
// must be a dir if we want to continue |
if (info->filetype != E2FS_FTYPE_DIR) |
return 0; |
dir_ino = info->ino; |
break; |
|
case E2FS_FTYPE_DIR: |
if (pathlen) |
dir_ino = SWAB_LE32(dir->inode); |
break; |
|
case E2FS_FTYPE_REG_FILE: |
if (pathlen) |
return 0; // regular file embedded in middle of path |
break; |
|
case E2FS_FTYPE_UNKNOWN: |
case E2FS_FTYPE_CHRDEV: |
case E2FS_FTYPE_BLKDEV: |
case E2FS_FTYPE_FIFO: |
case E2FS_FTYPE_SOCK: |
default: |
return 0; |
} |
} |
info->ino = SWAB_LE32(dir->inode); |
info->parent_ino = dir_ino; |
info->filetype = dir->filetype; |
return 1; |
} |
|
struct read_info { |
e2fs_desc_t e2fs_desc; |
e2fs_inode_t inode; |
cyg_uint32 fsize; |
cyg_uint32 fpos; |
}; |
|
static void * |
e2fs_open(partition_t *p, const char *filepath) |
{ |
static struct read_info rinfo; |
ino_info_t ino_info; |
|
// mount partition |
if (e2fs_mount(p, &rinfo.e2fs_desc) != 0) { |
diag_printf("mount failed.\n"); |
return NULL; |
} |
|
// find file inode |
if (!e2fs_inode_lookup(&rinfo.e2fs_desc, E2FS_ROOT_INO, filepath, &ino_info)) { |
diag_printf("%s: e2fs_inode_lookup failed\n", __FUNCTION__); |
return NULL; |
} |
|
// read inode |
if (!e2fs_get_inode(&rinfo.e2fs_desc, ino_info.ino, &rinfo.inode)) { |
diag_printf("%s: e2fs_get_inode failed for ino[%d]\n", __FUNCTION__, ino_info.ino); |
return NULL; |
} |
|
rinfo.fsize = SWAB_LE32(rinfo.inode.size); |
rinfo.fpos = 0; |
|
return &rinfo; |
} |
|
static int |
e2fs_read(void *fp, char *buf, cyg_uint32 nbytes) |
{ |
struct read_info *info = fp; |
e2fs_desc_t *e2fs; |
cyg_uint32 nread = 0, rem, block_nr, bindex, to_read; |
|
if ((info->fpos + nbytes) > info->fsize) |
nbytes = info->fsize - info->fpos; |
|
e2fs = &info->e2fs_desc; |
|
// see if we need to copy leftover data from last read call |
rem = e2fs->blocksize - (info->fpos % e2fs->blocksize); |
if (rem != e2fs->blocksize) { |
char *p = (char *)blockbuf + e2fs->blocksize - rem; |
|
if (rem > nbytes) |
rem = nbytes; |
|
memcpy(buf, p, rem); |
|
nread += rem; |
buf += rem; |
info->fpos += rem; |
} |
|
// now loop through blocks if we're not done |
bindex = info->fpos / e2fs->blocksize; |
while (nread < nbytes) { |
if (!e2fs_inode_block(e2fs, &info->inode, bindex, &block_nr)) |
return -1; |
|
if (block_nr) { |
if (!PARTITION_READ(e2fs->part, E2FS_BLOCK_TO_SECTOR(e2fs, block_nr), |
blockbuf, e2fs->blocksize/SECTOR_SIZE)) |
return 0; |
} else |
memset(blockbuf, 0, e2fs->blocksize); |
|
to_read = nbytes - nread; |
if (to_read > e2fs->blocksize) |
to_read = e2fs->blocksize; |
|
memcpy(buf, blockbuf, to_read); |
|
nread += to_read; |
buf += to_read; |
info->fpos += to_read; |
++bindex; |
} |
|
return nread; |
} |
|
#if DEBUG_E2FS > 4 |
static void dump_sb(struct e2fs_super_block *s) |
{ |
diag_printf("inode_count: %d\n", SWAB_LE32(s->inodes_count)); |
diag_printf("blocks_count: %d\n", SWAB_LE32(s->blocks_count)); |
diag_printf("r_blocks_count: %d\n", SWAB_LE32(s->r_blocks_count)); |
diag_printf("free_blocks_count: %d\n", SWAB_LE32(s->free_blocks_count)); |
diag_printf("free_inodes_count: %d\n", SWAB_LE32(s->free_inodes_count)); |
diag_printf("first_data_block: %d\n", SWAB_LE32(s->first_data_block)); |
diag_printf("log_block_size: %d\n", SWAB_LE32(s->log_block_size)); |
diag_printf("log_frag_size: %d\n", SWAB_LE32(s->log_frag_size)); |
diag_printf("blocks_per_group: %d\n", SWAB_LE32(s->blocks_per_group)); |
diag_printf("frags_per_group: %d\n", SWAB_LE32(s->frags_per_group)); |
diag_printf("inodes_per_group: %d\n", SWAB_LE32(s->inodes_per_group)); |
diag_printf("mnt_count: %d\n", SWAB_LE16(s->mnt_count)); |
diag_printf("max_mnt_count: %d\n", SWAB_LE16(s->max_mnt_count)); |
diag_printf("magic: %d\n", SWAB_LE16(s->magic)); |
diag_printf("state: %d\n", SWAB_LE16(s->state)); |
diag_printf("errors: %d\n", SWAB_LE16(s->errors)); |
diag_printf("minor_rev_level: %d\n", SWAB_LE16(s->minor_rev_level)); |
diag_printf("lastcheck: %d\n", SWAB_LE32(s->lastcheck)); |
diag_printf("checkinterval: %d\n", SWAB_LE32(s->checkinterval)); |
diag_printf("creator_os: %d\n", SWAB_LE32(s->creator_os)); |
diag_printf("rev_level: %d\n", SWAB_LE32(s->rev_level)); |
} |
|
static void dump_inode(struct e2fs_inode *i) |
{ |
int j, n; |
|
diag_printf("mode: %o\n", SWAB_LE16(i->mode)); |
diag_printf("uid: %o\n", SWAB_LE16(i->uid)); |
diag_printf("size: %d\n", SWAB_LE32(i->size)); |
diag_printf("gid: %o\n", SWAB_LE16(i->gid)); |
diag_printf("links: %d\n", SWAB_LE16(i->links_count)); |
diag_printf("blocks: %d\n", SWAB_LE32(i->blocks)); |
|
n = i->blocks; |
if (n > E2FS_N_BLOCKS) |
n = E2FS_N_BLOCKS; |
|
for (j = 0; j < n; j++) |
diag_printf(" block: %d\n", SWAB_LE32(i->block[j])); |
} |
#endif |
|
|
/ide.c
0,0 → 1,490
//========================================================================== |
// |
// ide.c |
// |
// RedBoot IDE support |
// |
//========================================================================== |
//####ECOSGPLCOPYRIGHTBEGIN#### |
// ------------------------------------------- |
// This file is part of eCos, the Embedded Configurable Operating System. |
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. |
// Copyright (C) 2003 Gary Thomas <gary@mind.be> |
// |
// 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 <cyg/hal/hal_io.h> |
#include <fs/disk.h> |
#include <fs/ide.h> |
|
static int ide_read(struct disk *d, |
cyg_uint32 start_sector, |
cyg_uint32 *buf, |
cyg_uint8 nr_sectors); |
|
static disk_funs_t ide_funs = { ide_read }; |
|
static struct ide_priv ide_privs[HAL_IDE_NUM_CONTROLLERS * 2]; |
|
static inline void |
__wait_for_ready(int ctlr) |
{ |
cyg_uint8 status; |
do { |
HAL_IDE_READ_UINT8(ctlr, IDE_REG_STATUS, status); |
} while (status & (IDE_STAT_BSY | IDE_STAT_DRQ)); |
} |
|
static inline int |
__wait_for_drq(int ctlr) |
{ |
cyg_uint8 status; |
|
CYGACC_CALL_IF_DELAY_US(10); |
do { |
HAL_IDE_READ_UINT8(ctlr, IDE_REG_STATUS, status); |
if (status & IDE_STAT_DRQ) |
return 1; |
} while (status & IDE_STAT_BSY); |
|
return 0; |
} |
|
static int |
ide_reset(int ctlr) |
{ |
cyg_uint8 status; |
int delay; |
|
HAL_IDE_WRITE_CONTROL(ctlr, 6); // polled mode, reset asserted |
CYGACC_CALL_IF_DELAY_US(5000); |
HAL_IDE_WRITE_CONTROL(ctlr, 2); // polled mode, reset cleared |
CYGACC_CALL_IF_DELAY_US((cyg_uint32)50000); |
|
// wait 30 seconds max for not busy |
for (delay = 0; delay < 300; ++delay) { |
CYGACC_CALL_IF_DELAY_US((cyg_uint32)100000); |
HAL_IDE_READ_UINT8(ctlr, IDE_REG_STATUS, status); |
// bail out early on bogus status |
if ((status & (IDE_STAT_BSY|IDE_STAT_DRDY)) == (IDE_STAT_BSY|IDE_STAT_DRDY)) |
break; |
if (!(status & IDE_STAT_BSY)) |
return 1; |
} |
return 0; |
} |
|
static int |
ide_ident(int ctlr, int dev, int is_packet_dev, cyg_uint16 *buf) |
{ |
int i; |
|
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_DEVICE, dev << 4); |
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_COMMAND, is_packet_dev ? 0xA1 : 0xEC); |
CYGACC_CALL_IF_DELAY_US((cyg_uint32)50000); |
|
if (!__wait_for_drq(ctlr)) |
return 0; |
|
for (i = 0; i < (SECTOR_SIZE / sizeof(cyg_uint16)); i++, buf++) |
HAL_IDE_READ_UINT16(ctlr, IDE_REG_DATA, *buf); |
|
return 1; |
} |
|
static int |
ide_read_sectors(int ctlr, int dev, cyg_uint32 start, cyg_uint8 count, cyg_uint16 *buf) |
{ |
int i, j; |
cyg_uint16 *p; |
|
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_COUNT, count); |
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_LBALOW, start & 0xff); |
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_LBAMID, (start >> 8) & 0xff); |
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_LBAHI, (start >> 16) & 0xff); |
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_DEVICE, |
((start >> 24) & 0xf) | (dev << 4) | 0x40); |
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_COMMAND, 0x20); |
|
for(p = buf, i = 0; i < count; i++) { |
|
if (!__wait_for_drq(ctlr)) { |
diag_printf("%s: NO DRQ for ide%d, device %d.\n", |
__FUNCTION__, ctlr, dev); |
return 0; |
} |
|
for (j = 0; j < (SECTOR_SIZE / sizeof(cyg_uint16)); j++, p++) |
HAL_IDE_READ_UINT16(ctlr, IDE_REG_DATA, *p); |
} |
return 1; |
} |
|
// max number of sectors to xfer during a single packet command |
#define MAX_CD_XFER 16 |
|
static inline int |
send_packet_command(int ctlr, int dev, cyg_uint16 len, cyg_uint16 *pkt, int pktlen) |
{ |
int i; |
cyg_uint8 status, reason; |
|
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_FEATURES, 0); |
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_COUNT, 0); |
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_LBALOW, 0); |
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_LBAMID, len & 0xff); |
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_LBAHI, (len >> 8) & 0xff); |
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_DEVICE, dev << 4); |
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_COMMAND, 0xA0); |
|
if (!__wait_for_drq(ctlr)) { |
diag_printf("%s: NO DRQ for ide%d, device %d.\n", |
__FUNCTION__, ctlr, dev); |
return 0; |
} |
|
// send packet |
for (i = 0; i < (pktlen/sizeof(cyg_uint16)); i++) |
HAL_IDE_WRITE_UINT16(ctlr, IDE_REG_DATA, pkt[i]); |
|
// wait for not busy transferring packet |
do { |
HAL_IDE_READ_UINT8(ctlr, IDE_REG_STATUS, status); |
HAL_IDE_READ_UINT8(ctlr, IDE_REG_REASON, reason); |
|
if ((status & (IDE_STAT_BSY | IDE_STAT_DRQ)) == IDE_STAT_DRQ) |
if (reason & IDE_REASON_COD) |
continue; // still wanting packet data (should timeout here) |
|
} while (status & IDE_STAT_BSY); |
|
return 1; |
} |
|
#define READ_COUNT(x) \ |
{ unsigned char tmp; \ |
HAL_IDE_READ_UINT8(ctlr, IDE_REG_LBAMID, tmp); \ |
(x) = tmp; \ |
HAL_IDE_READ_UINT8(ctlr, IDE_REG_LBAHI, tmp); \ |
(x) = cdcount |= (tmp << 8); \ |
} |
|
|
// Read the sense data |
static int |
request_sense(int ctlr, int dev, cyg_uint16 count, cyg_uint16 *buf) |
{ |
int i; |
cyg_uint16 cdcount, pkt[6]; |
unsigned char status, *cpkt = (unsigned char *)pkt; |
|
|
// Fill in REQUEST SENSE packet command block |
memset(cpkt, 0, sizeof(pkt)); |
cpkt[0] = 0x03; |
cpkt[4] = 254; // allocation length |
|
if (!send_packet_command(ctlr, dev, count, pkt, sizeof(pkt))) |
return 0; |
|
HAL_IDE_READ_UINT8(ctlr, IDE_REG_STATUS, status); |
if (!(status & IDE_STAT_DRQ)) { |
if (status & IDE_STAT_SERVICE) { |
unsigned char reason; |
HAL_IDE_READ_UINT8(ctlr, IDE_REG_REASON, reason); |
diag_printf("%s: SERVICE request for ide%d, device %d, status[%02x], reason[%02x].\n", |
__FUNCTION__, ctlr, dev, status, reason); |
} |
return 0; |
} |
|
READ_COUNT(cdcount); |
if (cdcount != count) |
diag_printf("%s: ide%d, dev%d: his cnt[%d] our count[%d].\n", |
__FUNCTION__, ctlr, dev, cdcount, count); |
|
for(i = 0; i < (cdcount / sizeof(*buf)); i++, buf++) |
HAL_IDE_READ_UINT16(ctlr, IDE_REG_DATA, *buf); |
|
// wait for not busy transferring data |
do { |
HAL_IDE_READ_UINT8(ctlr, IDE_REG_STATUS, status); |
} while ((status & (IDE_STAT_BSY | IDE_STAT_DRQ)) == IDE_STAT_DRQ); |
|
return cdcount; |
} |
|
// Interpret the sense data |
static int |
handle_sense(int ctlr, int dev, cyg_uint8 count, cyg_uint16 *buf) |
{ |
#if 0 |
unsigned char *p = (char *)buf; |
|
diag_printf("%s: %d bytes:\n", __FUNCTION__, count); |
diag_printf("sense key[%02x] additional sense[%02x]\n", |
p[2], p[12]); |
#endif |
return 1; |
} |
|
static int |
do_packet_read(int ctlr, int dev, cyg_uint32 start, cyg_uint8 count, cyg_uint16 *buf) |
{ |
int i, retry_cnt; |
cyg_uint16 cdcount, pkt[6], sense[127]; |
unsigned char status, *cpkt = (unsigned char *)pkt; |
|
// get count number of whole cdrom sectors |
while (count) { |
|
retry_cnt = 3; |
|
i = (count > MAX_CD_XFER) ? MAX_CD_XFER : count; |
|
retry: |
// Fill in READ(10) packet command block |
memset(cpkt, 0, sizeof(pkt)); |
cpkt[0] = 0x28; // READ(10) |
cpkt[2] = (start >> 24) & 0xff; |
cpkt[3] = (start >> 16) & 0xff; |
cpkt[4] = (start >> 8) & 0xff; |
cpkt[5] = (start >> 0) & 0xff; |
cpkt[7] = (i >> 8) & 0xff; |
cpkt[8] = i & 0xff; |
|
if (!send_packet_command(ctlr, dev, i * CDROM_SECTOR_SIZE, |
pkt, sizeof(pkt))) |
return 0; |
|
HAL_IDE_READ_UINT8(ctlr, IDE_REG_STATUS, status); |
if (!(status & IDE_STAT_DRQ)) { |
if (status & IDE_STAT_SERVICE) { |
unsigned char reason; |
int sense_count; |
HAL_IDE_READ_UINT8(ctlr, IDE_REG_REASON, reason); |
#if 1 |
diag_printf("%s: SERVICE request for ide%d, device %d, status[%02x], reason[%02x].\n", |
__FUNCTION__, ctlr, dev, status, reason); |
#endif |
sense_count = request_sense(ctlr, dev, sizeof(sense), sense); |
if (sense_count) { |
handle_sense(ctlr, dev, sense_count, sense); |
if (retry_cnt--) |
goto retry; |
} |
} |
return 0; |
} |
|
count -= i; |
start += i; |
|
READ_COUNT(cdcount); |
if (cdcount != (i * CDROM_SECTOR_SIZE)) |
diag_printf("%s: ide%d, dev%d: his cnt[%d] our count[%d].\n", |
__FUNCTION__, ctlr, dev, |
cdcount, i * CDROM_SECTOR_SIZE); |
|
for(i = 0; i < (cdcount / sizeof(*buf)); i++, buf++) |
HAL_IDE_READ_UINT16(ctlr, IDE_REG_DATA, *buf); |
|
// wait for not busy transferring data |
do { |
HAL_IDE_READ_UINT8(ctlr, IDE_REG_STATUS, status); |
} while ((status & (IDE_STAT_BSY | IDE_STAT_DRQ)) == IDE_STAT_DRQ); |
} |
return 1; |
} |
|
|
static int |
ide_packet_read_sectors(int ctlr, int dev, cyg_uint32 start, cyg_uint8 count, cyg_uint16 *buf) |
{ |
int i, extra; |
cyg_uint32 cdstart; |
static cyg_uint16 cdsec_buf[CDROM_SECTOR_SIZE/sizeof(cyg_uint16)]; |
|
cdstart = (start + SECTORS_PER_CDROM_SECTOR-1) / SECTORS_PER_CDROM_SECTOR; |
|
// align to cdrom sector boundary. |
if (start % SECTORS_PER_CDROM_SECTOR) { |
if (!ide_packet_read_sectors(ctlr, dev, |
cdstart * SECTORS_PER_CDROM_SECTOR, |
SECTORS_PER_CDROM_SECTOR, cdsec_buf)) |
return 0; |
|
i = SECTORS_PER_CDROM_SECTOR - (start % SECTORS_PER_CDROM_SECTOR); |
if (i > count) |
i = count; |
memcpy(buf, cdsec_buf + ((start % CDROM_SECTOR_SIZE) * SECTOR_SIZE), |
i * SECTOR_SIZE); |
|
count -= i; |
buf += (i * SECTOR_SIZE) / sizeof(*buf); |
++cdstart; |
} |
|
extra = count % SECTORS_PER_CDROM_SECTOR; |
count /= SECTORS_PER_CDROM_SECTOR; |
|
if (count) { |
if (!do_packet_read(ctlr, dev, cdstart, count, buf)) |
return 0; |
buf += count * SECTORS_PER_CDROM_SECTOR * SECTOR_SIZE; |
} |
|
if (extra) { |
// read cdrom sector |
if (!ide_packet_read_sectors(ctlr, dev, |
cdstart * SECTORS_PER_CDROM_SECTOR, |
extra, cdsec_buf)) |
return 0; |
memcpy(buf, cdsec_buf, extra * SECTOR_SIZE); |
} |
|
return 1; |
} |
|
static int |
ide_read(struct disk *d, |
cyg_uint32 start_sec, cyg_uint32 *buf, cyg_uint8 nr_secs) |
{ |
struct ide_priv *p = (struct ide_priv *)(d->private); |
|
if (p->flags & IDE_DEV_PACKET) |
return ide_packet_read_sectors(p->controller, p->drive, |
start_sec, nr_secs, (cyg_uint16 *)buf); |
|
return ide_read_sectors(p->controller, p->drive, |
start_sec, nr_secs, (cyg_uint16 *)buf); |
} |
|
|
static void |
ide_init(void) |
{ |
cyg_uint32 buf[SECTOR_SIZE/sizeof(cyg_uint32)], u32; |
cyg_uint16 u16; |
cyg_uint8 u8; |
int i, j; |
disk_t disk; |
struct ide_priv *priv; |
|
#define DEV_INIT_VAL ((j << 4) | 0xA0) |
|
HAL_IDE_INIT(); |
|
CYGACC_CALL_IF_DELAY_US(5); |
|
priv = ide_privs; |
for (i = 0; i < HAL_IDE_NUM_CONTROLLERS; i++) { |
|
// soft reset the devices on this controller |
if (!ide_reset(i)) |
continue; |
|
// 2 devices per controller |
for (j = 0; j < 2; j++, priv++) { |
|
priv->controller = i; |
priv->drive = j; |
priv->flags = 0; |
|
// This is reminiscent of a memory test. We write a value |
// to a certain location (device register), then write a |
// different value somewhere else so that the first value |
// is not hanging on the bus, then we read back the first |
// value to see if the write was succesful. |
// |
HAL_IDE_WRITE_UINT8(i, IDE_REG_DEVICE, DEV_INIT_VAL); |
HAL_IDE_WRITE_UINT8(i, IDE_REG_FEATURES, 0); |
CYGACC_CALL_IF_DELAY_US(50000); |
HAL_IDE_READ_UINT8(i, IDE_REG_DEVICE, u8); |
if (u8 != DEV_INIT_VAL) { |
diag_printf("IDE failed to identify unit %d - wrote: %x, read: %x\n", |
i, DEV_INIT_VAL, u8); |
continue; |
} |
|
// device present |
priv->flags |= IDE_DEV_PRESENT; |
|
if (ide_ident(i, j, 0, (cyg_uint16 *)buf) <= 0) { |
if (ide_ident(i, j, 1, (cyg_uint16 *)buf) <= 0) { |
priv->flags = 0; |
continue; // can't identify device |
} else { |
u16 = *(cyg_uint16 *)((char *)buf + IDE_DEVID_GENCONFIG); |
if (((u16 >> 8) & 0x1f) != 5) { |
diag_printf("Non-CDROM ATAPI device #%d - skipped\n", i); |
continue; |
} |
priv->flags |= IDE_DEV_PACKET; |
} |
} |
|
memset(&disk, 0, sizeof(disk)); |
disk.funs = &ide_funs; |
disk.private = priv; |
|
disk.kind = DISK_IDE_HD; // until proven otherwise |
|
if (priv->flags & IDE_DEV_PACKET) { |
u16 = *(cyg_uint16 *)((char *)buf + IDE_DEVID_GENCONFIG); |
if (((u16 >> 8) & 0x1f) == 5) |
disk.kind = DISK_IDE_CDROM; |
} else { |
u32 = *(cyg_uint32 *)((char *)buf + IDE_DEVID_LBA_CAPACITY); |
disk.nr_sectors = u32; |
} |
|
if (!disk_register(&disk)) |
return; |
} |
} |
} |
|
RedBoot_init(ide_init, RedBoot_INIT_FIRST); |
|
/disk.c
0,0 → 1,435
//========================================================================== |
// |
// disk.c |
// |
// RedBoot disk support |
// |
//========================================================================== |
//####ECOSGPLCOPYRIGHTBEGIN#### |
// ------------------------------------------- |
// This file is part of eCos, the Embedded Configurable Operating System. |
// Copyright (C) 1998, 1999, 2000, 2001, 2002 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 int |
find_dos_partitions(disk_t *d, cyg_uint8 *mbr) |
{ |
cyg_uint32 s, n, tmp; |
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++) { |
|
// Have to use memcpy because of alignment |
memcpy(&tmp, p->start_sect, 4); |
s = SWAB_LE32(tmp); |
memcpy(&tmp, p->nr_sects, 4); |
n = SWAB_LE32(tmp); |
|
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); |
|
// Have to use memcpy because of alignment |
memcpy(&tmp, p->start_sect, 4); |
s = SWAB_LE32(tmp); |
memcpy(&tmp, p->nr_sects, 4); |
n = SWAB_LE32(tmp); |
|
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; |
|
memcpy(&tmp, p->start_sect, 4); |
s = SWAB_LE32(tmp); |
memcpy(&tmp, p->nr_sects, 4); |
n = SWAB_LE32(tmp); |
|
// 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); |