diff options
author | bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162> | 2005-04-27 20:17:58 +0000 |
---|---|---|
committer | bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162> | 2005-04-27 20:17:58 +0000 |
commit | 6a0f9e82c5bf457b958fe7918f8ff0080035f35e (patch) | |
tree | 57363c056c3d276fc230afce411acd8e8d75e620 /block-vpc.c | |
parent | c2d551ff5acc0e236fe7fd8dfb1ce290d9ff4d99 (diff) |
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
Diffstat (limited to 'block-vpc.c')
-rw-r--r-- | block-vpc.c | 243 |
1 files changed, 243 insertions, 0 deletions
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, +}; |