From nobody Wed Nov 27 13:05:37 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1698638707; cv=none; d=zohomail.com; s=zohoarc; b=mY0KAxDmuIxPxTN5MQq7dcRa6MW11R7mlYtA3RFj+Ps64yTLUwLz255pXCXQgAFbDu1QMTsXGqEs2THotTI1FIJy+aTx5C9vuLW5EKDq6kdJyu2IDSp/QQyvG0+P2qnC8YGSvCzcwXr8k1lm2HdJSxpxcTaRfl32FoHDPelDAHQ= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1698638707; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=2jMBQWwIK7BxJ3zZ7H54cqn38PvJrQ+4Vnyx+l3cpL4=; b=LSYSTdEgWF9uorxVaT/gjWj3zZwhovaUKJ/zMvw/19y8YYT+s7s73otHFB+EEblo9/yAAtUINTL9VnEoCYBQf9zqEaCvgD+ufYOUIyqX2cgxGel/uOc+rHVj/DSCJxWuchXI5H6r2DvFsoyIsmAvtfhh7TIZUDYoMwbWedMwn+I= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1698638707042636.6312145254294; Sun, 29 Oct 2023 21:05:07 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1qxJVq-0004VA-Au; Mon, 30 Oct 2023 00:04:26 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1qxJUC-0003LV-8U; Mon, 30 Oct 2023 00:02:46 -0400 Received: from mail-pl1-x633.google.com ([2607:f8b0:4864:20::633]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1qxJU5-00046q-Hp; Mon, 30 Oct 2023 00:02:43 -0400 Received: by mail-pl1-x633.google.com with SMTP id d9443c01a7336-1cc3c51f830so4135715ad.1; Sun, 29 Oct 2023 21:02:35 -0700 (PDT) Received: from jeuk-MS-7D42.. ([218.147.112.168]) by smtp.gmail.com with ESMTPSA id jc3-20020a17090325c300b001cc3b988fd6sm2278339plb.265.2023.10.29.21.02.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 29 Oct 2023 21:02:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1698638555; x=1699243355; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=2jMBQWwIK7BxJ3zZ7H54cqn38PvJrQ+4Vnyx+l3cpL4=; b=ksikkQcEuzu3a5UBQ4JyX06ksRDHPZcfj6n9eIw+4a1X4djXvmhvtUHOxwE6eW5JPM Q/dmhmMbppBRQdt3VZx9CMKr09ES1qGwkIqiB9c6c/B6FDME4kCxxRHFWKWkl0rXkybZ zTh9kXUC/6cEn5VZIiSB+DMBZaH+qBaXpxGlsxbcp0jhqoOjeSn7V9O/+uKpT4nBeZUZ kNq3eqP+l9PSjO2m/8mzzK6wwHN6bu3CFIh6HCn0xh0vH5Ra/L6aywo8f/dMAqVb3G/2 neyGKUiDbp2ACWQVgH9oUrbKdEXjYc3b7uScrese8HUlIFsXyB7CVflGZ3U4KA+k6Pox SMUg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1698638555; x=1699243355; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=2jMBQWwIK7BxJ3zZ7H54cqn38PvJrQ+4Vnyx+l3cpL4=; b=s58rByFxPKa97tCMtpsqDTeMXvIe1mWbqtvYRXcpW+OaicmDd4YfJM5jtlDKZo1rGO SfAZ8IwdausuZiSw2EFNmPSZxc9EP7g8kHQSu3TOFiGC4RkZtKu3Jg6iSoStj8koZYIP U11OiGC7DlnUf/LqPCnqbb58s1MajUw7I2muRfEoTqFsvaBXkELxhDxmMgAak/AR+xqz HhUEq4mMgsiAzVjpUe6cj3SmdCT5F0isgt62ss/AQ1LoYWaRbNUS7gsYMAjXPJCGwdWK UnoZiUb/FBov6WN8CjoNAwjQiUjtUsRrz9uoaoacrr15tes6zLN/gGxoX+63oUH81UQ3 OaTA== X-Gm-Message-State: AOJu0YyoxC4I6AYojiIeJkpJCuYl/uWF28VT6l4P/NCHfZJrD88tThxQ E6lWxaNs1Blfa31J8L/Cp1oW/KRgAlhFlQ== X-Google-Smtp-Source: AGHT+IHxLN3uCuXqdKdRJYMuBatEOqoaKpu1Cd0YSTyAIGXYb+ufbCuc+K+nPq2TJ5OJeRRtnh4h9Q== X-Received: by 2002:a17:902:cac1:b0:1ca:4092:7200 with SMTP id y1-20020a170902cac100b001ca40927200mr5113215pld.54.1698638554132; Sun, 29 Oct 2023 21:02:34 -0700 (PDT) From: Jeuk Kim To: qemu-devel@nongnu.org, stefanha@redhat.com Cc: jeuk20.kim@samsung.com, pbonzini@redhat.com, fam@euphon.net, qemu-block@nongnu.org Subject: [PULL 1/1] hw/ufs: Modify lu.c to share codes with SCSI subsystem Date: Mon, 30 Oct 2023 13:02:19 +0900 Message-Id: <096434fea13acd19f4ead00cdf9babea8dc7e61e.1698631179.git.jeuk20.kim@samsung.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::633; envelope-from=jeuk20.kim@gmail.com; helo=mail-pl1-x633.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1698638708839100001 Content-Type: text/plain; charset="utf-8" From: Jeuk Kim This patch removes the code that ufs-lu was duplicating from scsi-hd and allows them to share code. It makes ufs-lu have a virtual scsi-bus and scsi-hd internally. This allows scsi related commands to be passed thorugh to the scsi-hd. The query request and nop command work the same as the existing logic. Well-known lus do not have a virtual scsi-bus and scsi-hd, and handle the necessary scsi commands by emulating them directly. Signed-off-by: Jeuk Kim --- hw/ufs/lu.c | 1473 +++++++--------------------------------- hw/ufs/trace-events | 25 - hw/ufs/ufs.c | 202 +----- hw/ufs/ufs.h | 36 +- include/block/ufs.h | 2 +- tests/qtest/ufs-test.c | 37 +- 6 files changed, 315 insertions(+), 1460 deletions(-) diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c index 13b5e37b53..81bfff9b4e 100644 --- a/hw/ufs/lu.c +++ b/hw/ufs/lu.c @@ -19,57 +19,117 @@ #include "trace.h" #include "ufs.h" =20 -/* - * The code below handling SCSI commands is copied from hw/scsi/scsi-disk.= c, - * with minor adjustments to make it work for UFS. - */ +#define SCSI_COMMAND_FAIL (-1) =20 -#define SCSI_DMA_BUF_SIZE (128 * KiB) -#define SCSI_MAX_INQUIRY_LEN 256 -#define SCSI_INQUIRY_DATA_SIZE 36 -#define SCSI_MAX_MODE_LEN 256 - -typedef struct UfsSCSIReq { - SCSIRequest req; - /* Both sector and sector_count are in terms of BDRV_SECTOR_SIZE bytes= . */ - uint64_t sector; - uint32_t sector_count; - uint32_t buflen; - bool started; - bool need_fua_emulation; - struct iovec iov; - QEMUIOVector qiov; - BlockAcctCookie acct; -} UfsSCSIReq; - -static void ufs_scsi_free_request(SCSIRequest *req) +static void ufs_build_upiu_sense_data(UfsRequest *req, uint8_t *sense, + uint32_t sense_len) { - UfsSCSIReq *r =3D DO_UPCAST(UfsSCSIReq, req, req); + req->rsp_upiu.sr.sense_data_len =3D cpu_to_be16(sense_len); + assert(sense_len <=3D SCSI_SENSE_LEN); + memcpy(req->rsp_upiu.sr.sense_data, sense, sense_len); +} + +static void ufs_build_scsi_response_upiu(UfsRequest *req, uint8_t *sense, + uint32_t sense_len, + uint32_t transfered_len, + int16_t status) +{ + uint32_t expected_len =3D be32_to_cpu(req->req_upiu.sc.exp_data_transf= er_len); + uint8_t flags =3D 0, response =3D UFS_COMMAND_RESULT_SUCCESS; + uint16_t data_segment_length; + + if (expected_len > transfered_len) { + req->rsp_upiu.sr.residual_transfer_count =3D + cpu_to_be32(expected_len - transfered_len); + flags |=3D UFS_UPIU_FLAG_UNDERFLOW; + } else if (expected_len < transfered_len) { + req->rsp_upiu.sr.residual_transfer_count =3D + cpu_to_be32(transfered_len - expected_len); + flags |=3D UFS_UPIU_FLAG_OVERFLOW; + } =20 - qemu_vfree(r->iov.iov_base); + if (status !=3D 0) { + ufs_build_upiu_sense_data(req, sense, sense_len); + response =3D UFS_COMMAND_RESULT_FAIL; + } + + data_segment_length =3D + cpu_to_be16(sense_len + sizeof(req->rsp_upiu.sr.sense_data_len)); + ufs_build_upiu_header(req, UFS_UPIU_TRANSACTION_RESPONSE, flags, respo= nse, + status, data_segment_length); } =20 -static void scsi_check_condition(UfsSCSIReq *r, SCSISense sense) +static void ufs_scsi_command_complete(SCSIRequest *scsi_req, size_t resid) { - trace_ufs_scsi_check_condition(r->req.tag, sense.key, sense.asc, - sense.ascq); - scsi_req_build_sense(&r->req, sense); - scsi_req_complete(&r->req, CHECK_CONDITION); + UfsRequest *req =3D scsi_req->hba_private; + int16_t status =3D scsi_req->status; + + uint32_t transfered_len =3D scsi_req->cmd.xfer - resid; + + ufs_build_scsi_response_upiu(req, scsi_req->sense, scsi_req->sense_len, + transfered_len, status); + + ufs_complete_req(req, UFS_REQUEST_SUCCESS); + + scsi_req->hba_private =3D NULL; + scsi_req_unref(scsi_req); } =20 -static int ufs_scsi_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf, - uint32_t outbuf_len) +static QEMUSGList *ufs_get_sg_list(SCSIRequest *scsi_req) { - UfsHc *u =3D UFS(req->bus->qbus.parent); - UfsLu *lu =3D DO_UPCAST(UfsLu, qdev, req->dev); - uint8_t page_code =3D req->cmd.buf[2]; - int start, buflen =3D 0; + UfsRequest *req =3D scsi_req->hba_private; + return req->sg; +} + +static const struct SCSIBusInfo ufs_scsi_info =3D { + .tcq =3D true, + .max_target =3D 0, + .max_lun =3D UFS_MAX_LUS, + .max_channel =3D 0, + + .get_sg_list =3D ufs_get_sg_list, + .complete =3D ufs_scsi_command_complete, +}; + +static int ufs_emulate_report_luns(UfsRequest *req, uint8_t *outbuf, + uint32_t outbuf_len) +{ + UfsHc *u =3D req->hc; + int len =3D 0; =20 - if (outbuf_len < SCSI_INQUIRY_DATA_SIZE) { - return -1; + /* TODO: Support for cases where SELECT REPORT is 1 and 2 */ + if (req->req_upiu.sc.cdb[2] !=3D 0) { + return SCSI_COMMAND_FAIL; } =20 - outbuf[buflen++] =3D lu->qdev.type & 0x1f; + len +=3D 8; + + for (uint8_t lun =3D 0; lun < UFS_MAX_LUS; ++lun) { + if (u->lus[lun]) { + if (len + 8 > outbuf_len) { + break; + } + + memset(outbuf + len, 0, 8); + outbuf[len] =3D 0; + outbuf[len + 1] =3D lun; + len +=3D 8; + } + } + + /* store the LUN list length */ + stl_be_p(outbuf, len - 8); + + return len; +} + +static int ufs_scsi_emulate_vpd_page(UfsRequest *req, uint8_t *outbuf, + uint32_t outbuf_len) +{ + uint8_t page_code =3D req->req_upiu.sc.cdb[2]; + int start, buflen =3D 0; + + outbuf[buflen++] =3D TYPE_WLUN; outbuf[buflen++] =3D page_code; outbuf[buflen++] =3D 0x00; outbuf[buflen++] =3D 0x00; @@ -78,36 +138,12 @@ static int ufs_scsi_emulate_vpd_page(SCSIRequest *req,= uint8_t *outbuf, switch (page_code) { case 0x00: /* Supported page codes, mandatory */ { - trace_ufs_scsi_emulate_vpd_page_00(req->cmd.xfer); outbuf[buflen++] =3D 0x00; /* list of supported pages (this page) = */ - if (u->params.serial) { - outbuf[buflen++] =3D 0x80; /* unit serial number */ - } outbuf[buflen++] =3D 0x87; /* mode page policy */ break; } - case 0x80: /* Device serial number, optional */ - { - int l; - - if (!u->params.serial) { - trace_ufs_scsi_emulate_vpd_page_80_not_supported(); - return -1; - } - - l =3D strlen(u->params.serial); - if (l > SCSI_INQUIRY_DATA_SIZE) { - l =3D SCSI_INQUIRY_DATA_SIZE; - } - - trace_ufs_scsi_emulate_vpd_page_80(req->cmd.xfer); - memcpy(outbuf + buflen, u->params.serial, l); - buflen +=3D l; - break; - } case 0x87: /* Mode Page Policy, mandatory */ { - trace_ufs_scsi_emulate_vpd_page_87(req->cmd.xfer); outbuf[buflen++] =3D 0x3f; /* apply to all mode pages and subpages= */ outbuf[buflen++] =3D 0xff; outbuf[buflen++] =3D 0; /* shared */ @@ -115,7 +151,7 @@ static int ufs_scsi_emulate_vpd_page(SCSIRequest *req, = uint8_t *outbuf, break; } default: - return -1; + return SCSI_COMMAND_FAIL; } /* done with EVPD */ assert(buflen - start <=3D 255); @@ -123,1150 +159,130 @@ static int ufs_scsi_emulate_vpd_page(SCSIRequest *= req, uint8_t *outbuf, return buflen; } =20 -static int ufs_scsi_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf, +static int ufs_emulate_wlun_inquiry(UfsRequest *req, uint8_t *outbuf, uint32_t outbuf_len) { - int buflen =3D 0; - - if (outbuf_len < SCSI_INQUIRY_DATA_SIZE) { - return -1; + if (outbuf_len < SCSI_INQUIRY_LEN) { + return 0; } =20 - if (req->cmd.buf[1] & 0x1) { + if (req->req_upiu.sc.cdb[1] & 0x1) { /* Vital product data */ return ufs_scsi_emulate_vpd_page(req, outbuf, outbuf_len); } =20 /* Standard INQUIRY data */ - if (req->cmd.buf[2] !=3D 0) { - return -1; + if (req->req_upiu.sc.cdb[2] !=3D 0) { + return SCSI_COMMAND_FAIL; } =20 - /* PAGE CODE =3D=3D 0 */ - buflen =3D req->cmd.xfer; - if (buflen > SCSI_MAX_INQUIRY_LEN) { - buflen =3D SCSI_MAX_INQUIRY_LEN; - } - - if (is_wlun(req->lun)) { - outbuf[0] =3D TYPE_WLUN; - } else { - outbuf[0] =3D 0; - } + outbuf[0] =3D TYPE_WLUN; outbuf[1] =3D 0; - - strpadcpy((char *)&outbuf[16], 16, "QEMU UFS", ' '); + outbuf[2] =3D 0x6; /* SPC-4 */ + outbuf[3] =3D 0x2; + outbuf[4] =3D 31; + outbuf[5] =3D 0; + outbuf[6] =3D 0; + outbuf[7] =3D 0x2; strpadcpy((char *)&outbuf[8], 8, "QEMU", ' '); - + strpadcpy((char *)&outbuf[16], 16, "QEMU UFS", ' '); memset(&outbuf[32], 0, 4); =20 - outbuf[2] =3D 0x06; /* SPC-4 */ - outbuf[3] =3D 0x2; - - if (buflen > SCSI_INQUIRY_DATA_SIZE) { - outbuf[4] =3D buflen - 5; /* Additional Length =3D (Len - 1) - 4 */ - } else { - /* - * If the allocation length of CDB is too small, the additional - * length is not adjusted - */ - outbuf[4] =3D SCSI_INQUIRY_DATA_SIZE - 5; - } - - /* Support TCQ. */ - outbuf[7] =3D req->bus->info->tcq ? 0x02 : 0; - return buflen; -} - -static int mode_sense_page(UfsLu *lu, int page, uint8_t **p_outbuf, - int page_control) -{ - static const int mode_sense_valid[0x3f] =3D { - [MODE_PAGE_CACHING] =3D 1, - [MODE_PAGE_R_W_ERROR] =3D 1, - [MODE_PAGE_CONTROL] =3D 1, - }; - - uint8_t *p =3D *p_outbuf + 2; - int length; - - assert(page < ARRAY_SIZE(mode_sense_valid)); - if ((mode_sense_valid[page]) =3D=3D 0) { - return -1; - } - - /* - * If Changeable Values are requested, a mask denoting those mode para= meters - * that are changeable shall be returned. As we currently don't support - * parameter changes via MODE_SELECT all bits are returned set to zero. - * The buffer was already memset to zero by the caller of this functio= n. - */ - switch (page) { - case MODE_PAGE_CACHING: - length =3D 0x12; - if (page_control =3D=3D 1 || /* Changeable Values */ - blk_enable_write_cache(lu->qdev.conf.blk)) { - p[0] =3D 4; /* WCE */ - } - break; - - case MODE_PAGE_R_W_ERROR: - length =3D 10; - if (page_control =3D=3D 1) { /* Changeable Values */ - break; - } - p[0] =3D 0x80; /* Automatic Write Reallocation Enabled */ - break; - - case MODE_PAGE_CONTROL: - length =3D 10; - if (page_control =3D=3D 1) { /* Changeable Values */ - break; - } - p[1] =3D 0x10; /* Queue Algorithm modifier */ - p[8] =3D 0xff; /* Busy Timeout Period */ - p[9] =3D 0xff; - break; - - default: - return -1; - } - - assert(length < 256); - (*p_outbuf)[0] =3D page; - (*p_outbuf)[1] =3D length; - *p_outbuf +=3D length + 2; - return length + 2; + return SCSI_INQUIRY_LEN; } =20 -static int ufs_scsi_emulate_mode_sense(UfsSCSIReq *r, uint8_t *outbuf) +static UfsReqResult ufs_emulate_scsi_cmd(UfsLu *lu, UfsRequest *req) { - UfsLu *lu =3D DO_UPCAST(UfsLu, qdev, r->req.dev); - bool dbd; - int page, buflen, ret, page_control; - uint8_t *p; - uint8_t dev_specific_param =3D 0; - - dbd =3D (r->req.cmd.buf[1] & 0x8) !=3D 0; - if (!dbd) { - return -1; - } + uint8_t lun =3D lu->lun; + uint8_t outbuf[4096]; + uint8_t sense_buf[UFS_SENSE_SIZE]; + uint8_t scsi_status; + int len =3D 0; =20 - page =3D r->req.cmd.buf[2] & 0x3f; - page_control =3D (r->req.cmd.buf[2] & 0xc0) >> 6; - - trace_ufs_scsi_emulate_mode_sense((r->req.cmd.buf[0] =3D=3D MODE_SENSE= ) ? 6 : - = 10, - page, r->req.cmd.xfer, page_control); - memset(outbuf, 0, r->req.cmd.xfer); - p =3D outbuf; - - if (!blk_is_writable(lu->qdev.conf.blk)) { - dev_specific_param |=3D 0x80; /* Readonly. */ - } - - p[2] =3D 0; /* Medium type. */ - p[3] =3D dev_specific_param; - p[6] =3D p[7] =3D 0; /* Block descriptor length. */ - p +=3D 8; - - if (page_control =3D=3D 3) { - /* Saved Values */ - scsi_check_condition(r, SENSE_CODE(SAVING_PARAMS_NOT_SUPPORTED)); - return -1; - } - - if (page =3D=3D 0x3f) { - for (page =3D 0; page <=3D 0x3e; page++) { - mode_sense_page(lu, page, &p, page_control); - } - } else { - ret =3D mode_sense_page(lu, page, &p, page_control); - if (ret =3D=3D -1) { - return -1; - } - } - - buflen =3D p - outbuf; - /* - * The mode data length field specifies the length in bytes of the - * following data that is available to be transferred. The mode data - * length does not include itself. - */ - outbuf[0] =3D ((buflen - 2) >> 8) & 0xff; - outbuf[1] =3D (buflen - 2) & 0xff; - return buflen; -} - -/* - * scsi_handle_rw_error has two return values. False means that the error - * must be ignored, true means that the error has been processed and the - * caller should not do anything else for this request. Note that - * scsi_handle_rw_error always manages its reference counts, independent - * of the return value. - */ -static bool scsi_handle_rw_error(UfsSCSIReq *r, int ret, bool acct_failed) -{ - bool is_read =3D (r->req.cmd.mode =3D=3D SCSI_XFER_FROM_DEV); - UfsLu *lu =3D DO_UPCAST(UfsLu, qdev, r->req.dev); - SCSISense sense =3D SENSE_CODE(NO_SENSE); - int error =3D 0; - bool req_has_sense =3D false; - BlockErrorAction action; - int status; - - if (ret < 0) { - status =3D scsi_sense_from_errno(-ret, &sense); - error =3D -ret; - } else { - /* A passthrough command has completed with nonzero status. */ - status =3D ret; - if (status =3D=3D CHECK_CONDITION) { - req_has_sense =3D true; - error =3D scsi_sense_buf_to_errno(r->req.sense, sizeof(r->req.= sense)); + switch (req->req_upiu.sc.cdb[0]) { + case REPORT_LUNS: + len =3D ufs_emulate_report_luns(req, outbuf, sizeof(outbuf)); + if (len =3D=3D SCSI_COMMAND_FAIL) { + scsi_build_sense(sense_buf, SENSE_CODE(INVALID_FIELD)); + scsi_status =3D CHECK_CONDITION; } else { - error =3D EINVAL; + scsi_status =3D GOOD; } - } - - /* - * Check whether the error has to be handled by the guest or should - * rather follow the rerror=3D/werror=3D settings. Guest-handled erro= rs - * are usually retried immediately, so do not post them to QMP and - * do not account them as failed I/O. - */ - if (req_has_sense && scsi_sense_buf_is_guest_recoverable( - r->req.sense, sizeof(r->req.sense))) { - action =3D BLOCK_ERROR_ACTION_REPORT; - acct_failed =3D false; - } else { - action =3D blk_get_error_action(lu->qdev.conf.blk, is_read, error); - blk_error_action(lu->qdev.conf.blk, action, is_read, error); - } - - switch (action) { - case BLOCK_ERROR_ACTION_REPORT: - if (acct_failed) { - block_acct_failed(blk_get_stats(lu->qdev.conf.blk), &r->acct); - } - if (!req_has_sense && status =3D=3D CHECK_CONDITION) { - scsi_req_build_sense(&r->req, sense); - } - scsi_req_complete(&r->req, status); - return true; - - case BLOCK_ERROR_ACTION_IGNORE: - return false; - - case BLOCK_ERROR_ACTION_STOP: - scsi_req_retry(&r->req); - return true; - - default: - g_assert_not_reached(); - } -} - -static bool ufs_scsi_req_check_error(UfsSCSIReq *r, int ret, bool acct_fai= led) -{ - if (r->req.io_canceled) { - scsi_req_cancel_complete(&r->req); - return true; - } - - if (ret < 0) { - return scsi_handle_rw_error(r, ret, acct_failed); - } - - return false; -} - -static void scsi_aio_complete(void *opaque, int ret) -{ - UfsSCSIReq *r =3D (UfsSCSIReq *)opaque; - UfsLu *lu =3D DO_UPCAST(UfsLu, qdev, r->req.dev); - - assert(r->req.aiocb !=3D NULL); - r->req.aiocb =3D NULL; - aio_context_acquire(blk_get_aio_context(lu->qdev.conf.blk)); - if (ufs_scsi_req_check_error(r, ret, true)) { - goto done; - } - - block_acct_done(blk_get_stats(lu->qdev.conf.blk), &r->acct); - scsi_req_complete(&r->req, GOOD); - -done: - aio_context_release(blk_get_aio_context(lu->qdev.conf.blk)); - scsi_req_unref(&r->req); -} - -static int32_t ufs_scsi_emulate_command(SCSIRequest *req, uint8_t *buf) -{ - UfsSCSIReq *r =3D DO_UPCAST(UfsSCSIReq, req, req); - UfsLu *lu =3D DO_UPCAST(UfsLu, qdev, req->dev); - uint32_t last_block =3D 0; - uint8_t *outbuf; - int buflen; - - switch (req->cmd.buf[0]) { - case INQUIRY: - case MODE_SENSE_10: - case START_STOP: - case REQUEST_SENSE: - break; - - default: - if (!blk_is_available(lu->qdev.conf.blk)) { - scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); - return 0; - } - break; - } - - /* - * FIXME: we shouldn't return anything bigger than 4k, but the code - * requires the buffer to be as big as req->cmd.xfer in several - * places. So, do not allow CDBs with a very large ALLOCATION - * LENGTH. The real fix would be to modify scsi_read_data and - * dma_buf_read, so that they return data beyond the buflen - * as all zeros. - */ - if (req->cmd.xfer > 65536) { - goto illegal_request; - } - r->buflen =3D MAX(4096, req->cmd.xfer); - - if (!r->iov.iov_base) { - r->iov.iov_base =3D blk_blockalign(lu->qdev.conf.blk, r->buflen); - } - - outbuf =3D r->iov.iov_base; - memset(outbuf, 0, r->buflen); - switch (req->cmd.buf[0]) { - case TEST_UNIT_READY: - assert(blk_is_available(lu->qdev.conf.blk)); break; case INQUIRY: - buflen =3D ufs_scsi_emulate_inquiry(req, outbuf, r->buflen); - if (buflen < 0) { - goto illegal_request; - } - break; - case MODE_SENSE_10: - buflen =3D ufs_scsi_emulate_mode_sense(r, outbuf); - if (buflen < 0) { - goto illegal_request; + len =3D ufs_emulate_wlun_inquiry(req, outbuf, sizeof(outbuf)); + if (len =3D=3D SCSI_COMMAND_FAIL) { + scsi_build_sense(sense_buf, SENSE_CODE(INVALID_FIELD)); + scsi_status =3D CHECK_CONDITION; + } else { + scsi_status =3D GOOD; } break; - case READ_CAPACITY_10: - /* The normal LEN field for this command is zero. */ - memset(outbuf, 0, 8); - if (lu->qdev.max_lba > 0) { - last_block =3D lu->qdev.max_lba - 1; - }; - outbuf[0] =3D (last_block >> 24) & 0xff; - outbuf[1] =3D (last_block >> 16) & 0xff; - outbuf[2] =3D (last_block >> 8) & 0xff; - outbuf[3] =3D last_block & 0xff; - outbuf[4] =3D (lu->qdev.blocksize >> 24) & 0xff; - outbuf[5] =3D (lu->qdev.blocksize >> 16) & 0xff; - outbuf[6] =3D (lu->qdev.blocksize >> 8) & 0xff; - outbuf[7] =3D lu->qdev.blocksize & 0xff; - break; case REQUEST_SENSE: - /* Just return "NO SENSE". */ - buflen =3D scsi_convert_sense(NULL, 0, outbuf, r->buflen, - (req->cmd.buf[1] & 1) =3D=3D 0); - if (buflen < 0) { - goto illegal_request; - } - break; - case SYNCHRONIZE_CACHE: - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - block_acct_start(blk_get_stats(lu->qdev.conf.blk), &r->acct, 0, - BLOCK_ACCT_FLUSH); - r->req.aiocb =3D blk_aio_flush(lu->qdev.conf.blk, scsi_aio_complet= e, r); - return 0; - case VERIFY_10: - trace_ufs_scsi_emulate_command_VERIFY((req->cmd.buf[1] >> 1) & 3); - if (req->cmd.buf[1] & 6) { - goto illegal_request; - } - break; - case SERVICE_ACTION_IN_16: - /* Service Action In subcommands. */ - if ((req->cmd.buf[1] & 31) =3D=3D SAI_READ_CAPACITY_16) { - trace_ufs_scsi_emulate_command_SAI_16(); - memset(outbuf, 0, req->cmd.xfer); - - if (lu->qdev.max_lba > 0) { - last_block =3D lu->qdev.max_lba - 1; - }; - outbuf[0] =3D 0; - outbuf[1] =3D 0; - outbuf[2] =3D 0; - outbuf[3] =3D 0; - outbuf[4] =3D (last_block >> 24) & 0xff; - outbuf[5] =3D (last_block >> 16) & 0xff; - outbuf[6] =3D (last_block >> 8) & 0xff; - outbuf[7] =3D last_block & 0xff; - outbuf[8] =3D (lu->qdev.blocksize >> 24) & 0xff; - outbuf[9] =3D (lu->qdev.blocksize >> 16) & 0xff; - outbuf[10] =3D (lu->qdev.blocksize >> 8) & 0xff; - outbuf[11] =3D lu->qdev.blocksize & 0xff; - outbuf[12] =3D 0; - outbuf[13] =3D get_physical_block_exp(&lu->qdev.conf); - - if (lu->unit_desc.provisioning_type =3D=3D 2 || - lu->unit_desc.provisioning_type =3D=3D 3) { - outbuf[14] =3D 0x80; - } - /* Protection, exponent and lowest lba field left blank. */ - break; - } - trace_ufs_scsi_emulate_command_SAI_unsupported(); - goto illegal_request; - case MODE_SELECT_10: - trace_ufs_scsi_emulate_command_MODE_SELECT_10(r->req.cmd.xfer); + /* Just return no sense data */ + len =3D scsi_build_sense_buf(outbuf, sizeof(outbuf), SENSE_CODE(NO= _SENSE), + true); + scsi_status =3D GOOD; break; case START_STOP: - /* - * TODO: START_STOP is not yet implemented. It always returns succ= ess. - * Revisit it when ufs power management is implemented. - */ - trace_ufs_scsi_emulate_command_START_STOP(); - break; - case FORMAT_UNIT: - trace_ufs_scsi_emulate_command_FORMAT_UNIT(); - break; - case SEND_DIAGNOSTIC: - trace_ufs_scsi_emulate_command_SEND_DIAGNOSTIC(); - break; - default: - trace_ufs_scsi_emulate_command_UNKNOWN(buf[0], - scsi_command_name(buf[0])); - scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE)); - return 0; - } - assert(!r->req.aiocb); - r->iov.iov_len =3D MIN(r->buflen, req->cmd.xfer); - if (r->iov.iov_len =3D=3D 0) { - scsi_req_complete(&r->req, GOOD); - } - if (r->req.cmd.mode =3D=3D SCSI_XFER_TO_DEV) { - assert(r->iov.iov_len =3D=3D req->cmd.xfer); - return -r->iov.iov_len; - } else { - return r->iov.iov_len; - } - -illegal_request: - if (r->req.status =3D=3D -1) { - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); - } - return 0; -} - -static void ufs_scsi_emulate_read_data(SCSIRequest *req) -{ - UfsSCSIReq *r =3D DO_UPCAST(UfsSCSIReq, req, req); - int buflen =3D r->iov.iov_len; - - if (buflen) { - trace_ufs_scsi_emulate_read_data(buflen); - r->iov.iov_len =3D 0; - r->started =3D true; - scsi_req_data(&r->req, buflen); - return; - } - - /* This also clears the sense buffer for REQUEST SENSE. */ - scsi_req_complete(&r->req, GOOD); -} - -static int ufs_scsi_check_mode_select(UfsLu *lu, int page, uint8_t *inbuf, - int inlen) -{ - uint8_t mode_current[SCSI_MAX_MODE_LEN]; - uint8_t mode_changeable[SCSI_MAX_MODE_LEN]; - uint8_t *p; - int len, expected_len, changeable_len, i; - - /* - * The input buffer does not include the page header, so it is - * off by 2 bytes. - */ - expected_len =3D inlen + 2; - if (expected_len > SCSI_MAX_MODE_LEN) { - return -1; - } - - /* MODE_PAGE_ALLS is only valid for MODE SENSE commands */ - if (page =3D=3D MODE_PAGE_ALLS) { - return -1; - } - - p =3D mode_current; - memset(mode_current, 0, inlen + 2); - len =3D mode_sense_page(lu, page, &p, 0); - if (len < 0 || len !=3D expected_len) { - return -1; - } - - p =3D mode_changeable; - memset(mode_changeable, 0, inlen + 2); - changeable_len =3D mode_sense_page(lu, page, &p, 1); - assert(changeable_len =3D=3D len); - - /* - * Check that unchangeable bits are the same as what MODE SENSE - * would return. - */ - for (i =3D 2; i < len; i++) { - if (((mode_current[i] ^ inbuf[i - 2]) & ~mode_changeable[i]) !=3D = 0) { - return -1; + /* TODO: Revisit it when Power Management is implemented */ + if (lun =3D=3D UFS_UPIU_UFS_DEVICE_WLUN) { + scsi_status =3D GOOD; + break; } - } - return 0; -} - -static void ufs_scsi_apply_mode_select(UfsLu *lu, int page, uint8_t *p) -{ - switch (page) { - case MODE_PAGE_CACHING: - blk_set_enable_write_cache(lu->qdev.conf.blk, (p[0] & 4) !=3D 0); - break; - + /* fallthrough */ default: - break; + scsi_build_sense(sense_buf, SENSE_CODE(INVALID_OPCODE)); + scsi_status =3D CHECK_CONDITION; } -} - -static int mode_select_pages(UfsSCSIReq *r, uint8_t *p, int len, bool chan= ge) -{ - UfsLu *lu =3D DO_UPCAST(UfsLu, qdev, r->req.dev); - - while (len > 0) { - int page, page_len; =20 - page =3D p[0] & 0x3f; - if (p[0] & 0x40) { - goto invalid_param; - } else { - if (len < 2) { - goto invalid_param_len; - } - page_len =3D p[1]; - p +=3D 2; - len -=3D 2; - } - - if (page_len > len) { - goto invalid_param_len; - } - - if (!change) { - if (ufs_scsi_check_mode_select(lu, page, p, page_len) < 0) { - goto invalid_param; - } - } else { - ufs_scsi_apply_mode_select(lu, page, p); - } - - p +=3D page_len; - len -=3D page_len; + len =3D MIN(len, (int)req->data_len); + if (scsi_status =3D=3D GOOD && len > 0 && + dma_buf_read(outbuf, len, NULL, req->sg, MEMTXATTRS_UNSPECIFIED) != =3D + MEMTX_OK) { + return UFS_REQUEST_FAIL; } - return 0; - -invalid_param: - scsi_check_condition(r, SENSE_CODE(INVALID_PARAM)); - return -1; =20 -invalid_param_len: - scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN)); - return -1; + ufs_build_scsi_response_upiu(req, sense_buf, sizeof(sense_buf), len, + scsi_status); + return UFS_REQUEST_SUCCESS; } =20 -static void ufs_scsi_emulate_mode_select(UfsSCSIReq *r, uint8_t *inbuf) +static UfsReqResult ufs_process_scsi_cmd(UfsLu *lu, UfsRequest *req) { - UfsLu *lu =3D DO_UPCAST(UfsLu, qdev, r->req.dev); - uint8_t *p =3D inbuf; - int len =3D r->req.cmd.xfer; - int hdr_len =3D 8; - int bd_len; - int pass; - - /* We only support PF=3D1, SP=3D0. */ - if ((r->req.cmd.buf[1] & 0x11) !=3D 0x10) { - goto invalid_field; - } - - if (len < hdr_len) { - goto invalid_param_len; - } - - bd_len =3D lduw_be_p(&p[6]); - if (bd_len !=3D 0) { - goto invalid_param; - } - - len -=3D hdr_len; - p +=3D hdr_len; - - /* Ensure no change is made if there is an error! */ - for (pass =3D 0; pass < 2; pass++) { - if (mode_select_pages(r, p, len, pass =3D=3D 1) < 0) { - assert(pass =3D=3D 0); - return; - } - } - - if (!blk_enable_write_cache(lu->qdev.conf.blk)) { - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - block_acct_start(blk_get_stats(lu->qdev.conf.blk), &r->acct, 0, - BLOCK_ACCT_FLUSH); - r->req.aiocb =3D blk_aio_flush(lu->qdev.conf.blk, scsi_aio_complet= e, r); - return; - } - - scsi_req_complete(&r->req, GOOD); - return; - -invalid_param: - scsi_check_condition(r, SENSE_CODE(INVALID_PARAM)); - return; + uint8_t task_tag =3D req->req_upiu.header.task_tag; =20 -invalid_param_len: - scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN)); - return; - -invalid_field: - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); -} - -/* block_num and nb_blocks expected to be in qdev blocksize */ -static inline bool check_lba_range(UfsLu *lu, uint64_t block_num, - uint32_t nb_blocks) -{ /* - * The first line tests that no overflow happens when computing the la= st - * block. The second line tests that the last accessed block is in - * range. - * - * Careful, the computations should not underflow for nb_blocks =3D=3D= 0, - * and a 0-block read to the first LBA beyond the end of device is - * valid. + * Each ufs-lu has its own independent virtual SCSI bus. Therefore, we= can't + * use scsi_target_emulate_report_luns() which gets all lu information= over + * the SCSI bus. Therefore, we use ufs_emulate_scsi_cmd() like the + * well-known lu. */ - return (block_num <=3D block_num + nb_blocks && - block_num + nb_blocks <=3D lu->qdev.max_lba + 1); -} - -static void ufs_scsi_emulate_write_data(SCSIRequest *req) -{ - UfsSCSIReq *r =3D DO_UPCAST(UfsSCSIReq, req, req); - - if (r->iov.iov_len) { - int buflen =3D r->iov.iov_len; - trace_ufs_scsi_emulate_write_data(buflen); - r->iov.iov_len =3D 0; - scsi_req_data(&r->req, buflen); - return; - } - - switch (req->cmd.buf[0]) { - case MODE_SELECT_10: - /* This also clears the sense buffer for REQUEST SENSE. */ - ufs_scsi_emulate_mode_select(r, r->iov.iov_base); - break; - default: - abort(); - } -} - -/* Return a pointer to the data buffer. */ -static uint8_t *ufs_scsi_get_buf(SCSIRequest *req) -{ - UfsSCSIReq *r =3D DO_UPCAST(UfsSCSIReq, req, req); - - return (uint8_t *)r->iov.iov_base; -} - -static int32_t ufs_scsi_dma_command(SCSIRequest *req, uint8_t *buf) -{ - UfsSCSIReq *r =3D DO_UPCAST(UfsSCSIReq, req, req); - UfsLu *lu =3D DO_UPCAST(UfsLu, qdev, req->dev); - uint32_t len; - uint8_t command; - - command =3D buf[0]; - - if (!blk_is_available(lu->qdev.conf.blk)) { - scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); - return 0; - } - - len =3D scsi_data_cdb_xfer(r->req.cmd.buf); - switch (command) { - case READ_6: - case READ_10: - trace_ufs_scsi_dma_command_READ(r->req.cmd.lba, len); - if (r->req.cmd.buf[1] & 0xe0) { - goto illegal_request; - } - if (!check_lba_range(lu, r->req.cmd.lba, len)) { - goto illegal_lba; - } - r->sector =3D r->req.cmd.lba * (lu->qdev.blocksize / BDRV_SECTOR_S= IZE); - r->sector_count =3D len * (lu->qdev.blocksize / BDRV_SECTOR_SIZE); - break; - case WRITE_6: - case WRITE_10: - trace_ufs_scsi_dma_command_WRITE(r->req.cmd.lba, len); - if (!blk_is_writable(lu->qdev.conf.blk)) { - scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED)); - return 0; - } - if (r->req.cmd.buf[1] & 0xe0) { - goto illegal_request; - } - if (!check_lba_range(lu, r->req.cmd.lba, len)) { - goto illegal_lba; - } - r->sector =3D r->req.cmd.lba * (lu->qdev.blocksize / BDRV_SECTOR_S= IZE); - r->sector_count =3D len * (lu->qdev.blocksize / BDRV_SECTOR_SIZE); - break; - default: - abort(); - illegal_request: - scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); - return 0; - illegal_lba: - scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE)); - return 0; - } - r->need_fua_emulation =3D ((r->req.cmd.buf[1] & 8) !=3D 0); - if (r->sector_count =3D=3D 0) { - scsi_req_complete(&r->req, GOOD); - } - assert(r->iov.iov_len =3D=3D 0); - if (r->req.cmd.mode =3D=3D SCSI_XFER_TO_DEV) { - return -r->sector_count * BDRV_SECTOR_SIZE; - } else { - return r->sector_count * BDRV_SECTOR_SIZE; - } -} - -static void scsi_write_do_fua(UfsSCSIReq *r) -{ - UfsLu *lu =3D DO_UPCAST(UfsLu, qdev, r->req.dev); - - assert(r->req.aiocb =3D=3D NULL); - assert(!r->req.io_canceled); - - if (r->need_fua_emulation) { - block_acct_start(blk_get_stats(lu->qdev.conf.blk), &r->acct, 0, - BLOCK_ACCT_FLUSH); - r->req.aiocb =3D blk_aio_flush(lu->qdev.conf.blk, scsi_aio_complet= e, r); - return; - } - - scsi_req_complete(&r->req, GOOD); - scsi_req_unref(&r->req); -} - -static void scsi_dma_complete_noio(UfsSCSIReq *r, int ret) -{ - assert(r->req.aiocb =3D=3D NULL); - if (ufs_scsi_req_check_error(r, ret, false)) { - goto done; - } - - r->sector +=3D r->sector_count; - r->sector_count =3D 0; - if (r->req.cmd.mode =3D=3D SCSI_XFER_TO_DEV) { - scsi_write_do_fua(r); - return; - } else { - scsi_req_complete(&r->req, GOOD); - } - -done: - scsi_req_unref(&r->req); -} - -static void scsi_dma_complete(void *opaque, int ret) -{ - UfsSCSIReq *r =3D (UfsSCSIReq *)opaque; - UfsLu *lu =3D DO_UPCAST(UfsLu, qdev, r->req.dev); - - assert(r->req.aiocb !=3D NULL); - r->req.aiocb =3D NULL; - - aio_context_acquire(blk_get_aio_context(lu->qdev.conf.blk)); - if (ret < 0) { - block_acct_failed(blk_get_stats(lu->qdev.conf.blk), &r->acct); - } else { - block_acct_done(blk_get_stats(lu->qdev.conf.blk), &r->acct); - } - scsi_dma_complete_noio(r, ret); - aio_context_release(blk_get_aio_context(lu->qdev.conf.blk)); -} - -static BlockAIOCB *scsi_dma_readv(int64_t offset, QEMUIOVector *iov, - BlockCompletionFunc *cb, void *cb_opaque, - void *opaque) -{ - UfsSCSIReq *r =3D opaque; - UfsLu *lu =3D DO_UPCAST(UfsLu, qdev, r->req.dev); - return blk_aio_preadv(lu->qdev.conf.blk, offset, iov, 0, cb, cb_opaque= ); -} - -static void scsi_init_iovec(UfsSCSIReq *r, size_t size) -{ - UfsLu *lu =3D DO_UPCAST(UfsLu, qdev, r->req.dev); - - if (!r->iov.iov_base) { - r->buflen =3D size; - r->iov.iov_base =3D blk_blockalign(lu->qdev.conf.blk, r->buflen); + if (req->req_upiu.sc.cdb[0] =3D=3D REPORT_LUNS) { + return ufs_emulate_scsi_cmd(lu, req); } - r->iov.iov_len =3D MIN(r->sector_count * BDRV_SECTOR_SIZE, r->buflen); - qemu_iovec_init_external(&r->qiov, &r->iov, 1); -} =20 -static void scsi_read_complete_noio(UfsSCSIReq *r, int ret) -{ - uint32_t n; + SCSIRequest *scsi_req =3D + scsi_req_new(lu->scsi_dev, task_tag, lu->lun, req->req_upiu.sc.cdb, + UFS_CDB_SIZE, req); =20 - assert(r->req.aiocb =3D=3D NULL); - if (ufs_scsi_req_check_error(r, ret, false)) { - goto done; + uint32_t len =3D scsi_req_enqueue(scsi_req); + if (len) { + scsi_req_continue(scsi_req); } =20 - n =3D r->qiov.size / BDRV_SECTOR_SIZE; - r->sector +=3D n; - r->sector_count -=3D n; - scsi_req_data(&r->req, r->qiov.size); - -done: - scsi_req_unref(&r->req); -} - -static void scsi_read_complete(void *opaque, int ret) -{ - UfsSCSIReq *r =3D (UfsSCSIReq *)opaque; - UfsLu *lu =3D DO_UPCAST(UfsLu, qdev, r->req.dev); - - assert(r->req.aiocb !=3D NULL); - r->req.aiocb =3D NULL; - trace_ufs_scsi_read_data_count(r->sector_count); - aio_context_acquire(blk_get_aio_context(lu->qdev.conf.blk)); - if (ret < 0) { - block_acct_failed(blk_get_stats(lu->qdev.conf.blk), &r->acct); - } else { - block_acct_done(blk_get_stats(lu->qdev.conf.blk), &r->acct); - trace_ufs_scsi_read_complete(r->req.tag, r->qiov.size); - } - scsi_read_complete_noio(r, ret); - aio_context_release(blk_get_aio_context(lu->qdev.conf.blk)); -} - -/* Actually issue a read to the block device. */ -static void scsi_do_read(UfsSCSIReq *r, int ret) -{ - UfsLu *lu =3D DO_UPCAST(UfsLu, qdev, r->req.dev); - - assert(r->req.aiocb =3D=3D NULL); - if (ufs_scsi_req_check_error(r, ret, false)) { - goto done; - } - - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - - if (r->req.sg) { - dma_acct_start(lu->qdev.conf.blk, &r->acct, r->req.sg, BLOCK_ACCT_= READ); - r->req.residual -=3D r->req.sg->size; - r->req.aiocb =3D dma_blk_io( - blk_get_aio_context(lu->qdev.conf.blk), r->req.sg, - r->sector << BDRV_SECTOR_BITS, BDRV_SECTOR_SIZE, scsi_dma_read= v, r, - scsi_dma_complete, r, DMA_DIRECTION_FROM_DEVICE); - } else { - scsi_init_iovec(r, SCSI_DMA_BUF_SIZE); - block_acct_start(blk_get_stats(lu->qdev.conf.blk), &r->acct, - r->qiov.size, BLOCK_ACCT_READ); - r->req.aiocb =3D scsi_dma_readv(r->sector << BDRV_SECTOR_BITS, &r-= >qiov, - scsi_read_complete, r, r); - } - -done: - scsi_req_unref(&r->req); -} - -static void scsi_do_read_cb(void *opaque, int ret) -{ - UfsSCSIReq *r =3D (UfsSCSIReq *)opaque; - UfsLu *lu =3D DO_UPCAST(UfsLu, qdev, r->req.dev); - - assert(r->req.aiocb !=3D NULL); - r->req.aiocb =3D NULL; - - aio_context_acquire(blk_get_aio_context(lu->qdev.conf.blk)); - if (ret < 0) { - block_acct_failed(blk_get_stats(lu->qdev.conf.blk), &r->acct); - } else { - block_acct_done(blk_get_stats(lu->qdev.conf.blk), &r->acct); - } - scsi_do_read(opaque, ret); - aio_context_release(blk_get_aio_context(lu->qdev.conf.blk)); -} - -/* Read more data from scsi device into buffer. */ -static void scsi_read_data(SCSIRequest *req) -{ - UfsSCSIReq *r =3D DO_UPCAST(UfsSCSIReq, req, req); - UfsLu *lu =3D DO_UPCAST(UfsLu, qdev, r->req.dev); - bool first; - - trace_ufs_scsi_read_data_count(r->sector_count); - if (r->sector_count =3D=3D 0) { - /* This also clears the sense buffer for REQUEST SENSE. */ - scsi_req_complete(&r->req, GOOD); - return; - } - - /* No data transfer may already be in progress */ - assert(r->req.aiocb =3D=3D NULL); - - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - if (r->req.cmd.mode =3D=3D SCSI_XFER_TO_DEV) { - trace_ufs_scsi_read_data_invalid(); - scsi_read_complete_noio(r, -EINVAL); - return; - } - - if (!blk_is_available(req->dev->conf.blk)) { - scsi_read_complete_noio(r, -ENOMEDIUM); - return; - } - - first =3D !r->started; - r->started =3D true; - if (first && r->need_fua_emulation) { - block_acct_start(blk_get_stats(lu->qdev.conf.blk), &r->acct, 0, - BLOCK_ACCT_FLUSH); - r->req.aiocb =3D blk_aio_flush(lu->qdev.conf.blk, scsi_do_read_cb,= r); - } else { - scsi_do_read(r, 0); - } -} - -static void scsi_write_complete_noio(UfsSCSIReq *r, int ret) -{ - uint32_t n; - - assert(r->req.aiocb =3D=3D NULL); - if (ufs_scsi_req_check_error(r, ret, false)) { - goto done; - } - - n =3D r->qiov.size / BDRV_SECTOR_SIZE; - r->sector +=3D n; - r->sector_count -=3D n; - if (r->sector_count =3D=3D 0) { - scsi_write_do_fua(r); - return; - } else { - scsi_init_iovec(r, SCSI_DMA_BUF_SIZE); - trace_ufs_scsi_write_complete_noio(r->req.tag, r->qiov.size); - scsi_req_data(&r->req, r->qiov.size); - } - -done: - scsi_req_unref(&r->req); -} - -static void scsi_write_complete(void *opaque, int ret) -{ - UfsSCSIReq *r =3D (UfsSCSIReq *)opaque; - UfsLu *lu =3D DO_UPCAST(UfsLu, qdev, r->req.dev); - - assert(r->req.aiocb !=3D NULL); - r->req.aiocb =3D NULL; - - aio_context_acquire(blk_get_aio_context(lu->qdev.conf.blk)); - if (ret < 0) { - block_acct_failed(blk_get_stats(lu->qdev.conf.blk), &r->acct); - } else { - block_acct_done(blk_get_stats(lu->qdev.conf.blk), &r->acct); - } - scsi_write_complete_noio(r, ret); - aio_context_release(blk_get_aio_context(lu->qdev.conf.blk)); -} - -static BlockAIOCB *scsi_dma_writev(int64_t offset, QEMUIOVector *iov, - BlockCompletionFunc *cb, void *cb_opaqu= e, - void *opaque) -{ - UfsSCSIReq *r =3D opaque; - UfsLu *lu =3D DO_UPCAST(UfsLu, qdev, r->req.dev); - return blk_aio_pwritev(lu->qdev.conf.blk, offset, iov, 0, cb, cb_opaqu= e); -} - -static void scsi_write_data(SCSIRequest *req) -{ - UfsSCSIReq *r =3D DO_UPCAST(UfsSCSIReq, req, req); - UfsLu *lu =3D DO_UPCAST(UfsLu, qdev, r->req.dev); - - /* No data transfer may already be in progress */ - assert(r->req.aiocb =3D=3D NULL); - - /* The request is used as the AIO opaque value, so add a ref. */ - scsi_req_ref(&r->req); - if (r->req.cmd.mode !=3D SCSI_XFER_TO_DEV) { - trace_ufs_scsi_write_data_invalid(); - scsi_write_complete_noio(r, -EINVAL); - return; - } - - if (!r->req.sg && !r->qiov.size) { - /* Called for the first time. Ask the driver to send us more data= . */ - r->started =3D true; - scsi_write_complete_noio(r, 0); - return; - } - if (!blk_is_available(req->dev->conf.blk)) { - scsi_write_complete_noio(r, -ENOMEDIUM); - return; - } - - if (r->req.sg) { - dma_acct_start(lu->qdev.conf.blk, &r->acct, r->req.sg, - BLOCK_ACCT_WRITE); - r->req.residual -=3D r->req.sg->size; - r->req.aiocb =3D dma_blk_io( - blk_get_aio_context(lu->qdev.conf.blk), r->req.sg, - r->sector << BDRV_SECTOR_BITS, BDRV_SECTOR_SIZE, scsi_dma_writ= ev, r, - scsi_dma_complete, r, DMA_DIRECTION_TO_DEVICE); - } else { - block_acct_start(blk_get_stats(lu->qdev.conf.blk), &r->acct, - r->qiov.size, BLOCK_ACCT_WRITE); - r->req.aiocb =3D scsi_dma_writev(r->sector << BDRV_SECTOR_BITS, &r= ->qiov, - scsi_write_complete, r, r); - } -} - -static const SCSIReqOps ufs_scsi_emulate_reqops =3D { - .size =3D sizeof(UfsSCSIReq), - .free_req =3D ufs_scsi_free_request, - .send_command =3D ufs_scsi_emulate_command, - .read_data =3D ufs_scsi_emulate_read_data, - .write_data =3D ufs_scsi_emulate_write_data, - .get_buf =3D ufs_scsi_get_buf, -}; - -static const SCSIReqOps ufs_scsi_dma_reqops =3D { - .size =3D sizeof(UfsSCSIReq), - .free_req =3D ufs_scsi_free_request, - .send_command =3D ufs_scsi_dma_command, - .read_data =3D scsi_read_data, - .write_data =3D scsi_write_data, - .get_buf =3D ufs_scsi_get_buf, -}; - -/* - * Following commands are not yet supported - * PRE_FETCH(10), - * UNMAP, - * WRITE_BUFFER, READ_BUFFER, - * SECURITY_PROTOCOL_IN, SECURITY_PROTOCOL_OUT - */ -static const SCSIReqOps *const ufs_scsi_reqops_dispatch[256] =3D { - [TEST_UNIT_READY] =3D &ufs_scsi_emulate_reqops, - [INQUIRY] =3D &ufs_scsi_emulate_reqops, - [MODE_SENSE_10] =3D &ufs_scsi_emulate_reqops, - [START_STOP] =3D &ufs_scsi_emulate_reqops, - [READ_CAPACITY_10] =3D &ufs_scsi_emulate_reqops, - [REQUEST_SENSE] =3D &ufs_scsi_emulate_reqops, - [SYNCHRONIZE_CACHE] =3D &ufs_scsi_emulate_reqops, - [MODE_SELECT_10] =3D &ufs_scsi_emulate_reqops, - [VERIFY_10] =3D &ufs_scsi_emulate_reqops, - [FORMAT_UNIT] =3D &ufs_scsi_emulate_reqops, - [SERVICE_ACTION_IN_16] =3D &ufs_scsi_emulate_reqops, - [SEND_DIAGNOSTIC] =3D &ufs_scsi_emulate_reqops, - - [READ_6] =3D &ufs_scsi_dma_reqops, - [READ_10] =3D &ufs_scsi_dma_reqops, - [WRITE_6] =3D &ufs_scsi_dma_reqops, - [WRITE_10] =3D &ufs_scsi_dma_reqops, -}; - -static SCSIRequest *scsi_new_request(SCSIDevice *dev, uint32_t tag, - uint32_t lun, uint8_t *buf, - void *hba_private) -{ - UfsLu *lu =3D DO_UPCAST(UfsLu, qdev, dev); - SCSIRequest *req; - const SCSIReqOps *ops; - uint8_t command; - - command =3D buf[0]; - ops =3D ufs_scsi_reqops_dispatch[command]; - if (!ops) { - ops =3D &ufs_scsi_emulate_reqops; - } - req =3D scsi_req_alloc(ops, &lu->qdev, tag, lun, hba_private); - - return req; + return UFS_REQUEST_NO_COMPLETE; } =20 static Property ufs_lu_props[] =3D { - DEFINE_PROP_DRIVE("drive", UfsLu, qdev.conf.blk), + DEFINE_PROP_DRIVE("drive", UfsLu, conf.blk), + DEFINE_PROP_UINT8("lun", UfsLu, lun, 0), DEFINE_PROP_END_OF_LIST(), }; =20 -static bool ufs_lu_brdv_init(UfsLu *lu, Error **errp) -{ - SCSIDevice *dev =3D &lu->qdev; - bool read_only; - - if (!lu->qdev.conf.blk) { - error_setg(errp, "drive property not set"); - return false; - } - - if (!blkconf_blocksizes(&lu->qdev.conf, errp)) { - return false; - } - - if (blk_get_aio_context(lu->qdev.conf.blk) !=3D qemu_get_aio_context()= && - !lu->qdev.hba_supports_iothread) { - error_setg(errp, "HBA does not support iothreads"); - return false; - } - - read_only =3D !blk_supports_write_perm(lu->qdev.conf.blk); - - if (!blkconf_apply_backend_options(&dev->conf, read_only, - dev->type =3D=3D TYPE_DISK, errp)) { - return false; - } - - if (blk_is_sg(lu->qdev.conf.blk)) { - error_setg(errp, "unwanted /dev/sg*"); - return false; - } - - blk_iostatus_enable(lu->qdev.conf.blk); - return true; -} - static bool ufs_add_lu(UfsHc *u, UfsLu *lu, Error **errp) { - BlockBackend *blk =3D lu->qdev.conf.blk; + BlockBackend *blk =3D lu->conf.blk; int64_t brdv_len =3D blk_getlength(blk); uint64_t raw_dev_cap =3D be64_to_cpu(u->geometry_desc.total_raw_device_capacity); @@ -1288,156 +304,143 @@ static bool ufs_add_lu(UfsHc *u, UfsLu *lu, Error = **errp) return true; } =20 -static inline uint8_t ufs_log2(uint64_t input) +void ufs_init_wlu(UfsLu *wlu, uint8_t wlun) { - int log =3D 0; - while (input >>=3D 1) { - log++; - } - return log; + wlu->lun =3D wlun; + wlu->scsi_op =3D &ufs_emulate_scsi_cmd; } =20 static void ufs_init_lu(UfsLu *lu) { - BlockBackend *blk =3D lu->qdev.conf.blk; + BlockBackend *blk =3D lu->conf.blk; int64_t brdv_len =3D blk_getlength(blk); =20 - lu->lun =3D lu->qdev.lun; memset(&lu->unit_desc, 0, sizeof(lu->unit_desc)); lu->unit_desc.length =3D sizeof(UnitDescriptor); lu->unit_desc.descriptor_idn =3D UFS_QUERY_DESC_IDN_UNIT; lu->unit_desc.lu_enable =3D 0x01; - lu->unit_desc.logical_block_size =3D ufs_log2(lu->qdev.blocksize); - lu->unit_desc.unit_index =3D lu->qdev.lun; + lu->unit_desc.logical_block_size =3D UFS_BLOCK_SIZE_SHIFT; + lu->unit_desc.unit_index =3D lu->lun; lu->unit_desc.logical_block_count =3D cpu_to_be64(brdv_len / (1 << lu->unit_desc.logical_block_size)); + + lu->scsi_op =3D &ufs_process_scsi_cmd; } =20 static bool ufs_lu_check_constraints(UfsLu *lu, Error **errp) { - if (!lu->qdev.conf.blk) { + if (!lu->conf.blk) { error_setg(errp, "drive property not set"); return false; } =20 - if (lu->qdev.channel !=3D 0) { - error_setg(errp, "ufs logical unit does not support channel"); + if (lu->lun >=3D UFS_MAX_LUS) { + error_setg(errp, "lun must be between 0 and %d", UFS_MAX_LUS - 1); return false; } =20 - if (lu->qdev.lun >=3D UFS_MAX_LUS) { - error_setg(errp, "lun must be between 1 and %d", UFS_MAX_LUS - 1); - return false; + return true; +} + +static void ufs_init_scsi_device(UfsLu *lu, BlockBackend *blk, Error **err= p) +{ + DeviceState *scsi_dev; + + scsi_bus_init(&lu->bus, sizeof(lu->bus), DEVICE(lu), &ufs_scsi_info); + + blk_ref(blk); + blk_detach_dev(blk, DEVICE(lu)); + lu->conf.blk =3D NULL; + + /* + * The ufs-lu is the device that is wrapping the scsi-hd. It owns a vi= rtual + * SCSI bus that serves the scsi-hd. + */ + scsi_dev =3D qdev_new("scsi-hd"); + object_property_add_child(OBJECT(&lu->bus), "ufs-scsi", OBJECT(scsi_de= v)); + + qdev_prop_set_uint32(scsi_dev, "physical_block_size", UFS_BLOCK_SIZE); + qdev_prop_set_uint32(scsi_dev, "logical_block_size", UFS_BLOCK_SIZE); + qdev_prop_set_uint32(scsi_dev, "scsi-id", 0); + qdev_prop_set_uint32(scsi_dev, "lun", lu->lun); + if (!qdev_prop_set_drive_err(scsi_dev, "drive", blk, errp)) { + object_unparent(OBJECT(scsi_dev)); + return; } =20 - return true; + if (!qdev_realize_and_unref(scsi_dev, &lu->bus.qbus, errp)) { + object_unparent(OBJECT(scsi_dev)); + return; + } + + blk_unref(blk); + lu->scsi_dev =3D SCSI_DEVICE(scsi_dev); } =20 -static void ufs_lu_realize(SCSIDevice *dev, Error **errp) +static void ufs_lu_realize(DeviceState *dev, Error **errp) { UfsLu *lu =3D DO_UPCAST(UfsLu, qdev, dev); - BusState *s =3D qdev_get_parent_bus(&dev->qdev); + BusState *s =3D qdev_get_parent_bus(dev); UfsHc *u =3D UFS(s->parent); - AioContext *ctx =3D NULL; - uint64_t nb_sectors, nb_blocks; + BlockBackend *blk =3D lu->conf.blk; =20 if (!ufs_lu_check_constraints(lu, errp)) { return; } =20 - ctx =3D blk_get_aio_context(lu->qdev.conf.blk); - aio_context_acquire(ctx); - if (!blkconf_blocksizes(&lu->qdev.conf, errp)) { - goto out; + if (!blk) { + error_setg(errp, "drive property not set"); + return; + } + + if (!blkconf_blocksizes(&lu->conf, errp)) { + return; } =20 - lu->qdev.blocksize =3D UFS_BLOCK_SIZE; - blk_get_geometry(lu->qdev.conf.blk, &nb_sectors); - nb_blocks =3D nb_sectors / (lu->qdev.blocksize / BDRV_SECTOR_SIZE); - if (nb_blocks > UINT32_MAX) { - nb_blocks =3D UINT32_MAX; + if (!blkconf_apply_backend_options(&lu->conf, !blk_supports_write_perm= (blk), + true, errp)) { + return; } - lu->qdev.max_lba =3D nb_blocks; - lu->qdev.type =3D TYPE_DISK; =20 ufs_init_lu(lu); if (!ufs_add_lu(u, lu, errp)) { - goto out; + return; } =20 - ufs_lu_brdv_init(lu, errp); - -out: - aio_context_release(ctx); + ufs_init_scsi_device(lu, blk, errp); } =20 -static void ufs_lu_unrealize(SCSIDevice *dev) +static void ufs_lu_unrealize(DeviceState *dev) { UfsLu *lu =3D DO_UPCAST(UfsLu, qdev, dev); =20 - blk_drain(lu->qdev.conf.blk); -} - -static void ufs_wlu_realize(DeviceState *qdev, Error **errp) -{ - UfsWLu *wlu =3D UFSWLU(qdev); - SCSIDevice *dev =3D &wlu->qdev; - - if (!is_wlun(dev->lun)) { - error_setg(errp, "not well-known logical unit number"); - return; + if (lu->scsi_dev) { + object_unref(OBJECT(lu->scsi_dev)); + lu->scsi_dev =3D NULL; } - - QTAILQ_INIT(&dev->requests); } =20 static void ufs_lu_class_init(ObjectClass *oc, void *data) { DeviceClass *dc =3D DEVICE_CLASS(oc); - SCSIDeviceClass *sc =3D SCSI_DEVICE_CLASS(oc); =20 - sc->realize =3D ufs_lu_realize; - sc->unrealize =3D ufs_lu_unrealize; - sc->alloc_req =3D scsi_new_request; + dc->realize =3D ufs_lu_realize; + dc->unrealize =3D ufs_lu_unrealize; dc->bus_type =3D TYPE_UFS_BUS; device_class_set_props(dc, ufs_lu_props); dc->desc =3D "Virtual UFS logical unit"; } =20 -static void ufs_wlu_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc =3D DEVICE_CLASS(oc); - SCSIDeviceClass *sc =3D SCSI_DEVICE_CLASS(oc); - - /* - * The realize() function of TYPE_SCSI_DEVICE causes a segmentation fa= ult - * if a block drive does not exist. Define a new realize function for - * well-known LUs that do not have a block drive. - */ - dc->realize =3D ufs_wlu_realize; - sc->alloc_req =3D scsi_new_request; - dc->bus_type =3D TYPE_UFS_BUS; - dc->desc =3D "Virtual UFS well-known logical unit"; -} - static const TypeInfo ufs_lu_info =3D { .name =3D TYPE_UFS_LU, - .parent =3D TYPE_SCSI_DEVICE, + .parent =3D TYPE_DEVICE, .class_init =3D ufs_lu_class_init, .instance_size =3D sizeof(UfsLu), }; =20 -static const TypeInfo ufs_wlu_info =3D { - .name =3D TYPE_UFS_WLU, - .parent =3D TYPE_SCSI_DEVICE, - .class_init =3D ufs_wlu_class_init, - .instance_size =3D sizeof(UfsWLu), -}; - static void ufs_lu_register_types(void) { type_register_static(&ufs_lu_info); - type_register_static(&ufs_wlu_info); } =20 type_init(ufs_lu_register_types) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events index 1e55fb0d08..665e1a942b 100644 --- a/hw/ufs/trace-events +++ b/hw/ufs/trace-events @@ -12,31 +12,6 @@ ufs_exec_scsi_cmd(uint32_t slot, uint8_t lun, uint8_t op= code) "slot %"PRIu32", l ufs_exec_query_cmd(uint32_t slot, uint8_t opcode) "slot %"PRIu32", opcode = 0x%"PRIx8"" ufs_process_uiccmd(uint32_t uiccmd, uint32_t ucmdarg1, uint32_t ucmdarg2, = uint32_t ucmdarg3) "uiccmd 0x%"PRIx32", ucmdarg1 0x%"PRIx32", ucmdarg2 0x%"= PRIx32", ucmdarg3 0x%"PRIx32"" =20 -# lu.c -ufs_scsi_check_condition(uint32_t tag, uint8_t key, uint8_t asc, uint8_t a= scq) "Command complete tag=3D0x%x sense=3D%d/%d/%d" -ufs_scsi_read_complete(uint32_t tag, size_t size) "Data ready tag=3D0x%x l= en=3D%zd" -ufs_scsi_read_data_count(uint32_t sector_count) "Read sector_count=3D%d" -ufs_scsi_read_data_invalid(void) "Data transfer direction invalid" -ufs_scsi_write_complete_noio(uint32_t tag, size_t size) "Write complete ta= g=3D0x%x more=3D%zd" -ufs_scsi_write_data_invalid(void) "Data transfer direction invalid" -ufs_scsi_emulate_vpd_page_00(size_t xfer) "Inquiry EVPD[Supported pages] b= uffer size %zd" -ufs_scsi_emulate_vpd_page_80_not_supported(void) "Inquiry EVPD[Serial numb= er] not supported" -ufs_scsi_emulate_vpd_page_80(size_t xfer) "Inquiry EVPD[Serial number] buf= fer size %zd" -ufs_scsi_emulate_vpd_page_87(size_t xfer) "Inquiry EVPD[Mode Page Policy] = buffer size %zd" -ufs_scsi_emulate_mode_sense(int cmd, int page, size_t xfer, int control) "= Mode Sense(%d) (page %d, xfer %zd, page_control %d)" -ufs_scsi_emulate_read_data(int buflen) "Read buf_len=3D%d" -ufs_scsi_emulate_write_data(int buflen) "Write buf_len=3D%d" -ufs_scsi_emulate_command_START_STOP(void) "START STOP UNIT" -ufs_scsi_emulate_command_FORMAT_UNIT(void) "FORMAT UNIT" -ufs_scsi_emulate_command_SEND_DIAGNOSTIC(void) "SEND DIAGNOSTIC" -ufs_scsi_emulate_command_SAI_16(void) "SAI READ CAPACITY(16)" -ufs_scsi_emulate_command_SAI_unsupported(void) "Unsupported Service Action= In" -ufs_scsi_emulate_command_MODE_SELECT_10(size_t xfer) "Mode Select(10) (len= %zd)" -ufs_scsi_emulate_command_VERIFY(int bytchk) "Verify (bytchk %d)" -ufs_scsi_emulate_command_UNKNOWN(int cmd, const char *name) "Unknown SCSI = command (0x%2.2x=3D%s)" -ufs_scsi_dma_command_READ(uint64_t lba, uint32_t len) "Read (block %" PRIu= 64 ", count %u)" -ufs_scsi_dma_command_WRITE(uint64_t lba, int len) "Write (block %" PRIu64 = ", count %u)" - # error condition ufs_err_dma_read_utrd(uint32_t slot, uint64_t addr) "failed to read utrd. = UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64"" ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read re= q upiu. UTRLDBR slot %"PRIu32", request upiu addr %"PRIu64"" diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 2e6d582cc3..68c5f1f6c9 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "migration/vmstate.h" +#include "scsi/constants.h" #include "trace.h" #include "ufs.h" =20 @@ -62,8 +63,6 @@ static MemTxResult ufs_addr_write(UfsHc *u, hwaddr addr, = const void *buf, return pci_dma_write(PCI_DEVICE(u), addr, buf, size); } =20 -static void ufs_complete_req(UfsRequest *req, UfsReqResult req_result); - static inline hwaddr ufs_get_utrd_addr(UfsHc *u, uint32_t slot) { hwaddr utrl_base_addr =3D (((hwaddr)u->reg.utrlbau) << 32) + u->reg.ut= rlba; @@ -163,11 +162,13 @@ static MemTxResult ufs_dma_read_prdt(UfsRequest *req) =20 req->sg =3D g_malloc0(sizeof(QEMUSGList)); pci_dma_sglist_init(req->sg, PCI_DEVICE(u), prdt_len); + req->data_len =3D 0; =20 for (uint16_t i =3D 0; i < prdt_len; ++i) { hwaddr data_dma_addr =3D le64_to_cpu(prd_entries[i].addr); uint32_t data_byte_count =3D le32_to_cpu(prd_entries[i].size) + 1; qemu_sglist_add(req->sg, data_dma_addr, data_byte_count); + req->data_len +=3D data_byte_count; } return MEMTX_OK; } @@ -433,23 +434,10 @@ static const MemoryRegionOps ufs_mmio_ops =3D { }, }; =20 -static QEMUSGList *ufs_get_sg_list(SCSIRequest *scsi_req) -{ - UfsRequest *req =3D scsi_req->hba_private; - return req->sg; -} - -static void ufs_build_upiu_sense_data(UfsRequest *req, SCSIRequest *scsi_r= eq) -{ - req->rsp_upiu.sr.sense_data_len =3D cpu_to_be16(scsi_req->sense_len); - assert(scsi_req->sense_len <=3D SCSI_SENSE_LEN); - memcpy(req->rsp_upiu.sr.sense_data, scsi_req->sense, scsi_req->sense_l= en); -} =20 -static void ufs_build_upiu_header(UfsRequest *req, uint8_t trans_type, - uint8_t flags, uint8_t response, - uint8_t scsi_status, - uint16_t data_segment_length) +void ufs_build_upiu_header(UfsRequest *req, uint8_t trans_type, uint8_t fl= ags, + uint8_t response, uint8_t scsi_status, + uint16_t data_segment_length) { memcpy(&req->rsp_upiu.header, &req->req_upiu.header, sizeof(UtpUpiuHea= der)); req->rsp_upiu.header.trans_type =3D trans_type; @@ -459,96 +447,38 @@ static void ufs_build_upiu_header(UfsRequest *req, ui= nt8_t trans_type, req->rsp_upiu.header.data_segment_length =3D cpu_to_be16(data_segment_= length); } =20 -static void ufs_scsi_command_complete(SCSIRequest *scsi_req, size_t resid) -{ - UfsRequest *req =3D scsi_req->hba_private; - int16_t status =3D scsi_req->status; - uint32_t expected_len =3D be32_to_cpu(req->req_upiu.sc.exp_data_transf= er_len); - uint32_t transfered_len =3D scsi_req->cmd.xfer - resid; - uint8_t flags =3D 0, response =3D UFS_COMMAND_RESULT_SUCESS; - uint16_t data_segment_length; - - if (expected_len > transfered_len) { - req->rsp_upiu.sr.residual_transfer_count =3D - cpu_to_be32(expected_len - transfered_len); - flags |=3D UFS_UPIU_FLAG_UNDERFLOW; - } else if (expected_len < transfered_len) { - req->rsp_upiu.sr.residual_transfer_count =3D - cpu_to_be32(transfered_len - expected_len); - flags |=3D UFS_UPIU_FLAG_OVERFLOW; - } - - if (status !=3D 0) { - ufs_build_upiu_sense_data(req, scsi_req); - response =3D UFS_COMMAND_RESULT_FAIL; - } - - data_segment_length =3D cpu_to_be16(scsi_req->sense_len + - sizeof(req->rsp_upiu.sr.sense_data_l= en)); - ufs_build_upiu_header(req, UFS_UPIU_TRANSACTION_RESPONSE, flags, respo= nse, - status, data_segment_length); - - ufs_complete_req(req, UFS_REQUEST_SUCCESS); - - scsi_req->hba_private =3D NULL; - scsi_req_unref(scsi_req); -} - -static const struct SCSIBusInfo ufs_scsi_info =3D { - .tcq =3D true, - .max_target =3D 0, - .max_lun =3D UFS_MAX_LUS, - .max_channel =3D 0, - - .get_sg_list =3D ufs_get_sg_list, - .complete =3D ufs_scsi_command_complete, -}; - static UfsReqResult ufs_exec_scsi_cmd(UfsRequest *req) { UfsHc *u =3D req->hc; uint8_t lun =3D req->req_upiu.header.lun; - uint8_t task_tag =3D req->req_upiu.header.task_tag; - SCSIDevice *dev =3D NULL; + + UfsLu *lu =3D NULL; =20 trace_ufs_exec_scsi_cmd(req->slot, lun, req->req_upiu.sc.cdb[0]); =20 - if (!is_wlun(lun)) { - if (lun >=3D u->device_desc.number_lu) { - trace_ufs_err_scsi_cmd_invalid_lun(lun); - return UFS_REQUEST_FAIL; - } else if (u->lus[lun] =3D=3D NULL) { - trace_ufs_err_scsi_cmd_invalid_lun(lun); - return UFS_REQUEST_FAIL; - } + if (!is_wlun(lun) && (lun >=3D UFS_MAX_LUS || u->lus[lun] =3D=3D NULL)= ) { + trace_ufs_err_scsi_cmd_invalid_lun(lun); + return UFS_REQUEST_FAIL; } =20 switch (lun) { case UFS_UPIU_REPORT_LUNS_WLUN: - dev =3D &u->report_wlu->qdev; + lu =3D &u->report_wlu; break; case UFS_UPIU_UFS_DEVICE_WLUN: - dev =3D &u->dev_wlu->qdev; + lu =3D &u->dev_wlu; break; case UFS_UPIU_BOOT_WLUN: - dev =3D &u->boot_wlu->qdev; + lu =3D &u->boot_wlu; break; case UFS_UPIU_RPMB_WLUN: - dev =3D &u->rpmb_wlu->qdev; + lu =3D &u->rpmb_wlu; break; default: - dev =3D &u->lus[lun]->qdev; - } - - SCSIRequest *scsi_req =3D scsi_req_new( - dev, task_tag, lun, req->req_upiu.sc.cdb, UFS_CDB_SIZE, req); - - uint32_t len =3D scsi_req_enqueue(scsi_req); - if (len) { - scsi_req_continue(scsi_req); + lu =3D u->lus[lun]; } =20 - return UFS_REQUEST_NO_COMPLETE; + return lu->scsi_op(lu, req); } =20 static UfsReqResult ufs_exec_nop_cmd(UfsRequest *req) @@ -1137,7 +1067,7 @@ static void ufs_process_req(void *opaque) } } =20 -static void ufs_complete_req(UfsRequest *req, UfsReqResult req_result) +void ufs_complete_req(UfsRequest *req, UfsReqResult req_result) { UfsHc *u =3D req->hc; assert(req->state =3D=3D UFS_REQUEST_RUNNING); @@ -1159,6 +1089,7 @@ static void ufs_clear_req(UfsRequest *req) qemu_sglist_destroy(req->sg); g_free(req->sg); req->sg =3D NULL; + req->data_len =3D 0; } =20 memset(&req->utrd, 0, sizeof(req->utrd)); @@ -1317,28 +1248,6 @@ static void ufs_init_hc(UfsHc *u) u->flags.permanently_disable_fw_update =3D 1; } =20 -static bool ufs_init_wlu(UfsHc *u, UfsWLu **wlu, uint8_t wlun, Error **err= p) -{ - UfsWLu *new_wlu =3D UFSWLU(qdev_new(TYPE_UFS_WLU)); - - qdev_prop_set_uint32(DEVICE(new_wlu), "lun", wlun); - - /* - * The well-known lu shares the same bus as the normal lu. If the well= -known - * lu writes the same channel value as the normal lu, the report will = be - * made not only for the normal lu but also for the well-known lu at - * REPORT_LUN time. To prevent this, the channel value of normal lu is= fixed - * to 0 and the channel value of well-known lu is fixed to 1. - */ - qdev_prop_set_uint32(DEVICE(new_wlu), "channel", 1); - if (!qdev_realize_and_unref(DEVICE(new_wlu), BUS(&u->bus), errp)) { - return false; - } - - *wlu =3D new_wlu; - return true; -} - static void ufs_realize(PCIDevice *pci_dev, Error **errp) { UfsHc *u =3D UFS(pci_dev); @@ -1349,53 +1258,21 @@ static void ufs_realize(PCIDevice *pci_dev, Error *= *errp) =20 qbus_init(&u->bus, sizeof(UfsBus), TYPE_UFS_BUS, &pci_dev->qdev, u->parent_obj.qdev.id); - u->bus.parent_bus.info =3D &ufs_scsi_info; =20 ufs_init_state(u); ufs_init_hc(u); ufs_init_pci(u, pci_dev); =20 - if (!ufs_init_wlu(u, &u->report_wlu, UFS_UPIU_REPORT_LUNS_WLUN, errp))= { - return; - } - - if (!ufs_init_wlu(u, &u->dev_wlu, UFS_UPIU_UFS_DEVICE_WLUN, errp)) { - return; - } - - if (!ufs_init_wlu(u, &u->boot_wlu, UFS_UPIU_BOOT_WLUN, errp)) { - return; - } - - if (!ufs_init_wlu(u, &u->rpmb_wlu, UFS_UPIU_RPMB_WLUN, errp)) { - return; - } + ufs_init_wlu(&u->report_wlu, UFS_UPIU_REPORT_LUNS_WLUN); + ufs_init_wlu(&u->dev_wlu, UFS_UPIU_UFS_DEVICE_WLUN); + ufs_init_wlu(&u->boot_wlu, UFS_UPIU_BOOT_WLUN); + ufs_init_wlu(&u->rpmb_wlu, UFS_UPIU_RPMB_WLUN); } =20 static void ufs_exit(PCIDevice *pci_dev) { UfsHc *u =3D UFS(pci_dev); =20 - if (u->dev_wlu) { - object_unref(OBJECT(u->dev_wlu)); - u->dev_wlu =3D NULL; - } - - if (u->report_wlu) { - object_unref(OBJECT(u->report_wlu)); - u->report_wlu =3D NULL; - } - - if (u->rpmb_wlu) { - object_unref(OBJECT(u->rpmb_wlu)); - u->rpmb_wlu =3D NULL; - } - - if (u->boot_wlu) { - object_unref(OBJECT(u->boot_wlu)); - u->boot_wlu =3D NULL; - } - qemu_bh_delete(u->doorbell_bh); qemu_bh_delete(u->complete_bh); =20 @@ -1437,43 +1314,18 @@ static void ufs_class_init(ObjectClass *oc, void *d= ata) static bool ufs_bus_check_address(BusState *qbus, DeviceState *qdev, Error **errp) { - SCSIDevice *dev =3D SCSI_DEVICE(qdev); - UfsBusClass *ubc =3D UFS_BUS_GET_CLASS(qbus); - UfsHc *u =3D UFS(qbus->parent); - - if (strcmp(object_get_typename(OBJECT(dev)), TYPE_UFS_WLU) =3D=3D 0) { - if (dev->lun !=3D UFS_UPIU_REPORT_LUNS_WLUN && - dev->lun !=3D UFS_UPIU_UFS_DEVICE_WLUN && - dev->lun !=3D UFS_UPIU_BOOT_WLUN && dev->lun !=3D UFS_UPIU_RPM= B_WLUN) { - error_setg(errp, "bad well-known lun: %d", dev->lun); - return false; - } - - if ((dev->lun =3D=3D UFS_UPIU_REPORT_LUNS_WLUN && u->report_wlu != =3D NULL) || - (dev->lun =3D=3D UFS_UPIU_UFS_DEVICE_WLUN && u->dev_wlu !=3D N= ULL) || - (dev->lun =3D=3D UFS_UPIU_BOOT_WLUN && u->boot_wlu !=3D NULL) = || - (dev->lun =3D=3D UFS_UPIU_RPMB_WLUN && u->rpmb_wlu !=3D NULL))= { - error_setg(errp, "well-known lun %d already exists", dev->lun); - return false; - } - - return true; - } - - if (strcmp(object_get_typename(OBJECT(dev)), TYPE_UFS_LU) !=3D 0) { + if (strcmp(object_get_typename(OBJECT(qdev)), TYPE_UFS_LU) !=3D 0) { error_setg(errp, "%s cannot be connected to ufs-bus", - object_get_typename(OBJECT(dev))); + object_get_typename(OBJECT(qdev))); return false; } =20 - return ubc->parent_check_address(qbus, qdev, errp); + return true; } =20 static void ufs_bus_class_init(ObjectClass *class, void *data) { BusClass *bc =3D BUS_CLASS(class); - UfsBusClass *ubc =3D UFS_BUS_CLASS(class); - ubc->parent_check_address =3D bc->check_address; bc->check_address =3D ufs_bus_check_address; } =20 @@ -1487,7 +1339,7 @@ static const TypeInfo ufs_info =3D { =20 static const TypeInfo ufs_bus_info =3D { .name =3D TYPE_UFS_BUS, - .parent =3D TYPE_SCSI_BUS, + .parent =3D TYPE_BUS, .class_init =3D ufs_bus_class_init, .class_size =3D sizeof(UfsBusClass), .instance_size =3D sizeof(UfsBus), diff --git a/hw/ufs/ufs.h b/hw/ufs/ufs.h index f244228617..8fda94f4ef 100644 --- a/hw/ufs/ufs.h +++ b/hw/ufs/ufs.h @@ -16,7 +16,8 @@ #include "block/ufs.h" =20 #define UFS_MAX_LUS 32 -#define UFS_BLOCK_SIZE 4096 +#define UFS_BLOCK_SIZE_SHIFT 12 +#define UFS_BLOCK_SIZE (1 << UFS_BLOCK_SIZE_SHIFT) =20 typedef struct UfsBusClass { BusClass parent_class; @@ -24,7 +25,7 @@ typedef struct UfsBusClass { } UfsBusClass; =20 typedef struct UfsBus { - SCSIBus parent_bus; + BusState parent_bus; } UfsBus; =20 #define TYPE_UFS_BUS "ufs-bus" @@ -55,19 +56,22 @@ typedef struct UfsRequest { =20 /* for scsi command */ QEMUSGList *sg; + uint32_t data_len; } UfsRequest; =20 +struct UfsLu; +typedef UfsReqResult (*UfsScsiOp)(struct UfsLu *, UfsRequest *); + typedef struct UfsLu { - SCSIDevice qdev; + DeviceState qdev; uint8_t lun; UnitDescriptor unit_desc; + SCSIBus bus; + SCSIDevice *scsi_dev; + BlockConf conf; + UfsScsiOp scsi_op; } UfsLu; =20 -typedef struct UfsWLu { - SCSIDevice qdev; - uint8_t lun; -} UfsWLu; - typedef struct UfsParams { char *serial; uint8_t nutrs; /* Number of UTP Transfer Request Slots */ @@ -84,10 +88,10 @@ typedef struct UfsHc { UfsRequest *req_list; =20 UfsLu *lus[UFS_MAX_LUS]; - UfsWLu *report_wlu; - UfsWLu *dev_wlu; - UfsWLu *boot_wlu; - UfsWLu *rpmb_wlu; + UfsLu report_wlu; + UfsLu dev_wlu; + UfsLu boot_wlu; + UfsLu rpmb_wlu; DeviceDescriptor device_desc; GeometryDescriptor geometry_desc; Attributes attributes; @@ -104,9 +108,6 @@ typedef struct UfsHc { #define TYPE_UFS_LU "ufs-lu" #define UFSLU(obj) OBJECT_CHECK(UfsLu, (obj), TYPE_UFS_LU) =20 -#define TYPE_UFS_WLU "ufs-wlu" -#define UFSWLU(obj) OBJECT_CHECK(UfsWLu, (obj), TYPE_UFS_WLU) - typedef enum UfsQueryFlagPerm { UFS_QUERY_FLAG_NONE =3D 0x0, UFS_QUERY_FLAG_READ =3D 0x1, @@ -128,4 +129,9 @@ static inline bool is_wlun(uint8_t lun) lun =3D=3D UFS_UPIU_RPMB_WLUN); } =20 +void ufs_build_upiu_header(UfsRequest *req, uint8_t trans_type, uint8_t fl= ags, + uint8_t response, uint8_t scsi_status, + uint16_t data_segment_length); +void ufs_complete_req(UfsRequest *req, UfsReqResult req_result); +void ufs_init_wlu(UfsLu *wlu, uint8_t wlun); #endif /* HW_UFS_UFS_H */ diff --git a/include/block/ufs.h b/include/block/ufs.h index 7631a5af10..0b6ec0814d 100644 --- a/include/block/ufs.h +++ b/include/block/ufs.h @@ -379,7 +379,7 @@ typedef struct Attributes { =20 /* Command response result code */ typedef enum CommandRespCode { - UFS_COMMAND_RESULT_SUCESS =3D 0x00, + UFS_COMMAND_RESULT_SUCCESS =3D 0x00, UFS_COMMAND_RESULT_FAIL =3D 0x01, } CommandRespCode; =20 diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c index 15d467630c..5daf8c9c49 100644 --- a/tests/qtest/ufs-test.c +++ b/tests/qtest/ufs-test.c @@ -425,6 +425,9 @@ static void ufstest_init(void *obj, void *data, QGuestA= llocator *alloc) const uint8_t test_unit_ready_cdb[UFS_CDB_SIZE] =3D { TEST_UNIT_READY, }; + const uint8_t request_sense_cdb[UFS_CDB_SIZE] =3D { + REQUEST_SENSE, + }; UtpTransferReqDesc utrd; UtpUpiuRsp rsp_upiu; =20 @@ -440,6 +443,12 @@ static void ufstest_init(void *obj, void *data, QGuest= Allocator *alloc) /* There is one logical unit whose lun is 0 */ g_assert_cmpuint(buf[9], =3D=3D, 0); =20 + /* Clear Unit Attention */ + ufs_send_scsi_command(ufs, 0, 0, request_sense_cdb, NULL, 0, buf, + sizeof(buf), &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), =3D=3D, UFS_OCS_SUC= CESS); + g_assert_cmpuint(rsp_upiu.header.scsi_status, =3D=3D, CHECK_CONDITION); + /* Check TEST_UNIT_READY */ ufs_send_scsi_command(ufs, 0, 0, test_unit_ready_cdb, NULL, 0, NULL, 0, &utrd, &rsp_upiu); @@ -473,6 +482,9 @@ static void ufstest_read_write(void *obj, void *data, Q= GuestAllocator *alloc) 0x00, 0x00 }; + const uint8_t request_sense_cdb[UFS_CDB_SIZE] =3D { + REQUEST_SENSE, + }; const uint8_t read_cdb[UFS_CDB_SIZE] =3D { /* READ(10) to LBA 0, transfer length 1 */ READ_10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 @@ -484,32 +496,39 @@ static void ufstest_read_write(void *obj, void *data,= QGuestAllocator *alloc) uint32_t block_size; UtpTransferReqDesc utrd; UtpUpiuRsp rsp_upiu; + const int test_lun =3D 1; =20 ufs_init(ufs, alloc); =20 + /* Clear Unit Attention */ + ufs_send_scsi_command(ufs, 0, test_lun, request_sense_cdb, NULL, 0, + read_buf, sizeof(read_buf), &utrd, &rsp_upiu); + g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), =3D=3D, UFS_OCS_SUC= CESS); + g_assert_cmpuint(rsp_upiu.header.scsi_status, =3D=3D, CHECK_CONDITION); + /* Read capacity */ - ufs_send_scsi_command(ufs, 0, 1, read_capacity_cdb, NULL, 0, read_buf, - sizeof(read_buf), &utrd, &rsp_upiu); + ufs_send_scsi_command(ufs, 0, test_lun, read_capacity_cdb, NULL, 0, + read_buf, sizeof(read_buf), &utrd, &rsp_upiu); g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), =3D=3D, UFS_OCS_SUC= CESS); g_assert_cmpuint(rsp_upiu.header.scsi_status, =3D=3D, - UFS_COMMAND_RESULT_SUCESS); + UFS_COMMAND_RESULT_SUCCESS); block_size =3D ldl_be_p(&read_buf[8]); g_assert_cmpuint(block_size, =3D=3D, 4096); =20 /* Write data */ memset(write_buf, 0xab, block_size); - ufs_send_scsi_command(ufs, 0, 1, write_cdb, write_buf, block_size, NUL= L, 0, - &utrd, &rsp_upiu); + ufs_send_scsi_command(ufs, 0, test_lun, write_cdb, write_buf, block_si= ze, + NULL, 0, &utrd, &rsp_upiu); g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), =3D=3D, UFS_OCS_SUC= CESS); g_assert_cmpuint(rsp_upiu.header.scsi_status, =3D=3D, - UFS_COMMAND_RESULT_SUCESS); + UFS_COMMAND_RESULT_SUCCESS); =20 /* Read data and verify */ - ufs_send_scsi_command(ufs, 0, 1, read_cdb, NULL, 0, read_buf, block_si= ze, - &utrd, &rsp_upiu); + ufs_send_scsi_command(ufs, 0, test_lun, read_cdb, NULL, 0, read_buf, + block_size, &utrd, &rsp_upiu); g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), =3D=3D, UFS_OCS_SUC= CESS); g_assert_cmpuint(rsp_upiu.header.scsi_status, =3D=3D, - UFS_COMMAND_RESULT_SUCESS); + UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpint(memcmp(read_buf, write_buf, block_size), =3D=3D, 0); =20 ufs_exit(ufs, alloc); --=20 2.34.1