| /* |
| * 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)); |
| |
| 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; |
| |
| 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 ); |
| } |