From 09236e603055b1833c85a6a5a99de401e32df7fc Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 4 Aug 2015 15:33:11 +0100 Subject: [PATCH] [block] Add generic block device translator Add a generic mechanism for providing block devices on top of a data transfer interface (such as HTTP). Signed-off-by: Michael Brown --- src/core/blocktrans.c | 261 ++++++++++++++++++++++++++++++++++ src/include/ipxe/blocktrans.h | 38 +++++ src/include/ipxe/errfile.h | 1 + 3 files changed, 300 insertions(+) create mode 100644 src/core/blocktrans.c create mode 100644 src/include/ipxe/blocktrans.h diff --git a/src/core/blocktrans.c b/src/core/blocktrans.c new file mode 100644 index 000000000..3f32f9cf8 --- /dev/null +++ b/src/core/blocktrans.c @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2015 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * @file + * + * Block device translator + * + */ + +#include +#include +#include +#include +#include +#include +#include + +/** + * Reallocate block device translator data buffer + * + * @v xferbuf Data transfer buffer + * @v len New length (or zero to free buffer) + * @ret rc Return status code + */ +static int blktrans_xferbuf_realloc ( struct xfer_buffer *xferbuf, + size_t len ) { + struct block_translator *blktrans = + container_of ( xferbuf, struct block_translator, xferbuf ); + + /* Record length, if applicable */ + if ( blktrans->buffer ) { + + /* We have a (non-reallocatable) data buffer */ + return -ENOTSUP; + + } else { + + /* Record length (for block device capacity) */ + xferbuf->len = len; + return 0; + } +} + +/** + * Write data to block device translator data buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to copy + * @v len Length of data + */ +static void blktrans_xferbuf_write ( struct xfer_buffer *xferbuf, size_t offset, + const void *data, size_t len ) { + struct block_translator *blktrans = + container_of ( xferbuf, struct block_translator, xferbuf ); + + /* Write data to buffer, if applicable */ + if ( blktrans->buffer ) { + + /* Write data to buffer */ + copy_to_user ( blktrans->buffer, offset, data, len ); + + } else { + + /* Sanity check */ + assert ( len == 0 ); + } +} + +/** + * Read data from block device translator data buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to read + * @v len Length of data + */ +static void blktrans_xferbuf_read ( struct xfer_buffer *xferbuf, size_t offset, + void *data, size_t len ) { + struct block_translator *blktrans = + container_of ( xferbuf, struct block_translator, xferbuf ); + + /* Read data from buffer, if applicable */ + if ( blktrans->buffer ) { + + /* Read data from buffer */ + copy_from_user ( data, blktrans->buffer, offset, len ); + + } else { + + /* Sanity check */ + assert ( len == 0 ); + } +} + +/** Block device translator data transfer buffer operations */ +static struct xfer_buffer_operations blktrans_xferbuf_operations = { + .realloc = blktrans_xferbuf_realloc, + .write = blktrans_xferbuf_write, + .read = blktrans_xferbuf_read, +}; + +/** + * Close block device translator + * + * @v blktrans Block device translator + * @v rc Reason for close + */ +static void blktrans_close ( struct block_translator *blktrans, int rc ) { + struct block_device_capacity capacity; + + /* Report block device capacity, if applicable */ + if ( ( rc == 0 ) && ( blktrans->blksize ) ) { + + /* Construct block device capacity */ + capacity.blocks = + ( blktrans->xferbuf.len / blktrans->blksize ); + capacity.blksize = blktrans->blksize; + capacity.max_count = -1U; + + /* Report block device capacity */ + block_capacity ( &blktrans->block, &capacity ); + } + + /* Shut down interfaces */ + intf_shutdown ( &blktrans->xfer, rc ); + intf_shutdown ( &blktrans->block, rc ); +} + +/** + * Deliver data + * + * @v blktrans Block device translator + * @v iobuf I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int blktrans_deliver ( struct block_translator *blktrans, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + int rc; + + /* Deliver to buffer */ + if ( ( rc = xferbuf_deliver ( &blktrans->xferbuf, iob_disown ( iobuf ), + meta ) ) != 0 ) { + DBGC ( blktrans, "BLKTRANS %p could not deliver: %s\n", + blktrans, strerror ( rc ) ); + goto err; + } + + return 0; + + err: + blktrans_close ( blktrans, rc ); + return rc; +} + +/** + * Get underlying data transfer buffer + * + * @v blktrans Block device translator + * @ret xferbuf Data transfer buffer + */ +static struct xfer_buffer * +blktrans_buffer ( struct block_translator *blktrans ) { + + return &blktrans->xferbuf; +} + +/** Block device translator block device interface operations */ +static struct interface_operation blktrans_block_operations[] = { + INTF_OP ( intf_close, struct block_translator *, blktrans_close ), +}; + +/** Block device translator block device interface descriptor */ +static struct interface_descriptor blktrans_block_desc = + INTF_DESC_PASSTHRU ( struct block_translator, block, + blktrans_block_operations, xfer ); + +/** Block device translator data transfer interface operations */ +static struct interface_operation blktrans_xfer_operations[] = { + INTF_OP ( xfer_deliver, struct block_translator *, blktrans_deliver ), + INTF_OP ( xfer_buffer, struct block_translator *, blktrans_buffer ), + INTF_OP ( intf_close, struct block_translator *, blktrans_close ), +}; + +/** Block device translator data transfer interface descriptor */ +static struct interface_descriptor blktrans_xfer_desc = + INTF_DESC_PASSTHRU ( struct block_translator, xfer, + blktrans_xfer_operations, block ); + +/** + * Insert block device translator + * + * @v block Block device interface + * @v buffer Data buffer (or UNULL) + * @v size Length of data buffer, or block size + * @ret rc Return status code + */ +int block_translate ( struct interface *block, userptr_t buffer, size_t size ) { + struct block_translator *blktrans; + int rc; + + /* Allocate and initialise structure */ + blktrans = zalloc ( sizeof ( *blktrans ) ); + if ( ! blktrans ) { + rc = -ENOMEM; + goto err_alloc; + } + ref_init ( &blktrans->refcnt, NULL ); + intf_init ( &blktrans->block, &blktrans_block_desc, &blktrans->refcnt ); + intf_init ( &blktrans->xfer, &blktrans_xfer_desc, &blktrans->refcnt ); + blktrans->xferbuf.op = &blktrans_xferbuf_operations; + blktrans->buffer = buffer; + if ( buffer ) { + blktrans->xferbuf.len = size; + } else { + blktrans->blksize = size; + } + + /* Attach to interfaces, mortalise self, and return */ + assert ( block->dest != &null_intf ); + intf_plug_plug ( &blktrans->xfer, block->dest ); + intf_plug_plug ( &blktrans->block, block ); + ref_put ( &blktrans->refcnt ); + + DBGC2 ( blktrans, "BLKTRANS %p created", blktrans ); + if ( buffer ) { + DBGC2 ( blktrans, " for %#lx+%#zx", + user_to_phys ( buffer, 0 ), size ); + } + DBGC2 ( blktrans, "\n" ); + return 0; + + ref_put ( &blktrans->refcnt ); + err_alloc: + return rc; +} diff --git a/src/include/ipxe/blocktrans.h b/src/include/ipxe/blocktrans.h new file mode 100644 index 000000000..fee71b96c --- /dev/null +++ b/src/include/ipxe/blocktrans.h @@ -0,0 +1,38 @@ +#ifndef _IPXE_BLOCKTRANS_H +#define _IPXE_BLOCKTRANS_H + +/** @file + * + * Block device translator + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include + +/** A block device translator */ +struct block_translator { + /** Reference count */ + struct refcnt refcnt; + /** Block device interface */ + struct interface block; + /** Data transfer interface */ + struct interface xfer; + + /** Data transfer buffer */ + struct xfer_buffer xferbuf; + /** Data buffer */ + userptr_t buffer; + /** Block size */ + size_t blksize; +}; + +extern int block_translate ( struct interface *block, + userptr_t buffer, size_t size ); + +#endif /* _IPXE_BLOCKTRANS_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index b48d2a0ce..5e46df836 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -69,6 +69,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_ansicol ( ERRFILE_CORE | 0x001d0000 ) #define ERRFILE_ansicoldef ( ERRFILE_CORE | 0x001e0000 ) #define ERRFILE_fault ( ERRFILE_CORE | 0x001f0000 ) +#define ERRFILE_blocktrans ( ERRFILE_CORE | 0x00200000 ) #define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 ) #define ERRFILE_isa ( ERRFILE_DRIVER | 0x00010000 )