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 */ 25019d6b8fSAnthony Liguori #include "qemu-common.h" 26737e150eSPaolo Bonzini #include "block/block_int.h" 271de7afc9SPaolo Bonzini #include "qemu/module.h" 28caf71f86SPaolo Bonzini #include "migration/migration.h" 291fe1fa51SCharles Arnold #if defined(CONFIG_UUID) 301fe1fa51SCharles Arnold #include <uuid/uuid.h> 311fe1fa51SCharles Arnold #endif 32019d6b8fSAnthony Liguori 33019d6b8fSAnthony Liguori /**************************************************************/ 34019d6b8fSAnthony Liguori 35019d6b8fSAnthony Liguori #define HEADER_SIZE 512 36019d6b8fSAnthony Liguori 37019d6b8fSAnthony Liguori //#define CACHE 38019d6b8fSAnthony Liguori 39019d6b8fSAnthony Liguori enum vhd_type { 40019d6b8fSAnthony Liguori VHD_FIXED = 2, 41019d6b8fSAnthony Liguori VHD_DYNAMIC = 3, 42019d6b8fSAnthony Liguori VHD_DIFFERENCING = 4, 43019d6b8fSAnthony Liguori }; 44019d6b8fSAnthony Liguori 45019d6b8fSAnthony Liguori // Seconds since Jan 1, 2000 0:00:00 (UTC) 46019d6b8fSAnthony Liguori #define VHD_TIMESTAMP_BASE 946684800 47019d6b8fSAnthony Liguori 4897f1c45cSJeff Cody #define VHD_MAX_SECTORS (65535LL * 255 * 255) 4997f1c45cSJeff Cody 50019d6b8fSAnthony Liguori // always big-endian 51e54835c0SJeff Cody typedef struct vhd_footer { 52019d6b8fSAnthony Liguori char creator[8]; // "conectix" 53019d6b8fSAnthony Liguori uint32_t features; 54019d6b8fSAnthony Liguori uint32_t version; 55019d6b8fSAnthony Liguori 56019d6b8fSAnthony Liguori // Offset of next header structure, 0xFFFFFFFF if none 57019d6b8fSAnthony Liguori uint64_t data_offset; 58019d6b8fSAnthony Liguori 59019d6b8fSAnthony Liguori // Seconds since Jan 1, 2000 0:00:00 (UTC) 60019d6b8fSAnthony Liguori uint32_t timestamp; 61019d6b8fSAnthony Liguori 62019d6b8fSAnthony Liguori char creator_app[4]; // "vpc " 63019d6b8fSAnthony Liguori uint16_t major; 64019d6b8fSAnthony Liguori uint16_t minor; 65019d6b8fSAnthony Liguori char creator_os[4]; // "Wi2k" 66019d6b8fSAnthony Liguori 67019d6b8fSAnthony Liguori uint64_t orig_size; 68019d6b8fSAnthony Liguori uint64_t size; 69019d6b8fSAnthony Liguori 70019d6b8fSAnthony Liguori uint16_t cyls; 71019d6b8fSAnthony Liguori uint8_t heads; 72019d6b8fSAnthony Liguori uint8_t secs_per_cyl; 73019d6b8fSAnthony Liguori 74019d6b8fSAnthony Liguori uint32_t type; 75019d6b8fSAnthony Liguori 76019d6b8fSAnthony Liguori // Checksum of the Hard Disk Footer ("one's complement of the sum of all 77019d6b8fSAnthony Liguori // the bytes in the footer without the checksum field") 78019d6b8fSAnthony Liguori uint32_t checksum; 79019d6b8fSAnthony Liguori 80019d6b8fSAnthony Liguori // UUID used to identify a parent hard disk (backing file) 81019d6b8fSAnthony Liguori uint8_t uuid[16]; 82019d6b8fSAnthony Liguori 83019d6b8fSAnthony Liguori uint8_t in_saved_state; 84e54835c0SJeff Cody } QEMU_PACKED VHDFooter; 85019d6b8fSAnthony Liguori 86e54835c0SJeff Cody typedef struct vhd_dyndisk_header { 87019d6b8fSAnthony Liguori char magic[8]; // "cxsparse" 88019d6b8fSAnthony Liguori 89019d6b8fSAnthony Liguori // Offset of next header structure, 0xFFFFFFFF if none 90019d6b8fSAnthony Liguori uint64_t data_offset; 91019d6b8fSAnthony Liguori 92019d6b8fSAnthony Liguori // Offset of the Block Allocation Table (BAT) 93019d6b8fSAnthony Liguori uint64_t table_offset; 94019d6b8fSAnthony Liguori 95019d6b8fSAnthony Liguori uint32_t version; 96019d6b8fSAnthony Liguori uint32_t max_table_entries; // 32bit/entry 97019d6b8fSAnthony Liguori 98019d6b8fSAnthony Liguori // 2 MB by default, must be a power of two 99019d6b8fSAnthony Liguori uint32_t block_size; 100019d6b8fSAnthony Liguori 101019d6b8fSAnthony Liguori uint32_t checksum; 102019d6b8fSAnthony Liguori uint8_t parent_uuid[16]; 103019d6b8fSAnthony Liguori uint32_t parent_timestamp; 104019d6b8fSAnthony Liguori uint32_t reserved; 105019d6b8fSAnthony Liguori 106019d6b8fSAnthony Liguori // Backing file name (in UTF-16) 107019d6b8fSAnthony Liguori uint8_t parent_name[512]; 108019d6b8fSAnthony Liguori 109019d6b8fSAnthony Liguori struct { 110019d6b8fSAnthony Liguori uint32_t platform; 111019d6b8fSAnthony Liguori uint32_t data_space; 112019d6b8fSAnthony Liguori uint32_t data_length; 113019d6b8fSAnthony Liguori uint32_t reserved; 114019d6b8fSAnthony Liguori uint64_t data_offset; 115019d6b8fSAnthony Liguori } parent_locator[8]; 116e54835c0SJeff Cody } QEMU_PACKED VHDDynDiskHeader; 117019d6b8fSAnthony Liguori 118019d6b8fSAnthony Liguori typedef struct BDRVVPCState { 119848c66e8SPaolo Bonzini CoMutex lock; 120019d6b8fSAnthony Liguori uint8_t footer_buf[HEADER_SIZE]; 121019d6b8fSAnthony Liguori uint64_t free_data_block_offset; 122019d6b8fSAnthony Liguori int max_table_entries; 123019d6b8fSAnthony Liguori uint32_t *pagetable; 124019d6b8fSAnthony Liguori uint64_t bat_offset; 125019d6b8fSAnthony Liguori uint64_t last_bitmap_offset; 126019d6b8fSAnthony Liguori 127019d6b8fSAnthony Liguori uint32_t block_size; 128019d6b8fSAnthony Liguori uint32_t bitmap_size; 129019d6b8fSAnthony Liguori 130019d6b8fSAnthony Liguori #ifdef CACHE 131019d6b8fSAnthony Liguori uint8_t *pageentry_u8; 132019d6b8fSAnthony Liguori uint32_t *pageentry_u32; 133019d6b8fSAnthony Liguori uint16_t *pageentry_u16; 134019d6b8fSAnthony Liguori 135019d6b8fSAnthony Liguori uint64_t last_bitmap; 136019d6b8fSAnthony Liguori #endif 137612ff3d8SKevin Wolf 138612ff3d8SKevin Wolf Error *migration_blocker; 139019d6b8fSAnthony Liguori } BDRVVPCState; 140019d6b8fSAnthony Liguori 141019d6b8fSAnthony Liguori static uint32_t vpc_checksum(uint8_t* buf, size_t size) 142019d6b8fSAnthony Liguori { 143019d6b8fSAnthony Liguori uint32_t res = 0; 144019d6b8fSAnthony Liguori int i; 145019d6b8fSAnthony Liguori 146019d6b8fSAnthony Liguori for (i = 0; i < size; i++) 147019d6b8fSAnthony Liguori res += buf[i]; 148019d6b8fSAnthony Liguori 149019d6b8fSAnthony Liguori return ~res; 150019d6b8fSAnthony Liguori } 151019d6b8fSAnthony Liguori 152019d6b8fSAnthony Liguori 153019d6b8fSAnthony Liguori static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename) 154019d6b8fSAnthony Liguori { 155019d6b8fSAnthony Liguori if (buf_size >= 8 && !strncmp((char *)buf, "conectix", 8)) 156019d6b8fSAnthony Liguori return 100; 157019d6b8fSAnthony Liguori return 0; 158019d6b8fSAnthony Liguori } 159019d6b8fSAnthony Liguori 160015a1036SMax Reitz static int vpc_open(BlockDriverState *bs, QDict *options, int flags, 161015a1036SMax Reitz Error **errp) 162019d6b8fSAnthony Liguori { 163019d6b8fSAnthony Liguori BDRVVPCState *s = bs->opaque; 16466f82ceeSKevin Wolf int i; 165e54835c0SJeff Cody VHDFooter *footer; 166e54835c0SJeff Cody VHDDynDiskHeader *dyndisk_header; 167019d6b8fSAnthony Liguori uint8_t buf[HEADER_SIZE]; 168019d6b8fSAnthony Liguori uint32_t checksum; 16997f1c45cSJeff Cody uint64_t computed_size; 17024da78dbSCharles Arnold int disk_type = VHD_DYNAMIC; 17159294e46SKevin Wolf int ret; 172019d6b8fSAnthony Liguori 17359294e46SKevin Wolf ret = bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE); 17459294e46SKevin Wolf if (ret < 0) { 175019d6b8fSAnthony Liguori goto fail; 17659294e46SKevin Wolf } 177019d6b8fSAnthony Liguori 178e54835c0SJeff Cody footer = (VHDFooter *) s->footer_buf; 17924da78dbSCharles Arnold if (strncmp(footer->creator, "conectix", 8)) { 18024da78dbSCharles Arnold int64_t offset = bdrv_getlength(bs->file); 18159294e46SKevin Wolf if (offset < 0) { 18259294e46SKevin Wolf ret = offset; 18359294e46SKevin Wolf goto fail; 18459294e46SKevin Wolf } else if (offset < HEADER_SIZE) { 18559294e46SKevin Wolf ret = -EINVAL; 186019d6b8fSAnthony Liguori goto fail; 18724da78dbSCharles Arnold } 18859294e46SKevin Wolf 18924da78dbSCharles Arnold /* If a fixed disk, the footer is found only at the end of the file */ 19059294e46SKevin Wolf ret = bdrv_pread(bs->file, offset-HEADER_SIZE, s->footer_buf, 19159294e46SKevin Wolf HEADER_SIZE); 19259294e46SKevin Wolf if (ret < 0) { 19324da78dbSCharles Arnold goto fail; 19424da78dbSCharles Arnold } 19524da78dbSCharles Arnold if (strncmp(footer->creator, "conectix", 8)) { 19676abe407SPaolo Bonzini error_setg(errp, "invalid VPC image"); 19776abe407SPaolo Bonzini ret = -EINVAL; 19824da78dbSCharles Arnold goto fail; 19924da78dbSCharles Arnold } 20024da78dbSCharles Arnold disk_type = VHD_FIXED; 20124da78dbSCharles Arnold } 202019d6b8fSAnthony Liguori 203019d6b8fSAnthony Liguori checksum = be32_to_cpu(footer->checksum); 204019d6b8fSAnthony Liguori footer->checksum = 0; 205019d6b8fSAnthony Liguori if (vpc_checksum(s->footer_buf, HEADER_SIZE) != checksum) 206019d6b8fSAnthony Liguori fprintf(stderr, "block-vpc: The header checksum of '%s' is " 20766f82ceeSKevin Wolf "incorrect.\n", bs->filename); 208019d6b8fSAnthony Liguori 209c088b691SZhang Shengju /* Write 'checksum' back to footer, or else will leave it with zero. */ 210c088b691SZhang Shengju footer->checksum = be32_to_cpu(checksum); 211c088b691SZhang Shengju 21233ccf667SStefan Hajnoczi // The visible size of a image in Virtual PC depends on the geometry 21333ccf667SStefan Hajnoczi // rather than on the size stored in the footer (the size in the footer 21433ccf667SStefan Hajnoczi // is too large usually) 21533ccf667SStefan Hajnoczi bs->total_sectors = (int64_t) 21633ccf667SStefan Hajnoczi be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl; 217019d6b8fSAnthony Liguori 2180173e7bbSPeter Lieven /* images created with disk2vhd report a far higher virtual size 2190173e7bbSPeter Lieven * than expected with the cyls * heads * sectors_per_cyl formula. 2200173e7bbSPeter Lieven * use the footer->size instead if the image was created with 2210173e7bbSPeter Lieven * disk2vhd. 2220173e7bbSPeter Lieven */ 2230173e7bbSPeter Lieven if (!strncmp(footer->creator_app, "d2v", 4)) { 2240173e7bbSPeter Lieven bs->total_sectors = be64_to_cpu(footer->size) / BDRV_SECTOR_SIZE; 2250173e7bbSPeter Lieven } 2260173e7bbSPeter Lieven 227258d2edbSCharles Arnold /* Allow a maximum disk size of approximately 2 TB */ 22897f1c45cSJeff Cody if (bs->total_sectors >= VHD_MAX_SECTORS) { 22959294e46SKevin Wolf ret = -EFBIG; 230efc8243dSSerge E. Hallyn goto fail; 231efc8243dSSerge E. Hallyn } 232efc8243dSSerge E. Hallyn 23324da78dbSCharles Arnold if (disk_type == VHD_DYNAMIC) { 23459294e46SKevin Wolf ret = bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf, 23559294e46SKevin Wolf HEADER_SIZE); 23659294e46SKevin Wolf if (ret < 0) { 237019d6b8fSAnthony Liguori goto fail; 23824da78dbSCharles Arnold } 239019d6b8fSAnthony Liguori 240e54835c0SJeff Cody dyndisk_header = (VHDDynDiskHeader *) buf; 241019d6b8fSAnthony Liguori 24224da78dbSCharles Arnold if (strncmp(dyndisk_header->magic, "cxsparse", 8)) { 24359294e46SKevin Wolf ret = -EINVAL; 244019d6b8fSAnthony Liguori goto fail; 24524da78dbSCharles Arnold } 246019d6b8fSAnthony Liguori 247019d6b8fSAnthony Liguori s->block_size = be32_to_cpu(dyndisk_header->block_size); 2485e71dfadSKevin Wolf if (!is_power_of_2(s->block_size) || s->block_size < BDRV_SECTOR_SIZE) { 2495e71dfadSKevin Wolf error_setg(errp, "Invalid block size %" PRIu32, s->block_size); 2505e71dfadSKevin Wolf ret = -EINVAL; 2515e71dfadSKevin Wolf goto fail; 2525e71dfadSKevin Wolf } 253019d6b8fSAnthony Liguori s->bitmap_size = ((s->block_size / (8 * 512)) + 511) & ~511; 254019d6b8fSAnthony Liguori 255019d6b8fSAnthony Liguori s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries); 25697f1c45cSJeff Cody 25797f1c45cSJeff Cody if ((bs->total_sectors * 512) / s->block_size > 0xffffffffU) { 25897f1c45cSJeff Cody ret = -EINVAL; 25997f1c45cSJeff Cody goto fail; 26097f1c45cSJeff Cody } 26197f1c45cSJeff Cody if (s->max_table_entries > (VHD_MAX_SECTORS * 512) / s->block_size) { 26297f1c45cSJeff Cody ret = -EINVAL; 26397f1c45cSJeff Cody goto fail; 26497f1c45cSJeff Cody } 26597f1c45cSJeff Cody 26697f1c45cSJeff Cody computed_size = (uint64_t) s->max_table_entries * s->block_size; 26797f1c45cSJeff Cody if (computed_size < bs->total_sectors * 512) { 26897f1c45cSJeff Cody ret = -EINVAL; 26997f1c45cSJeff Cody goto fail; 27097f1c45cSJeff Cody } 27197f1c45cSJeff Cody 27297f1c45cSJeff Cody s->pagetable = qemu_blockalign(bs, s->max_table_entries * 4); 273019d6b8fSAnthony Liguori 274019d6b8fSAnthony Liguori s->bat_offset = be64_to_cpu(dyndisk_header->table_offset); 27559294e46SKevin Wolf 27659294e46SKevin Wolf ret = bdrv_pread(bs->file, s->bat_offset, s->pagetable, 27759294e46SKevin Wolf s->max_table_entries * 4); 27859294e46SKevin Wolf if (ret < 0) { 279019d6b8fSAnthony Liguori goto fail; 28024da78dbSCharles Arnold } 281019d6b8fSAnthony Liguori 282019d6b8fSAnthony Liguori s->free_data_block_offset = 283019d6b8fSAnthony Liguori (s->bat_offset + (s->max_table_entries * 4) + 511) & ~511; 284019d6b8fSAnthony Liguori 285019d6b8fSAnthony Liguori for (i = 0; i < s->max_table_entries; i++) { 286019d6b8fSAnthony Liguori be32_to_cpus(&s->pagetable[i]); 287019d6b8fSAnthony Liguori if (s->pagetable[i] != 0xFFFFFFFF) { 288019d6b8fSAnthony Liguori int64_t next = (512 * (int64_t) s->pagetable[i]) + 289019d6b8fSAnthony Liguori s->bitmap_size + s->block_size; 290019d6b8fSAnthony Liguori 29124da78dbSCharles Arnold if (next > s->free_data_block_offset) { 292019d6b8fSAnthony Liguori s->free_data_block_offset = next; 293019d6b8fSAnthony Liguori } 294019d6b8fSAnthony Liguori } 29524da78dbSCharles Arnold } 296019d6b8fSAnthony Liguori 297fb8fe35fSPeter Lieven if (s->free_data_block_offset > bdrv_getlength(bs->file)) { 298fb8fe35fSPeter Lieven error_setg(errp, "block-vpc: free_data_block_offset points after " 299fb8fe35fSPeter Lieven "the end of file. The image has been truncated."); 300fb8fe35fSPeter Lieven ret = -EINVAL; 301fb8fe35fSPeter Lieven goto fail; 302fb8fe35fSPeter Lieven } 303fb8fe35fSPeter Lieven 304019d6b8fSAnthony Liguori s->last_bitmap_offset = (int64_t) -1; 305019d6b8fSAnthony Liguori 306019d6b8fSAnthony Liguori #ifdef CACHE 3077267c094SAnthony Liguori s->pageentry_u8 = g_malloc(512); 308019d6b8fSAnthony Liguori s->pageentry_u32 = s->pageentry_u8; 309019d6b8fSAnthony Liguori s->pageentry_u16 = s->pageentry_u8; 310019d6b8fSAnthony Liguori s->last_pagetable = -1; 311019d6b8fSAnthony Liguori #endif 31224da78dbSCharles Arnold } 313019d6b8fSAnthony Liguori 314848c66e8SPaolo Bonzini qemu_co_mutex_init(&s->lock); 315612ff3d8SKevin Wolf 316612ff3d8SKevin Wolf /* Disable migration when VHD images are used */ 317612ff3d8SKevin Wolf error_set(&s->migration_blocker, 318612ff3d8SKevin Wolf QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED, 319612ff3d8SKevin Wolf "vpc", bs->device_name, "live migration"); 320612ff3d8SKevin Wolf migrate_add_blocker(s->migration_blocker); 321612ff3d8SKevin Wolf 322019d6b8fSAnthony Liguori return 0; 32359294e46SKevin Wolf 324019d6b8fSAnthony Liguori fail: 32597f1c45cSJeff Cody qemu_vfree(s->pagetable); 32659294e46SKevin Wolf #ifdef CACHE 32759294e46SKevin Wolf g_free(s->pageentry_u8); 32859294e46SKevin Wolf #endif 32959294e46SKevin Wolf return ret; 330019d6b8fSAnthony Liguori } 331019d6b8fSAnthony Liguori 3323fe4b700SJeff Cody static int vpc_reopen_prepare(BDRVReopenState *state, 3333fe4b700SJeff Cody BlockReopenQueue *queue, Error **errp) 3343fe4b700SJeff Cody { 3353fe4b700SJeff Cody return 0; 3363fe4b700SJeff Cody } 3373fe4b700SJeff Cody 338019d6b8fSAnthony Liguori /* 339019d6b8fSAnthony Liguori * Returns the absolute byte offset of the given sector in the image file. 340019d6b8fSAnthony Liguori * If the sector is not allocated, -1 is returned instead. 341019d6b8fSAnthony Liguori * 342019d6b8fSAnthony Liguori * The parameter write must be 1 if the offset will be used for a write 343019d6b8fSAnthony Liguori * operation (the block bitmaps is updated then), 0 otherwise. 344019d6b8fSAnthony Liguori */ 345019d6b8fSAnthony Liguori static inline int64_t get_sector_offset(BlockDriverState *bs, 346019d6b8fSAnthony Liguori int64_t sector_num, int write) 347019d6b8fSAnthony Liguori { 348019d6b8fSAnthony Liguori BDRVVPCState *s = bs->opaque; 349019d6b8fSAnthony Liguori uint64_t offset = sector_num * 512; 350019d6b8fSAnthony Liguori uint64_t bitmap_offset, block_offset; 351019d6b8fSAnthony Liguori uint32_t pagetable_index, pageentry_index; 352019d6b8fSAnthony Liguori 353019d6b8fSAnthony Liguori pagetable_index = offset / s->block_size; 354019d6b8fSAnthony Liguori pageentry_index = (offset % s->block_size) / 512; 355019d6b8fSAnthony Liguori 356019d6b8fSAnthony Liguori if (pagetable_index >= s->max_table_entries || s->pagetable[pagetable_index] == 0xffffffff) 357019d6b8fSAnthony Liguori return -1; // not allocated 358019d6b8fSAnthony Liguori 359019d6b8fSAnthony Liguori bitmap_offset = 512 * (uint64_t) s->pagetable[pagetable_index]; 360019d6b8fSAnthony Liguori block_offset = bitmap_offset + s->bitmap_size + (512 * pageentry_index); 361019d6b8fSAnthony Liguori 362019d6b8fSAnthony Liguori // We must ensure that we don't write to any sectors which are marked as 363019d6b8fSAnthony Liguori // unused in the bitmap. We get away with setting all bits in the block 364019d6b8fSAnthony Liguori // bitmap each time we write to a new block. This might cause Virtual PC to 365019d6b8fSAnthony Liguori // miss sparse read optimization, but it's not a problem in terms of 366019d6b8fSAnthony Liguori // correctness. 367019d6b8fSAnthony Liguori if (write && (s->last_bitmap_offset != bitmap_offset)) { 368019d6b8fSAnthony Liguori uint8_t bitmap[s->bitmap_size]; 369019d6b8fSAnthony Liguori 370019d6b8fSAnthony Liguori s->last_bitmap_offset = bitmap_offset; 371019d6b8fSAnthony Liguori memset(bitmap, 0xff, s->bitmap_size); 372078a458eSKevin Wolf bdrv_pwrite_sync(bs->file, bitmap_offset, bitmap, s->bitmap_size); 373019d6b8fSAnthony Liguori } 374019d6b8fSAnthony Liguori 375019d6b8fSAnthony Liguori // printf("sector: %" PRIx64 ", index: %x, offset: %x, bioff: %" PRIx64 ", bloff: %" PRIx64 "\n", 376019d6b8fSAnthony Liguori // sector_num, pagetable_index, pageentry_index, 377019d6b8fSAnthony Liguori // bitmap_offset, block_offset); 378019d6b8fSAnthony Liguori 379019d6b8fSAnthony Liguori // disabled by reason 380019d6b8fSAnthony Liguori #if 0 381019d6b8fSAnthony Liguori #ifdef CACHE 382019d6b8fSAnthony Liguori if (bitmap_offset != s->last_bitmap) 383019d6b8fSAnthony Liguori { 384019d6b8fSAnthony Liguori lseek(s->fd, bitmap_offset, SEEK_SET); 385019d6b8fSAnthony Liguori 386019d6b8fSAnthony Liguori s->last_bitmap = bitmap_offset; 387019d6b8fSAnthony Liguori 388019d6b8fSAnthony Liguori // Scary! Bitmap is stored as big endian 32bit entries, 389019d6b8fSAnthony Liguori // while we used to look it up byte by byte 390019d6b8fSAnthony Liguori read(s->fd, s->pageentry_u8, 512); 391019d6b8fSAnthony Liguori for (i = 0; i < 128; i++) 392019d6b8fSAnthony Liguori be32_to_cpus(&s->pageentry_u32[i]); 393019d6b8fSAnthony Liguori } 394019d6b8fSAnthony Liguori 395019d6b8fSAnthony Liguori if ((s->pageentry_u8[pageentry_index / 8] >> (pageentry_index % 8)) & 1) 396019d6b8fSAnthony Liguori return -1; 397019d6b8fSAnthony Liguori #else 398019d6b8fSAnthony Liguori lseek(s->fd, bitmap_offset + (pageentry_index / 8), SEEK_SET); 399019d6b8fSAnthony Liguori 400019d6b8fSAnthony Liguori read(s->fd, &bitmap_entry, 1); 401019d6b8fSAnthony Liguori 402019d6b8fSAnthony Liguori if ((bitmap_entry >> (pageentry_index % 8)) & 1) 403019d6b8fSAnthony Liguori return -1; // not allocated 404019d6b8fSAnthony Liguori #endif 405019d6b8fSAnthony Liguori #endif 406019d6b8fSAnthony Liguori 407019d6b8fSAnthony Liguori return block_offset; 408019d6b8fSAnthony Liguori } 409019d6b8fSAnthony Liguori 410019d6b8fSAnthony Liguori /* 411019d6b8fSAnthony Liguori * Writes the footer to the end of the image file. This is needed when the 412019d6b8fSAnthony Liguori * file grows as it overwrites the old footer 413019d6b8fSAnthony Liguori * 414019d6b8fSAnthony Liguori * Returns 0 on success and < 0 on error 415019d6b8fSAnthony Liguori */ 416019d6b8fSAnthony Liguori static int rewrite_footer(BlockDriverState* bs) 417019d6b8fSAnthony Liguori { 418019d6b8fSAnthony Liguori int ret; 419019d6b8fSAnthony Liguori BDRVVPCState *s = bs->opaque; 420019d6b8fSAnthony Liguori int64_t offset = s->free_data_block_offset; 421019d6b8fSAnthony Liguori 422078a458eSKevin Wolf ret = bdrv_pwrite_sync(bs->file, offset, s->footer_buf, HEADER_SIZE); 423019d6b8fSAnthony Liguori if (ret < 0) 424019d6b8fSAnthony Liguori return ret; 425019d6b8fSAnthony Liguori 426019d6b8fSAnthony Liguori return 0; 427019d6b8fSAnthony Liguori } 428019d6b8fSAnthony Liguori 429019d6b8fSAnthony Liguori /* 430019d6b8fSAnthony Liguori * Allocates a new block. This involves writing a new footer and updating 431019d6b8fSAnthony Liguori * the Block Allocation Table to use the space at the old end of the image 432019d6b8fSAnthony Liguori * file (overwriting the old footer) 433019d6b8fSAnthony Liguori * 434019d6b8fSAnthony Liguori * Returns the sectors' offset in the image file on success and < 0 on error 435019d6b8fSAnthony Liguori */ 436019d6b8fSAnthony Liguori static int64_t alloc_block(BlockDriverState* bs, int64_t sector_num) 437019d6b8fSAnthony Liguori { 438019d6b8fSAnthony Liguori BDRVVPCState *s = bs->opaque; 439019d6b8fSAnthony Liguori int64_t bat_offset; 440019d6b8fSAnthony Liguori uint32_t index, bat_value; 441019d6b8fSAnthony Liguori int ret; 442019d6b8fSAnthony Liguori uint8_t bitmap[s->bitmap_size]; 443019d6b8fSAnthony Liguori 444019d6b8fSAnthony Liguori // Check if sector_num is valid 445019d6b8fSAnthony Liguori if ((sector_num < 0) || (sector_num > bs->total_sectors)) 446019d6b8fSAnthony Liguori return -1; 447019d6b8fSAnthony Liguori 448019d6b8fSAnthony Liguori // Write entry into in-memory BAT 449019d6b8fSAnthony Liguori index = (sector_num * 512) / s->block_size; 450019d6b8fSAnthony Liguori if (s->pagetable[index] != 0xFFFFFFFF) 451019d6b8fSAnthony Liguori return -1; 452019d6b8fSAnthony Liguori 453019d6b8fSAnthony Liguori s->pagetable[index] = s->free_data_block_offset / 512; 454019d6b8fSAnthony Liguori 455019d6b8fSAnthony Liguori // Initialize the block's bitmap 456019d6b8fSAnthony Liguori memset(bitmap, 0xff, s->bitmap_size); 4575bb1cbacSKevin Wolf ret = bdrv_pwrite_sync(bs->file, s->free_data_block_offset, bitmap, 458078a458eSKevin Wolf s->bitmap_size); 4595bb1cbacSKevin Wolf if (ret < 0) { 4605bb1cbacSKevin Wolf return ret; 4615bb1cbacSKevin Wolf } 462019d6b8fSAnthony Liguori 463019d6b8fSAnthony Liguori // Write new footer (the old one will be overwritten) 464019d6b8fSAnthony Liguori s->free_data_block_offset += s->block_size + s->bitmap_size; 465019d6b8fSAnthony Liguori ret = rewrite_footer(bs); 466019d6b8fSAnthony Liguori if (ret < 0) 467019d6b8fSAnthony Liguori goto fail; 468019d6b8fSAnthony Liguori 469019d6b8fSAnthony Liguori // Write BAT entry to disk 470019d6b8fSAnthony Liguori bat_offset = s->bat_offset + (4 * index); 471019d6b8fSAnthony Liguori bat_value = be32_to_cpu(s->pagetable[index]); 472078a458eSKevin Wolf ret = bdrv_pwrite_sync(bs->file, bat_offset, &bat_value, 4); 473019d6b8fSAnthony Liguori if (ret < 0) 474019d6b8fSAnthony Liguori goto fail; 475019d6b8fSAnthony Liguori 476019d6b8fSAnthony Liguori return get_sector_offset(bs, sector_num, 0); 477019d6b8fSAnthony Liguori 478019d6b8fSAnthony Liguori fail: 479019d6b8fSAnthony Liguori s->free_data_block_offset -= (s->block_size + s->bitmap_size); 480019d6b8fSAnthony Liguori return -1; 481019d6b8fSAnthony Liguori } 482019d6b8fSAnthony Liguori 48397b00e28SPaolo Bonzini static int vpc_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) 48497b00e28SPaolo Bonzini { 48597b00e28SPaolo Bonzini BDRVVPCState *s = (BDRVVPCState *)bs->opaque; 48697b00e28SPaolo Bonzini VHDFooter *footer = (VHDFooter *) s->footer_buf; 48797b00e28SPaolo Bonzini 48897b00e28SPaolo Bonzini if (cpu_to_be32(footer->type) != VHD_FIXED) { 48997b00e28SPaolo Bonzini bdi->cluster_size = s->block_size; 49097b00e28SPaolo Bonzini } 49197b00e28SPaolo Bonzini 49295de6d70SPaolo Bonzini bdi->unallocated_blocks_are_zero = true; 49397b00e28SPaolo Bonzini return 0; 49497b00e28SPaolo Bonzini } 49597b00e28SPaolo Bonzini 496019d6b8fSAnthony Liguori static int vpc_read(BlockDriverState *bs, int64_t sector_num, 497019d6b8fSAnthony Liguori uint8_t *buf, int nb_sectors) 498019d6b8fSAnthony Liguori { 4996c6ea921SKevin Wolf BDRVVPCState *s = bs->opaque; 500019d6b8fSAnthony Liguori int ret; 501019d6b8fSAnthony Liguori int64_t offset; 5026c6ea921SKevin Wolf int64_t sectors, sectors_per_block; 503e54835c0SJeff Cody VHDFooter *footer = (VHDFooter *) s->footer_buf; 504019d6b8fSAnthony Liguori 50524da78dbSCharles Arnold if (cpu_to_be32(footer->type) == VHD_FIXED) { 50624da78dbSCharles Arnold return bdrv_read(bs->file, sector_num, buf, nb_sectors); 50724da78dbSCharles Arnold } 508019d6b8fSAnthony Liguori while (nb_sectors > 0) { 509019d6b8fSAnthony Liguori offset = get_sector_offset(bs, sector_num, 0); 510019d6b8fSAnthony Liguori 5116c6ea921SKevin Wolf sectors_per_block = s->block_size >> BDRV_SECTOR_BITS; 5126c6ea921SKevin Wolf sectors = sectors_per_block - (sector_num % sectors_per_block); 5136c6ea921SKevin Wolf if (sectors > nb_sectors) { 5146c6ea921SKevin Wolf sectors = nb_sectors; 515019d6b8fSAnthony Liguori } 516019d6b8fSAnthony Liguori 5176c6ea921SKevin Wolf if (offset == -1) { 5186c6ea921SKevin Wolf memset(buf, 0, sectors * BDRV_SECTOR_SIZE); 5196c6ea921SKevin Wolf } else { 5206c6ea921SKevin Wolf ret = bdrv_pread(bs->file, offset, buf, 5216c6ea921SKevin Wolf sectors * BDRV_SECTOR_SIZE); 5226c6ea921SKevin Wolf if (ret != sectors * BDRV_SECTOR_SIZE) { 5236c6ea921SKevin Wolf return -1; 5246c6ea921SKevin Wolf } 5256c6ea921SKevin Wolf } 5266c6ea921SKevin Wolf 5276c6ea921SKevin Wolf nb_sectors -= sectors; 5286c6ea921SKevin Wolf sector_num += sectors; 5296c6ea921SKevin Wolf buf += sectors * BDRV_SECTOR_SIZE; 530019d6b8fSAnthony Liguori } 531019d6b8fSAnthony Liguori return 0; 532019d6b8fSAnthony Liguori } 533019d6b8fSAnthony Liguori 5342914caa0SPaolo Bonzini static coroutine_fn int vpc_co_read(BlockDriverState *bs, int64_t sector_num, 5352914caa0SPaolo Bonzini uint8_t *buf, int nb_sectors) 5362914caa0SPaolo Bonzini { 5372914caa0SPaolo Bonzini int ret; 5382914caa0SPaolo Bonzini BDRVVPCState *s = bs->opaque; 5392914caa0SPaolo Bonzini qemu_co_mutex_lock(&s->lock); 5402914caa0SPaolo Bonzini ret = vpc_read(bs, sector_num, buf, nb_sectors); 5412914caa0SPaolo Bonzini qemu_co_mutex_unlock(&s->lock); 5422914caa0SPaolo Bonzini return ret; 5432914caa0SPaolo Bonzini } 5442914caa0SPaolo Bonzini 545019d6b8fSAnthony Liguori static int vpc_write(BlockDriverState *bs, int64_t sector_num, 546019d6b8fSAnthony Liguori const uint8_t *buf, int nb_sectors) 547019d6b8fSAnthony Liguori { 5486c6ea921SKevin Wolf BDRVVPCState *s = bs->opaque; 549019d6b8fSAnthony Liguori int64_t offset; 5506c6ea921SKevin Wolf int64_t sectors, sectors_per_block; 551019d6b8fSAnthony Liguori int ret; 552e54835c0SJeff Cody VHDFooter *footer = (VHDFooter *) s->footer_buf; 553019d6b8fSAnthony Liguori 55424da78dbSCharles Arnold if (cpu_to_be32(footer->type) == VHD_FIXED) { 55524da78dbSCharles Arnold return bdrv_write(bs->file, sector_num, buf, nb_sectors); 55624da78dbSCharles Arnold } 557019d6b8fSAnthony Liguori while (nb_sectors > 0) { 558019d6b8fSAnthony Liguori offset = get_sector_offset(bs, sector_num, 1); 559019d6b8fSAnthony Liguori 5606c6ea921SKevin Wolf sectors_per_block = s->block_size >> BDRV_SECTOR_BITS; 5616c6ea921SKevin Wolf sectors = sectors_per_block - (sector_num % sectors_per_block); 5626c6ea921SKevin Wolf if (sectors > nb_sectors) { 5636c6ea921SKevin Wolf sectors = nb_sectors; 5646c6ea921SKevin Wolf } 5656c6ea921SKevin Wolf 566019d6b8fSAnthony Liguori if (offset == -1) { 567019d6b8fSAnthony Liguori offset = alloc_block(bs, sector_num); 568019d6b8fSAnthony Liguori if (offset < 0) 569019d6b8fSAnthony Liguori return -1; 570019d6b8fSAnthony Liguori } 571019d6b8fSAnthony Liguori 5726c6ea921SKevin Wolf ret = bdrv_pwrite(bs->file, offset, buf, sectors * BDRV_SECTOR_SIZE); 5736c6ea921SKevin Wolf if (ret != sectors * BDRV_SECTOR_SIZE) { 574019d6b8fSAnthony Liguori return -1; 5756c6ea921SKevin Wolf } 576019d6b8fSAnthony Liguori 5776c6ea921SKevin Wolf nb_sectors -= sectors; 5786c6ea921SKevin Wolf sector_num += sectors; 5796c6ea921SKevin Wolf buf += sectors * BDRV_SECTOR_SIZE; 580019d6b8fSAnthony Liguori } 581019d6b8fSAnthony Liguori 582019d6b8fSAnthony Liguori return 0; 583019d6b8fSAnthony Liguori } 584019d6b8fSAnthony Liguori 585e183ef75SPaolo Bonzini static coroutine_fn int vpc_co_write(BlockDriverState *bs, int64_t sector_num, 586e183ef75SPaolo Bonzini const uint8_t *buf, int nb_sectors) 587e183ef75SPaolo Bonzini { 588e183ef75SPaolo Bonzini int ret; 589e183ef75SPaolo Bonzini BDRVVPCState *s = bs->opaque; 590e183ef75SPaolo Bonzini qemu_co_mutex_lock(&s->lock); 591e183ef75SPaolo Bonzini ret = vpc_write(bs, sector_num, buf, nb_sectors); 592e183ef75SPaolo Bonzini qemu_co_mutex_unlock(&s->lock); 593e183ef75SPaolo Bonzini return ret; 594e183ef75SPaolo Bonzini } 595e183ef75SPaolo Bonzini 596019d6b8fSAnthony Liguori /* 597019d6b8fSAnthony Liguori * Calculates the number of cylinders, heads and sectors per cylinder 598019d6b8fSAnthony Liguori * based on a given number of sectors. This is the algorithm described 599019d6b8fSAnthony Liguori * in the VHD specification. 600019d6b8fSAnthony Liguori * 601019d6b8fSAnthony Liguori * Note that the geometry doesn't always exactly match total_sectors but 602019d6b8fSAnthony Liguori * may round it down. 603019d6b8fSAnthony Liguori * 604258d2edbSCharles Arnold * Returns 0 on success, -EFBIG if the size is larger than ~2 TB. Override 605258d2edbSCharles Arnold * the hardware EIDE and ATA-2 limit of 16 heads (max disk size of 127 GB) 606258d2edbSCharles Arnold * and instead allow up to 255 heads. 607019d6b8fSAnthony Liguori */ 608019d6b8fSAnthony Liguori static int calculate_geometry(int64_t total_sectors, uint16_t* cyls, 609019d6b8fSAnthony Liguori uint8_t* heads, uint8_t* secs_per_cyl) 610019d6b8fSAnthony Liguori { 611019d6b8fSAnthony Liguori uint32_t cyls_times_heads; 612019d6b8fSAnthony Liguori 613258d2edbSCharles Arnold /* Allow a maximum disk size of approximately 2 TB */ 614258d2edbSCharles Arnold if (total_sectors > 65535LL * 255 * 255) { 615019d6b8fSAnthony Liguori return -EFBIG; 616258d2edbSCharles Arnold } 617019d6b8fSAnthony Liguori 618019d6b8fSAnthony Liguori if (total_sectors > 65535 * 16 * 63) { 619019d6b8fSAnthony Liguori *secs_per_cyl = 255; 620258d2edbSCharles Arnold if (total_sectors > 65535 * 16 * 255) { 621258d2edbSCharles Arnold *heads = 255; 622258d2edbSCharles Arnold } else { 623019d6b8fSAnthony Liguori *heads = 16; 624258d2edbSCharles Arnold } 625019d6b8fSAnthony Liguori cyls_times_heads = total_sectors / *secs_per_cyl; 626019d6b8fSAnthony Liguori } else { 627019d6b8fSAnthony Liguori *secs_per_cyl = 17; 628019d6b8fSAnthony Liguori cyls_times_heads = total_sectors / *secs_per_cyl; 629019d6b8fSAnthony Liguori *heads = (cyls_times_heads + 1023) / 1024; 630019d6b8fSAnthony Liguori 631019d6b8fSAnthony Liguori if (*heads < 4) 632019d6b8fSAnthony Liguori *heads = 4; 633019d6b8fSAnthony Liguori 634019d6b8fSAnthony Liguori if (cyls_times_heads >= (*heads * 1024) || *heads > 16) { 635019d6b8fSAnthony Liguori *secs_per_cyl = 31; 636019d6b8fSAnthony Liguori *heads = 16; 637019d6b8fSAnthony Liguori cyls_times_heads = total_sectors / *secs_per_cyl; 638019d6b8fSAnthony Liguori } 639019d6b8fSAnthony Liguori 640019d6b8fSAnthony Liguori if (cyls_times_heads >= (*heads * 1024)) { 641019d6b8fSAnthony Liguori *secs_per_cyl = 63; 642019d6b8fSAnthony Liguori *heads = 16; 643019d6b8fSAnthony Liguori cyls_times_heads = total_sectors / *secs_per_cyl; 644019d6b8fSAnthony Liguori } 645019d6b8fSAnthony Liguori } 646019d6b8fSAnthony Liguori 647dede4188SStefan Weil *cyls = cyls_times_heads / *heads; 648019d6b8fSAnthony Liguori 649019d6b8fSAnthony Liguori return 0; 650019d6b8fSAnthony Liguori } 651019d6b8fSAnthony Liguori 652*fef6070eSJeff Cody static int create_dynamic_disk(BlockDriverState *bs, uint8_t *buf, 653*fef6070eSJeff Cody int64_t total_sectors) 654019d6b8fSAnthony Liguori { 655e54835c0SJeff Cody VHDDynDiskHeader *dyndisk_header = 656e54835c0SJeff Cody (VHDDynDiskHeader *) buf; 657019d6b8fSAnthony Liguori size_t block_size, num_bat_entries; 65824da78dbSCharles Arnold int i; 659*fef6070eSJeff Cody int ret; 660*fef6070eSJeff Cody int64_t offset = 0; 661019d6b8fSAnthony Liguori 662019d6b8fSAnthony Liguori // Write the footer (twice: at the beginning and at the end) 663019d6b8fSAnthony Liguori block_size = 0x200000; 664019d6b8fSAnthony Liguori num_bat_entries = (total_sectors + block_size / 512) / (block_size / 512); 665019d6b8fSAnthony Liguori 666*fef6070eSJeff Cody ret = bdrv_pwrite_sync(bs, offset, buf, HEADER_SIZE); 667*fef6070eSJeff Cody if (ret) { 668f0ff243aSBlue Swirl goto fail; 669f0ff243aSBlue Swirl } 670019d6b8fSAnthony Liguori 671*fef6070eSJeff Cody offset = 1536 + ((num_bat_entries * 4 + 511) & ~511); 672*fef6070eSJeff Cody ret = bdrv_pwrite_sync(bs, offset, buf, HEADER_SIZE); 673*fef6070eSJeff Cody if (ret < 0) { 674f0ff243aSBlue Swirl goto fail; 675f0ff243aSBlue Swirl } 676019d6b8fSAnthony Liguori 677019d6b8fSAnthony Liguori // Write the initial BAT 678*fef6070eSJeff Cody offset = 3 * 512; 679019d6b8fSAnthony Liguori 680019d6b8fSAnthony Liguori memset(buf, 0xFF, 512); 681f0ff243aSBlue Swirl for (i = 0; i < (num_bat_entries * 4 + 511) / 512; i++) { 682*fef6070eSJeff Cody ret = bdrv_pwrite_sync(bs, offset, buf, 512); 683*fef6070eSJeff Cody if (ret < 0) { 684f0ff243aSBlue Swirl goto fail; 685f0ff243aSBlue Swirl } 686*fef6070eSJeff Cody offset += 512; 687f0ff243aSBlue Swirl } 688019d6b8fSAnthony Liguori 689019d6b8fSAnthony Liguori // Prepare the Dynamic Disk Header 690019d6b8fSAnthony Liguori memset(buf, 0, 1024); 691019d6b8fSAnthony Liguori 6925ec4d682SNathan Froyd memcpy(dyndisk_header->magic, "cxsparse", 8); 693019d6b8fSAnthony Liguori 69478439f6aSCharles Arnold /* 69578439f6aSCharles Arnold * Note: The spec is actually wrong here for data_offset, it says 69678439f6aSCharles Arnold * 0xFFFFFFFF, but MS tools expect all 64 bits to be set. 69778439f6aSCharles Arnold */ 69878439f6aSCharles Arnold dyndisk_header->data_offset = be64_to_cpu(0xFFFFFFFFFFFFFFFFULL); 699019d6b8fSAnthony Liguori dyndisk_header->table_offset = be64_to_cpu(3 * 512); 700019d6b8fSAnthony Liguori dyndisk_header->version = be32_to_cpu(0x00010000); 701019d6b8fSAnthony Liguori dyndisk_header->block_size = be32_to_cpu(block_size); 702019d6b8fSAnthony Liguori dyndisk_header->max_table_entries = be32_to_cpu(num_bat_entries); 703019d6b8fSAnthony Liguori 704019d6b8fSAnthony Liguori dyndisk_header->checksum = be32_to_cpu(vpc_checksum(buf, 1024)); 705019d6b8fSAnthony Liguori 706019d6b8fSAnthony Liguori // Write the header 707*fef6070eSJeff Cody offset = 512; 708019d6b8fSAnthony Liguori 709*fef6070eSJeff Cody ret = bdrv_pwrite_sync(bs, offset, buf, 1024); 710*fef6070eSJeff Cody if (ret < 0) { 711f0ff243aSBlue Swirl goto fail; 712f0ff243aSBlue Swirl } 713f0ff243aSBlue Swirl 714f0ff243aSBlue Swirl fail: 71524da78dbSCharles Arnold return ret; 71624da78dbSCharles Arnold } 71724da78dbSCharles Arnold 718*fef6070eSJeff Cody static int create_fixed_disk(BlockDriverState *bs, uint8_t *buf, 719*fef6070eSJeff Cody int64_t total_size) 72024da78dbSCharles Arnold { 721*fef6070eSJeff Cody int ret; 72224da78dbSCharles Arnold 72324da78dbSCharles Arnold /* Add footer to total size */ 724*fef6070eSJeff Cody total_size += HEADER_SIZE; 725*fef6070eSJeff Cody 726*fef6070eSJeff Cody ret = bdrv_truncate(bs, total_size); 727*fef6070eSJeff Cody if (ret < 0) { 728*fef6070eSJeff Cody return ret; 72924da78dbSCharles Arnold } 73024da78dbSCharles Arnold 731*fef6070eSJeff Cody ret = bdrv_pwrite_sync(bs, total_size - HEADER_SIZE, buf, HEADER_SIZE); 732*fef6070eSJeff Cody if (ret < 0) { 733*fef6070eSJeff Cody return ret; 734*fef6070eSJeff Cody } 73524da78dbSCharles Arnold 73624da78dbSCharles Arnold return ret; 73724da78dbSCharles Arnold } 73824da78dbSCharles Arnold 739fec9921fSChunyan Liu static int vpc_create(const char *filename, QemuOpts *opts, Error **errp) 74024da78dbSCharles Arnold { 74124da78dbSCharles Arnold uint8_t buf[1024]; 742e54835c0SJeff Cody VHDFooter *footer = (VHDFooter *) buf; 743fec9921fSChunyan Liu char *disk_type_param; 744*fef6070eSJeff Cody int i; 74524da78dbSCharles Arnold uint16_t cyls = 0; 74624da78dbSCharles Arnold uint8_t heads = 0; 74724da78dbSCharles Arnold uint8_t secs_per_cyl = 0; 74824da78dbSCharles Arnold int64_t total_sectors; 74924da78dbSCharles Arnold int64_t total_size; 75024da78dbSCharles Arnold int disk_type; 75124da78dbSCharles Arnold int ret = -EIO; 752*fef6070eSJeff Cody Error *local_err = NULL; 753*fef6070eSJeff Cody BlockDriverState *bs = NULL; 75424da78dbSCharles Arnold 75524da78dbSCharles Arnold /* Read out options */ 756fec9921fSChunyan Liu total_size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); 757fec9921fSChunyan Liu disk_type_param = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT); 758fec9921fSChunyan Liu if (disk_type_param) { 759fec9921fSChunyan Liu if (!strcmp(disk_type_param, "dynamic")) { 76024da78dbSCharles Arnold disk_type = VHD_DYNAMIC; 761fec9921fSChunyan Liu } else if (!strcmp(disk_type_param, "fixed")) { 76224da78dbSCharles Arnold disk_type = VHD_FIXED; 76324da78dbSCharles Arnold } else { 764fec9921fSChunyan Liu ret = -EINVAL; 765fec9921fSChunyan Liu goto out; 76624da78dbSCharles Arnold } 76724da78dbSCharles Arnold } else { 76824da78dbSCharles Arnold disk_type = VHD_DYNAMIC; 76924da78dbSCharles Arnold } 77024da78dbSCharles Arnold 771*fef6070eSJeff Cody ret = bdrv_create_file(filename, opts, &local_err); 772*fef6070eSJeff Cody if (ret < 0) { 773*fef6070eSJeff Cody error_propagate(errp, local_err); 774fec9921fSChunyan Liu goto out; 77524da78dbSCharles Arnold } 776*fef6070eSJeff Cody ret = bdrv_open(&bs, filename, NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, 777*fef6070eSJeff Cody NULL, &local_err); 778*fef6070eSJeff Cody if (ret < 0) { 779*fef6070eSJeff Cody error_propagate(errp, local_err); 780*fef6070eSJeff Cody goto out; 7814ab15590SChunyan Liu } 7824ab15590SChunyan Liu 783ecd880d9SKevin Wolf /* 784ecd880d9SKevin Wolf * Calculate matching total_size and geometry. Increase the number of 785ecd880d9SKevin Wolf * sectors requested until we get enough (or fail). This ensures that 786ecd880d9SKevin Wolf * qemu-img convert doesn't truncate images, but rather rounds up. 787ecd880d9SKevin Wolf */ 78824da78dbSCharles Arnold total_sectors = total_size / BDRV_SECTOR_SIZE; 789ecd880d9SKevin Wolf for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) { 790ecd880d9SKevin Wolf if (calculate_geometry(total_sectors + i, &cyls, &heads, 791ecd880d9SKevin Wolf &secs_per_cyl)) 792ecd880d9SKevin Wolf { 79324da78dbSCharles Arnold ret = -EFBIG; 794*fef6070eSJeff Cody goto out; 79524da78dbSCharles Arnold } 79624da78dbSCharles Arnold } 797ecd880d9SKevin Wolf 79824da78dbSCharles Arnold total_sectors = (int64_t) cyls * heads * secs_per_cyl; 79924da78dbSCharles Arnold 80024da78dbSCharles Arnold /* Prepare the Hard Disk Footer */ 80124da78dbSCharles Arnold memset(buf, 0, 1024); 80224da78dbSCharles Arnold 80324da78dbSCharles Arnold memcpy(footer->creator, "conectix", 8); 80424da78dbSCharles Arnold /* TODO Check if "qemu" creator_app is ok for VPC */ 80524da78dbSCharles Arnold memcpy(footer->creator_app, "qemu", 4); 80624da78dbSCharles Arnold memcpy(footer->creator_os, "Wi2k", 4); 80724da78dbSCharles Arnold 80824da78dbSCharles Arnold footer->features = be32_to_cpu(0x02); 80924da78dbSCharles Arnold footer->version = be32_to_cpu(0x00010000); 81024da78dbSCharles Arnold if (disk_type == VHD_DYNAMIC) { 81124da78dbSCharles Arnold footer->data_offset = be64_to_cpu(HEADER_SIZE); 81224da78dbSCharles Arnold } else { 81324da78dbSCharles Arnold footer->data_offset = be64_to_cpu(0xFFFFFFFFFFFFFFFFULL); 81424da78dbSCharles Arnold } 81524da78dbSCharles Arnold footer->timestamp = be32_to_cpu(time(NULL) - VHD_TIMESTAMP_BASE); 81624da78dbSCharles Arnold 81724da78dbSCharles Arnold /* Version of Virtual PC 2007 */ 81824da78dbSCharles Arnold footer->major = be16_to_cpu(0x0005); 81924da78dbSCharles Arnold footer->minor = be16_to_cpu(0x0003); 82024da78dbSCharles Arnold if (disk_type == VHD_DYNAMIC) { 82124da78dbSCharles Arnold footer->orig_size = be64_to_cpu(total_sectors * 512); 82224da78dbSCharles Arnold footer->size = be64_to_cpu(total_sectors * 512); 82324da78dbSCharles Arnold } else { 82424da78dbSCharles Arnold footer->orig_size = be64_to_cpu(total_size); 82524da78dbSCharles Arnold footer->size = be64_to_cpu(total_size); 82624da78dbSCharles Arnold } 82724da78dbSCharles Arnold footer->cyls = be16_to_cpu(cyls); 82824da78dbSCharles Arnold footer->heads = heads; 82924da78dbSCharles Arnold footer->secs_per_cyl = secs_per_cyl; 83024da78dbSCharles Arnold 83124da78dbSCharles Arnold footer->type = be32_to_cpu(disk_type); 83224da78dbSCharles Arnold 8331fe1fa51SCharles Arnold #if defined(CONFIG_UUID) 8341fe1fa51SCharles Arnold uuid_generate(footer->uuid); 8351fe1fa51SCharles Arnold #endif 83624da78dbSCharles Arnold 83724da78dbSCharles Arnold footer->checksum = be32_to_cpu(vpc_checksum(buf, HEADER_SIZE)); 83824da78dbSCharles Arnold 83924da78dbSCharles Arnold if (disk_type == VHD_DYNAMIC) { 840*fef6070eSJeff Cody ret = create_dynamic_disk(bs, buf, total_sectors); 84124da78dbSCharles Arnold } else { 842*fef6070eSJeff Cody ret = create_fixed_disk(bs, buf, total_size); 84324da78dbSCharles Arnold } 84424da78dbSCharles Arnold 845fec9921fSChunyan Liu out: 846*fef6070eSJeff Cody bdrv_unref(bs); 847fec9921fSChunyan Liu g_free(disk_type_param); 848f0ff243aSBlue Swirl return ret; 849019d6b8fSAnthony Liguori } 850019d6b8fSAnthony Liguori 85172c6cc94SKevin Wolf static int vpc_has_zero_init(BlockDriverState *bs) 85272c6cc94SKevin Wolf { 85372c6cc94SKevin Wolf BDRVVPCState *s = bs->opaque; 854e54835c0SJeff Cody VHDFooter *footer = (VHDFooter *) s->footer_buf; 85572c6cc94SKevin Wolf 85672c6cc94SKevin Wolf if (cpu_to_be32(footer->type) == VHD_FIXED) { 85772c6cc94SKevin Wolf return bdrv_has_zero_init(bs->file); 85872c6cc94SKevin Wolf } else { 85972c6cc94SKevin Wolf return 1; 86072c6cc94SKevin Wolf } 86172c6cc94SKevin Wolf } 86272c6cc94SKevin Wolf 863019d6b8fSAnthony Liguori static void vpc_close(BlockDriverState *bs) 864019d6b8fSAnthony Liguori { 865019d6b8fSAnthony Liguori BDRVVPCState *s = bs->opaque; 86697f1c45cSJeff Cody qemu_vfree(s->pagetable); 867019d6b8fSAnthony Liguori #ifdef CACHE 8687267c094SAnthony Liguori g_free(s->pageentry_u8); 869019d6b8fSAnthony Liguori #endif 870612ff3d8SKevin Wolf 871612ff3d8SKevin Wolf migrate_del_blocker(s->migration_blocker); 872612ff3d8SKevin Wolf error_free(s->migration_blocker); 873019d6b8fSAnthony Liguori } 874019d6b8fSAnthony Liguori 875fec9921fSChunyan Liu static QemuOptsList vpc_create_opts = { 876fec9921fSChunyan Liu .name = "vpc-create-opts", 877fec9921fSChunyan Liu .head = QTAILQ_HEAD_INITIALIZER(vpc_create_opts.head), 878fec9921fSChunyan Liu .desc = { 879db08adf5SKevin Wolf { 880db08adf5SKevin Wolf .name = BLOCK_OPT_SIZE, 881fec9921fSChunyan Liu .type = QEMU_OPT_SIZE, 882db08adf5SKevin Wolf .help = "Virtual disk size" 883db08adf5SKevin Wolf }, 88424da78dbSCharles Arnold { 88524da78dbSCharles Arnold .name = BLOCK_OPT_SUBFMT, 886fec9921fSChunyan Liu .type = QEMU_OPT_STRING, 88724da78dbSCharles Arnold .help = 88824da78dbSCharles Arnold "Type of virtual hard disk format. Supported formats are " 88924da78dbSCharles Arnold "{dynamic (default) | fixed} " 89024da78dbSCharles Arnold }, 8914ab15590SChunyan Liu { 8924ab15590SChunyan Liu .name = BLOCK_OPT_NOCOW, 8934ab15590SChunyan Liu .type = QEMU_OPT_BOOL, 8944ab15590SChunyan Liu .help = "Turn off copy-on-write (valid only on btrfs)" 8954ab15590SChunyan Liu }, 896fec9921fSChunyan Liu { /* end of list */ } 897fec9921fSChunyan Liu } 8980e7e1989SKevin Wolf }; 8990e7e1989SKevin Wolf 900019d6b8fSAnthony Liguori static BlockDriver bdrv_vpc = { 901019d6b8fSAnthony Liguori .format_name = "vpc", 902019d6b8fSAnthony Liguori .instance_size = sizeof(BDRVVPCState), 903c68b89acSKevin Wolf 904019d6b8fSAnthony Liguori .bdrv_probe = vpc_probe, 905019d6b8fSAnthony Liguori .bdrv_open = vpc_open, 906019d6b8fSAnthony Liguori .bdrv_close = vpc_close, 9073fe4b700SJeff Cody .bdrv_reopen_prepare = vpc_reopen_prepare, 908c282e1fdSChunyan Liu .bdrv_create = vpc_create, 9090e7e1989SKevin Wolf 910c68b89acSKevin Wolf .bdrv_read = vpc_co_read, 911c68b89acSKevin Wolf .bdrv_write = vpc_co_write, 912c68b89acSKevin Wolf 91397b00e28SPaolo Bonzini .bdrv_get_info = vpc_get_info, 91497b00e28SPaolo Bonzini 915fec9921fSChunyan Liu .create_opts = &vpc_create_opts, 91672c6cc94SKevin Wolf .bdrv_has_zero_init = vpc_has_zero_init, 917019d6b8fSAnthony Liguori }; 918019d6b8fSAnthony Liguori 919019d6b8fSAnthony Liguori static void bdrv_vpc_init(void) 920019d6b8fSAnthony Liguori { 921019d6b8fSAnthony Liguori bdrv_register(&bdrv_vpc); 922019d6b8fSAnthony Liguori } 923019d6b8fSAnthony Liguori 924019d6b8fSAnthony Liguori block_init(bdrv_vpc_init); 925