/* * Creation Date: <2003/12/04 17:07:05 samuel> * Time-stamp: <2004/01/07 19:36:09 samuel> * * * * 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)); /* 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) ) { /* 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 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("Partition %d is not valid, allocated and readable\n", parnum); goto out; } 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 ); }