qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH] Add basic read, write and create support for AMD Si


From: François Revol
Subject: [Qemu-devel] [PATCH] Add basic read, write and create support for AMD SimNow HDD images.
Date: Sun, 28 Nov 2010 20:08:15 +0100

$subj.
Someone asked about this format, wanting to try Haiku in SimNow, so I wrote 
this.
I got a report of successfully booting a converted image in SimNow.
It doesn't yet support automatically growing the file, so we just preallocate 
on create.

François.

From b0602bc2b02dcd7b15f0f9a143f850defd767509 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois=20Revol?= <address@hidden>
Date: Sun, 28 Nov 2010 20:01:03 +0100
Subject: [PATCH] Add basic read, write and create support for AMD SimNow HDD 
images.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit


Signed-off-by: François Revol <address@hidden>
---
 Makefile.objs |    2 +-
 block/hdd.c   |  354 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 355 insertions(+), 1 deletions(-)
 create mode 100644 block/hdd.c

diff --git a/Makefile.objs b/Makefile.objs
index 23b17ce..20e346d 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -20,7 +20,7 @@ block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
 
 block-nested-y += raw.o cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o 
vvfat.o
 block-nested-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o
-block-nested-y += parallels.o nbd.o blkdebug.o sheepdog.o blkverify.o
+block-nested-y += parallels.o nbd.o blkdebug.o sheepdog.o blkverify.o hdd.o
 block-nested-$(CONFIG_WIN32) += raw-win32.o
 block-nested-$(CONFIG_POSIX) += raw-posix.o
 block-nested-$(CONFIG_CURL) += curl.o
