[Top][All Lists]
[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
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Qemu-devel] [PATCH] Add basic read, write and create support for AMD SimNow HDD images.,
François Revol <=