From 6a0f9e82c5bf457b958fe7918f8ff0080035f35e Mon Sep 17 00:00:00 2001 From: bellard Date: Wed, 27 Apr 2005 20:17:58 +0000 Subject: Virtual PC read-only disk image support (Alex Beregszaszi) git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1417 c046a42c-6fe2-441c-8c8c-71466251a162 --- Changelog | 1 + Makefile | 2 +- Makefile.target | 4 +- block-vpc.c | 243 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ block.c | 1 + vl.h | 1 + 6 files changed, 249 insertions(+), 3 deletions(-) create mode 100644 block-vpc.c diff --git a/Changelog b/Changelog index 26c3efacd2..c257d6157e 100644 --- a/Changelog +++ b/Changelog @@ -22,6 +22,7 @@ version 0.7.0: - Samba 3 support - initial Cocoa support (Pierre d'Herbemont) - generic FPU emulation code + - Virtual PC read-only disk image support (Alex Beregszaszi) version 0.6.1: diff --git a/Makefile b/Makefile index 10b5235cc0..7f4770c6cf 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ else endif endif -qemu-img$(EXESUF): qemu-img.c block.c block-cow.c block-qcow.c aes.c block-vmdk.c block-cloop.c block-dmg.c block-bochs.c +qemu-img$(EXESUF): qemu-img.c block.c block-cow.c block-qcow.c aes.c block-vmdk.c block-cloop.c block-dmg.c block-bochs.c block-vpc.c $(CC) -DQEMU_TOOL $(CFLAGS) $(LDFLAGS) $(DEFINES) -o $@ $^ -lz $(LIBS) dyngen$(EXESUF): dyngen.c diff --git a/Makefile.target b/Makefile.target index 991fcb9c58..1e6ff277e8 100644 --- a/Makefile.target +++ b/Makefile.target @@ -313,8 +313,8 @@ ifeq ($(ARCH),alpha) endif # must use static linking to avoid leaving stuff in virtual address space -VL_OBJS=vl.o osdep.o block.o readline.o monitor.o pci.o console.o -VL_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o block-dmg.o block-bochs.o +VL_OBJS=vl.o osdep.o block.o readline.o monitor.o pci.o console.o +VL_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o block-dmg.o block-bochs.o block-vpc.o SOUND_HW = sb16.o AUDIODRV = audio.o noaudio.o wavaudio.o diff --git a/block-vpc.c b/block-vpc.c new file mode 100644 index 0000000000..88ad575bf8 --- /dev/null +++ b/block-vpc.c @@ -0,0 +1,243 @@ +/* + * Block driver for Conectix/Microsoft Virtual PC images + * + * Copyright (c) 2005 Alex Beregszaszi + * + * 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. + */ +#include "vl.h" +#include "block_int.h" + +/**************************************************************/ + +#define HEADER_SIZE 512 + +//#define CACHE + +// always big-endian +struct vpc_subheader { + char magic[8]; // "conectix" / "cxsparse" + union { + struct { + uint32_t unk1[2]; + uint32_t unk2; // always zero? + uint32_t subheader_offset; + uint32_t unk3; // some size? + char creator[4]; // "vpc " + uint16_t major; + uint16_t minor; + char guest[4]; // "Wi2k" + uint32_t unk4[7]; + uint8_t vnet_id[16]; // virtual network id, purpose unknown + // next 16 longs are used, but dunno the purpose + // next 6 longs unknown, following 7 long maybe a serial + char padding[HEADER_SIZE - 84]; + } main; + struct { + uint32_t unk1[2]; // all bits set + uint32_t unk2; // always zero? + uint32_t pagetable_offset; + uint32_t unk3; + uint32_t pagetable_entries; // 32bit/entry + uint32_t pageentry_size; // 512*8*512 + uint32_t nb_sectors; + char padding[HEADER_SIZE - 40]; + } sparse; + char padding[HEADER_SIZE - 8]; + } type; +}; + +typedef struct BDRVVPCState { + int fd; + + int pagetable_entries; + uint32_t *pagetable; + + uint32_t pageentry_size; +#ifdef CACHE + uint8_t *pageentry_u8; + uint32_t *pageentry_u32; + uint16_t *pageentry_u16; + + uint64_t last_bitmap; +#endif +} BDRVVPCState; + +static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename) +{ + if (!strncmp(buf, "conectix", 8)) + return 100; + + return 0; +} + +static int vpc_open(BlockDriverState *bs, const char *filename) +{ + BDRVVPCState *s = bs->opaque; + int fd, i; + struct vpc_subheader header; + + fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE); + if (fd < 0) { + fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); + if (fd < 0) + return -1; + } + + bs->read_only = 1; // no write support yet + + s->fd = fd; + + if (read(fd, &header, HEADER_SIZE) != HEADER_SIZE) + goto fail; + + if (strncmp(header.magic, "conectix", 8)) + goto fail; + lseek(s->fd, be32_to_cpu(header.type.main.subheader_offset), SEEK_SET); + + if (read(fd, &header, HEADER_SIZE) != HEADER_SIZE) + goto fail; + + if (strncmp(header.magic, "cxsparse", 8)) + goto fail; + + bs->total_sectors = ((uint64_t)be32_to_cpu(header.type.sparse.pagetable_entries) * + be32_to_cpu(header.type.sparse.pageentry_size)) / 512; + + lseek(s->fd, be32_to_cpu(header.type.sparse.pagetable_offset), SEEK_SET); + + s->pagetable_entries = be32_to_cpu(header.type.sparse.pagetable_entries); + s->pagetable = qemu_malloc(s->pagetable_entries * 4); + if (!s->pagetable) + goto fail; + if (read(s->fd, s->pagetable, s->pagetable_entries * 4) != + s->pagetable_entries * 4) + goto fail; + for (i = 0; i < s->pagetable_entries; i++) + be32_to_cpus(&s->pagetable[i]); + + s->pageentry_size = be32_to_cpu(header.type.sparse.pageentry_size); +#ifdef CACHE + s->pageentry_u8 = qemu_malloc(512); + if (!s->pageentry_u8) + goto fail; + s->pageentry_u32 = s->pageentry_u8; + s->pageentry_u16 = s->pageentry_u8; + s->last_pagetable = -1; +#endif + + return 0; + fail: + close(fd); + return -1; +} + +static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num) +{ + BDRVVPCState *s = bs->opaque; + uint64_t offset = sector_num * 512; + uint64_t bitmap_offset, block_offset; + uint32_t pagetable_index, pageentry_index; + + pagetable_index = offset / s->pageentry_size; + pageentry_index = (offset % s->pageentry_size) / 512; + + if (pagetable_index > s->pagetable_entries || s->pagetable[pagetable_index] == 0xffffffff) + return -1; // not allocated + + bitmap_offset = 512 * s->pagetable[pagetable_index]; + block_offset = bitmap_offset + 512 + (512 * pageentry_index); + +// printf("sector: %llx, index: %x, offset: %x, bioff: %llx, bloff: %llx\n", +// sector_num, pagetable_index, pageentry_index, +// bitmap_offset, block_offset); + +// disabled by reason +#if 0 +#ifdef CACHE + if (bitmap_offset != s->last_bitmap) + { + lseek(s->fd, bitmap_offset, SEEK_SET); + + s->last_bitmap = bitmap_offset; + + // Scary! Bitmap is stored as big endian 32bit entries, + // while we used to look it up byte by byte + read(s->fd, s->pageentry_u8, 512); + for (i = 0; i < 128; i++) + be32_to_cpus(&s->pageentry_u32[i]); + } + + if ((s->pageentry_u8[pageentry_index / 8] >> (pageentry_index % 8)) & 1) + return -1; +#else + lseek(s->fd, bitmap_offset + (pageentry_index / 8), SEEK_SET); + + read(s->fd, &bitmap_entry, 1); + + if ((bitmap_entry >> (pageentry_index % 8)) & 1) + return -1; // not allocated +#endif +#endif + lseek(s->fd, block_offset, SEEK_SET); + + return 0; +} + +static int vpc_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + BDRVVPCState *s = bs->opaque; + int ret; + + while (nb_sectors > 0) { + if (!seek_to_sector(bs, sector_num)) + { + ret = read(s->fd, buf, 512); + if (ret != 512) + return -1; + } + else + memset(buf, 0, 512); + nb_sectors--; + sector_num++; + buf += 512; + } + return 0; +} + +static void vpc_close(BlockDriverState *bs) +{ + BDRVVPCState *s = bs->opaque; + qemu_free(s->pagetable); +#ifdef CACHE + qemu_free(s->pageentry_u8); +#endif + close(s->fd); +} + +BlockDriver bdrv_vpc = { + "vpc", + sizeof(BDRVVPCState), + vpc_probe, + vpc_open, + vpc_read, + NULL, + vpc_close, +}; diff --git a/block.c b/block.c index ce1c388b97..8baa3b87e2 100644 --- a/block.c +++ b/block.c @@ -652,4 +652,5 @@ void bdrv_init(void) bdrv_register(&bdrv_cloop); bdrv_register(&bdrv_dmg); bdrv_register(&bdrv_bochs); + bdrv_register(&bdrv_vpc); } diff --git a/vl.h b/vl.h index 4ad4330f37..8d29506ef2 100644 --- a/vl.h +++ b/vl.h @@ -383,6 +383,7 @@ extern BlockDriver bdrv_vmdk; extern BlockDriver bdrv_cloop; extern BlockDriver bdrv_dmg; extern BlockDriver bdrv_bochs; +extern BlockDriver bdrv_vpc; void bdrv_init(void); BlockDriver *bdrv_find_format(const char *format_name); -- cgit v1.2.1