summaryrefslogtreecommitdiffstats
path: root/qemu/roms/openbios/packages/pc-parts.c
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/roms/openbios/packages/pc-parts.c')
-rw-r--r--qemu/roms/openbios/packages/pc-parts.c393
1 files changed, 393 insertions, 0 deletions
diff --git a/qemu/roms/openbios/packages/pc-parts.c b/qemu/roms/openbios/packages/pc-parts.c
new file mode 100644
index 000000000..771923efe
--- /dev/null
+++ b/qemu/roms/openbios/packages/pc-parts.c
@@ -0,0 +1,393 @@
+/*
+ * 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 );
+}