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

copy-before-write.c (7746B)


      1/*
      2 * copy-before-write filter driver
      3 *
      4 * The driver performs Copy-Before-Write (CBW) operation: it is injected above
      5 * some node, and before each write it copies _old_ data to the target node.
      6 *
      7 * Copyright (c) 2018-2021 Virtuozzo International GmbH.
      8 *
      9 * Author:
     10 *  Sementsov-Ogievskiy Vladimir <vsementsov@virtuozzo.com>
     11 *
     12 * This program is free software; you can redistribute it and/or modify
     13 * it under the terms of the GNU General Public License as published by
     14 * the Free Software Foundation; either version 2 of the License, or
     15 * (at your option) any later version.
     16 *
     17 * This program is distributed in the hope that it will be useful,
     18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     20 * GNU General Public License for more details.
     21 *
     22 * You should have received a copy of the GNU General Public License
     23 * along with this program. If not, see <http://www.gnu.org/licenses/>.
     24 */
     25
     26#include "qemu/osdep.h"
     27
     28#include "sysemu/block-backend.h"
     29#include "qemu/cutils.h"
     30#include "qapi/error.h"
     31#include "block/block_int.h"
     32#include "block/qdict.h"
     33#include "block/block-copy.h"
     34
     35#include "block/copy-before-write.h"
     36
     37typedef struct BDRVCopyBeforeWriteState {
     38    BlockCopyState *bcs;
     39    BdrvChild *target;
     40} BDRVCopyBeforeWriteState;
     41
     42static coroutine_fn int cbw_co_preadv(
     43        BlockDriverState *bs, int64_t offset, int64_t bytes,
     44        QEMUIOVector *qiov, BdrvRequestFlags flags)
     45{
     46    return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
     47}
     48
     49static coroutine_fn int cbw_do_copy_before_write(BlockDriverState *bs,
     50        uint64_t offset, uint64_t bytes, BdrvRequestFlags flags)
     51{
     52    BDRVCopyBeforeWriteState *s = bs->opaque;
     53    uint64_t off, end;
     54    int64_t cluster_size = block_copy_cluster_size(s->bcs);
     55
     56    if (flags & BDRV_REQ_WRITE_UNCHANGED) {
     57        return 0;
     58    }
     59
     60    off = QEMU_ALIGN_DOWN(offset, cluster_size);
     61    end = QEMU_ALIGN_UP(offset + bytes, cluster_size);
     62
     63    return block_copy(s->bcs, off, end - off, true);
     64}
     65
     66static int coroutine_fn cbw_co_pdiscard(BlockDriverState *bs,
     67                                        int64_t offset, int64_t bytes)
     68{
     69    int ret = cbw_do_copy_before_write(bs, offset, bytes, 0);
     70    if (ret < 0) {
     71        return ret;
     72    }
     73
     74    return bdrv_co_pdiscard(bs->file, offset, bytes);
     75}
     76
     77static int coroutine_fn cbw_co_pwrite_zeroes(BlockDriverState *bs,
     78        int64_t offset, int64_t bytes, BdrvRequestFlags flags)
     79{
     80    int ret = cbw_do_copy_before_write(bs, offset, bytes, flags);
     81    if (ret < 0) {
     82        return ret;
     83    }
     84
     85    return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
     86}
     87
     88static coroutine_fn int cbw_co_pwritev(BlockDriverState *bs,
     89                                       int64_t offset,
     90                                       int64_t bytes,
     91                                       QEMUIOVector *qiov,
     92                                       BdrvRequestFlags flags)
     93{
     94    int ret = cbw_do_copy_before_write(bs, offset, bytes, flags);
     95    if (ret < 0) {
     96        return ret;
     97    }
     98
     99    return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
    100}
    101
    102static int coroutine_fn cbw_co_flush(BlockDriverState *bs)
    103{
    104    if (!bs->file) {
    105        return 0;
    106    }
    107
    108    return bdrv_co_flush(bs->file->bs);
    109}
    110
    111static void cbw_refresh_filename(BlockDriverState *bs)
    112{
    113    pstrcpy(bs->exact_filename, sizeof(bs->exact_filename),
    114            bs->file->bs->filename);
    115}
    116
    117static void cbw_child_perm(BlockDriverState *bs, BdrvChild *c,
    118                           BdrvChildRole role,
    119                           BlockReopenQueue *reopen_queue,
    120                           uint64_t perm, uint64_t shared,
    121                           uint64_t *nperm, uint64_t *nshared)
    122{
    123    if (!(role & BDRV_CHILD_FILTERED)) {
    124        /*
    125         * Target child
    126         *
    127         * Share write to target (child_file), to not interfere
    128         * with guest writes to its disk which may be in target backing chain.
    129         * Can't resize during a backup block job because we check the size
    130         * only upfront.
    131         */
    132        *nshared = BLK_PERM_ALL & ~BLK_PERM_RESIZE;
    133        *nperm = BLK_PERM_WRITE;
    134    } else {
    135        /* Source child */
    136        bdrv_default_perms(bs, c, role, reopen_queue,
    137                           perm, shared, nperm, nshared);
    138
    139        if (!QLIST_EMPTY(&bs->parents)) {
    140            if (perm & BLK_PERM_WRITE) {
    141                *nperm = *nperm | BLK_PERM_CONSISTENT_READ;
    142            }
    143            *nshared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
    144        }
    145    }
    146}
    147
    148static int cbw_open(BlockDriverState *bs, QDict *options, int flags,
    149                    Error **errp)
    150{
    151    BDRVCopyBeforeWriteState *s = bs->opaque;
    152    BdrvDirtyBitmap *copy_bitmap;
    153
    154    bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
    155                               BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
    156                               false, errp);
    157    if (!bs->file) {
    158        return -EINVAL;
    159    }
    160
    161    s->target = bdrv_open_child(NULL, options, "target", bs, &child_of_bds,
    162                                BDRV_CHILD_DATA, false, errp);
    163    if (!s->target) {
    164        return -EINVAL;
    165    }
    166
    167    bs->total_sectors = bs->file->bs->total_sectors;
    168    bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
    169            (BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
    170    bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
    171            ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
    172             bs->file->bs->supported_zero_flags);
    173
    174    s->bcs = block_copy_state_new(bs->file, s->target, errp);
    175    if (!s->bcs) {
    176        error_prepend(errp, "Cannot create block-copy-state: ");
    177        return -EINVAL;
    178    }
    179
    180    copy_bitmap = block_copy_dirty_bitmap(s->bcs);
    181    bdrv_set_dirty_bitmap(copy_bitmap, 0, bdrv_dirty_bitmap_size(copy_bitmap));
    182
    183    return 0;
    184}
    185
    186static void cbw_close(BlockDriverState *bs)
    187{
    188    BDRVCopyBeforeWriteState *s = bs->opaque;
    189
    190    block_copy_state_free(s->bcs);
    191    s->bcs = NULL;
    192}
    193
    194BlockDriver bdrv_cbw_filter = {
    195    .format_name = "copy-before-write",
    196    .instance_size = sizeof(BDRVCopyBeforeWriteState),
    197
    198    .bdrv_open                  = cbw_open,
    199    .bdrv_close                 = cbw_close,
    200
    201    .bdrv_co_preadv             = cbw_co_preadv,
    202    .bdrv_co_pwritev            = cbw_co_pwritev,
    203    .bdrv_co_pwrite_zeroes      = cbw_co_pwrite_zeroes,
    204    .bdrv_co_pdiscard           = cbw_co_pdiscard,
    205    .bdrv_co_flush              = cbw_co_flush,
    206
    207    .bdrv_refresh_filename      = cbw_refresh_filename,
    208
    209    .bdrv_child_perm            = cbw_child_perm,
    210
    211    .is_filter = true,
    212};
    213
    214BlockDriverState *bdrv_cbw_append(BlockDriverState *source,
    215                                  BlockDriverState *target,
    216                                  const char *filter_node_name,
    217                                  BlockCopyState **bcs,
    218                                  Error **errp)
    219{
    220    ERRP_GUARD();
    221    BDRVCopyBeforeWriteState *state;
    222    BlockDriverState *top;
    223    QDict *opts;
    224
    225    assert(source->total_sectors == target->total_sectors);
    226
    227    opts = qdict_new();
    228    qdict_put_str(opts, "driver", "copy-before-write");
    229    if (filter_node_name) {
    230        qdict_put_str(opts, "node-name", filter_node_name);
    231    }
    232    qdict_put_str(opts, "file", bdrv_get_node_name(source));
    233    qdict_put_str(opts, "target", bdrv_get_node_name(target));
    234
    235    top = bdrv_insert_node(source, opts, BDRV_O_RDWR, errp);
    236    if (!top) {
    237        return NULL;
    238    }
    239
    240    state = top->opaque;
    241    *bcs = state->bcs;
    242
    243    return top;
    244}
    245
    246void bdrv_cbw_drop(BlockDriverState *bs)
    247{
    248    bdrv_drop_filter(bs, &error_abort);
    249    bdrv_unref(bs);
    250}
    251
    252static void cbw_init(void)
    253{
    254    bdrv_register(&bdrv_cbw_filter);
    255}
    256
    257block_init(cbw_init);