cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

parallels-ext.c (9825B)


      1/*
      2 * Support of Parallels Format Extension. It's a part of Parallels format
      3 * driver.
      4 *
      5 * Copyright (c) 2021 Virtuozzo International GmbH
      6 *
      7 * Permission is hereby granted, free of charge, to any person obtaining a copy
      8 * of this software and associated documentation files (the "Software"), to deal
      9 * in the Software without restriction, including without limitation the rights
     10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     11 * copies of the Software, and to permit persons to whom the Software is
     12 * furnished to do so, subject to the following conditions:
     13 *
     14 * The above copyright notice and this permission notice shall be included in
     15 * all copies or substantial portions of the Software.
     16 *
     17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     23 * THE SOFTWARE.
     24 */
     25
     26#include "qemu/osdep.h"
     27#include "qapi/error.h"
     28#include "block/block_int.h"
     29#include "parallels.h"
     30#include "crypto/hash.h"
     31#include "qemu/uuid.h"
     32
     33#define PARALLELS_FORMAT_EXTENSION_MAGIC 0xAB234CEF23DCEA87ULL
     34
     35#define PARALLELS_END_OF_FEATURES_MAGIC 0x0ULL
     36#define PARALLELS_DIRTY_BITMAP_FEATURE_MAGIC 0x20385FAE252CB34AULL
     37
     38typedef struct ParallelsFormatExtensionHeader {
     39    uint64_t magic; /* PARALLELS_FORMAT_EXTENSION_MAGIC */
     40    uint8_t check_sum[16];
     41} QEMU_PACKED ParallelsFormatExtensionHeader;
     42
     43typedef struct ParallelsFeatureHeader {
     44    uint64_t magic;
     45    uint64_t flags;
     46    uint32_t data_size;
     47    uint32_t _unused;
     48} QEMU_PACKED ParallelsFeatureHeader;
     49
     50typedef struct ParallelsDirtyBitmapFeature {
     51    uint64_t size;
     52    uint8_t id[16];
     53    uint32_t granularity;
     54    uint32_t l1_size;
     55    /* L1 table follows */
     56} QEMU_PACKED ParallelsDirtyBitmapFeature;
     57
     58/* Given L1 table read bitmap data from the image and populate @bitmap */
     59static int parallels_load_bitmap_data(BlockDriverState *bs,
     60                                      const uint64_t *l1_table,
     61                                      uint32_t l1_size,
     62                                      BdrvDirtyBitmap *bitmap,
     63                                      Error **errp)
     64{
     65    BDRVParallelsState *s = bs->opaque;
     66    int ret = 0;
     67    uint64_t offset, limit;
     68    uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap);
     69    uint8_t *buf = NULL;
     70    uint64_t i, tab_size =
     71        DIV_ROUND_UP(bdrv_dirty_bitmap_serialization_size(bitmap, 0, bm_size),
     72                     s->cluster_size);
     73
     74    if (tab_size != l1_size) {
     75        error_setg(errp, "Bitmap table size %" PRIu32 " does not correspond "
     76                   "to bitmap size and cluster size. Expected %" PRIu64,
     77                   l1_size, tab_size);
     78        return -EINVAL;
     79    }
     80
     81    buf = qemu_blockalign(bs, s->cluster_size);
     82    limit = bdrv_dirty_bitmap_serialization_coverage(s->cluster_size, bitmap);
     83    for (i = 0, offset = 0; i < tab_size; ++i, offset += limit) {
     84        uint64_t count = MIN(bm_size - offset, limit);
     85        uint64_t entry = l1_table[i];
     86
     87        if (entry == 0) {
     88            /* No need to deserialize zeros because @bitmap is cleared. */
     89            continue;
     90        }
     91
     92        if (entry == 1) {
     93            bdrv_dirty_bitmap_deserialize_ones(bitmap, offset, count, false);
     94        } else {
     95            ret = bdrv_pread(bs->file, entry << BDRV_SECTOR_BITS, buf,
     96                             s->cluster_size);
     97            if (ret < 0) {
     98                error_setg_errno(errp, -ret,
     99                                 "Failed to read bitmap data cluster");
    100                goto finish;
    101            }
    102            bdrv_dirty_bitmap_deserialize_part(bitmap, buf, offset, count,
    103                                               false);
    104        }
    105    }
    106    ret = 0;
    107
    108    bdrv_dirty_bitmap_deserialize_finish(bitmap);
    109
    110finish:
    111    qemu_vfree(buf);
    112
    113    return ret;
    114}
    115
    116/*
    117 * @data buffer (of @data_size size) is the Dirty bitmaps feature which
    118 * consists of ParallelsDirtyBitmapFeature followed by L1 table.
    119 */
    120static BdrvDirtyBitmap *parallels_load_bitmap(BlockDriverState *bs,
    121                                              uint8_t *data,
    122                                              size_t data_size,
    123                                              Error **errp)
    124{
    125    int ret;
    126    ParallelsDirtyBitmapFeature bf;
    127    g_autofree uint64_t *l1_table = NULL;
    128    BdrvDirtyBitmap *bitmap;
    129    QemuUUID uuid;
    130    char uuidstr[UUID_FMT_LEN + 1];
    131    int i;
    132
    133    if (data_size < sizeof(bf)) {
    134        error_setg(errp, "Too small Bitmap Feature area in Parallels Format "
    135                   "Extension: %zu bytes, expected at least %zu bytes",
    136                   data_size, sizeof(bf));
    137        return NULL;
    138    }
    139    memcpy(&bf, data, sizeof(bf));
    140    bf.size = le64_to_cpu(bf.size);
    141    bf.granularity = le32_to_cpu(bf.granularity) << BDRV_SECTOR_BITS;
    142    bf.l1_size = le32_to_cpu(bf.l1_size);
    143    data += sizeof(bf);
    144    data_size -= sizeof(bf);
    145
    146    if (bf.size != bs->total_sectors) {
    147        error_setg(errp, "Bitmap size (in sectors) %" PRId64 " differs from "
    148                   "disk size in sectors %" PRId64, bf.size, bs->total_sectors);
    149        return NULL;
    150    }
    151
    152    if (bf.l1_size * sizeof(uint64_t) > data_size) {
    153        error_setg(errp, "Bitmaps feature corrupted: l1 table exceeds "
    154                   "extension data_size");
    155        return NULL;
    156    }
    157
    158    memcpy(&uuid, bf.id, sizeof(uuid));
    159    qemu_uuid_unparse(&uuid, uuidstr);
    160    bitmap = bdrv_create_dirty_bitmap(bs, bf.granularity, uuidstr, errp);
    161    if (!bitmap) {
    162        return NULL;
    163    }
    164
    165    l1_table = g_new(uint64_t, bf.l1_size);
    166    for (i = 0; i < bf.l1_size; i++, data += sizeof(uint64_t)) {
    167        l1_table[i] = ldq_le_p(data);
    168    }
    169
    170    ret = parallels_load_bitmap_data(bs, l1_table, bf.l1_size, bitmap, errp);
    171    if (ret < 0) {
    172        bdrv_release_dirty_bitmap(bitmap);
    173        return NULL;
    174    }
    175
    176    /* We support format extension only for RO parallels images. */
    177    assert(!(bs->open_flags & BDRV_O_RDWR));
    178    bdrv_dirty_bitmap_set_readonly(bitmap, true);
    179
    180    return bitmap;
    181}
    182
    183static int parallels_parse_format_extension(BlockDriverState *bs,
    184                                            uint8_t *ext_cluster, Error **errp)
    185{
    186    BDRVParallelsState *s = bs->opaque;
    187    int ret;
    188    int remaining = s->cluster_size;
    189    uint8_t *pos = ext_cluster;
    190    ParallelsFormatExtensionHeader eh;
    191    g_autofree uint8_t *hash = NULL;
    192    size_t hash_len = 0;
    193    GSList *bitmaps = NULL, *el;
    194
    195    memcpy(&eh, pos, sizeof(eh));
    196    eh.magic = le64_to_cpu(eh.magic);
    197    pos += sizeof(eh);
    198    remaining -= sizeof(eh);
    199
    200    if (eh.magic != PARALLELS_FORMAT_EXTENSION_MAGIC) {
    201        error_setg(errp, "Wrong parallels Format Extension magic: 0x%" PRIx64
    202                   ", expected: 0x%llx", eh.magic,
    203                   PARALLELS_FORMAT_EXTENSION_MAGIC);
    204        goto fail;
    205    }
    206
    207    ret = qcrypto_hash_bytes(QCRYPTO_HASH_ALG_MD5, (char *)pos, remaining,
    208                             &hash, &hash_len, errp);
    209    if (ret < 0) {
    210        goto fail;
    211    }
    212
    213    if (hash_len != sizeof(eh.check_sum) ||
    214        memcmp(hash, eh.check_sum, sizeof(eh.check_sum)) != 0) {
    215        error_setg(errp, "Wrong checksum in Format Extension header. Format "
    216                   "extension is corrupted.");
    217        goto fail;
    218    }
    219
    220    while (true) {
    221        ParallelsFeatureHeader fh;
    222        BdrvDirtyBitmap *bitmap;
    223
    224        if (remaining < sizeof(fh)) {
    225            error_setg(errp, "Can not read feature header, as remaining bytes "
    226                       "(%d) in Format Extension is less than Feature header "
    227                       "size (%zu)", remaining, sizeof(fh));
    228            goto fail;
    229        }
    230
    231        memcpy(&fh, pos, sizeof(fh));
    232        pos += sizeof(fh);
    233        remaining -= sizeof(fh);
    234
    235        fh.magic = le64_to_cpu(fh.magic);
    236        fh.flags = le64_to_cpu(fh.flags);
    237        fh.data_size = le32_to_cpu(fh.data_size);
    238
    239        if (fh.flags) {
    240            error_setg(errp, "Flags for extension feature are unsupported");
    241            goto fail;
    242        }
    243
    244        if (fh.data_size > remaining) {
    245            error_setg(errp, "Feature data_size exceedes Format Extension "
    246                       "cluster");
    247            goto fail;
    248        }
    249
    250        switch (fh.magic) {
    251        case PARALLELS_END_OF_FEATURES_MAGIC:
    252            return 0;
    253
    254        case PARALLELS_DIRTY_BITMAP_FEATURE_MAGIC:
    255            bitmap = parallels_load_bitmap(bs, pos, fh.data_size, errp);
    256            if (!bitmap) {
    257                goto fail;
    258            }
    259            bitmaps = g_slist_append(bitmaps, bitmap);
    260            break;
    261
    262        default:
    263            error_setg(errp, "Unknown feature: 0x%" PRIu64, fh.magic);
    264            goto fail;
    265        }
    266
    267        pos = ext_cluster + QEMU_ALIGN_UP(pos + fh.data_size - ext_cluster, 8);
    268    }
    269
    270fail:
    271    for (el = bitmaps; el; el = el->next) {
    272        bdrv_release_dirty_bitmap(el->data);
    273    }
    274    g_slist_free(bitmaps);
    275
    276    return -EINVAL;
    277}
    278
    279int parallels_read_format_extension(BlockDriverState *bs,
    280                                    int64_t ext_off, Error **errp)
    281{
    282    BDRVParallelsState *s = bs->opaque;
    283    int ret;
    284    uint8_t *ext_cluster = qemu_blockalign(bs, s->cluster_size);
    285
    286    assert(ext_off > 0);
    287
    288    ret = bdrv_pread(bs->file, ext_off, ext_cluster, s->cluster_size);
    289    if (ret < 0) {
    290        error_setg_errno(errp, -ret, "Failed to read Format Extension cluster");
    291        goto out;
    292    }
    293
    294    ret = parallels_parse_format_extension(bs, ext_cluster, errp);
    295
    296out:
    297    qemu_vfree(ext_cluster);
    298
    299    return ret;
    300}