Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / packages / sun-parts.c
diff --git a/qemu/roms/openbios/packages/sun-parts.c b/qemu/roms/openbios/packages/sun-parts.c
new file mode 100644 (file)
index 0000000..16b281b
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+ *   Sun (Sparc32/64) 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_SUN_PARTS
+
+#ifdef DEBUG_SUN_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;
+       int             type;
+       phandle_t       filesystem_ph;
+} sunparts_info_t;
+
+DECLARE_NODE( sunparts, INSTALL_OPEN, sizeof(sunparts_info_t), "+/packages/sun-parts" );
+
+#define SEEK( pos )            ({ DPUSH(pos); call_parent(di->seek_xt); POP(); })
+#define READ( buf, size )      ({ PUSH((ucell)buf); PUSH(size); call_parent(di->read_xt); POP(); })
+
+/* Layout of SUN partition table */
+struct sun_disklabel {
+    uint8_t info[128];   /* Informative text string */
+    uint8_t spare0[14];
+    struct sun_info {
+        uint16_t id;
+        uint16_t flags;
+    } infos[8];
+    uint8_t spare[246];  /* Boot information etc. */
+    uint16_t rspeed;     /* Disk rotational speed */
+    uint16_t pcylcount;  /* Physical cylinder count */
+    uint16_t sparecyl;   /* extra sects per cylinder */
+    uint8_t spare2[4];   /* More magic... */
+    uint16_t ilfact;     /* Interleave factor */
+    uint16_t ncyl;       /* Data cylinder count */
+    uint16_t nacyl;      /* Alt. cylinder count */
+    uint16_t ntrks;      /* Tracks per cylinder */
+    uint16_t nsect;      /* Sectors per track */
+    uint8_t spare3[4];   /* Even more magic... */
+    struct sun_partition {
+        uint32_t start_cylinder;
+        uint32_t num_sectors;
+    } partitions[8];
+    uint16_t magic;      /* Magic number */
+    uint16_t csum;       /* Label xor'd checksum */
+};
+
+/* two helper functions */
+
+static inline int
+has_sun_part_magic(unsigned char *sect)
+{
+    struct sun_disklabel *p = (struct sun_disklabel *)sect;
+    uint16_t csum, *ush, tmp16;
+
+    if (__be16_to_cpu(p->magic) != 0xDABE)
+        return 0;
+
+    csum = 0;
+    for (ush = (uint16_t *)p; ush < (uint16_t *)(p + 1); ush++) {
+        tmp16 = __be16_to_cpu(*ush);
+       csum ^= tmp16;
+    }
+    return csum == 0;
+}
+
+/* ( open -- flag ) */
+static void
+sunparts_open( sunparts_info_t *di )
+{
+       char *str = my_args_copy();
+        char *argstr = NULL;
+        char *parstr = NULL;
+       int parnum = -1;
+       unsigned char buf[512];
+        struct sun_disklabel *p;
+        unsigned int i, bs;
+        ducell offs, size;
+       phandle_t ph;
+
+       DPRINTF("sunparts_open '%s'\n", str );
+
+       /* 
+               Arguments that we accept:
+               id: [0-7] | [a-h]
+               [(id)][,][filespec]
+       */
+
+       if ( str && strlen(str) ) {
+               /* Detect the arguments */
+               if ((*str >= '0' && *str <= '9') || (*str >= 'a' && *str < ('a' + 8)) || (*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)) {
+                   if (parstr[0] >= 'a' && parstr[0] < ('a' + 8))
+                       parnum = parstr[0] - 'a';
+                   else
+                       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);
+
+       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) {
+                free(str);
+               RET(0);
+        }
+
+       /* Check Magic */
+       if (!has_sun_part_magic(buf)) {
+               DPRINTF("Sun partition magic not found.\n");
+                free(str);
+               RET(0);
+       }
+
+       bs = 512;
+       /* get partition data */
+       p = (struct sun_disklabel *)buf;
+
+        for (i = 0; i < 8; i++) {
+            DPRINTF("%c: %d + %d, id %x, flags %x\n", 'a' + i,
+                    __be32_to_cpu(p->partitions[i].start_cylinder),
+                    __be32_to_cpu(p->partitions[i].num_sectors),
+                    __be16_to_cpu(p->infos[i].id),
+                    __be16_to_cpu(p->infos[i].flags));
+        }
+
+        if (parnum < 0)
+            parnum = 0;
+
+       DPRINTF("Selected partition %d\n", parnum);
+
+        offs = (long long)__be32_to_cpu(p->partitions[parnum].start_cylinder) *
+            __be16_to_cpu(p->ntrks) * __be16_to_cpu(p->nsect) * bs;
+
+        di->offs_hi = offs >> BITS;
+        di->offs_lo = offs & (ucell) -1;
+        size = (long long)__be32_to_cpu(p->partitions[parnum].num_sectors) * bs;
+        if (size == 0) {
+                DPRINTF("Partition size is 0, exiting\n");
+                free(str);
+                RET(0);
+        }
+        di->size_hi = size >> BITS;
+        di->size_lo = size & (ucell) -1;
+        di->type = __be16_to_cpu(p->infos[parnum].id);
+
+        DPRINTF("Found Sun partition, offs %lld size %lld\n",
+                (long long)offs, (long long)size);
+
+       /* Probe for filesystem at current offset */
+       DPRINTF("sun-parts: about to probe for fs\n");
+       DPUSH( offs );
+       PUSH_ih( my_parent() );
+       parword("find-filesystem");
+       DPRINTF("sun-parts: done fs probe\n");
+
+       ph = POP_ph();
+       if( ph ) {
+               DPRINTF("sun-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 (argstr && strlen(argstr)) {
+                       push_str( argstr );
+                       PUSH_ph( ph );
+                       fword("interpose");
+               }
+       } else {
+               DPRINTF("sun-parts: no filesystem found; bypassing misc-files interpose\n");
+
+               /* Solaris Fcode boot blocks assume that the disk-label package will always
+                  automatically interpose the "ufs-file-system" package if it exists! We
+                  need to mimic this behaviour in order for the boot to work. */
+               push_str("ufs-file-system");
+               feval("find-package");
+               ph = POP_ph();
+
+                if (argstr && strlen(argstr) && ph) {
+                       ph = POP_ph();
+                       push_str(argstr);
+                       PUSH_ph(ph);
+                       fword("interpose");
+               }
+       }
+
+       free( str );
+        RET( -1 );
+}
+
+/* ( block0 -- flag? ) */
+static void
+sunparts_probe( __attribute__((unused))sunparts_info_t *dummy )
+{
+       unsigned char *buf = (unsigned char *)POP();
+
+       DPRINTF("probing for Sun partitions\n");
+
+       RET ( has_sun_part_magic(buf) );
+}
+
+/* ( -- type offset.d size.d ) */
+static void
+sunparts_get_info( sunparts_info_t *di )
+{
+       DPRINTF("Sun get_info\n");
+       PUSH( di->type );
+       PUSH( di->offs_lo );
+       PUSH( di->offs_hi );
+       PUSH( di->size_lo );
+       PUSH( di->size_hi );
+}
+
+static void
+sunparts_block_size( __attribute__((unused))sunparts_info_t *di )
+{
+       PUSH(512);
+}
+
+static void
+sunparts_initialize( __attribute__((unused))sunparts_info_t *di )
+{
+       fword("register-partition-package");
+}
+
+/* ( pos.d -- status ) */
+static void
+sunparts_seek(sunparts_info_t *di )
+{
+       long long pos = DPOP();
+       long long offs, size;;
+
+       DPRINTF("sunparts_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("sunparts_seek parent offset %llx:\n", offs);
+
+       call_package(di->seek_xt, my_parent());
+}
+
+/* ( buf len -- actlen ) */
+static void
+sunparts_read(sunparts_info_t *di )
+{
+       DPRINTF("sunparts_read\n");
+
+       /* Pass the read back up to the parent */
+       call_package(di->read_xt, my_parent());
+}
+
+/* ( addr -- size ) */
+static void
+sunparts_load( __attribute__((unused))sunparts_info_t *di )
+{
+       /* Invoke the loader */
+       load(my_self());
+}
+
+/* ( pathstr len -- ) */
+static void
+sunparts_dir( sunparts_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("sun-parts: Unable to determine filesystem\n");
+               POP();
+               POP();
+       }
+}
+
+NODE_METHODS( sunparts ) = {
+       { "probe",      sunparts_probe          },
+       { "open",       sunparts_open           },
+       { "get-info",   sunparts_get_info       },
+       { "block-size", sunparts_block_size     },
+       { "seek",       sunparts_seek           },
+       { "read",       sunparts_read           },
+       { "load",       sunparts_load           },
+       { "dir",        sunparts_dir            },
+       { NULL,         sunparts_initialize     },
+};
+
+void
+sunparts_init( void )
+{
+       REGISTER_NODE( sunparts );
+}