diff options
Diffstat (limited to 'roms/openbios/packages/pc-parts.c')
-rw-r--r-- | roms/openbios/packages/pc-parts.c | 403 |
1 files changed, 403 insertions, 0 deletions
diff --git a/roms/openbios/packages/pc-parts.c b/roms/openbios/packages/pc-parts.c new file mode 100644 index 000000000..dbbb2d44b --- /dev/null +++ b/roms/openbios/packages/pc-parts.c @@ -0,0 +1,403 @@ +/* + * pc partition support + * + * Copyright (C) 2004 Stefan Reinauer + * + * This code is based (and copied in many places) from + * mac partition support by Samuel Rydh (samuel@ibrium.se) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 + * + */ + +#include "config.h" +#include "libopenbios/bindings.h" +#include "libopenbios/load.h" +#include "libc/byteorder.h" +#include "libc/vsprintf.h" +#include "packages.h" + +//#define DEBUG_PC_PARTS + +#ifdef DEBUG_PC_PARTS +#define DPRINTF(fmt, args...) \ + do { printk(fmt , ##args); } while (0) +#else +#define DPRINTF(fmt, args...) +#endif + +typedef struct { + xt_t seek_xt, read_xt; + ucell offs_hi, offs_lo; + ucell size_hi, size_lo; + phandle_t filesystem_ph; +} pcparts_info_t; + +DECLARE_NODE( pcparts, INSTALL_OPEN, sizeof(pcparts_info_t), "+/packages/pc-parts" ); + +#define SEEK( pos ) ({ DPUSH(pos); call_parent(di->seek_xt); POP(); }) +#define READ( buf, size ) ({ PUSH(pointer2cell(buf)); PUSH(size); call_parent(di->read_xt); POP(); }) + +/* three helper functions */ + +static inline int has_pc_valid_partition(unsigned char *sect) +{ + /* Make sure the partition table contains at least one valid entry */ + return (sect[0x1c2] != 0 || sect[0x1d2] != 0 || sect[0x1e2] != 0); +} + +static inline int has_pc_part_magic(unsigned char *sect) +{ + return sect[0x1fe]==0x55 && sect[0x1ff]==0xAA; +} + +static inline int is_pc_extended_part(unsigned char type) +{ + return type==5 || type==0xf || type==0x85; +} + +/* ( open -- flag ) */ +static void +pcparts_open( pcparts_info_t *di ) +{ + char *str = my_args_copy(); + char *argstr = strdup(""); + char *parstr = strdup(""); + int bs, parnum=-1; + int found = 0; + phandle_t ph; + ducell offs, size; + + /* Layout of PC partition table */ + struct pc_partition { + unsigned char boot; + unsigned char head; + unsigned char sector; + unsigned char cyl; + unsigned char type; + unsigned char e_head; + unsigned char e_sector; + unsigned char e_cyl; + u32 start_sect; /* unaligned little endian */ + u32 nr_sects; /* ditto */ + } *p, *partition; + + unsigned char buf[512]; + + DPRINTF("pcparts_open '%s'\n", str ); + + /* + Arguments that we accept: + id: [0-7] + [(id)][,][filespec] + */ + + if ( strlen(str) ) { + /* Detect the arguments */ + if ((*str >= '0' && *str <= '7') || (*str == ',')) { + push_str(str); + PUSH(','); + fword("left-parse-string"); + parstr = pop_fstr_copy(); + argstr = pop_fstr_copy(); + } else { + argstr = str; + } + + /* Convert the id to a partition number */ + if (parstr && strlen(parstr)) + parnum = atol(parstr); + } + + /* Make sure argstr is not null */ + if (argstr == NULL) + argstr = strdup(""); + + DPRINTF("parstr: %s argstr: %s parnum: %d\n", parstr, argstr, parnum); + free(parstr); + + if( parnum < 0 ) + parnum = 0; + + di->filesystem_ph = 0; + di->read_xt = find_parent_method("read"); + di->seek_xt = find_parent_method("seek"); + + SEEK( 0 ); + if( READ(buf, 512) != 512 ) + RET(0); + + /* Check Magic */ + if (!has_pc_part_magic(buf)) { + DPRINTF("pc partition magic not found.\n"); + RET(0); + } + + /* Actual partition data */ + partition = (struct pc_partition *) (buf + 0x1be); + + /* Make sure we use a copy accessible from an aligned pointer (some archs + e.g. SPARC will crash otherwise) */ + p = malloc(sizeof(struct pc_partition)); + + bs = 512; + + if (parnum < 4) { + /* primary partition */ + partition += parnum; + memcpy(p, partition, sizeof(struct pc_partition)); + + if (p->type == 0 || is_pc_extended_part(p->type)) { + DPRINTF("partition %d does not exist\n", parnum+1 ); + RET( 0 ); + } + + offs = (long long)(__le32_to_cpu(p->start_sect)) * bs; + di->offs_hi = offs >> BITS; + di->offs_lo = offs & (ucell) -1; + + size = (long long)(__le32_to_cpu(p->nr_sects)) * bs; + di->size_hi = size >> BITS; + di->size_lo = size & (ucell) -1; + + DPRINTF("Primary partition at sector %x\n", __le32_to_cpu(p->start_sect)); + + /* If PReP boot partition, exit immediately with no filesystem probe */ + if (p->type == 0x41) { + RET(-1); + } + + found = 1; + } else { + /* Extended partition */ + int i, cur_part; + unsigned long ext_start, cur_table; + + /* Search for the extended partition + * which contains logical partitions */ + for (i = 0; i < 4; i++) { + if (is_pc_extended_part(p[i].type)) + break; + } + + if (i >= 4) { + DPRINTF("Extended partition not found\n"); + RET( 0 ); + } + + DPRINTF("Extended partition at %d\n", i+1); + + /* Visit each logical partition labels */ + ext_start = __le32_to_cpu(p[i].start_sect); + cur_table = ext_start; + cur_part = 4; + + while (cur_part <= parnum) { + DPRINTF("cur_part=%d at %lx\n", cur_part, cur_table); + + SEEK( cur_table * bs ); + if( READ(buf, sizeof(512)) != sizeof(512) ) + RET( 0 ); + + if (!has_pc_part_magic(buf)) { + DPRINTF("Extended partition has no magic\n"); + break; + } + + /* Read the extended partition, making sure we are aligned again */ + partition = (struct pc_partition *) (buf + 0x1be); + memcpy(p, partition, sizeof(struct pc_partition)); + + /* First entry is the logical partition */ + if (cur_part == parnum) { + if (p->type == 0) { + DPRINTF("Partition %d is empty\n", parnum+1); + RET( 0 ); + } + + offs = (long long)(cur_table+__le32_to_cpu(p->start_sect)) * bs; + di->offs_hi = offs >> BITS; + di->offs_lo = offs & (ucell) -1; + + size = (long long)__le32_to_cpu(p->nr_sects) * bs; + di->size_hi = size >> BITS; + di->size_lo = size & (ucell) -1; + + /* If PReP boot partition, exit immediately with no filesystem probe */ + if (p->type == 0x41) { + RET(-1); + } + + found = 1; + break; + } + + /* Second entry is link to next partition */ + if (!is_pc_extended_part(p[1].type)) { + DPRINTF("no link\n"); + break; + } + + cur_table = ext_start + __le32_to_cpu(p[1].start_sect); + cur_part++; + } + + if (!found) { + DPRINTF("Logical partition %d does not exist\n", parnum+1); + RET( 0 ); + } + } + + free(p); + + if (found) { + /* We have a valid partition - so probe for a filesystem at the current offset */ + DPRINTF("pc-parts: about to probe for fs\n"); + DPUSH( offs ); + PUSH_ih( my_parent() ); + parword("find-filesystem"); + DPRINTF("pc-parts: done fs probe\n"); + + ph = POP_ph(); + if( ph ) { + DPRINTF("pc-parts: filesystem found with ph " FMT_ucellx " and args %s\n", ph, argstr); + di->filesystem_ph = ph; + + /* If we have been asked to open a particular file, interpose the filesystem package with + the passed filename as an argument */ + if (strlen(argstr)) { + push_str( argstr ); + PUSH_ph( ph ); + fword("interpose"); + } + } else { + DPRINTF("pc-parts: no filesystem found; bypassing misc-files interpose\n"); + } + + free( str ); + RET( -1 ); + } else { + DPRINTF("pc-parts: unable to locate partition\n"); + + free( str ); + RET( 0 ); + } +} + +/* ( block0 -- flag? ) */ +static void +pcparts_probe( pcparts_info_t *dummy ) +{ + unsigned char *buf = (unsigned char *)cell2pointer(POP()); + + DPRINTF("probing for PC partitions\n"); + + /* We also check that at least one valid partition exists; this is because + some CDs seem broken in that they have a partition table but it is empty + e.g. MorphOS. */ + RET ( has_pc_part_magic(buf) && has_pc_valid_partition(buf) ); +} + +/* ( -- type offset.d size.d ) */ +static void +pcparts_get_info( pcparts_info_t *di ) +{ + DPRINTF("PC get_info\n"); + PUSH( -1 ); /* no type */ + PUSH( di->offs_lo ); + PUSH( di->offs_hi ); + PUSH( di->size_lo ); + PUSH( di->size_hi ); +} + +static void +pcparts_block_size( __attribute__((unused))pcparts_info_t *di ) +{ + PUSH(512); +} + +static void +pcparts_initialize( pcparts_info_t *di ) +{ + fword("register-partition-package"); +} + +/* ( pos.d -- status ) */ +static void +pcparts_seek(pcparts_info_t *di ) +{ + long long pos = DPOP(); + long long offs, size; + + DPRINTF("pcparts_seek %llx:\n", pos); + + /* Seek is invalid if we reach the end of the device */ + size = ((ducell)di->size_hi << BITS) | di->size_lo; + if (pos > size) + RET( -1 ); + + /* Calculate the seek offset for the parent */ + offs = ((ducell)di->offs_hi << BITS) | di->offs_lo; + offs += pos; + DPUSH(offs); + + DPRINTF("pcparts_seek parent offset %llx:\n", offs); + + call_package(di->seek_xt, my_parent()); +} + +/* ( buf len -- actlen ) */ +static void +pcparts_read(pcparts_info_t *di ) +{ + DPRINTF("pcparts_read\n"); + + /* Pass the read back up to the parent */ + call_package(di->read_xt, my_parent()); +} + +/* ( addr -- size ) */ +static void +pcparts_load( __attribute__((unused))pcparts_info_t *di ) +{ + /* Invoke the loader */ + load(my_self()); +} + +/* ( pathstr len -- ) */ +static void +pcparts_dir( pcparts_info_t *di ) +{ + if ( di->filesystem_ph ) { + PUSH( my_self() ); + push_str("dir"); + PUSH( di->filesystem_ph ); + fword("find-method"); + POP(); + fword("execute"); + } else { + forth_printf("pc-parts: Unable to determine filesystem\n"); + POP(); + POP(); + } +} + +NODE_METHODS( pcparts ) = { + { "probe", pcparts_probe }, + { "open", pcparts_open }, + { "seek", pcparts_seek }, + { "read", pcparts_read }, + { "load", pcparts_load }, + { "dir", pcparts_dir }, + { "get-info", pcparts_get_info }, + { "block-size", pcparts_block_size }, + { NULL, pcparts_initialize }, +}; + +void +pcparts_init( void ) +{ + REGISTER_NODE( pcparts ); +} |