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