404 lines
9.1 KiB
C
404 lines
9.1 KiB
C
|
/*
|
||
|
* 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 );
|
||
|
}
|