Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / packages / pc-parts.c
diff --git a/qemu/roms/openbios/packages/pc-parts.c b/qemu/roms/openbios/packages/pc-parts.c
new file mode 100644 (file)
index 0000000..771923e
--- /dev/null
@@ -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 );
+}