xref: /qemu/block/vpc.c (revision a4b740db)
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