Files
openbios/packages/mac-parts.c
Mark Cave-Ayland f3702e5b21 mac-parts.c: restrict checking of partition status flags to CHRP partitions only
Early versions of MacOS X don't correctly set the partition status flags when
creating the OS partition. Relax the status flag checks so they are only applied
to CHRP partitions which allows us to both meet the intention of the CHRP
specification and also enable OpenBIOS to boot OS partitions from OSs such as
MacOS X DPs.

With thanks to Steven Troughton-Smith <steve@highcaffeinecontent.com> for the
in-depth investigation.

Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
2017-02-11 10:30:16 +00:00

445 lines
12 KiB
C

/*
* Creation Date: <2003/12/04 17:07:05 samuel>
* Time-stamp: <2004/01/07 19:36:09 samuel>
*
* <mac-parts.c>
*
* macintosh partition support
*
* Copyright (C) 2003, 2004 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 "mac-parts.h"
#include "libc/byteorder.h"
#include "libc/vsprintf.h"
#include "packages.h"
//#define CONFIG_DEBUG_MAC_PARTS
#ifdef CONFIG_DEBUG_MAC_PARTS
#define DPRINTF(fmt, args...) \
do { printk("MAC-PARTS: " fmt , ##args); } while (0)
#else
#define DPRINTF(fmt, args...) do {} while(0)
#endif
typedef struct {
xt_t seek_xt, read_xt;
ucell offs_hi, offs_lo;
ucell size_hi, size_lo;
ucell bootcode_addr, bootcode_entry;
unsigned int blocksize;
phandle_t filesystem_ph;
} macparts_info_t;
DECLARE_NODE( macparts, INSTALL_OPEN, sizeof(macparts_info_t), "+/packages/mac-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(); })
/* ( open -- flag ) */
static void
macparts_open( macparts_info_t *di )
{
char *str = my_args_copy();
char *parstr = NULL, *argstr = NULL;
char *tmpstr;
int bs, parnum=-1, apple_parnum=-1;
int parlist[2], parlist_size = 0;
desc_map_t dmap;
part_entry_t par;
int ret = 0, i = 0, j = 0;
int want_bootcode = 0;
phandle_t ph;
ducell offs = 0, size = -1;
DPRINTF("macparts_open '%s'\n", str );
/*
Arguments that we accept:
id: [0-7]
[(id)][,][filespec]
*/
if ( str && strlen(str) ) {
/* Detect the arguments */
if ((*str >= '0' && *str <= '9') || (*str == ',')) {
push_str(str);
PUSH(',');
fword("left-parse-string");
parstr = pop_fstr_copy();
argstr = pop_fstr_copy();
} else {
argstr = str;
}
/* Make sure argstr is not null */
if (argstr == NULL)
argstr = strdup("");
/* Convert the id to a partition number */
if (parstr && strlen(parstr))
parnum = atol(parstr);
/* Detect if we are looking for the bootcode */
if (strcmp(argstr, "%BOOT") == 0) {
want_bootcode = 1;
}
}
DPRINTF("parstr: %s argstr: %s parnum: %d\n", parstr, argstr, parnum);
DPRINTF("want_bootcode %d\n", want_bootcode);
DPRINTF("macparts_open %d\n", parnum);
di->filesystem_ph = 0;
di->read_xt = find_parent_method("read");
di->seek_xt = find_parent_method("seek");
SEEK( 0 );
if( READ(&dmap, sizeof(dmap)) != sizeof(dmap) )
goto out;
/* partition maps might support multiple block sizes; in this case,
* pmPyPartStart is typically given in terms of 512 byte blocks.
*/
bs = __be16_to_cpu(dmap.sbBlockSize);
if( bs != 512 ) {
SEEK( 512 );
READ( &par, sizeof(par) );
if( __be16_to_cpu(par.pmSig) == DESC_PART_SIGNATURE )
bs = 512;
}
SEEK( bs );
if( READ(&par, sizeof(par)) != sizeof(par) )
goto out;
if (__be16_to_cpu(par.pmSig) != DESC_PART_SIGNATURE)
goto out;
/*
* Implement partition selection as per the PowerPC Microprocessor CHRP bindings
*/
if (argstr == NULL || parnum == 0) {
/* According to the spec, partition 0 as well as no arguments means the whole disk */
offs = (long long)0;
size = (long long)__be32_to_cpu(dmap.sbBlkCount) * bs;
di->blocksize = (unsigned int)bs;
di->offs_hi = offs >> BITS;
di->offs_lo = offs & (ucell) -1;
di->size_hi = size >> BITS;
di->size_lo = size & (ucell) -1;
ret = -1;
goto out;
} else if (parnum == -1) {
DPRINTF("mac-parts: counted %d partitions\n", __be32_to_cpu(par.pmMapBlkCnt));
/* No partition was explicitly requested so let's find a suitable partition... */
for (i = 1; i <= __be32_to_cpu(par.pmMapBlkCnt); i++) {
SEEK( bs * i );
READ( &par, sizeof(par) );
if ( __be16_to_cpu(par.pmSig) != DESC_PART_SIGNATURE ||
!__be32_to_cpu(par.pmPartBlkCnt) )
continue;
DPRINTF("found partition %d type: %s with status %x\n", i, par.pmPartType, __be32_to_cpu(par.pmPartStatus));
/* Unfortunately Apple's OF implementation doesn't follow the OF PowerPC CHRP bindings
* and instead will brute-force boot the first valid partition it finds with a
* type of either "Apple_Boot", "Apple_HFS" or "DOS_FAT_". Here we store the id
* of the first partition that matches these criteria to use as a fallback later
* if required. */
if (apple_parnum == -1 &&
(strcmp(par.pmPartType, "Apple_Boot") == 0 ||
strcmp(par.pmPartType, "Apple_Bootstrap") == 0 ||
strcmp(par.pmPartType, "Apple_HFS") == 0 ||
strcmp(par.pmPartType, "DOS_FAT_") == 0)) {
apple_parnum = i;
DPRINTF("Located Apple OF fallback partition %d\n", apple_parnum);
}
/* If we have a valid, allocated and readable partition... */
if( (__be32_to_cpu(par.pmPartStatus) & kPartitionAUXIsValid) &&
(__be32_to_cpu(par.pmPartStatus) & kPartitionAUXIsAllocated) &&
(__be32_to_cpu(par.pmPartStatus) & kPartitionAUXIsReadable) ) {
/* If the partition is also bootable and the pmProcessor field matches "PowerPC" (insensitive
* match), then according to the CHRP bindings this is our chosen partition */
for (j = 0; j < strlen(par.pmProcessor); j++) {
par.pmProcessor[j] = tolower(par.pmProcessor[j]);
}
if ((__be32_to_cpu(par.pmPartStatus) & kPartitionAUXIsBootValid) &&
strcmp(par.pmProcessor, "powerpc") == 0) {
parnum = i;
DPRINTF("Located CHRP-compliant boot partition %d\n", parnum);
}
}
}
/* If we found a valid CHRP partition, add it to the list */
if (parnum > 0) {
parlist[parlist_size++] = parnum;
}
/* If we found an Apple OF fallback partition, add it to the list */
if (apple_parnum > 0 && apple_parnum != parnum) {
parlist[parlist_size++] = apple_parnum;
}
} else {
/* Another partition was explicitly requested */
parlist[parlist_size++] = parnum;
DPRINTF("Partition %d explicitly requested\n", parnum);
}
/* Attempt to use our CHRP partition, optionally followed by our Apple OF fallback partition */
for (j = 0; j < parlist_size; j++) {
/* Make sure our partition is valid */
parnum = parlist[j];
DPRINTF("Selected partition %d\n", parnum);
SEEK( bs * parnum );
READ( &par, sizeof(par) );
if(! ((__be32_to_cpu(par.pmPartStatus) & kPartitionAUXIsValid) &&
(__be32_to_cpu(par.pmPartStatus) & kPartitionAUXIsAllocated) &&
(__be32_to_cpu(par.pmPartStatus) & kPartitionAUXIsReadable)) ) {
DPRINTF("WARNING: Partition %d is not valid, allocated and readable\n", parnum);
}
ret = -1;
offs = (long long)__be32_to_cpu(par.pmPyPartStart) * bs;
size = (long long)__be32_to_cpu(par.pmPartBlkCnt) * bs;
if (want_bootcode) {
/* If size == 0 then fail because we requested bootcode but it doesn't exist */
size = (long long)__be32_to_cpu(par.pmBootSize);
if (!size) {
ret = 0;
goto out;
}
/* Adjust seek position so 0 = start of bootcode */
offs += (long long)__be32_to_cpu(par.pmLgBootStart) * bs;
di->bootcode_addr = __be32_to_cpu(par.pmBootLoad);
di->bootcode_entry = __be32_to_cpu(par.pmBootEntry);
}
di->blocksize = (unsigned int)bs;
di->offs_hi = offs >> BITS;
di->offs_lo = offs & (ucell) -1;
di->size_hi = size >> BITS;
di->size_lo = size & (ucell) -1;
/* If we're trying to execute bootcode then we're all done */
if (want_bootcode) {
goto out;
}
/* We have a valid partition - so probe for a filesystem at the current offset */
DPRINTF("mac-parts: about to probe for fs\n");
DPUSH( offs );
PUSH_ih( my_parent() );
parword("find-filesystem");
DPRINTF("mac-parts: done fs probe\n");
ph = POP_ph();
if( ph ) {
DPRINTF("mac-parts: filesystem found on partition %d with ph " FMT_ucellx " and args %s\n", parnum, ph, argstr);
di->filesystem_ph = ph;
/* In case no partition was specified, set a special selected-partition-args property
giving the device parameters that we can use to generate bootpath */
tmpstr = malloc(strlen(argstr) + 2 + 1);
if (strlen(argstr)) {
sprintf(tmpstr, "%d,%s", parnum, argstr);
} else {
sprintf(tmpstr, "%d", parnum);
}
push_str(tmpstr);
feval("strdup encode-string \" selected-partition-args\" property");
free(tmpstr);
/* 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");
}
goto out;
} else {
DPRINTF("mac-parts: no filesystem found on partition %d; bypassing misc-files interpose\n", parnum);
/* Here we have a valid partition; however if we tried to pass in a file argument for a
partition that doesn't contain a filesystem, then we must fail */
if (strlen(argstr)) {
ret = 0;
}
}
}
free( str );
out:
PUSH( ret );
}
/* ( block0 -- flag? ) */
static void
macparts_probe( macparts_info_t *dummy )
{
desc_map_t *dmap = (desc_map_t*)cell2pointer(POP());
DPRINTF("macparts_probe %x ?= %x\n", dmap->sbSig, DESC_MAP_SIGNATURE);
if( __be16_to_cpu(dmap->sbSig) != DESC_MAP_SIGNATURE )
RET(0);
RET(-1);
}
/* ( -- type offset.d size.d ) */
static void
macparts_get_info( macparts_info_t *di )
{
DPRINTF("macparts_get_info");
PUSH( -1 ); /* no type */
PUSH( di->offs_lo );
PUSH( di->offs_hi );
PUSH( di->size_lo );
PUSH( di->size_hi );
}
/* ( -- size entry addr ) */
static void
macparts_get_bootcode_info( macparts_info_t *di )
{
DPRINTF("macparts_get_bootcode_info");
PUSH( di->size_lo );
PUSH( di->bootcode_entry );
PUSH( di->bootcode_addr );
}
static void
macparts_block_size( macparts_info_t *di )
{
DPRINTF("macparts_block_size = %x\n", di->blocksize);
PUSH(di->blocksize);
}
static void
macparts_initialize( macparts_info_t *di )
{
fword("register-partition-package");
}
/* ( pos.d -- status ) */
static void
macparts_seek(macparts_info_t *di )
{
long long pos = DPOP();
long long offs, size;
DPRINTF("macparts_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("macparts_seek parent offset %llx:\n", offs);
call_package(di->seek_xt, my_parent());
}
/* ( buf len -- actlen ) */
static void
macparts_read(macparts_info_t *di )
{
DPRINTF("macparts_read\n");
/* Pass the read back up to the parent */
call_package(di->read_xt, my_parent());
}
/* ( addr -- size ) */
static void
macparts_load( __attribute__((unused))macparts_info_t *di )
{
/* Invoke the loader */
load(my_self());
}
/* ( pathstr len -- ) */
static void
macparts_dir( macparts_info_t *di )
{
/* On PPC Mac, the first partition chosen according to the CHRP boot
specification (i.e. marked as bootable) may not necessarily contain
a valid FS */
if ( di->filesystem_ph ) {
PUSH( my_self() );
push_str("dir");
PUSH( di->filesystem_ph );
fword("find-method");
POP();
fword("execute");
} else {
forth_printf("mac-parts: Unable to determine filesystem\n");
POP();
POP();
}
}
NODE_METHODS( macparts ) = {
{ "probe", macparts_probe },
{ "open", macparts_open },
{ "seek", macparts_seek },
{ "read", macparts_read },
{ "load", macparts_load },
{ "dir", macparts_dir },
{ "get-info", macparts_get_info },
{ "get-bootcode-info", macparts_get_bootcode_info },
{ "block-size", macparts_block_size },
{ NULL, macparts_initialize },
};
void
macparts_init( void )
{
REGISTER_NODE( macparts );
}