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