block-vpc: Adapt header structures to official documentation (Kevin Wolf)
[qemu/mini2440/sniper_sniper_test.git] / block-vpc.c
blob3f257859a7146bb548b01171f5680acbc95f614d
1 /*
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
22 * THE SOFTWARE.
24 #include "qemu-common.h"
25 #include "block_int.h"
27 /**************************************************************/
29 #define HEADER_SIZE 512
31 //#define CACHE
33 enum vhd_type {
34 VHD_FIXED = 2,
35 VHD_DYNAMIC = 3,
36 VHD_DIFFERENCING = 4,
39 // always big-endian
40 struct vhd_footer {
41 char creator[8]; // "conectix"
42 uint32_t features;
43 uint32_t version;
45 // Offset of next header structure, 0xFFFFFFFF if none
46 uint64_t data_offset;
48 // Seconds since Jan 1, 2000 0:00:00 (UTC)
49 uint32_t timestamp;
51 char creator_app[4]; // "vpc "
52 uint16_t major;
53 uint16_t minor;
54 char creator_os[4]; // "Wi2k"
56 uint64_t orig_size;
57 uint64_t size;
59 uint16_t cyls;
60 uint8_t heads;
61 uint8_t secs_per_cyl;
63 uint32_t type;
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")
67 uint32_t checksum;
69 // UUID used to identify a parent hard disk (backing file)
70 uint8_t uuid[16];
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
79 uint64_t data_offset;
81 // Offset of the Block Allocation Table (BAT)
82 uint64_t table_offset;
84 uint32_t version;
85 uint32_t max_table_entries; // 32bit/entry
87 // 2 MB by default, must be a power of two
88 uint32_t block_size;
90 uint32_t checksum;
91 uint8_t parent_uuid[16];
92 uint32_t parent_timestamp;
93 uint32_t reserved;
95 // Backing file name (in UTF-16)
96 uint8_t parent_name[512];
98 struct {
99 uint32_t platform;
100 uint32_t data_space;
101 uint32_t data_length;
102 uint32_t reserved;
103 uint64_t data_offset;
104 } parent_locator[8];
107 typedef struct BDRVVPCState {
108 int fd;
110 int max_table_entries;
111 uint32_t *pagetable;
113 uint32_t block_size;
114 #ifdef CACHE
115 uint8_t *pageentry_u8;
116 uint32_t *pageentry_u32;
117 uint16_t *pageentry_u16;
119 uint64_t last_bitmap;
120 #endif
121 } BDRVVPCState;
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))
126 return 100;
127 return 0;
130 static int vpc_open(BlockDriverState *bs, const char *filename, int flags)
132 BDRVVPCState *s = bs->opaque;
133 int fd, i;
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);
139 if (fd < 0)
140 return -1;
142 bs->read_only = 1; // no write support yet
144 s->fd = fd;
146 if (read(fd, buf, HEADER_SIZE) != HEADER_SIZE)
147 goto fail;
149 footer = (struct vhd_footer*) buf;
150 if (strncmp(footer->creator, "conectix", 8))
151 goto fail;
153 lseek(s->fd, be64_to_cpu(footer->data_offset), SEEK_SET);
154 if (read(fd, buf, HEADER_SIZE) != HEADER_SIZE)
155 goto fail;
157 footer = NULL;
158 dyndisk_header = (struct vhd_dyndisk_header*) buf;
160 if (strncmp(dyndisk_header->magic, "cxsparse", 8))
161 goto fail;
163 bs->total_sectors = ((uint64_t)be32_to_cpu(dyndisk_header->max_table_entries) *
164 be32_to_cpu(dyndisk_header->block_size)) / 512;
166 lseek(s->fd, be64_to_cpu(dyndisk_header->table_offset), SEEK_SET);
168 s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries);
169 s->pagetable = qemu_malloc(s->max_table_entries * 4);
170 if (!s->pagetable)
171 goto fail;
172 if (read(s->fd, s->pagetable, s->max_table_entries * 4) !=
173 s->max_table_entries * 4)
174 goto fail;
175 for (i = 0; i < s->max_table_entries; i++)
176 be32_to_cpus(&s->pagetable[i]);
178 s->block_size = be32_to_cpu(dyndisk_header->block_size);
179 #ifdef CACHE
180 s->pageentry_u8 = qemu_malloc(512);
181 if (!s->pageentry_u8)
182 goto fail;
183 s->pageentry_u32 = s->pageentry_u8;
184 s->pageentry_u16 = s->pageentry_u8;
185 s->last_pagetable = -1;
186 #endif
188 return 0;
189 fail:
190 close(fd);
191 return -1;
194 static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num)
196 BDRVVPCState *s = bs->opaque;
197 uint64_t offset = sector_num * 512;
198 uint64_t bitmap_offset, block_offset;
199 uint32_t pagetable_index, pageentry_index;
201 pagetable_index = offset / s->block_size;
202 pageentry_index = (offset % s->block_size) / 512;
204 if (pagetable_index > s->max_table_entries || s->pagetable[pagetable_index] == 0xffffffff)
205 return -1; // not allocated
207 bitmap_offset = 512 * s->pagetable[pagetable_index];
208 block_offset = bitmap_offset + 512 + (512 * pageentry_index);
210 // printf("sector: %" PRIx64 ", index: %x, offset: %x, bioff: %" PRIx64 ", bloff: %" PRIx64 "\n",
211 // sector_num, pagetable_index, pageentry_index,
212 // bitmap_offset, block_offset);
214 // disabled by reason
215 #if 0
216 #ifdef CACHE
217 if (bitmap_offset != s->last_bitmap)
219 lseek(s->fd, bitmap_offset, SEEK_SET);
221 s->last_bitmap = bitmap_offset;
223 // Scary! Bitmap is stored as big endian 32bit entries,
224 // while we used to look it up byte by byte
225 read(s->fd, s->pageentry_u8, 512);
226 for (i = 0; i < 128; i++)
227 be32_to_cpus(&s->pageentry_u32[i]);
230 if ((s->pageentry_u8[pageentry_index / 8] >> (pageentry_index % 8)) & 1)
231 return -1;
232 #else
233 lseek(s->fd, bitmap_offset + (pageentry_index / 8), SEEK_SET);
235 read(s->fd, &bitmap_entry, 1);
237 if ((bitmap_entry >> (pageentry_index % 8)) & 1)
238 return -1; // not allocated
239 #endif
240 #endif
241 lseek(s->fd, block_offset, SEEK_SET);
243 return 0;
246 static int vpc_read(BlockDriverState *bs, int64_t sector_num,
247 uint8_t *buf, int nb_sectors)
249 BDRVVPCState *s = bs->opaque;
250 int ret;
252 while (nb_sectors > 0) {
253 if (!seek_to_sector(bs, sector_num))
255 ret = read(s->fd, buf, 512);
256 if (ret != 512)
257 return -1;
259 else
260 memset(buf, 0, 512);
261 nb_sectors--;
262 sector_num++;
263 buf += 512;
265 return 0;
268 static void vpc_close(BlockDriverState *bs)
270 BDRVVPCState *s = bs->opaque;
271 qemu_free(s->pagetable);
272 #ifdef CACHE
273 qemu_free(s->pageentry_u8);
274 #endif
275 close(s->fd);
278 BlockDriver bdrv_vpc = {
279 "vpc",
280 sizeof(BDRVVPCState),
281 vpc_probe,
282 vpc_open,
283 vpc_read,
284 NULL,
285 vpc_close,