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

dif.c (14533B)


      1/*
      2 * QEMU NVM Express End-to-End Data Protection support
      3 *
      4 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
      5 *
      6 * Authors:
      7 *   Klaus Jensen           <k.jensen@samsung.com>
      8 *   Gollu Appalanaidu      <anaidu.gollu@samsung.com>
      9 */
     10
     11#include "qemu/osdep.h"
     12#include "qapi/error.h"
     13#include "sysemu/block-backend.h"
     14
     15#include "nvme.h"
     16#include "trace.h"
     17
     18uint16_t nvme_check_prinfo(NvmeNamespace *ns, uint8_t prinfo, uint64_t slba,
     19                           uint32_t reftag)
     20{
     21    if ((NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) == NVME_ID_NS_DPS_TYPE_1) &&
     22        (prinfo & NVME_PRINFO_PRCHK_REF) && (slba & 0xffffffff) != reftag) {
     23        return NVME_INVALID_PROT_INFO | NVME_DNR;
     24    }
     25
     26    return NVME_SUCCESS;
     27}
     28
     29/* from Linux kernel (crypto/crct10dif_common.c) */
     30static uint16_t crc_t10dif(uint16_t crc, const unsigned char *buffer,
     31                           size_t len)
     32{
     33    unsigned int i;
     34
     35    for (i = 0; i < len; i++) {
     36        crc = (crc << 8) ^ t10_dif_crc_table[((crc >> 8) ^ buffer[i]) & 0xff];
     37    }
     38
     39    return crc;
     40}
     41
     42void nvme_dif_pract_generate_dif(NvmeNamespace *ns, uint8_t *buf, size_t len,
     43                                 uint8_t *mbuf, size_t mlen, uint16_t apptag,
     44                                 uint32_t *reftag)
     45{
     46    uint8_t *end = buf + len;
     47    int16_t pil = 0;
     48
     49    if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
     50        pil = ns->lbaf.ms - sizeof(NvmeDifTuple);
     51    }
     52
     53    trace_pci_nvme_dif_pract_generate_dif(len, ns->lbasz, ns->lbasz + pil,
     54                                          apptag, *reftag);
     55
     56    for (; buf < end; buf += ns->lbasz, mbuf += ns->lbaf.ms) {
     57        NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil);
     58        uint16_t crc = crc_t10dif(0x0, buf, ns->lbasz);
     59
     60        if (pil) {
     61            crc = crc_t10dif(crc, mbuf, pil);
     62        }
     63
     64        dif->guard = cpu_to_be16(crc);
     65        dif->apptag = cpu_to_be16(apptag);
     66        dif->reftag = cpu_to_be32(*reftag);
     67
     68        if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) != NVME_ID_NS_DPS_TYPE_3) {
     69            (*reftag)++;
     70        }
     71    }
     72}
     73
     74static uint16_t nvme_dif_prchk(NvmeNamespace *ns, NvmeDifTuple *dif,
     75                               uint8_t *buf, uint8_t *mbuf, size_t pil,
     76                               uint8_t prinfo, uint16_t apptag,
     77                               uint16_t appmask, uint32_t reftag)
     78{
     79    switch (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
     80    case NVME_ID_NS_DPS_TYPE_3:
     81        if (be32_to_cpu(dif->reftag) != 0xffffffff) {
     82            break;
     83        }
     84
     85        /* fallthrough */
     86    case NVME_ID_NS_DPS_TYPE_1:
     87    case NVME_ID_NS_DPS_TYPE_2:
     88        if (be16_to_cpu(dif->apptag) != 0xffff) {
     89            break;
     90        }
     91
     92        trace_pci_nvme_dif_prchk_disabled(be16_to_cpu(dif->apptag),
     93                                          be32_to_cpu(dif->reftag));
     94
     95        return NVME_SUCCESS;
     96    }
     97
     98    if (prinfo & NVME_PRINFO_PRCHK_GUARD) {
     99        uint16_t crc = crc_t10dif(0x0, buf, ns->lbasz);
    100
    101        if (pil) {
    102            crc = crc_t10dif(crc, mbuf, pil);
    103        }
    104
    105        trace_pci_nvme_dif_prchk_guard(be16_to_cpu(dif->guard), crc);
    106
    107        if (be16_to_cpu(dif->guard) != crc) {
    108            return NVME_E2E_GUARD_ERROR;
    109        }
    110    }
    111
    112    if (prinfo & NVME_PRINFO_PRCHK_APP) {
    113        trace_pci_nvme_dif_prchk_apptag(be16_to_cpu(dif->apptag), apptag,
    114                                        appmask);
    115
    116        if ((be16_to_cpu(dif->apptag) & appmask) != (apptag & appmask)) {
    117            return NVME_E2E_APP_ERROR;
    118        }
    119    }
    120
    121    if (prinfo & NVME_PRINFO_PRCHK_REF) {
    122        trace_pci_nvme_dif_prchk_reftag(be32_to_cpu(dif->reftag), reftag);
    123
    124        if (be32_to_cpu(dif->reftag) != reftag) {
    125            return NVME_E2E_REF_ERROR;
    126        }
    127    }
    128
    129    return NVME_SUCCESS;
    130}
    131
    132uint16_t nvme_dif_check(NvmeNamespace *ns, uint8_t *buf, size_t len,
    133                        uint8_t *mbuf, size_t mlen, uint8_t prinfo,
    134                        uint64_t slba, uint16_t apptag,
    135                        uint16_t appmask, uint32_t *reftag)
    136{
    137    uint8_t *end = buf + len;
    138    int16_t pil = 0;
    139    uint16_t status;
    140
    141    status = nvme_check_prinfo(ns, prinfo, slba, *reftag);
    142    if (status) {
    143        return status;
    144    }
    145
    146    if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
    147        pil = ns->lbaf.ms - sizeof(NvmeDifTuple);
    148    }
    149
    150    trace_pci_nvme_dif_check(prinfo, ns->lbasz + pil);
    151
    152    for (; buf < end; buf += ns->lbasz, mbuf += ns->lbaf.ms) {
    153        NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil);
    154
    155        status = nvme_dif_prchk(ns, dif, buf, mbuf, pil, prinfo, apptag,
    156                                appmask, *reftag);
    157        if (status) {
    158            return status;
    159        }
    160
    161        if (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps) != NVME_ID_NS_DPS_TYPE_3) {
    162            (*reftag)++;
    163        }
    164    }
    165
    166    return NVME_SUCCESS;
    167}
    168
    169uint16_t nvme_dif_mangle_mdata(NvmeNamespace *ns, uint8_t *mbuf, size_t mlen,
    170                               uint64_t slba)
    171{
    172    BlockBackend *blk = ns->blkconf.blk;
    173    BlockDriverState *bs = blk_bs(blk);
    174
    175    int64_t moffset = 0, offset = nvme_l2b(ns, slba);
    176    uint8_t *mbufp, *end;
    177    bool zeroed;
    178    int16_t pil = 0;
    179    int64_t bytes = (mlen / ns->lbaf.ms) << ns->lbaf.ds;
    180    int64_t pnum = 0;
    181
    182    Error *err = NULL;
    183
    184
    185    if (!(ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT)) {
    186        pil = ns->lbaf.ms - sizeof(NvmeDifTuple);
    187    }
    188
    189    do {
    190        int ret;
    191
    192        bytes -= pnum;
    193
    194        ret = bdrv_block_status(bs, offset, bytes, &pnum, NULL, NULL);
    195        if (ret < 0) {
    196            error_setg_errno(&err, -ret, "unable to get block status");
    197            error_report_err(err);
    198
    199            return NVME_INTERNAL_DEV_ERROR;
    200        }
    201
    202        zeroed = !!(ret & BDRV_BLOCK_ZERO);
    203
    204        trace_pci_nvme_block_status(offset, bytes, pnum, ret, zeroed);
    205
    206        if (zeroed) {
    207            mbufp = mbuf + moffset;
    208            mlen = (pnum >> ns->lbaf.ds) * ns->lbaf.ms;
    209            end = mbufp + mlen;
    210
    211            for (; mbufp < end; mbufp += ns->lbaf.ms) {
    212                memset(mbufp + pil, 0xff, sizeof(NvmeDifTuple));
    213            }
    214        }
    215
    216        moffset += (pnum >> ns->lbaf.ds) * ns->lbaf.ms;
    217        offset += pnum;
    218    } while (pnum != bytes);
    219
    220    return NVME_SUCCESS;
    221}
    222
    223static void nvme_dif_rw_cb(void *opaque, int ret)
    224{
    225    NvmeBounceContext *ctx = opaque;
    226    NvmeRequest *req = ctx->req;
    227    NvmeNamespace *ns = req->ns;
    228    BlockBackend *blk = ns->blkconf.blk;
    229
    230    trace_pci_nvme_dif_rw_cb(nvme_cid(req), blk_name(blk));
    231
    232    qemu_iovec_destroy(&ctx->data.iov);
    233    g_free(ctx->data.bounce);
    234
    235    qemu_iovec_destroy(&ctx->mdata.iov);
    236    g_free(ctx->mdata.bounce);
    237
    238    g_free(ctx);
    239
    240    nvme_rw_complete_cb(req, ret);
    241}
    242
    243static void nvme_dif_rw_check_cb(void *opaque, int ret)
    244{
    245    NvmeBounceContext *ctx = opaque;
    246    NvmeRequest *req = ctx->req;
    247    NvmeNamespace *ns = req->ns;
    248    NvmeCtrl *n = nvme_ctrl(req);
    249    NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
    250    uint64_t slba = le64_to_cpu(rw->slba);
    251    uint8_t prinfo = NVME_RW_PRINFO(le16_to_cpu(rw->control));
    252    uint16_t apptag = le16_to_cpu(rw->apptag);
    253    uint16_t appmask = le16_to_cpu(rw->appmask);
    254    uint32_t reftag = le32_to_cpu(rw->reftag);
    255    uint16_t status;
    256
    257    trace_pci_nvme_dif_rw_check_cb(nvme_cid(req), prinfo, apptag, appmask,
    258                                   reftag);
    259
    260    if (ret) {
    261        goto out;
    262    }
    263
    264    status = nvme_dif_mangle_mdata(ns, ctx->mdata.bounce, ctx->mdata.iov.size,
    265                                   slba);
    266    if (status) {
    267        req->status = status;
    268        goto out;
    269    }
    270
    271    status = nvme_dif_check(ns, ctx->data.bounce, ctx->data.iov.size,
    272                            ctx->mdata.bounce, ctx->mdata.iov.size, prinfo,
    273                            slba, apptag, appmask, &reftag);
    274    if (status) {
    275        req->status = status;
    276        goto out;
    277    }
    278
    279    status = nvme_bounce_data(n, ctx->data.bounce, ctx->data.iov.size,
    280                              NVME_TX_DIRECTION_FROM_DEVICE, req);
    281    if (status) {
    282        req->status = status;
    283        goto out;
    284    }
    285
    286    if (prinfo & NVME_PRINFO_PRACT && ns->lbaf.ms == 8) {
    287        goto out;
    288    }
    289
    290    status = nvme_bounce_mdata(n, ctx->mdata.bounce, ctx->mdata.iov.size,
    291                               NVME_TX_DIRECTION_FROM_DEVICE, req);
    292    if (status) {
    293        req->status = status;
    294    }
    295
    296out:
    297    nvme_dif_rw_cb(ctx, ret);
    298}
    299
    300static void nvme_dif_rw_mdata_in_cb(void *opaque, int ret)
    301{
    302    NvmeBounceContext *ctx = opaque;
    303    NvmeRequest *req = ctx->req;
    304    NvmeNamespace *ns = req->ns;
    305    NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
    306    uint64_t slba = le64_to_cpu(rw->slba);
    307    uint32_t nlb = le16_to_cpu(rw->nlb) + 1;
    308    size_t mlen = nvme_m2b(ns, nlb);
    309    uint64_t offset = nvme_moff(ns, slba);
    310    BlockBackend *blk = ns->blkconf.blk;
    311
    312    trace_pci_nvme_dif_rw_mdata_in_cb(nvme_cid(req), blk_name(blk));
    313
    314    if (ret) {
    315        goto out;
    316    }
    317
    318    ctx->mdata.bounce = g_malloc(mlen);
    319
    320    qemu_iovec_reset(&ctx->mdata.iov);
    321    qemu_iovec_add(&ctx->mdata.iov, ctx->mdata.bounce, mlen);
    322
    323    req->aiocb = blk_aio_preadv(blk, offset, &ctx->mdata.iov, 0,
    324                                nvme_dif_rw_check_cb, ctx);
    325    return;
    326
    327out:
    328    nvme_dif_rw_cb(ctx, ret);
    329}
    330
    331static void nvme_dif_rw_mdata_out_cb(void *opaque, int ret)
    332{
    333    NvmeBounceContext *ctx = opaque;
    334    NvmeRequest *req = ctx->req;
    335    NvmeNamespace *ns = req->ns;
    336    NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
    337    uint64_t slba = le64_to_cpu(rw->slba);
    338    uint64_t offset = nvme_moff(ns, slba);
    339    BlockBackend *blk = ns->blkconf.blk;
    340
    341    trace_pci_nvme_dif_rw_mdata_out_cb(nvme_cid(req), blk_name(blk));
    342
    343    if (ret) {
    344        goto out;
    345    }
    346
    347    req->aiocb = blk_aio_pwritev(blk, offset, &ctx->mdata.iov, 0,
    348                                 nvme_dif_rw_cb, ctx);
    349    return;
    350
    351out:
    352    nvme_dif_rw_cb(ctx, ret);
    353}
    354
    355uint16_t nvme_dif_rw(NvmeCtrl *n, NvmeRequest *req)
    356{
    357    NvmeRwCmd *rw = (NvmeRwCmd *)&req->cmd;
    358    NvmeNamespace *ns = req->ns;
    359    BlockBackend *blk = ns->blkconf.blk;
    360    bool wrz = rw->opcode == NVME_CMD_WRITE_ZEROES;
    361    uint32_t nlb = le16_to_cpu(rw->nlb) + 1;
    362    uint64_t slba = le64_to_cpu(rw->slba);
    363    size_t len = nvme_l2b(ns, nlb);
    364    size_t mlen = nvme_m2b(ns, nlb);
    365    size_t mapped_len = len;
    366    int64_t offset = nvme_l2b(ns, slba);
    367    uint8_t prinfo = NVME_RW_PRINFO(le16_to_cpu(rw->control));
    368    uint16_t apptag = le16_to_cpu(rw->apptag);
    369    uint16_t appmask = le16_to_cpu(rw->appmask);
    370    uint32_t reftag = le32_to_cpu(rw->reftag);
    371    bool pract = !!(prinfo & NVME_PRINFO_PRACT);
    372    NvmeBounceContext *ctx;
    373    uint16_t status;
    374
    375    trace_pci_nvme_dif_rw(pract, prinfo);
    376
    377    ctx = g_new0(NvmeBounceContext, 1);
    378    ctx->req = req;
    379
    380    if (wrz) {
    381        BdrvRequestFlags flags = BDRV_REQ_MAY_UNMAP;
    382
    383        if (prinfo & NVME_PRINFO_PRCHK_MASK) {
    384            status = NVME_INVALID_PROT_INFO | NVME_DNR;
    385            goto err;
    386        }
    387
    388        if (pract) {
    389            uint8_t *mbuf, *end;
    390            int16_t pil = ns->lbaf.ms - sizeof(NvmeDifTuple);
    391
    392            status = nvme_check_prinfo(ns, prinfo, slba, reftag);
    393            if (status) {
    394                goto err;
    395            }
    396
    397            flags = 0;
    398
    399            ctx->mdata.bounce = g_malloc0(mlen);
    400
    401            qemu_iovec_init(&ctx->mdata.iov, 1);
    402            qemu_iovec_add(&ctx->mdata.iov, ctx->mdata.bounce, mlen);
    403
    404            mbuf = ctx->mdata.bounce;
    405            end = mbuf + mlen;
    406
    407            if (ns->id_ns.dps & NVME_ID_NS_DPS_FIRST_EIGHT) {
    408                pil = 0;
    409            }
    410
    411            for (; mbuf < end; mbuf += ns->lbaf.ms) {
    412                NvmeDifTuple *dif = (NvmeDifTuple *)(mbuf + pil);
    413
    414                dif->apptag = cpu_to_be16(apptag);
    415                dif->reftag = cpu_to_be32(reftag);
    416
    417                switch (NVME_ID_NS_DPS_TYPE(ns->id_ns.dps)) {
    418                case NVME_ID_NS_DPS_TYPE_1:
    419                case NVME_ID_NS_DPS_TYPE_2:
    420                    reftag++;
    421                }
    422            }
    423        }
    424
    425        req->aiocb = blk_aio_pwrite_zeroes(blk, offset, len, flags,
    426                                           nvme_dif_rw_mdata_out_cb, ctx);
    427        return NVME_NO_COMPLETE;
    428    }
    429
    430    if (nvme_ns_ext(ns) && !(pract && ns->lbaf.ms == 8)) {
    431        mapped_len += mlen;
    432    }
    433
    434    status = nvme_map_dptr(n, &req->sg, mapped_len, &req->cmd);
    435    if (status) {
    436        goto err;
    437    }
    438
    439    ctx->data.bounce = g_malloc(len);
    440
    441    qemu_iovec_init(&ctx->data.iov, 1);
    442    qemu_iovec_add(&ctx->data.iov, ctx->data.bounce, len);
    443
    444    if (req->cmd.opcode == NVME_CMD_READ) {
    445        block_acct_start(blk_get_stats(blk), &req->acct, ctx->data.iov.size,
    446                         BLOCK_ACCT_READ);
    447
    448        req->aiocb = blk_aio_preadv(ns->blkconf.blk, offset, &ctx->data.iov, 0,
    449                                    nvme_dif_rw_mdata_in_cb, ctx);
    450        return NVME_NO_COMPLETE;
    451    }
    452
    453    status = nvme_bounce_data(n, ctx->data.bounce, ctx->data.iov.size,
    454                              NVME_TX_DIRECTION_TO_DEVICE, req);
    455    if (status) {
    456        goto err;
    457    }
    458
    459    ctx->mdata.bounce = g_malloc(mlen);
    460
    461    qemu_iovec_init(&ctx->mdata.iov, 1);
    462    qemu_iovec_add(&ctx->mdata.iov, ctx->mdata.bounce, mlen);
    463
    464    if (!(pract && ns->lbaf.ms == 8)) {
    465        status = nvme_bounce_mdata(n, ctx->mdata.bounce, ctx->mdata.iov.size,
    466                                   NVME_TX_DIRECTION_TO_DEVICE, req);
    467        if (status) {
    468            goto err;
    469        }
    470    }
    471
    472    status = nvme_check_prinfo(ns, prinfo, slba, reftag);
    473    if (status) {
    474        goto err;
    475    }
    476
    477    if (pract) {
    478        /* splice generated protection information into the buffer */
    479        nvme_dif_pract_generate_dif(ns, ctx->data.bounce, ctx->data.iov.size,
    480                                    ctx->mdata.bounce, ctx->mdata.iov.size,
    481                                    apptag, &reftag);
    482    } else {
    483        status = nvme_dif_check(ns, ctx->data.bounce, ctx->data.iov.size,
    484                                ctx->mdata.bounce, ctx->mdata.iov.size, prinfo,
    485                                slba, apptag, appmask, &reftag);
    486        if (status) {
    487            goto err;
    488        }
    489    }
    490
    491    block_acct_start(blk_get_stats(blk), &req->acct, ctx->data.iov.size,
    492                     BLOCK_ACCT_WRITE);
    493
    494    req->aiocb = blk_aio_pwritev(ns->blkconf.blk, offset, &ctx->data.iov, 0,
    495                                 nvme_dif_rw_mdata_out_cb, ctx);
    496
    497    return NVME_NO_COMPLETE;
    498
    499err:
    500    qemu_iovec_destroy(&ctx->data.iov);
    501    g_free(ctx->data.bounce);
    502
    503    qemu_iovec_destroy(&ctx->mdata.iov);
    504    g_free(ctx->mdata.bounce);
    505
    506    g_free(ctx);
    507
    508    return status;
    509}