diff --git a/block/hdd.c b/block/hdd.c
new file mode 100644
index 0000000..aed609e
--- /dev/null
+++ b/block/hdd.c
@@ -0,0 +1,354 @@
+/*
+ * Block driver for the AMD SimNow HDD disk image
+ *
+ * Copyright (c) 2010 François Revol
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to 
deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * Reference:
+ * http://developer.amd.com/Assets/SimNowUsersManual4.6.2.pdf page 181
+ * 
+ * "the hard-disk image file contains a 512-byte header before the raw data.
+ *  This 512-byte header is identical to the information provided by the drive
+ *  in response to the ATA command "IDENTIFY". Following the 512-byte header
+ *  is the data for each sector from the device."
+ */
+
+#include "qemu-common.h"
+#include "block_int.h"
+#include "module.h"
+
+/* Command line option for static images. */
+#define BLOCK_OPT_STATIC "static"
+
+#define SECTOR_SIZE 512
+#define DATA_START 1
+#define MAX_MULT_SECTORS 1
+
+typedef struct BDRVHddState {
+    uint8_t identify_data[SECTOR_SIZE];
+} BDRVHddState;
+
+
+static void padstr(char *str, const char *src, int len)
+{
+    int i, v;
+    for(i = 0; i < len; i++) {
+        if (*src)
+            v = *src++;
+        else
+            v = ' ';
+        str[i^1] = v;
+    }
+}
+
+static void put_le16(uint16_t *p, unsigned int v)
+{
+    *p = cpu_to_le16(v);
+}
+
+static int isvalid_ide_chr(char c)
+{
+    /* XXX: other chars also maybe? */
+    return (isalnum(c) || c == ' ' || c == '.');
+}
+
+static int hdd_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+    int name_len;
+    uint16_t *p = (uint16_t *)buf;
+    int64_t nb_sectors;
+    uint32_t nb_sectors_clipped;
+    int result = 0;
+    int i;
+    
+    if (buf_size < SECTOR_SIZE) {
+        /* Header too small, no VDI. */
+        return 0;
+    }
+
+    /* best effort sanity check */
+    /* TODO: check more (CHS size...) */
+
+    /* serial number */
+    for (i = 10 * 2; i < 10 * 2 + 20; i++) {
+        if (!isvalid_ide_chr(buf[i])) {
+            return 0;
+        }
+    }
+    result += 20;
+
+    /* firmware version */
+    for (i = 23 * 2; i < 23 * 2 + 8; i++) {
+        if (!isvalid_ide_chr(buf[i])) {
+            return 0;
+        }
+    }
+    result += 8;
+
+    /* model */
+    for (i = 27 * 2; i < 27 * 2 + 40; i++) {
+        if (!isvalid_ide_chr(buf[i])) {
+            return 0;
+        }
+    }
+    result += 40;
+
+    nb_sectors = le16_to_cpu(p[100]);
+    nb_sectors |= (uint64_t)le16_to_cpu(p[101]) << 16;
+    nb_sectors |= (uint64_t)le16_to_cpu(p[102]) << 32;
+    nb_sectors |= (uint64_t)le16_to_cpu(p[103]) << 48;
+
+    nb_sectors_clipped = le16_to_cpu(p[60]) | (le16_to_cpu(p[61]) << 16);
+
+    if (nb_sectors < 1 || ((uint32_t)nb_sectors) != nb_sectors_clipped) {
+        return 0;
+    }
+    result += 10;
+
+    if (filename != NULL) {
+        name_len = strlen(filename);
+        if (name_len > 4 && !strcmp(filename + name_len - 4, ".hdd"))
+            result += 20;
+    }
+
+    return result;
+}
+
+static int hdd_open(BlockDriverState *bs, int flags)
+{
+    BDRVHddState *s = bs->opaque;
+    uint16_t *p = (uint16_t *)s->identify_data;
+    int64_t nb_sectors;
+    int sectors;
+    int cylinders;
+    int heads;
+
+    if (bdrv_read(bs->file, 0, s->identify_data, 1) < 0) {
+        goto fail;
+    }
+
+    if (hdd_probe(s->identify_data, SECTOR_SIZE, NULL) == 0) {
+        goto fail;
+    }
+
+    nb_sectors = le16_to_cpu(p[100]);
+    nb_sectors |= (uint64_t)le16_to_cpu(p[101]) << 16;
+    nb_sectors |= (uint64_t)le16_to_cpu(p[102]) << 32;
+    nb_sectors |= (uint64_t)le16_to_cpu(p[103]) << 48;
+
+    bs->total_sectors = nb_sectors;
+
+    /* hints */
+    /*
+    bs->cyls = le16_to_cpu(p[54]);
+    bs->heads = le16_to_cpu(p[55]);
+    bs->secs = le16_to_cpu(p[56]);
+    */
+
+    return 0;
+
+ fail:
+    return -1;
+}
+
+static int hdd_read(BlockDriverState *bs, int64_t sector_num,
+                    uint8_t *buf, int nb_sectors)
+{
+    int ret;
+    if (bdrv_read(bs->file, sector_num + DATA_START, buf, nb_sectors) < 0) {
+        return -1;
+    }
+    return 0;
+}
+
+static int hdd_write(BlockDriverState *bs, int64_t sector_num,
+                    const uint8_t *buf, int nb_sectors)
+{
+    int ret;
+
+    if (sector_num > bs->total_sectors) {
+        fprintf(stderr,
+                "(HDD) Wrong offset: sector_num=0x%" PRIx64
+                " total_sectors=0x%" PRIx64 "\n",
+                sector_num, bs->total_sectors);
+        return -1;
+    }
+    /* TODO: check if already allocated, else truncate() */
+    if (bdrv_write(bs->file, sector_num + DATA_START, buf, nb_sectors) < 0) {
+        return -1;
+    }
+    return 0;
+}
+
+static int hdd_create(const char *filename, QEMUOptionParameter *options)
+{
+    int fd;
+    int result = 0;
+    int static_image = 1; /* make it default for now */
+    uint64_t bytes = 0;
+    uint32_t blocks;
+    uint8_t header[SECTOR_SIZE];
+    size_t i;
+    uint16_t *p;
+    unsigned int oldsize;
+    char drive_serial_str[20];
+    int mult_sectors = 0;
+    int64_t nb_sectors;
+    int sectors;
+    int cylinders;
+    int heads;
+
+    /* Read out options. */
+    while (options && options->name) {
+        if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
+            bytes = options->value.n;
+        } else if (!strcmp(options->name, BLOCK_OPT_STATIC)) {
+            if (options->value.n) {
+                static_image = 1;
+            }
+        }
+        options++;
+    }
+
+    nb_sectors = bytes / SECTOR_SIZE;
+    /* we can't use bdrv_guess_geometry, use a standard physical disk geometry 
*/
+    cylinders = nb_sectors / (16 * 63);
+    if (cylinders > 16383)
+        cylinders = 16383;
+    else if (cylinders < 2)
+        cylinders = 2;
+    heads = 16;
+    sectors = 63;
+
+    /* XXX: generate one ? */
+    snprintf(drive_serial_str, sizeof(drive_serial_str), "QM%05d", 0);
+
+    fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
+              0644);
+    if (fd < 0) {
+        return -errno;
+    }
+
+    /* We need enough blocks to store the given disk size,
+       so always round up. */
+    blocks = (bytes + SECTOR_SIZE - 1) / SECTOR_SIZE;
+
+    /* Stolen from hw/ide/core.c */
+    /* XXX: are the hw flags ok there ? */
+    memset(header, 0, sizeof(header));
+    p = (uint16_t *)header;
+    put_le16(p + 0, 0x0040);
+    put_le16(p + 1, cylinders);
+    put_le16(p + 3, heads);
+    put_le16(p + 4, 512 * sectors); /* XXX: retired, remove ? */
+    put_le16(p + 5, 512); /* XXX: retired, remove ? */
+    put_le16(p + 6, sectors);
+    padstr((char *)(p + 10), drive_serial_str, 20); /* serial number */
+    put_le16(p + 20, 3); /* XXX: retired, remove ? */
+    put_le16(p + 21, 512); /* cache size in sectors */
+    put_le16(p + 22, 4); /* ecc bytes */
+    padstr((char *)(p + 23), QEMU_VERSION/*s->version*/, 8); /* firmware 
version */
+    padstr((char *)(p + 27), "QEMU HARDDISK", 40); /* model */
+#if MAX_MULT_SECTORS > 1
+    put_le16(p + 47, 0x8000 /*| MAX_MULT_SECTORS*/);
+#endif
+    put_le16(p + 48, 1); /* dword I/O */
+    put_le16(p + 49, (1 << 11) | (1 << 9) | (1 << 8)); /* DMA and LBA 
supported */
+    put_le16(p + 51, 0x200); /* PIO transfer cycle */
+    put_le16(p + 52, 0x200); /* DMA transfer cycle */
+    put_le16(p + 53, 1 | (1 << 1) | (1 << 2)); /* words 54-58,64-70,88 are 
valid */
+    put_le16(p + 54, cylinders);
+    put_le16(p + 55, heads);
+    put_le16(p + 56, sectors);
+    oldsize = cylinders * heads * sectors;
+    put_le16(p + 57, oldsize);
+    put_le16(p + 58, oldsize >> 16);
+    if (mult_sectors)
+        put_le16(p + 59, 0x100 | mult_sectors);
+    put_le16(p + 60, nb_sectors);
+    put_le16(p + 61, nb_sectors >> 16);
+    put_le16(p + 62, 0x07); /* single word dma0-2 supported */
+    put_le16(p + 63, 0x07); /* mdma0-2 supported */
+    put_le16(p + 64, 0x03); /* pio3-4 supported */
+    put_le16(p + 65, 120);
+    put_le16(p + 66, 120);
+    put_le16(p + 67, 120);
+    put_le16(p + 68, 120);
+    put_le16(p + 80, 0xf0); /* ata3 -> ata6 supported */
+    put_le16(p + 81, 0x16); /* conforms to ata5 */
+    /* 14=NOP supported, 5=WCACHE supported, 0=SMART supported */
+    put_le16(p + 82, (1 << 14) | (1 << 5) | 1);
+    /* 13=flush_cache_ext,12=flush_cache,10=lba48 */
+    put_le16(p + 83, (1 << 14) | (1 << 13) | (1 <<12) | (1 << 10));
+    /* 14=set to 1, 1=SMART self test, 0=SMART error logging */
+    put_le16(p + 84, (1 << 14) | 0);
+    /* 14 = NOP supported, 5=WCACHE enabled, 0=SMART feature set enabled */
+    put_le16(p + 85, (1 << 14) | 1);
+    /* 13=flush_cache_ext,12=flush_cache,10=lba48 */
+    put_le16(p + 86, (1 << 14) | (1 << 13) | (1 <<12) | (1 << 10));
+    /* 14=set to 1, 1=smart self test, 0=smart error logging */
+    put_le16(p + 87, (1 << 14) | 0);
+    put_le16(p + 88, 0x3f | (1 << 13)); /* udma5 set and supported */
+    put_le16(p + 93, 1 | (1 << 14) | 0x2000);
+    put_le16(p + 100, nb_sectors);
+    put_le16(p + 101, nb_sectors >> 16);
+    put_le16(p + 102, nb_sectors >> 32);
+    put_le16(p + 103, nb_sectors >> 48);
+
+    if (write(fd, &header, sizeof(header)) < 0) {
+        result = -errno;
+    }
+
+    /* TODO: specs says it can grow, so no need to always do this */
+    if (static_image) {
+        if (ftruncate(fd, sizeof(header) + blocks * SECTOR_SIZE)) {
+            result = -errno;
+        }
+    }
+
+    if (close(fd) < 0) {
+        result = -errno;
+    }
+
+    return result;
+}
+
+static void hdd_close(BlockDriverState *bs)
+{
+    BDRVHddState *s = bs->opaque;
+}
+
+static BlockDriver bdrv_hdd = {
+    .format_name    = "hdd",
+    .instance_size  = sizeof(BDRVHddState),
+    .bdrv_probe     = hdd_probe,
+    .bdrv_open      = hdd_open,
+    .bdrv_read      = hdd_read,
+    .bdrv_write     = hdd_write,
+    .bdrv_close     = hdd_close,
+    .bdrv_create    = hdd_create,
+};
+
+static void bdrv_hdd_init(void)
+{
+    bdrv_register(&bdrv_hdd);
+}
+
+block_init(bdrv_hdd_init);
-- 
1.7.2.2





reply via email to

[Prev in Thread] Current Thread [Next in Thread]