|
1 /* |
|
2 * Block driver for Conectix/Microsoft Virtual PC images |
|
3 * |
|
4 * Copyright (c) 2005 Alex Beregszaszi |
|
5 * |
|
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: |
|
12 * |
|
13 * The above copyright notice and this permission notice shall be included in |
|
14 * all copies or substantial portions of the Software. |
|
15 * |
|
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. |
|
23 */ |
|
24 #include "qemu-common.h" |
|
25 #include "block_int.h" |
|
26 |
|
27 /**************************************************************/ |
|
28 |
|
29 #define HEADER_SIZE 512 |
|
30 |
|
31 //#define CACHE |
|
32 |
|
33 // always big-endian |
|
34 struct vpc_subheader { |
|
35 char magic[8]; // "conectix" / "cxsparse" |
|
36 union { |
|
37 struct { |
|
38 uint32_t unk1[2]; |
|
39 uint32_t unk2; // always zero? |
|
40 uint32_t subheader_offset; |
|
41 uint32_t unk3; // some size? |
|
42 char creator[4]; // "vpc " |
|
43 uint16_t major; |
|
44 uint16_t minor; |
|
45 char guest[4]; // "Wi2k" |
|
46 uint32_t unk4[7]; |
|
47 uint8_t vnet_id[16]; // virtual network id, purpose unknown |
|
48 // next 16 longs are used, but dunno the purpose |
|
49 // next 6 longs unknown, following 7 long maybe a serial |
|
50 char padding[HEADER_SIZE - 84]; |
|
51 } main; |
|
52 struct { |
|
53 uint32_t unk1[2]; // all bits set |
|
54 uint32_t unk2; // always zero? |
|
55 uint32_t pagetable_offset; |
|
56 uint32_t unk3; |
|
57 uint32_t pagetable_entries; // 32bit/entry |
|
58 uint32_t pageentry_size; // 512*8*512 |
|
59 uint32_t nb_sectors; |
|
60 char padding[HEADER_SIZE - 40]; |
|
61 } sparse; |
|
62 char padding[HEADER_SIZE - 8]; |
|
63 } type; |
|
64 }; |
|
65 |
|
66 typedef struct BDRVVPCState { |
|
67 int fd; |
|
68 |
|
69 int pagetable_entries; |
|
70 uint32_t *pagetable; |
|
71 |
|
72 uint32_t pageentry_size; |
|
73 #ifdef CACHE |
|
74 uint8_t *pageentry_u8; |
|
75 uint32_t *pageentry_u32; |
|
76 uint16_t *pageentry_u16; |
|
77 |
|
78 uint64_t last_bitmap; |
|
79 #endif |
|
80 } BDRVVPCState; |
|
81 |
|
82 static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename) |
|
83 { |
|
84 if (buf_size >= 8 && !strncmp((char *)buf, "conectix", 8)) |
|
85 return 100; |
|
86 return 0; |
|
87 } |
|
88 |
|
89 static int vpc_open(BlockDriverState *bs, const char *filename, int flags) |
|
90 { |
|
91 BDRVVPCState *s = bs->opaque; |
|
92 int fd, i; |
|
93 struct vpc_subheader header; |
|
94 |
|
95 fd = open(filename, O_RDONLY | O_BINARY); |
|
96 if (fd < 0) |
|
97 return -1; |
|
98 |
|
99 bs->read_only = 1; // no write support yet |
|
100 |
|
101 s->fd = fd; |
|
102 |
|
103 if (read(fd, &header, HEADER_SIZE) != HEADER_SIZE) |
|
104 goto fail; |
|
105 |
|
106 if (strncmp(header.magic, "conectix", 8)) |
|
107 goto fail; |
|
108 lseek(s->fd, be32_to_cpu(header.type.main.subheader_offset), SEEK_SET); |
|
109 |
|
110 if (read(fd, &header, HEADER_SIZE) != HEADER_SIZE) |
|
111 goto fail; |
|
112 |
|
113 if (strncmp(header.magic, "cxsparse", 8)) |
|
114 goto fail; |
|
115 |
|
116 bs->total_sectors = ((uint64_t)be32_to_cpu(header.type.sparse.pagetable_entries) * |
|
117 be32_to_cpu(header.type.sparse.pageentry_size)) / 512; |
|
118 |
|
119 lseek(s->fd, be32_to_cpu(header.type.sparse.pagetable_offset), SEEK_SET); |
|
120 |
|
121 s->pagetable_entries = be32_to_cpu(header.type.sparse.pagetable_entries); |
|
122 s->pagetable = qemu_malloc(s->pagetable_entries * 4); |
|
123 if (!s->pagetable) |
|
124 goto fail; |
|
125 if (read(s->fd, s->pagetable, s->pagetable_entries * 4) != |
|
126 s->pagetable_entries * 4) |
|
127 goto fail; |
|
128 for (i = 0; i < s->pagetable_entries; i++) |
|
129 be32_to_cpus(&s->pagetable[i]); |
|
130 |
|
131 s->pageentry_size = be32_to_cpu(header.type.sparse.pageentry_size); |
|
132 #ifdef CACHE |
|
133 s->pageentry_u8 = qemu_malloc(512); |
|
134 if (!s->pageentry_u8) |
|
135 goto fail; |
|
136 s->pageentry_u32 = s->pageentry_u8; |
|
137 s->pageentry_u16 = s->pageentry_u8; |
|
138 s->last_pagetable = -1; |
|
139 #endif |
|
140 |
|
141 return 0; |
|
142 fail: |
|
143 close(fd); |
|
144 return -1; |
|
145 } |
|
146 |
|
147 static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num) |
|
148 { |
|
149 BDRVVPCState *s = bs->opaque; |
|
150 uint64_t offset = sector_num * 512; |
|
151 uint64_t bitmap_offset, block_offset; |
|
152 uint32_t pagetable_index, pageentry_index; |
|
153 |
|
154 pagetable_index = offset / s->pageentry_size; |
|
155 pageentry_index = (offset % s->pageentry_size) / 512; |
|
156 |
|
157 if (pagetable_index > s->pagetable_entries || s->pagetable[pagetable_index] == 0xffffffff) |
|
158 return -1; // not allocated |
|
159 |
|
160 bitmap_offset = 512 * s->pagetable[pagetable_index]; |
|
161 block_offset = bitmap_offset + 512 + (512 * pageentry_index); |
|
162 |
|
163 // printf("sector: %" PRIx64 ", index: %x, offset: %x, bioff: %" PRIx64 ", bloff: %" PRIx64 "\n", |
|
164 // sector_num, pagetable_index, pageentry_index, |
|
165 // bitmap_offset, block_offset); |
|
166 |
|
167 // disabled by reason |
|
168 #if 0 |
|
169 #ifdef CACHE |
|
170 if (bitmap_offset != s->last_bitmap) |
|
171 { |
|
172 lseek(s->fd, bitmap_offset, SEEK_SET); |
|
173 |
|
174 s->last_bitmap = bitmap_offset; |
|
175 |
|
176 // Scary! Bitmap is stored as big endian 32bit entries, |
|
177 // while we used to look it up byte by byte |
|
178 read(s->fd, s->pageentry_u8, 512); |
|
179 for (i = 0; i < 128; i++) |
|
180 be32_to_cpus(&s->pageentry_u32[i]); |
|
181 } |
|
182 |
|
183 if ((s->pageentry_u8[pageentry_index / 8] >> (pageentry_index % 8)) & 1) |
|
184 return -1; |
|
185 #else |
|
186 lseek(s->fd, bitmap_offset + (pageentry_index / 8), SEEK_SET); |
|
187 |
|
188 read(s->fd, &bitmap_entry, 1); |
|
189 |
|
190 if ((bitmap_entry >> (pageentry_index % 8)) & 1) |
|
191 return -1; // not allocated |
|
192 #endif |
|
193 #endif |
|
194 lseek(s->fd, block_offset, SEEK_SET); |
|
195 |
|
196 return 0; |
|
197 } |
|
198 |
|
199 static int vpc_read(BlockDriverState *bs, int64_t sector_num, |
|
200 uint8_t *buf, int nb_sectors) |
|
201 { |
|
202 BDRVVPCState *s = bs->opaque; |
|
203 int ret; |
|
204 |
|
205 while (nb_sectors > 0) { |
|
206 if (!seek_to_sector(bs, sector_num)) |
|
207 { |
|
208 ret = read(s->fd, buf, 512); |
|
209 if (ret != 512) |
|
210 return -1; |
|
211 } |
|
212 else |
|
213 memset(buf, 0, 512); |
|
214 nb_sectors--; |
|
215 sector_num++; |
|
216 buf += 512; |
|
217 } |
|
218 return 0; |
|
219 } |
|
220 |
|
221 static void vpc_close(BlockDriverState *bs) |
|
222 { |
|
223 BDRVVPCState *s = bs->opaque; |
|
224 qemu_free(s->pagetable); |
|
225 #ifdef CACHE |
|
226 qemu_free(s->pageentry_u8); |
|
227 #endif |
|
228 close(s->fd); |
|
229 } |
|
230 |
|
231 BlockDriver bdrv_vpc = { |
|
232 "vpc", |
|
233 sizeof(BDRVVPCState), |
|
234 vpc_probe, |
|
235 vpc_open, |
|
236 vpc_read, |
|
237 NULL, |
|
238 vpc_close, |
|
239 }; |