2 * Block driver for Conectix/Microsoft Virtual PC images
4 * Copyright (c) 2005 Alex Beregszaszi
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 #include "qemu-common.h"
25 #include "block_int.h"
27 /**************************************************************/
29 #define HEADER_SIZE 512
41 char creator[8]; // "conectix"
45 // Offset of next header structure, 0xFFFFFFFF if none
48 // Seconds since Jan 1, 2000 0:00:00 (UTC)
51 char creator_app[4]; // "vpc "
54 char creator_os[4]; // "Wi2k"
65 // Checksum of the Hard Disk Footer ("one's complement of the sum of all
66 // the bytes in the footer without the checksum field")
69 // UUID used to identify a parent hard disk (backing file)
72 uint8_t in_saved_state;
75 struct vhd_dyndisk_header {
76 char magic[8]; // "cxsparse"
78 // Offset of next header structure, 0xFFFFFFFF if none
81 // Offset of the Block Allocation Table (BAT)
82 uint64_t table_offset;
85 uint32_t max_table_entries; // 32bit/entry
87 // 2 MB by default, must be a power of two
91 uint8_t parent_uuid[16];
92 uint32_t parent_timestamp;
95 // Backing file name (in UTF-16)
96 uint8_t parent_name[512];
101 uint32_t data_length;
103 uint64_t data_offset;
107 typedef struct BDRVVPCState {
110 int max_table_entries;
115 uint8_t *pageentry_u8;
116 uint32_t *pageentry_u32;
117 uint16_t *pageentry_u16;
119 uint64_t last_bitmap;
123 static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename)
125 if (buf_size >= 8 && !strncmp((char *)buf, "conectix", 8))
130 static int vpc_open(BlockDriverState *bs, const char *filename, int flags)
132 BDRVVPCState *s = bs->opaque;
134 struct vhd_footer* footer;
135 struct vhd_dyndisk_header* dyndisk_header;
136 uint8_t buf[HEADER_SIZE];
138 fd = open(filename, O_RDONLY | O_BINARY);
142 bs->read_only = 1; // no write support yet
146 if (read(fd, buf, HEADER_SIZE) != HEADER_SIZE)
149 footer = (struct vhd_footer*) buf;
150 if (strncmp(footer->creator, "conectix", 8))
153 // The visible size of a image in Virtual PC depends on the geometry
154 // rather than on the size stored in the footer (the size in the footer
155 // is too large usually)
156 bs->total_sectors = (int64_t)
157 be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl;
159 lseek(s->fd, be64_to_cpu(footer->data_offset), SEEK_SET);
160 if (read(fd, buf, HEADER_SIZE) != HEADER_SIZE)
164 dyndisk_header = (struct vhd_dyndisk_header*) buf;
166 if (strncmp(dyndisk_header->magic, "cxsparse", 8))
169 lseek(s->fd, be64_to_cpu(dyndisk_header->table_offset), SEEK_SET);
171 s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries);
172 s->pagetable = qemu_malloc(s->max_table_entries * 4);
175 if (read(s->fd, s->pagetable, s->max_table_entries * 4) !=
176 s->max_table_entries * 4)
178 for (i = 0; i < s->max_table_entries; i++)
179 be32_to_cpus(&s->pagetable[i]);
181 s->block_size = be32_to_cpu(dyndisk_header->block_size);
183 s->pageentry_u8 = qemu_malloc(512);
184 if (!s->pageentry_u8)
186 s->pageentry_u32 = s->pageentry_u8;
187 s->pageentry_u16 = s->pageentry_u8;
188 s->last_pagetable = -1;
197 static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num)
199 BDRVVPCState *s = bs->opaque;
200 uint64_t offset = sector_num * 512;
201 uint64_t bitmap_offset, block_offset;
202 uint32_t pagetable_index, pageentry_index;
204 pagetable_index = offset / s->block_size;
205 pageentry_index = (offset % s->block_size) / 512;
207 if (pagetable_index > s->max_table_entries || s->pagetable[pagetable_index] == 0xffffffff)
208 return -1; // not allocated
210 bitmap_offset = 512 * s->pagetable[pagetable_index];
211 block_offset = bitmap_offset + 512 + (512 * pageentry_index);
213 // printf("sector: %" PRIx64 ", index: %x, offset: %x, bioff: %" PRIx64 ", bloff: %" PRIx64 "\n",
214 // sector_num, pagetable_index, pageentry_index,
215 // bitmap_offset, block_offset);
217 // disabled by reason
220 if (bitmap_offset != s->last_bitmap)
222 lseek(s->fd, bitmap_offset, SEEK_SET);
224 s->last_bitmap = bitmap_offset;
226 // Scary! Bitmap is stored as big endian 32bit entries,
227 // while we used to look it up byte by byte
228 read(s->fd, s->pageentry_u8, 512);
229 for (i = 0; i < 128; i++)
230 be32_to_cpus(&s->pageentry_u32[i]);
233 if ((s->pageentry_u8[pageentry_index / 8] >> (pageentry_index % 8)) & 1)
236 lseek(s->fd, bitmap_offset + (pageentry_index / 8), SEEK_SET);
238 read(s->fd, &bitmap_entry, 1);
240 if ((bitmap_entry >> (pageentry_index % 8)) & 1)
241 return -1; // not allocated
244 lseek(s->fd, block_offset, SEEK_SET);
249 static int vpc_read(BlockDriverState *bs, int64_t sector_num,
250 uint8_t *buf, int nb_sectors)
252 BDRVVPCState *s = bs->opaque;
255 while (nb_sectors > 0) {
256 if (!seek_to_sector(bs, sector_num))
258 ret = read(s->fd, buf, 512);
271 static void vpc_close(BlockDriverState *bs)
273 BDRVVPCState *s = bs->opaque;
274 qemu_free(s->pagetable);
276 qemu_free(s->pageentry_u8);
281 BlockDriver bdrv_vpc = {
283 sizeof(BDRVVPCState),