1019d6b8fSAnthony Liguori /*
2cc2040f8SStefan Weil * Block driver for Connectix / Microsoft Virtual PC images
3019d6b8fSAnthony Liguori *
4019d6b8fSAnthony Liguori * Copyright (c) 2005 Alex Beregszaszi
5019d6b8fSAnthony Liguori * Copyright (c) 2009 Kevin Wolf <kwolf@suse.de>
6019d6b8fSAnthony Liguori *
7019d6b8fSAnthony Liguori * Permission is hereby granted, free of charge, to any person obtaining a copy
8019d6b8fSAnthony Liguori * of this software and associated documentation files (the "Software"), to deal
9019d6b8fSAnthony Liguori * in the Software without restriction, including without limitation the rights
10019d6b8fSAnthony Liguori * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11019d6b8fSAnthony Liguori * copies of the Software, and to permit persons to whom the Software is
12019d6b8fSAnthony Liguori * furnished to do so, subject to the following conditions:
13019d6b8fSAnthony Liguori *
14019d6b8fSAnthony Liguori * The above copyright notice and this permission notice shall be included in
15019d6b8fSAnthony Liguori * all copies or substantial portions of the Software.
16019d6b8fSAnthony Liguori *
17019d6b8fSAnthony Liguori * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18019d6b8fSAnthony Liguori * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19019d6b8fSAnthony Liguori * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20019d6b8fSAnthony Liguori * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21019d6b8fSAnthony Liguori * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22019d6b8fSAnthony Liguori * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23019d6b8fSAnthony Liguori * THE SOFTWARE.
24019d6b8fSAnthony Liguori */
25922a01a0SMarkus Armbruster
2680c71a24SPeter Maydell #include "qemu/osdep.h"
27da34e65cSMarkus Armbruster #include "qapi/error.h"
28737e150eSPaolo Bonzini #include "block/block_int.h"
29609f45eaSMax Reitz #include "block/qdict.h"
30b8f45cdfSKevin Wolf #include "sysemu/block-backend.h"
311de7afc9SPaolo Bonzini #include "qemu/module.h"
32922a01a0SMarkus Armbruster #include "qemu/option.h"
33795c40b8SJuan Quintela #include "migration/blocker.h"
3458369e22SPaolo Bonzini #include "qemu/bswap.h"
3538440a21SFam Zheng #include "qemu/uuid.h"
365df022cfSPeter Maydell #include "qemu/memalign.h"
37182c8835SKevin Wolf #include "qapi/qmp/qdict.h"
38182c8835SKevin Wolf #include "qapi/qobject-input-visitor.h"
39182c8835SKevin Wolf #include "qapi/qapi-visit-block-core.h"
40019d6b8fSAnthony Liguori
41019d6b8fSAnthony Liguori /**************************************************************/
42019d6b8fSAnthony Liguori
43019d6b8fSAnthony Liguori //#define CACHE
44019d6b8fSAnthony Liguori
45019d6b8fSAnthony Liguori enum vhd_type {
46019d6b8fSAnthony Liguori VHD_FIXED = 2,
47019d6b8fSAnthony Liguori VHD_DYNAMIC = 3,
48019d6b8fSAnthony Liguori VHD_DIFFERENCING = 4,
49019d6b8fSAnthony Liguori };
50019d6b8fSAnthony Liguori
519c057d0bSJeff Cody /* Seconds since Jan 1, 2000 0:00:00 (UTC) */
52019d6b8fSAnthony Liguori #define VHD_TIMESTAMP_BASE 946684800
53019d6b8fSAnthony Liguori
54fb9245c2SJeff Cody #define VHD_CHS_MAX_C 65535LL
55fb9245c2SJeff Cody #define VHD_CHS_MAX_H 16
56fb9245c2SJeff Cody #define VHD_CHS_MAX_S 255
57fb9245c2SJeff Cody
58c23fb11bSJeff Cody #define VHD_MAX_SECTORS 0xff000000 /* 2040 GiB max image size */
59fb9245c2SJeff Cody #define VHD_MAX_GEOMETRY (VHD_CHS_MAX_C * VHD_CHS_MAX_H * VHD_CHS_MAX_S)
60fb9245c2SJeff Cody
61fb9245c2SJeff Cody #define VPC_OPT_FORCE_SIZE "force_size"
6297f1c45cSJeff Cody
639c057d0bSJeff Cody /* always big-endian */
64e54835c0SJeff Cody typedef struct vhd_footer {
659c057d0bSJeff Cody char creator[8]; /* "conectix" */
66019d6b8fSAnthony Liguori uint32_t features;
67019d6b8fSAnthony Liguori uint32_t version;
68019d6b8fSAnthony Liguori
699c057d0bSJeff Cody /* Offset of next header structure, 0xFFFFFFFF if none */
70019d6b8fSAnthony Liguori uint64_t data_offset;
71019d6b8fSAnthony Liguori
729c057d0bSJeff Cody /* Seconds since Jan 1, 2000 0:00:00 (UTC) */
73019d6b8fSAnthony Liguori uint32_t timestamp;
74019d6b8fSAnthony Liguori
759c057d0bSJeff Cody char creator_app[4]; /* e.g., "vpc " */
76019d6b8fSAnthony Liguori uint16_t major;
77019d6b8fSAnthony Liguori uint16_t minor;
789c057d0bSJeff Cody char creator_os[4]; /* "Wi2k" */
79019d6b8fSAnthony Liguori
80019d6b8fSAnthony Liguori uint64_t orig_size;
8103671dedSPeter Lieven uint64_t current_size;
82019d6b8fSAnthony Liguori
83019d6b8fSAnthony Liguori uint16_t cyls;
84019d6b8fSAnthony Liguori uint8_t heads;
85019d6b8fSAnthony Liguori uint8_t secs_per_cyl;
86019d6b8fSAnthony Liguori
87019d6b8fSAnthony Liguori uint32_t type;
88019d6b8fSAnthony Liguori
899c057d0bSJeff Cody /* Checksum of the Hard Disk Footer ("one's complement of the sum of all
909c057d0bSJeff Cody the bytes in the footer without the checksum field") */
91019d6b8fSAnthony Liguori uint32_t checksum;
92019d6b8fSAnthony Liguori
939c057d0bSJeff Cody /* UUID used to identify a parent hard disk (backing file) */
9438440a21SFam Zheng QemuUUID uuid;
95019d6b8fSAnthony Liguori
96019d6b8fSAnthony Liguori uint8_t in_saved_state;
97275734e4SMarkus Armbruster uint8_t reserved[427];
98e54835c0SJeff Cody } QEMU_PACKED VHDFooter;
99019d6b8fSAnthony Liguori
100275734e4SMarkus Armbruster QEMU_BUILD_BUG_ON(sizeof(VHDFooter) != 512);
101275734e4SMarkus Armbruster
102e54835c0SJeff Cody typedef struct vhd_dyndisk_header {
1039c057d0bSJeff Cody char magic[8]; /* "cxsparse" */
104019d6b8fSAnthony Liguori
1059c057d0bSJeff Cody /* Offset of next header structure, 0xFFFFFFFF if none */
106019d6b8fSAnthony Liguori uint64_t data_offset;
107019d6b8fSAnthony Liguori
1089c057d0bSJeff Cody /* Offset of the Block Allocation Table (BAT) */
109019d6b8fSAnthony Liguori uint64_t table_offset;
110019d6b8fSAnthony Liguori
111019d6b8fSAnthony Liguori uint32_t version;
1129c057d0bSJeff Cody uint32_t max_table_entries; /* 32bit/entry */
113019d6b8fSAnthony Liguori
1149c057d0bSJeff Cody /* 2 MB by default, must be a power of two */
115019d6b8fSAnthony Liguori uint32_t block_size;
116019d6b8fSAnthony Liguori
117019d6b8fSAnthony Liguori uint32_t checksum;
118019d6b8fSAnthony Liguori uint8_t parent_uuid[16];
119019d6b8fSAnthony Liguori uint32_t parent_timestamp;
120019d6b8fSAnthony Liguori uint32_t reserved;
121019d6b8fSAnthony Liguori
1229c057d0bSJeff Cody /* Backing file name (in UTF-16) */
123019d6b8fSAnthony Liguori uint8_t parent_name[512];
124019d6b8fSAnthony Liguori
125019d6b8fSAnthony Liguori struct {
126019d6b8fSAnthony Liguori uint32_t platform;
127019d6b8fSAnthony Liguori uint32_t data_space;
128019d6b8fSAnthony Liguori uint32_t data_length;
129019d6b8fSAnthony Liguori uint32_t reserved;
130019d6b8fSAnthony Liguori uint64_t data_offset;
131019d6b8fSAnthony Liguori } parent_locator[8];
132e326f078SMarkus Armbruster uint8_t reserved2[256];
133e54835c0SJeff Cody } QEMU_PACKED VHDDynDiskHeader;
134019d6b8fSAnthony Liguori
135e326f078SMarkus Armbruster QEMU_BUILD_BUG_ON(sizeof(VHDDynDiskHeader) != 1024);
136e326f078SMarkus Armbruster
137019d6b8fSAnthony Liguori typedef struct BDRVVPCState {
138848c66e8SPaolo Bonzini CoMutex lock;
139275734e4SMarkus Armbruster VHDFooter footer;
140019d6b8fSAnthony Liguori uint64_t free_data_block_offset;
141019d6b8fSAnthony Liguori int max_table_entries;
142019d6b8fSAnthony Liguori uint32_t *pagetable;
143019d6b8fSAnthony Liguori uint64_t bat_offset;
144019d6b8fSAnthony Liguori uint64_t last_bitmap_offset;
145019d6b8fSAnthony Liguori
146019d6b8fSAnthony Liguori uint32_t block_size;
147019d6b8fSAnthony Liguori uint32_t bitmap_size;
148c540d53aSJeff Cody bool force_use_chs;
149c540d53aSJeff Cody bool force_use_sz;
150019d6b8fSAnthony Liguori
151019d6b8fSAnthony Liguori #ifdef CACHE
152019d6b8fSAnthony Liguori uint8_t *pageentry_u8;
153019d6b8fSAnthony Liguori uint32_t *pageentry_u32;
154019d6b8fSAnthony Liguori uint16_t *pageentry_u16;
155019d6b8fSAnthony Liguori
156019d6b8fSAnthony Liguori uint64_t last_bitmap;
157019d6b8fSAnthony Liguori #endif
158612ff3d8SKevin Wolf
159612ff3d8SKevin Wolf Error *migration_blocker;
160019d6b8fSAnthony Liguori } BDRVVPCState;
161019d6b8fSAnthony Liguori
162c540d53aSJeff Cody #define VPC_OPT_SIZE_CALC "force_size_calc"
163c540d53aSJeff Cody static QemuOptsList vpc_runtime_opts = {
164c540d53aSJeff Cody .name = "vpc-runtime-opts",
165c540d53aSJeff Cody .head = QTAILQ_HEAD_INITIALIZER(vpc_runtime_opts.head),
166c540d53aSJeff Cody .desc = {
167c540d53aSJeff Cody {
168c540d53aSJeff Cody .name = VPC_OPT_SIZE_CALC,
169c540d53aSJeff Cody .type = QEMU_OPT_STRING,
170c540d53aSJeff Cody .help = "Force disk size calculation to use either CHS geometry, "
171c540d53aSJeff Cody "or use the disk current_size specified in the VHD footer. "
172c540d53aSJeff Cody "{chs, current_size}"
173c540d53aSJeff Cody },
174c540d53aSJeff Cody { /* end of list */ }
175c540d53aSJeff Cody }
176c540d53aSJeff Cody };
177c540d53aSJeff Cody
178182c8835SKevin Wolf static QemuOptsList vpc_create_opts;
179182c8835SKevin Wolf
vpc_checksum(void * p,size_t size)1807550379dSMarkus Armbruster static uint32_t vpc_checksum(void *p, size_t size)
181019d6b8fSAnthony Liguori {
1827550379dSMarkus Armbruster uint8_t *buf = p;
183019d6b8fSAnthony Liguori uint32_t res = 0;
184019d6b8fSAnthony Liguori int i;
185019d6b8fSAnthony Liguori
186019d6b8fSAnthony Liguori for (i = 0; i < size; i++)
187019d6b8fSAnthony Liguori res += buf[i];
188019d6b8fSAnthony Liguori
189019d6b8fSAnthony Liguori return ~res;
190019d6b8fSAnthony Liguori }
191019d6b8fSAnthony Liguori
192019d6b8fSAnthony Liguori
vpc_probe(const uint8_t * buf,int buf_size,const char * filename)193019d6b8fSAnthony Liguori static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename)
194019d6b8fSAnthony Liguori {
195019d6b8fSAnthony Liguori if (buf_size >= 8 && !strncmp((char *)buf, "conectix", 8))
196019d6b8fSAnthony Liguori return 100;
197019d6b8fSAnthony Liguori return 0;
198019d6b8fSAnthony Liguori }
199019d6b8fSAnthony Liguori
vpc_parse_options(BlockDriverState * bs,QemuOpts * opts,Error ** errp)200c540d53aSJeff Cody static void vpc_parse_options(BlockDriverState *bs, QemuOpts *opts,
201c540d53aSJeff Cody Error **errp)
202c540d53aSJeff Cody {
203c540d53aSJeff Cody BDRVVPCState *s = bs->opaque;
204c540d53aSJeff Cody const char *size_calc;
205c540d53aSJeff Cody
206c540d53aSJeff Cody size_calc = qemu_opt_get(opts, VPC_OPT_SIZE_CALC);
207c540d53aSJeff Cody
208c540d53aSJeff Cody if (!size_calc) {
209c540d53aSJeff Cody /* no override, use autodetect only */
210c540d53aSJeff Cody } else if (!strcmp(size_calc, "current_size")) {
211c540d53aSJeff Cody s->force_use_sz = true;
212c540d53aSJeff Cody } else if (!strcmp(size_calc, "chs")) {
213c540d53aSJeff Cody s->force_use_chs = true;
214c540d53aSJeff Cody } else {
215c540d53aSJeff Cody error_setg(errp, "Invalid size calculation mode: '%s'", size_calc);
216c540d53aSJeff Cody }
217c540d53aSJeff Cody }
218c540d53aSJeff Cody
vpc_open(BlockDriverState * bs,QDict * options,int flags,Error ** errp)219015a1036SMax Reitz static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
220015a1036SMax Reitz Error **errp)
221019d6b8fSAnthony Liguori {
222019d6b8fSAnthony Liguori BDRVVPCState *s = bs->opaque;
22366f82ceeSKevin Wolf int i;
224e54835c0SJeff Cody VHDFooter *footer;
225c540d53aSJeff Cody QemuOpts *opts = NULL;
226c540d53aSJeff Cody Error *local_err = NULL;
227c540d53aSJeff Cody bool use_chs;
228e326f078SMarkus Armbruster VHDDynDiskHeader dyndisk_header;
229019d6b8fSAnthony Liguori uint32_t checksum;
23097f1c45cSJeff Cody uint64_t computed_size;
231b15deac7SJeff Cody uint64_t pagetable_size;
23224da78dbSCharles Arnold int disk_type = VHD_DYNAMIC;
23359294e46SKevin Wolf int ret;
23481caa3ccSEric Blake int64_t bs_size;
235019d6b8fSAnthony Liguori
23683930780SVladimir Sementsov-Ogievskiy ret = bdrv_open_file_child(NULL, options, "file", bs, errp);
23783930780SVladimir Sementsov-Ogievskiy if (ret < 0) {
23883930780SVladimir Sementsov-Ogievskiy return ret;
2394e4bf5c4SKevin Wolf }
2404e4bf5c4SKevin Wolf
241a4b740dbSKevin Wolf GRAPH_RDLOCK_GUARD_MAINLOOP();
242a4b740dbSKevin Wolf
243c540d53aSJeff Cody opts = qemu_opts_create(&vpc_runtime_opts, NULL, 0, &error_abort);
244af175e85SMarkus Armbruster if (!qemu_opts_absorb_qdict(opts, options, errp)) {
245c540d53aSJeff Cody ret = -EINVAL;
246c540d53aSJeff Cody goto fail;
247c540d53aSJeff Cody }
248c540d53aSJeff Cody
249c540d53aSJeff Cody vpc_parse_options(bs, opts, &local_err);
250c540d53aSJeff Cody if (local_err) {
251c540d53aSJeff Cody error_propagate(errp, local_err);
252c540d53aSJeff Cody ret = -EINVAL;
253c540d53aSJeff Cody goto fail;
254c540d53aSJeff Cody }
255c540d53aSJeff Cody
25632cc71deSAlberto Faria ret = bdrv_pread(bs->file, 0, sizeof(s->footer), &s->footer, 0);
25759294e46SKevin Wolf if (ret < 0) {
25832f6439cSJeff Cody error_setg(errp, "Unable to read VHD header");
259019d6b8fSAnthony Liguori goto fail;
26059294e46SKevin Wolf }
261019d6b8fSAnthony Liguori
262275734e4SMarkus Armbruster footer = &s->footer;
26324da78dbSCharles Arnold if (strncmp(footer->creator, "conectix", 8)) {
2649a4f4c31SKevin Wolf int64_t offset = bdrv_getlength(bs->file->bs);
26559294e46SKevin Wolf if (offset < 0) {
26659294e46SKevin Wolf ret = offset;
26732f6439cSJeff Cody error_setg(errp, "Invalid file size");
26859294e46SKevin Wolf goto fail;
269be7c5dddSMarkus Armbruster } else if (offset < sizeof(*footer)) {
27059294e46SKevin Wolf ret = -EINVAL;
27132f6439cSJeff Cody error_setg(errp, "File too small for a VHD header");
272019d6b8fSAnthony Liguori goto fail;
27324da78dbSCharles Arnold }
27459294e46SKevin Wolf
27524da78dbSCharles Arnold /* If a fixed disk, the footer is found only at the end of the file */
27632cc71deSAlberto Faria ret = bdrv_pread(bs->file, offset - sizeof(*footer), sizeof(*footer),
27732cc71deSAlberto Faria footer, 0);
27859294e46SKevin Wolf if (ret < 0) {
27924da78dbSCharles Arnold goto fail;
28024da78dbSCharles Arnold }
2817da9623cSThomas Huth if (strncmp(footer->creator, "conectix", 8) ||
2827da9623cSThomas Huth be32_to_cpu(footer->type) != VHD_FIXED) {
28376abe407SPaolo Bonzini error_setg(errp, "invalid VPC image");
28476abe407SPaolo Bonzini ret = -EINVAL;
28524da78dbSCharles Arnold goto fail;
28624da78dbSCharles Arnold }
28724da78dbSCharles Arnold disk_type = VHD_FIXED;
28824da78dbSCharles Arnold }
289019d6b8fSAnthony Liguori
290019d6b8fSAnthony Liguori checksum = be32_to_cpu(footer->checksum);
291019d6b8fSAnthony Liguori footer->checksum = 0;
292be7c5dddSMarkus Armbruster if (vpc_checksum(footer, sizeof(*footer)) != checksum) {
29304788ba2SMarkus Armbruster error_setg(errp, "Incorrect header checksum");
29404788ba2SMarkus Armbruster ret = -EINVAL;
29504788ba2SMarkus Armbruster goto fail;
29604788ba2SMarkus Armbruster }
297019d6b8fSAnthony Liguori
298c088b691SZhang Shengju /* Write 'checksum' back to footer, or else will leave it with zero. */
299a4127c42SStefan Hajnoczi footer->checksum = cpu_to_be32(checksum);
300c088b691SZhang Shengju
3019c057d0bSJeff Cody /* The visible size of a image in Virtual PC depends on the geometry
3029c057d0bSJeff Cody rather than on the size stored in the footer (the size in the footer
3039c057d0bSJeff Cody is too large usually) */
30433ccf667SStefan Hajnoczi bs->total_sectors = (int64_t)
30533ccf667SStefan Hajnoczi be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl;
306019d6b8fSAnthony Liguori
307c540d53aSJeff Cody /* Microsoft Virtual PC and Microsoft Hyper-V produce and read
308c540d53aSJeff Cody * VHD image sizes differently. VPC will rely on CHS geometry,
309c540d53aSJeff Cody * while Hyper-V and disk2vhd use the size specified in the footer.
310c540d53aSJeff Cody *
311c540d53aSJeff Cody * We use a couple of approaches to try and determine the correct method:
312c540d53aSJeff Cody * look at the Creator App field, and look for images that have CHS
313c540d53aSJeff Cody * geometry that is the maximum value.
314c540d53aSJeff Cody *
315c540d53aSJeff Cody * If the CHS geometry is the maximum CHS geometry, then we assume that
316c540d53aSJeff Cody * the size is the footer->current_size to avoid truncation. Otherwise,
317c540d53aSJeff Cody * we follow the table based on footer->creator_app:
318c540d53aSJeff Cody *
319c540d53aSJeff Cody * Known creator apps:
320c540d53aSJeff Cody * 'vpc ' : CHS Virtual PC (uses disk geometry)
321c540d53aSJeff Cody * 'qemu' : CHS QEMU (uses disk geometry)
322fb9245c2SJeff Cody * 'qem2' : current_size QEMU (uses current_size)
323c540d53aSJeff Cody * 'win ' : current_size Hyper-V
324c540d53aSJeff Cody * 'd2v ' : current_size Disk2vhd
3259bdfb9e8SStefan Hajnoczi * 'tap\0' : current_size XenServer
326bab246dbSJeff Cody * 'CTXS' : current_size XenConverter
327c540d53aSJeff Cody *
328c540d53aSJeff Cody * The user can override the table values via drive options, however
329c540d53aSJeff Cody * even with an override we will still use current_size for images
330c540d53aSJeff Cody * that have CHS geometry of the maximum size.
331c540d53aSJeff Cody */
332c540d53aSJeff Cody use_chs = (!!strncmp(footer->creator_app, "win ", 4) &&
333fb9245c2SJeff Cody !!strncmp(footer->creator_app, "qem2", 4) &&
3349bdfb9e8SStefan Hajnoczi !!strncmp(footer->creator_app, "d2v ", 4) &&
335bab246dbSJeff Cody !!strncmp(footer->creator_app, "CTXS", 4) &&
3369bdfb9e8SStefan Hajnoczi !!memcmp(footer->creator_app, "tap", 4)) || s->force_use_chs;
337c540d53aSJeff Cody
338c540d53aSJeff Cody if (!use_chs || bs->total_sectors == VHD_MAX_GEOMETRY || s->force_use_sz) {
33903671dedSPeter Lieven bs->total_sectors = be64_to_cpu(footer->current_size) /
34003671dedSPeter Lieven BDRV_SECTOR_SIZE;
3410173e7bbSPeter Lieven }
3420173e7bbSPeter Lieven
343c23fb11bSJeff Cody /* Allow a maximum disk size of 2040 GiB */
344c23fb11bSJeff Cody if (bs->total_sectors > VHD_MAX_SECTORS) {
34559294e46SKevin Wolf ret = -EFBIG;
346efc8243dSSerge E. Hallyn goto fail;
347efc8243dSSerge E. Hallyn }
348efc8243dSSerge E. Hallyn
34924da78dbSCharles Arnold if (disk_type == VHD_DYNAMIC) {
35002df95c4SMarkus Armbruster ret = bdrv_pread(bs->file, be64_to_cpu(footer->data_offset),
35132cc71deSAlberto Faria sizeof(dyndisk_header), &dyndisk_header, 0);
35259294e46SKevin Wolf if (ret < 0) {
35332f6439cSJeff Cody error_setg(errp, "Error reading dynamic VHD header");
354019d6b8fSAnthony Liguori goto fail;
35524da78dbSCharles Arnold }
356019d6b8fSAnthony Liguori
357e326f078SMarkus Armbruster if (strncmp(dyndisk_header.magic, "cxsparse", 8)) {
35832f6439cSJeff Cody error_setg(errp, "Invalid header magic");
35959294e46SKevin Wolf ret = -EINVAL;
360019d6b8fSAnthony Liguori goto fail;
36124da78dbSCharles Arnold }
362019d6b8fSAnthony Liguori
363e326f078SMarkus Armbruster s->block_size = be32_to_cpu(dyndisk_header.block_size);
3645e71dfadSKevin Wolf if (!is_power_of_2(s->block_size) || s->block_size < BDRV_SECTOR_SIZE) {
3655e71dfadSKevin Wolf error_setg(errp, "Invalid block size %" PRIu32, s->block_size);
3665e71dfadSKevin Wolf ret = -EINVAL;
3675e71dfadSKevin Wolf goto fail;
3685e71dfadSKevin Wolf }
369019d6b8fSAnthony Liguori s->bitmap_size = ((s->block_size / (8 * 512)) + 511) & ~511;
370019d6b8fSAnthony Liguori
371e326f078SMarkus Armbruster s->max_table_entries = be32_to_cpu(dyndisk_header.max_table_entries);
37297f1c45cSJeff Cody
37397f1c45cSJeff Cody if ((bs->total_sectors * 512) / s->block_size > 0xffffffffU) {
37432f6439cSJeff Cody error_setg(errp, "Too many blocks");
37597f1c45cSJeff Cody ret = -EINVAL;
37697f1c45cSJeff Cody goto fail;
37797f1c45cSJeff Cody }
37897f1c45cSJeff Cody
37997f1c45cSJeff Cody computed_size = (uint64_t) s->max_table_entries * s->block_size;
38097f1c45cSJeff Cody if (computed_size < bs->total_sectors * 512) {
38132f6439cSJeff Cody error_setg(errp, "Page table too small");
38297f1c45cSJeff Cody ret = -EINVAL;
38397f1c45cSJeff Cody goto fail;
38497f1c45cSJeff Cody }
38597f1c45cSJeff Cody
386b15deac7SJeff Cody if (s->max_table_entries > SIZE_MAX / 4 ||
387b15deac7SJeff Cody s->max_table_entries > (int) INT_MAX / 4) {
388b15deac7SJeff Cody error_setg(errp, "Max Table Entries too large (%" PRId32 ")",
389b15deac7SJeff Cody s->max_table_entries);
390b15deac7SJeff Cody ret = -EINVAL;
391b15deac7SJeff Cody goto fail;
392b15deac7SJeff Cody }
393b15deac7SJeff Cody
394b15deac7SJeff Cody pagetable_size = (uint64_t) s->max_table_entries * 4;
395b15deac7SJeff Cody
3969a4f4c31SKevin Wolf s->pagetable = qemu_try_blockalign(bs->file->bs, pagetable_size);
3975fb09cd5SKevin Wolf if (s->pagetable == NULL) {
39832f6439cSJeff Cody error_setg(errp, "Unable to allocate memory for page table");
3995fb09cd5SKevin Wolf ret = -ENOMEM;
4005fb09cd5SKevin Wolf goto fail;
4015fb09cd5SKevin Wolf }
402019d6b8fSAnthony Liguori
403e326f078SMarkus Armbruster s->bat_offset = be64_to_cpu(dyndisk_header.table_offset);
40459294e46SKevin Wolf
40532cc71deSAlberto Faria ret = bdrv_pread(bs->file, s->bat_offset, pagetable_size,
40632cc71deSAlberto Faria s->pagetable, 0);
40759294e46SKevin Wolf if (ret < 0) {
40832f6439cSJeff Cody error_setg(errp, "Error reading pagetable");
409019d6b8fSAnthony Liguori goto fail;
41024da78dbSCharles Arnold }
411019d6b8fSAnthony Liguori
412019d6b8fSAnthony Liguori s->free_data_block_offset =
413b15deac7SJeff Cody ROUND_UP(s->bat_offset + pagetable_size, 512);
414019d6b8fSAnthony Liguori
415019d6b8fSAnthony Liguori for (i = 0; i < s->max_table_entries; i++) {
416019d6b8fSAnthony Liguori be32_to_cpus(&s->pagetable[i]);
417019d6b8fSAnthony Liguori if (s->pagetable[i] != 0xFFFFFFFF) {
418019d6b8fSAnthony Liguori int64_t next = (512 * (int64_t) s->pagetable[i]) +
419019d6b8fSAnthony Liguori s->bitmap_size + s->block_size;
420019d6b8fSAnthony Liguori
42124da78dbSCharles Arnold if (next > s->free_data_block_offset) {
422019d6b8fSAnthony Liguori s->free_data_block_offset = next;
423019d6b8fSAnthony Liguori }
424019d6b8fSAnthony Liguori }
42524da78dbSCharles Arnold }
426019d6b8fSAnthony Liguori
42781caa3ccSEric Blake bs_size = bdrv_getlength(bs->file->bs);
42881caa3ccSEric Blake if (bs_size < 0) {
42981caa3ccSEric Blake error_setg_errno(errp, -bs_size, "Unable to learn image size");
43081caa3ccSEric Blake ret = bs_size;
43181caa3ccSEric Blake goto fail;
43281caa3ccSEric Blake }
43381caa3ccSEric Blake if (s->free_data_block_offset > bs_size) {
434fb8fe35fSPeter Lieven error_setg(errp, "block-vpc: free_data_block_offset points after "
435fb8fe35fSPeter Lieven "the end of file. The image has been truncated.");
436fb8fe35fSPeter Lieven ret = -EINVAL;
437fb8fe35fSPeter Lieven goto fail;
438fb8fe35fSPeter Lieven }
439fb8fe35fSPeter Lieven
440019d6b8fSAnthony Liguori s->last_bitmap_offset = (int64_t) -1;
441019d6b8fSAnthony Liguori
442019d6b8fSAnthony Liguori #ifdef CACHE
4437267c094SAnthony Liguori s->pageentry_u8 = g_malloc(512);
444019d6b8fSAnthony Liguori s->pageentry_u32 = s->pageentry_u8;
445019d6b8fSAnthony Liguori s->pageentry_u16 = s->pageentry_u8;
446019d6b8fSAnthony Liguori s->last_pagetable = -1;
447019d6b8fSAnthony Liguori #endif
44824da78dbSCharles Arnold }
449019d6b8fSAnthony Liguori
450612ff3d8SKevin Wolf /* Disable migration when VHD images are used */
45181e5f78aSAlberto Garcia error_setg(&s->migration_blocker, "The vpc format used by node '%s' "
45281e5f78aSAlberto Garcia "does not support live migration",
45381e5f78aSAlberto Garcia bdrv_get_device_or_node_name(bs));
4544026f1c4SKevin Wolf
455e0ee3a8fSSteve Sistare ret = migrate_add_blocker_normal(&s->migration_blocker, errp);
456386f6c07SMarkus Armbruster if (ret < 0) {
457fe44dc91SAshijeet Acharya goto fail;
458fe44dc91SAshijeet Acharya }
459fe44dc91SAshijeet Acharya
460fe44dc91SAshijeet Acharya qemu_co_mutex_init(&s->lock);
461c317b646SKevin Wolf qemu_opts_del(opts);
462612ff3d8SKevin Wolf
463019d6b8fSAnthony Liguori return 0;
46459294e46SKevin Wolf
465019d6b8fSAnthony Liguori fail:
466c317b646SKevin Wolf qemu_opts_del(opts);
46797f1c45cSJeff Cody qemu_vfree(s->pagetable);
46859294e46SKevin Wolf #ifdef CACHE
46959294e46SKevin Wolf g_free(s->pageentry_u8);
47059294e46SKevin Wolf #endif
47159294e46SKevin Wolf return ret;
472019d6b8fSAnthony Liguori }
473019d6b8fSAnthony Liguori
vpc_reopen_prepare(BDRVReopenState * state,BlockReopenQueue * queue,Error ** errp)4743fe4b700SJeff Cody static int vpc_reopen_prepare(BDRVReopenState *state,
4753fe4b700SJeff Cody BlockReopenQueue *queue, Error **errp)
4763fe4b700SJeff Cody {
4773fe4b700SJeff Cody return 0;
4783fe4b700SJeff Cody }
4793fe4b700SJeff Cody
480019d6b8fSAnthony Liguori /*
481019d6b8fSAnthony Liguori * Returns the absolute byte offset of the given sector in the image file.
482019d6b8fSAnthony Liguori * If the sector is not allocated, -1 is returned instead.
483cfc87e00SPeter Maydell * If an error occurred trying to write an updated block bitmap back to
484cfc87e00SPeter Maydell * the file, -2 is returned, and the error value is written to *err.
485cfc87e00SPeter Maydell * This can only happen for a write operation.
486019d6b8fSAnthony Liguori *
487019d6b8fSAnthony Liguori * The parameter write must be 1 if the offset will be used for a write
488019d6b8fSAnthony Liguori * operation (the block bitmaps is updated then), 0 otherwise.
489cfc87e00SPeter Maydell * If write is true then err must not be NULL.
490019d6b8fSAnthony Liguori */
491517b5dffSPaolo Bonzini static int64_t coroutine_fn GRAPH_RDLOCK
get_image_offset(BlockDriverState * bs,uint64_t offset,bool write,int * err)492517b5dffSPaolo Bonzini get_image_offset(BlockDriverState *bs, uint64_t offset, bool write, int *err)
493019d6b8fSAnthony Liguori {
494019d6b8fSAnthony Liguori BDRVVPCState *s = bs->opaque;
495019d6b8fSAnthony Liguori uint64_t bitmap_offset, block_offset;
496d46b7cc6SKevin Wolf uint32_t pagetable_index, offset_in_block;
497019d6b8fSAnthony Liguori
498cfc87e00SPeter Maydell assert(!(write && err == NULL));
499cfc87e00SPeter Maydell
500019d6b8fSAnthony Liguori pagetable_index = offset / s->block_size;
501d46b7cc6SKevin Wolf offset_in_block = offset % s->block_size;
502019d6b8fSAnthony Liguori
503019d6b8fSAnthony Liguori if (pagetable_index >= s->max_table_entries || s->pagetable[pagetable_index] == 0xffffffff)
5049c057d0bSJeff Cody return -1; /* not allocated */
505019d6b8fSAnthony Liguori
506019d6b8fSAnthony Liguori bitmap_offset = 512 * (uint64_t) s->pagetable[pagetable_index];
507d46b7cc6SKevin Wolf block_offset = bitmap_offset + s->bitmap_size + offset_in_block;
508019d6b8fSAnthony Liguori
5099c057d0bSJeff Cody /* We must ensure that we don't write to any sectors which are marked as
5109c057d0bSJeff Cody unused in the bitmap. We get away with setting all bits in the block
5119c057d0bSJeff Cody bitmap each time we write to a new block. This might cause Virtual PC to
5129c057d0bSJeff Cody miss sparse read optimization, but it's not a problem in terms of
5139c057d0bSJeff Cody correctness. */
514019d6b8fSAnthony Liguori if (write && (s->last_bitmap_offset != bitmap_offset)) {
5153c2c599cSPhilippe Mathieu-Daudé g_autofree uint8_t *bitmap = g_malloc(s->bitmap_size);
516cfc87e00SPeter Maydell int r;
517019d6b8fSAnthony Liguori
518019d6b8fSAnthony Liguori s->last_bitmap_offset = bitmap_offset;
519019d6b8fSAnthony Liguori memset(bitmap, 0xff, s->bitmap_size);
520517b5dffSPaolo Bonzini r = bdrv_co_pwrite_sync(bs->file, bitmap_offset, s->bitmap_size, bitmap, 0);
521cfc87e00SPeter Maydell if (r < 0) {
522cfc87e00SPeter Maydell *err = r;
523cfc87e00SPeter Maydell return -2;
524cfc87e00SPeter Maydell }
525019d6b8fSAnthony Liguori }
526019d6b8fSAnthony Liguori
527019d6b8fSAnthony Liguori return block_offset;
528019d6b8fSAnthony Liguori }
529019d6b8fSAnthony Liguori
530019d6b8fSAnthony Liguori /*
531019d6b8fSAnthony Liguori * Writes the footer to the end of the image file. This is needed when the
532019d6b8fSAnthony Liguori * file grows as it overwrites the old footer
533019d6b8fSAnthony Liguori *
534019d6b8fSAnthony Liguori * Returns 0 on success and < 0 on error
535019d6b8fSAnthony Liguori */
rewrite_footer(BlockDriverState * bs)536517b5dffSPaolo Bonzini static int coroutine_fn GRAPH_RDLOCK rewrite_footer(BlockDriverState *bs)
537019d6b8fSAnthony Liguori {
538019d6b8fSAnthony Liguori int ret;
539019d6b8fSAnthony Liguori BDRVVPCState *s = bs->opaque;
540019d6b8fSAnthony Liguori int64_t offset = s->free_data_block_offset;
541019d6b8fSAnthony Liguori
542517b5dffSPaolo Bonzini ret = bdrv_co_pwrite_sync(bs->file, offset, sizeof(s->footer), &s->footer, 0);
543019d6b8fSAnthony Liguori if (ret < 0)
544019d6b8fSAnthony Liguori return ret;
545019d6b8fSAnthony Liguori
546019d6b8fSAnthony Liguori return 0;
547019d6b8fSAnthony Liguori }
548019d6b8fSAnthony Liguori
549019d6b8fSAnthony Liguori /*
550019d6b8fSAnthony Liguori * Allocates a new block. This involves writing a new footer and updating
551019d6b8fSAnthony Liguori * the Block Allocation Table to use the space at the old end of the image
552019d6b8fSAnthony Liguori * file (overwriting the old footer)
553019d6b8fSAnthony Liguori *
554019d6b8fSAnthony Liguori * Returns the sectors' offset in the image file on success and < 0 on error
555019d6b8fSAnthony Liguori */
556517b5dffSPaolo Bonzini static int64_t coroutine_fn GRAPH_RDLOCK
alloc_block(BlockDriverState * bs,int64_t offset)557517b5dffSPaolo Bonzini alloc_block(BlockDriverState *bs, int64_t offset)
558019d6b8fSAnthony Liguori {
559019d6b8fSAnthony Liguori BDRVVPCState *s = bs->opaque;
560019d6b8fSAnthony Liguori int64_t bat_offset;
561019d6b8fSAnthony Liguori uint32_t index, bat_value;
562019d6b8fSAnthony Liguori int ret;
5633c2c599cSPhilippe Mathieu-Daudé g_autofree uint8_t *bitmap = g_malloc(s->bitmap_size);
564019d6b8fSAnthony Liguori
5659c057d0bSJeff Cody /* Check if sector_num is valid */
566513b0f02SKevin Wolf if ((offset < 0) || (offset > bs->total_sectors * BDRV_SECTOR_SIZE)) {
567513b0f02SKevin Wolf return -EINVAL;
568513b0f02SKevin Wolf }
569019d6b8fSAnthony Liguori
5709c057d0bSJeff Cody /* Write entry into in-memory BAT */
571513b0f02SKevin Wolf index = offset / s->block_size;
572513b0f02SKevin Wolf assert(s->pagetable[index] == 0xFFFFFFFF);
573019d6b8fSAnthony Liguori s->pagetable[index] = s->free_data_block_offset / 512;
574019d6b8fSAnthony Liguori
5759c057d0bSJeff Cody /* Initialize the block's bitmap */
576019d6b8fSAnthony Liguori memset(bitmap, 0xff, s->bitmap_size);
577517b5dffSPaolo Bonzini ret = bdrv_co_pwrite_sync(bs->file, s->free_data_block_offset,
57832cc71deSAlberto Faria s->bitmap_size, bitmap, 0);
5795bb1cbacSKevin Wolf if (ret < 0) {
5805bb1cbacSKevin Wolf return ret;
5815bb1cbacSKevin Wolf }
582019d6b8fSAnthony Liguori
5839c057d0bSJeff Cody /* Write new footer (the old one will be overwritten) */
584019d6b8fSAnthony Liguori s->free_data_block_offset += s->block_size + s->bitmap_size;
585019d6b8fSAnthony Liguori ret = rewrite_footer(bs);
586019d6b8fSAnthony Liguori if (ret < 0)
587019d6b8fSAnthony Liguori goto fail;
588019d6b8fSAnthony Liguori
5899c057d0bSJeff Cody /* Write BAT entry to disk */
590019d6b8fSAnthony Liguori bat_offset = s->bat_offset + (4 * index);
591a4127c42SStefan Hajnoczi bat_value = cpu_to_be32(s->pagetable[index]);
592517b5dffSPaolo Bonzini ret = bdrv_co_pwrite_sync(bs->file, bat_offset, 4, &bat_value, 0);
593019d6b8fSAnthony Liguori if (ret < 0)
594019d6b8fSAnthony Liguori goto fail;
595019d6b8fSAnthony Liguori
596cfc87e00SPeter Maydell return get_image_offset(bs, offset, false, NULL);
597019d6b8fSAnthony Liguori
598019d6b8fSAnthony Liguori fail:
599019d6b8fSAnthony Liguori s->free_data_block_offset -= (s->block_size + s->bitmap_size);
600513b0f02SKevin Wolf return ret;
601019d6b8fSAnthony Liguori }
602019d6b8fSAnthony Liguori
6033d47eb0aSEmanuele Giuseppe Esposito static int coroutine_fn
vpc_co_get_info(BlockDriverState * bs,BlockDriverInfo * bdi)6043d47eb0aSEmanuele Giuseppe Esposito vpc_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
60597b00e28SPaolo Bonzini {
60697b00e28SPaolo Bonzini BDRVVPCState *s = (BDRVVPCState *)bs->opaque;
60797b00e28SPaolo Bonzini
608275734e4SMarkus Armbruster if (be32_to_cpu(s->footer.type) != VHD_FIXED) {
60997b00e28SPaolo Bonzini bdi->cluster_size = s->block_size;
61097b00e28SPaolo Bonzini }
61197b00e28SPaolo Bonzini
61297b00e28SPaolo Bonzini return 0;
61397b00e28SPaolo Bonzini }
61497b00e28SPaolo Bonzini
615b9b10c35SKevin Wolf static int coroutine_fn GRAPH_RDLOCK
vpc_co_preadv(BlockDriverState * bs,int64_t offset,int64_t bytes,QEMUIOVector * qiov,BdrvRequestFlags flags)616f7ef38ddSVladimir Sementsov-Ogievskiy vpc_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes,
617f7ef38ddSVladimir Sementsov-Ogievskiy QEMUIOVector *qiov, BdrvRequestFlags flags)
618019d6b8fSAnthony Liguori {
6196c6ea921SKevin Wolf BDRVVPCState *s = bs->opaque;
620019d6b8fSAnthony Liguori int ret;
621d46b7cc6SKevin Wolf int64_t image_offset;
622d46b7cc6SKevin Wolf int64_t n_bytes;
623d46b7cc6SKevin Wolf int64_t bytes_done = 0;
624d46b7cc6SKevin Wolf QEMUIOVector local_qiov;
625019d6b8fSAnthony Liguori
626275734e4SMarkus Armbruster if (be32_to_cpu(s->footer.type) == VHD_FIXED) {
627a03ef88fSKevin Wolf return bdrv_co_preadv(bs->file, offset, bytes, qiov, 0);
628019d6b8fSAnthony Liguori }
629019d6b8fSAnthony Liguori
6302914caa0SPaolo Bonzini qemu_co_mutex_lock(&s->lock);
631d46b7cc6SKevin Wolf qemu_iovec_init(&local_qiov, qiov->niov);
632d46b7cc6SKevin Wolf
633d46b7cc6SKevin Wolf while (bytes > 0) {
634cfc87e00SPeter Maydell image_offset = get_image_offset(bs, offset, false, NULL);
635d46b7cc6SKevin Wolf n_bytes = MIN(bytes, s->block_size - (offset % s->block_size));
636d46b7cc6SKevin Wolf
637d46b7cc6SKevin Wolf if (image_offset == -1) {
638d46b7cc6SKevin Wolf qemu_iovec_memset(qiov, bytes_done, 0, n_bytes);
639d46b7cc6SKevin Wolf } else {
640d46b7cc6SKevin Wolf qemu_iovec_reset(&local_qiov);
641d46b7cc6SKevin Wolf qemu_iovec_concat(&local_qiov, qiov, bytes_done, n_bytes);
642d46b7cc6SKevin Wolf
643126734c4SZhengui li qemu_co_mutex_unlock(&s->lock);
644a03ef88fSKevin Wolf ret = bdrv_co_preadv(bs->file, image_offset, n_bytes,
645d46b7cc6SKevin Wolf &local_qiov, 0);
646126734c4SZhengui li qemu_co_mutex_lock(&s->lock);
647d46b7cc6SKevin Wolf if (ret < 0) {
648d46b7cc6SKevin Wolf goto fail;
649d46b7cc6SKevin Wolf }
650d46b7cc6SKevin Wolf }
651d46b7cc6SKevin Wolf
652d46b7cc6SKevin Wolf bytes -= n_bytes;
653d46b7cc6SKevin Wolf offset += n_bytes;
654d46b7cc6SKevin Wolf bytes_done += n_bytes;
655d46b7cc6SKevin Wolf }
656d46b7cc6SKevin Wolf
657d46b7cc6SKevin Wolf ret = 0;
658d46b7cc6SKevin Wolf fail:
659d46b7cc6SKevin Wolf qemu_iovec_destroy(&local_qiov);
6602914caa0SPaolo Bonzini qemu_co_mutex_unlock(&s->lock);
661d46b7cc6SKevin Wolf
6622914caa0SPaolo Bonzini return ret;
6632914caa0SPaolo Bonzini }
6642914caa0SPaolo Bonzini
665b9b10c35SKevin Wolf static int coroutine_fn GRAPH_RDLOCK
vpc_co_pwritev(BlockDriverState * bs,int64_t offset,int64_t bytes,QEMUIOVector * qiov,BdrvRequestFlags flags)666e75abedaSVladimir Sementsov-Ogievskiy vpc_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes,
667e75abedaSVladimir Sementsov-Ogievskiy QEMUIOVector *qiov, BdrvRequestFlags flags)
668019d6b8fSAnthony Liguori {
6696c6ea921SKevin Wolf BDRVVPCState *s = bs->opaque;
670513b0f02SKevin Wolf int64_t image_offset;
671513b0f02SKevin Wolf int64_t n_bytes;
672513b0f02SKevin Wolf int64_t bytes_done = 0;
673c8115f8eSMark Cave-Ayland int ret = 0;
674513b0f02SKevin Wolf QEMUIOVector local_qiov;
675019d6b8fSAnthony Liguori
676275734e4SMarkus Armbruster if (be32_to_cpu(s->footer.type) == VHD_FIXED) {
677a03ef88fSKevin Wolf return bdrv_co_pwritev(bs->file, offset, bytes, qiov, 0);
6786c6ea921SKevin Wolf }
6796c6ea921SKevin Wolf
680e183ef75SPaolo Bonzini qemu_co_mutex_lock(&s->lock);
681513b0f02SKevin Wolf qemu_iovec_init(&local_qiov, qiov->niov);
682513b0f02SKevin Wolf
683513b0f02SKevin Wolf while (bytes > 0) {
684cfc87e00SPeter Maydell image_offset = get_image_offset(bs, offset, true, &ret);
685cfc87e00SPeter Maydell if (image_offset == -2) {
686cfc87e00SPeter Maydell /* Failed to write block bitmap: can't proceed with write */
687cfc87e00SPeter Maydell goto fail;
688cfc87e00SPeter Maydell }
689513b0f02SKevin Wolf n_bytes = MIN(bytes, s->block_size - (offset % s->block_size));
690513b0f02SKevin Wolf
691513b0f02SKevin Wolf if (image_offset == -1) {
692513b0f02SKevin Wolf image_offset = alloc_block(bs, offset);
693513b0f02SKevin Wolf if (image_offset < 0) {
694513b0f02SKevin Wolf ret = image_offset;
695513b0f02SKevin Wolf goto fail;
696513b0f02SKevin Wolf }
697513b0f02SKevin Wolf }
698513b0f02SKevin Wolf
699513b0f02SKevin Wolf qemu_iovec_reset(&local_qiov);
700513b0f02SKevin Wolf qemu_iovec_concat(&local_qiov, qiov, bytes_done, n_bytes);
701513b0f02SKevin Wolf
702126734c4SZhengui li qemu_co_mutex_unlock(&s->lock);
703a03ef88fSKevin Wolf ret = bdrv_co_pwritev(bs->file, image_offset, n_bytes,
704513b0f02SKevin Wolf &local_qiov, 0);
705126734c4SZhengui li qemu_co_mutex_lock(&s->lock);
706513b0f02SKevin Wolf if (ret < 0) {
707513b0f02SKevin Wolf goto fail;
708513b0f02SKevin Wolf }
709513b0f02SKevin Wolf
710513b0f02SKevin Wolf bytes -= n_bytes;
711513b0f02SKevin Wolf offset += n_bytes;
712513b0f02SKevin Wolf bytes_done += n_bytes;
713513b0f02SKevin Wolf }
714513b0f02SKevin Wolf
715513b0f02SKevin Wolf ret = 0;
716513b0f02SKevin Wolf fail:
717513b0f02SKevin Wolf qemu_iovec_destroy(&local_qiov);
718e183ef75SPaolo Bonzini qemu_co_mutex_unlock(&s->lock);
719513b0f02SKevin Wolf
720e183ef75SPaolo Bonzini return ret;
721e183ef75SPaolo Bonzini }
722e183ef75SPaolo Bonzini
723517b5dffSPaolo Bonzini static int coroutine_fn GRAPH_RDLOCK
vpc_co_block_status(BlockDriverState * bs,bool want_zero,int64_t offset,int64_t bytes,int64_t * pnum,int64_t * map,BlockDriverState ** file)724517b5dffSPaolo Bonzini vpc_co_block_status(BlockDriverState *bs, bool want_zero,
7252f83673bSEric Blake int64_t offset, int64_t bytes,
7262f83673bSEric Blake int64_t *pnum, int64_t *map,
7272f83673bSEric Blake BlockDriverState **file)
7280cc84887SKevin Wolf {
7290cc84887SKevin Wolf BDRVVPCState *s = bs->opaque;
7302f83673bSEric Blake int64_t image_offset;
7310cc84887SKevin Wolf bool allocated;
7322f83673bSEric Blake int ret;
7332f83673bSEric Blake int64_t n;
7340cc84887SKevin Wolf
735275734e4SMarkus Armbruster if (be32_to_cpu(s->footer.type) == VHD_FIXED) {
7362f83673bSEric Blake *pnum = bytes;
7372f83673bSEric Blake *map = offset;
7387429e207SFam Zheng *file = bs->file->bs;
739fbc8e1b7SMax Reitz return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_RECURSE;
7400cc84887SKevin Wolf }
7410cc84887SKevin Wolf
742778b087eSPaolo Bonzini qemu_co_mutex_lock(&s->lock);
743778b087eSPaolo Bonzini
7442f83673bSEric Blake image_offset = get_image_offset(bs, offset, false, NULL);
7452f83673bSEric Blake allocated = (image_offset != -1);
7460cc84887SKevin Wolf *pnum = 0;
7472c060c0fSVladimir Sementsov-Ogievskiy ret = BDRV_BLOCK_ZERO;
7480cc84887SKevin Wolf
7490cc84887SKevin Wolf do {
7500cc84887SKevin Wolf /* All sectors in a block are contiguous (without using the bitmap) */
7512f83673bSEric Blake n = ROUND_UP(offset + 1, s->block_size) - offset;
7522f83673bSEric Blake n = MIN(n, bytes);
7530cc84887SKevin Wolf
7540cc84887SKevin Wolf *pnum += n;
7552f83673bSEric Blake offset += n;
7562f83673bSEric Blake bytes -= n;
7572ec711dcSPeter Lieven /* *pnum can't be greater than one block for allocated
7582ec711dcSPeter Lieven * sectors since there is always a bitmap in between. */
7592ec711dcSPeter Lieven if (allocated) {
7607429e207SFam Zheng *file = bs->file->bs;
7612f83673bSEric Blake *map = image_offset;
7622f83673bSEric Blake ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID;
763778b087eSPaolo Bonzini break;
7642ec711dcSPeter Lieven }
7652f83673bSEric Blake if (bytes == 0) {
7660cc84887SKevin Wolf break;
7670cc84887SKevin Wolf }
7682f83673bSEric Blake image_offset = get_image_offset(bs, offset, false, NULL);
7692f83673bSEric Blake } while (image_offset == -1);
7700cc84887SKevin Wolf
771778b087eSPaolo Bonzini qemu_co_mutex_unlock(&s->lock);
772778b087eSPaolo Bonzini return ret;
7730cc84887SKevin Wolf }
7740cc84887SKevin Wolf
775019d6b8fSAnthony Liguori /*
776019d6b8fSAnthony Liguori * Calculates the number of cylinders, heads and sectors per cylinder
777019d6b8fSAnthony Liguori * based on a given number of sectors. This is the algorithm described
778019d6b8fSAnthony Liguori * in the VHD specification.
779019d6b8fSAnthony Liguori *
780019d6b8fSAnthony Liguori * Note that the geometry doesn't always exactly match total_sectors but
781019d6b8fSAnthony Liguori * may round it down.
782019d6b8fSAnthony Liguori *
783c23fb11bSJeff Cody * Returns 0 on success, -EFBIG if the size is larger than 2040 GiB. Override
784258d2edbSCharles Arnold * the hardware EIDE and ATA-2 limit of 16 heads (max disk size of 127 GB)
785258d2edbSCharles Arnold * and instead allow up to 255 heads.
786019d6b8fSAnthony Liguori */
calculate_geometry(int64_t total_sectors,uint16_t * cyls,uint8_t * heads,uint8_t * secs_per_cyl)787019d6b8fSAnthony Liguori static int calculate_geometry(int64_t total_sectors, uint16_t *cyls,
788019d6b8fSAnthony Liguori uint8_t *heads, uint8_t *secs_per_cyl)
789019d6b8fSAnthony Liguori {
790019d6b8fSAnthony Liguori uint32_t cyls_times_heads;
791019d6b8fSAnthony Liguori
792690cbb09SPeter Lieven total_sectors = MIN(total_sectors, VHD_MAX_GEOMETRY);
793019d6b8fSAnthony Liguori
794690cbb09SPeter Lieven if (total_sectors >= 65535LL * 16 * 63) {
795019d6b8fSAnthony Liguori *secs_per_cyl = 255;
796019d6b8fSAnthony Liguori *heads = 16;
797019d6b8fSAnthony Liguori cyls_times_heads = total_sectors / *secs_per_cyl;
798019d6b8fSAnthony Liguori } else {
799019d6b8fSAnthony Liguori *secs_per_cyl = 17;
800019d6b8fSAnthony Liguori cyls_times_heads = total_sectors / *secs_per_cyl;
80113f1493fSMarc-André Lureau *heads = DIV_ROUND_UP(cyls_times_heads, 1024);
802019d6b8fSAnthony Liguori
803690cbb09SPeter Lieven if (*heads < 4) {
804019d6b8fSAnthony Liguori *heads = 4;
805690cbb09SPeter Lieven }
806019d6b8fSAnthony Liguori
807019d6b8fSAnthony Liguori if (cyls_times_heads >= (*heads * 1024) || *heads > 16) {
808019d6b8fSAnthony Liguori *secs_per_cyl = 31;
809019d6b8fSAnthony Liguori *heads = 16;
810019d6b8fSAnthony Liguori cyls_times_heads = total_sectors / *secs_per_cyl;
811019d6b8fSAnthony Liguori }
812019d6b8fSAnthony Liguori
813019d6b8fSAnthony Liguori if (cyls_times_heads >= (*heads * 1024)) {
814019d6b8fSAnthony Liguori *secs_per_cyl = 63;
815019d6b8fSAnthony Liguori *heads = 16;
816019d6b8fSAnthony Liguori cyls_times_heads = total_sectors / *secs_per_cyl;
817019d6b8fSAnthony Liguori }
818019d6b8fSAnthony Liguori }
819019d6b8fSAnthony Liguori
820dede4188SStefan Weil *cyls = cyls_times_heads / *heads;
821019d6b8fSAnthony Liguori
822019d6b8fSAnthony Liguori return 0;
823019d6b8fSAnthony Liguori }
824019d6b8fSAnthony Liguori
create_dynamic_disk(BlockBackend * blk,VHDFooter * footer,int64_t total_sectors)825517b5dffSPaolo Bonzini static int coroutine_fn create_dynamic_disk(BlockBackend *blk, VHDFooter *footer,
826fef6070eSJeff Cody int64_t total_sectors)
827019d6b8fSAnthony Liguori {
828e326f078SMarkus Armbruster VHDDynDiskHeader dyndisk_header;
829b0ce8cb0SMarkus Armbruster uint8_t bat_sector[512];
830019d6b8fSAnthony Liguori size_t block_size, num_bat_entries;
83124da78dbSCharles Arnold int i;
832fef6070eSJeff Cody int ret;
833fef6070eSJeff Cody int64_t offset = 0;
834019d6b8fSAnthony Liguori
8359c057d0bSJeff Cody /* Write the footer (twice: at the beginning and at the end) */
836019d6b8fSAnthony Liguori block_size = 0x200000;
8373f6de653SKevin Wolf num_bat_entries = DIV_ROUND_UP(total_sectors, block_size / 512);
838019d6b8fSAnthony Liguori
839517b5dffSPaolo Bonzini ret = blk_co_pwrite(blk, offset, sizeof(*footer), footer, 0);
84040a99aacSPaolo Bonzini if (ret < 0) {
841f0ff243aSBlue Swirl goto fail;
842f0ff243aSBlue Swirl }
843019d6b8fSAnthony Liguori
844fef6070eSJeff Cody offset = 1536 + ((num_bat_entries * 4 + 511) & ~511);
845517b5dffSPaolo Bonzini ret = blk_co_pwrite(blk, offset, sizeof(*footer), footer, 0);
846fef6070eSJeff Cody if (ret < 0) {
847f0ff243aSBlue Swirl goto fail;
848f0ff243aSBlue Swirl }
849019d6b8fSAnthony Liguori
8509c057d0bSJeff Cody /* Write the initial BAT */
851fef6070eSJeff Cody offset = 3 * 512;
852019d6b8fSAnthony Liguori
853b0ce8cb0SMarkus Armbruster memset(bat_sector, 0xFF, 512);
85413f1493fSMarc-André Lureau for (i = 0; i < DIV_ROUND_UP(num_bat_entries * 4, 512); i++) {
855517b5dffSPaolo Bonzini ret = blk_co_pwrite(blk, offset, 512, bat_sector, 0);
856fef6070eSJeff Cody if (ret < 0) {
857f0ff243aSBlue Swirl goto fail;
858f0ff243aSBlue Swirl }
859fef6070eSJeff Cody offset += 512;
860f0ff243aSBlue Swirl }
861019d6b8fSAnthony Liguori
8629c057d0bSJeff Cody /* Prepare the Dynamic Disk Header */
8633d6101a3SMarkus Armbruster memset(&dyndisk_header, 0, sizeof(dyndisk_header));
864019d6b8fSAnthony Liguori
865e326f078SMarkus Armbruster memcpy(dyndisk_header.magic, "cxsparse", 8);
866019d6b8fSAnthony Liguori
86778439f6aSCharles Arnold /*
86878439f6aSCharles Arnold * Note: The spec is actually wrong here for data_offset, it says
86978439f6aSCharles Arnold * 0xFFFFFFFF, but MS tools expect all 64 bits to be set.
87078439f6aSCharles Arnold */
871e326f078SMarkus Armbruster dyndisk_header.data_offset = cpu_to_be64(0xFFFFFFFFFFFFFFFFULL);
872e326f078SMarkus Armbruster dyndisk_header.table_offset = cpu_to_be64(3 * 512);
873e326f078SMarkus Armbruster dyndisk_header.version = cpu_to_be32(0x00010000);
874e326f078SMarkus Armbruster dyndisk_header.block_size = cpu_to_be32(block_size);
875e326f078SMarkus Armbruster dyndisk_header.max_table_entries = cpu_to_be32(num_bat_entries);
876019d6b8fSAnthony Liguori
8773d6101a3SMarkus Armbruster dyndisk_header.checksum = cpu_to_be32(
8783d6101a3SMarkus Armbruster vpc_checksum(&dyndisk_header, sizeof(dyndisk_header)));
879019d6b8fSAnthony Liguori
8809c057d0bSJeff Cody /* Write the header */
881fef6070eSJeff Cody offset = 512;
882019d6b8fSAnthony Liguori
883517b5dffSPaolo Bonzini ret = blk_co_pwrite(blk, offset, sizeof(dyndisk_header), &dyndisk_header, 0);
884fef6070eSJeff Cody if (ret < 0) {
885f0ff243aSBlue Swirl goto fail;
886f0ff243aSBlue Swirl }
887f0ff243aSBlue Swirl
8881a37e312SMax Reitz ret = 0;
889f0ff243aSBlue Swirl fail:
89024da78dbSCharles Arnold return ret;
89124da78dbSCharles Arnold }
89224da78dbSCharles Arnold
create_fixed_disk(BlockBackend * blk,VHDFooter * footer,int64_t total_size,Error ** errp)893517b5dffSPaolo Bonzini static int coroutine_fn create_fixed_disk(BlockBackend *blk, VHDFooter *footer,
894ed3d2ec9SMax Reitz int64_t total_size, Error **errp)
89524da78dbSCharles Arnold {
896fef6070eSJeff Cody int ret;
89724da78dbSCharles Arnold
89824da78dbSCharles Arnold /* Add footer to total size */
899be7c5dddSMarkus Armbruster total_size += sizeof(*footer);
900fef6070eSJeff Cody
901517b5dffSPaolo Bonzini ret = blk_co_truncate(blk, total_size, false, PREALLOC_MODE_OFF, 0, errp);
902fef6070eSJeff Cody if (ret < 0) {
903fef6070eSJeff Cody return ret;
90424da78dbSCharles Arnold }
90524da78dbSCharles Arnold
906517b5dffSPaolo Bonzini ret = blk_co_pwrite(blk, total_size - sizeof(*footer), sizeof(*footer),
907a9262f55SAlberto Faria footer, 0);
908fef6070eSJeff Cody if (ret < 0) {
909ed3d2ec9SMax Reitz error_setg_errno(errp, -ret, "Unable to write VHD header");
910fef6070eSJeff Cody return ret;
911fef6070eSJeff Cody }
91224da78dbSCharles Arnold
9131a37e312SMax Reitz return 0;
91424da78dbSCharles Arnold }
91524da78dbSCharles Arnold
calculate_rounded_image_size(BlockdevCreateOptionsVpc * vpc_opts,uint16_t * out_cyls,uint8_t * out_heads,uint8_t * out_secs_per_cyl,int64_t * out_total_sectors,Error ** errp)9161cfeaf38SKevin Wolf static int calculate_rounded_image_size(BlockdevCreateOptionsVpc *vpc_opts,
9171cfeaf38SKevin Wolf uint16_t *out_cyls,
9181cfeaf38SKevin Wolf uint8_t *out_heads,
9191cfeaf38SKevin Wolf uint8_t *out_secs_per_cyl,
9201cfeaf38SKevin Wolf int64_t *out_total_sectors,
9211cfeaf38SKevin Wolf Error **errp)
9221cfeaf38SKevin Wolf {
9231cfeaf38SKevin Wolf int64_t total_size = vpc_opts->size;
9241cfeaf38SKevin Wolf uint16_t cyls = 0;
9251cfeaf38SKevin Wolf uint8_t heads = 0;
9261cfeaf38SKevin Wolf uint8_t secs_per_cyl = 0;
9271cfeaf38SKevin Wolf int64_t total_sectors;
9281cfeaf38SKevin Wolf int i;
9291cfeaf38SKevin Wolf
9301cfeaf38SKevin Wolf /*
9311cfeaf38SKevin Wolf * Calculate matching total_size and geometry. Increase the number of
9321cfeaf38SKevin Wolf * sectors requested until we get enough (or fail). This ensures that
9331cfeaf38SKevin Wolf * qemu-img convert doesn't truncate images, but rather rounds up.
9341cfeaf38SKevin Wolf *
9351cfeaf38SKevin Wolf * If the image size can't be represented by a spec conformant CHS geometry,
9361cfeaf38SKevin Wolf * we set the geometry to 65535 x 16 x 255 (CxHxS) sectors and use
9371cfeaf38SKevin Wolf * the image size from the VHD footer to calculate total_sectors.
9381cfeaf38SKevin Wolf */
9391cfeaf38SKevin Wolf if (vpc_opts->force_size) {
9401cfeaf38SKevin Wolf /* This will force the use of total_size for sector count, below */
9411cfeaf38SKevin Wolf cyls = VHD_CHS_MAX_C;
9421cfeaf38SKevin Wolf heads = VHD_CHS_MAX_H;
9431cfeaf38SKevin Wolf secs_per_cyl = VHD_CHS_MAX_S;
9441cfeaf38SKevin Wolf } else {
9451cfeaf38SKevin Wolf total_sectors = MIN(VHD_MAX_GEOMETRY, total_size / BDRV_SECTOR_SIZE);
9461cfeaf38SKevin Wolf for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) {
9471cfeaf38SKevin Wolf calculate_geometry(total_sectors + i, &cyls, &heads, &secs_per_cyl);
9481cfeaf38SKevin Wolf }
9491cfeaf38SKevin Wolf }
9501cfeaf38SKevin Wolf
9511cfeaf38SKevin Wolf if ((int64_t)cyls * heads * secs_per_cyl == VHD_MAX_GEOMETRY) {
9521cfeaf38SKevin Wolf total_sectors = total_size / BDRV_SECTOR_SIZE;
9531cfeaf38SKevin Wolf /* Allow a maximum disk size of 2040 GiB */
9541cfeaf38SKevin Wolf if (total_sectors > VHD_MAX_SECTORS) {
9551cfeaf38SKevin Wolf error_setg(errp, "Disk size is too large, max size is 2040 GiB");
9561cfeaf38SKevin Wolf return -EFBIG;
9571cfeaf38SKevin Wolf }
9581cfeaf38SKevin Wolf } else {
9591cfeaf38SKevin Wolf total_sectors = (int64_t) cyls * heads * secs_per_cyl;
9601cfeaf38SKevin Wolf }
9611cfeaf38SKevin Wolf
9621cfeaf38SKevin Wolf *out_total_sectors = total_sectors;
9631cfeaf38SKevin Wolf if (out_cyls) {
9641cfeaf38SKevin Wolf *out_cyls = cyls;
9651cfeaf38SKevin Wolf *out_heads = heads;
9661cfeaf38SKevin Wolf *out_secs_per_cyl = secs_per_cyl;
9671cfeaf38SKevin Wolf }
9681cfeaf38SKevin Wolf
9691cfeaf38SKevin Wolf return 0;
9701cfeaf38SKevin Wolf }
9711cfeaf38SKevin Wolf
9724db7ba3bSKevin Wolf static int coroutine_fn GRAPH_UNLOCKED
vpc_co_create(BlockdevCreateOptions * opts,Error ** errp)9734db7ba3bSKevin Wolf vpc_co_create(BlockdevCreateOptions *opts, Error **errp)
97424da78dbSCharles Arnold {
975182c8835SKevin Wolf BlockdevCreateOptionsVpc *vpc_opts;
976182c8835SKevin Wolf BlockBackend *blk = NULL;
977182c8835SKevin Wolf BlockDriverState *bs = NULL;
978182c8835SKevin Wolf
979275734e4SMarkus Armbruster VHDFooter footer;
98024da78dbSCharles Arnold uint16_t cyls = 0;
98124da78dbSCharles Arnold uint8_t heads = 0;
98224da78dbSCharles Arnold uint8_t secs_per_cyl = 0;
98324da78dbSCharles Arnold int64_t total_sectors;
98424da78dbSCharles Arnold int64_t total_size;
98524da78dbSCharles Arnold int disk_type;
98624da78dbSCharles Arnold int ret = -EIO;
9870dbaaa79SPeter Maydell QemuUUID uuid;
98824da78dbSCharles Arnold
989182c8835SKevin Wolf assert(opts->driver == BLOCKDEV_DRIVER_VPC);
990182c8835SKevin Wolf vpc_opts = &opts->u.vpc;
991182c8835SKevin Wolf
992182c8835SKevin Wolf /* Validate options and set default values */
993182c8835SKevin Wolf total_size = vpc_opts->size;
994182c8835SKevin Wolf
995182c8835SKevin Wolf if (!vpc_opts->has_subformat) {
996182c8835SKevin Wolf vpc_opts->subformat = BLOCKDEV_VPC_SUBFORMAT_DYNAMIC;
997182c8835SKevin Wolf }
998182c8835SKevin Wolf switch (vpc_opts->subformat) {
999182c8835SKevin Wolf case BLOCKDEV_VPC_SUBFORMAT_DYNAMIC:
100024da78dbSCharles Arnold disk_type = VHD_DYNAMIC;
1001182c8835SKevin Wolf break;
1002182c8835SKevin Wolf case BLOCKDEV_VPC_SUBFORMAT_FIXED:
100324da78dbSCharles Arnold disk_type = VHD_FIXED;
1004182c8835SKevin Wolf break;
1005182c8835SKevin Wolf default:
1006182c8835SKevin Wolf g_assert_not_reached();
100724da78dbSCharles Arnold }
100824da78dbSCharles Arnold
1009182c8835SKevin Wolf /* Create BlockBackend to write to the image */
10106ef02851SKevin Wolf bs = bdrv_co_open_blockdev_ref(vpc_opts->file, errp);
1011182c8835SKevin Wolf if (bs == NULL) {
1012182c8835SKevin Wolf return -EIO;
1013182c8835SKevin Wolf }
1014fb9245c2SJeff Cody
10156ef02851SKevin Wolf blk = blk_co_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL,
1016a3aeeab5SEric Blake errp);
1017a3aeeab5SEric Blake if (!blk) {
1018a3aeeab5SEric Blake ret = -EPERM;
1019fec9921fSChunyan Liu goto out;
102024da78dbSCharles Arnold }
1021b8f45cdfSKevin Wolf blk_set_allow_write_beyond_eof(blk, true);
1022b8f45cdfSKevin Wolf
10231cfeaf38SKevin Wolf /* Get geometry and check that it matches the image size*/
10241cfeaf38SKevin Wolf ret = calculate_rounded_image_size(vpc_opts, &cyls, &heads, &secs_per_cyl,
10251cfeaf38SKevin Wolf &total_sectors, errp);
10261cfeaf38SKevin Wolf if (ret < 0) {
1027fef6070eSJeff Cody goto out;
102824da78dbSCharles Arnold }
10291cfeaf38SKevin Wolf
10301cfeaf38SKevin Wolf if (total_size != total_sectors * BDRV_SECTOR_SIZE) {
10311cfeaf38SKevin Wolf error_setg(errp, "The requested image size cannot be represented in "
10321cfeaf38SKevin Wolf "CHS geometry");
10331cfeaf38SKevin Wolf error_append_hint(errp, "Try size=%llu or force-size=on (the "
10341cfeaf38SKevin Wolf "latter makes the image incompatible with "
10351cfeaf38SKevin Wolf "Virtual PC)",
10361cfeaf38SKevin Wolf total_sectors * BDRV_SECTOR_SIZE);
10371cfeaf38SKevin Wolf ret = -EINVAL;
10381cfeaf38SKevin Wolf goto out;
1039690cbb09SPeter Lieven }
104024da78dbSCharles Arnold
104124da78dbSCharles Arnold /* Prepare the Hard Disk Footer */
1042be7c5dddSMarkus Armbruster memset(&footer, 0, sizeof(footer));
104324da78dbSCharles Arnold
1044275734e4SMarkus Armbruster memcpy(footer.creator, "conectix", 8);
1045182c8835SKevin Wolf if (vpc_opts->force_size) {
1046275734e4SMarkus Armbruster memcpy(footer.creator_app, "qem2", 4);
1047fb9245c2SJeff Cody } else {
1048275734e4SMarkus Armbruster memcpy(footer.creator_app, "qemu", 4);
1049fb9245c2SJeff Cody }
1050275734e4SMarkus Armbruster memcpy(footer.creator_os, "Wi2k", 4);
105124da78dbSCharles Arnold
1052275734e4SMarkus Armbruster footer.features = cpu_to_be32(0x02);
1053275734e4SMarkus Armbruster footer.version = cpu_to_be32(0x00010000);
105424da78dbSCharles Arnold if (disk_type == VHD_DYNAMIC) {
1055be7c5dddSMarkus Armbruster footer.data_offset = cpu_to_be64(sizeof(footer));
105624da78dbSCharles Arnold } else {
1057275734e4SMarkus Armbruster footer.data_offset = cpu_to_be64(0xFFFFFFFFFFFFFFFFULL);
105824da78dbSCharles Arnold }
1059275734e4SMarkus Armbruster footer.timestamp = cpu_to_be32(time(NULL) - VHD_TIMESTAMP_BASE);
106024da78dbSCharles Arnold
106124da78dbSCharles Arnold /* Version of Virtual PC 2007 */
1062275734e4SMarkus Armbruster footer.major = cpu_to_be16(0x0005);
1063275734e4SMarkus Armbruster footer.minor = cpu_to_be16(0x0003);
1064275734e4SMarkus Armbruster footer.orig_size = cpu_to_be64(total_size);
1065275734e4SMarkus Armbruster footer.current_size = cpu_to_be64(total_size);
1066275734e4SMarkus Armbruster footer.cyls = cpu_to_be16(cyls);
1067275734e4SMarkus Armbruster footer.heads = heads;
1068275734e4SMarkus Armbruster footer.secs_per_cyl = secs_per_cyl;
106924da78dbSCharles Arnold
1070275734e4SMarkus Armbruster footer.type = cpu_to_be32(disk_type);
107124da78dbSCharles Arnold
10720dbaaa79SPeter Maydell qemu_uuid_generate(&uuid);
1073275734e4SMarkus Armbruster footer.uuid = uuid;
107424da78dbSCharles Arnold
1075be7c5dddSMarkus Armbruster footer.checksum = cpu_to_be32(vpc_checksum(&footer, sizeof(footer)));
107624da78dbSCharles Arnold
107724da78dbSCharles Arnold if (disk_type == VHD_DYNAMIC) {
1078a3d27617SMarkus Armbruster ret = create_dynamic_disk(blk, &footer, total_sectors);
10790211b9beSJeff Cody if (ret < 0) {
10800211b9beSJeff Cody error_setg(errp, "Unable to create or write VHD header");
10810211b9beSJeff Cody }
1082ed3d2ec9SMax Reitz } else {
1083a3d27617SMarkus Armbruster ret = create_fixed_disk(blk, &footer, total_size, errp);
1084ed3d2ec9SMax Reitz }
108524da78dbSCharles Arnold
1086fec9921fSChunyan Liu out:
1087b2ab5f54SKevin Wolf blk_co_unref(blk);
1088b2ab5f54SKevin Wolf bdrv_co_unref(bs);
1089f0ff243aSBlue Swirl return ret;
1090019d6b8fSAnthony Liguori }
1091019d6b8fSAnthony Liguori
10924db7ba3bSKevin Wolf static int coroutine_fn GRAPH_UNLOCKED
vpc_co_create_opts(BlockDriver * drv,const char * filename,QemuOpts * opts,Error ** errp)10934ec8df01SKevin Wolf vpc_co_create_opts(BlockDriver *drv, const char *filename,
10944ec8df01SKevin Wolf QemuOpts *opts, Error **errp)
1095182c8835SKevin Wolf {
1096182c8835SKevin Wolf BlockdevCreateOptions *create_options = NULL;
109792adf9dbSMarkus Armbruster QDict *qdict;
1098182c8835SKevin Wolf Visitor *v;
1099182c8835SKevin Wolf BlockDriverState *bs = NULL;
1100182c8835SKevin Wolf int ret;
1101182c8835SKevin Wolf
1102182c8835SKevin Wolf static const QDictRenames opt_renames[] = {
1103182c8835SKevin Wolf { VPC_OPT_FORCE_SIZE, "force-size" },
1104182c8835SKevin Wolf { NULL, NULL },
1105182c8835SKevin Wolf };
1106182c8835SKevin Wolf
1107182c8835SKevin Wolf /* Parse options and convert legacy syntax */
1108182c8835SKevin Wolf qdict = qemu_opts_to_qdict_filtered(opts, NULL, &vpc_create_opts, true);
1109182c8835SKevin Wolf
1110182c8835SKevin Wolf if (!qdict_rename_keys(qdict, opt_renames, errp)) {
1111182c8835SKevin Wolf ret = -EINVAL;
1112182c8835SKevin Wolf goto fail;
1113182c8835SKevin Wolf }
1114182c8835SKevin Wolf
1115182c8835SKevin Wolf /* Create and open the file (protocol layer) */
11162475a0d0SEmanuele Giuseppe Esposito ret = bdrv_co_create_file(filename, opts, errp);
1117182c8835SKevin Wolf if (ret < 0) {
1118182c8835SKevin Wolf goto fail;
1119182c8835SKevin Wolf }
1120182c8835SKevin Wolf
11216ef02851SKevin Wolf bs = bdrv_co_open(filename, NULL, NULL,
1122182c8835SKevin Wolf BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
1123182c8835SKevin Wolf if (bs == NULL) {
1124182c8835SKevin Wolf ret = -EIO;
1125182c8835SKevin Wolf goto fail;
1126182c8835SKevin Wolf }
1127182c8835SKevin Wolf
1128182c8835SKevin Wolf /* Now get the QAPI type BlockdevCreateOptions */
1129182c8835SKevin Wolf qdict_put_str(qdict, "driver", "vpc");
1130182c8835SKevin Wolf qdict_put_str(qdict, "file", bs->node_name);
1131182c8835SKevin Wolf
1132af91062eSMarkus Armbruster v = qobject_input_visitor_new_flat_confused(qdict, errp);
1133af91062eSMarkus Armbruster if (!v) {
1134182c8835SKevin Wolf ret = -EINVAL;
1135182c8835SKevin Wolf goto fail;
1136182c8835SKevin Wolf }
1137182c8835SKevin Wolf
1138b11a093cSMarkus Armbruster visit_type_BlockdevCreateOptions(v, NULL, &create_options, errp);
1139182c8835SKevin Wolf visit_free(v);
1140b11a093cSMarkus Armbruster if (!create_options) {
1141182c8835SKevin Wolf ret = -EINVAL;
1142182c8835SKevin Wolf goto fail;
1143182c8835SKevin Wolf }
1144182c8835SKevin Wolf
1145182c8835SKevin Wolf /* Silently round up size */
1146182c8835SKevin Wolf assert(create_options->driver == BLOCKDEV_DRIVER_VPC);
1147182c8835SKevin Wolf create_options->u.vpc.size =
1148182c8835SKevin Wolf ROUND_UP(create_options->u.vpc.size, BDRV_SECTOR_SIZE);
1149182c8835SKevin Wolf
11501cfeaf38SKevin Wolf if (!create_options->u.vpc.force_size) {
11511cfeaf38SKevin Wolf int64_t total_sectors;
11521cfeaf38SKevin Wolf ret = calculate_rounded_image_size(&create_options->u.vpc, NULL, NULL,
11531cfeaf38SKevin Wolf NULL, &total_sectors, errp);
11541cfeaf38SKevin Wolf if (ret < 0) {
11551cfeaf38SKevin Wolf goto fail;
11561cfeaf38SKevin Wolf }
11571cfeaf38SKevin Wolf
11581cfeaf38SKevin Wolf create_options->u.vpc.size = total_sectors * BDRV_SECTOR_SIZE;
11591cfeaf38SKevin Wolf }
11601cfeaf38SKevin Wolf
11611cfeaf38SKevin Wolf
1162182c8835SKevin Wolf /* Create the vpc image (format layer) */
1163182c8835SKevin Wolf ret = vpc_co_create(create_options, errp);
1164182c8835SKevin Wolf
1165182c8835SKevin Wolf fail:
1166cb3e7f08SMarc-André Lureau qobject_unref(qdict);
1167b2ab5f54SKevin Wolf bdrv_co_unref(bs);
1168182c8835SKevin Wolf qapi_free_BlockdevCreateOptions(create_options);
1169182c8835SKevin Wolf return ret;
1170182c8835SKevin Wolf }
1171182c8835SKevin Wolf
1172182c8835SKevin Wolf
vpc_has_zero_init(BlockDriverState * bs)117306717986SKevin Wolf static int GRAPH_RDLOCK vpc_has_zero_init(BlockDriverState *bs)
117472c6cc94SKevin Wolf {
117572c6cc94SKevin Wolf BDRVVPCState *s = bs->opaque;
117672c6cc94SKevin Wolf
1177275734e4SMarkus Armbruster if (be32_to_cpu(s->footer.type) == VHD_FIXED) {
11789a4f4c31SKevin Wolf return bdrv_has_zero_init(bs->file->bs);
117972c6cc94SKevin Wolf } else {
118072c6cc94SKevin Wolf return 1;
118172c6cc94SKevin Wolf }
118272c6cc94SKevin Wolf }
118372c6cc94SKevin Wolf
vpc_close(BlockDriverState * bs)1184019d6b8fSAnthony Liguori static void vpc_close(BlockDriverState *bs)
1185019d6b8fSAnthony Liguori {
1186019d6b8fSAnthony Liguori BDRVVPCState *s = bs->opaque;
118797f1c45cSJeff Cody qemu_vfree(s->pagetable);
1188019d6b8fSAnthony Liguori #ifdef CACHE
11897267c094SAnthony Liguori g_free(s->pageentry_u8);
1190019d6b8fSAnthony Liguori #endif
1191612ff3d8SKevin Wolf
1192c8a7fc51SSteve Sistare migrate_del_blocker(&s->migration_blocker);
1193019d6b8fSAnthony Liguori }
1194019d6b8fSAnthony Liguori
1195fec9921fSChunyan Liu static QemuOptsList vpc_create_opts = {
1196fec9921fSChunyan Liu .name = "vpc-create-opts",
1197fec9921fSChunyan Liu .head = QTAILQ_HEAD_INITIALIZER(vpc_create_opts.head),
1198fec9921fSChunyan Liu .desc = {
1199db08adf5SKevin Wolf {
1200db08adf5SKevin Wolf .name = BLOCK_OPT_SIZE,
1201fec9921fSChunyan Liu .type = QEMU_OPT_SIZE,
1202db08adf5SKevin Wolf .help = "Virtual disk size"
1203db08adf5SKevin Wolf },
120424da78dbSCharles Arnold {
120524da78dbSCharles Arnold .name = BLOCK_OPT_SUBFMT,
1206fec9921fSChunyan Liu .type = QEMU_OPT_STRING,
120724da78dbSCharles Arnold .help =
120824da78dbSCharles Arnold "Type of virtual hard disk format. Supported formats are "
120924da78dbSCharles Arnold "{dynamic (default) | fixed} "
121024da78dbSCharles Arnold },
1211fb9245c2SJeff Cody {
1212fb9245c2SJeff Cody .name = VPC_OPT_FORCE_SIZE,
1213fb9245c2SJeff Cody .type = QEMU_OPT_BOOL,
1214fb9245c2SJeff Cody .help = "Force disk size calculation to use the actual size "
1215fb9245c2SJeff Cody "specified, rather than using the nearest CHS-based "
1216fb9245c2SJeff Cody "calculation"
1217fb9245c2SJeff Cody },
1218fec9921fSChunyan Liu { /* end of list */ }
1219fec9921fSChunyan Liu }
12200e7e1989SKevin Wolf };
12210e7e1989SKevin Wolf
12222654267cSMax Reitz static const char *const vpc_strong_runtime_opts[] = {
12232654267cSMax Reitz VPC_OPT_SIZE_CALC,
12242654267cSMax Reitz
12252654267cSMax Reitz NULL
12262654267cSMax Reitz };
12272654267cSMax Reitz
1228019d6b8fSAnthony Liguori static BlockDriver bdrv_vpc = {
1229019d6b8fSAnthony Liguori .format_name = "vpc",
1230019d6b8fSAnthony Liguori .instance_size = sizeof(BDRVVPCState),
1231c68b89acSKevin Wolf
1232019d6b8fSAnthony Liguori .bdrv_probe = vpc_probe,
1233019d6b8fSAnthony Liguori .bdrv_open = vpc_open,
1234019d6b8fSAnthony Liguori .bdrv_close = vpc_close,
12353fe4b700SJeff Cody .bdrv_reopen_prepare = vpc_reopen_prepare,
123669dca43dSMax Reitz .bdrv_child_perm = bdrv_default_perms,
1237182c8835SKevin Wolf .bdrv_co_create = vpc_co_create,
1238efc75e2aSStefan Hajnoczi .bdrv_co_create_opts = vpc_co_create_opts,
12390e7e1989SKevin Wolf
1240d46b7cc6SKevin Wolf .bdrv_co_preadv = vpc_co_preadv,
1241513b0f02SKevin Wolf .bdrv_co_pwritev = vpc_co_pwritev,
12422f83673bSEric Blake .bdrv_co_block_status = vpc_co_block_status,
1243c68b89acSKevin Wolf
12443d47eb0aSEmanuele Giuseppe Esposito .bdrv_co_get_info = vpc_co_get_info,
124597b00e28SPaolo Bonzini
1246d67066d8SMax Reitz .is_format = true,
1247fec9921fSChunyan Liu .create_opts = &vpc_create_opts,
124872c6cc94SKevin Wolf .bdrv_has_zero_init = vpc_has_zero_init,
12492654267cSMax Reitz .strong_runtime_opts = vpc_strong_runtime_opts,
1250019d6b8fSAnthony Liguori };
1251019d6b8fSAnthony Liguori
bdrv_vpc_init(void)1252019d6b8fSAnthony Liguori static void bdrv_vpc_init(void)
1253019d6b8fSAnthony Liguori {
1254019d6b8fSAnthony Liguori bdrv_register(&bdrv_vpc);
1255019d6b8fSAnthony Liguori }
1256019d6b8fSAnthony Liguori
1257019d6b8fSAnthony Liguori block_init(bdrv_vpc_init);
1258