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 lseek(s
->fd
, be64_to_cpu(footer
->data_offset
), SEEK_SET
);
154 if (read(fd
, buf
, HEADER_SIZE
) != HEADER_SIZE
)
158 dyndisk_header
= (struct vhd_dyndisk_header
*) buf
;
160 if (strncmp(dyndisk_header
->magic
, "cxsparse", 8))
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);
172 if (read(s
->fd
, s
->pagetable
, s
->max_table_entries
* 4) !=
173 s
->max_table_entries
* 4)
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
);
180 s
->pageentry_u8
= qemu_malloc(512);
181 if (!s
->pageentry_u8
)
183 s
->pageentry_u32
= s
->pageentry_u8
;
184 s
->pageentry_u16
= s
->pageentry_u8
;
185 s
->last_pagetable
= -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
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)
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
241 lseek(s
->fd
, block_offset
, SEEK_SET
);
246 static int vpc_read(BlockDriverState
*bs
, int64_t sector_num
,
247 uint8_t *buf
, int nb_sectors
)
249 BDRVVPCState
*s
= bs
->opaque
;
252 while (nb_sectors
> 0) {
253 if (!seek_to_sector(bs
, sector_num
))
255 ret
= read(s
->fd
, buf
, 512);
268 static void vpc_close(BlockDriverState
*bs
)
270 BDRVVPCState
*s
= bs
->opaque
;
271 qemu_free(s
->pagetable
);
273 qemu_free(s
->pageentry_u8
);
278 BlockDriver bdrv_vpc
= {
280 sizeof(BDRVVPCState
),