blob: 16b281b6eca14f2769cd98d158caa5df0e8c5a9d [file] [log] [blame]
/*
* 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 );
}