15d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner/*
2cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner * Block driver for Connectix / Microsoft Virtual PC images
35d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner *
45d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Copyright (c) 2005 Alex Beregszaszi
55d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Copyright (c) 2009 Kevin Wolf <kwolf@suse.de>
65d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner *
75d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Permission is hereby granted, free of charge, to any person obtaining a copy
85d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * of this software and associated documentation files (the "Software"), to deal
95d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * in the Software without restriction, including without limitation the rights
105d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
115d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * copies of the Software, and to permit persons to whom the Software is
125d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * furnished to do so, subject to the following conditions:
135d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner *
145d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * The above copyright notice and this permission notice shall be included in
155d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * all copies or substantial portions of the Software.
165d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner *
175d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
185d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
195d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
205d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
215d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
225d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
235d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * THE SOFTWARE.
245d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner */
255d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#include "qemu-common.h"
265d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#include "block_int.h"
275d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#include "module.h"
285d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
295d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner/**************************************************************/
305d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
315d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#define HEADER_SIZE 512
325d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
335d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner//#define CACHE
345d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
355d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerenum vhd_type {
365d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    VHD_FIXED           = 2,
375d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    VHD_DYNAMIC         = 3,
385d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    VHD_DIFFERENCING    = 4,
395d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner};
405d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
415d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner// Seconds since Jan 1, 2000 0:00:00 (UTC)
425d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#define VHD_TIMESTAMP_BASE 946684800
435d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
445d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner// always big-endian
455d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstruct vhd_footer {
465d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    char        creator[8]; // "conectix"
475d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint32_t    features;
485d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint32_t    version;
495d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
505d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // Offset of next header structure, 0xFFFFFFFF if none
515d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint64_t    data_offset;
525d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
535d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // Seconds since Jan 1, 2000 0:00:00 (UTC)
545d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint32_t    timestamp;
555d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
565d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    char        creator_app[4]; // "vpc "
575d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint16_t    major;
585d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint16_t    minor;
595d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    char        creator_os[4]; // "Wi2k"
605d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
615d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint64_t    orig_size;
625d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint64_t    size;
635d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
645d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint16_t    cyls;
655d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint8_t     heads;
665d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint8_t     secs_per_cyl;
675d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
685d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint32_t    type;
695d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
705d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // Checksum of the Hard Disk Footer ("one's complement of the sum of all
715d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // the bytes in the footer without the checksum field")
725d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint32_t    checksum;
735d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
745d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // UUID used to identify a parent hard disk (backing file)
755d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint8_t     uuid[16];
765d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
775d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint8_t     in_saved_state;
785d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner};
795d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
805d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstruct vhd_dyndisk_header {
815d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    char        magic[8]; // "cxsparse"
825d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
835d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // Offset of next header structure, 0xFFFFFFFF if none
845d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint64_t    data_offset;
855d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
865d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // Offset of the Block Allocation Table (BAT)
875d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint64_t    table_offset;
885d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
895d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint32_t    version;
905d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint32_t    max_table_entries; // 32bit/entry
915d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
925d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // 2 MB by default, must be a power of two
935d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint32_t    block_size;
945d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
955d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint32_t    checksum;
965d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint8_t     parent_uuid[16];
975d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint32_t    parent_timestamp;
985d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint32_t    reserved;
995d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1005d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // Backing file name (in UTF-16)
1015d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint8_t     parent_name[512];
1025d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1035d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    struct {
1045d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        uint32_t    platform;
1055d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        uint32_t    data_space;
1065d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        uint32_t    data_length;
1075d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        uint32_t    reserved;
1085d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        uint64_t    data_offset;
1095d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    } parent_locator[8];
1105d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner};
1115d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1125d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnertypedef struct BDRVVPCState {
1135d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BlockDriverState *hd;
1145d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1155d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint8_t footer_buf[HEADER_SIZE];
1165d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint64_t free_data_block_offset;
1175d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    int max_table_entries;
1185d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint32_t *pagetable;
1195d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint64_t bat_offset;
1205d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint64_t last_bitmap_offset;
1215d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1225d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint32_t block_size;
1235d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint32_t bitmap_size;
1245d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1255d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#ifdef CACHE
1265d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint8_t *pageentry_u8;
1275d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint32_t *pageentry_u32;
1285d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint16_t *pageentry_u16;
1295d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1305d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint64_t last_bitmap;
1315d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#endif
1325d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner} BDRVVPCState;
1335d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1345d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic uint32_t vpc_checksum(uint8_t* buf, size_t size)
1355d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
1365d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint32_t res = 0;
1375d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    int i;
1385d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1395d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    for (i = 0; i < size; i++)
1405d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        res += buf[i];
1415d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1425d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    return ~res;
1435d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
1445d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1455d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1465d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic int vpc_probe(const uint8_t *buf, int buf_size, const char *filename)
1475d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
1485d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (buf_size >= 8 && !strncmp((char *)buf, "conectix", 8))
1495d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner	return 100;
1505d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    return 0;
1515d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
1525d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
153cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turnerstatic int vpc_open(BlockDriverState *bs, int flags)
1545d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
1555d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BDRVVPCState *s = bs->opaque;
156cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner    int i;
1575d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    struct vhd_footer* footer;
1585d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    struct vhd_dyndisk_header* dyndisk_header;
1595d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint8_t buf[HEADER_SIZE];
1605d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint32_t checksum;
1615d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
162cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner    if (bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE) != HEADER_SIZE)
1635d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        goto fail;
1645d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1655d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    footer = (struct vhd_footer*) s->footer_buf;
1665d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (strncmp(footer->creator, "conectix", 8))
1675d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        goto fail;
1685d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1695d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    checksum = be32_to_cpu(footer->checksum);
1705d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    footer->checksum = 0;
1715d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (vpc_checksum(s->footer_buf, HEADER_SIZE) != checksum)
1725d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        fprintf(stderr, "block-vpc: The header checksum of '%s' is "
173cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner            "incorrect.\n", bs->filename);
1745d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1755d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // The visible size of a image in Virtual PC depends on the geometry
1765d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // rather than on the size stored in the footer (the size in the footer
1775d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // is too large usually)
1785d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    bs->total_sectors = (int64_t)
1795d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl;
1805d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
181cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner    if (bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf, HEADER_SIZE)
1825d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            != HEADER_SIZE)
1835d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        goto fail;
1845d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1855d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    dyndisk_header = (struct vhd_dyndisk_header*) buf;
1865d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1875d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (strncmp(dyndisk_header->magic, "cxsparse", 8))
1885d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        goto fail;
1895d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1905d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1915d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->block_size = be32_to_cpu(dyndisk_header->block_size);
1925d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->bitmap_size = ((s->block_size / (8 * 512)) + 511) & ~511;
1935d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1945d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries);
1955d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->pagetable = qemu_malloc(s->max_table_entries * 4);
1965d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
1975d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->bat_offset = be64_to_cpu(dyndisk_header->table_offset);
198cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner    if (bdrv_pread(bs->file, s->bat_offset, s->pagetable,
1995d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            s->max_table_entries * 4) != s->max_table_entries * 4)
2005d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner	    goto fail;
2015d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
2025d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->free_data_block_offset =
2035d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        (s->bat_offset + (s->max_table_entries * 4) + 511) & ~511;
2045d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
2055d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    for (i = 0; i < s->max_table_entries; i++) {
2065d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        be32_to_cpus(&s->pagetable[i]);
2075d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        if (s->pagetable[i] != 0xFFFFFFFF) {
2085d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            int64_t next = (512 * (int64_t) s->pagetable[i]) +
2095d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                s->bitmap_size + s->block_size;
2105d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
2115d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            if (next> s->free_data_block_offset)
2125d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                s->free_data_block_offset = next;
2135d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        }
2145d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    }
2155d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
2165d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->last_bitmap_offset = (int64_t) -1;
2175d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
2185d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#ifdef CACHE
2195d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->pageentry_u8 = qemu_malloc(512);
2205d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->pageentry_u32 = s->pageentry_u8;
2215d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->pageentry_u16 = s->pageentry_u8;
2225d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->last_pagetable = -1;
2235d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#endif
2245d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
2255d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    return 0;
2265d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner fail:
2275d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    return -1;
2285d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
2295d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
2305d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner/*
2315d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Returns the absolute byte offset of the given sector in the image file.
2325d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * If the sector is not allocated, -1 is returned instead.
2335d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner *
2345d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * The parameter write must be 1 if the offset will be used for a write
2355d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * operation (the block bitmaps is updated then), 0 otherwise.
2365d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner */
2375d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic inline int64_t get_sector_offset(BlockDriverState *bs,
2385d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    int64_t sector_num, int write)
2395d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
2405d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BDRVVPCState *s = bs->opaque;
2415d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint64_t offset = sector_num * 512;
2425d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint64_t bitmap_offset, block_offset;
2435d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint32_t pagetable_index, pageentry_index;
2445d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
2455d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    pagetable_index = offset / s->block_size;
2465d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    pageentry_index = (offset % s->block_size) / 512;
2475d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
2485d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (pagetable_index >= s->max_table_entries || s->pagetable[pagetable_index] == 0xffffffff)
2495d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        return -1; // not allocated
2505d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
2515d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    bitmap_offset = 512 * (uint64_t) s->pagetable[pagetable_index];
2525d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    block_offset = bitmap_offset + s->bitmap_size + (512 * pageentry_index);
2535d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
2545d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // We must ensure that we don't write to any sectors which are marked as
2555d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // unused in the bitmap. We get away with setting all bits in the block
2565d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // bitmap each time we write to a new block. This might cause Virtual PC to
2575d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // miss sparse read optimization, but it's not a problem in terms of
2585d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // correctness.
2595d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (write && (s->last_bitmap_offset != bitmap_offset)) {
2605d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        uint8_t bitmap[s->bitmap_size];
2615d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
2625d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        s->last_bitmap_offset = bitmap_offset;
2635d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        memset(bitmap, 0xff, s->bitmap_size);
264cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner        bdrv_pwrite_sync(bs->file, bitmap_offset, bitmap, s->bitmap_size);
2655d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    }
2665d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
2675d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner//    printf("sector: %" PRIx64 ", index: %x, offset: %x, bioff: %" PRIx64 ", bloff: %" PRIx64 "\n",
2685d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner//	sector_num, pagetable_index, pageentry_index,
2695d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner//	bitmap_offset, block_offset);
2705d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
2715d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner// disabled by reason
2725d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#if 0
2735d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#ifdef CACHE
2745d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (bitmap_offset != s->last_bitmap)
2755d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    {
2765d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner	lseek(s->fd, bitmap_offset, SEEK_SET);
2775d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
2785d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner	s->last_bitmap = bitmap_offset;
2795d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
2805d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner	// Scary! Bitmap is stored as big endian 32bit entries,
2815d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner	// while we used to look it up byte by byte
2825d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner	read(s->fd, s->pageentry_u8, 512);
2835d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner	for (i = 0; i < 128; i++)
2845d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner	    be32_to_cpus(&s->pageentry_u32[i]);
2855d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    }
2865d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
2875d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if ((s->pageentry_u8[pageentry_index / 8] >> (pageentry_index % 8)) & 1)
2885d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner	return -1;
2895d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#else
2905d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    lseek(s->fd, bitmap_offset + (pageentry_index / 8), SEEK_SET);
2915d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
2925d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    read(s->fd, &bitmap_entry, 1);
2935d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
2945d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if ((bitmap_entry >> (pageentry_index % 8)) & 1)
2955d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner	return -1; // not allocated
2965d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#endif
2975d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#endif
2985d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
2995d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    return block_offset;
3005d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
3015d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
3025d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner/*
3035d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Writes the footer to the end of the image file. This is needed when the
3045d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * file grows as it overwrites the old footer
3055d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner *
3065d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Returns 0 on success and < 0 on error
3075d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner */
3085d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic int rewrite_footer(BlockDriverState* bs)
3095d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
3105d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    int ret;
3115d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BDRVVPCState *s = bs->opaque;
3125d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    int64_t offset = s->free_data_block_offset;
3135d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
314cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner    ret = bdrv_pwrite_sync(bs->file, offset, s->footer_buf, HEADER_SIZE);
3155d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (ret < 0)
3165d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        return ret;
3175d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
3185d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    return 0;
3195d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
3205d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
3215d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner/*
3225d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Allocates a new block. This involves writing a new footer and updating
3235d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * the Block Allocation Table to use the space at the old end of the image
3245d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * file (overwriting the old footer)
3255d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner *
3265d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Returns the sectors' offset in the image file on success and < 0 on error
3275d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner */
3285d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic int64_t alloc_block(BlockDriverState* bs, int64_t sector_num)
3295d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
3305d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BDRVVPCState *s = bs->opaque;
3315d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    int64_t bat_offset;
3325d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint32_t index, bat_value;
3335d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    int ret;
3345d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint8_t bitmap[s->bitmap_size];
3355d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
3365d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // Check if sector_num is valid
3375d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if ((sector_num < 0) || (sector_num > bs->total_sectors))
3385d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        return -1;
3395d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
3405d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // Write entry into in-memory BAT
3415d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    index = (sector_num * 512) / s->block_size;
3425d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (s->pagetable[index] != 0xFFFFFFFF)
3435d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        return -1;
3445d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
3455d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->pagetable[index] = s->free_data_block_offset / 512;
3465d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
3475d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // Initialize the block's bitmap
3485d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    memset(bitmap, 0xff, s->bitmap_size);
349cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner    bdrv_pwrite_sync(bs->file, s->free_data_block_offset, bitmap,
350cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner        s->bitmap_size);
3515d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
3525d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // Write new footer (the old one will be overwritten)
3535d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->free_data_block_offset += s->block_size + s->bitmap_size;
3545d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    ret = rewrite_footer(bs);
3555d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (ret < 0)
3565d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        goto fail;
3575d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
3585d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // Write BAT entry to disk
3595d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    bat_offset = s->bat_offset + (4 * index);
3605d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    bat_value = be32_to_cpu(s->pagetable[index]);
361cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner    ret = bdrv_pwrite_sync(bs->file, bat_offset, &bat_value, 4);
3625d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (ret < 0)
3635d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        goto fail;
3645d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
3655d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    return get_sector_offset(bs, sector_num, 0);
3665d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
3675d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerfail:
3685d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    s->free_data_block_offset -= (s->block_size + s->bitmap_size);
3695d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    return -1;
3705d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
3715d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
3725d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic int vpc_read(BlockDriverState *bs, int64_t sector_num,
3735d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                    uint8_t *buf, int nb_sectors)
3745d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
3755d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BDRVVPCState *s = bs->opaque;
3765d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    int ret;
3775d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    int64_t offset;
378cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner    int64_t sectors, sectors_per_block;
3795d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
3805d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    while (nb_sectors > 0) {
3815d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        offset = get_sector_offset(bs, sector_num, 0);
3825d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
383cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner        sectors_per_block = s->block_size >> BDRV_SECTOR_BITS;
384cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner        sectors = sectors_per_block - (sector_num % sectors_per_block);
385cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner        if (sectors > nb_sectors) {
386cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner            sectors = nb_sectors;
387cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner        }
388cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner
3895d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        if (offset == -1) {
390cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner            memset(buf, 0, sectors * BDRV_SECTOR_SIZE);
3915d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        } else {
392cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner            ret = bdrv_pread(bs->file, offset, buf,
393cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner                sectors * BDRV_SECTOR_SIZE);
394cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner            if (ret != sectors * BDRV_SECTOR_SIZE) {
3955d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                return -1;
396cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner            }
3975d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        }
3985d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
399cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner        nb_sectors -= sectors;
400cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner        sector_num += sectors;
401cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner        buf += sectors * BDRV_SECTOR_SIZE;
4025d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    }
4035d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    return 0;
4045d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
4055d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4065d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic int vpc_write(BlockDriverState *bs, int64_t sector_num,
4075d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    const uint8_t *buf, int nb_sectors)
4085d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
4095d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BDRVVPCState *s = bs->opaque;
4105d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    int64_t offset;
411cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner    int64_t sectors, sectors_per_block;
4125d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    int ret;
4135d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4145d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    while (nb_sectors > 0) {
4155d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        offset = get_sector_offset(bs, sector_num, 1);
4165d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
417cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner        sectors_per_block = s->block_size >> BDRV_SECTOR_BITS;
418cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner        sectors = sectors_per_block - (sector_num % sectors_per_block);
419cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner        if (sectors > nb_sectors) {
420cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner            sectors = nb_sectors;
421cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner        }
422cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner
4235d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        if (offset == -1) {
4245d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            offset = alloc_block(bs, sector_num);
4255d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            if (offset < 0)
4265d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner                return -1;
4275d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        }
4285d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
429cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner        ret = bdrv_pwrite(bs->file, offset, buf, sectors * BDRV_SECTOR_SIZE);
430cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner        if (ret != sectors * BDRV_SECTOR_SIZE) {
4315d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            return -1;
432cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner        }
4335d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
434cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner        nb_sectors -= sectors;
435cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner        sector_num += sectors;
436cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner        buf += sectors * BDRV_SECTOR_SIZE;
4375d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    }
4385d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4395d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    return 0;
4405d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
4415d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4425d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4435d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner/*
4445d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Calculates the number of cylinders, heads and sectors per cylinder
4455d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * based on a given number of sectors. This is the algorithm described
4465d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * in the VHD specification.
4475d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner *
4485d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Note that the geometry doesn't always exactly match total_sectors but
4495d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * may round it down.
4505d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner *
4515d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner * Returns 0 on success, -EFBIG if the size is larger than 127 GB
4525d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner */
4535d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic int calculate_geometry(int64_t total_sectors, uint16_t* cyls,
4545d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint8_t* heads, uint8_t* secs_per_cyl)
4555d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
4565d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint32_t cyls_times_heads;
4575d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4585d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (total_sectors > 65535 * 16 * 255)
4595d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        return -EFBIG;
4605d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4615d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (total_sectors > 65535 * 16 * 63) {
4625d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        *secs_per_cyl = 255;
4635d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        *heads = 16;
4645d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        cyls_times_heads = total_sectors / *secs_per_cyl;
4655d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    } else {
4665d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        *secs_per_cyl = 17;
4675d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        cyls_times_heads = total_sectors / *secs_per_cyl;
4685d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        *heads = (cyls_times_heads + 1023) / 1024;
4695d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4705d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        if (*heads < 4)
4715d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            *heads = 4;
4725d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4735d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        if (cyls_times_heads >= (*heads * 1024) || *heads > 16) {
4745d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            *secs_per_cyl = 31;
4755d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            *heads = 16;
4765d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            cyls_times_heads = total_sectors / *secs_per_cyl;
4775d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        }
4785d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4795d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        if (cyls_times_heads >= (*heads * 1024)) {
4805d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            *secs_per_cyl = 63;
4815d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            *heads = 16;
4825d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            cyls_times_heads = total_sectors / *secs_per_cyl;
4835d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        }
4845d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    }
4855d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
486cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner    *cyls = cyls_times_heads / *heads;
4875d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4885d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    return 0;
4895d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
4905d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
4915d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic int vpc_create(const char *filename, QEMUOptionParameter *options)
4925d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
4935d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    uint8_t buf[1024];
4945d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    struct vhd_footer* footer = (struct vhd_footer*) buf;
4955d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    struct vhd_dyndisk_header* dyndisk_header =
4965d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        (struct vhd_dyndisk_header*) buf;
4975d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    int fd, i;
498cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner    uint16_t cyls = 0;
499cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner    uint8_t heads = 0;
500cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner    uint8_t secs_per_cyl = 0;
5015d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    size_t block_size, num_bat_entries;
5025d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    int64_t total_sectors = 0;
5035d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5045d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // Read out options
5055d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    while (options && options->name) {
5065d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        if (!strcmp(options->name, "size")) {
5075d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            total_sectors = options->value.n / 512;
5085d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        }
5095d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        options++;
5105d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    }
5115d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5125d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // Create the file
5135d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
5145d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (fd < 0)
5155d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        return -EIO;
5165d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
517cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner    /* Calculate matching total_size and geometry. Increase the number of
518cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner       sectors requested until we get enough (or fail). */
519cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner    for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) {
520cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner        if (calculate_geometry(total_sectors + i,
521cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner                               &cyls, &heads, &secs_per_cyl)) {
522cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner            return -EFBIG;
523cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner        }
524cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner    }
5255d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    total_sectors = (int64_t) cyls * heads * secs_per_cyl;
5265d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5275d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // Prepare the Hard Disk Footer
5285d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    memset(buf, 0, 1024);
5295d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
530cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner    memcpy(footer->creator, "conectix", 8);
5315d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // TODO Check if "qemu" creator_app is ok for VPC
532cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner    memcpy(footer->creator_app, "qemu", 4);
533cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner    memcpy(footer->creator_os, "Wi2k", 4);
5345d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5355d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    footer->features = be32_to_cpu(0x02);
5365d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    footer->version = be32_to_cpu(0x00010000);
5375d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    footer->data_offset = be64_to_cpu(HEADER_SIZE);
5385d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    footer->timestamp = be32_to_cpu(time(NULL) - VHD_TIMESTAMP_BASE);
5395d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5405d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // Version of Virtual PC 2007
5415d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    footer->major = be16_to_cpu(0x0005);
5425d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    footer->minor =be16_to_cpu(0x0003);
5435d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5445d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    footer->orig_size = be64_to_cpu(total_sectors * 512);
5455d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    footer->size = be64_to_cpu(total_sectors * 512);
5465d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5475d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    footer->cyls = be16_to_cpu(cyls);
5485d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    footer->heads = heads;
5495d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    footer->secs_per_cyl = secs_per_cyl;
5505d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5515d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    footer->type = be32_to_cpu(VHD_DYNAMIC);
5525d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5535d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // TODO uuid is missing
5545d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5555d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    footer->checksum = be32_to_cpu(vpc_checksum(buf, HEADER_SIZE));
5565d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5575d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // Write the footer (twice: at the beginning and at the end)
5585d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    block_size = 0x200000;
5595d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    num_bat_entries = (total_sectors + block_size / 512) / (block_size / 512);
5605d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5615d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE)
5625d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        return -EIO;
5635d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5645d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (lseek(fd, 1536 + ((num_bat_entries * 4 + 511) & ~511), SEEK_SET) < 0)
5655d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        return -EIO;
5665d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE)
5675d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        return -EIO;
5685d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5695d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // Write the initial BAT
5705d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (lseek(fd, 3 * 512, SEEK_SET) < 0)
5715d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        return -EIO;
5725d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5735d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    memset(buf, 0xFF, 512);
5745d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    for (i = 0; i < (num_bat_entries * 4 + 511) / 512; i++)
5755d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        if (write(fd, buf, 512) != 512)
5765d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner            return -EIO;
5775d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5785d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5795d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // Prepare the Dynamic Disk Header
5805d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    memset(buf, 0, 1024);
5815d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
582cb42a1b1461e02efb034582ac5d8f71534723b92David 'Digit' Turner    memcpy(dyndisk_header->magic, "cxsparse", 8);
5835d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5845d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    dyndisk_header->data_offset = be64_to_cpu(0xFFFFFFFF);
5855d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    dyndisk_header->table_offset = be64_to_cpu(3 * 512);
5865d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    dyndisk_header->version = be32_to_cpu(0x00010000);
5875d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    dyndisk_header->block_size = be32_to_cpu(block_size);
5885d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    dyndisk_header->max_table_entries = be32_to_cpu(num_bat_entries);
5895d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5905d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    dyndisk_header->checksum = be32_to_cpu(vpc_checksum(buf, 1024));
5915d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5925d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    // Write the header
5935d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (lseek(fd, 512, SEEK_SET) < 0)
5945d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        return -EIO;
5955d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    if (write(fd, buf, 1024) != 1024)
5965d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        return -EIO;
5975d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
5985d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    close(fd);
5995d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    return 0;
6005d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
6015d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
6025d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic void vpc_close(BlockDriverState *bs)
6035d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
6045d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    BDRVVPCState *s = bs->opaque;
6055d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    qemu_free(s->pagetable);
6065d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#ifdef CACHE
6075d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    qemu_free(s->pageentry_u8);
6085d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner#endif
6095d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
6105d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
6115d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic QEMUOptionParameter vpc_create_options[] = {
6125d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    {
6135d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        .name = BLOCK_OPT_SIZE,
6145d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        .type = OPT_SIZE,
6155d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner        .help = "Virtual disk size"
6165d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    },
6175d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    { NULL }
6185d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner};
6195d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
6205d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic BlockDriver bdrv_vpc = {
6215d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    .format_name	= "vpc",
6225d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    .instance_size	= sizeof(BDRVVPCState),
6235d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    .bdrv_probe		= vpc_probe,
6245d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    .bdrv_open		= vpc_open,
6255d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    .bdrv_read		= vpc_read,
6265d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    .bdrv_write		= vpc_write,
6275d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    .bdrv_close		= vpc_close,
6285d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    .bdrv_create	= vpc_create,
6295d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
6305d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    .create_options = vpc_create_options,
6315d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner};
6325d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
6335d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerstatic void bdrv_vpc_init(void)
6345d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner{
6355d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner    bdrv_register(&bdrv_vpc);
6365d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner}
6375d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turner
6385d8f37ad78fc66901af50c762029a501561f3b23David 'Digit' Turnerblock_init(bdrv_vpc_init);
639