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