--- /dev/null
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file
+ *
+ * x86 bootsector image format
+ *
+ */
+
+#include <errno.h>
+#include <realmode.h>
+#include <biosint.h>
+#include <bootsector.h>
+
+/** Vector for storing original INT 18 handler
+ *
+ * We do not chain to this vector, so there is no need to place it in
+ * .text16.
+ */
+static struct segoff int18_vector;
+
+/** Vector for storing original INT 19 handler
+ *
+ * We do not chain to this vector, so there is no need to place it in
+ * .text16.
+ */
+static struct segoff int19_vector;
+
+/** Restart point for INT 18 or 19 */
+extern void bootsector_exec_fail ( void );
+
+/**
+ * Jump to preloaded bootsector
+ *
+ * @v segment Real-mode segment
+ * @v offset Real-mode offset
+ * @v drive Drive number to pass to boot sector
+ * @ret rc Return status code
+ */
+int call_bootsector ( unsigned int segment, unsigned int offset,
+ unsigned int drive ) {
+ int discard_b, discard_D, discard_d;
+
+ DBG ( "Booting from boot sector at %04x:%04x\n", segment, offset );
+
+ /* Hook INTs 18 and 19 to capture failure paths */
+ hook_bios_interrupt ( 0x18, ( unsigned int ) bootsector_exec_fail,
+ &int18_vector );
+ hook_bios_interrupt ( 0x19, ( unsigned int ) bootsector_exec_fail,
+ &int19_vector );
+
+ /* Boot the loaded sector
+ *
+ * We assume that the boot sector may completely destroy our
+ * real-mode stack, so we preserve everything we need in
+ * static storage.
+ */
+ __asm__ __volatile__ ( REAL_CODE ( /* Save return address off-stack */
+ "popw %%cs:saved_retaddr\n\t"
+ /* Save stack pointer */
+ "movw %%ss, %%ax\n\t"
+ "movw %%ax, %%cs:saved_ss\n\t"
+ "movw %%sp, %%cs:saved_sp\n\t"
+ /* Jump to boot sector */
+ "pushw %%bx\n\t"
+ "pushw %%di\n\t"
+ "lret\n\t"
+ /* Preserved variables */
+ "\nsaved_ss: .word 0\n\t"
+ "\nsaved_sp: .word 0\n\t"
+ "\nsaved_retaddr: .word 0\n\t"
+ /* Boot failure return point */
+ "\nbootsector_exec_fail:\n\t"
+ /* Restore stack pointer */
+ "movw %%cs:saved_ss, %%ax\n\t"
+ "movw %%ax, %%ss\n\t"
+ "movw %%cs:saved_sp, %%sp\n\t"
+ /* Return via saved address */
+ "jmp *%%cs:saved_retaddr\n\t" )
+ : "=b" ( discard_b ), "=D" ( discard_D ),
+ "=d" ( discard_d )
+ : "b" ( segment ), "D" ( offset ),
+ "d" ( drive )
+ : "eax", "ecx", "esi", "ebp" );
+
+ DBG ( "Booted disk returned via INT 18 or 19\n" );
+
+ /* Unhook INTs 18 and 19 */
+ unhook_bios_interrupt ( 0x18, ( unsigned int ) bootsector_exec_fail,
+ &int18_vector );
+ unhook_bios_interrupt ( 0x19, ( unsigned int ) bootsector_exec_fail,
+ &int19_vector );
+
+ return -ECANCELED;
+}
--- /dev/null
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file
+ *
+ * El Torito bootable ISO image format
+ *
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <realmode.h>
+#include <bootsector.h>
+#include <int13.h>
+#include <gpxe/uaccess.h>
+#include <gpxe/image.h>
+#include <gpxe/segment.h>
+#include <gpxe/ramdisk.h>
+#include <gpxe/shutdown.h>
+
+#define ISO9660_BLKSIZE 2048
+#define ELTORITO_VOL_DESC_OFFSET ( 17 * ISO9660_BLKSIZE )
+
+/** An El Torito Boot Record Volume Descriptor */
+struct eltorito_vol_desc {
+ /** Boot record indicator; must be 0 */
+ uint8_t record_indicator;
+ /** ISO-9660 identifier; must be "CD001" */
+ uint8_t iso9660_id[5];
+ /** Version, must be 1 */
+ uint8_t version;
+ /** Boot system indicator; must be "EL TORITO SPECIFICATION" */
+ uint8_t system_indicator[32];
+ /** Unused */
+ uint8_t unused[32];
+ /** Boot catalog sector */
+ uint32_t sector;
+} __attribute__ (( packed ));
+
+/** An El Torito Boot Catalog Validation Entry */
+struct eltorito_validation_entry {
+ /** Header ID; must be 1 */
+ uint8_t header_id;
+ /** Platform ID
+ *
+ * 0 = 80x86
+ * 1 = PowerPC
+ * 2 = Mac
+ */
+ uint8_t platform_id;
+ /** Reserved */
+ uint16_t reserved;
+ /** ID string */
+ uint8_t id_string[24];
+ /** Checksum word */
+ uint16_t checksum;
+ /** Signature; must be 0xaa55 */
+ uint16_t signature;
+} __attribute__ (( packed ));
+
+/** A bootable entry in the El Torito Boot Catalog */
+struct eltorito_boot_entry {
+ /** Boot indicator
+ *
+ * Must be @c ELTORITO_BOOTABLE for a bootable ISO image
+ */
+ uint8_t indicator;
+ /** Media type
+ *
+ */
+ uint8_t media_type;
+ /** Load segment */
+ uint16_t load_segment;
+ /** System type */
+ uint8_t filesystem;
+ /** Unused */
+ uint8_t reserved_a;
+ /** Sector count */
+ uint16_t length;
+ /** Starting sector */
+ uint32_t start;
+ /** Unused */
+ uint8_t reserved_b[20];
+} __attribute__ (( packed ));
+
+/** Boot indicator for a bootable ISO image */
+#define ELTORITO_BOOTABLE 0x88
+
+/** El Torito media types */
+enum eltorito_media_type {
+ /** No emulation */
+ ELTORITO_NO_EMULATION = 0,
+};
+
+struct image_type eltorito_image_type __image_type ( PROBE_NORMAL );
+
+/**
+ * Calculate 16-bit word checksum
+ *
+ * @v data Data to checksum
+ * @v len Length (in bytes, must be even)
+ * @ret sum Checksum
+ */
+static unsigned int word_checksum ( void *data, size_t len ) {
+ uint16_t *words;
+ uint16_t sum = 0;
+
+ for ( words = data ; len ; words++, len -= 2 ) {
+ sum += *words;
+ }
+ return sum;
+}
+
+/**
+ * Execute El Torito image
+ *
+ * @v image El Torito image
+ * @ret rc Return status code
+ */
+static int eltorito_exec ( struct image *image ) {
+ struct ramdisk ramdisk;
+ struct int13_drive int13_drive;
+ unsigned int load_segment = image->priv.ul;
+ unsigned int load_offset = ( load_segment ? 0 : 0x7c00 );
+ int rc;
+
+ memset ( &ramdisk, 0, sizeof ( ramdisk ) );
+ init_ramdisk ( &ramdisk, image->data, image->len, ISO9660_BLKSIZE );
+
+ memset ( &int13_drive, 0, sizeof ( int13_drive ) );
+ int13_drive.blockdev = &ramdisk.blockdev;
+ register_int13_drive ( &int13_drive );
+
+ if ( ( rc = call_bootsector ( load_segment, load_offset,
+ int13_drive.drive ) ) != 0 ) {
+ DBGC ( image, "ElTorito %p boot failed: %s\n",
+ image, strerror ( rc ) );
+ goto err;
+ }
+
+ rc = -ECANCELED; /* -EIMPOSSIBLE */
+ err:
+ unregister_int13_drive ( &int13_drive );
+ return rc;
+}
+
+/**
+ * Read and verify El Torito Boot Record Volume Descriptor
+ *
+ * @v image El Torito file
+ * @ret catalog_offset Offset of Boot Catalog
+ * @ret rc Return status code
+ */
+static int eltorito_read_voldesc ( struct image *image,
+ unsigned long *catalog_offset ) {
+ static const struct eltorito_vol_desc vol_desc_signature = {
+ .record_indicator = 0,
+ .iso9660_id = "CD001",
+ .version = 1,
+ .system_indicator = "EL TORITO SPECIFICATION",
+ };
+ struct eltorito_vol_desc vol_desc;
+
+ /* Sanity check */
+ if ( image->len < ( ELTORITO_VOL_DESC_OFFSET + ISO9660_BLKSIZE ) ) {
+ DBGC ( image, "ElTorito %p too short\n", image );
+ return -ENOEXEC;
+ }
+
+ /* Read and verify Boot Record Volume Descriptor */
+ copy_from_user ( &vol_desc, image->data, ELTORITO_VOL_DESC_OFFSET,
+ sizeof ( vol_desc ) );
+ if ( memcmp ( &vol_desc, &vol_desc_signature,
+ offsetof ( typeof ( vol_desc ), sector ) ) != 0 ) {
+ DBGC ( image, "ElTorito %p invalid Boot Record Volume "
+ "Descriptor\n", image );
+ return -ENOEXEC;
+ }
+ *catalog_offset = ( vol_desc.sector * ISO9660_BLKSIZE );
+
+ DBGC ( image, "ElTorito %p boot catalog at offset %#lx\n",
+ image, *catalog_offset );
+
+ return 0;
+}
+
+/**
+ * Read and verify El Torito Boot Catalog
+ *
+ * @v image El Torito file
+ * @v catalog_offset Offset of Boot Catalog
+ * @ret boot_entry El Torito boot entry
+ * @ret rc Return status code
+ */
+static int eltorito_read_catalog ( struct image *image,
+ unsigned long catalog_offset,
+ struct eltorito_boot_entry *boot_entry ) {
+ struct eltorito_validation_entry validation_entry;
+
+ /* Sanity check */
+ if ( image->len < ( catalog_offset + ISO9660_BLKSIZE ) ) {
+ DBGC ( image, "ElTorito %p bad boot catalog offset %#lx\n",
+ image, catalog_offset );
+ return -ENOEXEC;
+ }
+
+ /* Read and verify the Validation Entry of the Boot Catalog */
+ copy_from_user ( &validation_entry, image->data, catalog_offset,
+ sizeof ( validation_entry ) );
+ if ( word_checksum ( &validation_entry,
+ sizeof ( validation_entry ) ) != 0 ) {
+ DBGC ( image, "ElTorito %p bad Validation Entry checksum\n",
+ image );
+ return -ENOEXEC;
+ }
+
+ /* Read and verify the Initial/Default entry */
+ copy_from_user ( boot_entry, image->data,
+ ( catalog_offset + sizeof ( validation_entry ) ),
+ sizeof ( *boot_entry ) );
+ if ( boot_entry->indicator != ELTORITO_BOOTABLE ) {
+ DBGC ( image, "ElTorito %p not bootable\n", image );
+ return -ENOEXEC;
+ }
+ if ( boot_entry->media_type != ELTORITO_NO_EMULATION ) {
+ DBGC ( image, "ElTorito %p cannot support media type %d\n",
+ image, boot_entry->media_type );
+ return -ENOTSUP;
+ }
+
+ DBGC ( image, "ElTorito %p media type %d segment %04x\n",
+ image, boot_entry->media_type, boot_entry->load_segment );
+
+ return 0;
+}
+
+/**
+ * Load El Torito virtual disk image into memory
+ *
+ * @v image El Torito file
+ * @v boot_entry El Torito boot entry
+ * @ret rc Return status code
+ */
+static int eltorito_load_disk ( struct image *image,
+ struct eltorito_boot_entry *boot_entry ) {
+ unsigned long start = ( boot_entry->start * ISO9660_BLKSIZE );
+ unsigned long length = ( boot_entry->length * ISO9660_BLKSIZE );
+ unsigned int load_segment;
+ userptr_t buffer;
+ int rc;
+
+ /* Sanity check */
+ if ( image->len < ( start + length ) ) {
+ DBGC ( image, "ElTorito %p virtual disk lies outside image\n",
+ image );
+ return -ENOEXEC;
+ }
+ DBGC ( image, "ElTorito %p virtual disk at %#lx+%#lx\n",
+ image, start, length );
+
+ /* Calculate load address */
+ load_segment = boot_entry->load_segment;
+ buffer = real_to_user ( load_segment, ( load_segment ? 0 : 0x7c00 ) );
+
+ /* Verify and prepare segment */
+ if ( ( rc = prep_segment ( buffer, length, length ) ) != 0 ) {
+ DBGC ( image, "ElTorito %p could not prepare segment: %s\n",
+ image, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Copy image to segment */
+ memcpy_user ( buffer, 0, image->data, start, length );
+
+ return 0;
+}
+
+/**
+ * Load El Torito image into memory
+ *
+ * @v image El Torito file
+ * @ret rc Return status code
+ */
+int eltorito_load ( struct image *image ) {
+ struct eltorito_boot_entry boot_entry;
+ unsigned long bootcat_offset;
+ int rc;
+
+ /* Read Boot Record Volume Descriptor, if present */
+ if ( ( rc = eltorito_read_voldesc ( image, &bootcat_offset ) ) != 0 )
+ return rc;
+
+ /* This is an El Torito image, valid or otherwise */
+ if ( ! image->type )
+ image->type = &eltorito_image_type;
+
+ /* Read Boot Catalog */
+ if ( ( rc = eltorito_read_catalog ( image, bootcat_offset,
+ &boot_entry ) ) != 0 )
+ return rc;
+
+ /* Load Virtual Disk image */
+ if ( ( rc = eltorito_load_disk ( image, &boot_entry ) ) != 0 )
+ return rc;
+
+ /* Record load segment in image private data field */
+ image->priv.ul = boot_entry.load_segment;
+
+ return 0;
+}
+
+/** El Torito image type */
+struct image_type eltorito_image_type __image_type ( PROBE_NORMAL ) = {
+ .name = "El Torito",
+ .load = eltorito_load,
+ .exec = eltorito_exec,
+};
--- /dev/null
+#ifndef _BOOTSECTOR_H
+#define _BOOTSECTOR_H
+
+/** @file
+ *
+ * x86 bootsector image format
+ */
+
+extern int call_bootsector ( unsigned int segment, unsigned int offset,
+ unsigned int drive );
+
+#endif /* _BOOTSECTOR_H */
#define INT13_EXTENDED_WRITE 0x43
/** Get extended drive parameters */
#define INT13_GET_EXTENDED_PARAMETERS 0x48
+/** Get CD-ROM status / terminate emulation */
+#define INT13_CDROM_STATUS_TERMINATE 0x4b
/** @} */
#include <realmode.h>
#include <bios.h>
#include <biosint.h>
+#include <bootsector.h>
#include <int13.h>
/** @file
/** Assembly wrapper */
extern void int13_wrapper ( void );
-/** Vector for storing original INT 18 handler
- *
- * We do not chain to this vector, so there is no need to place it in
- * .text16.
- */
-static struct segoff int18_vector;
-
-/** Vector for storing original INT 19 handler
- *
- * We do not chain to this vector, so there is no need to place it in
- * .text16.
- */
-static struct segoff int19_vector;
-
-/** Restart point for INT 18 or 19 */
-extern void int13_exec_fail ( void );
-
/** List of registered emulated drives */
static LIST_HEAD ( drives );
unsigned int count;
userptr_t buffer;
+ /* Validate blocksize */
+ if ( blockdev->blksize != INT13_BLKSIZE ) {
+ DBG ( "Invalid blocksize (%zd) for non-extended read/write\n",
+ blockdev->blksize );
+ return -INT13_STATUS_INVALID;
+ }
+
/* Calculate parameters */
cylinder = ( ( ( ix86->regs.cl & 0xc0 ) << 2 ) | ix86->regs.ch );
assert ( cylinder < drive->cylinders );
DBG ( "C/H/S %d/%d/%d = LBA %#lx <-> %04x:%04x (count %d)\n", cylinder,
head, sector, lba, ix86->segs.es, ix86->regs.bx, count );
- /* Validate blocksize */
- if ( blockdev->blksize != INT13_BLKSIZE ) {
- DBG ( "Invalid blocksize (%zd) for non-extended read/write\n",
- blockdev->blksize );
- return -INT13_STATUS_INVALID;
- }
-
/* Read from / write to block device */
if ( io ( blockdev, lba, count, buffer ) != 0 )
return -INT13_STATUS_READ_ERROR;
return 0;
}
+struct int13_cdrom_specification {
+ /** Size of packet in bytes */
+ uint8_t size;
+ /** Boot media type */
+ uint8_t media_type;
+ /** Drive number */
+ uint8_t drive;
+};
+
+/**
+ * INT 13, 4b - Get CD-ROM status / terminate emulation
+ *
+ * @v drive Emulated drive
+ * @v ds:si El Torito specification packet to fill in
+ * @ret status Status code
+ */
+static int int13_cdrom_status_terminate ( struct int13_drive *drive,
+ struct i386_all_regs *ix86 ) {
+ struct int13_cdrom_specification specification;
+
+ DBG ( "Get CD-ROM emulation parameters to %04x:%04x\n",
+ ix86->segs.ds, ix86->regs.di );
+
+ memset ( &specification, 0, sizeof ( specification ) );
+ specification.size = sizeof ( specification );
+ specification.drive = drive->drive;
+
+ copy_to_real ( ix86->segs.ds, ix86->regs.si, &specification,
+ sizeof ( specification ) );
+ return 0;
+}
+
/**
* INT 13 handler
*
if ( drive->drive != ix86->regs.dl )
continue;
- DBG ( "INT 13,%02x (%02x): ", command, drive->drive );
+ DBG ( "INT 13,%04x (%02x): ", ix86->regs.ax, drive->drive );
switch ( command ) {
case INT13_RESET:
case INT13_GET_EXTENDED_PARAMETERS:
status = int13_get_extended_parameters ( drive, ix86 );
break;
+ case INT13_CDROM_STATUS_TERMINATE:
+ status = int13_cdrom_status_terminate ( drive, ix86 );
+ break;
default:
DBG ( "*** Unrecognised INT 13 ***\n" );
status = -INT13_STATUS_INVALID;
/* Set OF to indicate to wrapper not to chain this call */
ix86->flags |= OF;
+
+ break;
}
}
unsigned long blocks_per_cyl;
unsigned int i;
+ /* Don't even try when the blksize is invalid for C/H/S access */
+ if ( drive->blockdev->blksize != INT13_BLKSIZE )
+ return;
+
/* Scan through partition table and modify guesses for heads
* and sectors_per_track if we find any used partitions.
*/
int int13_boot ( unsigned int drive ) {
int status, signature;
int discard_c, discard_d;
+ int rc;
DBG ( "Booting from INT 13 drive %02x\n", drive );
return -ENOEXEC;
}
- /* Hook INTs 18 and 19 to capture failure paths */
- hook_bios_interrupt ( 0x18, ( unsigned int ) int13_exec_fail,
- &int18_vector );
- hook_bios_interrupt ( 0x19, ( unsigned int ) int13_exec_fail,
- &int19_vector );
-
- /* Boot the loaded sector
- *
- * We assume that the boot sector may completely destroy our
- * real-mode stack, so we preserve everything we need in
- * static storage.
- */
- __asm__ __volatile__ ( REAL_CODE ( /* Save return address off-stack */
- "popw %%cs:int13_saved_retaddr\n\t"
- /* Save stack pointer */
- "movw %%ss, %%ax\n\t"
- "movw %%ax, %%cs:int13_saved_ss\n\t"
- "movw %%sp, %%cs:int13_saved_sp\n\t"
- /* Jump to boot sector */
- "ljmp $0, $0x7c00\n\t"
- /* Preserved variables */
- "\nint13_saved_ss: .word 0\n\t"
- "\nint13_saved_sp: .word 0\n\t"
- "\nint13_saved_retaddr: .word 0\n\t"
- /* Boot failure return point */
- "\nint13_exec_fail:\n\t"
- /* Restore stack pointer */
- "movw %%cs:int13_saved_ss, %%ax\n\t"
- "movw %%ax, %%ss\n\t"
- "movw %%cs:int13_saved_sp, %%sp\n\t"
- /* Return via saved address */
- "jmp *%%cs:int13_saved_retaddr\n\t")
- : "=d" ( discard_d ) : "d" ( drive )
- : "eax", "ebx", "ecx", "esi", "edi", "ebp" );
-
- DBG ( "Booted disk returned via INT 18 or 19\n" );
-
- /* Unhook INTs 18 and 19 */
- unhook_bios_interrupt ( 0x18, ( unsigned int ) int13_exec_fail,
- &int18_vector );
- unhook_bios_interrupt ( 0x19, ( unsigned int ) int13_exec_fail,
- &int19_vector );
-
- return -ECANCELED;
+ /* Jump to boot sector */
+ if ( ( rc = call_bootsector ( 0x0, 0x7c00, drive ) ) != 0 ) {
+ DBG ( "INT 13 drive %02x boot returned\n", drive );
+ return rc;
+ }
+
+ return -ECANCELED; /* -EIMPOSSIBLE */
}
#ifdef IMAGE_BZIMAGE
REQUIRE_OBJECT ( bzimage );
#endif
+#ifdef IMAGE_ELTORITO
+REQUIRE_OBJECT ( eltorito );
+#endif
/*
* Drag in all requested commands
--- /dev/null
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <gpxe/blockdev.h>
+#include <gpxe/ramdisk.h>
+
+/**
+ * @file
+ *
+ * RAM disks
+ *
+ */
+
+static inline __attribute__ (( always_inline )) struct ramdisk *
+block_to_ramdisk ( struct block_device *blockdev ) {
+ return container_of ( blockdev, struct ramdisk, blockdev );
+}
+
+/**
+ * Read block
+ *
+ * @v blockdev Block device
+ * @v block Block number
+ * @v count Block count
+ * @v buffer Data buffer
+ * @ret rc Return status code
+ */
+static int ramdisk_read ( struct block_device *blockdev, uint64_t block,
+ unsigned long count, userptr_t buffer ) {
+ struct ramdisk *ramdisk = block_to_ramdisk ( blockdev );
+ unsigned long offset = ( block * blockdev->blksize );
+ unsigned long length = ( count * blockdev->blksize );
+
+ DBGC ( ramdisk, "RAMDISK %p reading [%lx,%lx)\n",
+ ramdisk, offset, length );
+
+ memcpy_user ( buffer, 0, ramdisk->data, offset, length );
+ return 0;
+}
+
+/**
+ * Write block
+ *
+ * @v blockdev Block device
+ * @v block Block number
+ * @v count Block count
+ * @v buffer Data buffer
+ * @ret rc Return status code
+ */
+static int ramdisk_write ( struct block_device *blockdev, uint64_t block,
+ unsigned long count, userptr_t buffer ) {
+ struct ramdisk *ramdisk = block_to_ramdisk ( blockdev );
+ unsigned long offset = ( block * blockdev->blksize );
+ unsigned long length = ( count * blockdev->blksize );
+
+ DBGC ( ramdisk, "RAMDISK %p writing [%lx,%lx)\n",
+ ramdisk, offset, length );
+
+ memcpy_user ( ramdisk->data, offset, buffer, 0, length );
+ return 0;
+}
+
+int init_ramdisk ( struct ramdisk *ramdisk, userptr_t data, size_t len,
+ unsigned int blksize ) {
+
+ if ( ! blksize )
+ blksize = 512;
+
+ ramdisk->data = data;
+ ramdisk->blockdev.read = ramdisk_read;
+ ramdisk->blockdev.write = ramdisk_write;
+ ramdisk->blockdev.blksize = blksize;
+ ramdisk->blockdev.blocks = ( len / blksize );
+
+ return 0;
+}