/* * Creation Date: <2003/12/03 22:10:45 samuel> * Time-stamp: <2004/01/07 19:17:45 samuel> * * * * 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 "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(""); } if (!path) { goto out; } 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 ); }