blob: a810d00cbc1af5f95c6379205d8d612eab90690b [file] [log] [blame]
/*
* Creation Date: <2003/12/03 22:10:45 samuel>
* Time-stamp: <2004/01/07 19:17:45 samuel>
*
* <disk-label.c>
*
* Partition support
*
* Copyright (C) 2003, 2004 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 "openbios/config.h"
#include "libopenbios/bindings.h"
#include "libc/diskio.h"
#include "packages.h"
//#define DEBUG_DISK_LABEL
#ifdef DEBUG_DISK_LABEL
#define DPRINTF(fmt, args...) \
do { printk("DISK-LABEL - %s: " fmt, __func__ , ##args); } while (0)
#else
#define DPRINTF(fmt, args...) do { } while (0)
#endif
typedef struct {
int fd;
ucell offs_hi, offs_lo;
ucell size_hi, size_lo;
int block_size;
int type; /* partition type or -1 */
ihandle_t part_ih;
} dlabel_info_t;
DECLARE_NODE( dlabel, 0, sizeof(dlabel_info_t), "/packages/disk-label" );
/* ( -- ) */
static void
dlabel_close( dlabel_info_t *di )
{
if( di->part_ih )
close_package( di->part_ih );
if( di->fd )
close_io( di->fd );
}
/* ( -- success? ) */
static void
dlabel_open( dlabel_info_t *di )
{
const char *s, *filename;
char *path;
char block0[512];
phandle_t ph;
int fd, success=0;
xt_t xt;
path = my_args_copy();
if (!path) {
path = strdup("");
}
DPRINTF("dlabel-open '%s'\n", path );
/* open disk interface */
if( (fd=open_ih(my_parent())) == -1 )
goto out;
di->fd = fd;
/* argument format: parnum,filename */
s = path;
filename = "";
if( *s == '-' || isdigit(*s) ||
(*s >= 'a' && *s < ('a' + 8)
&& (*(s + 1) == ',' || *(s + 1) == '\0'))) {
if( (s=strpbrk(path,",")) ) {
filename = s+1;
}
} else {
filename = s;
if( *s == ',' )
filename++;
}
DPRINTF("filename %s\n", filename);
/* find partition handler */
seek_io( fd, 0 );
if( read_io(fd, block0, sizeof(block0)) != sizeof(block0) )
goto out;
PUSH( (ucell)block0 );
selfword("find-part-handler");
ph = POP_ph();
/* open partition package */
if( ph ) {
if( !(di->part_ih=open_package(path, ph)) )
goto out;
if( !(xt=find_ih_method("get-info", di->part_ih)) )
goto out;
call_package( xt , di->part_ih );
di->size_hi = POP();
di->size_lo = POP();
di->offs_hi = POP();
di->offs_lo = POP();
di->type = POP();
di->block_size = 512;
xt = find_ih_method("block-size", di->part_ih);
if (xt) {
call_package(xt, di->part_ih);
di->block_size = POP();
}
} else {
/* unknown (or missing) partition map,
* try the whole disk
*/
di->offs_hi = 0;
di->offs_lo = 0;
di->size_hi = 0;
di->size_lo = 0;
di->part_ih = 0;
di->type = -1;
di->block_size = 512;
xt = find_parent_method("block-size");
if (xt) {
call_parent(xt);
di->block_size = POP();
}
}
/* probe for filesystem */
PUSH_ih( my_self() );
selfword("find-filesystem");
ph = POP_ph();
if( ph ) {
push_str( filename );
PUSH_ph( ph );
fword("interpose");
} else if (*filename && strcmp(filename, "%BOOT") != 0) {
goto out;
}
success = 1;
out:
if( path )
free( path );
if( !success ) {
dlabel_close( di );
RET(0);
}
PUSH(-1);
}
/* ( addr len -- actual ) */
static void
dlabel_read( dlabel_info_t *di )
{
int ret, len = POP();
char *buf = (char*)POP();
llong pos = tell( di->fd );
ducell offs = ((ducell)di->offs_hi << BITS) | di->offs_lo;
ducell size = ((ducell)di->size_hi << BITS) | di->size_lo;
if (size && len > pos - offs + size) {
len = size - (pos - offs);
}
ret = read_io( di->fd, buf, len );
PUSH( ret );
}
/* ( pos.d -- status ) */
static void
dlabel_seek( dlabel_info_t *di )
{
llong pos = DPOP();
int ret;
ducell offs = ((ducell)di->offs_hi << BITS) | di->offs_lo;
ducell size = ((ducell)di->size_hi << BITS) | di->size_lo;
DPRINTF("dlabel_seek %llx [%llx, %llx]\n", pos, offs, size);
if( pos != -1 )
pos += offs;
else if( size ) {
DPRINTF("Seek EOF\n");
pos = offs + size;
} else {
/* let parent handle the EOF seek. */
}
DPRINTF("dlabel_seek: 0x%llx\n", pos );
if (size && (pos - offs >= size )) {
PUSH(-1);
return;
}
ret = seek_io( di->fd, pos );
PUSH( ret );
}
/* ( -- filepos.d ) */
static void
dlabel_tell( dlabel_info_t *di )
{
llong pos = tell( di->fd );
ducell offs = ((ducell)di->offs_hi << BITS) | di->offs_lo;
if( pos != -1 )
pos -= offs;
DPUSH( pos );
}
/* ( addr len -- actual ) */
static void
dlabel_write( __attribute__((unused)) dlabel_info_t *di )
{
DDROP();
PUSH( -1 );
}
/* ( rel.d -- abs.d ) */
static void
dlabel_offset( dlabel_info_t *di )
{
ullong rel = DPOP();
ducell offs = ((ducell)di->offs_hi << BITS) | di->offs_lo;
rel += offs;
DPUSH( rel );
}
/* ( addr -- size ) */
static void
dlabel_load( __attribute__((unused)) dlabel_info_t *di )
{
/* XXX: try the load method of the part package */
printk("Can't load from this device!\n");
POP();
PUSH(0);
}
static void
dlabel_block_size( dlabel_info_t *di )
{
PUSH(di->block_size);
}
NODE_METHODS( dlabel ) = {
{ "open", dlabel_open },
{ "close", dlabel_close },
{ "offset", dlabel_offset },
{ "load", dlabel_load },
{ "block-size", dlabel_block_size },
{ "read", dlabel_read },
{ "write", dlabel_write },
{ "seek", dlabel_seek },
{ "tell", dlabel_tell },
};
void
disklabel_init( void )
{
REGISTER_NODE( dlabel );
}