From 2cf1e33df188538695e05d937bbafc706663cea7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 29 Jan 2007 04:21:38 +0000 Subject: [PATCH] Split bootsector execution code out into bootsector.c. Added basic El Torito ISO image boot capability --- src/arch/i386/image/bootsector.c | 111 ++++++++ src/arch/i386/image/eltorito.c | 334 +++++++++++++++++++++++++ src/arch/i386/include/bootsector.h | 12 + src/arch/i386/include/int13.h | 2 + src/arch/i386/interface/pcbios/int13.c | 125 +++++---- src/core/config.c | 3 + src/drivers/block/ramdisk.c | 91 +++++++ 7 files changed, 610 insertions(+), 68 deletions(-) create mode 100644 src/arch/i386/image/bootsector.c create mode 100644 src/arch/i386/image/eltorito.c create mode 100644 src/arch/i386/include/bootsector.h create mode 100644 src/drivers/block/ramdisk.c diff --git a/src/arch/i386/image/bootsector.c b/src/arch/i386/image/bootsector.c new file mode 100644 index 00000000..d2711a87 --- /dev/null +++ b/src/arch/i386/image/bootsector.c @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2007 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** + * @file + * + * x86 bootsector image format + * + */ + +#include +#include +#include +#include + +/** 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; +} diff --git a/src/arch/i386/image/eltorito.c b/src/arch/i386/image/eltorito.c new file mode 100644 index 00000000..456a5100 --- /dev/null +++ b/src/arch/i386/image/eltorito.c @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2007 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/** + * @file + * + * El Torito bootable ISO image format + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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, +}; diff --git a/src/arch/i386/include/bootsector.h b/src/arch/i386/include/bootsector.h new file mode 100644 index 00000000..e9071052 --- /dev/null +++ b/src/arch/i386/include/bootsector.h @@ -0,0 +1,12 @@ +#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 */ diff --git a/src/arch/i386/include/int13.h b/src/arch/i386/include/int13.h index 16802a01..c9d76585 100644 --- a/src/arch/i386/include/int13.h +++ b/src/arch/i386/include/int13.h @@ -37,6 +37,8 @@ struct block_device; #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 /** @} */ diff --git a/src/arch/i386/interface/pcbios/int13.c b/src/arch/i386/interface/pcbios/int13.c index 8796f9a7..b97d066c 100644 --- a/src/arch/i386/interface/pcbios/int13.c +++ b/src/arch/i386/interface/pcbios/int13.c @@ -26,6 +26,7 @@ #include #include #include +#include #include /** @file @@ -44,23 +45,6 @@ static struct segoff __text16 ( int13_vector ); /** 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 ); @@ -114,6 +98,13 @@ static int int13_rw_sectors ( struct int13_drive *drive, 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 ); @@ -129,13 +120,6 @@ static int int13_rw_sectors ( struct int13_drive *drive, 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; @@ -333,6 +317,38 @@ static int int13_get_extended_parameters ( struct int13_drive *drive, 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 * @@ -346,7 +362,7 @@ static void int13 ( struct i386_all_regs *ix86 ) { 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: @@ -379,6 +395,9 @@ static void int13 ( struct i386_all_regs *ix86 ) { 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; @@ -398,6 +417,8 @@ static void int13 ( struct i386_all_regs *ix86 ) { /* Set OF to indicate to wrapper not to chain this call */ ix86->flags |= OF; + + break; } } @@ -453,6 +474,10 @@ static void guess_int13_geometry ( struct int13_drive *drive ) { 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. */ @@ -564,6 +589,7 @@ void unregister_int13_drive ( struct int13_drive *drive ) { 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 ); @@ -593,48 +619,11 @@ int int13_boot ( unsigned int 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 ); + /* Jump to boot sector */ + if ( ( rc = call_bootsector ( 0x0, 0x7c00, drive ) ) != 0 ) { + DBG ( "INT 13 drive %02x boot returned\n", drive ); + return rc; + } - /* 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; + return -ECANCELED; /* -EIMPOSSIBLE */ } diff --git a/src/core/config.c b/src/core/config.c index e27917a6..a4c31681 100644 --- a/src/core/config.c +++ b/src/core/config.c @@ -143,6 +143,9 @@ REQUIRE_OBJECT ( script ); #ifdef IMAGE_BZIMAGE REQUIRE_OBJECT ( bzimage ); #endif +#ifdef IMAGE_ELTORITO +REQUIRE_OBJECT ( eltorito ); +#endif /* * Drag in all requested commands diff --git a/src/drivers/block/ramdisk.c b/src/drivers/block/ramdisk.c new file mode 100644 index 00000000..b5324bf1 --- /dev/null +++ b/src/drivers/block/ramdisk.c @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2007 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include + +/** + * @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; +}