1
The following changes since commit 418fa86dd465b4fd8394373cf83db8fa65d7611c:
1
The following changes since commit 813bac3d8d70d85cb7835f7945eb9eed84c2d8d0:
2
2
3
Merge remote-tracking branch 'remotes/stsquad/tags/pull-testing-040220-1' into staging (2020-02-04 18:55:06 +0000)
3
Merge tag '2023q3-bsd-user-pull-request' of https://gitlab.com/bsdimp/qemu into staging (2023-08-29 08:58:00 -0400)
4
4
5
are available in the Git repository at:
5
are available in the Git repository at:
6
6
7
https://github.com/XanClic/qemu.git tags/pull-block-2020-02-06
7
https://gitlab.com/stefanha/qemu.git tags/block-pull-request
8
8
9
for you to fetch changes up to a541fcc27c98b96da187c7d4573f3270f3ddd283:
9
for you to fetch changes up to 3f5f2285bfcdd855508a55da7875fb92de1a6ed0:
10
10
11
iotests: add test for backup-top failure on permission activation (2020-02-06 13:47:45 +0100)
11
tests/qemu-iotests/197: add testcase for CoR with subclusters (2023-08-29 13:19:56 -0400)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block patches:
14
Pull request
15
- Drop BDRV_SECTOR_SIZE from qcow2
15
16
- Allow Python iotests to be added to the auto group
16
v2:
17
(and add some)
17
- Fix authorship information lost by the mailing list for Andrey Drobyshev
18
- Fix for the backup job
19
- Fix memleak in bdrv_refresh_filename()
20
- Use GStrings in two places for greater efficiency (than manually
21
handling string allocation)
22
18
23
----------------------------------------------------------------
19
----------------------------------------------------------------
24
Alberto Garcia (8):
25
qcow2: Assert that host cluster offsets fit in L2 table entries
26
block: Use a GString in bdrv_perm_names()
27
qcow2: Use a GString in report_unsupported_feature()
28
qcow2: Don't round the L1 table allocation up to the sector size
29
qcow2: Tighten cluster_offset alignment assertions
30
qcow2: Use bs->bl.request_alignment when updating an L1 entry
31
qcow2: Don't require aligned offsets in qcow2_co_copy_range_from()
32
qcow2: Use BDRV_SECTOR_SIZE instead of the hardcoded value
33
20
34
John Snow (1):
21
Andrey Drobyshev (3):
35
iotests: remove 'linux' from default supported platforms
22
block: add subcluster_size field to BlockDriverInfo
23
block/io: align requests to subcluster_size
24
tests/qemu-iotests/197: add testcase for CoR with subclusters
36
25
37
Pan Nengyuan (1):
26
Fabiano Rosas (1):
38
block: fix memleaks in bdrv_refresh_filename
27
block-migration: Ensure we don't crash during migration cleanup
39
28
40
Thomas Huth (5):
29
Jeuk Kim (4):
41
iotests: Test 041 only works on certain systems
30
hw/ufs: Initial commit for emulated Universal-Flash-Storage
42
iotests: Test 183 does not work on macOS and OpenBSD
31
hw/ufs: Support for Query Transfer Requests
43
iotests: Check for the availability of the required devices in 267 and
32
hw/ufs: Support for UFS logical unit
44
127
33
tests/qtest: Introduce tests for UFS
45
iotests: Skip Python-based tests if QEMU does not support virtio-blk
46
iotests: Enable more tests in the 'auto' group to improve test
47
coverage
48
34
49
Vladimir Sementsov-Ogievskiy (2):
35
MAINTAINERS | 7 +
50
block/backup-top: fix failure path
36
docs/specs/pci-ids.rst | 2 +
51
iotests: add test for backup-top failure on permission activation
37
meson.build | 1 +
52
38
hw/ufs/trace.h | 1 +
53
block.c | 12 +++--
39
hw/ufs/ufs.h | 131 +++
54
block/backup-top.c | 21 ++++----
40
include/block/block-common.h | 5 +
55
block/qcow2-cluster.c | 44 +++++++++++------
41
include/block/block-io.h | 8 +-
56
block/qcow2-refcount.c | 2 +-
42
include/block/ufs.h | 1090 +++++++++++++++++++++++++
57
block/qcow2-snapshot.c | 3 +-
43
include/hw/pci/pci.h | 1 +
58
block/qcow2.c | 46 ++++++++----------
44
include/hw/pci/pci_ids.h | 1 +
59
tests/qemu-iotests/041 | 3 +-
45
include/scsi/constants.h | 1 +
60
tests/qemu-iotests/127 | 2 +
46
block.c | 7 +
61
tests/qemu-iotests/183 | 1 +
47
block/io.c | 50 +-
62
tests/qemu-iotests/267 | 2 +
48
block/mirror.c | 8 +-
63
tests/qemu-iotests/283 | 92 +++++++++++++++++++++++++++++++++++
49
block/qcow2.c | 1 +
64
tests/qemu-iotests/283.out | 8 +++
50
hw/ufs/lu.c | 1445 ++++++++++++++++++++++++++++++++
65
tests/qemu-iotests/check | 12 ++++-
51
hw/ufs/ufs.c | 1494 ++++++++++++++++++++++++++++++++++
66
tests/qemu-iotests/common.rc | 14 ++++++
52
migration/block.c | 11 +-
67
tests/qemu-iotests/group | 15 +++---
53
tests/qtest/ufs-test.c | 584 +++++++++++++
68
tests/qemu-iotests/iotests.py | 16 ++++--
54
hw/Kconfig | 1 +
69
16 files changed, 220 insertions(+), 73 deletions(-)
55
hw/meson.build | 1 +
70
create mode 100644 tests/qemu-iotests/283
56
hw/ufs/Kconfig | 4 +
71
create mode 100644 tests/qemu-iotests/283.out
57
hw/ufs/meson.build | 1 +
58
hw/ufs/trace-events | 58 ++
59
tests/qemu-iotests/197 | 29 +
60
tests/qemu-iotests/197.out | 24 +
61
tests/qtest/meson.build | 1 +
62
27 files changed, 4932 insertions(+), 35 deletions(-)
63
create mode 100644 hw/ufs/trace.h
64
create mode 100644 hw/ufs/ufs.h
65
create mode 100644 include/block/ufs.h
66
create mode 100644 hw/ufs/lu.c
67
create mode 100644 hw/ufs/ufs.c
68
create mode 100644 tests/qtest/ufs-test.c
69
create mode 100644 hw/ufs/Kconfig
70
create mode 100644 hw/ufs/meson.build
71
create mode 100644 hw/ufs/trace-events
72
72
73
--
73
--
74
2.24.1
74
2.41.0
75
76
diff view generated by jsdifflib
Deleted patch
1
From: Alberto Garcia <berto@igalia.com>
2
1
3
The standard cluster descriptor in L2 table entries has a field to
4
store the host cluster offset. When we need to get that offset from an
5
entry we use L2E_OFFSET_MASK to ensure that we only use the bits that
6
belong to that field.
7
8
But while that mask is used every time we read from an L2 entry, it
9
is never used when we write to it. Due to the QCOW_MAX_CLUSTER_OFFSET
10
limit set in the cluster allocation code QEMU can never produce
11
offsets that don't fit in that field so any such offset would indicate
12
a bug in QEMU.
13
14
Compressed cluster descriptors contain two fields (host cluster offset
15
and size of the compressed data) and the situation with them is
16
similar. In this case the masks are not constant but are stored in the
17
csize_mask and cluster_offset_mask fields of BDRVQcow2State.
18
19
Signed-off-by: Alberto Garcia <berto@igalia.com>
20
Reviewed-by: Eric Blake <eblake@redhat.com>
21
Message-id: 20200113161146.20099-1-berto@igalia.com
22
Signed-off-by: Max Reitz <mreitz@redhat.com>
23
---
24
block/qcow2-cluster.c | 14 ++++++++++++--
25
1 file changed, 12 insertions(+), 2 deletions(-)
26
27
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
28
index XXXXXXX..XXXXXXX 100644
29
--- a/block/qcow2-cluster.c
30
+++ b/block/qcow2-cluster.c
31
@@ -XXX,XX +XXX,XX @@ int qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
32
(cluster_offset + compressed_size - 1) / QCOW2_COMPRESSED_SECTOR_SIZE -
33
(cluster_offset / QCOW2_COMPRESSED_SECTOR_SIZE);
34
35
+ /* The offset and size must fit in their fields of the L2 table entry */
36
+ assert((cluster_offset & s->cluster_offset_mask) == cluster_offset);
37
+ assert((nb_csectors & s->csize_mask) == nb_csectors);
38
+
39
cluster_offset |= QCOW_OFLAG_COMPRESSED |
40
((uint64_t)nb_csectors << s->csize_shift);
41
42
@@ -XXX,XX +XXX,XX @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
43
44
assert(l2_index + m->nb_clusters <= s->l2_slice_size);
45
for (i = 0; i < m->nb_clusters; i++) {
46
+ uint64_t offset = cluster_offset + (i << s->cluster_bits);
47
/* if two concurrent writes happen to the same unallocated cluster
48
* each write allocates separate cluster and writes data concurrently.
49
* The first one to complete updates l2 table with pointer to its
50
@@ -XXX,XX +XXX,XX @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
51
old_cluster[j++] = l2_slice[l2_index + i];
52
}
53
54
- l2_slice[l2_index + i] = cpu_to_be64((cluster_offset +
55
- (i << s->cluster_bits)) | QCOW_OFLAG_COPIED);
56
+ /* The offset must fit in the offset field of the L2 table entry */
57
+ assert((offset & L2E_OFFSET_MASK) == offset);
58
+
59
+ l2_slice[l2_index + i] = cpu_to_be64(offset | QCOW_OFLAG_COPIED);
60
}
61
62
63
@@ -XXX,XX +XXX,XX @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
64
goto fail;
65
}
66
67
+ /* The offset must fit in the offset field */
68
+ assert((offset & L2E_OFFSET_MASK) == offset);
69
+
70
if (l2_refcount > 1) {
71
/* For shared L2 tables, set the refcount accordingly
72
* (it is already 1 and needs to be l2_refcount) */
73
--
74
2.24.1
75
76
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Jeuk Kim <jeuk20.kim@gmail.com>
2
2
3
This test checks that bug is really fixed by previous commit.
3
Universal Flash Storage (UFS) is a high-performance mass storage device
4
with a serial interface. It is primarily used as a high-performance
5
data storage device for embedded applications.
4
6
5
Cc: qemu-stable@nongnu.org # v4.2.0
7
This commit contains code for UFS device to be recognized
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
as a UFS PCI device.
7
Message-id: 20200121142802.21467-3-vsementsov@virtuozzo.com
9
Patches to handle UFS logical unit and Transfer Request will follow.
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
11
Signed-off-by: Jeuk Kim <jeuk20.kim@samsung.com>
12
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
13
Message-id: 9f3db32fe1c708090a6bb764d456973b5abef55f.1691062912.git.jeuk20.kim@samsung.com
14
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
9
---
15
---
10
tests/qemu-iotests/283 | 92 ++++++++++++++++++++++++++++++++++++++
16
MAINTAINERS | 6 +
11
tests/qemu-iotests/283.out | 8 ++++
17
docs/specs/pci-ids.rst | 2 +
12
tests/qemu-iotests/group | 1 +
18
meson.build | 1 +
13
3 files changed, 101 insertions(+)
19
hw/ufs/trace.h | 1 +
14
create mode 100644 tests/qemu-iotests/283
20
hw/ufs/ufs.h | 42 ++
15
create mode 100644 tests/qemu-iotests/283.out
21
include/block/ufs.h | 1090 ++++++++++++++++++++++++++++++++++++++
22
include/hw/pci/pci.h | 1 +
23
include/hw/pci/pci_ids.h | 1 +
24
hw/ufs/ufs.c | 278 ++++++++++
25
hw/Kconfig | 1 +
26
hw/meson.build | 1 +
27
hw/ufs/Kconfig | 4 +
28
hw/ufs/meson.build | 1 +
29
hw/ufs/trace-events | 32 ++
30
14 files changed, 1461 insertions(+)
31
create mode 100644 hw/ufs/trace.h
32
create mode 100644 hw/ufs/ufs.h
33
create mode 100644 include/block/ufs.h
34
create mode 100644 hw/ufs/ufs.c
35
create mode 100644 hw/ufs/Kconfig
36
create mode 100644 hw/ufs/meson.build
37
create mode 100644 hw/ufs/trace-events
16
38
17
diff --git a/tests/qemu-iotests/283 b/tests/qemu-iotests/283
39
diff --git a/MAINTAINERS b/MAINTAINERS
40
index XXXXXXX..XXXXXXX 100644
41
--- a/MAINTAINERS
42
+++ b/MAINTAINERS
43
@@ -XXX,XX +XXX,XX @@ F: tests/qtest/nvme-test.c
44
F: docs/system/devices/nvme.rst
45
T: git git://git.infradead.org/qemu-nvme.git nvme-next
46
47
+ufs
48
+M: Jeuk Kim <jeuk20.kim@samsung.com>
49
+S: Supported
50
+F: hw/ufs/*
51
+F: include/block/ufs.h
52
+
53
megasas
54
M: Hannes Reinecke <hare@suse.com>
55
L: qemu-block@nongnu.org
56
diff --git a/docs/specs/pci-ids.rst b/docs/specs/pci-ids.rst
57
index XXXXXXX..XXXXXXX 100644
58
--- a/docs/specs/pci-ids.rst
59
+++ b/docs/specs/pci-ids.rst
60
@@ -XXX,XX +XXX,XX @@ PCI devices (other than virtio):
61
PCI PVPanic device (``-device pvpanic-pci``)
62
1b36:0012
63
PCI ACPI ERST device (``-device acpi-erst``)
64
+1b36:0013
65
+ PCI UFS device (``-device ufs``)
66
67
All these devices are documented in :doc:`index`.
68
69
diff --git a/meson.build b/meson.build
70
index XXXXXXX..XXXXXXX 100644
71
--- a/meson.build
72
+++ b/meson.build
73
@@ -XXX,XX +XXX,XX @@ if have_system
74
'hw/ssi',
75
'hw/timer',
76
'hw/tpm',
77
+ 'hw/ufs',
78
'hw/usb',
79
'hw/vfio',
80
'hw/virtio',
81
diff --git a/hw/ufs/trace.h b/hw/ufs/trace.h
18
new file mode 100644
82
new file mode 100644
19
index XXXXXXX..XXXXXXX
83
index XXXXXXX..XXXXXXX
20
--- /dev/null
84
--- /dev/null
21
+++ b/tests/qemu-iotests/283
85
+++ b/hw/ufs/trace.h
22
@@ -XXX,XX +XXX,XX @@
86
@@ -0,0 +1 @@
23
+#!/usr/bin/env python
87
+#include "trace/trace-hw_ufs.h"
24
+#
88
diff --git a/hw/ufs/ufs.h b/hw/ufs/ufs.h
25
+# Test for backup-top filter permission activation failure
26
+#
27
+# Copyright (c) 2019 Virtuozzo International GmbH.
28
+#
29
+# This program is free software; you can redistribute it and/or modify
30
+# it under the terms of the GNU General Public License as published by
31
+# the Free Software Foundation; either version 2 of the License, or
32
+# (at your option) any later version.
33
+#
34
+# This program is distributed in the hope that it will be useful,
35
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
36
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37
+# GNU General Public License for more details.
38
+#
39
+# You should have received a copy of the GNU General Public License
40
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
41
+#
42
+
43
+import iotests
44
+
45
+# The test is unrelated to formats, restrict it to qcow2 to avoid extra runs
46
+iotests.verify_image_format(supported_fmts=['qcow2'])
47
+
48
+size = 1024 * 1024
49
+
50
+""" Test description
51
+
52
+When performing a backup, all writes on the source subtree must go through the
53
+backup-top filter so it can copy all data to the target before it is changed.
54
+backup-top filter is appended above source node, to achieve this thing, so all
55
+parents of source node are handled. A configuration with side parents of source
56
+sub-tree with write permission is unsupported (we'd have append several
57
+backup-top filter like nodes to handle such parents). The test create an
58
+example of such configuration and checks that a backup is then not allowed
59
+(blockdev-backup command should fail).
60
+
61
+The configuration:
62
+
63
+ ┌────────┐ target ┌─────────────┐
64
+ │ target │ ◀─────── │ backup_top │
65
+ └────────┘ └─────────────┘
66
+ │
67
+ │ backing
68
+ ▼
69
+ ┌─────────────┐
70
+ │ source │
71
+ └─────────────┘
72
+ │
73
+ │ file
74
+ ▼
75
+ ┌─────────────┐ write perm ┌───────┐
76
+ │ base │ ◀──────────── │ other │
77
+ └─────────────┘ └───────┘
78
+
79
+On activation (see .active field of backup-top state in block/backup-top.c),
80
+backup-top is going to unshare write permission on its source child. Write
81
+unsharing will be propagated to the "source->base" link and will conflict with
82
+other node write permission. So permission update will fail and backup job will
83
+not be started.
84
+
85
+Note, that the only thing which prevents backup of running on such
86
+configuration is default permission propagation scheme. It may be altered by
87
+different block drivers, so backup will run in invalid configuration. But
88
+something is better than nothing. Also, before the previous commit (commit
89
+preceding this test creation), starting backup on such configuration led to
90
+crash, so current "something" is a lot better, and this test actual goal is
91
+to check that crash is fixed :)
92
+"""
93
+
94
+vm = iotests.VM()
95
+vm.launch()
96
+
97
+vm.qmp_log('blockdev-add', **{'node-name': 'target', 'driver': 'null-co'})
98
+
99
+vm.qmp_log('blockdev-add', **{
100
+ 'node-name': 'source',
101
+ 'driver': 'blkdebug',
102
+ 'image': {'node-name': 'base', 'driver': 'null-co', 'size': size}
103
+})
104
+
105
+vm.qmp_log('blockdev-add', **{
106
+ 'node-name': 'other',
107
+ 'driver': 'blkdebug',
108
+ 'image': 'base',
109
+ 'take-child-perms': ['write']
110
+})
111
+
112
+vm.qmp_log('blockdev-backup', sync='full', device='source', target='target')
113
+
114
+vm.shutdown()
115
diff --git a/tests/qemu-iotests/283.out b/tests/qemu-iotests/283.out
116
new file mode 100644
89
new file mode 100644
117
index XXXXXXX..XXXXXXX
90
index XXXXXXX..XXXXXXX
118
--- /dev/null
91
--- /dev/null
119
+++ b/tests/qemu-iotests/283.out
92
+++ b/hw/ufs/ufs.h
120
@@ -XXX,XX +XXX,XX @@
93
@@ -XXX,XX +XXX,XX @@
121
+{"execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "target"}}
94
+/*
122
+{"return": {}}
95
+ * QEMU UFS
123
+{"execute": "blockdev-add", "arguments": {"driver": "blkdebug", "image": {"driver": "null-co", "node-name": "base", "size": 1048576}, "node-name": "source"}}
96
+ *
124
+{"return": {}}
97
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved.
125
+{"execute": "blockdev-add", "arguments": {"driver": "blkdebug", "image": "base", "node-name": "other", "take-child-perms": ["write"]}}
98
+ *
126
+{"return": {}}
99
+ * Written by Jeuk Kim <jeuk20.kim@samsung.com>
127
+{"execute": "blockdev-backup", "arguments": {"device": "source", "sync": "full", "target": "target"}}
100
+ *
128
+{"error": {"class": "GenericError", "desc": "Cannot set permissions for backup-top filter: Conflicts with use by other as 'image', which uses 'write' on base"}}
101
+ * SPDX-License-Identifier: GPL-2.0-or-later
129
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
102
+ */
103
+
104
+#ifndef HW_UFS_UFS_H
105
+#define HW_UFS_UFS_H
106
+
107
+#include "hw/pci/pci_device.h"
108
+#include "hw/scsi/scsi.h"
109
+#include "block/ufs.h"
110
+
111
+#define UFS_MAX_LUS 32
112
+#define UFS_BLOCK_SIZE 4096
113
+
114
+typedef struct UfsParams {
115
+ char *serial;
116
+ uint8_t nutrs; /* Number of UTP Transfer Request Slots */
117
+ uint8_t nutmrs; /* Number of UTP Task Management Request Slots */
118
+} UfsParams;
119
+
120
+typedef struct UfsHc {
121
+ PCIDevice parent_obj;
122
+ MemoryRegion iomem;
123
+ UfsReg reg;
124
+ UfsParams params;
125
+ uint32_t reg_size;
126
+
127
+ qemu_irq irq;
128
+ QEMUBH *doorbell_bh;
129
+ QEMUBH *complete_bh;
130
+} UfsHc;
131
+
132
+#define TYPE_UFS "ufs"
133
+#define UFS(obj) OBJECT_CHECK(UfsHc, (obj), TYPE_UFS)
134
+
135
+#endif /* HW_UFS_UFS_H */
136
diff --git a/include/block/ufs.h b/include/block/ufs.h
137
new file mode 100644
138
index XXXXXXX..XXXXXXX
139
--- /dev/null
140
+++ b/include/block/ufs.h
141
@@ -XXX,XX +XXX,XX @@
142
+/* SPDX-License-Identifier: GPL-2.0-or-later */
143
+
144
+#ifndef BLOCK_UFS_H
145
+#define BLOCK_UFS_H
146
+
147
+#include "hw/registerfields.h"
148
+
149
+typedef struct QEMU_PACKED UfsReg {
150
+ uint32_t cap;
151
+ uint32_t rsvd0;
152
+ uint32_t ver;
153
+ uint32_t rsvd1;
154
+ uint32_t hcpid;
155
+ uint32_t hcmid;
156
+ uint32_t ahit;
157
+ uint32_t rsvd2;
158
+ uint32_t is;
159
+ uint32_t ie;
160
+ uint32_t rsvd3[2];
161
+ uint32_t hcs;
162
+ uint32_t hce;
163
+ uint32_t uecpa;
164
+ uint32_t uecdl;
165
+ uint32_t uecn;
166
+ uint32_t uect;
167
+ uint32_t uecdme;
168
+ uint32_t utriacr;
169
+ uint32_t utrlba;
170
+ uint32_t utrlbau;
171
+ uint32_t utrldbr;
172
+ uint32_t utrlclr;
173
+ uint32_t utrlrsr;
174
+ uint32_t utrlcnr;
175
+ uint32_t rsvd4[2];
176
+ uint32_t utmrlba;
177
+ uint32_t utmrlbau;
178
+ uint32_t utmrldbr;
179
+ uint32_t utmrlclr;
180
+ uint32_t utmrlrsr;
181
+ uint32_t rsvd5[3];
182
+ uint32_t uiccmd;
183
+ uint32_t ucmdarg1;
184
+ uint32_t ucmdarg2;
185
+ uint32_t ucmdarg3;
186
+ uint32_t rsvd6[4];
187
+ uint32_t rsvd7[4];
188
+ uint32_t rsvd8[16];
189
+ uint32_t ccap;
190
+} UfsReg;
191
+
192
+REG32(CAP, offsetof(UfsReg, cap))
193
+ FIELD(CAP, NUTRS, 0, 5)
194
+ FIELD(CAP, RTT, 8, 8)
195
+ FIELD(CAP, NUTMRS, 16, 3)
196
+ FIELD(CAP, AUTOH8, 23, 1)
197
+ FIELD(CAP, 64AS, 24, 1)
198
+ FIELD(CAP, OODDS, 25, 1)
199
+ FIELD(CAP, UICDMETMS, 26, 1)
200
+ FIELD(CAP, CS, 28, 1)
201
+REG32(VER, offsetof(UfsReg, ver))
202
+REG32(HCPID, offsetof(UfsReg, hcpid))
203
+REG32(HCMID, offsetof(UfsReg, hcmid))
204
+REG32(AHIT, offsetof(UfsReg, ahit))
205
+REG32(IS, offsetof(UfsReg, is))
206
+ FIELD(IS, UTRCS, 0, 1)
207
+ FIELD(IS, UDEPRI, 1, 1)
208
+ FIELD(IS, UE, 2, 1)
209
+ FIELD(IS, UTMS, 3, 1)
210
+ FIELD(IS, UPMS, 4, 1)
211
+ FIELD(IS, UHXS, 5, 1)
212
+ FIELD(IS, UHES, 6, 1)
213
+ FIELD(IS, ULLS, 7, 1)
214
+ FIELD(IS, ULSS, 8, 1)
215
+ FIELD(IS, UTMRCS, 9, 1)
216
+ FIELD(IS, UCCS, 10, 1)
217
+ FIELD(IS, DFES, 11, 1)
218
+ FIELD(IS, UTPES, 12, 1)
219
+ FIELD(IS, HCFES, 16, 1)
220
+ FIELD(IS, SBFES, 17, 1)
221
+ FIELD(IS, CEFES, 18, 1)
222
+REG32(IE, offsetof(UfsReg, ie))
223
+ FIELD(IE, UTRCE, 0, 1)
224
+ FIELD(IE, UDEPRIE, 1, 1)
225
+ FIELD(IE, UEE, 2, 1)
226
+ FIELD(IE, UTMSE, 3, 1)
227
+ FIELD(IE, UPMSE, 4, 1)
228
+ FIELD(IE, UHXSE, 5, 1)
229
+ FIELD(IE, UHESE, 6, 1)
230
+ FIELD(IE, ULLSE, 7, 1)
231
+ FIELD(IE, ULSSE, 8, 1)
232
+ FIELD(IE, UTMRCE, 9, 1)
233
+ FIELD(IE, UCCE, 10, 1)
234
+ FIELD(IE, DFEE, 11, 1)
235
+ FIELD(IE, UTPEE, 12, 1)
236
+ FIELD(IE, HCFEE, 16, 1)
237
+ FIELD(IE, SBFEE, 17, 1)
238
+ FIELD(IE, CEFEE, 18, 1)
239
+REG32(HCS, offsetof(UfsReg, hcs))
240
+ FIELD(HCS, DP, 0, 1)
241
+ FIELD(HCS, UTRLRDY, 1, 1)
242
+ FIELD(HCS, UTMRLRDY, 2, 1)
243
+ FIELD(HCS, UCRDY, 3, 1)
244
+ FIELD(HCS, UPMCRS, 8, 3)
245
+REG32(HCE, offsetof(UfsReg, hce))
246
+ FIELD(HCE, HCE, 0, 1)
247
+ FIELD(HCE, CGE, 1, 1)
248
+REG32(UECPA, offsetof(UfsReg, uecpa))
249
+REG32(UECDL, offsetof(UfsReg, uecdl))
250
+REG32(UECN, offsetof(UfsReg, uecn))
251
+REG32(UECT, offsetof(UfsReg, uect))
252
+REG32(UECDME, offsetof(UfsReg, uecdme))
253
+REG32(UTRIACR, offsetof(UfsReg, utriacr))
254
+REG32(UTRLBA, offsetof(UfsReg, utrlba))
255
+ FIELD(UTRLBA, UTRLBA, 9, 22)
256
+REG32(UTRLBAU, offsetof(UfsReg, utrlbau))
257
+REG32(UTRLDBR, offsetof(UfsReg, utrldbr))
258
+REG32(UTRLCLR, offsetof(UfsReg, utrlclr))
259
+REG32(UTRLRSR, offsetof(UfsReg, utrlrsr))
260
+REG32(UTRLCNR, offsetof(UfsReg, utrlcnr))
261
+REG32(UTMRLBA, offsetof(UfsReg, utmrlba))
262
+ FIELD(UTMRLBA, UTMRLBA, 9, 22)
263
+REG32(UTMRLBAU, offsetof(UfsReg, utmrlbau))
264
+REG32(UTMRLDBR, offsetof(UfsReg, utmrldbr))
265
+REG32(UTMRLCLR, offsetof(UfsReg, utmrlclr))
266
+REG32(UTMRLRSR, offsetof(UfsReg, utmrlrsr))
267
+REG32(UICCMD, offsetof(UfsReg, uiccmd))
268
+REG32(UCMDARG1, offsetof(UfsReg, ucmdarg1))
269
+REG32(UCMDARG2, offsetof(UfsReg, ucmdarg2))
270
+REG32(UCMDARG3, offsetof(UfsReg, ucmdarg3))
271
+REG32(CCAP, offsetof(UfsReg, ccap))
272
+
273
+#define UFS_INTR_MASK \
274
+ ((1 << R_IS_CEFES_SHIFT) | (1 << R_IS_SBFES_SHIFT) | \
275
+ (1 << R_IS_HCFES_SHIFT) | (1 << R_IS_UTPES_SHIFT) | \
276
+ (1 << R_IS_DFES_SHIFT) | (1 << R_IS_UCCS_SHIFT) | \
277
+ (1 << R_IS_UTMRCS_SHIFT) | (1 << R_IS_ULSS_SHIFT) | \
278
+ (1 << R_IS_ULLS_SHIFT) | (1 << R_IS_UHES_SHIFT) | \
279
+ (1 << R_IS_UHXS_SHIFT) | (1 << R_IS_UPMS_SHIFT) | \
280
+ (1 << R_IS_UTMS_SHIFT) | (1 << R_IS_UE_SHIFT) | \
281
+ (1 << R_IS_UDEPRI_SHIFT) | (1 << R_IS_UTRCS_SHIFT))
282
+
283
+#define UFS_UPIU_HEADER_TRANSACTION_TYPE_SHIFT 24
284
+#define UFS_UPIU_HEADER_TRANSACTION_TYPE_MASK 0xff
285
+#define UFS_UPIU_HEADER_TRANSACTION_TYPE(dword0) \
286
+ ((be32_to_cpu(dword0) >> UFS_UPIU_HEADER_TRANSACTION_TYPE_SHIFT) & \
287
+ UFS_UPIU_HEADER_TRANSACTION_TYPE_MASK)
288
+
289
+#define UFS_UPIU_HEADER_QUERY_FUNC_SHIFT 16
290
+#define UFS_UPIU_HEADER_QUERY_FUNC_MASK 0xff
291
+#define UFS_UPIU_HEADER_QUERY_FUNC(dword1) \
292
+ ((be32_to_cpu(dword1) >> UFS_UPIU_HEADER_QUERY_FUNC_SHIFT) & \
293
+ UFS_UPIU_HEADER_QUERY_FUNC_MASK)
294
+
295
+#define UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH_SHIFT 0
296
+#define UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH_MASK 0xffff
297
+#define UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH(dword2) \
298
+ ((be32_to_cpu(dword2) >> UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH_SHIFT) & \
299
+ UFS_UPIU_HEADER_DATA_SEGMENT_LENGTH_MASK)
300
+
301
+typedef struct QEMU_PACKED DeviceDescriptor {
302
+ uint8_t length;
303
+ uint8_t descriptor_idn;
304
+ uint8_t device;
305
+ uint8_t device_class;
306
+ uint8_t device_sub_class;
307
+ uint8_t protocol;
308
+ uint8_t number_lu;
309
+ uint8_t number_wlu;
310
+ uint8_t boot_enable;
311
+ uint8_t descr_access_en;
312
+ uint8_t init_power_mode;
313
+ uint8_t high_priority_lun;
314
+ uint8_t secure_removal_type;
315
+ uint8_t security_lu;
316
+ uint8_t background_ops_term_lat;
317
+ uint8_t init_active_icc_level;
318
+ uint16_t spec_version;
319
+ uint16_t manufacture_date;
320
+ uint8_t manufacturer_name;
321
+ uint8_t product_name;
322
+ uint8_t serial_number;
323
+ uint8_t oem_id;
324
+ uint16_t manufacturer_id;
325
+ uint8_t ud_0_base_offset;
326
+ uint8_t ud_config_p_length;
327
+ uint8_t device_rtt_cap;
328
+ uint16_t periodic_rtc_update;
329
+ uint8_t ufs_features_support;
330
+ uint8_t ffu_timeout;
331
+ uint8_t queue_depth;
332
+ uint16_t device_version;
333
+ uint8_t num_secure_wp_area;
334
+ uint32_t psa_max_data_size;
335
+ uint8_t psa_state_timeout;
336
+ uint8_t product_revision_level;
337
+ uint8_t reserved[36];
338
+ uint32_t extended_ufs_features_support;
339
+ uint8_t write_booster_buffer_preserve_user_space_en;
340
+ uint8_t write_booster_buffer_type;
341
+ uint32_t num_shared_write_booster_buffer_alloc_units;
342
+} DeviceDescriptor;
343
+
344
+typedef struct QEMU_PACKED GeometryDescriptor {
345
+ uint8_t length;
346
+ uint8_t descriptor_idn;
347
+ uint8_t media_technology;
348
+ uint8_t reserved;
349
+ uint64_t total_raw_device_capacity;
350
+ uint8_t max_number_lu;
351
+ uint32_t segment_size;
352
+ uint8_t allocation_unit_size;
353
+ uint8_t min_addr_block_size;
354
+ uint8_t optimal_read_block_size;
355
+ uint8_t optimal_write_block_size;
356
+ uint8_t max_in_buffer_size;
357
+ uint8_t max_out_buffer_size;
358
+ uint8_t rpmb_read_write_size;
359
+ uint8_t dynamic_capacity_resource_policy;
360
+ uint8_t data_ordering;
361
+ uint8_t max_context_id_number;
362
+ uint8_t sys_data_tag_unit_size;
363
+ uint8_t sys_data_tag_res_size;
364
+ uint8_t supported_sec_r_types;
365
+ uint16_t supported_memory_types;
366
+ uint32_t system_code_max_n_alloc_u;
367
+ uint16_t system_code_cap_adj_fac;
368
+ uint32_t non_persist_max_n_alloc_u;
369
+ uint16_t non_persist_cap_adj_fac;
370
+ uint32_t enhanced_1_max_n_alloc_u;
371
+ uint16_t enhanced_1_cap_adj_fac;
372
+ uint32_t enhanced_2_max_n_alloc_u;
373
+ uint16_t enhanced_2_cap_adj_fac;
374
+ uint32_t enhanced_3_max_n_alloc_u;
375
+ uint16_t enhanced_3_cap_adj_fac;
376
+ uint32_t enhanced_4_max_n_alloc_u;
377
+ uint16_t enhanced_4_cap_adj_fac;
378
+ uint32_t optimal_logical_block_size;
379
+ uint8_t reserved2[7];
380
+ uint32_t write_booster_buffer_max_n_alloc_units;
381
+ uint8_t device_max_write_booster_l_us;
382
+ uint8_t write_booster_buffer_cap_adj_fac;
383
+ uint8_t supported_write_booster_buffer_user_space_reduction_types;
384
+ uint8_t supported_write_booster_buffer_types;
385
+} GeometryDescriptor;
386
+
387
+#define UFS_GEOMETRY_CAPACITY_SHIFT 9
388
+
389
+typedef struct QEMU_PACKED UnitDescriptor {
390
+ uint8_t length;
391
+ uint8_t descriptor_idn;
392
+ uint8_t unit_index;
393
+ uint8_t lu_enable;
394
+ uint8_t boot_lun_id;
395
+ uint8_t lu_write_protect;
396
+ uint8_t lu_queue_depth;
397
+ uint8_t psa_sensitive;
398
+ uint8_t memory_type;
399
+ uint8_t data_reliability;
400
+ uint8_t logical_block_size;
401
+ uint64_t logical_block_count;
402
+ uint32_t erase_block_size;
403
+ uint8_t provisioning_type;
404
+ uint64_t phy_mem_resource_count;
405
+ uint16_t context_capabilities;
406
+ uint8_t large_unit_granularity_m1;
407
+ uint8_t reserved[6];
408
+ uint32_t lu_num_write_booster_buffer_alloc_units;
409
+} UnitDescriptor;
410
+
411
+typedef struct QEMU_PACKED RpmbUnitDescriptor {
412
+ uint8_t length;
413
+ uint8_t descriptor_idn;
414
+ uint8_t unit_index;
415
+ uint8_t lu_enable;
416
+ uint8_t boot_lun_id;
417
+ uint8_t lu_write_protect;
418
+ uint8_t lu_queue_depth;
419
+ uint8_t psa_sensitive;
420
+ uint8_t memory_type;
421
+ uint8_t reserved;
422
+ uint8_t logical_block_size;
423
+ uint64_t logical_block_count;
424
+ uint32_t erase_block_size;
425
+ uint8_t provisioning_type;
426
+ uint64_t phy_mem_resource_count;
427
+ uint8_t reserved2[3];
428
+} RpmbUnitDescriptor;
429
+
430
+typedef struct QEMU_PACKED PowerParametersDescriptor {
431
+ uint8_t length;
432
+ uint8_t descriptor_idn;
433
+ uint16_t active_icc_levels_vcc[16];
434
+ uint16_t active_icc_levels_vccq[16];
435
+ uint16_t active_icc_levels_vccq_2[16];
436
+} PowerParametersDescriptor;
437
+
438
+typedef struct QEMU_PACKED InterconnectDescriptor {
439
+ uint8_t length;
440
+ uint8_t descriptor_idn;
441
+ uint16_t bcd_unipro_version;
442
+ uint16_t bcd_mphy_version;
443
+} InterconnectDescriptor;
444
+
445
+typedef struct QEMU_PACKED StringDescriptor {
446
+ uint8_t length;
447
+ uint8_t descriptor_idn;
448
+ uint16_t UC[126];
449
+} StringDescriptor;
450
+
451
+typedef struct QEMU_PACKED DeviceHealthDescriptor {
452
+ uint8_t length;
453
+ uint8_t descriptor_idn;
454
+ uint8_t pre_eol_info;
455
+ uint8_t device_life_time_est_a;
456
+ uint8_t device_life_time_est_b;
457
+ uint8_t vendor_prop_info[32];
458
+ uint32_t refresh_total_count;
459
+ uint32_t refresh_progress;
460
+} DeviceHealthDescriptor;
461
+
462
+typedef struct QEMU_PACKED Flags {
463
+ uint8_t reserved;
464
+ uint8_t device_init;
465
+ uint8_t permanent_wp_en;
466
+ uint8_t power_on_wp_en;
467
+ uint8_t background_ops_en;
468
+ uint8_t device_life_span_mode_en;
469
+ uint8_t purge_enable;
470
+ uint8_t refresh_enable;
471
+ uint8_t phy_resource_removal;
472
+ uint8_t busy_rtc;
473
+ uint8_t reserved2;
474
+ uint8_t permanently_disable_fw_update;
475
+ uint8_t reserved3[2];
476
+ uint8_t wb_en;
477
+ uint8_t wb_buffer_flush_en;
478
+ uint8_t wb_buffer_flush_during_hibernate;
479
+ uint8_t reserved4[2];
480
+} Flags;
481
+
482
+typedef struct Attributes {
483
+ uint8_t boot_lun_en;
484
+ uint8_t reserved;
485
+ uint8_t current_power_mode;
486
+ uint8_t active_icc_level;
487
+ uint8_t out_of_order_data_en;
488
+ uint8_t background_op_status;
489
+ uint8_t purge_status;
490
+ uint8_t max_data_in_size;
491
+ uint8_t max_data_out_size;
492
+ uint32_t dyn_cap_needed;
493
+ uint8_t ref_clk_freq;
494
+ uint8_t config_descr_lock;
495
+ uint8_t max_num_of_rtt;
496
+ uint16_t exception_event_control;
497
+ uint16_t exception_event_status;
498
+ uint32_t seconds_passed;
499
+ uint16_t context_conf;
500
+ uint8_t device_ffu_status;
501
+ uint8_t psa_state;
502
+ uint32_t psa_data_size;
503
+ uint8_t ref_clk_gating_wait_time;
504
+ uint8_t device_case_rough_temperaure;
505
+ uint8_t device_too_high_temp_boundary;
506
+ uint8_t device_too_low_temp_boundary;
507
+ uint8_t throttling_status;
508
+ uint8_t wb_buffer_flush_status;
509
+ uint8_t available_wb_buffer_size;
510
+ uint8_t wb_buffer_life_time_est;
511
+ uint32_t current_wb_buffer_size;
512
+ uint8_t refresh_status;
513
+ uint8_t refresh_freq;
514
+ uint8_t refresh_unit;
515
+ uint8_t refresh_method;
516
+} Attributes;
517
+
518
+#define UFS_TRANSACTION_SPECIFIC_FIELD_SIZE 20
519
+#define UFS_MAX_QUERY_DATA_SIZE 256
520
+
521
+/* Command response result code */
522
+typedef enum CommandRespCode {
523
+ COMMAND_RESULT_SUCESS = 0x00,
524
+ COMMAND_RESULT_FAIL = 0x01,
525
+} CommandRespCode;
526
+
527
+enum {
528
+ UFS_UPIU_FLAG_UNDERFLOW = 0x20,
529
+ UFS_UPIU_FLAG_OVERFLOW = 0x40,
530
+};
531
+
532
+typedef struct QEMU_PACKED UtpUpiuHeader {
533
+ uint8_t trans_type;
534
+ uint8_t flags;
535
+ uint8_t lun;
536
+ uint8_t task_tag;
537
+ uint8_t iid_cmd_set_type;
538
+ uint8_t query_func;
539
+ uint8_t response;
540
+ uint8_t scsi_status;
541
+ uint8_t ehs_len;
542
+ uint8_t device_inf;
543
+ uint16_t data_segment_length;
544
+} UtpUpiuHeader;
545
+
546
+/*
547
+ * The code below is copied from the linux kernel
548
+ * ("include/uapi/scsi/scsi_bsg_ufs.h") and modified to fit the qemu style.
549
+ */
550
+
551
+typedef struct QEMU_PACKED UtpUpiuQuery {
552
+ uint8_t opcode;
553
+ uint8_t idn;
554
+ uint8_t index;
555
+ uint8_t selector;
556
+ uint16_t reserved_osf;
557
+ uint16_t length;
558
+ uint32_t value;
559
+ uint32_t reserved[2];
560
+ /* EHS length should be 0. We don't have to worry about EHS area. */
561
+ uint8_t data[UFS_MAX_QUERY_DATA_SIZE];
562
+} UtpUpiuQuery;
563
+
564
+#define UFS_CDB_SIZE 16
565
+
566
+/*
567
+ * struct UtpUpiuCmd - Command UPIU structure
568
+ * @data_transfer_len: Data Transfer Length DW-3
569
+ * @cdb: Command Descriptor Block CDB DW-4 to DW-7
570
+ */
571
+typedef struct QEMU_PACKED UtpUpiuCmd {
572
+ uint32_t exp_data_transfer_len;
573
+ uint8_t cdb[UFS_CDB_SIZE];
574
+} UtpUpiuCmd;
575
+
576
+/*
577
+ * struct UtpUpiuReq - general upiu request structure
578
+ * @header:UPIU header structure DW-0 to DW-2
579
+ * @sc: fields structure for scsi command DW-3 to DW-7
580
+ * @qr: fields structure for query request DW-3 to DW-7
581
+ * @uc: use utp_upiu_query to host the 4 dwords of uic command
582
+ */
583
+typedef struct QEMU_PACKED UtpUpiuReq {
584
+ UtpUpiuHeader header;
585
+ union {
586
+ UtpUpiuCmd sc;
587
+ UtpUpiuQuery qr;
588
+ };
589
+} UtpUpiuReq;
590
+
591
+/*
592
+ * The code below is copied from the linux kernel ("include/ufs/ufshci.h") and
593
+ * modified to fit the qemu style.
594
+ */
595
+
596
+enum {
597
+ PWR_OK = 0x0,
598
+ PWR_LOCAL = 0x01,
599
+ PWR_REMOTE = 0x02,
600
+ PWR_BUSY = 0x03,
601
+ PWR_ERROR_CAP = 0x04,
602
+ PWR_FATAL_ERROR = 0x05,
603
+};
604
+
605
+/* UIC Commands */
606
+enum uic_cmd_dme {
607
+ UIC_CMD_DME_GET = 0x01,
608
+ UIC_CMD_DME_SET = 0x02,
609
+ UIC_CMD_DME_PEER_GET = 0x03,
610
+ UIC_CMD_DME_PEER_SET = 0x04,
611
+ UIC_CMD_DME_POWERON = 0x10,
612
+ UIC_CMD_DME_POWEROFF = 0x11,
613
+ UIC_CMD_DME_ENABLE = 0x12,
614
+ UIC_CMD_DME_RESET = 0x14,
615
+ UIC_CMD_DME_END_PT_RST = 0x15,
616
+ UIC_CMD_DME_LINK_STARTUP = 0x16,
617
+ UIC_CMD_DME_HIBER_ENTER = 0x17,
618
+ UIC_CMD_DME_HIBER_EXIT = 0x18,
619
+ UIC_CMD_DME_TEST_MODE = 0x1A,
620
+};
621
+
622
+/* UIC Config result code / Generic error code */
623
+enum {
624
+ UIC_CMD_RESULT_SUCCESS = 0x00,
625
+ UIC_CMD_RESULT_INVALID_ATTR = 0x01,
626
+ UIC_CMD_RESULT_FAILURE = 0x01,
627
+ UIC_CMD_RESULT_INVALID_ATTR_VALUE = 0x02,
628
+ UIC_CMD_RESULT_READ_ONLY_ATTR = 0x03,
629
+ UIC_CMD_RESULT_WRITE_ONLY_ATTR = 0x04,
630
+ UIC_CMD_RESULT_BAD_INDEX = 0x05,
631
+ UIC_CMD_RESULT_LOCKED_ATTR = 0x06,
632
+ UIC_CMD_RESULT_BAD_TEST_FEATURE_INDEX = 0x07,
633
+ UIC_CMD_RESULT_PEER_COMM_FAILURE = 0x08,
634
+ UIC_CMD_RESULT_BUSY = 0x09,
635
+ UIC_CMD_RESULT_DME_FAILURE = 0x0A,
636
+};
637
+
638
+#define MASK_UIC_COMMAND_RESULT 0xFF
639
+
640
+/*
641
+ * Request Descriptor Definitions
642
+ */
643
+
644
+/* Transfer request command type */
645
+enum {
646
+ UTP_CMD_TYPE_SCSI = 0x0,
647
+ UTP_CMD_TYPE_UFS = 0x1,
648
+ UTP_CMD_TYPE_DEV_MANAGE = 0x2,
649
+};
650
+
651
+/* To accommodate UFS2.0 required Command type */
652
+enum {
653
+ UTP_CMD_TYPE_UFS_STORAGE = 0x1,
654
+};
655
+
656
+enum {
657
+ UTP_SCSI_COMMAND = 0x00000000,
658
+ UTP_NATIVE_UFS_COMMAND = 0x10000000,
659
+ UTP_DEVICE_MANAGEMENT_FUNCTION = 0x20000000,
660
+ UTP_REQ_DESC_INT_CMD = 0x01000000,
661
+ UTP_REQ_DESC_CRYPTO_ENABLE_CMD = 0x00800000,
662
+};
663
+
664
+/* UTP Transfer Request Data Direction (DD) */
665
+enum {
666
+ UTP_NO_DATA_TRANSFER = 0x00000000,
667
+ UTP_HOST_TO_DEVICE = 0x02000000,
668
+ UTP_DEVICE_TO_HOST = 0x04000000,
669
+};
670
+
671
+/* Overall command status values */
672
+enum UtpOcsCodes {
673
+ OCS_SUCCESS = 0x0,
674
+ OCS_INVALID_CMD_TABLE_ATTR = 0x1,
675
+ OCS_INVALID_PRDT_ATTR = 0x2,
676
+ OCS_MISMATCH_DATA_BUF_SIZE = 0x3,
677
+ OCS_MISMATCH_RESP_UPIU_SIZE = 0x4,
678
+ OCS_PEER_COMM_FAILURE = 0x5,
679
+ OCS_ABORTED = 0x6,
680
+ OCS_FATAL_ERROR = 0x7,
681
+ OCS_DEVICE_FATAL_ERROR = 0x8,
682
+ OCS_INVALID_CRYPTO_CONFIG = 0x9,
683
+ OCS_GENERAL_CRYPTO_ERROR = 0xa,
684
+ OCS_INVALID_COMMAND_STATUS = 0xf,
685
+};
686
+
687
+enum {
688
+ MASK_OCS = 0x0F,
689
+};
690
+
691
+/*
692
+ * struct UfshcdSgEntry - UFSHCI PRD Entry
693
+ * @addr: Physical address; DW-0 and DW-1.
694
+ * @reserved: Reserved for future use DW-2
695
+ * @size: size of physical segment DW-3
696
+ */
697
+typedef struct QEMU_PACKED UfshcdSgEntry {
698
+ uint64_t addr;
699
+ uint32_t reserved;
700
+ uint32_t size;
701
+ /*
702
+ * followed by variant-specific fields if
703
+ * CONFIG_SCSI_UFS_VARIABLE_SG_ENTRY_SIZE has been defined.
704
+ */
705
+} UfshcdSgEntry;
706
+
707
+/*
708
+ * struct RequestDescHeader - Descriptor Header common to both UTRD and UTMRD
709
+ * @dword0: Descriptor Header DW0
710
+ * @dword1: Descriptor Header DW1
711
+ * @dword2: Descriptor Header DW2
712
+ * @dword3: Descriptor Header DW3
713
+ */
714
+typedef struct QEMU_PACKED RequestDescHeader {
715
+ uint32_t dword_0;
716
+ uint32_t dword_1;
717
+ uint32_t dword_2;
718
+ uint32_t dword_3;
719
+} RequestDescHeader;
720
+
721
+/*
722
+ * struct UtpTransferReqDesc - UTP Transfer Request Descriptor (UTRD)
723
+ * @header: UTRD header DW-0 to DW-3
724
+ * @command_desc_base_addr_lo: UCD base address low DW-4
725
+ * @command_desc_base_addr_hi: UCD base address high DW-5
726
+ * @response_upiu_length: response UPIU length DW-6
727
+ * @response_upiu_offset: response UPIU offset DW-6
728
+ * @prd_table_length: Physical region descriptor length DW-7
729
+ * @prd_table_offset: Physical region descriptor offset DW-7
730
+ */
731
+typedef struct QEMU_PACKED UtpTransferReqDesc {
732
+ /* DW 0-3 */
733
+ RequestDescHeader header;
734
+
735
+ /* DW 4-5*/
736
+ uint32_t command_desc_base_addr_lo;
737
+ uint32_t command_desc_base_addr_hi;
738
+
739
+ /* DW 6 */
740
+ uint16_t response_upiu_length;
741
+ uint16_t response_upiu_offset;
742
+
743
+ /* DW 7 */
744
+ uint16_t prd_table_length;
745
+ uint16_t prd_table_offset;
746
+} UtpTransferReqDesc;
747
+
748
+/*
749
+ * UTMRD structure.
750
+ */
751
+typedef struct QEMU_PACKED UtpTaskReqDesc {
752
+ /* DW 0-3 */
753
+ RequestDescHeader header;
754
+
755
+ /* DW 4-11 - Task request UPIU structure */
756
+ struct {
757
+ UtpUpiuHeader req_header;
758
+ uint32_t input_param1;
759
+ uint32_t input_param2;
760
+ uint32_t input_param3;
761
+ uint32_t reserved1[2];
762
+ } upiu_req;
763
+
764
+ /* DW 12-19 - Task Management Response UPIU structure */
765
+ struct {
766
+ UtpUpiuHeader rsp_header;
767
+ uint32_t output_param1;
768
+ uint32_t output_param2;
769
+ uint32_t reserved2[3];
770
+ } upiu_rsp;
771
+} UtpTaskReqDesc;
772
+
773
+/*
774
+ * The code below is copied from the linux kernel ("include/ufs/ufs.h") and
775
+ * modified to fit the qemu style.
776
+ */
777
+
778
+#define GENERAL_UPIU_REQUEST_SIZE (sizeof(UtpUpiuReq))
779
+#define QUERY_DESC_MAX_SIZE 255
780
+#define QUERY_DESC_MIN_SIZE 2
781
+#define QUERY_DESC_HDR_SIZE 2
782
+#define QUERY_OSF_SIZE (GENERAL_UPIU_REQUEST_SIZE - (sizeof(UtpUpiuHeader)))
783
+#define UFS_SENSE_SIZE 18
784
+
785
+/*
786
+ * UFS device may have standard LUs and LUN id could be from 0x00 to
787
+ * 0x7F. Standard LUs use "Peripheral Device Addressing Format".
788
+ * UFS device may also have the Well Known LUs (also referred as W-LU)
789
+ * which again could be from 0x00 to 0x7F. For W-LUs, device only use
790
+ * the "Extended Addressing Format" which means the W-LUNs would be
791
+ * from 0xc100 (SCSI_W_LUN_BASE) onwards.
792
+ * This means max. LUN number reported from UFS device could be 0xC17F.
793
+ */
794
+#define UFS_UPIU_MAX_UNIT_NUM_ID 0x7F
795
+#define UFS_UPIU_WLUN_ID (1 << 7)
796
+
797
+/* WriteBooster buffer is available only for the logical unit from 0 to 7 */
798
+#define UFS_UPIU_MAX_WB_LUN_ID 8
799
+
800
+/*
801
+ * WriteBooster buffer lifetime has a limit setted by vendor.
802
+ * If it is over the limit, WriteBooster feature will be disabled.
803
+ */
804
+#define UFS_WB_EXCEED_LIFETIME 0x0B
805
+
806
+/*
807
+ * In UFS Spec, the Extra Header Segment (EHS) starts from byte 32 in UPIU
808
+ * request/response packet
809
+ */
810
+#define EHS_OFFSET_IN_RESPONSE 32
811
+
812
+/* Well known logical unit id in LUN field of UPIU */
813
+enum {
814
+ UFS_UPIU_REPORT_LUNS_WLUN = 0x81,
815
+ UFS_UPIU_UFS_DEVICE_WLUN = 0xD0,
816
+ UFS_UPIU_BOOT_WLUN = 0xB0,
817
+ UFS_UPIU_RPMB_WLUN = 0xC4,
818
+};
819
+
820
+/*
821
+ * UFS Protocol Information Unit related definitions
822
+ */
823
+
824
+/* Task management functions */
825
+enum {
826
+ UFS_ABORT_TASK = 0x01,
827
+ UFS_ABORT_TASK_SET = 0x02,
828
+ UFS_CLEAR_TASK_SET = 0x04,
829
+ UFS_LOGICAL_RESET = 0x08,
830
+ UFS_QUERY_TASK = 0x80,
831
+ UFS_QUERY_TASK_SET = 0x81,
832
+};
833
+
834
+/* UTP UPIU Transaction Codes Initiator to Target */
835
+enum {
836
+ UPIU_TRANSACTION_NOP_OUT = 0x00,
837
+ UPIU_TRANSACTION_COMMAND = 0x01,
838
+ UPIU_TRANSACTION_DATA_OUT = 0x02,
839
+ UPIU_TRANSACTION_TASK_REQ = 0x04,
840
+ UPIU_TRANSACTION_QUERY_REQ = 0x16,
841
+};
842
+
843
+/* UTP UPIU Transaction Codes Target to Initiator */
844
+enum {
845
+ UPIU_TRANSACTION_NOP_IN = 0x20,
846
+ UPIU_TRANSACTION_RESPONSE = 0x21,
847
+ UPIU_TRANSACTION_DATA_IN = 0x22,
848
+ UPIU_TRANSACTION_TASK_RSP = 0x24,
849
+ UPIU_TRANSACTION_READY_XFER = 0x31,
850
+ UPIU_TRANSACTION_QUERY_RSP = 0x36,
851
+ UPIU_TRANSACTION_REJECT_UPIU = 0x3F,
852
+};
853
+
854
+/* UPIU Read/Write flags */
855
+enum {
856
+ UPIU_CMD_FLAGS_NONE = 0x00,
857
+ UPIU_CMD_FLAGS_WRITE = 0x20,
858
+ UPIU_CMD_FLAGS_READ = 0x40,
859
+};
860
+
861
+/* UPIU Task Attributes */
862
+enum {
863
+ UPIU_TASK_ATTR_SIMPLE = 0x00,
864
+ UPIU_TASK_ATTR_ORDERED = 0x01,
865
+ UPIU_TASK_ATTR_HEADQ = 0x02,
866
+ UPIU_TASK_ATTR_ACA = 0x03,
867
+};
868
+
869
+/* UPIU Query request function */
870
+enum {
871
+ UPIU_QUERY_FUNC_STANDARD_READ_REQUEST = 0x01,
872
+ UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST = 0x81,
873
+};
874
+
875
+/* Flag idn for Query Requests*/
876
+enum flag_idn {
877
+ QUERY_FLAG_IDN_FDEVICEINIT = 0x01,
878
+ QUERY_FLAG_IDN_PERMANENT_WPE = 0x02,
879
+ QUERY_FLAG_IDN_PWR_ON_WPE = 0x03,
880
+ QUERY_FLAG_IDN_BKOPS_EN = 0x04,
881
+ QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE = 0x05,
882
+ QUERY_FLAG_IDN_PURGE_ENABLE = 0x06,
883
+ QUERY_FLAG_IDN_REFRESH_ENABLE = 0x07,
884
+ QUERY_FLAG_IDN_FPHYRESOURCEREMOVAL = 0x08,
885
+ QUERY_FLAG_IDN_BUSY_RTC = 0x09,
886
+ QUERY_FLAG_IDN_RESERVED3 = 0x0A,
887
+ QUERY_FLAG_IDN_PERMANENTLY_DISABLE_FW_UPDATE = 0x0B,
888
+ QUERY_FLAG_IDN_WB_EN = 0x0E,
889
+ QUERY_FLAG_IDN_WB_BUFF_FLUSH_EN = 0x0F,
890
+ QUERY_FLAG_IDN_WB_BUFF_FLUSH_DURING_HIBERN8 = 0x10,
891
+ QUERY_FLAG_IDN_HPB_RESET = 0x11,
892
+ QUERY_FLAG_IDN_HPB_EN = 0x12,
893
+ QUERY_FLAG_IDN_COUNT,
894
+};
895
+
896
+/* Attribute idn for Query requests */
897
+enum attr_idn {
898
+ QUERY_ATTR_IDN_BOOT_LU_EN = 0x00,
899
+ QUERY_ATTR_IDN_MAX_HPB_SINGLE_CMD = 0x01,
900
+ QUERY_ATTR_IDN_POWER_MODE = 0x02,
901
+ QUERY_ATTR_IDN_ACTIVE_ICC_LVL = 0x03,
902
+ QUERY_ATTR_IDN_OOO_DATA_EN = 0x04,
903
+ QUERY_ATTR_IDN_BKOPS_STATUS = 0x05,
904
+ QUERY_ATTR_IDN_PURGE_STATUS = 0x06,
905
+ QUERY_ATTR_IDN_MAX_DATA_IN = 0x07,
906
+ QUERY_ATTR_IDN_MAX_DATA_OUT = 0x08,
907
+ QUERY_ATTR_IDN_DYN_CAP_NEEDED = 0x09,
908
+ QUERY_ATTR_IDN_REF_CLK_FREQ = 0x0A,
909
+ QUERY_ATTR_IDN_CONF_DESC_LOCK = 0x0B,
910
+ QUERY_ATTR_IDN_MAX_NUM_OF_RTT = 0x0C,
911
+ QUERY_ATTR_IDN_EE_CONTROL = 0x0D,
912
+ QUERY_ATTR_IDN_EE_STATUS = 0x0E,
913
+ QUERY_ATTR_IDN_SECONDS_PASSED = 0x0F,
914
+ QUERY_ATTR_IDN_CNTX_CONF = 0x10,
915
+ QUERY_ATTR_IDN_CORR_PRG_BLK_NUM = 0x11,
916
+ QUERY_ATTR_IDN_RESERVED2 = 0x12,
917
+ QUERY_ATTR_IDN_RESERVED3 = 0x13,
918
+ QUERY_ATTR_IDN_FFU_STATUS = 0x14,
919
+ QUERY_ATTR_IDN_PSA_STATE = 0x15,
920
+ QUERY_ATTR_IDN_PSA_DATA_SIZE = 0x16,
921
+ QUERY_ATTR_IDN_REF_CLK_GATING_WAIT_TIME = 0x17,
922
+ QUERY_ATTR_IDN_CASE_ROUGH_TEMP = 0x18,
923
+ QUERY_ATTR_IDN_HIGH_TEMP_BOUND = 0x19,
924
+ QUERY_ATTR_IDN_LOW_TEMP_BOUND = 0x1A,
925
+ QUERY_ATTR_IDN_THROTTLING_STATUS = 0x1B,
926
+ QUERY_ATTR_IDN_WB_FLUSH_STATUS = 0x1C,
927
+ QUERY_ATTR_IDN_AVAIL_WB_BUFF_SIZE = 0x1D,
928
+ QUERY_ATTR_IDN_WB_BUFF_LIFE_TIME_EST = 0x1E,
929
+ QUERY_ATTR_IDN_CURR_WB_BUFF_SIZE = 0x1F,
930
+ QUERY_ATTR_IDN_REFRESH_STATUS = 0x2C,
931
+ QUERY_ATTR_IDN_REFRESH_FREQ = 0x2D,
932
+ QUERY_ATTR_IDN_REFRESH_UNIT = 0x2E,
933
+ QUERY_ATTR_IDN_COUNT,
934
+};
935
+
936
+/* Descriptor idn for Query requests */
937
+enum desc_idn {
938
+ QUERY_DESC_IDN_DEVICE = 0x0,
939
+ QUERY_DESC_IDN_CONFIGURATION = 0x1,
940
+ QUERY_DESC_IDN_UNIT = 0x2,
941
+ QUERY_DESC_IDN_RFU_0 = 0x3,
942
+ QUERY_DESC_IDN_INTERCONNECT = 0x4,
943
+ QUERY_DESC_IDN_STRING = 0x5,
944
+ QUERY_DESC_IDN_RFU_1 = 0x6,
945
+ QUERY_DESC_IDN_GEOMETRY = 0x7,
946
+ QUERY_DESC_IDN_POWER = 0x8,
947
+ QUERY_DESC_IDN_HEALTH = 0x9,
948
+ QUERY_DESC_IDN_MAX,
949
+};
950
+
951
+enum desc_header_offset {
952
+ QUERY_DESC_LENGTH_OFFSET = 0x00,
953
+ QUERY_DESC_DESC_TYPE_OFFSET = 0x01,
954
+};
955
+
956
+/* Unit descriptor parameters offsets in bytes*/
957
+enum unit_desc_param {
958
+ UNIT_DESC_PARAM_LEN = 0x0,
959
+ UNIT_DESC_PARAM_TYPE = 0x1,
960
+ UNIT_DESC_PARAM_UNIT_INDEX = 0x2,
961
+ UNIT_DESC_PARAM_LU_ENABLE = 0x3,
962
+ UNIT_DESC_PARAM_BOOT_LUN_ID = 0x4,
963
+ UNIT_DESC_PARAM_LU_WR_PROTECT = 0x5,
964
+ UNIT_DESC_PARAM_LU_Q_DEPTH = 0x6,
965
+ UNIT_DESC_PARAM_PSA_SENSITIVE = 0x7,
966
+ UNIT_DESC_PARAM_MEM_TYPE = 0x8,
967
+ UNIT_DESC_PARAM_DATA_RELIABILITY = 0x9,
968
+ UNIT_DESC_PARAM_LOGICAL_BLK_SIZE = 0xA,
969
+ UNIT_DESC_PARAM_LOGICAL_BLK_COUNT = 0xB,
970
+ UNIT_DESC_PARAM_ERASE_BLK_SIZE = 0x13,
971
+ UNIT_DESC_PARAM_PROVISIONING_TYPE = 0x17,
972
+ UNIT_DESC_PARAM_PHY_MEM_RSRC_CNT = 0x18,
973
+ UNIT_DESC_PARAM_CTX_CAPABILITIES = 0x20,
974
+ UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1 = 0x22,
975
+ UNIT_DESC_PARAM_HPB_LU_MAX_ACTIVE_RGNS = 0x23,
976
+ UNIT_DESC_PARAM_HPB_PIN_RGN_START_OFF = 0x25,
977
+ UNIT_DESC_PARAM_HPB_NUM_PIN_RGNS = 0x27,
978
+ UNIT_DESC_PARAM_WB_BUF_ALLOC_UNITS = 0x29,
979
+};
980
+
981
+/* RPMB Unit descriptor parameters offsets in bytes*/
982
+enum rpmb_unit_desc_param {
983
+ RPMB_UNIT_DESC_PARAM_LEN = 0x0,
984
+ RPMB_UNIT_DESC_PARAM_TYPE = 0x1,
985
+ RPMB_UNIT_DESC_PARAM_UNIT_INDEX = 0x2,
986
+ RPMB_UNIT_DESC_PARAM_LU_ENABLE = 0x3,
987
+ RPMB_UNIT_DESC_PARAM_BOOT_LUN_ID = 0x4,
988
+ RPMB_UNIT_DESC_PARAM_LU_WR_PROTECT = 0x5,
989
+ RPMB_UNIT_DESC_PARAM_LU_Q_DEPTH = 0x6,
990
+ RPMB_UNIT_DESC_PARAM_PSA_SENSITIVE = 0x7,
991
+ RPMB_UNIT_DESC_PARAM_MEM_TYPE = 0x8,
992
+ RPMB_UNIT_DESC_PARAM_REGION_EN = 0x9,
993
+ RPMB_UNIT_DESC_PARAM_LOGICAL_BLK_SIZE = 0xA,
994
+ RPMB_UNIT_DESC_PARAM_LOGICAL_BLK_COUNT = 0xB,
995
+ RPMB_UNIT_DESC_PARAM_REGION0_SIZE = 0x13,
996
+ RPMB_UNIT_DESC_PARAM_REGION1_SIZE = 0x14,
997
+ RPMB_UNIT_DESC_PARAM_REGION2_SIZE = 0x15,
998
+ RPMB_UNIT_DESC_PARAM_REGION3_SIZE = 0x16,
999
+ RPMB_UNIT_DESC_PARAM_PROVISIONING_TYPE = 0x17,
1000
+ RPMB_UNIT_DESC_PARAM_PHY_MEM_RSRC_CNT = 0x18,
1001
+};
1002
+
1003
+/* Device descriptor parameters offsets in bytes*/
1004
+enum device_desc_param {
1005
+ DEVICE_DESC_PARAM_LEN = 0x0,
1006
+ DEVICE_DESC_PARAM_TYPE = 0x1,
1007
+ DEVICE_DESC_PARAM_DEVICE_TYPE = 0x2,
1008
+ DEVICE_DESC_PARAM_DEVICE_CLASS = 0x3,
1009
+ DEVICE_DESC_PARAM_DEVICE_SUB_CLASS = 0x4,
1010
+ DEVICE_DESC_PARAM_PRTCL = 0x5,
1011
+ DEVICE_DESC_PARAM_NUM_LU = 0x6,
1012
+ DEVICE_DESC_PARAM_NUM_WLU = 0x7,
1013
+ DEVICE_DESC_PARAM_BOOT_ENBL = 0x8,
1014
+ DEVICE_DESC_PARAM_DESC_ACCSS_ENBL = 0x9,
1015
+ DEVICE_DESC_PARAM_INIT_PWR_MODE = 0xA,
1016
+ DEVICE_DESC_PARAM_HIGH_PR_LUN = 0xB,
1017
+ DEVICE_DESC_PARAM_SEC_RMV_TYPE = 0xC,
1018
+ DEVICE_DESC_PARAM_SEC_LU = 0xD,
1019
+ DEVICE_DESC_PARAM_BKOP_TERM_LT = 0xE,
1020
+ DEVICE_DESC_PARAM_ACTVE_ICC_LVL = 0xF,
1021
+ DEVICE_DESC_PARAM_SPEC_VER = 0x10,
1022
+ DEVICE_DESC_PARAM_MANF_DATE = 0x12,
1023
+ DEVICE_DESC_PARAM_MANF_NAME = 0x14,
1024
+ DEVICE_DESC_PARAM_PRDCT_NAME = 0x15,
1025
+ DEVICE_DESC_PARAM_SN = 0x16,
1026
+ DEVICE_DESC_PARAM_OEM_ID = 0x17,
1027
+ DEVICE_DESC_PARAM_MANF_ID = 0x18,
1028
+ DEVICE_DESC_PARAM_UD_OFFSET = 0x1A,
1029
+ DEVICE_DESC_PARAM_UD_LEN = 0x1B,
1030
+ DEVICE_DESC_PARAM_RTT_CAP = 0x1C,
1031
+ DEVICE_DESC_PARAM_FRQ_RTC = 0x1D,
1032
+ DEVICE_DESC_PARAM_UFS_FEAT = 0x1F,
1033
+ DEVICE_DESC_PARAM_FFU_TMT = 0x20,
1034
+ DEVICE_DESC_PARAM_Q_DPTH = 0x21,
1035
+ DEVICE_DESC_PARAM_DEV_VER = 0x22,
1036
+ DEVICE_DESC_PARAM_NUM_SEC_WPA = 0x24,
1037
+ DEVICE_DESC_PARAM_PSA_MAX_DATA = 0x25,
1038
+ DEVICE_DESC_PARAM_PSA_TMT = 0x29,
1039
+ DEVICE_DESC_PARAM_PRDCT_REV = 0x2A,
1040
+ DEVICE_DESC_PARAM_HPB_VER = 0x40,
1041
+ DEVICE_DESC_PARAM_HPB_CONTROL = 0x42,
1042
+ DEVICE_DESC_PARAM_EXT_UFS_FEATURE_SUP = 0x4F,
1043
+ DEVICE_DESC_PARAM_WB_PRESRV_USRSPC_EN = 0x53,
1044
+ DEVICE_DESC_PARAM_WB_TYPE = 0x54,
1045
+ DEVICE_DESC_PARAM_WB_SHARED_ALLOC_UNITS = 0x55,
1046
+};
1047
+
1048
+/* Interconnect descriptor parameters offsets in bytes*/
1049
+enum interconnect_desc_param {
1050
+ INTERCONNECT_DESC_PARAM_LEN = 0x0,
1051
+ INTERCONNECT_DESC_PARAM_TYPE = 0x1,
1052
+ INTERCONNECT_DESC_PARAM_UNIPRO_VER = 0x2,
1053
+ INTERCONNECT_DESC_PARAM_MPHY_VER = 0x4,
1054
+};
1055
+
1056
+/* Geometry descriptor parameters offsets in bytes*/
1057
+enum geometry_desc_param {
1058
+ GEOMETRY_DESC_PARAM_LEN = 0x0,
1059
+ GEOMETRY_DESC_PARAM_TYPE = 0x1,
1060
+ GEOMETRY_DESC_PARAM_DEV_CAP = 0x4,
1061
+ GEOMETRY_DESC_PARAM_MAX_NUM_LUN = 0xC,
1062
+ GEOMETRY_DESC_PARAM_SEG_SIZE = 0xD,
1063
+ GEOMETRY_DESC_PARAM_ALLOC_UNIT_SIZE = 0x11,
1064
+ GEOMETRY_DESC_PARAM_MIN_BLK_SIZE = 0x12,
1065
+ GEOMETRY_DESC_PARAM_OPT_RD_BLK_SIZE = 0x13,
1066
+ GEOMETRY_DESC_PARAM_OPT_WR_BLK_SIZE = 0x14,
1067
+ GEOMETRY_DESC_PARAM_MAX_IN_BUF_SIZE = 0x15,
1068
+ GEOMETRY_DESC_PARAM_MAX_OUT_BUF_SIZE = 0x16,
1069
+ GEOMETRY_DESC_PARAM_RPMB_RW_SIZE = 0x17,
1070
+ GEOMETRY_DESC_PARAM_DYN_CAP_RSRC_PLC = 0x18,
1071
+ GEOMETRY_DESC_PARAM_DATA_ORDER = 0x19,
1072
+ GEOMETRY_DESC_PARAM_MAX_NUM_CTX = 0x1A,
1073
+ GEOMETRY_DESC_PARAM_TAG_UNIT_SIZE = 0x1B,
1074
+ GEOMETRY_DESC_PARAM_TAG_RSRC_SIZE = 0x1C,
1075
+ GEOMETRY_DESC_PARAM_SEC_RM_TYPES = 0x1D,
1076
+ GEOMETRY_DESC_PARAM_MEM_TYPES = 0x1E,
1077
+ GEOMETRY_DESC_PARAM_SCM_MAX_NUM_UNITS = 0x20,
1078
+ GEOMETRY_DESC_PARAM_SCM_CAP_ADJ_FCTR = 0x24,
1079
+ GEOMETRY_DESC_PARAM_NPM_MAX_NUM_UNITS = 0x26,
1080
+ GEOMETRY_DESC_PARAM_NPM_CAP_ADJ_FCTR = 0x2A,
1081
+ GEOMETRY_DESC_PARAM_ENM1_MAX_NUM_UNITS = 0x2C,
1082
+ GEOMETRY_DESC_PARAM_ENM1_CAP_ADJ_FCTR = 0x30,
1083
+ GEOMETRY_DESC_PARAM_ENM2_MAX_NUM_UNITS = 0x32,
1084
+ GEOMETRY_DESC_PARAM_ENM2_CAP_ADJ_FCTR = 0x36,
1085
+ GEOMETRY_DESC_PARAM_ENM3_MAX_NUM_UNITS = 0x38,
1086
+ GEOMETRY_DESC_PARAM_ENM3_CAP_ADJ_FCTR = 0x3C,
1087
+ GEOMETRY_DESC_PARAM_ENM4_MAX_NUM_UNITS = 0x3E,
1088
+ GEOMETRY_DESC_PARAM_ENM4_CAP_ADJ_FCTR = 0x42,
1089
+ GEOMETRY_DESC_PARAM_OPT_LOG_BLK_SIZE = 0x44,
1090
+ GEOMETRY_DESC_PARAM_HPB_REGION_SIZE = 0x48,
1091
+ GEOMETRY_DESC_PARAM_HPB_NUMBER_LU = 0x49,
1092
+ GEOMETRY_DESC_PARAM_HPB_SUBREGION_SIZE = 0x4A,
1093
+ GEOMETRY_DESC_PARAM_HPB_MAX_ACTIVE_REGS = 0x4B,
1094
+ GEOMETRY_DESC_PARAM_WB_MAX_ALLOC_UNITS = 0x4F,
1095
+ GEOMETRY_DESC_PARAM_WB_MAX_WB_LUNS = 0x53,
1096
+ GEOMETRY_DESC_PARAM_WB_BUFF_CAP_ADJ = 0x54,
1097
+ GEOMETRY_DESC_PARAM_WB_SUP_RED_TYPE = 0x55,
1098
+ GEOMETRY_DESC_PARAM_WB_SUP_WB_TYPE = 0x56,
1099
+};
1100
+
1101
+/* Health descriptor parameters offsets in bytes*/
1102
+enum health_desc_param {
1103
+ HEALTH_DESC_PARAM_LEN = 0x0,
1104
+ HEALTH_DESC_PARAM_TYPE = 0x1,
1105
+ HEALTH_DESC_PARAM_EOL_INFO = 0x2,
1106
+ HEALTH_DESC_PARAM_LIFE_TIME_EST_A = 0x3,
1107
+ HEALTH_DESC_PARAM_LIFE_TIME_EST_B = 0x4,
1108
+};
1109
+
1110
+/* WriteBooster buffer mode */
1111
+enum {
1112
+ WB_BUF_MODE_LU_DEDICATED = 0x0,
1113
+ WB_BUF_MODE_SHARED = 0x1,
1114
+};
1115
+
1116
+/*
1117
+ * Logical Unit Write Protect
1118
+ * 00h: LU not write protected
1119
+ * 01h: LU write protected when fPowerOnWPEn =1
1120
+ * 02h: LU permanently write protected when fPermanentWPEn =1
1121
+ */
1122
+enum ufs_lu_wp_type {
1123
+ UFS_LU_NO_WP = 0x00,
1124
+ UFS_LU_POWER_ON_WP = 0x01,
1125
+ UFS_LU_PERM_WP = 0x02,
1126
+};
1127
+
1128
+/* UTP QUERY Transaction Specific Fields OpCode */
1129
+enum query_opcode {
1130
+ UPIU_QUERY_OPCODE_NOP = 0x0,
1131
+ UPIU_QUERY_OPCODE_READ_DESC = 0x1,
1132
+ UPIU_QUERY_OPCODE_WRITE_DESC = 0x2,
1133
+ UPIU_QUERY_OPCODE_READ_ATTR = 0x3,
1134
+ UPIU_QUERY_OPCODE_WRITE_ATTR = 0x4,
1135
+ UPIU_QUERY_OPCODE_READ_FLAG = 0x5,
1136
+ UPIU_QUERY_OPCODE_SET_FLAG = 0x6,
1137
+ UPIU_QUERY_OPCODE_CLEAR_FLAG = 0x7,
1138
+ UPIU_QUERY_OPCODE_TOGGLE_FLAG = 0x8,
1139
+};
1140
+
1141
+/* Query response result code */
1142
+typedef enum QueryRespCode {
1143
+ QUERY_RESULT_SUCCESS = 0x00,
1144
+ QUERY_RESULT_NOT_READABLE = 0xF6,
1145
+ QUERY_RESULT_NOT_WRITEABLE = 0xF7,
1146
+ QUERY_RESULT_ALREADY_WRITTEN = 0xF8,
1147
+ QUERY_RESULT_INVALID_LENGTH = 0xF9,
1148
+ QUERY_RESULT_INVALID_VALUE = 0xFA,
1149
+ QUERY_RESULT_INVALID_SELECTOR = 0xFB,
1150
+ QUERY_RESULT_INVALID_INDEX = 0xFC,
1151
+ QUERY_RESULT_INVALID_IDN = 0xFD,
1152
+ QUERY_RESULT_INVALID_OPCODE = 0xFE,
1153
+ QUERY_RESULT_GENERAL_FAILURE = 0xFF,
1154
+} QueryRespCode;
1155
+
1156
+/* UTP Transfer Request Command Type (CT) */
1157
+enum {
1158
+ UPIU_COMMAND_SET_TYPE_SCSI = 0x0,
1159
+ UPIU_COMMAND_SET_TYPE_UFS = 0x1,
1160
+ UPIU_COMMAND_SET_TYPE_QUERY = 0x2,
1161
+};
1162
+
1163
+/* Task management service response */
1164
+enum {
1165
+ UPIU_TASK_MANAGEMENT_FUNC_COMPL = 0x00,
1166
+ UPIU_TASK_MANAGEMENT_FUNC_NOT_SUPPORTED = 0x04,
1167
+ UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED = 0x08,
1168
+ UPIU_TASK_MANAGEMENT_FUNC_FAILED = 0x05,
1169
+ UPIU_INCORRECT_LOGICAL_UNIT_NO = 0x09,
1170
+};
1171
+
1172
+/* UFS device power modes */
1173
+enum ufs_dev_pwr_mode {
1174
+ UFS_ACTIVE_PWR_MODE = 1,
1175
+ UFS_SLEEP_PWR_MODE = 2,
1176
+ UFS_POWERDOWN_PWR_MODE = 3,
1177
+ UFS_DEEPSLEEP_PWR_MODE = 4,
1178
+};
1179
+
1180
+/*
1181
+ * struct UtpCmdRsp - Response UPIU structure
1182
+ * @residual_transfer_count: Residual transfer count DW-3
1183
+ * @reserved: Reserved double words DW-4 to DW-7
1184
+ * @sense_data_len: Sense data length DW-8 U16
1185
+ * @sense_data: Sense data field DW-8 to DW-12
1186
+ */
1187
+typedef struct QEMU_PACKED UtpCmdRsp {
1188
+ uint32_t residual_transfer_count;
1189
+ uint32_t reserved[4];
1190
+ uint16_t sense_data_len;
1191
+ uint8_t sense_data[UFS_SENSE_SIZE];
1192
+} UtpCmdRsp;
1193
+
1194
+/*
1195
+ * struct UtpUpiuRsp - general upiu response structure
1196
+ * @header: UPIU header structure DW-0 to DW-2
1197
+ * @sr: fields structure for scsi command DW-3 to DW-12
1198
+ * @qr: fields structure for query request DW-3 to DW-7
1199
+ */
1200
+typedef struct QEMU_PACKED UtpUpiuRsp {
1201
+ UtpUpiuHeader header;
1202
+ union {
1203
+ UtpCmdRsp sr;
1204
+ UtpUpiuQuery qr;
1205
+ };
1206
+} UtpUpiuRsp;
1207
+
1208
+static inline void _ufs_check_size(void)
1209
+{
1210
+ QEMU_BUILD_BUG_ON(sizeof(UfsReg) != 0x104);
1211
+ QEMU_BUILD_BUG_ON(sizeof(DeviceDescriptor) != 89);
1212
+ QEMU_BUILD_BUG_ON(sizeof(GeometryDescriptor) != 87);
1213
+ QEMU_BUILD_BUG_ON(sizeof(UnitDescriptor) != 45);
1214
+ QEMU_BUILD_BUG_ON(sizeof(RpmbUnitDescriptor) != 35);
1215
+ QEMU_BUILD_BUG_ON(sizeof(PowerParametersDescriptor) != 98);
1216
+ QEMU_BUILD_BUG_ON(sizeof(InterconnectDescriptor) != 6);
1217
+ QEMU_BUILD_BUG_ON(sizeof(StringDescriptor) != 254);
1218
+ QEMU_BUILD_BUG_ON(sizeof(DeviceHealthDescriptor) != 45);
1219
+ QEMU_BUILD_BUG_ON(sizeof(Flags) != 0x13);
1220
+ QEMU_BUILD_BUG_ON(sizeof(UtpUpiuHeader) != 12);
1221
+ QEMU_BUILD_BUG_ON(sizeof(UtpUpiuQuery) != 276);
1222
+ QEMU_BUILD_BUG_ON(sizeof(UtpUpiuCmd) != 20);
1223
+ QEMU_BUILD_BUG_ON(sizeof(UtpUpiuReq) != 288);
1224
+ QEMU_BUILD_BUG_ON(sizeof(UfshcdSgEntry) != 16);
1225
+ QEMU_BUILD_BUG_ON(sizeof(RequestDescHeader) != 16);
1226
+ QEMU_BUILD_BUG_ON(sizeof(UtpTransferReqDesc) != 32);
1227
+ QEMU_BUILD_BUG_ON(sizeof(UtpTaskReqDesc) != 80);
1228
+ QEMU_BUILD_BUG_ON(sizeof(UtpCmdRsp) != 40);
1229
+ QEMU_BUILD_BUG_ON(sizeof(UtpUpiuRsp) != 288);
1230
+}
1231
+#endif
1232
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
130
index XXXXXXX..XXXXXXX 100644
1233
index XXXXXXX..XXXXXXX 100644
131
--- a/tests/qemu-iotests/group
1234
--- a/include/hw/pci/pci.h
132
+++ b/tests/qemu-iotests/group
1235
+++ b/include/hw/pci/pci.h
1236
@@ -XXX,XX +XXX,XX @@ extern bool pci_available;
1237
#define PCI_DEVICE_ID_REDHAT_NVME 0x0010
1238
#define PCI_DEVICE_ID_REDHAT_PVPANIC 0x0011
1239
#define PCI_DEVICE_ID_REDHAT_ACPI_ERST 0x0012
1240
+#define PCI_DEVICE_ID_REDHAT_UFS 0x0013
1241
#define PCI_DEVICE_ID_REDHAT_QXL 0x0100
1242
1243
#define FMT_PCIBUS PRIx64
1244
diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h
1245
index XXXXXXX..XXXXXXX 100644
1246
--- a/include/hw/pci/pci_ids.h
1247
+++ b/include/hw/pci/pci_ids.h
133
@@ -XXX,XX +XXX,XX @@
1248
@@ -XXX,XX +XXX,XX @@
134
279 rw backing quick
1249
#define PCI_CLASS_STORAGE_SATA 0x0106
135
280 rw migration quick
1250
#define PCI_CLASS_STORAGE_SAS 0x0107
136
281 rw quick
1251
#define PCI_CLASS_STORAGE_EXPRESS 0x0108
137
+283 auto quick
1252
+#define PCI_CLASS_STORAGE_UFS 0x0109
1253
#define PCI_CLASS_STORAGE_OTHER 0x0180
1254
1255
#define PCI_BASE_CLASS_NETWORK 0x02
1256
diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c
1257
new file mode 100644
1258
index XXXXXXX..XXXXXXX
1259
--- /dev/null
1260
+++ b/hw/ufs/ufs.c
1261
@@ -XXX,XX +XXX,XX @@
1262
+/*
1263
+ * QEMU Universal Flash Storage (UFS) Controller
1264
+ *
1265
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved.
1266
+ *
1267
+ * Written by Jeuk Kim <jeuk20.kim@samsung.com>
1268
+ *
1269
+ * SPDX-License-Identifier: GPL-2.0-or-later
1270
+ */
1271
+
1272
+#include "qemu/osdep.h"
1273
+#include "qapi/error.h"
1274
+#include "migration/vmstate.h"
1275
+#include "trace.h"
1276
+#include "ufs.h"
1277
+
1278
+/* The QEMU-UFS device follows spec version 3.1 */
1279
+#define UFS_SPEC_VER 0x00000310
1280
+#define UFS_MAX_NUTRS 32
1281
+#define UFS_MAX_NUTMRS 8
1282
+
1283
+static void ufs_irq_check(UfsHc *u)
1284
+{
1285
+ PCIDevice *pci = PCI_DEVICE(u);
1286
+
1287
+ if ((u->reg.is & UFS_INTR_MASK) & u->reg.ie) {
1288
+ trace_ufs_irq_raise();
1289
+ pci_irq_assert(pci);
1290
+ } else {
1291
+ trace_ufs_irq_lower();
1292
+ pci_irq_deassert(pci);
1293
+ }
1294
+}
1295
+
1296
+static void ufs_process_uiccmd(UfsHc *u, uint32_t val)
1297
+{
1298
+ trace_ufs_process_uiccmd(val, u->reg.ucmdarg1, u->reg.ucmdarg2,
1299
+ u->reg.ucmdarg3);
1300
+ /*
1301
+ * Only the essential uic commands for running drivers on Linux and Windows
1302
+ * are implemented.
1303
+ */
1304
+ switch (val) {
1305
+ case UIC_CMD_DME_LINK_STARTUP:
1306
+ u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, DP, 1);
1307
+ u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, UTRLRDY, 1);
1308
+ u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, UTMRLRDY, 1);
1309
+ u->reg.ucmdarg2 = UIC_CMD_RESULT_SUCCESS;
1310
+ break;
1311
+ /* TODO: Revisit it when Power Management is implemented */
1312
+ case UIC_CMD_DME_HIBER_ENTER:
1313
+ u->reg.is = FIELD_DP32(u->reg.is, IS, UHES, 1);
1314
+ u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, UPMCRS, PWR_LOCAL);
1315
+ u->reg.ucmdarg2 = UIC_CMD_RESULT_SUCCESS;
1316
+ break;
1317
+ case UIC_CMD_DME_HIBER_EXIT:
1318
+ u->reg.is = FIELD_DP32(u->reg.is, IS, UHXS, 1);
1319
+ u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, UPMCRS, PWR_LOCAL);
1320
+ u->reg.ucmdarg2 = UIC_CMD_RESULT_SUCCESS;
1321
+ break;
1322
+ default:
1323
+ u->reg.ucmdarg2 = UIC_CMD_RESULT_FAILURE;
1324
+ }
1325
+
1326
+ u->reg.is = FIELD_DP32(u->reg.is, IS, UCCS, 1);
1327
+
1328
+ ufs_irq_check(u);
1329
+}
1330
+
1331
+static void ufs_write_reg(UfsHc *u, hwaddr offset, uint32_t data, unsigned size)
1332
+{
1333
+ switch (offset) {
1334
+ case A_IS:
1335
+ u->reg.is &= ~data;
1336
+ ufs_irq_check(u);
1337
+ break;
1338
+ case A_IE:
1339
+ u->reg.ie = data;
1340
+ ufs_irq_check(u);
1341
+ break;
1342
+ case A_HCE:
1343
+ if (!FIELD_EX32(u->reg.hce, HCE, HCE) && FIELD_EX32(data, HCE, HCE)) {
1344
+ u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, UCRDY, 1);
1345
+ u->reg.hce = FIELD_DP32(u->reg.hce, HCE, HCE, 1);
1346
+ } else if (FIELD_EX32(u->reg.hce, HCE, HCE) &&
1347
+ !FIELD_EX32(data, HCE, HCE)) {
1348
+ u->reg.hcs = 0;
1349
+ u->reg.hce = FIELD_DP32(u->reg.hce, HCE, HCE, 0);
1350
+ }
1351
+ break;
1352
+ case A_UTRLBA:
1353
+ u->reg.utrlba = data & R_UTRLBA_UTRLBA_MASK;
1354
+ break;
1355
+ case A_UTRLBAU:
1356
+ u->reg.utrlbau = data;
1357
+ break;
1358
+ case A_UTRLDBR:
1359
+ /* Not yet supported */
1360
+ break;
1361
+ case A_UTRLRSR:
1362
+ u->reg.utrlrsr = data;
1363
+ break;
1364
+ case A_UTRLCNR:
1365
+ u->reg.utrlcnr &= ~data;
1366
+ break;
1367
+ case A_UTMRLBA:
1368
+ u->reg.utmrlba = data & R_UTMRLBA_UTMRLBA_MASK;
1369
+ break;
1370
+ case A_UTMRLBAU:
1371
+ u->reg.utmrlbau = data;
1372
+ break;
1373
+ case A_UICCMD:
1374
+ ufs_process_uiccmd(u, data);
1375
+ break;
1376
+ case A_UCMDARG1:
1377
+ u->reg.ucmdarg1 = data;
1378
+ break;
1379
+ case A_UCMDARG2:
1380
+ u->reg.ucmdarg2 = data;
1381
+ break;
1382
+ case A_UCMDARG3:
1383
+ u->reg.ucmdarg3 = data;
1384
+ break;
1385
+ case A_UTRLCLR:
1386
+ case A_UTMRLDBR:
1387
+ case A_UTMRLCLR:
1388
+ case A_UTMRLRSR:
1389
+ trace_ufs_err_unsupport_register_offset(offset);
1390
+ break;
1391
+ default:
1392
+ trace_ufs_err_invalid_register_offset(offset);
1393
+ break;
1394
+ }
1395
+}
1396
+
1397
+static uint64_t ufs_mmio_read(void *opaque, hwaddr addr, unsigned size)
1398
+{
1399
+ UfsHc *u = (UfsHc *)opaque;
1400
+ uint8_t *ptr = (uint8_t *)&u->reg;
1401
+ uint64_t value;
1402
+
1403
+ if (addr > sizeof(u->reg) - size) {
1404
+ trace_ufs_err_invalid_register_offset(addr);
1405
+ return 0;
1406
+ }
1407
+
1408
+ value = *(uint32_t *)(ptr + addr);
1409
+ trace_ufs_mmio_read(addr, value, size);
1410
+ return value;
1411
+}
1412
+
1413
+static void ufs_mmio_write(void *opaque, hwaddr addr, uint64_t data,
1414
+ unsigned size)
1415
+{
1416
+ UfsHc *u = (UfsHc *)opaque;
1417
+
1418
+ if (addr > sizeof(u->reg) - size) {
1419
+ trace_ufs_err_invalid_register_offset(addr);
1420
+ return;
1421
+ }
1422
+
1423
+ trace_ufs_mmio_write(addr, data, size);
1424
+ ufs_write_reg(u, addr, data, size);
1425
+}
1426
+
1427
+static const MemoryRegionOps ufs_mmio_ops = {
1428
+ .read = ufs_mmio_read,
1429
+ .write = ufs_mmio_write,
1430
+ .endianness = DEVICE_LITTLE_ENDIAN,
1431
+ .impl = {
1432
+ .min_access_size = 4,
1433
+ .max_access_size = 4,
1434
+ },
1435
+};
1436
+
1437
+static bool ufs_check_constraints(UfsHc *u, Error **errp)
1438
+{
1439
+ if (u->params.nutrs > UFS_MAX_NUTRS) {
1440
+ error_setg(errp, "nutrs must be less than or equal to %d",
1441
+ UFS_MAX_NUTRS);
1442
+ return false;
1443
+ }
1444
+
1445
+ if (u->params.nutmrs > UFS_MAX_NUTMRS) {
1446
+ error_setg(errp, "nutmrs must be less than or equal to %d",
1447
+ UFS_MAX_NUTMRS);
1448
+ return false;
1449
+ }
1450
+
1451
+ return true;
1452
+}
1453
+
1454
+static void ufs_init_pci(UfsHc *u, PCIDevice *pci_dev)
1455
+{
1456
+ uint8_t *pci_conf = pci_dev->config;
1457
+
1458
+ pci_conf[PCI_INTERRUPT_PIN] = 1;
1459
+ pci_config_set_prog_interface(pci_conf, 0x1);
1460
+
1461
+ memory_region_init_io(&u->iomem, OBJECT(u), &ufs_mmio_ops, u, "ufs",
1462
+ u->reg_size);
1463
+ pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &u->iomem);
1464
+ u->irq = pci_allocate_irq(pci_dev);
1465
+}
1466
+
1467
+static void ufs_init_hc(UfsHc *u)
1468
+{
1469
+ uint32_t cap = 0;
1470
+
1471
+ u->reg_size = pow2ceil(sizeof(UfsReg));
1472
+
1473
+ memset(&u->reg, 0, sizeof(u->reg));
1474
+ cap = FIELD_DP32(cap, CAP, NUTRS, (u->params.nutrs - 1));
1475
+ cap = FIELD_DP32(cap, CAP, RTT, 2);
1476
+ cap = FIELD_DP32(cap, CAP, NUTMRS, (u->params.nutmrs - 1));
1477
+ cap = FIELD_DP32(cap, CAP, AUTOH8, 0);
1478
+ cap = FIELD_DP32(cap, CAP, 64AS, 1);
1479
+ cap = FIELD_DP32(cap, CAP, OODDS, 0);
1480
+ cap = FIELD_DP32(cap, CAP, UICDMETMS, 0);
1481
+ cap = FIELD_DP32(cap, CAP, CS, 0);
1482
+ u->reg.cap = cap;
1483
+ u->reg.ver = UFS_SPEC_VER;
1484
+}
1485
+
1486
+static void ufs_realize(PCIDevice *pci_dev, Error **errp)
1487
+{
1488
+ UfsHc *u = UFS(pci_dev);
1489
+
1490
+ if (!ufs_check_constraints(u, errp)) {
1491
+ return;
1492
+ }
1493
+
1494
+ ufs_init_hc(u);
1495
+ ufs_init_pci(u, pci_dev);
1496
+}
1497
+
1498
+static Property ufs_props[] = {
1499
+ DEFINE_PROP_STRING("serial", UfsHc, params.serial),
1500
+ DEFINE_PROP_UINT8("nutrs", UfsHc, params.nutrs, 32),
1501
+ DEFINE_PROP_UINT8("nutmrs", UfsHc, params.nutmrs, 8),
1502
+ DEFINE_PROP_END_OF_LIST(),
1503
+};
1504
+
1505
+static const VMStateDescription ufs_vmstate = {
1506
+ .name = "ufs",
1507
+ .unmigratable = 1,
1508
+};
1509
+
1510
+static void ufs_class_init(ObjectClass *oc, void *data)
1511
+{
1512
+ DeviceClass *dc = DEVICE_CLASS(oc);
1513
+ PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
1514
+
1515
+ pc->realize = ufs_realize;
1516
+ pc->vendor_id = PCI_VENDOR_ID_REDHAT;
1517
+ pc->device_id = PCI_DEVICE_ID_REDHAT_UFS;
1518
+ pc->class_id = PCI_CLASS_STORAGE_UFS;
1519
+
1520
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
1521
+ dc->desc = "Universal Flash Storage";
1522
+ device_class_set_props(dc, ufs_props);
1523
+ dc->vmsd = &ufs_vmstate;
1524
+}
1525
+
1526
+static const TypeInfo ufs_info = {
1527
+ .name = TYPE_UFS,
1528
+ .parent = TYPE_PCI_DEVICE,
1529
+ .class_init = ufs_class_init,
1530
+ .instance_size = sizeof(UfsHc),
1531
+ .interfaces = (InterfaceInfo[]){ { INTERFACE_PCIE_DEVICE }, {} },
1532
+};
1533
+
1534
+static void ufs_register_types(void)
1535
+{
1536
+ type_register_static(&ufs_info);
1537
+}
1538
+
1539
+type_init(ufs_register_types)
1540
diff --git a/hw/Kconfig b/hw/Kconfig
1541
index XXXXXXX..XXXXXXX 100644
1542
--- a/hw/Kconfig
1543
+++ b/hw/Kconfig
1544
@@ -XXX,XX +XXX,XX @@ source smbios/Kconfig
1545
source ssi/Kconfig
1546
source timer/Kconfig
1547
source tpm/Kconfig
1548
+source ufs/Kconfig
1549
source usb/Kconfig
1550
source virtio/Kconfig
1551
source vfio/Kconfig
1552
diff --git a/hw/meson.build b/hw/meson.build
1553
index XXXXXXX..XXXXXXX 100644
1554
--- a/hw/meson.build
1555
+++ b/hw/meson.build
1556
@@ -XXX,XX +XXX,XX @@ subdir('smbios')
1557
subdir('ssi')
1558
subdir('timer')
1559
subdir('tpm')
1560
+subdir('ufs')
1561
subdir('usb')
1562
subdir('vfio')
1563
subdir('virtio')
1564
diff --git a/hw/ufs/Kconfig b/hw/ufs/Kconfig
1565
new file mode 100644
1566
index XXXXXXX..XXXXXXX
1567
--- /dev/null
1568
+++ b/hw/ufs/Kconfig
1569
@@ -XXX,XX +XXX,XX @@
1570
+config UFS_PCI
1571
+ bool
1572
+ default y if PCI_DEVICES
1573
+ depends on PCI
1574
diff --git a/hw/ufs/meson.build b/hw/ufs/meson.build
1575
new file mode 100644
1576
index XXXXXXX..XXXXXXX
1577
--- /dev/null
1578
+++ b/hw/ufs/meson.build
1579
@@ -0,0 +1 @@
1580
+system_ss.add(when: 'CONFIG_UFS_PCI', if_true: files('ufs.c'))
1581
diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events
1582
new file mode 100644
1583
index XXXXXXX..XXXXXXX
1584
--- /dev/null
1585
+++ b/hw/ufs/trace-events
1586
@@ -XXX,XX +XXX,XX @@
1587
+# ufs.c
1588
+ufs_irq_raise(void) "INTx"
1589
+ufs_irq_lower(void) "INTx"
1590
+ufs_mmio_read(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d"
1591
+ufs_mmio_write(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d"
1592
+ufs_process_db(uint32_t slot) "UTRLDBR slot %"PRIu32""
1593
+ufs_process_req(uint32_t slot) "UTRLDBR slot %"PRIu32""
1594
+ufs_complete_req(uint32_t slot) "UTRLDBR slot %"PRIu32""
1595
+ufs_sendback_req(uint32_t slot) "UTRLDBR slot %"PRIu32""
1596
+ufs_exec_nop_cmd(uint32_t slot) "UTRLDBR slot %"PRIu32""
1597
+ufs_exec_scsi_cmd(uint32_t slot, uint8_t lun, uint8_t opcode) "slot %"PRIu32", lun 0x%"PRIx8", opcode 0x%"PRIx8""
1598
+ufs_exec_query_cmd(uint32_t slot, uint8_t opcode) "slot %"PRIu32", opcode 0x%"PRIx8""
1599
+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""
1600
+
1601
+# error condition
1602
+ufs_err_dma_read_utrd(uint32_t slot, uint64_t addr) "failed to read utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64""
1603
+ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu. UTRLDBR slot %"PRIu32", request upiu addr %"PRIu64""
1604
+ufs_err_dma_read_prdt(uint32_t slot, uint64_t addr) "failed to read prdt. UTRLDBR slot %"PRIu32", prdt addr %"PRIu64""
1605
+ufs_err_dma_write_utrd(uint32_t slot, uint64_t addr) "failed to write utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64""
1606
+ufs_err_dma_write_rsp_upiu(uint32_t slot, uint64_t addr) "failed to write rsp upiu. UTRLDBR slot %"PRIu32", response upiu addr %"PRIu64""
1607
+ufs_err_utrl_slot_busy(uint32_t slot) "UTRLDBR slot %"PRIu32" is busy"
1608
+ufs_err_unsupport_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is not yet supported"
1609
+ufs_err_invalid_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is invalid"
1610
+ufs_err_scsi_cmd_invalid_lun(uint8_t lun) "scsi command has invalid lun: 0x%"PRIx8""
1611
+ufs_err_query_flag_not_readable(uint8_t idn) "query flag idn 0x%"PRIx8" is denied to read"
1612
+ufs_err_query_flag_not_writable(uint8_t idn) "query flag idn 0x%"PRIx8" is denied to write"
1613
+ufs_err_query_attr_not_readable(uint8_t idn) "query attribute idn 0x%"PRIx8" is denied to read"
1614
+ufs_err_query_attr_not_writable(uint8_t idn) "query attribute idn 0x%"PRIx8" is denied to write"
1615
+ufs_err_query_invalid_opcode(uint8_t opcode) "query request has invalid opcode. opcode: 0x%"PRIx8""
1616
+ufs_err_query_invalid_idn(uint8_t opcode, uint8_t idn) "query request has invalid idn. opcode: 0x%"PRIx8", idn 0x%"PRIx8""
1617
+ufs_err_query_invalid_index(uint8_t opcode, uint8_t index) "query request has invalid index. opcode: 0x%"PRIx8", index 0x%"PRIx8""
1618
+ufs_err_invalid_trans_code(uint32_t slot, uint8_t trans_code) "request upiu has invalid transaction code. slot: %"PRIu32", trans_code: 0x%"PRIx8""
138
--
1619
--
139
2.24.1
1620
2.41.0
140
141
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Jeuk Kim <jeuk20.kim@gmail.com>
2
2
3
When updating an L1 entry the qcow2 driver writes a (512-byte) sector
3
This commit makes the UFS device support query
4
worth of data to avoid a read-modify-write cycle. Instead of always
4
and nop out transfer requests.
5
writing 512 bytes we should follow the alignment requirements of the
6
storage backend.
7
5
8
(the only exception is when the alignment is larger than the cluster
6
The next patch would be support for UFS logical
9
size because then we could be overwriting data after the L1 table)
7
unit and scsi command transfer request.
10
8
11
Signed-off-by: Alberto Garcia <berto@igalia.com>
9
Signed-off-by: Jeuk Kim <jeuk20.kim@samsung.com>
12
Message-id: 71f34d4ae4b367b32fb36134acbf4f4f7ee681f4.1579374329.git.berto@igalia.com
10
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
13
Reviewed-by: Max Reitz <mreitz@redhat.com>
11
Message-id: d06b440d660872092f70af1b8167bd5f4704c957.1691062912.git.jeuk20.kim@samsung.com
14
Signed-off-by: Max Reitz <mreitz@redhat.com>
12
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
15
---
13
---
16
block/qcow2-cluster.c | 25 +++++++++++++++----------
14
hw/ufs/ufs.h | 46 +++
17
1 file changed, 15 insertions(+), 10 deletions(-)
15
hw/ufs/ufs.c | 980 +++++++++++++++++++++++++++++++++++++++++++-
16
hw/ufs/trace-events | 1 +
17
3 files changed, 1025 insertions(+), 2 deletions(-)
18
18
19
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
19
diff --git a/hw/ufs/ufs.h b/hw/ufs/ufs.h
20
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
21
--- a/block/qcow2-cluster.c
21
--- a/hw/ufs/ufs.h
22
+++ b/block/qcow2-cluster.c
22
+++ b/hw/ufs/ufs.h
23
@@ -XXX,XX +XXX,XX @@ static int l2_load(BlockDriverState *bs, uint64_t offset,
23
@@ -XXX,XX +XXX,XX @@
24
#define UFS_MAX_LUS 32
25
#define UFS_BLOCK_SIZE 4096
26
27
+typedef enum UfsRequestState {
28
+ UFS_REQUEST_IDLE = 0,
29
+ UFS_REQUEST_READY = 1,
30
+ UFS_REQUEST_RUNNING = 2,
31
+ UFS_REQUEST_COMPLETE = 3,
32
+ UFS_REQUEST_ERROR = 4,
33
+} UfsRequestState;
34
+
35
+typedef enum UfsReqResult {
36
+ UFS_REQUEST_SUCCESS = 0,
37
+ UFS_REQUEST_FAIL = 1,
38
+} UfsReqResult;
39
+
40
+typedef struct UfsRequest {
41
+ struct UfsHc *hc;
42
+ UfsRequestState state;
43
+ int slot;
44
+
45
+ UtpTransferReqDesc utrd;
46
+ UtpUpiuReq req_upiu;
47
+ UtpUpiuRsp rsp_upiu;
48
+
49
+ /* for scsi command */
50
+ QEMUSGList *sg;
51
+} UfsRequest;
52
+
53
typedef struct UfsParams {
54
char *serial;
55
uint8_t nutrs; /* Number of UTP Transfer Request Slots */
56
@@ -XXX,XX +XXX,XX @@ typedef struct UfsHc {
57
UfsReg reg;
58
UfsParams params;
59
uint32_t reg_size;
60
+ UfsRequest *req_list;
61
+
62
+ DeviceDescriptor device_desc;
63
+ GeometryDescriptor geometry_desc;
64
+ Attributes attributes;
65
+ Flags flags;
66
67
qemu_irq irq;
68
QEMUBH *doorbell_bh;
69
@@ -XXX,XX +XXX,XX @@ typedef struct UfsHc {
70
#define TYPE_UFS "ufs"
71
#define UFS(obj) OBJECT_CHECK(UfsHc, (obj), TYPE_UFS)
72
73
+typedef enum UfsQueryFlagPerm {
74
+ UFS_QUERY_FLAG_NONE = 0x0,
75
+ UFS_QUERY_FLAG_READ = 0x1,
76
+ UFS_QUERY_FLAG_SET = 0x2,
77
+ UFS_QUERY_FLAG_CLEAR = 0x4,
78
+ UFS_QUERY_FLAG_TOGGLE = 0x8,
79
+} UfsQueryFlagPerm;
80
+
81
+typedef enum UfsQueryAttrPerm {
82
+ UFS_QUERY_ATTR_NONE = 0x0,
83
+ UFS_QUERY_ATTR_READ = 0x1,
84
+ UFS_QUERY_ATTR_WRITE = 0x2,
85
+} UfsQueryAttrPerm;
86
+
87
#endif /* HW_UFS_UFS_H */
88
diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c
89
index XXXXXXX..XXXXXXX 100644
90
--- a/hw/ufs/ufs.c
91
+++ b/hw/ufs/ufs.c
92
@@ -XXX,XX +XXX,XX @@
93
#include "ufs.h"
94
95
/* The QEMU-UFS device follows spec version 3.1 */
96
-#define UFS_SPEC_VER 0x00000310
97
+#define UFS_SPEC_VER 0x0310
98
#define UFS_MAX_NUTRS 32
99
#define UFS_MAX_NUTMRS 8
100
101
+static MemTxResult ufs_addr_read(UfsHc *u, hwaddr addr, void *buf, int size)
102
+{
103
+ hwaddr hi = addr + size - 1;
104
+
105
+ if (hi < addr) {
106
+ return MEMTX_DECODE_ERROR;
107
+ }
108
+
109
+ if (!FIELD_EX32(u->reg.cap, CAP, 64AS) && (hi >> 32)) {
110
+ return MEMTX_DECODE_ERROR;
111
+ }
112
+
113
+ return pci_dma_read(PCI_DEVICE(u), addr, buf, size);
114
+}
115
+
116
+static MemTxResult ufs_addr_write(UfsHc *u, hwaddr addr, const void *buf,
117
+ int size)
118
+{
119
+ hwaddr hi = addr + size - 1;
120
+ if (hi < addr) {
121
+ return MEMTX_DECODE_ERROR;
122
+ }
123
+
124
+ if (!FIELD_EX32(u->reg.cap, CAP, 64AS) && (hi >> 32)) {
125
+ return MEMTX_DECODE_ERROR;
126
+ }
127
+
128
+ return pci_dma_write(PCI_DEVICE(u), addr, buf, size);
129
+}
130
+
131
+static void ufs_complete_req(UfsRequest *req, UfsReqResult req_result);
132
+
133
+static inline hwaddr ufs_get_utrd_addr(UfsHc *u, uint32_t slot)
134
+{
135
+ hwaddr utrl_base_addr = (((hwaddr)u->reg.utrlbau) << 32) + u->reg.utrlba;
136
+ hwaddr utrd_addr = utrl_base_addr + slot * sizeof(UtpTransferReqDesc);
137
+
138
+ return utrd_addr;
139
+}
140
+
141
+static inline hwaddr ufs_get_req_upiu_base_addr(const UtpTransferReqDesc *utrd)
142
+{
143
+ uint32_t cmd_desc_base_addr_lo =
144
+ le32_to_cpu(utrd->command_desc_base_addr_lo);
145
+ uint32_t cmd_desc_base_addr_hi =
146
+ le32_to_cpu(utrd->command_desc_base_addr_hi);
147
+
148
+ return (((hwaddr)cmd_desc_base_addr_hi) << 32) + cmd_desc_base_addr_lo;
149
+}
150
+
151
+static inline hwaddr ufs_get_rsp_upiu_base_addr(const UtpTransferReqDesc *utrd)
152
+{
153
+ hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(utrd);
154
+ uint32_t rsp_upiu_byte_off =
155
+ le16_to_cpu(utrd->response_upiu_offset) * sizeof(uint32_t);
156
+ return req_upiu_base_addr + rsp_upiu_byte_off;
157
+}
158
+
159
+static MemTxResult ufs_dma_read_utrd(UfsRequest *req)
160
+{
161
+ UfsHc *u = req->hc;
162
+ hwaddr utrd_addr = ufs_get_utrd_addr(u, req->slot);
163
+ MemTxResult ret;
164
+
165
+ ret = ufs_addr_read(u, utrd_addr, &req->utrd, sizeof(req->utrd));
166
+ if (ret) {
167
+ trace_ufs_err_dma_read_utrd(req->slot, utrd_addr);
168
+ }
169
+ return ret;
170
+}
171
+
172
+static MemTxResult ufs_dma_read_req_upiu(UfsRequest *req)
173
+{
174
+ UfsHc *u = req->hc;
175
+ hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(&req->utrd);
176
+ UtpUpiuReq *req_upiu = &req->req_upiu;
177
+ uint32_t copy_size;
178
+ uint16_t data_segment_length;
179
+ MemTxResult ret;
180
+
181
+ /*
182
+ * To know the size of the req_upiu, we need to read the
183
+ * data_segment_length in the header first.
184
+ */
185
+ ret = ufs_addr_read(u, req_upiu_base_addr, &req_upiu->header,
186
+ sizeof(UtpUpiuHeader));
187
+ if (ret) {
188
+ trace_ufs_err_dma_read_req_upiu(req->slot, req_upiu_base_addr);
189
+ return ret;
190
+ }
191
+ data_segment_length = be16_to_cpu(req_upiu->header.data_segment_length);
192
+
193
+ copy_size = sizeof(UtpUpiuHeader) + UFS_TRANSACTION_SPECIFIC_FIELD_SIZE +
194
+ data_segment_length;
195
+
196
+ ret = ufs_addr_read(u, req_upiu_base_addr, &req->req_upiu, copy_size);
197
+ if (ret) {
198
+ trace_ufs_err_dma_read_req_upiu(req->slot, req_upiu_base_addr);
199
+ }
200
+ return ret;
201
+}
202
+
203
+static MemTxResult ufs_dma_read_prdt(UfsRequest *req)
204
+{
205
+ UfsHc *u = req->hc;
206
+ uint16_t prdt_len = le16_to_cpu(req->utrd.prd_table_length);
207
+ uint16_t prdt_byte_off =
208
+ le16_to_cpu(req->utrd.prd_table_offset) * sizeof(uint32_t);
209
+ uint32_t prdt_size = prdt_len * sizeof(UfshcdSgEntry);
210
+ g_autofree UfshcdSgEntry *prd_entries = NULL;
211
+ hwaddr req_upiu_base_addr, prdt_base_addr;
212
+ int err;
213
+
214
+ assert(!req->sg);
215
+
216
+ if (prdt_size == 0) {
217
+ return MEMTX_OK;
218
+ }
219
+ prd_entries = g_new(UfshcdSgEntry, prdt_size);
220
+
221
+ req_upiu_base_addr = ufs_get_req_upiu_base_addr(&req->utrd);
222
+ prdt_base_addr = req_upiu_base_addr + prdt_byte_off;
223
+
224
+ err = ufs_addr_read(u, prdt_base_addr, prd_entries, prdt_size);
225
+ if (err) {
226
+ trace_ufs_err_dma_read_prdt(req->slot, prdt_base_addr);
227
+ return err;
228
+ }
229
+
230
+ req->sg = g_malloc0(sizeof(QEMUSGList));
231
+ pci_dma_sglist_init(req->sg, PCI_DEVICE(u), prdt_len);
232
+
233
+ for (uint16_t i = 0; i < prdt_len; ++i) {
234
+ hwaddr data_dma_addr = le64_to_cpu(prd_entries[i].addr);
235
+ uint32_t data_byte_count = le32_to_cpu(prd_entries[i].size) + 1;
236
+ qemu_sglist_add(req->sg, data_dma_addr, data_byte_count);
237
+ }
238
+ return MEMTX_OK;
239
+}
240
+
241
+static MemTxResult ufs_dma_read_upiu(UfsRequest *req)
242
+{
243
+ MemTxResult ret;
244
+
245
+ ret = ufs_dma_read_utrd(req);
246
+ if (ret) {
247
+ return ret;
248
+ }
249
+
250
+ ret = ufs_dma_read_req_upiu(req);
251
+ if (ret) {
252
+ return ret;
253
+ }
254
+
255
+ ret = ufs_dma_read_prdt(req);
256
+ if (ret) {
257
+ return ret;
258
+ }
259
+
260
+ return 0;
261
+}
262
+
263
+static MemTxResult ufs_dma_write_utrd(UfsRequest *req)
264
+{
265
+ UfsHc *u = req->hc;
266
+ hwaddr utrd_addr = ufs_get_utrd_addr(u, req->slot);
267
+ MemTxResult ret;
268
+
269
+ ret = ufs_addr_write(u, utrd_addr, &req->utrd, sizeof(req->utrd));
270
+ if (ret) {
271
+ trace_ufs_err_dma_write_utrd(req->slot, utrd_addr);
272
+ }
273
+ return ret;
274
+}
275
+
276
+static MemTxResult ufs_dma_write_rsp_upiu(UfsRequest *req)
277
+{
278
+ UfsHc *u = req->hc;
279
+ hwaddr rsp_upiu_base_addr = ufs_get_rsp_upiu_base_addr(&req->utrd);
280
+ uint32_t rsp_upiu_byte_len =
281
+ le16_to_cpu(req->utrd.response_upiu_length) * sizeof(uint32_t);
282
+ uint16_t data_segment_length =
283
+ be16_to_cpu(req->rsp_upiu.header.data_segment_length);
284
+ uint32_t copy_size = sizeof(UtpUpiuHeader) +
285
+ UFS_TRANSACTION_SPECIFIC_FIELD_SIZE +
286
+ data_segment_length;
287
+ MemTxResult ret;
288
+
289
+ if (copy_size > rsp_upiu_byte_len) {
290
+ copy_size = rsp_upiu_byte_len;
291
+ }
292
+
293
+ ret = ufs_addr_write(u, rsp_upiu_base_addr, &req->rsp_upiu, copy_size);
294
+ if (ret) {
295
+ trace_ufs_err_dma_write_rsp_upiu(req->slot, rsp_upiu_base_addr);
296
+ }
297
+ return ret;
298
+}
299
+
300
+static MemTxResult ufs_dma_write_upiu(UfsRequest *req)
301
+{
302
+ MemTxResult ret;
303
+
304
+ ret = ufs_dma_write_rsp_upiu(req);
305
+ if (ret) {
306
+ return ret;
307
+ }
308
+
309
+ return ufs_dma_write_utrd(req);
310
+}
311
+
312
static void ufs_irq_check(UfsHc *u)
313
{
314
PCIDevice *pci = PCI_DEVICE(u);
315
@@ -XXX,XX +XXX,XX @@ static void ufs_irq_check(UfsHc *u)
316
}
24
}
317
}
25
318
26
/*
319
+static void ufs_process_db(UfsHc *u, uint32_t val)
27
- * Writes one sector of the L1 table to the disk (can't update single entries
320
+{
28
- * and we really don't want bdrv_pread to perform a read-modify-write)
321
+ uint32_t slot;
29
+ * Writes an L1 entry to disk (note that depending on the alignment
322
+ uint32_t nutrs = u->params.nutrs;
30
+ * requirements this function may write more that just one entry in
323
+ UfsRequest *req;
31
+ * order to prevent bdrv_pwrite from performing a read-modify-write)
324
+
32
*/
325
+ val &= ~u->reg.utrldbr;
33
-#define L1_ENTRIES_PER_SECTOR (512 / 8)
326
+ if (!val) {
34
int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index)
327
+ return;
328
+ }
329
+
330
+ slot = find_first_bit((unsigned long *)&val, nutrs);
331
+
332
+ while (slot < nutrs) {
333
+ req = &u->req_list[slot];
334
+ if (req->state == UFS_REQUEST_ERROR) {
335
+ trace_ufs_err_utrl_slot_error(req->slot);
336
+ return;
337
+ }
338
+
339
+ if (req->state != UFS_REQUEST_IDLE) {
340
+ trace_ufs_err_utrl_slot_busy(req->slot);
341
+ return;
342
+ }
343
+
344
+ trace_ufs_process_db(slot);
345
+ req->state = UFS_REQUEST_READY;
346
+ slot = find_next_bit((unsigned long *)&val, nutrs, slot + 1);
347
+ }
348
+
349
+ qemu_bh_schedule(u->doorbell_bh);
350
+}
351
+
352
static void ufs_process_uiccmd(UfsHc *u, uint32_t val)
35
{
353
{
36
BDRVQcow2State *s = bs->opaque;
354
trace_ufs_process_uiccmd(val, u->reg.ucmdarg1, u->reg.ucmdarg2,
37
- uint64_t buf[L1_ENTRIES_PER_SECTOR] = { 0 };
355
@@ -XXX,XX +XXX,XX @@ static void ufs_write_reg(UfsHc *u, hwaddr offset, uint32_t data, unsigned size)
38
int l1_start_index;
356
u->reg.utrlbau = data;
39
int i, ret;
357
break;
40
+ int bufsize = MAX(sizeof(uint64_t),
358
case A_UTRLDBR:
41
+ MIN(bs->file->bs->bl.request_alignment, s->cluster_size));
359
- /* Not yet supported */
42
+ int nentries = bufsize / sizeof(uint64_t);
360
+ ufs_process_db(u, data);
43
+ g_autofree uint64_t *buf = g_try_new0(uint64_t, nentries);
361
+ u->reg.utrldbr |= data;
44
362
break;
45
- l1_start_index = l1_index & ~(L1_ENTRIES_PER_SECTOR - 1);
363
case A_UTRLRSR:
46
- for (i = 0; i < L1_ENTRIES_PER_SECTOR && l1_start_index + i < s->l1_size;
364
u->reg.utrlrsr = data;
47
- i++)
365
@@ -XXX,XX +XXX,XX @@ static const MemoryRegionOps ufs_mmio_ops = {
48
- {
366
},
49
+ if (buf == NULL) {
367
};
50
+ return -ENOMEM;
368
51
+ }
369
+static void ufs_build_upiu_header(UfsRequest *req, uint8_t trans_type,
52
+
370
+ uint8_t flags, uint8_t response,
53
+ l1_start_index = QEMU_ALIGN_DOWN(l1_index, nentries);
371
+ uint8_t scsi_status,
54
+ for (i = 0; i < MIN(nentries, s->l1_size - l1_start_index); i++) {
372
+ uint16_t data_segment_length)
55
buf[i] = cpu_to_be64(s->l1_table[l1_start_index + i]);
373
+{
374
+ memcpy(&req->rsp_upiu.header, &req->req_upiu.header, sizeof(UtpUpiuHeader));
375
+ req->rsp_upiu.header.trans_type = trans_type;
376
+ req->rsp_upiu.header.flags = flags;
377
+ req->rsp_upiu.header.response = response;
378
+ req->rsp_upiu.header.scsi_status = scsi_status;
379
+ req->rsp_upiu.header.data_segment_length = cpu_to_be16(data_segment_length);
380
+}
381
+
382
+static UfsReqResult ufs_exec_nop_cmd(UfsRequest *req)
383
+{
384
+ trace_ufs_exec_nop_cmd(req->slot);
385
+ ufs_build_upiu_header(req, UPIU_TRANSACTION_NOP_IN, 0, 0, 0, 0);
386
+ return UFS_REQUEST_SUCCESS;
387
+}
388
+
389
+/*
390
+ * This defines the permission of flags based on their IDN. There are some
391
+ * things that are declared read-only, which is inconsistent with the ufs spec,
392
+ * because we want to return an error for features that are not yet supported.
393
+ */
394
+static const int flag_permission[QUERY_FLAG_IDN_COUNT] = {
395
+ [QUERY_FLAG_IDN_FDEVICEINIT] = UFS_QUERY_FLAG_READ | UFS_QUERY_FLAG_SET,
396
+ /* Write protection is not supported */
397
+ [QUERY_FLAG_IDN_PERMANENT_WPE] = UFS_QUERY_FLAG_READ,
398
+ [QUERY_FLAG_IDN_PWR_ON_WPE] = UFS_QUERY_FLAG_READ,
399
+ [QUERY_FLAG_IDN_BKOPS_EN] = UFS_QUERY_FLAG_READ | UFS_QUERY_FLAG_SET |
400
+ UFS_QUERY_FLAG_CLEAR | UFS_QUERY_FLAG_TOGGLE,
401
+ [QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE] =
402
+ UFS_QUERY_FLAG_READ | UFS_QUERY_FLAG_SET | UFS_QUERY_FLAG_CLEAR |
403
+ UFS_QUERY_FLAG_TOGGLE,
404
+ /* Purge Operation is not supported */
405
+ [QUERY_FLAG_IDN_PURGE_ENABLE] = UFS_QUERY_FLAG_NONE,
406
+ /* Refresh Operation is not supported */
407
+ [QUERY_FLAG_IDN_REFRESH_ENABLE] = UFS_QUERY_FLAG_NONE,
408
+ /* Physical Resource Removal is not supported */
409
+ [QUERY_FLAG_IDN_FPHYRESOURCEREMOVAL] = UFS_QUERY_FLAG_READ,
410
+ [QUERY_FLAG_IDN_BUSY_RTC] = UFS_QUERY_FLAG_READ,
411
+ [QUERY_FLAG_IDN_PERMANENTLY_DISABLE_FW_UPDATE] = UFS_QUERY_FLAG_READ,
412
+ /* Write Booster is not supported */
413
+ [QUERY_FLAG_IDN_WB_EN] = UFS_QUERY_FLAG_READ,
414
+ [QUERY_FLAG_IDN_WB_BUFF_FLUSH_EN] = UFS_QUERY_FLAG_READ,
415
+ [QUERY_FLAG_IDN_WB_BUFF_FLUSH_DURING_HIBERN8] = UFS_QUERY_FLAG_READ,
416
+};
417
+
418
+static inline QueryRespCode ufs_flag_check_idn_valid(uint8_t idn, int op)
419
+{
420
+ if (idn >= QUERY_FLAG_IDN_COUNT) {
421
+ return QUERY_RESULT_INVALID_IDN;
422
+ }
423
+
424
+ if (!(flag_permission[idn] & op)) {
425
+ if (op == UFS_QUERY_FLAG_READ) {
426
+ trace_ufs_err_query_flag_not_readable(idn);
427
+ return QUERY_RESULT_NOT_READABLE;
428
+ }
429
+ trace_ufs_err_query_flag_not_writable(idn);
430
+ return QUERY_RESULT_NOT_WRITEABLE;
431
+ }
432
+
433
+ return QUERY_RESULT_SUCCESS;
434
+}
435
+
436
+static const int attr_permission[QUERY_ATTR_IDN_COUNT] = {
437
+ /* booting is not supported */
438
+ [QUERY_ATTR_IDN_BOOT_LU_EN] = UFS_QUERY_ATTR_READ,
439
+ [QUERY_ATTR_IDN_POWER_MODE] = UFS_QUERY_ATTR_READ,
440
+ [QUERY_ATTR_IDN_ACTIVE_ICC_LVL] =
441
+ UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
442
+ [QUERY_ATTR_IDN_OOO_DATA_EN] = UFS_QUERY_ATTR_READ,
443
+ [QUERY_ATTR_IDN_BKOPS_STATUS] = UFS_QUERY_ATTR_READ,
444
+ [QUERY_ATTR_IDN_PURGE_STATUS] = UFS_QUERY_ATTR_READ,
445
+ [QUERY_ATTR_IDN_MAX_DATA_IN] = UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
446
+ [QUERY_ATTR_IDN_MAX_DATA_OUT] = UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
447
+ [QUERY_ATTR_IDN_DYN_CAP_NEEDED] = UFS_QUERY_ATTR_READ,
448
+ [QUERY_ATTR_IDN_REF_CLK_FREQ] = UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
449
+ [QUERY_ATTR_IDN_CONF_DESC_LOCK] = UFS_QUERY_ATTR_READ,
450
+ [QUERY_ATTR_IDN_MAX_NUM_OF_RTT] =
451
+ UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
452
+ [QUERY_ATTR_IDN_EE_CONTROL] = UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
453
+ [QUERY_ATTR_IDN_EE_STATUS] = UFS_QUERY_ATTR_READ,
454
+ [QUERY_ATTR_IDN_SECONDS_PASSED] = UFS_QUERY_ATTR_WRITE,
455
+ [QUERY_ATTR_IDN_CNTX_CONF] = UFS_QUERY_ATTR_READ,
456
+ [QUERY_ATTR_IDN_FFU_STATUS] = UFS_QUERY_ATTR_READ,
457
+ [QUERY_ATTR_IDN_PSA_STATE] = UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
458
+ [QUERY_ATTR_IDN_PSA_DATA_SIZE] = UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
459
+ [QUERY_ATTR_IDN_REF_CLK_GATING_WAIT_TIME] = UFS_QUERY_ATTR_READ,
460
+ [QUERY_ATTR_IDN_CASE_ROUGH_TEMP] = UFS_QUERY_ATTR_READ,
461
+ [QUERY_ATTR_IDN_HIGH_TEMP_BOUND] = UFS_QUERY_ATTR_READ,
462
+ [QUERY_ATTR_IDN_LOW_TEMP_BOUND] = UFS_QUERY_ATTR_READ,
463
+ [QUERY_ATTR_IDN_THROTTLING_STATUS] = UFS_QUERY_ATTR_READ,
464
+ [QUERY_ATTR_IDN_WB_FLUSH_STATUS] = UFS_QUERY_ATTR_READ,
465
+ [QUERY_ATTR_IDN_AVAIL_WB_BUFF_SIZE] = UFS_QUERY_ATTR_READ,
466
+ [QUERY_ATTR_IDN_WB_BUFF_LIFE_TIME_EST] = UFS_QUERY_ATTR_READ,
467
+ [QUERY_ATTR_IDN_CURR_WB_BUFF_SIZE] = UFS_QUERY_ATTR_READ,
468
+ /* refresh operation is not supported */
469
+ [QUERY_ATTR_IDN_REFRESH_STATUS] = UFS_QUERY_ATTR_READ,
470
+ [QUERY_ATTR_IDN_REFRESH_FREQ] = UFS_QUERY_ATTR_READ,
471
+ [QUERY_ATTR_IDN_REFRESH_UNIT] = UFS_QUERY_ATTR_READ,
472
+};
473
+
474
+static inline QueryRespCode ufs_attr_check_idn_valid(uint8_t idn, int op)
475
+{
476
+ if (idn >= QUERY_ATTR_IDN_COUNT) {
477
+ return QUERY_RESULT_INVALID_IDN;
478
+ }
479
+
480
+ if (!(attr_permission[idn] & op)) {
481
+ if (op == UFS_QUERY_ATTR_READ) {
482
+ trace_ufs_err_query_attr_not_readable(idn);
483
+ return QUERY_RESULT_NOT_READABLE;
484
+ }
485
+ trace_ufs_err_query_attr_not_writable(idn);
486
+ return QUERY_RESULT_NOT_WRITEABLE;
487
+ }
488
+
489
+ return QUERY_RESULT_SUCCESS;
490
+}
491
+
492
+static QueryRespCode ufs_exec_query_flag(UfsRequest *req, int op)
493
+{
494
+ UfsHc *u = req->hc;
495
+ uint8_t idn = req->req_upiu.qr.idn;
496
+ uint32_t value;
497
+ QueryRespCode ret;
498
+
499
+ ret = ufs_flag_check_idn_valid(idn, op);
500
+ if (ret) {
501
+ return ret;
502
+ }
503
+
504
+ if (idn == QUERY_FLAG_IDN_FDEVICEINIT) {
505
+ value = 0;
506
+ } else if (op == UFS_QUERY_FLAG_READ) {
507
+ value = *(((uint8_t *)&u->flags) + idn);
508
+ } else if (op == UFS_QUERY_FLAG_SET) {
509
+ value = 1;
510
+ } else if (op == UFS_QUERY_FLAG_CLEAR) {
511
+ value = 0;
512
+ } else if (op == UFS_QUERY_FLAG_TOGGLE) {
513
+ value = *(((uint8_t *)&u->flags) + idn);
514
+ value = !value;
515
+ } else {
516
+ trace_ufs_err_query_invalid_opcode(op);
517
+ return QUERY_RESULT_INVALID_OPCODE;
518
+ }
519
+
520
+ *(((uint8_t *)&u->flags) + idn) = value;
521
+ req->rsp_upiu.qr.value = cpu_to_be32(value);
522
+ return QUERY_RESULT_SUCCESS;
523
+}
524
+
525
+static uint32_t ufs_read_attr_value(UfsHc *u, uint8_t idn)
526
+{
527
+ switch (idn) {
528
+ case QUERY_ATTR_IDN_BOOT_LU_EN:
529
+ return u->attributes.boot_lun_en;
530
+ case QUERY_ATTR_IDN_POWER_MODE:
531
+ return u->attributes.current_power_mode;
532
+ case QUERY_ATTR_IDN_ACTIVE_ICC_LVL:
533
+ return u->attributes.active_icc_level;
534
+ case QUERY_ATTR_IDN_OOO_DATA_EN:
535
+ return u->attributes.out_of_order_data_en;
536
+ case QUERY_ATTR_IDN_BKOPS_STATUS:
537
+ return u->attributes.background_op_status;
538
+ case QUERY_ATTR_IDN_PURGE_STATUS:
539
+ return u->attributes.purge_status;
540
+ case QUERY_ATTR_IDN_MAX_DATA_IN:
541
+ return u->attributes.max_data_in_size;
542
+ case QUERY_ATTR_IDN_MAX_DATA_OUT:
543
+ return u->attributes.max_data_out_size;
544
+ case QUERY_ATTR_IDN_DYN_CAP_NEEDED:
545
+ return be32_to_cpu(u->attributes.dyn_cap_needed);
546
+ case QUERY_ATTR_IDN_REF_CLK_FREQ:
547
+ return u->attributes.ref_clk_freq;
548
+ case QUERY_ATTR_IDN_CONF_DESC_LOCK:
549
+ return u->attributes.config_descr_lock;
550
+ case QUERY_ATTR_IDN_MAX_NUM_OF_RTT:
551
+ return u->attributes.max_num_of_rtt;
552
+ case QUERY_ATTR_IDN_EE_CONTROL:
553
+ return be16_to_cpu(u->attributes.exception_event_control);
554
+ case QUERY_ATTR_IDN_EE_STATUS:
555
+ return be16_to_cpu(u->attributes.exception_event_status);
556
+ case QUERY_ATTR_IDN_SECONDS_PASSED:
557
+ return be32_to_cpu(u->attributes.seconds_passed);
558
+ case QUERY_ATTR_IDN_CNTX_CONF:
559
+ return be16_to_cpu(u->attributes.context_conf);
560
+ case QUERY_ATTR_IDN_FFU_STATUS:
561
+ return u->attributes.device_ffu_status;
562
+ case QUERY_ATTR_IDN_PSA_STATE:
563
+ return be32_to_cpu(u->attributes.psa_state);
564
+ case QUERY_ATTR_IDN_PSA_DATA_SIZE:
565
+ return be32_to_cpu(u->attributes.psa_data_size);
566
+ case QUERY_ATTR_IDN_REF_CLK_GATING_WAIT_TIME:
567
+ return u->attributes.ref_clk_gating_wait_time;
568
+ case QUERY_ATTR_IDN_CASE_ROUGH_TEMP:
569
+ return u->attributes.device_case_rough_temperaure;
570
+ case QUERY_ATTR_IDN_HIGH_TEMP_BOUND:
571
+ return u->attributes.device_too_high_temp_boundary;
572
+ case QUERY_ATTR_IDN_LOW_TEMP_BOUND:
573
+ return u->attributes.device_too_low_temp_boundary;
574
+ case QUERY_ATTR_IDN_THROTTLING_STATUS:
575
+ return u->attributes.throttling_status;
576
+ case QUERY_ATTR_IDN_WB_FLUSH_STATUS:
577
+ return u->attributes.wb_buffer_flush_status;
578
+ case QUERY_ATTR_IDN_AVAIL_WB_BUFF_SIZE:
579
+ return u->attributes.available_wb_buffer_size;
580
+ case QUERY_ATTR_IDN_WB_BUFF_LIFE_TIME_EST:
581
+ return u->attributes.wb_buffer_life_time_est;
582
+ case QUERY_ATTR_IDN_CURR_WB_BUFF_SIZE:
583
+ return be32_to_cpu(u->attributes.current_wb_buffer_size);
584
+ case QUERY_ATTR_IDN_REFRESH_STATUS:
585
+ return u->attributes.refresh_status;
586
+ case QUERY_ATTR_IDN_REFRESH_FREQ:
587
+ return u->attributes.refresh_freq;
588
+ case QUERY_ATTR_IDN_REFRESH_UNIT:
589
+ return u->attributes.refresh_unit;
590
+ }
591
+ return 0;
592
+}
593
+
594
+static void ufs_write_attr_value(UfsHc *u, uint8_t idn, uint32_t value)
595
+{
596
+ switch (idn) {
597
+ case QUERY_ATTR_IDN_ACTIVE_ICC_LVL:
598
+ u->attributes.active_icc_level = value;
599
+ break;
600
+ case QUERY_ATTR_IDN_MAX_DATA_IN:
601
+ u->attributes.max_data_in_size = value;
602
+ break;
603
+ case QUERY_ATTR_IDN_MAX_DATA_OUT:
604
+ u->attributes.max_data_out_size = value;
605
+ break;
606
+ case QUERY_ATTR_IDN_REF_CLK_FREQ:
607
+ u->attributes.ref_clk_freq = value;
608
+ break;
609
+ case QUERY_ATTR_IDN_MAX_NUM_OF_RTT:
610
+ u->attributes.max_num_of_rtt = value;
611
+ break;
612
+ case QUERY_ATTR_IDN_EE_CONTROL:
613
+ u->attributes.exception_event_control = cpu_to_be16(value);
614
+ break;
615
+ case QUERY_ATTR_IDN_SECONDS_PASSED:
616
+ u->attributes.seconds_passed = cpu_to_be32(value);
617
+ break;
618
+ case QUERY_ATTR_IDN_PSA_STATE:
619
+ u->attributes.psa_state = value;
620
+ break;
621
+ case QUERY_ATTR_IDN_PSA_DATA_SIZE:
622
+ u->attributes.psa_data_size = cpu_to_be32(value);
623
+ break;
624
+ }
625
+}
626
+
627
+static QueryRespCode ufs_exec_query_attr(UfsRequest *req, int op)
628
+{
629
+ UfsHc *u = req->hc;
630
+ uint8_t idn = req->req_upiu.qr.idn;
631
+ uint32_t value;
632
+ QueryRespCode ret;
633
+
634
+ ret = ufs_attr_check_idn_valid(idn, op);
635
+ if (ret) {
636
+ return ret;
637
+ }
638
+
639
+ if (op == UFS_QUERY_ATTR_READ) {
640
+ value = ufs_read_attr_value(u, idn);
641
+ } else {
642
+ value = be32_to_cpu(req->req_upiu.qr.value);
643
+ ufs_write_attr_value(u, idn, value);
644
+ }
645
+
646
+ req->rsp_upiu.qr.value = cpu_to_be32(value);
647
+ return QUERY_RESULT_SUCCESS;
648
+}
649
+
650
+static const RpmbUnitDescriptor rpmb_unit_desc = {
651
+ .length = sizeof(RpmbUnitDescriptor),
652
+ .descriptor_idn = 2,
653
+ .unit_index = UFS_UPIU_RPMB_WLUN,
654
+ .lu_enable = 0,
655
+};
656
+
657
+static QueryRespCode ufs_read_unit_desc(UfsRequest *req)
658
+{
659
+ uint8_t lun = req->req_upiu.qr.index;
660
+
661
+ if (lun != UFS_UPIU_RPMB_WLUN && lun > UFS_MAX_LUS) {
662
+ trace_ufs_err_query_invalid_index(req->req_upiu.qr.opcode, lun);
663
+ return QUERY_RESULT_INVALID_INDEX;
664
+ }
665
+
666
+ if (lun == UFS_UPIU_RPMB_WLUN) {
667
+ memcpy(&req->rsp_upiu.qr.data, &rpmb_unit_desc, rpmb_unit_desc.length);
668
+ } else {
669
+ /* unit descriptor is not yet supported */
670
+ return QUERY_RESULT_INVALID_INDEX;
671
+ }
672
+
673
+ return QUERY_RESULT_SUCCESS;
674
+}
675
+
676
+static inline StringDescriptor manufacturer_str_desc(void)
677
+{
678
+ StringDescriptor desc = {
679
+ .length = 0x12,
680
+ .descriptor_idn = QUERY_DESC_IDN_STRING,
681
+ };
682
+ desc.UC[0] = cpu_to_be16('R');
683
+ desc.UC[1] = cpu_to_be16('E');
684
+ desc.UC[2] = cpu_to_be16('D');
685
+ desc.UC[3] = cpu_to_be16('H');
686
+ desc.UC[4] = cpu_to_be16('A');
687
+ desc.UC[5] = cpu_to_be16('T');
688
+ return desc;
689
+}
690
+
691
+static inline StringDescriptor product_name_str_desc(void)
692
+{
693
+ StringDescriptor desc = {
694
+ .length = 0x22,
695
+ .descriptor_idn = QUERY_DESC_IDN_STRING,
696
+ };
697
+ desc.UC[0] = cpu_to_be16('Q');
698
+ desc.UC[1] = cpu_to_be16('E');
699
+ desc.UC[2] = cpu_to_be16('M');
700
+ desc.UC[3] = cpu_to_be16('U');
701
+ desc.UC[4] = cpu_to_be16(' ');
702
+ desc.UC[5] = cpu_to_be16('U');
703
+ desc.UC[6] = cpu_to_be16('F');
704
+ desc.UC[7] = cpu_to_be16('S');
705
+ return desc;
706
+}
707
+
708
+static inline StringDescriptor product_rev_level_str_desc(void)
709
+{
710
+ StringDescriptor desc = {
711
+ .length = 0x0a,
712
+ .descriptor_idn = QUERY_DESC_IDN_STRING,
713
+ };
714
+ desc.UC[0] = cpu_to_be16('0');
715
+ desc.UC[1] = cpu_to_be16('0');
716
+ desc.UC[2] = cpu_to_be16('0');
717
+ desc.UC[3] = cpu_to_be16('1');
718
+ return desc;
719
+}
720
+
721
+static const StringDescriptor null_str_desc = {
722
+ .length = 0x02,
723
+ .descriptor_idn = QUERY_DESC_IDN_STRING,
724
+};
725
+
726
+static QueryRespCode ufs_read_string_desc(UfsRequest *req)
727
+{
728
+ UfsHc *u = req->hc;
729
+ uint8_t index = req->req_upiu.qr.index;
730
+ StringDescriptor desc;
731
+
732
+ if (index == u->device_desc.manufacturer_name) {
733
+ desc = manufacturer_str_desc();
734
+ memcpy(&req->rsp_upiu.qr.data, &desc, desc.length);
735
+ } else if (index == u->device_desc.product_name) {
736
+ desc = product_name_str_desc();
737
+ memcpy(&req->rsp_upiu.qr.data, &desc, desc.length);
738
+ } else if (index == u->device_desc.serial_number) {
739
+ memcpy(&req->rsp_upiu.qr.data, &null_str_desc, null_str_desc.length);
740
+ } else if (index == u->device_desc.oem_id) {
741
+ memcpy(&req->rsp_upiu.qr.data, &null_str_desc, null_str_desc.length);
742
+ } else if (index == u->device_desc.product_revision_level) {
743
+ desc = product_rev_level_str_desc();
744
+ memcpy(&req->rsp_upiu.qr.data, &desc, desc.length);
745
+ } else {
746
+ trace_ufs_err_query_invalid_index(req->req_upiu.qr.opcode, index);
747
+ return QUERY_RESULT_INVALID_INDEX;
748
+ }
749
+ return QUERY_RESULT_SUCCESS;
750
+}
751
+
752
+static inline InterconnectDescriptor interconnect_desc(void)
753
+{
754
+ InterconnectDescriptor desc = {
755
+ .length = sizeof(InterconnectDescriptor),
756
+ .descriptor_idn = QUERY_DESC_IDN_INTERCONNECT,
757
+ };
758
+ desc.bcd_unipro_version = cpu_to_be16(0x180);
759
+ desc.bcd_mphy_version = cpu_to_be16(0x410);
760
+ return desc;
761
+}
762
+
763
+static QueryRespCode ufs_read_desc(UfsRequest *req)
764
+{
765
+ UfsHc *u = req->hc;
766
+ QueryRespCode status;
767
+ uint8_t idn = req->req_upiu.qr.idn;
768
+ uint16_t length = be16_to_cpu(req->req_upiu.qr.length);
769
+ InterconnectDescriptor desc;
770
+
771
+ switch (idn) {
772
+ case QUERY_DESC_IDN_DEVICE:
773
+ memcpy(&req->rsp_upiu.qr.data, &u->device_desc, sizeof(u->device_desc));
774
+ status = QUERY_RESULT_SUCCESS;
775
+ break;
776
+ case QUERY_DESC_IDN_UNIT:
777
+ status = ufs_read_unit_desc(req);
778
+ break;
779
+ case QUERY_DESC_IDN_GEOMETRY:
780
+ memcpy(&req->rsp_upiu.qr.data, &u->geometry_desc,
781
+ sizeof(u->geometry_desc));
782
+ status = QUERY_RESULT_SUCCESS;
783
+ break;
784
+ case QUERY_DESC_IDN_INTERCONNECT: {
785
+ desc = interconnect_desc();
786
+ memcpy(&req->rsp_upiu.qr.data, &desc, sizeof(InterconnectDescriptor));
787
+ status = QUERY_RESULT_SUCCESS;
788
+ break;
789
+ }
790
+ case QUERY_DESC_IDN_STRING:
791
+ status = ufs_read_string_desc(req);
792
+ break;
793
+ case QUERY_DESC_IDN_POWER:
794
+ /* mocking of power descriptor is not supported */
795
+ memset(&req->rsp_upiu.qr.data, 0, sizeof(PowerParametersDescriptor));
796
+ req->rsp_upiu.qr.data[0] = sizeof(PowerParametersDescriptor);
797
+ req->rsp_upiu.qr.data[1] = QUERY_DESC_IDN_POWER;
798
+ status = QUERY_RESULT_SUCCESS;
799
+ break;
800
+ case QUERY_DESC_IDN_HEALTH:
801
+ /* mocking of health descriptor is not supported */
802
+ memset(&req->rsp_upiu.qr.data, 0, sizeof(DeviceHealthDescriptor));
803
+ req->rsp_upiu.qr.data[0] = sizeof(DeviceHealthDescriptor);
804
+ req->rsp_upiu.qr.data[1] = QUERY_DESC_IDN_HEALTH;
805
+ status = QUERY_RESULT_SUCCESS;
806
+ break;
807
+ default:
808
+ length = 0;
809
+ trace_ufs_err_query_invalid_idn(req->req_upiu.qr.opcode, idn);
810
+ status = QUERY_RESULT_INVALID_IDN;
811
+ }
812
+
813
+ if (length > req->rsp_upiu.qr.data[0]) {
814
+ length = req->rsp_upiu.qr.data[0];
815
+ }
816
+ req->rsp_upiu.qr.opcode = req->req_upiu.qr.opcode;
817
+ req->rsp_upiu.qr.idn = req->req_upiu.qr.idn;
818
+ req->rsp_upiu.qr.index = req->req_upiu.qr.index;
819
+ req->rsp_upiu.qr.selector = req->req_upiu.qr.selector;
820
+ req->rsp_upiu.qr.length = cpu_to_be16(length);
821
+
822
+ return status;
823
+}
824
+
825
+static QueryRespCode ufs_exec_query_read(UfsRequest *req)
826
+{
827
+ QueryRespCode status;
828
+ switch (req->req_upiu.qr.opcode) {
829
+ case UPIU_QUERY_OPCODE_NOP:
830
+ status = QUERY_RESULT_SUCCESS;
831
+ break;
832
+ case UPIU_QUERY_OPCODE_READ_DESC:
833
+ status = ufs_read_desc(req);
834
+ break;
835
+ case UPIU_QUERY_OPCODE_READ_ATTR:
836
+ status = ufs_exec_query_attr(req, UFS_QUERY_ATTR_READ);
837
+ break;
838
+ case UPIU_QUERY_OPCODE_READ_FLAG:
839
+ status = ufs_exec_query_flag(req, UFS_QUERY_FLAG_READ);
840
+ break;
841
+ default:
842
+ trace_ufs_err_query_invalid_opcode(req->req_upiu.qr.opcode);
843
+ status = QUERY_RESULT_INVALID_OPCODE;
844
+ break;
845
+ }
846
+
847
+ return status;
848
+}
849
+
850
+static QueryRespCode ufs_exec_query_write(UfsRequest *req)
851
+{
852
+ QueryRespCode status;
853
+ switch (req->req_upiu.qr.opcode) {
854
+ case UPIU_QUERY_OPCODE_NOP:
855
+ status = QUERY_RESULT_SUCCESS;
856
+ break;
857
+ case UPIU_QUERY_OPCODE_WRITE_DESC:
858
+ /* write descriptor is not supported */
859
+ status = QUERY_RESULT_NOT_WRITEABLE;
860
+ break;
861
+ case UPIU_QUERY_OPCODE_WRITE_ATTR:
862
+ status = ufs_exec_query_attr(req, UFS_QUERY_ATTR_WRITE);
863
+ break;
864
+ case UPIU_QUERY_OPCODE_SET_FLAG:
865
+ status = ufs_exec_query_flag(req, UFS_QUERY_FLAG_SET);
866
+ break;
867
+ case UPIU_QUERY_OPCODE_CLEAR_FLAG:
868
+ status = ufs_exec_query_flag(req, UFS_QUERY_FLAG_CLEAR);
869
+ break;
870
+ case UPIU_QUERY_OPCODE_TOGGLE_FLAG:
871
+ status = ufs_exec_query_flag(req, UFS_QUERY_FLAG_TOGGLE);
872
+ break;
873
+ default:
874
+ trace_ufs_err_query_invalid_opcode(req->req_upiu.qr.opcode);
875
+ status = QUERY_RESULT_INVALID_OPCODE;
876
+ break;
877
+ }
878
+
879
+ return status;
880
+}
881
+
882
+static UfsReqResult ufs_exec_query_cmd(UfsRequest *req)
883
+{
884
+ uint8_t query_func = req->req_upiu.header.query_func;
885
+ uint16_t data_segment_length;
886
+ QueryRespCode status;
887
+
888
+ trace_ufs_exec_query_cmd(req->slot, req->req_upiu.qr.opcode);
889
+ if (query_func == UPIU_QUERY_FUNC_STANDARD_READ_REQUEST) {
890
+ status = ufs_exec_query_read(req);
891
+ } else if (query_func == UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST) {
892
+ status = ufs_exec_query_write(req);
893
+ } else {
894
+ status = QUERY_RESULT_GENERAL_FAILURE;
895
+ }
896
+
897
+ data_segment_length = be16_to_cpu(req->rsp_upiu.qr.length);
898
+ ufs_build_upiu_header(req, UPIU_TRANSACTION_QUERY_RSP, 0, status, 0,
899
+ data_segment_length);
900
+
901
+ if (status != QUERY_RESULT_SUCCESS) {
902
+ return UFS_REQUEST_FAIL;
903
+ }
904
+ return UFS_REQUEST_SUCCESS;
905
+}
906
+
907
+static void ufs_exec_req(UfsRequest *req)
908
+{
909
+ UfsReqResult req_result;
910
+
911
+ if (ufs_dma_read_upiu(req)) {
912
+ return;
913
+ }
914
+
915
+ switch (req->req_upiu.header.trans_type) {
916
+ case UPIU_TRANSACTION_NOP_OUT:
917
+ req_result = ufs_exec_nop_cmd(req);
918
+ break;
919
+ case UPIU_TRANSACTION_COMMAND:
920
+ /* Not yet implemented */
921
+ req_result = UFS_REQUEST_FAIL;
922
+ break;
923
+ case UPIU_TRANSACTION_QUERY_REQ:
924
+ req_result = ufs_exec_query_cmd(req);
925
+ break;
926
+ default:
927
+ trace_ufs_err_invalid_trans_code(req->slot,
928
+ req->req_upiu.header.trans_type);
929
+ req_result = UFS_REQUEST_FAIL;
930
+ }
931
+
932
+ ufs_complete_req(req, req_result);
933
+}
934
+
935
+static void ufs_process_req(void *opaque)
936
+{
937
+ UfsHc *u = opaque;
938
+ UfsRequest *req;
939
+ int slot;
940
+
941
+ for (slot = 0; slot < u->params.nutrs; slot++) {
942
+ req = &u->req_list[slot];
943
+
944
+ if (req->state != UFS_REQUEST_READY) {
945
+ continue;
946
+ }
947
+ trace_ufs_process_req(slot);
948
+ req->state = UFS_REQUEST_RUNNING;
949
+
950
+ ufs_exec_req(req);
951
+ }
952
+}
953
+
954
+static void ufs_complete_req(UfsRequest *req, UfsReqResult req_result)
955
+{
956
+ UfsHc *u = req->hc;
957
+ assert(req->state == UFS_REQUEST_RUNNING);
958
+
959
+ if (req_result == UFS_REQUEST_SUCCESS) {
960
+ req->utrd.header.dword_2 = cpu_to_le32(OCS_SUCCESS);
961
+ } else {
962
+ req->utrd.header.dword_2 = cpu_to_le32(OCS_INVALID_CMD_TABLE_ATTR);
963
+ }
964
+
965
+ trace_ufs_complete_req(req->slot);
966
+ req->state = UFS_REQUEST_COMPLETE;
967
+ qemu_bh_schedule(u->complete_bh);
968
+}
969
+
970
+static void ufs_clear_req(UfsRequest *req)
971
+{
972
+ if (req->sg != NULL) {
973
+ qemu_sglist_destroy(req->sg);
974
+ g_free(req->sg);
975
+ req->sg = NULL;
976
+ }
977
+
978
+ memset(&req->utrd, 0, sizeof(req->utrd));
979
+ memset(&req->req_upiu, 0, sizeof(req->req_upiu));
980
+ memset(&req->rsp_upiu, 0, sizeof(req->rsp_upiu));
981
+}
982
+
983
+static void ufs_sendback_req(void *opaque)
984
+{
985
+ UfsHc *u = opaque;
986
+ UfsRequest *req;
987
+ int slot;
988
+
989
+ for (slot = 0; slot < u->params.nutrs; slot++) {
990
+ req = &u->req_list[slot];
991
+
992
+ if (req->state != UFS_REQUEST_COMPLETE) {
993
+ continue;
994
+ }
995
+
996
+ if (ufs_dma_write_upiu(req)) {
997
+ req->state = UFS_REQUEST_ERROR;
998
+ continue;
999
+ }
1000
+
1001
+ /*
1002
+ * TODO: UTP Transfer Request Interrupt Aggregation Control is not yet
1003
+ * supported
1004
+ */
1005
+ if (le32_to_cpu(req->utrd.header.dword_2) != OCS_SUCCESS ||
1006
+ le32_to_cpu(req->utrd.header.dword_0) & UTP_REQ_DESC_INT_CMD) {
1007
+ u->reg.is = FIELD_DP32(u->reg.is, IS, UTRCS, 1);
1008
+ }
1009
+
1010
+ u->reg.utrldbr &= ~(1 << slot);
1011
+ u->reg.utrlcnr |= (1 << slot);
1012
+
1013
+ trace_ufs_sendback_req(req->slot);
1014
+
1015
+ ufs_clear_req(req);
1016
+ req->state = UFS_REQUEST_IDLE;
1017
+ }
1018
+
1019
+ ufs_irq_check(u);
1020
+}
1021
+
1022
static bool ufs_check_constraints(UfsHc *u, Error **errp)
1023
{
1024
if (u->params.nutrs > UFS_MAX_NUTRS) {
1025
@@ -XXX,XX +XXX,XX @@ static void ufs_init_pci(UfsHc *u, PCIDevice *pci_dev)
1026
u->irq = pci_allocate_irq(pci_dev);
1027
}
1028
1029
+static void ufs_init_state(UfsHc *u)
1030
+{
1031
+ u->req_list = g_new0(UfsRequest, u->params.nutrs);
1032
+
1033
+ for (int i = 0; i < u->params.nutrs; i++) {
1034
+ u->req_list[i].hc = u;
1035
+ u->req_list[i].slot = i;
1036
+ u->req_list[i].sg = NULL;
1037
+ u->req_list[i].state = UFS_REQUEST_IDLE;
1038
+ }
1039
+
1040
+ u->doorbell_bh = qemu_bh_new_guarded(ufs_process_req, u,
1041
+ &DEVICE(u)->mem_reentrancy_guard);
1042
+ u->complete_bh = qemu_bh_new_guarded(ufs_sendback_req, u,
1043
+ &DEVICE(u)->mem_reentrancy_guard);
1044
+}
1045
+
1046
static void ufs_init_hc(UfsHc *u)
1047
{
1048
uint32_t cap = 0;
1049
@@ -XXX,XX +XXX,XX @@ static void ufs_init_hc(UfsHc *u)
1050
cap = FIELD_DP32(cap, CAP, CS, 0);
1051
u->reg.cap = cap;
1052
u->reg.ver = UFS_SPEC_VER;
1053
+
1054
+ memset(&u->device_desc, 0, sizeof(DeviceDescriptor));
1055
+ u->device_desc.length = sizeof(DeviceDescriptor);
1056
+ u->device_desc.descriptor_idn = QUERY_DESC_IDN_DEVICE;
1057
+ u->device_desc.device_sub_class = 0x01;
1058
+ u->device_desc.number_lu = 0x00;
1059
+ u->device_desc.number_wlu = 0x04;
1060
+ /* TODO: Revisit it when Power Management is implemented */
1061
+ u->device_desc.init_power_mode = 0x01; /* Active Mode */
1062
+ u->device_desc.high_priority_lun = 0x7F; /* Same Priority */
1063
+ u->device_desc.spec_version = cpu_to_be16(UFS_SPEC_VER);
1064
+ u->device_desc.manufacturer_name = 0x00;
1065
+ u->device_desc.product_name = 0x01;
1066
+ u->device_desc.serial_number = 0x02;
1067
+ u->device_desc.oem_id = 0x03;
1068
+ u->device_desc.ud_0_base_offset = 0x16;
1069
+ u->device_desc.ud_config_p_length = 0x1A;
1070
+ u->device_desc.device_rtt_cap = 0x02;
1071
+ u->device_desc.queue_depth = u->params.nutrs;
1072
+ u->device_desc.product_revision_level = 0x04;
1073
+
1074
+ memset(&u->geometry_desc, 0, sizeof(GeometryDescriptor));
1075
+ u->geometry_desc.length = sizeof(GeometryDescriptor);
1076
+ u->geometry_desc.descriptor_idn = QUERY_DESC_IDN_GEOMETRY;
1077
+ u->geometry_desc.max_number_lu = (UFS_MAX_LUS == 32) ? 0x1 : 0x0;
1078
+ u->geometry_desc.segment_size = cpu_to_be32(0x2000); /* 4KB */
1079
+ u->geometry_desc.allocation_unit_size = 0x1; /* 4KB */
1080
+ u->geometry_desc.min_addr_block_size = 0x8; /* 4KB */
1081
+ u->geometry_desc.max_in_buffer_size = 0x8;
1082
+ u->geometry_desc.max_out_buffer_size = 0x8;
1083
+ u->geometry_desc.rpmb_read_write_size = 0x40;
1084
+ u->geometry_desc.data_ordering =
1085
+ 0x0; /* out-of-order data transfer is not supported */
1086
+ u->geometry_desc.max_context_id_number = 0x5;
1087
+ u->geometry_desc.supported_memory_types = cpu_to_be16(0x8001);
1088
+
1089
+ memset(&u->attributes, 0, sizeof(u->attributes));
1090
+ u->attributes.max_data_in_size = 0x08;
1091
+ u->attributes.max_data_out_size = 0x08;
1092
+ u->attributes.ref_clk_freq = 0x01; /* 26 MHz */
1093
+ /* configure descriptor is not supported */
1094
+ u->attributes.config_descr_lock = 0x01;
1095
+ u->attributes.max_num_of_rtt = 0x02;
1096
+
1097
+ memset(&u->flags, 0, sizeof(u->flags));
1098
+ u->flags.permanently_disable_fw_update = 1;
1099
}
1100
1101
static void ufs_realize(PCIDevice *pci_dev, Error **errp)
1102
@@ -XXX,XX +XXX,XX @@ static void ufs_realize(PCIDevice *pci_dev, Error **errp)
1103
return;
56
}
1104
}
57
1105
58
ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L1,
1106
+ ufs_init_state(u);
59
- s->l1_table_offset + 8 * l1_start_index, sizeof(buf), false);
1107
ufs_init_hc(u);
60
+ s->l1_table_offset + 8 * l1_start_index, bufsize, false);
1108
ufs_init_pci(u, pci_dev);
61
if (ret < 0) {
1109
}
62
return ret;
1110
63
}
1111
+static void ufs_exit(PCIDevice *pci_dev)
64
@@ -XXX,XX +XXX,XX @@ int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index)
1112
+{
65
BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE);
1113
+ UfsHc *u = UFS(pci_dev);
66
ret = bdrv_pwrite_sync(bs->file,
1114
+
67
s->l1_table_offset + 8 * l1_start_index,
1115
+ qemu_bh_delete(u->doorbell_bh);
68
- buf, sizeof(buf));
1116
+ qemu_bh_delete(u->complete_bh);
69
+ buf, bufsize);
1117
+
70
if (ret < 0) {
1118
+ for (int i = 0; i < u->params.nutrs; i++) {
71
return ret;
1119
+ ufs_clear_req(&u->req_list[i]);
72
}
1120
+ }
1121
+ g_free(u->req_list);
1122
+}
1123
+
1124
static Property ufs_props[] = {
1125
DEFINE_PROP_STRING("serial", UfsHc, params.serial),
1126
DEFINE_PROP_UINT8("nutrs", UfsHc, params.nutrs, 32),
1127
@@ -XXX,XX +XXX,XX @@ static void ufs_class_init(ObjectClass *oc, void *data)
1128
PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
1129
1130
pc->realize = ufs_realize;
1131
+ pc->exit = ufs_exit;
1132
pc->vendor_id = PCI_VENDOR_ID_REDHAT;
1133
pc->device_id = PCI_DEVICE_ID_REDHAT_UFS;
1134
pc->class_id = PCI_CLASS_STORAGE_UFS;
1135
diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events
1136
index XXXXXXX..XXXXXXX 100644
1137
--- a/hw/ufs/trace-events
1138
+++ b/hw/ufs/trace-events
1139
@@ -XXX,XX +XXX,XX @@ ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu
1140
ufs_err_dma_read_prdt(uint32_t slot, uint64_t addr) "failed to read prdt. UTRLDBR slot %"PRIu32", prdt addr %"PRIu64""
1141
ufs_err_dma_write_utrd(uint32_t slot, uint64_t addr) "failed to write utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64""
1142
ufs_err_dma_write_rsp_upiu(uint32_t slot, uint64_t addr) "failed to write rsp upiu. UTRLDBR slot %"PRIu32", response upiu addr %"PRIu64""
1143
+ufs_err_utrl_slot_error(uint32_t slot) "UTRLDBR slot %"PRIu32" is in error"
1144
ufs_err_utrl_slot_busy(uint32_t slot) "UTRLDBR slot %"PRIu32" is busy"
1145
ufs_err_unsupport_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is not yet supported"
1146
ufs_err_invalid_register_offset(uint32_t offset) "Register offset 0x%"PRIx32" is invalid"
73
--
1147
--
74
2.24.1
1148
2.41.0
75
76
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Jeuk Kim <jeuk20.kim@gmail.com>
2
2
3
This replaces all remaining instances in the qcow2 code.
3
This commit adds support for ufs logical unit.
4
The LU handles processing for the SCSI command,
5
unit descriptor query request.
4
6
5
Signed-off-by: Alberto Garcia <berto@igalia.com>
7
This commit enables the UFS device to process
6
Message-id: b5f74b606c2d9873b12d29acdb7fd498029c4025.1579374329.git.berto@igalia.com
8
IO requests.
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
Signed-off-by: Jeuk Kim <jeuk20.kim@samsung.com>
11
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
12
Message-id: 898cea923e819dc21a99597bf045a12d7983be28.1691062912.git.jeuk20.kim@samsung.com
13
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
9
---
14
---
10
block/qcow2.c | 8 +++++---
15
hw/ufs/ufs.h | 43 ++
11
1 file changed, 5 insertions(+), 3 deletions(-)
16
include/scsi/constants.h | 1 +
17
hw/ufs/lu.c | 1445 ++++++++++++++++++++++++++++++++++++++
18
hw/ufs/ufs.c | 252 ++++++-
19
hw/ufs/meson.build | 2 +-
20
hw/ufs/trace-events | 25 +
21
6 files changed, 1761 insertions(+), 7 deletions(-)
22
create mode 100644 hw/ufs/lu.c
12
23
13
diff --git a/block/qcow2.c b/block/qcow2.c
24
diff --git a/hw/ufs/ufs.h b/hw/ufs/ufs.h
14
index XXXXXXX..XXXXXXX 100644
25
index XXXXXXX..XXXXXXX 100644
15
--- a/block/qcow2.c
26
--- a/hw/ufs/ufs.h
16
+++ b/block/qcow2.c
27
+++ b/hw/ufs/ufs.h
17
@@ -XXX,XX +XXX,XX @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
28
@@ -XXX,XX +XXX,XX @@
18
29
#define UFS_MAX_LUS 32
19
/* Validate options and set default values */
30
#define UFS_BLOCK_SIZE 4096
20
if (!QEMU_IS_ALIGNED(qcow2_opts->size, BDRV_SECTOR_SIZE)) {
31
21
- error_setg(errp, "Image size must be a multiple of 512 bytes");
32
+typedef struct UfsBusClass {
22
+ error_setg(errp, "Image size must be a multiple of %u bytes",
33
+ BusClass parent_class;
23
+ (unsigned) BDRV_SECTOR_SIZE);
34
+ bool (*parent_check_address)(BusState *bus, DeviceState *dev, Error **errp);
24
ret = -EINVAL;
35
+} UfsBusClass;
25
goto out;
36
+
37
+typedef struct UfsBus {
38
+ SCSIBus parent_bus;
39
+} UfsBus;
40
+
41
+#define TYPE_UFS_BUS "ufs-bus"
42
+DECLARE_OBJ_CHECKERS(UfsBus, UfsBusClass, UFS_BUS, TYPE_UFS_BUS)
43
+
44
typedef enum UfsRequestState {
45
UFS_REQUEST_IDLE = 0,
46
UFS_REQUEST_READY = 1,
47
@@ -XXX,XX +XXX,XX @@ typedef enum UfsRequestState {
48
typedef enum UfsReqResult {
49
UFS_REQUEST_SUCCESS = 0,
50
UFS_REQUEST_FAIL = 1,
51
+ UFS_REQUEST_NO_COMPLETE = 2,
52
} UfsReqResult;
53
54
typedef struct UfsRequest {
55
@@ -XXX,XX +XXX,XX @@ typedef struct UfsRequest {
56
QEMUSGList *sg;
57
} UfsRequest;
58
59
+typedef struct UfsLu {
60
+ SCSIDevice qdev;
61
+ uint8_t lun;
62
+ UnitDescriptor unit_desc;
63
+} UfsLu;
64
+
65
+typedef struct UfsWLu {
66
+ SCSIDevice qdev;
67
+ uint8_t lun;
68
+} UfsWLu;
69
+
70
typedef struct UfsParams {
71
char *serial;
72
uint8_t nutrs; /* Number of UTP Transfer Request Slots */
73
@@ -XXX,XX +XXX,XX @@ typedef struct UfsParams {
74
75
typedef struct UfsHc {
76
PCIDevice parent_obj;
77
+ UfsBus bus;
78
MemoryRegion iomem;
79
UfsReg reg;
80
UfsParams params;
81
uint32_t reg_size;
82
UfsRequest *req_list;
83
84
+ UfsLu *lus[UFS_MAX_LUS];
85
+ UfsWLu *report_wlu;
86
+ UfsWLu *dev_wlu;
87
+ UfsWLu *boot_wlu;
88
+ UfsWLu *rpmb_wlu;
89
DeviceDescriptor device_desc;
90
GeometryDescriptor geometry_desc;
91
Attributes attributes;
92
@@ -XXX,XX +XXX,XX @@ typedef struct UfsHc {
93
#define TYPE_UFS "ufs"
94
#define UFS(obj) OBJECT_CHECK(UfsHc, (obj), TYPE_UFS)
95
96
+#define TYPE_UFS_LU "ufs-lu"
97
+#define UFSLU(obj) OBJECT_CHECK(UfsLu, (obj), TYPE_UFS_LU)
98
+
99
+#define TYPE_UFS_WLU "ufs-wlu"
100
+#define UFSWLU(obj) OBJECT_CHECK(UfsWLu, (obj), TYPE_UFS_WLU)
101
+
102
typedef enum UfsQueryFlagPerm {
103
UFS_QUERY_FLAG_NONE = 0x0,
104
UFS_QUERY_FLAG_READ = 0x1,
105
@@ -XXX,XX +XXX,XX @@ typedef enum UfsQueryAttrPerm {
106
UFS_QUERY_ATTR_WRITE = 0x2,
107
} UfsQueryAttrPerm;
108
109
+static inline bool is_wlun(uint8_t lun)
110
+{
111
+ return (lun == UFS_UPIU_REPORT_LUNS_WLUN ||
112
+ lun == UFS_UPIU_UFS_DEVICE_WLUN || lun == UFS_UPIU_BOOT_WLUN ||
113
+ lun == UFS_UPIU_RPMB_WLUN);
114
+}
115
+
116
#endif /* HW_UFS_UFS_H */
117
diff --git a/include/scsi/constants.h b/include/scsi/constants.h
118
index XXXXXXX..XXXXXXX 100644
119
--- a/include/scsi/constants.h
120
+++ b/include/scsi/constants.h
121
@@ -XXX,XX +XXX,XX @@
122
#define MODE_PAGE_FLEXIBLE_DISK_GEOMETRY 0x05
123
#define MODE_PAGE_CACHING 0x08
124
#define MODE_PAGE_AUDIO_CTL 0x0e
125
+#define MODE_PAGE_CONTROL 0x0a
126
#define MODE_PAGE_POWER 0x1a
127
#define MODE_PAGE_FAULT_FAIL 0x1c
128
#define MODE_PAGE_TO_PROTECT 0x1d
129
diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c
130
new file mode 100644
131
index XXXXXXX..XXXXXXX
132
--- /dev/null
133
+++ b/hw/ufs/lu.c
134
@@ -XXX,XX +XXX,XX @@
135
+/*
136
+ * QEMU UFS Logical Unit
137
+ *
138
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved.
139
+ *
140
+ * Written by Jeuk Kim <jeuk20.kim@samsung.com>
141
+ *
142
+ * This code is licensed under the GNU GPL v2 or later.
143
+ */
144
+
145
+#include "qemu/osdep.h"
146
+#include "qemu/units.h"
147
+#include "qapi/error.h"
148
+#include "qemu/memalign.h"
149
+#include "hw/scsi/scsi.h"
150
+#include "scsi/constants.h"
151
+#include "sysemu/block-backend.h"
152
+#include "qemu/cutils.h"
153
+#include "trace.h"
154
+#include "ufs.h"
155
+
156
+/*
157
+ * The code below handling SCSI commands is copied from hw/scsi/scsi-disk.c,
158
+ * with minor adjustments to make it work for UFS.
159
+ */
160
+
161
+#define SCSI_DMA_BUF_SIZE (128 * KiB)
162
+#define SCSI_MAX_INQUIRY_LEN 256
163
+#define SCSI_INQUIRY_DATA_SIZE 36
164
+#define SCSI_MAX_MODE_LEN 256
165
+
166
+typedef struct UfsSCSIReq {
167
+ SCSIRequest req;
168
+ /* Both sector and sector_count are in terms of BDRV_SECTOR_SIZE bytes. */
169
+ uint64_t sector;
170
+ uint32_t sector_count;
171
+ uint32_t buflen;
172
+ bool started;
173
+ bool need_fua_emulation;
174
+ struct iovec iov;
175
+ QEMUIOVector qiov;
176
+ BlockAcctCookie acct;
177
+} UfsSCSIReq;
178
+
179
+static void ufs_scsi_free_request(SCSIRequest *req)
180
+{
181
+ UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req);
182
+
183
+ qemu_vfree(r->iov.iov_base);
184
+}
185
+
186
+static void scsi_check_condition(UfsSCSIReq *r, SCSISense sense)
187
+{
188
+ trace_ufs_scsi_check_condition(r->req.tag, sense.key, sense.asc,
189
+ sense.ascq);
190
+ scsi_req_build_sense(&r->req, sense);
191
+ scsi_req_complete(&r->req, CHECK_CONDITION);
192
+}
193
+
194
+static int ufs_scsi_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf,
195
+ uint32_t outbuf_len)
196
+{
197
+ UfsHc *u = UFS(req->bus->qbus.parent);
198
+ UfsLu *lu = DO_UPCAST(UfsLu, qdev, req->dev);
199
+ uint8_t page_code = req->cmd.buf[2];
200
+ int start, buflen = 0;
201
+
202
+ if (outbuf_len < SCSI_INQUIRY_DATA_SIZE) {
203
+ return -1;
204
+ }
205
+
206
+ outbuf[buflen++] = lu->qdev.type & 0x1f;
207
+ outbuf[buflen++] = page_code;
208
+ outbuf[buflen++] = 0x00;
209
+ outbuf[buflen++] = 0x00;
210
+ start = buflen;
211
+
212
+ switch (page_code) {
213
+ case 0x00: /* Supported page codes, mandatory */
214
+ {
215
+ trace_ufs_scsi_emulate_vpd_page_00(req->cmd.xfer);
216
+ outbuf[buflen++] = 0x00; /* list of supported pages (this page) */
217
+ if (u->params.serial) {
218
+ outbuf[buflen++] = 0x80; /* unit serial number */
219
+ }
220
+ outbuf[buflen++] = 0x87; /* mode page policy */
221
+ break;
222
+ }
223
+ case 0x80: /* Device serial number, optional */
224
+ {
225
+ int l;
226
+
227
+ if (!u->params.serial) {
228
+ trace_ufs_scsi_emulate_vpd_page_80_not_supported();
229
+ return -1;
230
+ }
231
+
232
+ l = strlen(u->params.serial);
233
+ if (l > SCSI_INQUIRY_DATA_SIZE) {
234
+ l = SCSI_INQUIRY_DATA_SIZE;
235
+ }
236
+
237
+ trace_ufs_scsi_emulate_vpd_page_80(req->cmd.xfer);
238
+ memcpy(outbuf + buflen, u->params.serial, l);
239
+ buflen += l;
240
+ break;
241
+ }
242
+ case 0x87: /* Mode Page Policy, mandatory */
243
+ {
244
+ trace_ufs_scsi_emulate_vpd_page_87(req->cmd.xfer);
245
+ outbuf[buflen++] = 0x3f; /* apply to all mode pages and subpages */
246
+ outbuf[buflen++] = 0xff;
247
+ outbuf[buflen++] = 0; /* shared */
248
+ outbuf[buflen++] = 0;
249
+ break;
250
+ }
251
+ default:
252
+ return -1;
253
+ }
254
+ /* done with EVPD */
255
+ assert(buflen - start <= 255);
256
+ outbuf[start - 1] = buflen - start;
257
+ return buflen;
258
+}
259
+
260
+static int ufs_scsi_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf,
261
+ uint32_t outbuf_len)
262
+{
263
+ int buflen = 0;
264
+
265
+ if (outbuf_len < SCSI_INQUIRY_DATA_SIZE) {
266
+ return -1;
267
+ }
268
+
269
+ if (req->cmd.buf[1] & 0x1) {
270
+ /* Vital product data */
271
+ return ufs_scsi_emulate_vpd_page(req, outbuf, outbuf_len);
272
+ }
273
+
274
+ /* Standard INQUIRY data */
275
+ if (req->cmd.buf[2] != 0) {
276
+ return -1;
277
+ }
278
+
279
+ /* PAGE CODE == 0 */
280
+ buflen = req->cmd.xfer;
281
+ if (buflen > SCSI_MAX_INQUIRY_LEN) {
282
+ buflen = SCSI_MAX_INQUIRY_LEN;
283
+ }
284
+
285
+ if (is_wlun(req->lun)) {
286
+ outbuf[0] = TYPE_WLUN;
287
+ } else {
288
+ outbuf[0] = 0;
289
+ }
290
+ outbuf[1] = 0;
291
+
292
+ strpadcpy((char *)&outbuf[16], 16, "QEMU UFS", ' ');
293
+ strpadcpy((char *)&outbuf[8], 8, "QEMU", ' ');
294
+
295
+ memset(&outbuf[32], 0, 4);
296
+
297
+ outbuf[2] = 0x06; /* SPC-4 */
298
+ outbuf[3] = 0x2;
299
+
300
+ if (buflen > SCSI_INQUIRY_DATA_SIZE) {
301
+ outbuf[4] = buflen - 5; /* Additional Length = (Len - 1) - 4 */
302
+ } else {
303
+ /*
304
+ * If the allocation length of CDB is too small, the additional
305
+ * length is not adjusted
306
+ */
307
+ outbuf[4] = SCSI_INQUIRY_DATA_SIZE - 5;
308
+ }
309
+
310
+ /* Support TCQ. */
311
+ outbuf[7] = req->bus->info->tcq ? 0x02 : 0;
312
+ return buflen;
313
+}
314
+
315
+static int mode_sense_page(UfsLu *lu, int page, uint8_t **p_outbuf,
316
+ int page_control)
317
+{
318
+ static const int mode_sense_valid[0x3f] = {
319
+ [MODE_PAGE_CACHING] = 1,
320
+ [MODE_PAGE_R_W_ERROR] = 1,
321
+ [MODE_PAGE_CONTROL] = 1,
322
+ };
323
+
324
+ uint8_t *p = *p_outbuf + 2;
325
+ int length;
326
+
327
+ assert(page < ARRAY_SIZE(mode_sense_valid));
328
+ if ((mode_sense_valid[page]) == 0) {
329
+ return -1;
330
+ }
331
+
332
+ /*
333
+ * If Changeable Values are requested, a mask denoting those mode parameters
334
+ * that are changeable shall be returned. As we currently don't support
335
+ * parameter changes via MODE_SELECT all bits are returned set to zero.
336
+ * The buffer was already memset to zero by the caller of this function.
337
+ */
338
+ switch (page) {
339
+ case MODE_PAGE_CACHING:
340
+ length = 0x12;
341
+ if (page_control == 1 || /* Changeable Values */
342
+ blk_enable_write_cache(lu->qdev.conf.blk)) {
343
+ p[0] = 4; /* WCE */
344
+ }
345
+ break;
346
+
347
+ case MODE_PAGE_R_W_ERROR:
348
+ length = 10;
349
+ if (page_control == 1) { /* Changeable Values */
350
+ break;
351
+ }
352
+ p[0] = 0x80; /* Automatic Write Reallocation Enabled */
353
+ break;
354
+
355
+ case MODE_PAGE_CONTROL:
356
+ length = 10;
357
+ if (page_control == 1) { /* Changeable Values */
358
+ break;
359
+ }
360
+ p[1] = 0x10; /* Queue Algorithm modifier */
361
+ p[8] = 0xff; /* Busy Timeout Period */
362
+ p[9] = 0xff;
363
+ break;
364
+
365
+ default:
366
+ return -1;
367
+ }
368
+
369
+ assert(length < 256);
370
+ (*p_outbuf)[0] = page;
371
+ (*p_outbuf)[1] = length;
372
+ *p_outbuf += length + 2;
373
+ return length + 2;
374
+}
375
+
376
+static int ufs_scsi_emulate_mode_sense(UfsSCSIReq *r, uint8_t *outbuf)
377
+{
378
+ UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev);
379
+ bool dbd;
380
+ int page, buflen, ret, page_control;
381
+ uint8_t *p;
382
+ uint8_t dev_specific_param = 0;
383
+
384
+ dbd = (r->req.cmd.buf[1] & 0x8) != 0;
385
+ if (!dbd) {
386
+ return -1;
387
+ }
388
+
389
+ page = r->req.cmd.buf[2] & 0x3f;
390
+ page_control = (r->req.cmd.buf[2] & 0xc0) >> 6;
391
+
392
+ trace_ufs_scsi_emulate_mode_sense((r->req.cmd.buf[0] == MODE_SENSE) ? 6 :
393
+ 10,
394
+ page, r->req.cmd.xfer, page_control);
395
+ memset(outbuf, 0, r->req.cmd.xfer);
396
+ p = outbuf;
397
+
398
+ if (!blk_is_writable(lu->qdev.conf.blk)) {
399
+ dev_specific_param |= 0x80; /* Readonly. */
400
+ }
401
+
402
+ p[2] = 0; /* Medium type. */
403
+ p[3] = dev_specific_param;
404
+ p[6] = p[7] = 0; /* Block descriptor length. */
405
+ p += 8;
406
+
407
+ if (page_control == 3) {
408
+ /* Saved Values */
409
+ scsi_check_condition(r, SENSE_CODE(SAVING_PARAMS_NOT_SUPPORTED));
410
+ return -1;
411
+ }
412
+
413
+ if (page == 0x3f) {
414
+ for (page = 0; page <= 0x3e; page++) {
415
+ mode_sense_page(lu, page, &p, page_control);
416
+ }
417
+ } else {
418
+ ret = mode_sense_page(lu, page, &p, page_control);
419
+ if (ret == -1) {
420
+ return -1;
421
+ }
422
+ }
423
+
424
+ buflen = p - outbuf;
425
+ /*
426
+ * The mode data length field specifies the length in bytes of the
427
+ * following data that is available to be transferred. The mode data
428
+ * length does not include itself.
429
+ */
430
+ outbuf[0] = ((buflen - 2) >> 8) & 0xff;
431
+ outbuf[1] = (buflen - 2) & 0xff;
432
+ return buflen;
433
+}
434
+
435
+/*
436
+ * scsi_handle_rw_error has two return values. False means that the error
437
+ * must be ignored, true means that the error has been processed and the
438
+ * caller should not do anything else for this request. Note that
439
+ * scsi_handle_rw_error always manages its reference counts, independent
440
+ * of the return value.
441
+ */
442
+static bool scsi_handle_rw_error(UfsSCSIReq *r, int ret, bool acct_failed)
443
+{
444
+ bool is_read = (r->req.cmd.mode == SCSI_XFER_FROM_DEV);
445
+ UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev);
446
+ SCSISense sense = SENSE_CODE(NO_SENSE);
447
+ int error = 0;
448
+ bool req_has_sense = false;
449
+ BlockErrorAction action;
450
+ int status;
451
+
452
+ if (ret < 0) {
453
+ status = scsi_sense_from_errno(-ret, &sense);
454
+ error = -ret;
455
+ } else {
456
+ /* A passthrough command has completed with nonzero status. */
457
+ status = ret;
458
+ if (status == CHECK_CONDITION) {
459
+ req_has_sense = true;
460
+ error = scsi_sense_buf_to_errno(r->req.sense, sizeof(r->req.sense));
461
+ } else {
462
+ error = EINVAL;
463
+ }
464
+ }
465
+
466
+ /*
467
+ * Check whether the error has to be handled by the guest or should
468
+ * rather follow the rerror=/werror= settings. Guest-handled errors
469
+ * are usually retried immediately, so do not post them to QMP and
470
+ * do not account them as failed I/O.
471
+ */
472
+ if (req_has_sense && scsi_sense_buf_is_guest_recoverable(
473
+ r->req.sense, sizeof(r->req.sense))) {
474
+ action = BLOCK_ERROR_ACTION_REPORT;
475
+ acct_failed = false;
476
+ } else {
477
+ action = blk_get_error_action(lu->qdev.conf.blk, is_read, error);
478
+ blk_error_action(lu->qdev.conf.blk, action, is_read, error);
479
+ }
480
+
481
+ switch (action) {
482
+ case BLOCK_ERROR_ACTION_REPORT:
483
+ if (acct_failed) {
484
+ block_acct_failed(blk_get_stats(lu->qdev.conf.blk), &r->acct);
485
+ }
486
+ if (!req_has_sense && status == CHECK_CONDITION) {
487
+ scsi_req_build_sense(&r->req, sense);
488
+ }
489
+ scsi_req_complete(&r->req, status);
490
+ return true;
491
+
492
+ case BLOCK_ERROR_ACTION_IGNORE:
493
+ return false;
494
+
495
+ case BLOCK_ERROR_ACTION_STOP:
496
+ scsi_req_retry(&r->req);
497
+ return true;
498
+
499
+ default:
500
+ g_assert_not_reached();
501
+ }
502
+}
503
+
504
+static bool ufs_scsi_req_check_error(UfsSCSIReq *r, int ret, bool acct_failed)
505
+{
506
+ if (r->req.io_canceled) {
507
+ scsi_req_cancel_complete(&r->req);
508
+ return true;
509
+ }
510
+
511
+ if (ret < 0) {
512
+ return scsi_handle_rw_error(r, ret, acct_failed);
513
+ }
514
+
515
+ return false;
516
+}
517
+
518
+static void scsi_aio_complete(void *opaque, int ret)
519
+{
520
+ UfsSCSIReq *r = (UfsSCSIReq *)opaque;
521
+ UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev);
522
+
523
+ assert(r->req.aiocb != NULL);
524
+ r->req.aiocb = NULL;
525
+ aio_context_acquire(blk_get_aio_context(lu->qdev.conf.blk));
526
+ if (ufs_scsi_req_check_error(r, ret, true)) {
527
+ goto done;
528
+ }
529
+
530
+ block_acct_done(blk_get_stats(lu->qdev.conf.blk), &r->acct);
531
+ scsi_req_complete(&r->req, GOOD);
532
+
533
+done:
534
+ aio_context_release(blk_get_aio_context(lu->qdev.conf.blk));
535
+ scsi_req_unref(&r->req);
536
+}
537
+
538
+static int32_t ufs_scsi_emulate_command(SCSIRequest *req, uint8_t *buf)
539
+{
540
+ UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req);
541
+ UfsLu *lu = DO_UPCAST(UfsLu, qdev, req->dev);
542
+ uint32_t last_block = 0;
543
+ uint8_t *outbuf;
544
+ int buflen;
545
+
546
+ switch (req->cmd.buf[0]) {
547
+ case INQUIRY:
548
+ case MODE_SENSE_10:
549
+ case START_STOP:
550
+ case REQUEST_SENSE:
551
+ break;
552
+
553
+ default:
554
+ if (!blk_is_available(lu->qdev.conf.blk)) {
555
+ scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
556
+ return 0;
557
+ }
558
+ break;
559
+ }
560
+
561
+ /*
562
+ * FIXME: we shouldn't return anything bigger than 4k, but the code
563
+ * requires the buffer to be as big as req->cmd.xfer in several
564
+ * places. So, do not allow CDBs with a very large ALLOCATION
565
+ * LENGTH. The real fix would be to modify scsi_read_data and
566
+ * dma_buf_read, so that they return data beyond the buflen
567
+ * as all zeros.
568
+ */
569
+ if (req->cmd.xfer > 65536) {
570
+ goto illegal_request;
571
+ }
572
+ r->buflen = MAX(4096, req->cmd.xfer);
573
+
574
+ if (!r->iov.iov_base) {
575
+ r->iov.iov_base = blk_blockalign(lu->qdev.conf.blk, r->buflen);
576
+ }
577
+
578
+ outbuf = r->iov.iov_base;
579
+ memset(outbuf, 0, r->buflen);
580
+ switch (req->cmd.buf[0]) {
581
+ case TEST_UNIT_READY:
582
+ assert(blk_is_available(lu->qdev.conf.blk));
583
+ break;
584
+ case INQUIRY:
585
+ buflen = ufs_scsi_emulate_inquiry(req, outbuf, r->buflen);
586
+ if (buflen < 0) {
587
+ goto illegal_request;
588
+ }
589
+ break;
590
+ case MODE_SENSE_10:
591
+ buflen = ufs_scsi_emulate_mode_sense(r, outbuf);
592
+ if (buflen < 0) {
593
+ goto illegal_request;
594
+ }
595
+ break;
596
+ case READ_CAPACITY_10:
597
+ /* The normal LEN field for this command is zero. */
598
+ memset(outbuf, 0, 8);
599
+ if (lu->qdev.max_lba > 0) {
600
+ last_block = lu->qdev.max_lba - 1;
601
+ };
602
+ outbuf[0] = (last_block >> 24) & 0xff;
603
+ outbuf[1] = (last_block >> 16) & 0xff;
604
+ outbuf[2] = (last_block >> 8) & 0xff;
605
+ outbuf[3] = last_block & 0xff;
606
+ outbuf[4] = (lu->qdev.blocksize >> 24) & 0xff;
607
+ outbuf[5] = (lu->qdev.blocksize >> 16) & 0xff;
608
+ outbuf[6] = (lu->qdev.blocksize >> 8) & 0xff;
609
+ outbuf[7] = lu->qdev.blocksize & 0xff;
610
+ break;
611
+ case REQUEST_SENSE:
612
+ /* Just return "NO SENSE". */
613
+ buflen = scsi_convert_sense(NULL, 0, outbuf, r->buflen,
614
+ (req->cmd.buf[1] & 1) == 0);
615
+ if (buflen < 0) {
616
+ goto illegal_request;
617
+ }
618
+ break;
619
+ case SYNCHRONIZE_CACHE:
620
+ /* The request is used as the AIO opaque value, so add a ref. */
621
+ scsi_req_ref(&r->req);
622
+ block_acct_start(blk_get_stats(lu->qdev.conf.blk), &r->acct, 0,
623
+ BLOCK_ACCT_FLUSH);
624
+ r->req.aiocb = blk_aio_flush(lu->qdev.conf.blk, scsi_aio_complete, r);
625
+ return 0;
626
+ case VERIFY_10:
627
+ trace_ufs_scsi_emulate_command_VERIFY((req->cmd.buf[1] >> 1) & 3);
628
+ if (req->cmd.buf[1] & 6) {
629
+ goto illegal_request;
630
+ }
631
+ break;
632
+ case SERVICE_ACTION_IN_16:
633
+ /* Service Action In subcommands. */
634
+ if ((req->cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) {
635
+ trace_ufs_scsi_emulate_command_SAI_16();
636
+ memset(outbuf, 0, req->cmd.xfer);
637
+
638
+ if (lu->qdev.max_lba > 0) {
639
+ last_block = lu->qdev.max_lba - 1;
640
+ };
641
+ outbuf[0] = 0;
642
+ outbuf[1] = 0;
643
+ outbuf[2] = 0;
644
+ outbuf[3] = 0;
645
+ outbuf[4] = (last_block >> 24) & 0xff;
646
+ outbuf[5] = (last_block >> 16) & 0xff;
647
+ outbuf[6] = (last_block >> 8) & 0xff;
648
+ outbuf[7] = last_block & 0xff;
649
+ outbuf[8] = (lu->qdev.blocksize >> 24) & 0xff;
650
+ outbuf[9] = (lu->qdev.blocksize >> 16) & 0xff;
651
+ outbuf[10] = (lu->qdev.blocksize >> 8) & 0xff;
652
+ outbuf[11] = lu->qdev.blocksize & 0xff;
653
+ outbuf[12] = 0;
654
+ outbuf[13] = get_physical_block_exp(&lu->qdev.conf);
655
+
656
+ if (lu->unit_desc.provisioning_type == 2 ||
657
+ lu->unit_desc.provisioning_type == 3) {
658
+ outbuf[14] = 0x80;
659
+ }
660
+ /* Protection, exponent and lowest lba field left blank. */
661
+ break;
662
+ }
663
+ trace_ufs_scsi_emulate_command_SAI_unsupported();
664
+ goto illegal_request;
665
+ case MODE_SELECT_10:
666
+ trace_ufs_scsi_emulate_command_MODE_SELECT_10(r->req.cmd.xfer);
667
+ break;
668
+ case START_STOP:
669
+ /*
670
+ * TODO: START_STOP is not yet implemented. It always returns success.
671
+ * Revisit it when ufs power management is implemented.
672
+ */
673
+ trace_ufs_scsi_emulate_command_START_STOP();
674
+ break;
675
+ case FORMAT_UNIT:
676
+ trace_ufs_scsi_emulate_command_FORMAT_UNIT();
677
+ break;
678
+ case SEND_DIAGNOSTIC:
679
+ trace_ufs_scsi_emulate_command_SEND_DIAGNOSTIC();
680
+ break;
681
+ default:
682
+ trace_ufs_scsi_emulate_command_UNKNOWN(buf[0],
683
+ scsi_command_name(buf[0]));
684
+ scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE));
685
+ return 0;
686
+ }
687
+ assert(!r->req.aiocb);
688
+ r->iov.iov_len = MIN(r->buflen, req->cmd.xfer);
689
+ if (r->iov.iov_len == 0) {
690
+ scsi_req_complete(&r->req, GOOD);
691
+ }
692
+ if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
693
+ assert(r->iov.iov_len == req->cmd.xfer);
694
+ return -r->iov.iov_len;
695
+ } else {
696
+ return r->iov.iov_len;
697
+ }
698
+
699
+illegal_request:
700
+ if (r->req.status == -1) {
701
+ scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
702
+ }
703
+ return 0;
704
+}
705
+
706
+static void ufs_scsi_emulate_read_data(SCSIRequest *req)
707
+{
708
+ UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req);
709
+ int buflen = r->iov.iov_len;
710
+
711
+ if (buflen) {
712
+ trace_ufs_scsi_emulate_read_data(buflen);
713
+ r->iov.iov_len = 0;
714
+ r->started = true;
715
+ scsi_req_data(&r->req, buflen);
716
+ return;
717
+ }
718
+
719
+ /* This also clears the sense buffer for REQUEST SENSE. */
720
+ scsi_req_complete(&r->req, GOOD);
721
+}
722
+
723
+static int ufs_scsi_check_mode_select(UfsLu *lu, int page, uint8_t *inbuf,
724
+ int inlen)
725
+{
726
+ uint8_t mode_current[SCSI_MAX_MODE_LEN];
727
+ uint8_t mode_changeable[SCSI_MAX_MODE_LEN];
728
+ uint8_t *p;
729
+ int len, expected_len, changeable_len, i;
730
+
731
+ /*
732
+ * The input buffer does not include the page header, so it is
733
+ * off by 2 bytes.
734
+ */
735
+ expected_len = inlen + 2;
736
+ if (expected_len > SCSI_MAX_MODE_LEN) {
737
+ return -1;
738
+ }
739
+
740
+ /* MODE_PAGE_ALLS is only valid for MODE SENSE commands */
741
+ if (page == MODE_PAGE_ALLS) {
742
+ return -1;
743
+ }
744
+
745
+ p = mode_current;
746
+ memset(mode_current, 0, inlen + 2);
747
+ len = mode_sense_page(lu, page, &p, 0);
748
+ if (len < 0 || len != expected_len) {
749
+ return -1;
750
+ }
751
+
752
+ p = mode_changeable;
753
+ memset(mode_changeable, 0, inlen + 2);
754
+ changeable_len = mode_sense_page(lu, page, &p, 1);
755
+ assert(changeable_len == len);
756
+
757
+ /*
758
+ * Check that unchangeable bits are the same as what MODE SENSE
759
+ * would return.
760
+ */
761
+ for (i = 2; i < len; i++) {
762
+ if (((mode_current[i] ^ inbuf[i - 2]) & ~mode_changeable[i]) != 0) {
763
+ return -1;
764
+ }
765
+ }
766
+ return 0;
767
+}
768
+
769
+static void ufs_scsi_apply_mode_select(UfsLu *lu, int page, uint8_t *p)
770
+{
771
+ switch (page) {
772
+ case MODE_PAGE_CACHING:
773
+ blk_set_enable_write_cache(lu->qdev.conf.blk, (p[0] & 4) != 0);
774
+ break;
775
+
776
+ default:
777
+ break;
778
+ }
779
+}
780
+
781
+static int mode_select_pages(UfsSCSIReq *r, uint8_t *p, int len, bool change)
782
+{
783
+ UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev);
784
+
785
+ while (len > 0) {
786
+ int page, page_len;
787
+
788
+ page = p[0] & 0x3f;
789
+ if (p[0] & 0x40) {
790
+ goto invalid_param;
791
+ } else {
792
+ if (len < 2) {
793
+ goto invalid_param_len;
794
+ }
795
+ page_len = p[1];
796
+ p += 2;
797
+ len -= 2;
798
+ }
799
+
800
+ if (page_len > len) {
801
+ goto invalid_param_len;
802
+ }
803
+
804
+ if (!change) {
805
+ if (ufs_scsi_check_mode_select(lu, page, p, page_len) < 0) {
806
+ goto invalid_param;
807
+ }
808
+ } else {
809
+ ufs_scsi_apply_mode_select(lu, page, p);
810
+ }
811
+
812
+ p += page_len;
813
+ len -= page_len;
814
+ }
815
+ return 0;
816
+
817
+invalid_param:
818
+ scsi_check_condition(r, SENSE_CODE(INVALID_PARAM));
819
+ return -1;
820
+
821
+invalid_param_len:
822
+ scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN));
823
+ return -1;
824
+}
825
+
826
+static void ufs_scsi_emulate_mode_select(UfsSCSIReq *r, uint8_t *inbuf)
827
+{
828
+ UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev);
829
+ uint8_t *p = inbuf;
830
+ int len = r->req.cmd.xfer;
831
+ int hdr_len = 8;
832
+ int bd_len;
833
+ int pass;
834
+
835
+ /* We only support PF=1, SP=0. */
836
+ if ((r->req.cmd.buf[1] & 0x11) != 0x10) {
837
+ goto invalid_field;
838
+ }
839
+
840
+ if (len < hdr_len) {
841
+ goto invalid_param_len;
842
+ }
843
+
844
+ bd_len = lduw_be_p(&p[6]);
845
+ if (bd_len != 0) {
846
+ goto invalid_param;
847
+ }
848
+
849
+ len -= hdr_len;
850
+ p += hdr_len;
851
+
852
+ /* Ensure no change is made if there is an error! */
853
+ for (pass = 0; pass < 2; pass++) {
854
+ if (mode_select_pages(r, p, len, pass == 1) < 0) {
855
+ assert(pass == 0);
856
+ return;
857
+ }
858
+ }
859
+
860
+ if (!blk_enable_write_cache(lu->qdev.conf.blk)) {
861
+ /* The request is used as the AIO opaque value, so add a ref. */
862
+ scsi_req_ref(&r->req);
863
+ block_acct_start(blk_get_stats(lu->qdev.conf.blk), &r->acct, 0,
864
+ BLOCK_ACCT_FLUSH);
865
+ r->req.aiocb = blk_aio_flush(lu->qdev.conf.blk, scsi_aio_complete, r);
866
+ return;
867
+ }
868
+
869
+ scsi_req_complete(&r->req, GOOD);
870
+ return;
871
+
872
+invalid_param:
873
+ scsi_check_condition(r, SENSE_CODE(INVALID_PARAM));
874
+ return;
875
+
876
+invalid_param_len:
877
+ scsi_check_condition(r, SENSE_CODE(INVALID_PARAM_LEN));
878
+ return;
879
+
880
+invalid_field:
881
+ scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
882
+}
883
+
884
+/* block_num and nb_blocks expected to be in qdev blocksize */
885
+static inline bool check_lba_range(UfsLu *lu, uint64_t block_num,
886
+ uint32_t nb_blocks)
887
+{
888
+ /*
889
+ * The first line tests that no overflow happens when computing the last
890
+ * block. The second line tests that the last accessed block is in
891
+ * range.
892
+ *
893
+ * Careful, the computations should not underflow for nb_blocks == 0,
894
+ * and a 0-block read to the first LBA beyond the end of device is
895
+ * valid.
896
+ */
897
+ return (block_num <= block_num + nb_blocks &&
898
+ block_num + nb_blocks <= lu->qdev.max_lba + 1);
899
+}
900
+
901
+static void ufs_scsi_emulate_write_data(SCSIRequest *req)
902
+{
903
+ UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req);
904
+
905
+ if (r->iov.iov_len) {
906
+ int buflen = r->iov.iov_len;
907
+ trace_ufs_scsi_emulate_write_data(buflen);
908
+ r->iov.iov_len = 0;
909
+ scsi_req_data(&r->req, buflen);
910
+ return;
911
+ }
912
+
913
+ switch (req->cmd.buf[0]) {
914
+ case MODE_SELECT_10:
915
+ /* This also clears the sense buffer for REQUEST SENSE. */
916
+ ufs_scsi_emulate_mode_select(r, r->iov.iov_base);
917
+ break;
918
+ default:
919
+ abort();
920
+ }
921
+}
922
+
923
+/* Return a pointer to the data buffer. */
924
+static uint8_t *ufs_scsi_get_buf(SCSIRequest *req)
925
+{
926
+ UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req);
927
+
928
+ return (uint8_t *)r->iov.iov_base;
929
+}
930
+
931
+static int32_t ufs_scsi_dma_command(SCSIRequest *req, uint8_t *buf)
932
+{
933
+ UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req);
934
+ UfsLu *lu = DO_UPCAST(UfsLu, qdev, req->dev);
935
+ uint32_t len;
936
+ uint8_t command;
937
+
938
+ command = buf[0];
939
+
940
+ if (!blk_is_available(lu->qdev.conf.blk)) {
941
+ scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
942
+ return 0;
943
+ }
944
+
945
+ len = scsi_data_cdb_xfer(r->req.cmd.buf);
946
+ switch (command) {
947
+ case READ_6:
948
+ case READ_10:
949
+ trace_ufs_scsi_dma_command_READ(r->req.cmd.lba, len);
950
+ if (r->req.cmd.buf[1] & 0xe0) {
951
+ goto illegal_request;
952
+ }
953
+ if (!check_lba_range(lu, r->req.cmd.lba, len)) {
954
+ goto illegal_lba;
955
+ }
956
+ r->sector = r->req.cmd.lba * (lu->qdev.blocksize / BDRV_SECTOR_SIZE);
957
+ r->sector_count = len * (lu->qdev.blocksize / BDRV_SECTOR_SIZE);
958
+ break;
959
+ case WRITE_6:
960
+ case WRITE_10:
961
+ trace_ufs_scsi_dma_command_WRITE(r->req.cmd.lba, len);
962
+ if (!blk_is_writable(lu->qdev.conf.blk)) {
963
+ scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED));
964
+ return 0;
965
+ }
966
+ if (r->req.cmd.buf[1] & 0xe0) {
967
+ goto illegal_request;
968
+ }
969
+ if (!check_lba_range(lu, r->req.cmd.lba, len)) {
970
+ goto illegal_lba;
971
+ }
972
+ r->sector = r->req.cmd.lba * (lu->qdev.blocksize / BDRV_SECTOR_SIZE);
973
+ r->sector_count = len * (lu->qdev.blocksize / BDRV_SECTOR_SIZE);
974
+ break;
975
+ default:
976
+ abort();
977
+ illegal_request:
978
+ scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
979
+ return 0;
980
+ illegal_lba:
981
+ scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE));
982
+ return 0;
983
+ }
984
+ r->need_fua_emulation = ((r->req.cmd.buf[1] & 8) != 0);
985
+ if (r->sector_count == 0) {
986
+ scsi_req_complete(&r->req, GOOD);
987
+ }
988
+ assert(r->iov.iov_len == 0);
989
+ if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
990
+ return -r->sector_count * BDRV_SECTOR_SIZE;
991
+ } else {
992
+ return r->sector_count * BDRV_SECTOR_SIZE;
993
+ }
994
+}
995
+
996
+static void scsi_write_do_fua(UfsSCSIReq *r)
997
+{
998
+ UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev);
999
+
1000
+ assert(r->req.aiocb == NULL);
1001
+ assert(!r->req.io_canceled);
1002
+
1003
+ if (r->need_fua_emulation) {
1004
+ block_acct_start(blk_get_stats(lu->qdev.conf.blk), &r->acct, 0,
1005
+ BLOCK_ACCT_FLUSH);
1006
+ r->req.aiocb = blk_aio_flush(lu->qdev.conf.blk, scsi_aio_complete, r);
1007
+ return;
1008
+ }
1009
+
1010
+ scsi_req_complete(&r->req, GOOD);
1011
+ scsi_req_unref(&r->req);
1012
+}
1013
+
1014
+static void scsi_dma_complete_noio(UfsSCSIReq *r, int ret)
1015
+{
1016
+ assert(r->req.aiocb == NULL);
1017
+ if (ufs_scsi_req_check_error(r, ret, false)) {
1018
+ goto done;
1019
+ }
1020
+
1021
+ r->sector += r->sector_count;
1022
+ r->sector_count = 0;
1023
+ if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
1024
+ scsi_write_do_fua(r);
1025
+ return;
1026
+ } else {
1027
+ scsi_req_complete(&r->req, GOOD);
1028
+ }
1029
+
1030
+done:
1031
+ scsi_req_unref(&r->req);
1032
+}
1033
+
1034
+static void scsi_dma_complete(void *opaque, int ret)
1035
+{
1036
+ UfsSCSIReq *r = (UfsSCSIReq *)opaque;
1037
+ UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev);
1038
+
1039
+ assert(r->req.aiocb != NULL);
1040
+ r->req.aiocb = NULL;
1041
+
1042
+ aio_context_acquire(blk_get_aio_context(lu->qdev.conf.blk));
1043
+ if (ret < 0) {
1044
+ block_acct_failed(blk_get_stats(lu->qdev.conf.blk), &r->acct);
1045
+ } else {
1046
+ block_acct_done(blk_get_stats(lu->qdev.conf.blk), &r->acct);
1047
+ }
1048
+ scsi_dma_complete_noio(r, ret);
1049
+ aio_context_release(blk_get_aio_context(lu->qdev.conf.blk));
1050
+}
1051
+
1052
+static BlockAIOCB *scsi_dma_readv(int64_t offset, QEMUIOVector *iov,
1053
+ BlockCompletionFunc *cb, void *cb_opaque,
1054
+ void *opaque)
1055
+{
1056
+ UfsSCSIReq *r = opaque;
1057
+ UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev);
1058
+ return blk_aio_preadv(lu->qdev.conf.blk, offset, iov, 0, cb, cb_opaque);
1059
+}
1060
+
1061
+static void scsi_init_iovec(UfsSCSIReq *r, size_t size)
1062
+{
1063
+ UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev);
1064
+
1065
+ if (!r->iov.iov_base) {
1066
+ r->buflen = size;
1067
+ r->iov.iov_base = blk_blockalign(lu->qdev.conf.blk, r->buflen);
1068
+ }
1069
+ r->iov.iov_len = MIN(r->sector_count * BDRV_SECTOR_SIZE, r->buflen);
1070
+ qemu_iovec_init_external(&r->qiov, &r->iov, 1);
1071
+}
1072
+
1073
+static void scsi_read_complete_noio(UfsSCSIReq *r, int ret)
1074
+{
1075
+ uint32_t n;
1076
+
1077
+ assert(r->req.aiocb == NULL);
1078
+ if (ufs_scsi_req_check_error(r, ret, false)) {
1079
+ goto done;
1080
+ }
1081
+
1082
+ n = r->qiov.size / BDRV_SECTOR_SIZE;
1083
+ r->sector += n;
1084
+ r->sector_count -= n;
1085
+ scsi_req_data(&r->req, r->qiov.size);
1086
+
1087
+done:
1088
+ scsi_req_unref(&r->req);
1089
+}
1090
+
1091
+static void scsi_read_complete(void *opaque, int ret)
1092
+{
1093
+ UfsSCSIReq *r = (UfsSCSIReq *)opaque;
1094
+ UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev);
1095
+
1096
+ assert(r->req.aiocb != NULL);
1097
+ r->req.aiocb = NULL;
1098
+ trace_ufs_scsi_read_data_count(r->sector_count);
1099
+ aio_context_acquire(blk_get_aio_context(lu->qdev.conf.blk));
1100
+ if (ret < 0) {
1101
+ block_acct_failed(blk_get_stats(lu->qdev.conf.blk), &r->acct);
1102
+ } else {
1103
+ block_acct_done(blk_get_stats(lu->qdev.conf.blk), &r->acct);
1104
+ trace_ufs_scsi_read_complete(r->req.tag, r->qiov.size);
1105
+ }
1106
+ scsi_read_complete_noio(r, ret);
1107
+ aio_context_release(blk_get_aio_context(lu->qdev.conf.blk));
1108
+}
1109
+
1110
+/* Actually issue a read to the block device. */
1111
+static void scsi_do_read(UfsSCSIReq *r, int ret)
1112
+{
1113
+ UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev);
1114
+
1115
+ assert(r->req.aiocb == NULL);
1116
+ if (ufs_scsi_req_check_error(r, ret, false)) {
1117
+ goto done;
1118
+ }
1119
+
1120
+ /* The request is used as the AIO opaque value, so add a ref. */
1121
+ scsi_req_ref(&r->req);
1122
+
1123
+ if (r->req.sg) {
1124
+ dma_acct_start(lu->qdev.conf.blk, &r->acct, r->req.sg, BLOCK_ACCT_READ);
1125
+ r->req.residual -= r->req.sg->size;
1126
+ r->req.aiocb = dma_blk_io(
1127
+ blk_get_aio_context(lu->qdev.conf.blk), r->req.sg,
1128
+ r->sector << BDRV_SECTOR_BITS, BDRV_SECTOR_SIZE, scsi_dma_readv, r,
1129
+ scsi_dma_complete, r, DMA_DIRECTION_FROM_DEVICE);
1130
+ } else {
1131
+ scsi_init_iovec(r, SCSI_DMA_BUF_SIZE);
1132
+ block_acct_start(blk_get_stats(lu->qdev.conf.blk), &r->acct,
1133
+ r->qiov.size, BLOCK_ACCT_READ);
1134
+ r->req.aiocb = scsi_dma_readv(r->sector << BDRV_SECTOR_BITS, &r->qiov,
1135
+ scsi_read_complete, r, r);
1136
+ }
1137
+
1138
+done:
1139
+ scsi_req_unref(&r->req);
1140
+}
1141
+
1142
+static void scsi_do_read_cb(void *opaque, int ret)
1143
+{
1144
+ UfsSCSIReq *r = (UfsSCSIReq *)opaque;
1145
+ UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev);
1146
+
1147
+ assert(r->req.aiocb != NULL);
1148
+ r->req.aiocb = NULL;
1149
+
1150
+ aio_context_acquire(blk_get_aio_context(lu->qdev.conf.blk));
1151
+ if (ret < 0) {
1152
+ block_acct_failed(blk_get_stats(lu->qdev.conf.blk), &r->acct);
1153
+ } else {
1154
+ block_acct_done(blk_get_stats(lu->qdev.conf.blk), &r->acct);
1155
+ }
1156
+ scsi_do_read(opaque, ret);
1157
+ aio_context_release(blk_get_aio_context(lu->qdev.conf.blk));
1158
+}
1159
+
1160
+/* Read more data from scsi device into buffer. */
1161
+static void scsi_read_data(SCSIRequest *req)
1162
+{
1163
+ UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req);
1164
+ UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev);
1165
+ bool first;
1166
+
1167
+ trace_ufs_scsi_read_data_count(r->sector_count);
1168
+ if (r->sector_count == 0) {
1169
+ /* This also clears the sense buffer for REQUEST SENSE. */
1170
+ scsi_req_complete(&r->req, GOOD);
1171
+ return;
1172
+ }
1173
+
1174
+ /* No data transfer may already be in progress */
1175
+ assert(r->req.aiocb == NULL);
1176
+
1177
+ /* The request is used as the AIO opaque value, so add a ref. */
1178
+ scsi_req_ref(&r->req);
1179
+ if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
1180
+ trace_ufs_scsi_read_data_invalid();
1181
+ scsi_read_complete_noio(r, -EINVAL);
1182
+ return;
1183
+ }
1184
+
1185
+ if (!blk_is_available(req->dev->conf.blk)) {
1186
+ scsi_read_complete_noio(r, -ENOMEDIUM);
1187
+ return;
1188
+ }
1189
+
1190
+ first = !r->started;
1191
+ r->started = true;
1192
+ if (first && r->need_fua_emulation) {
1193
+ block_acct_start(blk_get_stats(lu->qdev.conf.blk), &r->acct, 0,
1194
+ BLOCK_ACCT_FLUSH);
1195
+ r->req.aiocb = blk_aio_flush(lu->qdev.conf.blk, scsi_do_read_cb, r);
1196
+ } else {
1197
+ scsi_do_read(r, 0);
1198
+ }
1199
+}
1200
+
1201
+static void scsi_write_complete_noio(UfsSCSIReq *r, int ret)
1202
+{
1203
+ uint32_t n;
1204
+
1205
+ assert(r->req.aiocb == NULL);
1206
+ if (ufs_scsi_req_check_error(r, ret, false)) {
1207
+ goto done;
1208
+ }
1209
+
1210
+ n = r->qiov.size / BDRV_SECTOR_SIZE;
1211
+ r->sector += n;
1212
+ r->sector_count -= n;
1213
+ if (r->sector_count == 0) {
1214
+ scsi_write_do_fua(r);
1215
+ return;
1216
+ } else {
1217
+ scsi_init_iovec(r, SCSI_DMA_BUF_SIZE);
1218
+ trace_ufs_scsi_write_complete_noio(r->req.tag, r->qiov.size);
1219
+ scsi_req_data(&r->req, r->qiov.size);
1220
+ }
1221
+
1222
+done:
1223
+ scsi_req_unref(&r->req);
1224
+}
1225
+
1226
+static void scsi_write_complete(void *opaque, int ret)
1227
+{
1228
+ UfsSCSIReq *r = (UfsSCSIReq *)opaque;
1229
+ UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev);
1230
+
1231
+ assert(r->req.aiocb != NULL);
1232
+ r->req.aiocb = NULL;
1233
+
1234
+ aio_context_acquire(blk_get_aio_context(lu->qdev.conf.blk));
1235
+ if (ret < 0) {
1236
+ block_acct_failed(blk_get_stats(lu->qdev.conf.blk), &r->acct);
1237
+ } else {
1238
+ block_acct_done(blk_get_stats(lu->qdev.conf.blk), &r->acct);
1239
+ }
1240
+ scsi_write_complete_noio(r, ret);
1241
+ aio_context_release(blk_get_aio_context(lu->qdev.conf.blk));
1242
+}
1243
+
1244
+static BlockAIOCB *scsi_dma_writev(int64_t offset, QEMUIOVector *iov,
1245
+ BlockCompletionFunc *cb, void *cb_opaque,
1246
+ void *opaque)
1247
+{
1248
+ UfsSCSIReq *r = opaque;
1249
+ UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev);
1250
+ return blk_aio_pwritev(lu->qdev.conf.blk, offset, iov, 0, cb, cb_opaque);
1251
+}
1252
+
1253
+static void scsi_write_data(SCSIRequest *req)
1254
+{
1255
+ UfsSCSIReq *r = DO_UPCAST(UfsSCSIReq, req, req);
1256
+ UfsLu *lu = DO_UPCAST(UfsLu, qdev, r->req.dev);
1257
+
1258
+ /* No data transfer may already be in progress */
1259
+ assert(r->req.aiocb == NULL);
1260
+
1261
+ /* The request is used as the AIO opaque value, so add a ref. */
1262
+ scsi_req_ref(&r->req);
1263
+ if (r->req.cmd.mode != SCSI_XFER_TO_DEV) {
1264
+ trace_ufs_scsi_write_data_invalid();
1265
+ scsi_write_complete_noio(r, -EINVAL);
1266
+ return;
1267
+ }
1268
+
1269
+ if (!r->req.sg && !r->qiov.size) {
1270
+ /* Called for the first time. Ask the driver to send us more data. */
1271
+ r->started = true;
1272
+ scsi_write_complete_noio(r, 0);
1273
+ return;
1274
+ }
1275
+ if (!blk_is_available(req->dev->conf.blk)) {
1276
+ scsi_write_complete_noio(r, -ENOMEDIUM);
1277
+ return;
1278
+ }
1279
+
1280
+ if (r->req.sg) {
1281
+ dma_acct_start(lu->qdev.conf.blk, &r->acct, r->req.sg,
1282
+ BLOCK_ACCT_WRITE);
1283
+ r->req.residual -= r->req.sg->size;
1284
+ r->req.aiocb = dma_blk_io(
1285
+ blk_get_aio_context(lu->qdev.conf.blk), r->req.sg,
1286
+ r->sector << BDRV_SECTOR_BITS, BDRV_SECTOR_SIZE, scsi_dma_writev, r,
1287
+ scsi_dma_complete, r, DMA_DIRECTION_TO_DEVICE);
1288
+ } else {
1289
+ block_acct_start(blk_get_stats(lu->qdev.conf.blk), &r->acct,
1290
+ r->qiov.size, BLOCK_ACCT_WRITE);
1291
+ r->req.aiocb = scsi_dma_writev(r->sector << BDRV_SECTOR_BITS, &r->qiov,
1292
+ scsi_write_complete, r, r);
1293
+ }
1294
+}
1295
+
1296
+static const SCSIReqOps ufs_scsi_emulate_reqops = {
1297
+ .size = sizeof(UfsSCSIReq),
1298
+ .free_req = ufs_scsi_free_request,
1299
+ .send_command = ufs_scsi_emulate_command,
1300
+ .read_data = ufs_scsi_emulate_read_data,
1301
+ .write_data = ufs_scsi_emulate_write_data,
1302
+ .get_buf = ufs_scsi_get_buf,
1303
+};
1304
+
1305
+static const SCSIReqOps ufs_scsi_dma_reqops = {
1306
+ .size = sizeof(UfsSCSIReq),
1307
+ .free_req = ufs_scsi_free_request,
1308
+ .send_command = ufs_scsi_dma_command,
1309
+ .read_data = scsi_read_data,
1310
+ .write_data = scsi_write_data,
1311
+ .get_buf = ufs_scsi_get_buf,
1312
+};
1313
+
1314
+/*
1315
+ * Following commands are not yet supported
1316
+ * PRE_FETCH(10),
1317
+ * UNMAP,
1318
+ * WRITE_BUFFER, READ_BUFFER,
1319
+ * SECURITY_PROTOCOL_IN, SECURITY_PROTOCOL_OUT
1320
+ */
1321
+static const SCSIReqOps *const ufs_scsi_reqops_dispatch[256] = {
1322
+ [TEST_UNIT_READY] = &ufs_scsi_emulate_reqops,
1323
+ [INQUIRY] = &ufs_scsi_emulate_reqops,
1324
+ [MODE_SENSE_10] = &ufs_scsi_emulate_reqops,
1325
+ [START_STOP] = &ufs_scsi_emulate_reqops,
1326
+ [READ_CAPACITY_10] = &ufs_scsi_emulate_reqops,
1327
+ [REQUEST_SENSE] = &ufs_scsi_emulate_reqops,
1328
+ [SYNCHRONIZE_CACHE] = &ufs_scsi_emulate_reqops,
1329
+ [MODE_SELECT_10] = &ufs_scsi_emulate_reqops,
1330
+ [VERIFY_10] = &ufs_scsi_emulate_reqops,
1331
+ [FORMAT_UNIT] = &ufs_scsi_emulate_reqops,
1332
+ [SERVICE_ACTION_IN_16] = &ufs_scsi_emulate_reqops,
1333
+ [SEND_DIAGNOSTIC] = &ufs_scsi_emulate_reqops,
1334
+
1335
+ [READ_6] = &ufs_scsi_dma_reqops,
1336
+ [READ_10] = &ufs_scsi_dma_reqops,
1337
+ [WRITE_6] = &ufs_scsi_dma_reqops,
1338
+ [WRITE_10] = &ufs_scsi_dma_reqops,
1339
+};
1340
+
1341
+static SCSIRequest *scsi_new_request(SCSIDevice *dev, uint32_t tag,
1342
+ uint32_t lun, uint8_t *buf,
1343
+ void *hba_private)
1344
+{
1345
+ UfsLu *lu = DO_UPCAST(UfsLu, qdev, dev);
1346
+ SCSIRequest *req;
1347
+ const SCSIReqOps *ops;
1348
+ uint8_t command;
1349
+
1350
+ command = buf[0];
1351
+ ops = ufs_scsi_reqops_dispatch[command];
1352
+ if (!ops) {
1353
+ ops = &ufs_scsi_emulate_reqops;
1354
+ }
1355
+ req = scsi_req_alloc(ops, &lu->qdev, tag, lun, hba_private);
1356
+
1357
+ return req;
1358
+}
1359
+
1360
+static Property ufs_lu_props[] = {
1361
+ DEFINE_PROP_DRIVE("drive", UfsLu, qdev.conf.blk),
1362
+ DEFINE_PROP_END_OF_LIST(),
1363
+};
1364
+
1365
+static bool ufs_lu_brdv_init(UfsLu *lu, Error **errp)
1366
+{
1367
+ SCSIDevice *dev = &lu->qdev;
1368
+ bool read_only;
1369
+
1370
+ if (!lu->qdev.conf.blk) {
1371
+ error_setg(errp, "drive property not set");
1372
+ return false;
1373
+ }
1374
+
1375
+ if (!blkconf_blocksizes(&lu->qdev.conf, errp)) {
1376
+ return false;
1377
+ }
1378
+
1379
+ if (blk_get_aio_context(lu->qdev.conf.blk) != qemu_get_aio_context() &&
1380
+ !lu->qdev.hba_supports_iothread) {
1381
+ error_setg(errp, "HBA does not support iothreads");
1382
+ return false;
1383
+ }
1384
+
1385
+ read_only = !blk_supports_write_perm(lu->qdev.conf.blk);
1386
+
1387
+ if (!blkconf_apply_backend_options(&dev->conf, read_only,
1388
+ dev->type == TYPE_DISK, errp)) {
1389
+ return false;
1390
+ }
1391
+
1392
+ if (blk_is_sg(lu->qdev.conf.blk)) {
1393
+ error_setg(errp, "unwanted /dev/sg*");
1394
+ return false;
1395
+ }
1396
+
1397
+ blk_iostatus_enable(lu->qdev.conf.blk);
1398
+ return true;
1399
+}
1400
+
1401
+static bool ufs_add_lu(UfsHc *u, UfsLu *lu, Error **errp)
1402
+{
1403
+ BlockBackend *blk = lu->qdev.conf.blk;
1404
+ int64_t brdv_len = blk_getlength(blk);
1405
+ uint64_t raw_dev_cap =
1406
+ be64_to_cpu(u->geometry_desc.total_raw_device_capacity);
1407
+
1408
+ if (u->device_desc.number_lu >= UFS_MAX_LUS) {
1409
+ error_setg(errp, "ufs host controller has too many logical units.");
1410
+ return false;
1411
+ }
1412
+
1413
+ if (u->lus[lu->lun] != NULL) {
1414
+ error_setg(errp, "ufs logical unit %d already exists.", lu->lun);
1415
+ return false;
1416
+ }
1417
+
1418
+ u->lus[lu->lun] = lu;
1419
+ u->device_desc.number_lu++;
1420
+ raw_dev_cap += (brdv_len >> UFS_GEOMETRY_CAPACITY_SHIFT);
1421
+ u->geometry_desc.total_raw_device_capacity = cpu_to_be64(raw_dev_cap);
1422
+ return true;
1423
+}
1424
+
1425
+static inline uint8_t ufs_log2(uint64_t input)
1426
+{
1427
+ int log = 0;
1428
+ while (input >>= 1) {
1429
+ log++;
1430
+ }
1431
+ return log;
1432
+}
1433
+
1434
+static void ufs_init_lu(UfsLu *lu)
1435
+{
1436
+ BlockBackend *blk = lu->qdev.conf.blk;
1437
+ int64_t brdv_len = blk_getlength(blk);
1438
+
1439
+ lu->lun = lu->qdev.lun;
1440
+ memset(&lu->unit_desc, 0, sizeof(lu->unit_desc));
1441
+ lu->unit_desc.length = sizeof(UnitDescriptor);
1442
+ lu->unit_desc.descriptor_idn = QUERY_DESC_IDN_UNIT;
1443
+ lu->unit_desc.lu_enable = 0x01;
1444
+ lu->unit_desc.logical_block_size = ufs_log2(lu->qdev.blocksize);
1445
+ lu->unit_desc.unit_index = lu->qdev.lun;
1446
+ lu->unit_desc.logical_block_count =
1447
+ cpu_to_be64(brdv_len / (1 << lu->unit_desc.logical_block_size));
1448
+}
1449
+
1450
+static bool ufs_lu_check_constraints(UfsLu *lu, Error **errp)
1451
+{
1452
+ if (!lu->qdev.conf.blk) {
1453
+ error_setg(errp, "drive property not set");
1454
+ return false;
1455
+ }
1456
+
1457
+ if (lu->qdev.channel != 0) {
1458
+ error_setg(errp, "ufs logical unit does not support channel");
1459
+ return false;
1460
+ }
1461
+
1462
+ if (lu->qdev.lun >= UFS_MAX_LUS) {
1463
+ error_setg(errp, "lun must be between 1 and %d", UFS_MAX_LUS - 1);
1464
+ return false;
1465
+ }
1466
+
1467
+ return true;
1468
+}
1469
+
1470
+static void ufs_lu_realize(SCSIDevice *dev, Error **errp)
1471
+{
1472
+ UfsLu *lu = DO_UPCAST(UfsLu, qdev, dev);
1473
+ BusState *s = qdev_get_parent_bus(&dev->qdev);
1474
+ UfsHc *u = UFS(s->parent);
1475
+ AioContext *ctx = NULL;
1476
+ uint64_t nb_sectors, nb_blocks;
1477
+
1478
+ if (!ufs_lu_check_constraints(lu, errp)) {
1479
+ return;
1480
+ }
1481
+
1482
+ if (lu->qdev.conf.blk) {
1483
+ ctx = blk_get_aio_context(lu->qdev.conf.blk);
1484
+ aio_context_acquire(ctx);
1485
+ if (!blkconf_blocksizes(&lu->qdev.conf, errp)) {
1486
+ goto out;
1487
+ }
1488
+ }
1489
+ lu->qdev.blocksize = UFS_BLOCK_SIZE;
1490
+ blk_get_geometry(lu->qdev.conf.blk, &nb_sectors);
1491
+ nb_blocks = nb_sectors / (lu->qdev.blocksize / BDRV_SECTOR_SIZE);
1492
+ if (nb_blocks > UINT32_MAX) {
1493
+ nb_blocks = UINT32_MAX;
1494
+ }
1495
+ lu->qdev.max_lba = nb_blocks;
1496
+ lu->qdev.type = TYPE_DISK;
1497
+
1498
+ ufs_init_lu(lu);
1499
+ if (!ufs_add_lu(u, lu, errp)) {
1500
+ goto out;
1501
+ }
1502
+
1503
+ ufs_lu_brdv_init(lu, errp);
1504
+out:
1505
+ if (ctx) {
1506
+ aio_context_release(ctx);
1507
+ }
1508
+}
1509
+
1510
+static void ufs_lu_unrealize(SCSIDevice *dev)
1511
+{
1512
+ UfsLu *lu = DO_UPCAST(UfsLu, qdev, dev);
1513
+
1514
+ blk_drain(lu->qdev.conf.blk);
1515
+}
1516
+
1517
+static void ufs_wlu_realize(DeviceState *qdev, Error **errp)
1518
+{
1519
+ UfsWLu *wlu = UFSWLU(qdev);
1520
+ SCSIDevice *dev = &wlu->qdev;
1521
+
1522
+ if (!is_wlun(dev->lun)) {
1523
+ error_setg(errp, "not well-known logical unit number");
1524
+ return;
1525
+ }
1526
+
1527
+ QTAILQ_INIT(&dev->requests);
1528
+}
1529
+
1530
+static void ufs_lu_class_init(ObjectClass *oc, void *data)
1531
+{
1532
+ DeviceClass *dc = DEVICE_CLASS(oc);
1533
+ SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(oc);
1534
+
1535
+ sc->realize = ufs_lu_realize;
1536
+ sc->unrealize = ufs_lu_unrealize;
1537
+ sc->alloc_req = scsi_new_request;
1538
+ dc->bus_type = TYPE_UFS_BUS;
1539
+ device_class_set_props(dc, ufs_lu_props);
1540
+ dc->desc = "Virtual UFS logical unit";
1541
+}
1542
+
1543
+static void ufs_wlu_class_init(ObjectClass *oc, void *data)
1544
+{
1545
+ DeviceClass *dc = DEVICE_CLASS(oc);
1546
+ SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(oc);
1547
+
1548
+ /*
1549
+ * The realize() function of TYPE_SCSI_DEVICE causes a segmentation fault
1550
+ * if a block drive does not exist. Define a new realize function for
1551
+ * well-known LUs that do not have a block drive.
1552
+ */
1553
+ dc->realize = ufs_wlu_realize;
1554
+ sc->alloc_req = scsi_new_request;
1555
+ dc->bus_type = TYPE_UFS_BUS;
1556
+ dc->desc = "Virtual UFS well-known logical unit";
1557
+}
1558
+
1559
+static const TypeInfo ufs_lu_info = {
1560
+ .name = TYPE_UFS_LU,
1561
+ .parent = TYPE_SCSI_DEVICE,
1562
+ .class_init = ufs_lu_class_init,
1563
+ .instance_size = sizeof(UfsLu),
1564
+};
1565
+
1566
+static const TypeInfo ufs_wlu_info = {
1567
+ .name = TYPE_UFS_WLU,
1568
+ .parent = TYPE_SCSI_DEVICE,
1569
+ .class_init = ufs_wlu_class_init,
1570
+ .instance_size = sizeof(UfsWLu),
1571
+};
1572
+
1573
+static void ufs_lu_register_types(void)
1574
+{
1575
+ type_register_static(&ufs_lu_info);
1576
+ type_register_static(&ufs_wlu_info);
1577
+}
1578
+
1579
+type_init(ufs_lu_register_types)
1580
diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c
1581
index XXXXXXX..XXXXXXX 100644
1582
--- a/hw/ufs/ufs.c
1583
+++ b/hw/ufs/ufs.c
1584
@@ -XXX,XX +XXX,XX @@
1585
* SPDX-License-Identifier: GPL-2.0-or-later
1586
*/
1587
1588
+/**
1589
+ * Reference Specs: https://www.jedec.org/, 3.1
1590
+ *
1591
+ * Usage
1592
+ * -----
1593
+ *
1594
+ * Add options:
1595
+ * -drive file=<file>,if=none,id=<drive_id>
1596
+ * -device ufs,serial=<serial>,id=<bus_name>, \
1597
+ * nutrs=<N[optional]>,nutmrs=<N[optional]>
1598
+ * -device ufs-lu,drive=<drive_id>,bus=<bus_name>
1599
+ */
1600
+
1601
#include "qemu/osdep.h"
1602
#include "qapi/error.h"
1603
#include "migration/vmstate.h"
1604
@@ -XXX,XX +XXX,XX @@ static const MemoryRegionOps ufs_mmio_ops = {
1605
},
1606
};
1607
1608
+static QEMUSGList *ufs_get_sg_list(SCSIRequest *scsi_req)
1609
+{
1610
+ UfsRequest *req = scsi_req->hba_private;
1611
+ return req->sg;
1612
+}
1613
+
1614
+static void ufs_build_upiu_sense_data(UfsRequest *req, SCSIRequest *scsi_req)
1615
+{
1616
+ req->rsp_upiu.sr.sense_data_len = cpu_to_be16(scsi_req->sense_len);
1617
+ assert(scsi_req->sense_len <= SCSI_SENSE_LEN);
1618
+ memcpy(req->rsp_upiu.sr.sense_data, scsi_req->sense, scsi_req->sense_len);
1619
+}
1620
+
1621
static void ufs_build_upiu_header(UfsRequest *req, uint8_t trans_type,
1622
uint8_t flags, uint8_t response,
1623
uint8_t scsi_status,
1624
@@ -XXX,XX +XXX,XX @@ static void ufs_build_upiu_header(UfsRequest *req, uint8_t trans_type,
1625
req->rsp_upiu.header.data_segment_length = cpu_to_be16(data_segment_length);
1626
}
1627
1628
+static void ufs_scsi_command_complete(SCSIRequest *scsi_req, size_t resid)
1629
+{
1630
+ UfsRequest *req = scsi_req->hba_private;
1631
+ int16_t status = scsi_req->status;
1632
+ uint32_t expected_len = be32_to_cpu(req->req_upiu.sc.exp_data_transfer_len);
1633
+ uint32_t transfered_len = scsi_req->cmd.xfer - resid;
1634
+ uint8_t flags = 0, response = COMMAND_RESULT_SUCESS;
1635
+ uint16_t data_segment_length;
1636
+
1637
+ if (expected_len > transfered_len) {
1638
+ req->rsp_upiu.sr.residual_transfer_count =
1639
+ cpu_to_be32(expected_len - transfered_len);
1640
+ flags |= UFS_UPIU_FLAG_UNDERFLOW;
1641
+ } else if (expected_len < transfered_len) {
1642
+ req->rsp_upiu.sr.residual_transfer_count =
1643
+ cpu_to_be32(transfered_len - expected_len);
1644
+ flags |= UFS_UPIU_FLAG_OVERFLOW;
1645
+ }
1646
+
1647
+ if (status != 0) {
1648
+ ufs_build_upiu_sense_data(req, scsi_req);
1649
+ response = COMMAND_RESULT_FAIL;
1650
+ }
1651
+
1652
+ data_segment_length = cpu_to_be16(scsi_req->sense_len +
1653
+ sizeof(req->rsp_upiu.sr.sense_data_len));
1654
+ ufs_build_upiu_header(req, UPIU_TRANSACTION_RESPONSE, flags, response,
1655
+ status, data_segment_length);
1656
+
1657
+ ufs_complete_req(req, UFS_REQUEST_SUCCESS);
1658
+
1659
+ scsi_req->hba_private = NULL;
1660
+ scsi_req_unref(scsi_req);
1661
+}
1662
+
1663
+static const struct SCSIBusInfo ufs_scsi_info = {
1664
+ .tcq = true,
1665
+ .max_target = 0,
1666
+ .max_lun = UFS_MAX_LUS,
1667
+ .max_channel = 0,
1668
+
1669
+ .get_sg_list = ufs_get_sg_list,
1670
+ .complete = ufs_scsi_command_complete,
1671
+};
1672
+
1673
+static UfsReqResult ufs_exec_scsi_cmd(UfsRequest *req)
1674
+{
1675
+ UfsHc *u = req->hc;
1676
+ uint8_t lun = req->req_upiu.header.lun;
1677
+ uint8_t task_tag = req->req_upiu.header.task_tag;
1678
+ SCSIDevice *dev = NULL;
1679
+
1680
+ trace_ufs_exec_scsi_cmd(req->slot, lun, req->req_upiu.sc.cdb[0]);
1681
+
1682
+ if (!is_wlun(lun)) {
1683
+ if (lun >= u->device_desc.number_lu) {
1684
+ trace_ufs_err_scsi_cmd_invalid_lun(lun);
1685
+ return UFS_REQUEST_FAIL;
1686
+ } else if (u->lus[lun] == NULL) {
1687
+ trace_ufs_err_scsi_cmd_invalid_lun(lun);
1688
+ return UFS_REQUEST_FAIL;
1689
+ }
1690
+ }
1691
+
1692
+ switch (lun) {
1693
+ case UFS_UPIU_REPORT_LUNS_WLUN:
1694
+ dev = &u->report_wlu->qdev;
1695
+ break;
1696
+ case UFS_UPIU_UFS_DEVICE_WLUN:
1697
+ dev = &u->dev_wlu->qdev;
1698
+ break;
1699
+ case UFS_UPIU_BOOT_WLUN:
1700
+ dev = &u->boot_wlu->qdev;
1701
+ break;
1702
+ case UFS_UPIU_RPMB_WLUN:
1703
+ dev = &u->rpmb_wlu->qdev;
1704
+ break;
1705
+ default:
1706
+ dev = &u->lus[lun]->qdev;
1707
+ }
1708
+
1709
+ SCSIRequest *scsi_req = scsi_req_new(
1710
+ dev, task_tag, lun, req->req_upiu.sc.cdb, UFS_CDB_SIZE, req);
1711
+
1712
+ uint32_t len = scsi_req_enqueue(scsi_req);
1713
+ if (len) {
1714
+ scsi_req_continue(scsi_req);
1715
+ }
1716
+
1717
+ return UFS_REQUEST_NO_COMPLETE;
1718
+}
1719
+
1720
static UfsReqResult ufs_exec_nop_cmd(UfsRequest *req)
1721
{
1722
trace_ufs_exec_nop_cmd(req->slot);
1723
@@ -XXX,XX +XXX,XX @@ static const RpmbUnitDescriptor rpmb_unit_desc = {
1724
1725
static QueryRespCode ufs_read_unit_desc(UfsRequest *req)
1726
{
1727
+ UfsHc *u = req->hc;
1728
uint8_t lun = req->req_upiu.qr.index;
1729
1730
- if (lun != UFS_UPIU_RPMB_WLUN && lun > UFS_MAX_LUS) {
1731
+ if (lun != UFS_UPIU_RPMB_WLUN &&
1732
+ (lun > UFS_MAX_LUS || u->lus[lun] == NULL)) {
1733
trace_ufs_err_query_invalid_index(req->req_upiu.qr.opcode, lun);
1734
return QUERY_RESULT_INVALID_INDEX;
26
}
1735
}
27
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
1736
@@ -XXX,XX +XXX,XX @@ static QueryRespCode ufs_read_unit_desc(UfsRequest *req)
28
return -ENOTSUP;
1737
if (lun == UFS_UPIU_RPMB_WLUN) {
1738
memcpy(&req->rsp_upiu.qr.data, &rpmb_unit_desc, rpmb_unit_desc.length);
1739
} else {
1740
- /* unit descriptor is not yet supported */
1741
- return QUERY_RESULT_INVALID_INDEX;
1742
+ memcpy(&req->rsp_upiu.qr.data, &u->lus[lun]->unit_desc,
1743
+ sizeof(u->lus[lun]->unit_desc));
29
}
1744
}
30
1745
31
- if (offset & 511) {
1746
return QUERY_RESULT_SUCCESS;
32
- error_setg(errp, "The new size must be a multiple of 512");
1747
@@ -XXX,XX +XXX,XX @@ static void ufs_exec_req(UfsRequest *req)
33
+ if (!QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)) {
1748
req_result = ufs_exec_nop_cmd(req);
34
+ error_setg(errp, "The new size must be a multiple of %u",
1749
break;
35
+ (unsigned) BDRV_SECTOR_SIZE);
1750
case UPIU_TRANSACTION_COMMAND:
36
return -EINVAL;
1751
- /* Not yet implemented */
1752
- req_result = UFS_REQUEST_FAIL;
1753
+ req_result = ufs_exec_scsi_cmd(req);
1754
break;
1755
case UPIU_TRANSACTION_QUERY_REQ:
1756
req_result = ufs_exec_query_cmd(req);
1757
@@ -XXX,XX +XXX,XX @@ static void ufs_exec_req(UfsRequest *req)
1758
req_result = UFS_REQUEST_FAIL;
37
}
1759
}
38
1760
1761
- ufs_complete_req(req, req_result);
1762
+ /*
1763
+ * The ufs_complete_req for scsi commands is handled by the
1764
+ * ufs_scsi_command_complete() callback function. Therefore, to avoid
1765
+ * duplicate processing, ufs_complete_req() is not called for scsi commands.
1766
+ */
1767
+ if (req_result != UFS_REQUEST_NO_COMPLETE) {
1768
+ ufs_complete_req(req, req_result);
1769
+ }
1770
}
1771
1772
static void ufs_process_req(void *opaque)
1773
@@ -XXX,XX +XXX,XX @@ static void ufs_init_hc(UfsHc *u)
1774
u->flags.permanently_disable_fw_update = 1;
1775
}
1776
1777
+static bool ufs_init_wlu(UfsHc *u, UfsWLu **wlu, uint8_t wlun, Error **errp)
1778
+{
1779
+ UfsWLu *new_wlu = UFSWLU(qdev_new(TYPE_UFS_WLU));
1780
+
1781
+ qdev_prop_set_uint32(DEVICE(new_wlu), "lun", wlun);
1782
+
1783
+ /*
1784
+ * The well-known lu shares the same bus as the normal lu. If the well-known
1785
+ * lu writes the same channel value as the normal lu, the report will be
1786
+ * made not only for the normal lu but also for the well-known lu at
1787
+ * REPORT_LUN time. To prevent this, the channel value of normal lu is fixed
1788
+ * to 0 and the channel value of well-known lu is fixed to 1.
1789
+ */
1790
+ qdev_prop_set_uint32(DEVICE(new_wlu), "channel", 1);
1791
+ if (!qdev_realize_and_unref(DEVICE(new_wlu), BUS(&u->bus), errp)) {
1792
+ return false;
1793
+ }
1794
+
1795
+ *wlu = new_wlu;
1796
+ return true;
1797
+}
1798
+
1799
static void ufs_realize(PCIDevice *pci_dev, Error **errp)
1800
{
1801
UfsHc *u = UFS(pci_dev);
1802
@@ -XXX,XX +XXX,XX @@ static void ufs_realize(PCIDevice *pci_dev, Error **errp)
1803
return;
1804
}
1805
1806
+ qbus_init(&u->bus, sizeof(UfsBus), TYPE_UFS_BUS, &pci_dev->qdev,
1807
+ u->parent_obj.qdev.id);
1808
+ u->bus.parent_bus.info = &ufs_scsi_info;
1809
+
1810
ufs_init_state(u);
1811
ufs_init_hc(u);
1812
ufs_init_pci(u, pci_dev);
1813
+
1814
+ if (!ufs_init_wlu(u, &u->report_wlu, UFS_UPIU_REPORT_LUNS_WLUN, errp)) {
1815
+ return;
1816
+ }
1817
+
1818
+ if (!ufs_init_wlu(u, &u->dev_wlu, UFS_UPIU_UFS_DEVICE_WLUN, errp)) {
1819
+ return;
1820
+ }
1821
+
1822
+ if (!ufs_init_wlu(u, &u->boot_wlu, UFS_UPIU_BOOT_WLUN, errp)) {
1823
+ return;
1824
+ }
1825
+
1826
+ if (!ufs_init_wlu(u, &u->rpmb_wlu, UFS_UPIU_RPMB_WLUN, errp)) {
1827
+ return;
1828
+ }
1829
}
1830
1831
static void ufs_exit(PCIDevice *pci_dev)
1832
{
1833
UfsHc *u = UFS(pci_dev);
1834
1835
+ if (u->dev_wlu) {
1836
+ object_unref(OBJECT(u->dev_wlu));
1837
+ u->dev_wlu = NULL;
1838
+ }
1839
+
1840
+ if (u->report_wlu) {
1841
+ object_unref(OBJECT(u->report_wlu));
1842
+ u->report_wlu = NULL;
1843
+ }
1844
+
1845
+ if (u->rpmb_wlu) {
1846
+ object_unref(OBJECT(u->rpmb_wlu));
1847
+ u->rpmb_wlu = NULL;
1848
+ }
1849
+
1850
+ if (u->boot_wlu) {
1851
+ object_unref(OBJECT(u->boot_wlu));
1852
+ u->boot_wlu = NULL;
1853
+ }
1854
+
1855
qemu_bh_delete(u->doorbell_bh);
1856
qemu_bh_delete(u->complete_bh);
1857
1858
@@ -XXX,XX +XXX,XX @@ static void ufs_class_init(ObjectClass *oc, void *data)
1859
dc->vmsd = &ufs_vmstate;
1860
}
1861
1862
+static bool ufs_bus_check_address(BusState *qbus, DeviceState *qdev,
1863
+ Error **errp)
1864
+{
1865
+ SCSIDevice *dev = SCSI_DEVICE(qdev);
1866
+ UfsBusClass *ubc = UFS_BUS_GET_CLASS(qbus);
1867
+ UfsHc *u = UFS(qbus->parent);
1868
+
1869
+ if (strcmp(object_get_typename(OBJECT(dev)), TYPE_UFS_WLU) == 0) {
1870
+ if (dev->lun != UFS_UPIU_REPORT_LUNS_WLUN &&
1871
+ dev->lun != UFS_UPIU_UFS_DEVICE_WLUN &&
1872
+ dev->lun != UFS_UPIU_BOOT_WLUN && dev->lun != UFS_UPIU_RPMB_WLUN) {
1873
+ error_setg(errp, "bad well-known lun: %d", dev->lun);
1874
+ return false;
1875
+ }
1876
+
1877
+ if ((dev->lun == UFS_UPIU_REPORT_LUNS_WLUN && u->report_wlu != NULL) ||
1878
+ (dev->lun == UFS_UPIU_UFS_DEVICE_WLUN && u->dev_wlu != NULL) ||
1879
+ (dev->lun == UFS_UPIU_BOOT_WLUN && u->boot_wlu != NULL) ||
1880
+ (dev->lun == UFS_UPIU_RPMB_WLUN && u->rpmb_wlu != NULL)) {
1881
+ error_setg(errp, "well-known lun %d already exists", dev->lun);
1882
+ return false;
1883
+ }
1884
+
1885
+ return true;
1886
+ }
1887
+
1888
+ if (strcmp(object_get_typename(OBJECT(dev)), TYPE_UFS_LU) != 0) {
1889
+ error_setg(errp, "%s cannot be connected to ufs-bus",
1890
+ object_get_typename(OBJECT(dev)));
1891
+ return false;
1892
+ }
1893
+
1894
+ return ubc->parent_check_address(qbus, qdev, errp);
1895
+}
1896
+
1897
+static void ufs_bus_class_init(ObjectClass *class, void *data)
1898
+{
1899
+ BusClass *bc = BUS_CLASS(class);
1900
+ UfsBusClass *ubc = UFS_BUS_CLASS(class);
1901
+ ubc->parent_check_address = bc->check_address;
1902
+ bc->check_address = ufs_bus_check_address;
1903
+}
1904
+
1905
static const TypeInfo ufs_info = {
1906
.name = TYPE_UFS,
1907
.parent = TYPE_PCI_DEVICE,
1908
@@ -XXX,XX +XXX,XX @@ static const TypeInfo ufs_info = {
1909
.interfaces = (InterfaceInfo[]){ { INTERFACE_PCIE_DEVICE }, {} },
1910
};
1911
1912
+static const TypeInfo ufs_bus_info = {
1913
+ .name = TYPE_UFS_BUS,
1914
+ .parent = TYPE_SCSI_BUS,
1915
+ .class_init = ufs_bus_class_init,
1916
+ .class_size = sizeof(UfsBusClass),
1917
+ .instance_size = sizeof(UfsBus),
1918
+};
1919
+
1920
static void ufs_register_types(void)
1921
{
1922
type_register_static(&ufs_info);
1923
+ type_register_static(&ufs_bus_info);
1924
}
1925
1926
type_init(ufs_register_types)
1927
diff --git a/hw/ufs/meson.build b/hw/ufs/meson.build
1928
index XXXXXXX..XXXXXXX 100644
1929
--- a/hw/ufs/meson.build
1930
+++ b/hw/ufs/meson.build
1931
@@ -1 +1 @@
1932
-system_ss.add(when: 'CONFIG_UFS_PCI', if_true: files('ufs.c'))
1933
+system_ss.add(when: 'CONFIG_UFS_PCI', if_true: files('ufs.c', 'lu.c'))
1934
diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events
1935
index XXXXXXX..XXXXXXX 100644
1936
--- a/hw/ufs/trace-events
1937
+++ b/hw/ufs/trace-events
1938
@@ -XXX,XX +XXX,XX @@ ufs_exec_scsi_cmd(uint32_t slot, uint8_t lun, uint8_t opcode) "slot %"PRIu32", l
1939
ufs_exec_query_cmd(uint32_t slot, uint8_t opcode) "slot %"PRIu32", opcode 0x%"PRIx8""
1940
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""
1941
1942
+# lu.c
1943
+ufs_scsi_check_condition(uint32_t tag, uint8_t key, uint8_t asc, uint8_t ascq) "Command complete tag=0x%x sense=%d/%d/%d"
1944
+ufs_scsi_read_complete(uint32_t tag, size_t size) "Data ready tag=0x%x len=%zd"
1945
+ufs_scsi_read_data_count(uint32_t sector_count) "Read sector_count=%d"
1946
+ufs_scsi_read_data_invalid(void) "Data transfer direction invalid"
1947
+ufs_scsi_write_complete_noio(uint32_t tag, size_t size) "Write complete tag=0x%x more=%zd"
1948
+ufs_scsi_write_data_invalid(void) "Data transfer direction invalid"
1949
+ufs_scsi_emulate_vpd_page_00(size_t xfer) "Inquiry EVPD[Supported pages] buffer size %zd"
1950
+ufs_scsi_emulate_vpd_page_80_not_supported(void) "Inquiry EVPD[Serial number] not supported"
1951
+ufs_scsi_emulate_vpd_page_80(size_t xfer) "Inquiry EVPD[Serial number] buffer size %zd"
1952
+ufs_scsi_emulate_vpd_page_87(size_t xfer) "Inquiry EVPD[Mode Page Policy] buffer size %zd"
1953
+ufs_scsi_emulate_mode_sense(int cmd, int page, size_t xfer, int control) "Mode Sense(%d) (page %d, xfer %zd, page_control %d)"
1954
+ufs_scsi_emulate_read_data(int buflen) "Read buf_len=%d"
1955
+ufs_scsi_emulate_write_data(int buflen) "Write buf_len=%d"
1956
+ufs_scsi_emulate_command_START_STOP(void) "START STOP UNIT"
1957
+ufs_scsi_emulate_command_FORMAT_UNIT(void) "FORMAT UNIT"
1958
+ufs_scsi_emulate_command_SEND_DIAGNOSTIC(void) "SEND DIAGNOSTIC"
1959
+ufs_scsi_emulate_command_SAI_16(void) "SAI READ CAPACITY(16)"
1960
+ufs_scsi_emulate_command_SAI_unsupported(void) "Unsupported Service Action In"
1961
+ufs_scsi_emulate_command_MODE_SELECT_10(size_t xfer) "Mode Select(10) (len %zd)"
1962
+ufs_scsi_emulate_command_VERIFY(int bytchk) "Verify (bytchk %d)"
1963
+ufs_scsi_emulate_command_UNKNOWN(int cmd, const char *name) "Unknown SCSI command (0x%2.2x=%s)"
1964
+ufs_scsi_dma_command_READ(uint64_t lba, uint32_t len) "Read (block %" PRIu64 ", count %u)"
1965
+ufs_scsi_dma_command_WRITE(uint64_t lba, int len) "Write (block %" PRIu64 ", count %u)"
1966
+
1967
# error condition
1968
ufs_err_dma_read_utrd(uint32_t slot, uint64_t addr) "failed to read utrd. UTRLDBR slot %"PRIu32", UTRD dma addr %"PRIu64""
1969
ufs_err_dma_read_req_upiu(uint32_t slot, uint64_t addr) "failed to read req upiu. UTRLDBR slot %"PRIu32", request upiu addr %"PRIu64""
39
--
1970
--
40
2.24.1
1971
2.41.0
41
42
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Jeuk Kim <jeuk20.kim@gmail.com>
2
2
3
We can't access top after call bdrv_backup_top_drop, as it is already
3
This patch includes the following tests
4
freed at this time.
4
Test mmio read
5
Test ufs device initialization and ufs-lu recognition
6
Test I/O (Performs a write followed by a read to verify)
5
7
6
Also, no needs to unref target child by hand, it will be unrefed on
8
Signed-off-by: Jeuk Kim <jeuk20.kim@samsung.com>
7
bdrv_close() automatically.
9
Acked-by: Thomas Huth <thuth@redhat.com>
10
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
11
Message-id: af6b8d54c049490b3533a784a0aeac4798bb9217.1691062912.git.jeuk20.kim@samsung.com
12
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
13
---
14
MAINTAINERS | 1 +
15
tests/qtest/ufs-test.c | 584 ++++++++++++++++++++++++++++++++++++++++
16
tests/qtest/meson.build | 1 +
17
3 files changed, 586 insertions(+)
18
create mode 100644 tests/qtest/ufs-test.c
8
19
9
So, just do bdrv_backup_top_drop if append succeed and one bdrv_unref
20
diff --git a/MAINTAINERS b/MAINTAINERS
10
otherwise.
11
12
Note, that in !appended case bdrv_unref(top) moved into drained section
13
on source. It doesn't really matter, but just for code simplicity.
14
15
Fixes: 7df7868b96404
16
Cc: qemu-stable@nongnu.org # v4.2.0
17
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
18
Reviewed-by: Max Reitz <mreitz@redhat.com>
19
Message-id: 20200121142802.21467-2-vsementsov@virtuozzo.com
20
Signed-off-by: Max Reitz <mreitz@redhat.com>
21
---
22
block/backup-top.c | 21 ++++++++++++---------
23
1 file changed, 12 insertions(+), 9 deletions(-)
24
25
diff --git a/block/backup-top.c b/block/backup-top.c
26
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX 100644
27
--- a/block/backup-top.c
22
--- a/MAINTAINERS
28
+++ b/block/backup-top.c
23
+++ b/MAINTAINERS
29
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
24
@@ -XXX,XX +XXX,XX @@ M: Jeuk Kim <jeuk20.kim@samsung.com>
30
BlockDriverState *top = bdrv_new_open_driver(&bdrv_backup_top_filter,
25
S: Supported
31
filter_node_name,
26
F: hw/ufs/*
32
BDRV_O_RDWR, errp);
27
F: include/block/ufs.h
33
+ bool appended = false;
28
+F: tests/qtest/ufs-test.c
34
29
35
if (!top) {
30
megasas
36
return NULL;
31
M: Hannes Reinecke <hare@suse.com>
37
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
32
diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c
38
bdrv_append(top, source, &local_err);
33
new file mode 100644
39
if (local_err) {
34
index XXXXXXX..XXXXXXX
40
error_prepend(&local_err, "Cannot append backup-top filter: ");
35
--- /dev/null
41
- goto append_failed;
36
+++ b/tests/qtest/ufs-test.c
42
+ goto fail;
37
@@ -XXX,XX +XXX,XX @@
43
}
38
+/*
44
+ appended = true;
39
+ * QTest testcase for UFS
45
40
+ *
46
/*
41
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved.
47
* bdrv_append() finished successfully, now we can require permissions
42
+ *
48
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
43
+ * SPDX-License-Identifier: GPL-2.0-or-later
49
if (local_err) {
44
+ */
50
error_prepend(&local_err,
45
+
51
"Cannot set permissions for backup-top filter: ");
46
+#include "qemu/osdep.h"
52
- goto failed_after_append;
47
+#include "qemu/module.h"
53
+ goto fail;
48
+#include "qemu/units.h"
54
}
49
+#include "libqtest.h"
55
50
+#include "libqos/qgraph.h"
56
state->bcs = block_copy_state_new(top->backing, state->target,
51
+#include "libqos/pci.h"
57
cluster_size, write_flags, &local_err);
52
+#include "scsi/constants.h"
58
if (local_err) {
53
+#include "include/block/ufs.h"
59
error_prepend(&local_err, "Cannot create block-copy-state: ");
54
+
60
- goto failed_after_append;
55
+/* Test images sizes in Bytes */
61
+ goto fail;
56
+#define TEST_IMAGE_SIZE (64 * 1024 * 1024)
62
}
57
+/* Timeout for various operations, in seconds. */
63
*bcs = state->bcs;
58
+#define TIMEOUT_SECONDS 10
64
59
+/* Maximum PRD entry count */
65
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
60
+#define MAX_PRD_ENTRY_COUNT 10
66
61
+#define PRD_ENTRY_DATA_SIZE 4096
67
return top;
62
+/* Constants to build upiu */
68
63
+#define UTP_COMMAND_DESCRIPTOR_SIZE 4096
69
-failed_after_append:
64
+#define UTP_RESPONSE_UPIU_OFFSET 1024
70
- state->active = false;
65
+#define UTP_PRDT_UPIU_OFFSET 2048
71
- bdrv_backup_top_drop(top);
66
+
72
+fail:
67
+typedef struct QUfs QUfs;
73
+ if (appended) {
68
+
74
+ state->active = false;
69
+struct QUfs {
75
+ bdrv_backup_top_drop(top);
70
+ QOSGraphObject obj;
71
+ QPCIDevice dev;
72
+ QPCIBar bar;
73
+
74
+ uint64_t utrlba;
75
+ uint64_t utmrlba;
76
+ uint64_t cmd_desc_addr;
77
+ uint64_t data_buffer_addr;
78
+
79
+ bool enabled;
80
+};
81
+
82
+static inline uint32_t ufs_rreg(QUfs *ufs, size_t offset)
83
+{
84
+ return qpci_io_readl(&ufs->dev, ufs->bar, offset);
85
+}
86
+
87
+static inline void ufs_wreg(QUfs *ufs, size_t offset, uint32_t value)
88
+{
89
+ qpci_io_writel(&ufs->dev, ufs->bar, offset, value);
90
+}
91
+
92
+static void ufs_wait_for_irq(QUfs *ufs)
93
+{
94
+ uint64_t end_time;
95
+ uint32_t is;
96
+ /* Wait for device to reset as the linux driver does. */
97
+ end_time = g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND;
98
+ do {
99
+ qtest_clock_step(ufs->dev.bus->qts, 100);
100
+ is = ufs_rreg(ufs, A_IS);
101
+ } while (is == 0 && g_get_monotonic_time() < end_time);
102
+}
103
+
104
+static UtpTransferReqDesc ufs_build_req_utrd(uint64_t cmd_desc_addr,
105
+ uint8_t slot,
106
+ uint32_t data_direction,
107
+ uint16_t prd_table_length)
108
+{
109
+ UtpTransferReqDesc req = { 0 };
110
+ uint64_t command_desc_base_addr =
111
+ cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE;
112
+
113
+ req.header.dword_0 =
114
+ cpu_to_le32(1 << 28 | data_direction | UTP_REQ_DESC_INT_CMD);
115
+ req.header.dword_2 = cpu_to_le32(OCS_INVALID_COMMAND_STATUS);
116
+
117
+ req.command_desc_base_addr_hi = cpu_to_le32(command_desc_base_addr >> 32);
118
+ req.command_desc_base_addr_lo =
119
+ cpu_to_le32(command_desc_base_addr & 0xffffffff);
120
+ req.response_upiu_offset =
121
+ cpu_to_le16(UTP_RESPONSE_UPIU_OFFSET / sizeof(uint32_t));
122
+ req.response_upiu_length = cpu_to_le16(sizeof(UtpUpiuRsp));
123
+ req.prd_table_offset = cpu_to_le16(UTP_PRDT_UPIU_OFFSET / sizeof(uint32_t));
124
+ req.prd_table_length = cpu_to_le16(prd_table_length);
125
+ return req;
126
+}
127
+
128
+static void ufs_send_nop_out(QUfs *ufs, uint8_t slot,
129
+ UtpTransferReqDesc *utrd_out, UtpUpiuRsp *rsp_out)
130
+{
131
+ /* Build up utp transfer request descriptor */
132
+ UtpTransferReqDesc utrd =
133
+ ufs_build_req_utrd(ufs->cmd_desc_addr, slot, UTP_NO_DATA_TRANSFER, 0);
134
+ uint64_t utrd_addr = ufs->utrlba + slot * sizeof(UtpTransferReqDesc);
135
+ uint64_t req_upiu_addr =
136
+ ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE;
137
+ uint64_t rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET;
138
+ qtest_memwrite(ufs->dev.bus->qts, utrd_addr, &utrd, sizeof(utrd));
139
+
140
+ /* Build up request upiu */
141
+ UtpUpiuReq req_upiu = { 0 };
142
+ req_upiu.header.trans_type = UPIU_TRANSACTION_NOP_OUT;
143
+ req_upiu.header.task_tag = slot;
144
+ qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, &req_upiu,
145
+ sizeof(req_upiu));
146
+
147
+ /* Ring Doorbell */
148
+ ufs_wreg(ufs, A_UTRLDBR, 1);
149
+ ufs_wait_for_irq(ufs);
150
+ g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, UTRCS));
151
+ ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UTRCS, 1));
152
+
153
+ qtest_memread(ufs->dev.bus->qts, utrd_addr, utrd_out, sizeof(*utrd_out));
154
+ qtest_memread(ufs->dev.bus->qts, rsp_upiu_addr, rsp_out, sizeof(*rsp_out));
155
+}
156
+
157
+static void ufs_send_query(QUfs *ufs, uint8_t slot, uint8_t query_function,
158
+ uint8_t query_opcode, uint8_t idn, uint8_t index,
159
+ UtpTransferReqDesc *utrd_out, UtpUpiuRsp *rsp_out)
160
+{
161
+ /* Build up utp transfer request descriptor */
162
+ UtpTransferReqDesc utrd =
163
+ ufs_build_req_utrd(ufs->cmd_desc_addr, slot, UTP_NO_DATA_TRANSFER, 0);
164
+ uint64_t utrd_addr = ufs->utrlba + slot * sizeof(UtpTransferReqDesc);
165
+ uint64_t req_upiu_addr =
166
+ ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE;
167
+ uint64_t rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET;
168
+ qtest_memwrite(ufs->dev.bus->qts, utrd_addr, &utrd, sizeof(utrd));
169
+
170
+ /* Build up request upiu */
171
+ UtpUpiuReq req_upiu = { 0 };
172
+ req_upiu.header.trans_type = UPIU_TRANSACTION_QUERY_REQ;
173
+ req_upiu.header.query_func = query_function;
174
+ req_upiu.header.task_tag = slot;
175
+ /*
176
+ * QEMU UFS does not currently support Write descriptor and Write attribute,
177
+ * so the value of data_segment_length is always 0.
178
+ */
179
+ req_upiu.header.data_segment_length = 0;
180
+ req_upiu.qr.opcode = query_opcode;
181
+ req_upiu.qr.idn = idn;
182
+ req_upiu.qr.index = index;
183
+ qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, &req_upiu,
184
+ sizeof(req_upiu));
185
+
186
+ /* Ring Doorbell */
187
+ ufs_wreg(ufs, A_UTRLDBR, 1);
188
+ ufs_wait_for_irq(ufs);
189
+ g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, UTRCS));
190
+ ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UTRCS, 1));
191
+
192
+ qtest_memread(ufs->dev.bus->qts, utrd_addr, utrd_out, sizeof(*utrd_out));
193
+ qtest_memread(ufs->dev.bus->qts, rsp_upiu_addr, rsp_out, sizeof(*rsp_out));
194
+}
195
+
196
+static void ufs_send_scsi_command(QUfs *ufs, uint8_t slot, uint8_t lun,
197
+ const uint8_t *cdb, const uint8_t *data_in,
198
+ size_t data_in_len, uint8_t *data_out,
199
+ size_t data_out_len,
200
+ UtpTransferReqDesc *utrd_out,
201
+ UtpUpiuRsp *rsp_out)
202
+
203
+{
204
+ /* Build up PRDT */
205
+ UfshcdSgEntry entries[MAX_PRD_ENTRY_COUNT] = {
206
+ 0,
207
+ };
208
+ uint8_t flags;
209
+ uint16_t prd_table_length, i;
210
+ uint32_t data_direction, data_len;
211
+ uint64_t req_upiu_addr =
212
+ ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE;
213
+ uint64_t prdt_addr = req_upiu_addr + UTP_PRDT_UPIU_OFFSET;
214
+
215
+ g_assert_true(data_in_len < MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE);
216
+ g_assert_true(data_out_len < MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE);
217
+ if (data_in_len > 0) {
218
+ g_assert_nonnull(data_in);
219
+ data_direction = UTP_HOST_TO_DEVICE;
220
+ data_len = data_in_len;
221
+ flags = UPIU_CMD_FLAGS_WRITE;
222
+ } else if (data_out_len > 0) {
223
+ g_assert_nonnull(data_out);
224
+ data_direction = UTP_DEVICE_TO_HOST;
225
+ data_len = data_out_len;
226
+ flags = UPIU_CMD_FLAGS_READ;
76
+ } else {
227
+ } else {
77
+ bdrv_unref(top);
228
+ data_direction = UTP_NO_DATA_TRANSFER;
229
+ data_len = 0;
230
+ flags = UPIU_CMD_FLAGS_NONE;
78
+ }
231
+ }
79
232
+ prd_table_length = DIV_ROUND_UP(data_len, PRD_ENTRY_DATA_SIZE);
80
-append_failed:
233
+
81
bdrv_drained_end(source);
234
+ qtest_memset(ufs->dev.bus->qts, ufs->data_buffer_addr, 0,
82
- bdrv_unref_child(top, state->target);
235
+ MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE);
83
- bdrv_unref(top);
236
+ if (data_in_len) {
84
error_propagate(errp, local_err);
237
+ qtest_memwrite(ufs->dev.bus->qts, ufs->data_buffer_addr, data_in,
85
238
+ data_in_len);
86
return NULL;
239
+ }
240
+
241
+ for (i = 0; i < prd_table_length; i++) {
242
+ entries[i].addr =
243
+ cpu_to_le64(ufs->data_buffer_addr + i * sizeof(UfshcdSgEntry));
244
+ if (i + 1 != prd_table_length) {
245
+ entries[i].size = cpu_to_le32(PRD_ENTRY_DATA_SIZE - 1);
246
+ } else {
247
+ entries[i].size = cpu_to_le32(
248
+ data_len - (PRD_ENTRY_DATA_SIZE * (prd_table_length - 1)) - 1);
249
+ }
250
+ }
251
+ qtest_memwrite(ufs->dev.bus->qts, prdt_addr, entries,
252
+ prd_table_length * sizeof(UfshcdSgEntry));
253
+
254
+ /* Build up utp transfer request descriptor */
255
+ UtpTransferReqDesc utrd = ufs_build_req_utrd(
256
+ ufs->cmd_desc_addr, slot, data_direction, prd_table_length);
257
+ uint64_t utrd_addr = ufs->utrlba + slot * sizeof(UtpTransferReqDesc);
258
+ uint64_t rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET;
259
+ qtest_memwrite(ufs->dev.bus->qts, utrd_addr, &utrd, sizeof(utrd));
260
+
261
+ /* Build up request upiu */
262
+ UtpUpiuReq req_upiu = { 0 };
263
+ req_upiu.header.trans_type = UPIU_TRANSACTION_COMMAND;
264
+ req_upiu.header.flags = flags;
265
+ req_upiu.header.lun = lun;
266
+ req_upiu.header.task_tag = slot;
267
+ req_upiu.sc.exp_data_transfer_len = cpu_to_be32(data_len);
268
+ memcpy(req_upiu.sc.cdb, cdb, UFS_CDB_SIZE);
269
+ qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, &req_upiu,
270
+ sizeof(req_upiu));
271
+
272
+ /* Ring Doorbell */
273
+ ufs_wreg(ufs, A_UTRLDBR, 1);
274
+ ufs_wait_for_irq(ufs);
275
+ g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, UTRCS));
276
+ ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UTRCS, 1));
277
+
278
+ qtest_memread(ufs->dev.bus->qts, utrd_addr, utrd_out, sizeof(*utrd_out));
279
+ qtest_memread(ufs->dev.bus->qts, rsp_upiu_addr, rsp_out, sizeof(*rsp_out));
280
+ if (data_out_len) {
281
+ qtest_memread(ufs->dev.bus->qts, ufs->data_buffer_addr, data_out,
282
+ data_out_len);
283
+ }
284
+}
285
+
286
+/**
287
+ * Initialize Ufs host controller and logical unit.
288
+ * After running this function, you can make a transfer request to the UFS.
289
+ */
290
+static void ufs_init(QUfs *ufs, QGuestAllocator *alloc)
291
+{
292
+ uint64_t end_time;
293
+ uint32_t nutrs, nutmrs;
294
+ uint32_t hcs, is, ucmdarg2, cap;
295
+ uint32_t hce = 0, ie = 0;
296
+ UtpTransferReqDesc utrd;
297
+ UtpUpiuRsp rsp_upiu;
298
+
299
+ ufs->bar = qpci_iomap(&ufs->dev, 0, NULL);
300
+ qpci_device_enable(&ufs->dev);
301
+
302
+ /* Start host controller initialization */
303
+ hce = FIELD_DP32(hce, HCE, HCE, 1);
304
+ ufs_wreg(ufs, A_HCE, hce);
305
+
306
+ /* Wait for device to reset */
307
+ end_time = g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND;
308
+ do {
309
+ qtest_clock_step(ufs->dev.bus->qts, 100);
310
+ hce = FIELD_EX32(ufs_rreg(ufs, A_HCE), HCE, HCE);
311
+ } while (hce == 0 && g_get_monotonic_time() < end_time);
312
+ g_assert_cmpuint(hce, ==, 1);
313
+
314
+ /* Enable interrupt */
315
+ ie = FIELD_DP32(ie, IE, UCCE, 1);
316
+ ie = FIELD_DP32(ie, IE, UHESE, 1);
317
+ ie = FIELD_DP32(ie, IE, UHXSE, 1);
318
+ ie = FIELD_DP32(ie, IE, UPMSE, 1);
319
+ ufs_wreg(ufs, A_IE, ie);
320
+
321
+ /* Send DME_LINK_STARTUP uic command */
322
+ hcs = ufs_rreg(ufs, A_HCS);
323
+ g_assert_true(FIELD_EX32(hcs, HCS, UCRDY));
324
+
325
+ ufs_wreg(ufs, A_UCMDARG1, 0);
326
+ ufs_wreg(ufs, A_UCMDARG2, 0);
327
+ ufs_wreg(ufs, A_UCMDARG3, 0);
328
+ ufs_wreg(ufs, A_UICCMD, UIC_CMD_DME_LINK_STARTUP);
329
+
330
+ is = ufs_rreg(ufs, A_IS);
331
+ g_assert_true(FIELD_EX32(is, IS, UCCS));
332
+ ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UCCS, 1));
333
+
334
+ ucmdarg2 = ufs_rreg(ufs, A_UCMDARG2);
335
+ g_assert_cmpuint(ucmdarg2, ==, 0);
336
+ is = ufs_rreg(ufs, A_IS);
337
+ g_assert_cmpuint(is, ==, 0);
338
+ hcs = ufs_rreg(ufs, A_HCS);
339
+ g_assert_true(FIELD_EX32(hcs, HCS, DP));
340
+ g_assert_true(FIELD_EX32(hcs, HCS, UTRLRDY));
341
+ g_assert_true(FIELD_EX32(hcs, HCS, UTMRLRDY));
342
+ g_assert_true(FIELD_EX32(hcs, HCS, UCRDY));
343
+
344
+ /* Enable all interrupt functions */
345
+ ie = FIELD_DP32(ie, IE, UTRCE, 1);
346
+ ie = FIELD_DP32(ie, IE, UEE, 1);
347
+ ie = FIELD_DP32(ie, IE, UPMSE, 1);
348
+ ie = FIELD_DP32(ie, IE, UHXSE, 1);
349
+ ie = FIELD_DP32(ie, IE, UHESE, 1);
350
+ ie = FIELD_DP32(ie, IE, UTMRCE, 1);
351
+ ie = FIELD_DP32(ie, IE, UCCE, 1);
352
+ ie = FIELD_DP32(ie, IE, DFEE, 1);
353
+ ie = FIELD_DP32(ie, IE, HCFEE, 1);
354
+ ie = FIELD_DP32(ie, IE, SBFEE, 1);
355
+ ie = FIELD_DP32(ie, IE, CEFEE, 1);
356
+ ufs_wreg(ufs, A_IE, ie);
357
+ ufs_wreg(ufs, A_UTRIACR, 0);
358
+
359
+ /* Enable tranfer request and task management request */
360
+ cap = ufs_rreg(ufs, A_CAP);
361
+ nutrs = FIELD_EX32(cap, CAP, NUTRS) + 1;
362
+ nutmrs = FIELD_EX32(cap, CAP, NUTMRS) + 1;
363
+ ufs->cmd_desc_addr =
364
+ guest_alloc(alloc, nutrs * UTP_COMMAND_DESCRIPTOR_SIZE);
365
+ ufs->data_buffer_addr =
366
+ guest_alloc(alloc, MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE);
367
+ ufs->utrlba = guest_alloc(alloc, nutrs * sizeof(UtpTransferReqDesc));
368
+ ufs->utmrlba = guest_alloc(alloc, nutmrs * sizeof(UtpTaskReqDesc));
369
+
370
+ ufs_wreg(ufs, A_UTRLBA, ufs->utrlba & 0xffffffff);
371
+ ufs_wreg(ufs, A_UTRLBAU, ufs->utrlba >> 32);
372
+ ufs_wreg(ufs, A_UTMRLBA, ufs->utmrlba & 0xffffffff);
373
+ ufs_wreg(ufs, A_UTMRLBAU, ufs->utmrlba >> 32);
374
+ ufs_wreg(ufs, A_UTRLRSR, 1);
375
+ ufs_wreg(ufs, A_UTMRLRSR, 1);
376
+
377
+ /* Send nop out to test transfer request */
378
+ ufs_send_nop_out(ufs, 0, &utrd, &rsp_upiu);
379
+ g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, OCS_SUCCESS);
380
+
381
+ /* Set fDeviceInit flag via query request */
382
+ ufs_send_query(ufs, 0, UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST,
383
+ UPIU_QUERY_OPCODE_SET_FLAG, QUERY_FLAG_IDN_FDEVICEINIT, 0,
384
+ &utrd, &rsp_upiu);
385
+ g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, OCS_SUCCESS);
386
+
387
+ /* Wait for device to reset */
388
+ end_time = g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND;
389
+ do {
390
+ qtest_clock_step(ufs->dev.bus->qts, 100);
391
+ ufs_send_query(ufs, 0, UPIU_QUERY_FUNC_STANDARD_READ_REQUEST,
392
+ UPIU_QUERY_OPCODE_READ_FLAG, QUERY_FLAG_IDN_FDEVICEINIT,
393
+ 0, &utrd, &rsp_upiu);
394
+ } while (be32_to_cpu(rsp_upiu.qr.value) != 0 &&
395
+ g_get_monotonic_time() < end_time);
396
+ g_assert_cmpuint(be32_to_cpu(rsp_upiu.qr.value), ==, 0);
397
+
398
+ ufs->enabled = true;
399
+}
400
+
401
+static void ufs_exit(QUfs *ufs, QGuestAllocator *alloc)
402
+{
403
+ if (ufs->enabled) {
404
+ guest_free(alloc, ufs->utrlba);
405
+ guest_free(alloc, ufs->utmrlba);
406
+ guest_free(alloc, ufs->cmd_desc_addr);
407
+ guest_free(alloc, ufs->data_buffer_addr);
408
+ }
409
+
410
+ qpci_iounmap(&ufs->dev, ufs->bar);
411
+}
412
+
413
+static void *ufs_get_driver(void *obj, const char *interface)
414
+{
415
+ QUfs *ufs = obj;
416
+
417
+ if (!g_strcmp0(interface, "pci-device")) {
418
+ return &ufs->dev;
419
+ }
420
+
421
+ fprintf(stderr, "%s not present in ufs\n", interface);
422
+ g_assert_not_reached();
423
+}
424
+
425
+static void *ufs_create(void *pci_bus, QGuestAllocator *alloc, void *addr)
426
+{
427
+ QUfs *ufs = g_new0(QUfs, 1);
428
+ QPCIBus *bus = pci_bus;
429
+
430
+ qpci_device_init(&ufs->dev, bus, addr);
431
+ ufs->obj.get_driver = ufs_get_driver;
432
+
433
+ return &ufs->obj;
434
+}
435
+
436
+static void ufstest_reg_read(void *obj, void *data, QGuestAllocator *alloc)
437
+{
438
+ QUfs *ufs = obj;
439
+ uint32_t cap;
440
+
441
+ ufs->bar = qpci_iomap(&ufs->dev, 0, NULL);
442
+ qpci_device_enable(&ufs->dev);
443
+
444
+ cap = ufs_rreg(ufs, A_CAP);
445
+ g_assert_cmpuint(FIELD_EX32(cap, CAP, NUTRS), ==, 31);
446
+ g_assert_cmpuint(FIELD_EX32(cap, CAP, NUTMRS), ==, 7);
447
+ g_assert_cmpuint(FIELD_EX32(cap, CAP, 64AS), ==, 1);
448
+
449
+ qpci_iounmap(&ufs->dev, ufs->bar);
450
+}
451
+
452
+static void ufstest_init(void *obj, void *data, QGuestAllocator *alloc)
453
+{
454
+ QUfs *ufs = obj;
455
+
456
+ uint8_t buf[4096] = { 0 };
457
+ const uint8_t report_luns_cdb[UFS_CDB_SIZE] = {
458
+ /* allocation length 4096 */
459
+ REPORT_LUNS, 0x00, 0x00, 0x00, 0x00, 0x00,
460
+ 0x00, 0x00, 0x10, 0x00, 0x00, 0x00
461
+ };
462
+ const uint8_t test_unit_ready_cdb[UFS_CDB_SIZE] = {
463
+ TEST_UNIT_READY,
464
+ };
465
+ UtpTransferReqDesc utrd;
466
+ UtpUpiuRsp rsp_upiu;
467
+
468
+ ufs_init(ufs, alloc);
469
+
470
+ /* Check REPORT_LUNS */
471
+ ufs_send_scsi_command(ufs, 0, 0, report_luns_cdb, NULL, 0, buf, sizeof(buf),
472
+ &utrd, &rsp_upiu);
473
+ g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, OCS_SUCCESS);
474
+ g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD);
475
+ /* LUN LIST LENGTH should be 8, in big endian */
476
+ g_assert_cmpuint(buf[3], ==, 8);
477
+ /* There is one logical unit whose lun is 0 */
478
+ g_assert_cmpuint(buf[9], ==, 0);
479
+
480
+ /* Check TEST_UNIT_READY */
481
+ ufs_send_scsi_command(ufs, 0, 0, test_unit_ready_cdb, NULL, 0, NULL, 0,
482
+ &utrd, &rsp_upiu);
483
+ g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, OCS_SUCCESS);
484
+ g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD);
485
+
486
+ ufs_exit(ufs, alloc);
487
+}
488
+
489
+static void ufstest_read_write(void *obj, void *data, QGuestAllocator *alloc)
490
+{
491
+ QUfs *ufs = obj;
492
+ uint8_t read_buf[4096] = { 0 };
493
+ uint8_t write_buf[4096] = { 0 };
494
+ const uint8_t read_capacity_cdb[UFS_CDB_SIZE] = {
495
+ /* allocation length 4096 */
496
+ SERVICE_ACTION_IN_16,
497
+ SAI_READ_CAPACITY_16,
498
+ 0x00,
499
+ 0x00,
500
+ 0x00,
501
+ 0x00,
502
+ 0x00,
503
+ 0x00,
504
+ 0x00,
505
+ 0x00,
506
+ 0x00,
507
+ 0x00,
508
+ 0x10,
509
+ 0x00,
510
+ 0x00,
511
+ 0x00
512
+ };
513
+ const uint8_t read_cdb[UFS_CDB_SIZE] = {
514
+ /* READ(10) to LBA 0, transfer length 1 */
515
+ READ_10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00
516
+ };
517
+ const uint8_t write_cdb[UFS_CDB_SIZE] = {
518
+ /* WRITE(10) to LBA 0, transfer length 1 */
519
+ WRITE_10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00
520
+ };
521
+ uint32_t block_size;
522
+ UtpTransferReqDesc utrd;
523
+ UtpUpiuRsp rsp_upiu;
524
+
525
+ ufs_init(ufs, alloc);
526
+
527
+ /* Read capacity */
528
+ ufs_send_scsi_command(ufs, 0, 1, read_capacity_cdb, NULL, 0, read_buf,
529
+ sizeof(read_buf), &utrd, &rsp_upiu);
530
+ g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, OCS_SUCCESS);
531
+ g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, COMMAND_RESULT_SUCESS);
532
+ block_size = ldl_be_p(&read_buf[8]);
533
+ g_assert_cmpuint(block_size, ==, 4096);
534
+
535
+ /* Write data */
536
+ memset(write_buf, rand() % 255 + 1, block_size);
537
+ ufs_send_scsi_command(ufs, 0, 1, write_cdb, write_buf, block_size, NULL, 0,
538
+ &utrd, &rsp_upiu);
539
+ g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, OCS_SUCCESS);
540
+ g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, COMMAND_RESULT_SUCESS);
541
+
542
+ /* Read data and verify */
543
+ ufs_send_scsi_command(ufs, 0, 1, read_cdb, NULL, 0, read_buf, block_size,
544
+ &utrd, &rsp_upiu);
545
+ g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, OCS_SUCCESS);
546
+ g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, COMMAND_RESULT_SUCESS);
547
+ g_assert_cmpint(memcmp(read_buf, write_buf, block_size), ==, 0);
548
+
549
+ ufs_exit(ufs, alloc);
550
+}
551
+
552
+static void drive_destroy(void *path)
553
+{
554
+ unlink(path);
555
+ g_free(path);
556
+ qos_invalidate_command_line();
557
+}
558
+
559
+static char *drive_create(void)
560
+{
561
+ int fd, ret;
562
+ char *t_path;
563
+
564
+ /* Create a temporary raw image */
565
+ fd = g_file_open_tmp("qtest-ufs.XXXXXX", &t_path, NULL);
566
+ g_assert_cmpint(fd, >=, 0);
567
+ ret = ftruncate(fd, TEST_IMAGE_SIZE);
568
+ g_assert_cmpint(ret, ==, 0);
569
+ close(fd);
570
+
571
+ g_test_queue_destroy(drive_destroy, t_path);
572
+ return t_path;
573
+}
574
+
575
+static void *ufs_blk_test_setup(GString *cmd_line, void *arg)
576
+{
577
+ char *tmp_path = drive_create();
578
+
579
+ g_string_append_printf(cmd_line,
580
+ " -blockdev file,filename=%s,node-name=drv1 "
581
+ "-device ufs-lu,bus=ufs0,drive=drv1,lun=1 ",
582
+ tmp_path);
583
+
584
+ return arg;
585
+}
586
+
587
+static void ufs_register_nodes(void)
588
+{
589
+ const char *arch;
590
+ QOSGraphEdgeOptions edge_opts = {
591
+ .before_cmd_line = "-blockdev null-co,node-name=drv0,read-zeroes=on",
592
+ .after_cmd_line = "-device ufs-lu,bus=ufs0,drive=drv0,lun=0",
593
+ .extra_device_opts = "addr=04.0,id=ufs0,nutrs=32,nutmrs=8"
594
+ };
595
+
596
+ QOSGraphTestOptions io_test_opts = {
597
+ .before = ufs_blk_test_setup,
598
+ };
599
+
600
+ add_qpci_address(&edge_opts, &(QPCIAddress){ .devfn = QPCI_DEVFN(4, 0) });
601
+
602
+ qos_node_create_driver("ufs", ufs_create);
603
+ qos_node_consumes("ufs", "pci-bus", &edge_opts);
604
+ qos_node_produces("ufs", "pci-device");
605
+
606
+ qos_add_test("reg-read", "ufs", ufstest_reg_read, NULL);
607
+
608
+ /*
609
+ * Check architecture
610
+ * TODO: Enable ufs io tests for ppc64
611
+ */
612
+ arch = qtest_get_arch();
613
+ if (!strcmp(arch, "ppc64")) {
614
+ g_test_message("Skipping ufs io tests for ppc64");
615
+ return;
616
+ }
617
+ qos_add_test("init", "ufs", ufstest_init, NULL);
618
+ qos_add_test("read-write", "ufs", ufstest_read_write, &io_test_opts);
619
+}
620
+
621
+libqos_init(ufs_register_nodes);
622
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
623
index XXXXXXX..XXXXXXX 100644
624
--- a/tests/qtest/meson.build
625
+++ b/tests/qtest/meson.build
626
@@ -XXX,XX +XXX,XX @@ qos_test_ss.add(
627
'virtio-iommu-test.c',
628
'vmxnet3-test.c',
629
'igb-test.c',
630
+ 'ufs-test.c',
631
)
632
633
if config_all_devices.has_key('CONFIG_VIRTIO_SERIAL')
87
--
634
--
88
2.24.1
635
2.41.0
89
90
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Fabiano Rosas <farosas@suse.de>
2
2
3
This is a bit more efficient than having to allocate and free memory
3
We can fail the blk_insert_bs() at init_blk_migration(), leaving the
4
for each new permission.
4
BlkMigDevState without a dirty_bitmap and BlockDriverState. Account
5
for the possibly missing elements when doing cleanup.
5
6
6
The default size (30) is enough for "consistent read, write, resize".
7
Fix the following crashes:
7
8
8
Signed-off-by: Alberto Garcia <berto@igalia.com>
9
Thread 1 "qemu-system-x86" received signal SIGSEGV, Segmentation fault.
9
Message-id: 20200110171518.22168-1-berto@igalia.com
10
0x0000555555ec83ef in bdrv_release_dirty_bitmap (bitmap=0x0) at ../block/dirty-bitmap.c:359
10
Reviewed-by: Eric Blake <eblake@redhat.com>
11
359 BlockDriverState *bs = bitmap->bs;
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
12
#0 0x0000555555ec83ef in bdrv_release_dirty_bitmap (bitmap=0x0) at ../block/dirty-bitmap.c:359
13
#1 0x0000555555bba331 in unset_dirty_tracking () at ../migration/block.c:371
14
#2 0x0000555555bbad98 in block_migration_cleanup_bmds () at ../migration/block.c:681
15
16
Thread 1 "qemu-system-x86" received signal SIGSEGV, Segmentation fault.
17
0x0000555555e971ff in bdrv_op_unblock (bs=0x0, op=BLOCK_OP_TYPE_BACKUP_SOURCE, reason=0x0) at ../block.c:7073
18
7073 QLIST_FOREACH_SAFE(blocker, &bs->op_blockers[op], list, next) {
19
#0 0x0000555555e971ff in bdrv_op_unblock (bs=0x0, op=BLOCK_OP_TYPE_BACKUP_SOURCE, reason=0x0) at ../block.c:7073
20
#1 0x0000555555e9734a in bdrv_op_unblock_all (bs=0x0, reason=0x0) at ../block.c:7095
21
#2 0x0000555555bbae13 in block_migration_cleanup_bmds () at ../migration/block.c:690
22
23
Signed-off-by: Fabiano Rosas <farosas@suse.de>
24
Message-id: 20230731203338.27581-1-farosas@suse.de
25
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
12
---
26
---
13
block.c | 11 ++++++-----
27
migration/block.c | 11 +++++++++--
14
1 file changed, 6 insertions(+), 5 deletions(-)
28
1 file changed, 9 insertions(+), 2 deletions(-)
15
29
16
diff --git a/block.c b/block.c
30
diff --git a/migration/block.c b/migration/block.c
17
index XXXXXXX..XXXXXXX 100644
31
index XXXXXXX..XXXXXXX 100644
18
--- a/block.c
32
--- a/migration/block.c
19
+++ b/block.c
33
+++ b/migration/block.c
20
@@ -XXX,XX +XXX,XX @@ char *bdrv_perm_names(uint64_t perm)
34
@@ -XXX,XX +XXX,XX @@ static void unset_dirty_tracking(void)
21
{ 0, NULL }
35
BlkMigDevState *bmds;
22
};
36
23
37
QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
24
- char *result = g_strdup("");
38
- bdrv_release_dirty_bitmap(bmds->dirty_bitmap);
25
+ GString *result = g_string_sized_new(30);
39
+ if (bmds->dirty_bitmap) {
26
struct perm_name *p;
40
+ bdrv_release_dirty_bitmap(bmds->dirty_bitmap);
27
41
+ }
28
for (p = permissions; p->name; p++) {
29
if (perm & p->perm) {
30
- char *old = result;
31
- result = g_strdup_printf("%s%s%s", old, *old ? ", " : "", p->name);
32
- g_free(old);
33
+ if (result->len > 0) {
34
+ g_string_append(result, ", ");
35
+ }
36
+ g_string_append(result, p->name);
37
}
38
}
42
}
39
40
- return result;
41
+ return g_string_free(result, FALSE);
42
}
43
}
43
44
44
/*
45
@@ -XXX,XX +XXX,XX @@ static int64_t get_remaining_dirty(void)
46
static void block_migration_cleanup_bmds(void)
47
{
48
BlkMigDevState *bmds;
49
+ BlockDriverState *bs;
50
AioContext *ctx;
51
52
unset_dirty_tracking();
53
54
while ((bmds = QSIMPLEQ_FIRST(&block_mig_state.bmds_list)) != NULL) {
55
QSIMPLEQ_REMOVE_HEAD(&block_mig_state.bmds_list, entry);
56
- bdrv_op_unblock_all(blk_bs(bmds->blk), bmds->blocker);
57
+
58
+ bs = blk_bs(bmds->blk);
59
+ if (bs) {
60
+ bdrv_op_unblock_all(bs, bmds->blocker);
61
+ }
62
error_free(bmds->blocker);
63
64
/* Save ctx, because bmds->blk can disappear during blk_unref. */
45
--
65
--
46
2.24.1
66
2.41.0
47
48
diff view generated by jsdifflib
Deleted patch
1
From: Pan Nengyuan <pannengyuan@huawei.com>
2
1
3
If we call the qmp 'query-block' while qemu is working on
4
'block-commit', it will cause memleaks, the memory leak stack is as
5
follow:
6
7
Indirect leak of 12360 byte(s) in 3 object(s) allocated from:
8
#0 0x7f80f0b6d970 in __interceptor_calloc (/lib64/libasan.so.5+0xef970)
9
#1 0x7f80ee86049d in g_malloc0 (/lib64/libglib-2.0.so.0+0x5249d)
10
#2 0x55ea95b5bb67 in qdict_new /mnt/sdb/qemu-4.2.0-rc0/qobject/qdict.c:29
11
#3 0x55ea956cd043 in bdrv_refresh_filename /mnt/sdb/qemu-4.2.0-rc0/block.c:6427
12
#4 0x55ea956cc950 in bdrv_refresh_filename /mnt/sdb/qemu-4.2.0-rc0/block.c:6399
13
#5 0x55ea956cc950 in bdrv_refresh_filename /mnt/sdb/qemu-4.2.0-rc0/block.c:6399
14
#6 0x55ea956cc950 in bdrv_refresh_filename /mnt/sdb/qemu-4.2.0-rc0/block.c:6399
15
#7 0x55ea958818ea in bdrv_block_device_info /mnt/sdb/qemu-4.2.0-rc0/block/qapi.c:56
16
#8 0x55ea958879de in bdrv_query_info /mnt/sdb/qemu-4.2.0-rc0/block/qapi.c:392
17
#9 0x55ea9588b58f in qmp_query_block /mnt/sdb/qemu-4.2.0-rc0/block/qapi.c:578
18
#10 0x55ea95567392 in qmp_marshal_query_block qapi/qapi-commands-block-core.c:95
19
20
Indirect leak of 4120 byte(s) in 1 object(s) allocated from:
21
#0 0x7f80f0b6d970 in __interceptor_calloc (/lib64/libasan.so.5+0xef970)
22
#1 0x7f80ee86049d in g_malloc0 (/lib64/libglib-2.0.so.0+0x5249d)
23
#2 0x55ea95b5bb67 in qdict_new /mnt/sdb/qemu-4.2.0-rc0/qobject/qdict.c:29
24
#3 0x55ea956cd043 in bdrv_refresh_filename /mnt/sdb/qemu-4.2.0-rc0/block.c:6427
25
#4 0x55ea956cc950 in bdrv_refresh_filename /mnt/sdb/qemu-4.2.0-rc0/block.c:6399
26
#5 0x55ea956cc950 in bdrv_refresh_filename /mnt/sdb/qemu-4.2.0-rc0/block.c:6399
27
#6 0x55ea9569f301 in bdrv_backing_attach /mnt/sdb/qemu-4.2.0-rc0/block.c:1064
28
#7 0x55ea956a99dd in bdrv_replace_child_noperm /mnt/sdb/qemu-4.2.0-rc0/block.c:2283
29
#8 0x55ea956b9b53 in bdrv_replace_node /mnt/sdb/qemu-4.2.0-rc0/block.c:4196
30
#9 0x55ea956b9e49 in bdrv_append /mnt/sdb/qemu-4.2.0-rc0/block.c:4236
31
#10 0x55ea958c3472 in commit_start /mnt/sdb/qemu-4.2.0-rc0/block/commit.c:306
32
#11 0x55ea94b68ab0 in qmp_block_commit /mnt/sdb/qemu-4.2.0-rc0/blockdev.c:3459
33
#12 0x55ea9556a7a7 in qmp_marshal_block_commit qapi/qapi-commands-block-core.c:407
34
35
Fixes: bb808d5f5c0978828a974d547e6032402c339555
36
Reported-by: Euler Robot <euler.robot@huawei.com>
37
Signed-off-by: Pan Nengyuan <pannengyuan@huawei.com>
38
Message-id: 20200116085600.24056-1-pannengyuan@huawei.com
39
Signed-off-by: Max Reitz <mreitz@redhat.com>
40
---
41
block.c | 1 +
42
1 file changed, 1 insertion(+)
43
44
diff --git a/block.c b/block.c
45
index XXXXXXX..XXXXXXX 100644
46
--- a/block.c
47
+++ b/block.c
48
@@ -XXX,XX +XXX,XX @@ void bdrv_refresh_filename(BlockDriverState *bs)
49
child->bs->exact_filename);
50
pstrcpy(bs->filename, sizeof(bs->filename), child->bs->filename);
51
52
+ qobject_unref(bs->full_open_options);
53
bs->full_open_options = qobject_ref(child->bs->full_open_options);
54
55
return;
56
--
57
2.24.1
58
59
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
2
2
3
The L1 table is read from disk using the byte-based bdrv_pread() and
3
This is going to be used in the subsequent commit as requests alignment
4
is never accessed beyond its last element, so there's no need to
4
(in particular, during copy-on-read). This value only makes sense for
5
allocate more memory than that.
5
the formats which support subclusters (currently QCOW2 only). If this
6
field isn't set by driver's own bdrv_get_info() implementation, we
7
simply set it equal to the cluster size thus treating each cluster as
8
having a single subcluster.
6
9
7
Signed-off-by: Alberto Garcia <berto@igalia.com>
10
Reviewed-by: Eric Blake <eblake@redhat.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
11
Reviewed-by: Denis V. Lunev <den@openvz.org>
9
Message-id: b2e27214ec7b03a585931bcf383ee1ac3a641a10.1579374329.git.berto@igalia.com
12
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
13
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
14
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
15
Message-ID: <20230711172553.234055-2-andrey.drobyshev@virtuozzo.com>
11
---
16
---
12
block/qcow2-cluster.c | 5 ++---
17
include/block/block-common.h | 5 +++++
13
block/qcow2-refcount.c | 2 +-
18
block.c | 7 +++++++
14
block/qcow2-snapshot.c | 3 +--
19
block/qcow2.c | 1 +
15
block/qcow2.c | 2 +-
20
3 files changed, 13 insertions(+)
16
4 files changed, 5 insertions(+), 7 deletions(-)
17
21
18
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
22
diff --git a/include/block/block-common.h b/include/block/block-common.h
19
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
20
--- a/block/qcow2-cluster.c
24
--- a/include/block/block-common.h
21
+++ b/block/qcow2-cluster.c
25
+++ b/include/block/block-common.h
22
@@ -XXX,XX +XXX,XX @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
26
@@ -XXX,XX +XXX,XX @@ typedef struct BlockZoneWps {
23
#endif
27
typedef struct BlockDriverInfo {
24
28
/* in bytes, 0 if irrelevant */
25
new_l1_size2 = sizeof(uint64_t) * new_l1_size;
29
int cluster_size;
26
- new_l1_table = qemu_try_blockalign(bs->file->bs,
30
+ /*
27
- ROUND_UP(new_l1_size2, 512));
31
+ * A fraction of cluster_size, if supported (currently QCOW2 only); if
28
+ new_l1_table = qemu_try_blockalign(bs->file->bs, new_l1_size2);
32
+ * disabled or unsupported, set equal to cluster_size.
29
if (new_l1_table == NULL) {
33
+ */
30
return -ENOMEM;
34
+ int subcluster_size;
35
/* offset at which the VM state can be saved (0 if not possible) */
36
int64_t vm_state_offset;
37
bool is_dirty;
38
diff --git a/block.c b/block.c
39
index XXXXXXX..XXXXXXX 100644
40
--- a/block.c
41
+++ b/block.c
42
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
31
}
43
}
32
- memset(new_l1_table, 0, ROUND_UP(new_l1_size2, 512));
44
memset(bdi, 0, sizeof(*bdi));
33
+ memset(new_l1_table, 0, new_l1_size2);
45
ret = drv->bdrv_co_get_info(bs, bdi);
34
46
+ if (bdi->subcluster_size == 0) {
35
if (s->l1_size) {
47
+ /*
36
memcpy(new_l1_table, s->l1_table, s->l1_size * sizeof(uint64_t));
48
+ * If the driver left this unset, subclusters are not supported.
37
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
49
+ * Then it is safe to treat each cluster as having only one subcluster.
38
index XXXXXXX..XXXXXXX 100644
50
+ */
39
--- a/block/qcow2-refcount.c
51
+ bdi->subcluster_size = bdi->cluster_size;
40
+++ b/block/qcow2-refcount.c
52
+ }
41
@@ -XXX,XX +XXX,XX @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
53
if (ret < 0) {
42
* l1_table_offset when it is the current s->l1_table_offset! Be careful
43
* when changing this! */
44
if (l1_table_offset != s->l1_table_offset) {
45
- l1_table = g_try_malloc0(ROUND_UP(l1_size2, 512));
46
+ l1_table = g_try_malloc0(l1_size2);
47
if (l1_size2 && l1_table == NULL) {
48
ret = -ENOMEM;
49
goto fail;
50
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
51
index XXXXXXX..XXXXXXX 100644
52
--- a/block/qcow2-snapshot.c
53
+++ b/block/qcow2-snapshot.c
54
@@ -XXX,XX +XXX,XX @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs,
55
return ret;
54
return ret;
56
}
57
new_l1_bytes = sn->l1_size * sizeof(uint64_t);
58
- new_l1_table = qemu_try_blockalign(bs->file->bs,
59
- ROUND_UP(new_l1_bytes, 512));
60
+ new_l1_table = qemu_try_blockalign(bs->file->bs, new_l1_bytes);
61
if (new_l1_table == NULL) {
62
return -ENOMEM;
63
}
55
}
64
diff --git a/block/qcow2.c b/block/qcow2.c
56
diff --git a/block/qcow2.c b/block/qcow2.c
65
index XXXXXXX..XXXXXXX 100644
57
index XXXXXXX..XXXXXXX 100644
66
--- a/block/qcow2.c
58
--- a/block/qcow2.c
67
+++ b/block/qcow2.c
59
+++ b/block/qcow2.c
68
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
60
@@ -XXX,XX +XXX,XX @@ qcow2_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
69
61
{
70
if (s->l1_size > 0) {
62
BDRVQcow2State *s = bs->opaque;
71
s->l1_table = qemu_try_blockalign(bs->file->bs,
63
bdi->cluster_size = s->cluster_size;
72
- ROUND_UP(s->l1_size * sizeof(uint64_t), 512));
64
+ bdi->subcluster_size = s->subcluster_size;
73
+ s->l1_size * sizeof(uint64_t));
65
bdi->vm_state_offset = qcow2_vm_state_offset(s);
74
if (s->l1_table == NULL) {
66
bdi->is_dirty = s->incompatible_features & QCOW2_INCOMPAT_DIRTY;
75
error_setg(errp, "Could not allocate L1 table");
67
return 0;
76
ret = -ENOMEM;
77
--
68
--
78
2.24.1
69
2.41.0
79
80
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
2
2
3
This is a bit more efficient than having to allocate and free memory
3
When target image is using subclusters, and we align the request during
4
for each item.
4
copy-on-read, it makes sense to align to subcluster_size rather than
5
5
cluster_size. Otherwise we end up with unnecessary allocations.
6
The default size (60) is enough for all the existing incompatible
6
7
features or the "Unknown incompatible feature" message.
7
This commit renames bdrv_round_to_clusters() to bdrv_round_to_subclusters()
8
8
and utilizes subcluster_size field of BlockDriverInfo to make necessary
9
Suggested-by: Philippe Mathieu-Daudé <philmd@redhat.com>
9
alignments. It affects copy-on-read as well as mirror job (which is
10
Signed-off-by: Alberto Garcia <berto@igalia.com>
10
using bdrv_round_to_clusters()).
11
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
11
12
Message-id: 20200115135626.19442-1-berto@igalia.com
12
This change also fixes the following bug with failing assert (covered by
13
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
13
the test in the subsequent commit):
14
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
14
15
Signed-off-by: Max Reitz <mreitz@redhat.com>
15
qemu-img create -f qcow2 base.qcow2 64K
16
qemu-img create -f qcow2 -o extended_l2=on,backing_file=base.qcow2,backing_fmt=qcow2 img.qcow2 64K
17
qemu-io -c "write -P 0xaa 0 2K" img.qcow2
18
qemu-io -C -c "read -P 0x00 2K 62K" img.qcow2
19
20
qemu-io: ../block/io.c:1236: bdrv_co_do_copy_on_readv: Assertion `skip_bytes < pnum' failed.
21
22
Reviewed-by: Eric Blake <eblake@redhat.com>
23
Reviewed-by: Denis V. Lunev <den@openvz.org>
24
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
25
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
26
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
27
Message-ID: <20230711172553.234055-3-andrey.drobyshev@virtuozzo.com>
16
---
28
---
17
block/qcow2.c | 23 +++++++++++------------
29
include/block/block-io.h | 8 +++----
18
1 file changed, 11 insertions(+), 12 deletions(-)
30
block/io.c | 50 ++++++++++++++++++++--------------------
19
31
block/mirror.c | 8 +++----
20
diff --git a/block/qcow2.c b/block/qcow2.c
32
3 files changed, 33 insertions(+), 33 deletions(-)
33
34
diff --git a/include/block/block-io.h b/include/block/block-io.h
21
index XXXXXXX..XXXXXXX 100644
35
index XXXXXXX..XXXXXXX 100644
22
--- a/block/qcow2.c
36
--- a/include/block/block-io.h
23
+++ b/block/qcow2.c
37
+++ b/include/block/block-io.h
24
@@ -XXX,XX +XXX,XX @@ static void cleanup_unknown_header_ext(BlockDriverState *bs)
38
@@ -XXX,XX +XXX,XX @@ bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi);
25
static void report_unsupported_feature(Error **errp, Qcow2Feature *table,
39
ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs,
26
uint64_t mask)
40
Error **errp);
41
BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs);
42
-void bdrv_round_to_clusters(BlockDriverState *bs,
43
- int64_t offset, int64_t bytes,
44
- int64_t *cluster_offset,
45
- int64_t *cluster_bytes);
46
+void bdrv_round_to_subclusters(BlockDriverState *bs,
47
+ int64_t offset, int64_t bytes,
48
+ int64_t *cluster_offset,
49
+ int64_t *cluster_bytes);
50
51
void bdrv_get_backing_filename(BlockDriverState *bs,
52
char *filename, int filename_size);
53
diff --git a/block/io.c b/block/io.c
54
index XXXXXXX..XXXXXXX 100644
55
--- a/block/io.c
56
+++ b/block/io.c
57
@@ -XXX,XX +XXX,XX @@ BdrvTrackedRequest *coroutine_fn bdrv_co_get_self_request(BlockDriverState *bs)
58
}
59
60
/**
61
- * Round a region to cluster boundaries
62
+ * Round a region to subcluster (if supported) or cluster boundaries
63
*/
64
void coroutine_fn GRAPH_RDLOCK
65
-bdrv_round_to_clusters(BlockDriverState *bs, int64_t offset, int64_t bytes,
66
- int64_t *cluster_offset, int64_t *cluster_bytes)
67
+bdrv_round_to_subclusters(BlockDriverState *bs, int64_t offset, int64_t bytes,
68
+ int64_t *align_offset, int64_t *align_bytes)
27
{
69
{
28
- char *features = g_strdup("");
70
BlockDriverInfo bdi;
29
- char *old;
71
IO_CODE();
30
+ g_autoptr(GString) features = g_string_sized_new(60);
72
- if (bdrv_co_get_info(bs, &bdi) < 0 || bdi.cluster_size == 0) {
31
73
- *cluster_offset = offset;
32
while (table && table->name[0] != '\0') {
74
- *cluster_bytes = bytes;
33
if (table->type == QCOW2_FEAT_TYPE_INCOMPATIBLE) {
75
+ if (bdrv_co_get_info(bs, &bdi) < 0 || bdi.subcluster_size == 0) {
34
if (mask & (1ULL << table->bit)) {
76
+ *align_offset = offset;
35
- old = features;
77
+ *align_bytes = bytes;
36
- features = g_strdup_printf("%s%s%.46s", old, *old ? ", " : "",
78
} else {
37
- table->name);
79
- int64_t c = bdi.cluster_size;
38
- g_free(old);
80
- *cluster_offset = QEMU_ALIGN_DOWN(offset, c);
39
+ if (features->len > 0) {
81
- *cluster_bytes = QEMU_ALIGN_UP(offset - *cluster_offset + bytes, c);
40
+ g_string_append(features, ", ");
82
+ int64_t c = bdi.subcluster_size;
41
+ }
83
+ *align_offset = QEMU_ALIGN_DOWN(offset, c);
42
+ g_string_append_printf(features, "%.46s", table->name);
84
+ *align_bytes = QEMU_ALIGN_UP(offset - *align_offset + bytes, c);
43
mask &= ~(1ULL << table->bit);
85
}
86
}
87
88
@@ -XXX,XX +XXX,XX @@ bdrv_co_do_copy_on_readv(BdrvChild *child, int64_t offset, int64_t bytes,
89
void *bounce_buffer = NULL;
90
91
BlockDriver *drv = bs->drv;
92
- int64_t cluster_offset;
93
- int64_t cluster_bytes;
94
+ int64_t align_offset;
95
+ int64_t align_bytes;
96
int64_t skip_bytes;
97
int ret;
98
int max_transfer = MIN_NON_ZERO(bs->bl.max_transfer,
99
@@ -XXX,XX +XXX,XX @@ bdrv_co_do_copy_on_readv(BdrvChild *child, int64_t offset, int64_t bytes,
100
* BDRV_REQUEST_MAX_BYTES (even when the original read did not), which
101
* is one reason we loop rather than doing it all at once.
102
*/
103
- bdrv_round_to_clusters(bs, offset, bytes, &cluster_offset, &cluster_bytes);
104
- skip_bytes = offset - cluster_offset;
105
+ bdrv_round_to_subclusters(bs, offset, bytes, &align_offset, &align_bytes);
106
+ skip_bytes = offset - align_offset;
107
108
trace_bdrv_co_do_copy_on_readv(bs, offset, bytes,
109
- cluster_offset, cluster_bytes);
110
+ align_offset, align_bytes);
111
112
- while (cluster_bytes) {
113
+ while (align_bytes) {
114
int64_t pnum;
115
116
if (skip_write) {
117
ret = 1; /* "already allocated", so nothing will be copied */
118
- pnum = MIN(cluster_bytes, max_transfer);
119
+ pnum = MIN(align_bytes, max_transfer);
120
} else {
121
- ret = bdrv_is_allocated(bs, cluster_offset,
122
- MIN(cluster_bytes, max_transfer), &pnum);
123
+ ret = bdrv_is_allocated(bs, align_offset,
124
+ MIN(align_bytes, max_transfer), &pnum);
125
if (ret < 0) {
126
/*
127
* Safe to treat errors in querying allocation as if
128
* unallocated; we'll probably fail again soon on the
129
* read, but at least that will set a decent errno.
130
*/
131
- pnum = MIN(cluster_bytes, max_transfer);
132
+ pnum = MIN(align_bytes, max_transfer);
133
}
134
135
/* Stop at EOF if the image ends in the middle of the cluster */
136
@@ -XXX,XX +XXX,XX @@ bdrv_co_do_copy_on_readv(BdrvChild *child, int64_t offset, int64_t bytes,
137
/* Must copy-on-read; use the bounce buffer */
138
pnum = MIN(pnum, MAX_BOUNCE_BUFFER);
139
if (!bounce_buffer) {
140
- int64_t max_we_need = MAX(pnum, cluster_bytes - pnum);
141
+ int64_t max_we_need = MAX(pnum, align_bytes - pnum);
142
int64_t max_allowed = MIN(max_transfer, MAX_BOUNCE_BUFFER);
143
int64_t bounce_buffer_len = MIN(max_we_need, max_allowed);
144
145
@@ -XXX,XX +XXX,XX @@ bdrv_co_do_copy_on_readv(BdrvChild *child, int64_t offset, int64_t bytes,
146
}
147
qemu_iovec_init_buf(&local_qiov, bounce_buffer, pnum);
148
149
- ret = bdrv_driver_preadv(bs, cluster_offset, pnum,
150
+ ret = bdrv_driver_preadv(bs, align_offset, pnum,
151
&local_qiov, 0, 0);
152
if (ret < 0) {
153
goto err;
154
@@ -XXX,XX +XXX,XX @@ bdrv_co_do_copy_on_readv(BdrvChild *child, int64_t offset, int64_t bytes,
155
/* FIXME: Should we (perhaps conditionally) be setting
156
* BDRV_REQ_MAY_UNMAP, if it will allow for a sparser copy
157
* that still correctly reads as zero? */
158
- ret = bdrv_co_do_pwrite_zeroes(bs, cluster_offset, pnum,
159
+ ret = bdrv_co_do_pwrite_zeroes(bs, align_offset, pnum,
160
BDRV_REQ_WRITE_UNCHANGED);
161
} else {
162
/* This does not change the data on the disk, it is not
163
* necessary to flush even in cache=writethrough mode.
164
*/
165
- ret = bdrv_driver_pwritev(bs, cluster_offset, pnum,
166
+ ret = bdrv_driver_pwritev(bs, align_offset, pnum,
167
&local_qiov, 0,
168
BDRV_REQ_WRITE_UNCHANGED);
169
}
170
@@ -XXX,XX +XXX,XX @@ bdrv_co_do_copy_on_readv(BdrvChild *child, int64_t offset, int64_t bytes,
44
}
171
}
45
}
172
}
46
@@ -XXX,XX +XXX,XX @@ static void report_unsupported_feature(Error **errp, Qcow2Feature *table,
173
174
- cluster_offset += pnum;
175
- cluster_bytes -= pnum;
176
+ align_offset += pnum;
177
+ align_bytes -= pnum;
178
progress += pnum - skip_bytes;
179
skip_bytes = 0;
47
}
180
}
48
181
diff --git a/block/mirror.c b/block/mirror.c
49
if (mask) {
182
index XXXXXXX..XXXXXXX 100644
50
- old = features;
183
--- a/block/mirror.c
51
- features = g_strdup_printf("%s%sUnknown incompatible feature: %" PRIx64,
184
+++ b/block/mirror.c
52
- old, *old ? ", " : "", mask);
185
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn mirror_cow_align(MirrorBlockJob *s, int64_t *offset,
53
- g_free(old);
186
need_cow |= !test_bit((*offset + *bytes - 1) / s->granularity,
54
+ if (features->len > 0) {
187
s->cow_bitmap);
55
+ g_string_append(features, ", ");
188
if (need_cow) {
56
+ }
189
- bdrv_round_to_clusters(blk_bs(s->target), *offset, *bytes,
57
+ g_string_append_printf(features,
190
- &align_offset, &align_bytes);
58
+ "Unknown incompatible feature: %" PRIx64, mask);
191
+ bdrv_round_to_subclusters(blk_bs(s->target), *offset, *bytes,
192
+ &align_offset, &align_bytes);
59
}
193
}
60
194
61
- error_setg(errp, "Unsupported qcow2 feature(s): %s", features);
195
if (align_bytes > max_bytes) {
62
- g_free(features);
196
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_iteration(MirrorBlockJob *s)
63
+ error_setg(errp, "Unsupported qcow2 feature(s): %s", features->str);
197
int64_t target_offset;
64
}
198
int64_t target_bytes;
65
199
WITH_GRAPH_RDLOCK_GUARD() {
66
/*
200
- bdrv_round_to_clusters(blk_bs(s->target), offset, io_bytes,
201
- &target_offset, &target_bytes);
202
+ bdrv_round_to_subclusters(blk_bs(s->target), offset, io_bytes,
203
+ &target_offset, &target_bytes);
204
}
205
if (target_offset == offset &&
206
target_bytes == io_bytes) {
67
--
207
--
68
2.24.1
208
2.41.0
69
70
diff view generated by jsdifflib
Deleted patch
1
From: John Snow <jsnow@redhat.com>
2
1
3
verify_platform will check an explicit whitelist and blacklist instead.
4
The default will now be assumed to be allowed to run anywhere.
5
6
For tests that do not specify their platforms explicitly, this has the effect of
7
enabling these tests on non-linux platforms. For tests that always specified
8
linux explicitly, there is no change.
9
10
For Python tests on FreeBSD at least; only seven python tests fail:
11
045 147 149 169 194 199 211
12
13
045 and 149 appear to be misconfigurations,
14
147 and 194 are the AF_UNIX path too long error,
15
169 and 199 are bitmap migration bugs, and
16
211 is a bug that shows up on Linux platforms, too.
17
18
This is at least good evidence that these tests are not Linux-only. If
19
they aren't suitable for other platforms, they should be disabled on a
20
per-platform basis as appropriate.
21
22
Therefore, let's switch these on and deal with the failures.
23
24
Reviewed-by: Max Reitz <mreitz@redhat.com>
25
Signed-off-by: John Snow <jsnow@redhat.com>
26
Message-id: 20200121095205.26323-2-thuth@redhat.com
27
Signed-off-by: Max Reitz <mreitz@redhat.com>
28
---
29
tests/qemu-iotests/iotests.py | 16 +++++++++++-----
30
1 file changed, 11 insertions(+), 5 deletions(-)
31
32
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
33
index XXXXXXX..XXXXXXX 100644
34
--- a/tests/qemu-iotests/iotests.py
35
+++ b/tests/qemu-iotests/iotests.py
36
@@ -XXX,XX +XXX,XX @@ def verify_protocol(supported=[], unsupported=[]):
37
if not_sup or (imgproto in unsupported):
38
notrun('not suitable for this protocol: %s' % imgproto)
39
40
-def verify_platform(supported_oses=['linux']):
41
- if True not in [sys.platform.startswith(x) for x in supported_oses]:
42
- notrun('not suitable for this OS: %s' % sys.platform)
43
+def verify_platform(supported=None, unsupported=None):
44
+ if unsupported is not None:
45
+ if any((sys.platform.startswith(x) for x in unsupported)):
46
+ notrun('not suitable for this OS: %s' % sys.platform)
47
+
48
+ if supported is not None:
49
+ if not any((sys.platform.startswith(x) for x in supported)):
50
+ notrun('not suitable for this OS: %s' % sys.platform)
51
52
def verify_cache_mode(supported_cache_modes=[]):
53
if supported_cache_modes and (cachemode not in supported_cache_modes):
54
@@ -XXX,XX +XXX,XX @@ def execute_unittest(output, verbosity, debug):
55
sys.stderr.write(out)
56
57
def execute_test(test_function=None,
58
- supported_fmts=[], supported_oses=['linux'],
59
+ supported_fmts=[],
60
+ supported_platforms=None,
61
supported_cache_modes=[], supported_aio_modes={},
62
unsupported_fmts=[], supported_protocols=[],
63
unsupported_protocols=[]):
64
@@ -XXX,XX +XXX,XX @@ def execute_test(test_function=None,
65
verbosity = 1
66
verify_image_format(supported_fmts, unsupported_fmts)
67
verify_protocol(supported_protocols, unsupported_protocols)
68
- verify_platform(supported_oses)
69
+ verify_platform(supported=supported_platforms)
70
verify_cache_mode(supported_cache_modes)
71
verify_aio_mode(supported_aio_modes)
72
73
--
74
2.24.1
75
76
diff view generated by jsdifflib
Deleted patch
1
From: Thomas Huth <thuth@redhat.com>
2
1
3
041 works fine on Linux, FreeBSD, NetBSD and OpenBSD, but fails on macOS.
4
Let's mark it as only supported on the systems where we know that it is
5
working fine.
6
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Signed-off-by: Thomas Huth <thuth@redhat.com>
9
Message-id: 20200121095205.26323-3-thuth@redhat.com
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
---
12
tests/qemu-iotests/041 | 3 ++-
13
1 file changed, 2 insertions(+), 1 deletion(-)
14
15
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
16
index XXXXXXX..XXXXXXX 100755
17
--- a/tests/qemu-iotests/041
18
+++ b/tests/qemu-iotests/041
19
@@ -XXX,XX +XXX,XX @@ class TestOrphanedSource(iotests.QMPTestCase):
20
21
if __name__ == '__main__':
22
iotests.main(supported_fmts=['qcow2', 'qed'],
23
- supported_protocols=['file'])
24
+ supported_protocols=['file'],
25
+ supported_platforms=['linux', 'freebsd', 'netbsd', 'openbsd'])
26
--
27
2.24.1
28
29
diff view generated by jsdifflib
Deleted patch
1
From: Thomas Huth <thuth@redhat.com>
2
1
3
In the long run, we might want to add test 183 to the "auto" group
4
(but it still fails occasionally, so we cannot do that yet). However,
5
when running 183 in Cirrus-CI on macOS, or with our vm-build-openbsd
6
target, it currently always fails with an "Timeout waiting for return
7
on handle 0" error.
8
9
Let's mark it as supported only on systems where the test is working
10
most of the time (i.e. Linux, FreeBSD and NetBSD).
11
12
Reviewed-by: Max Reitz <mreitz@redhat.com>
13
Signed-off-by: Thomas Huth <thuth@redhat.com>
14
Message-id: 20200121095205.26323-4-thuth@redhat.com
15
Signed-off-by: Max Reitz <mreitz@redhat.com>
16
---
17
tests/qemu-iotests/183 | 1 +
18
1 file changed, 1 insertion(+)
19
20
diff --git a/tests/qemu-iotests/183 b/tests/qemu-iotests/183
21
index XXXXXXX..XXXXXXX 100755
22
--- a/tests/qemu-iotests/183
23
+++ b/tests/qemu-iotests/183
24
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
25
. ./common.filter
26
. ./common.qemu
27
28
+_supported_os Linux FreeBSD NetBSD
29
_supported_fmt qcow2 raw qed quorum
30
_supported_proto file
31
32
--
33
2.24.1
34
35
diff view generated by jsdifflib
Deleted patch
1
From: Thomas Huth <thuth@redhat.com>
2
1
3
We are going to enable 127 in the "auto" group, but it only works if
4
virtio-scsi and scsi-hd are available - which is not the case with
5
QEMU binaries like qemu-system-tricore for example, so we need a
6
proper check for the availability of these devices here.
7
8
A very similar problem exists in iotest 267 - it has been added to
9
the "auto" group already, but requires virtio-blk and thus currently
10
fails with qemu-system-tricore for example. Let's also add aproper
11
check there.
12
13
Reviewed-by: Max Reitz <mreitz@redhat.com>
14
Signed-off-by: Thomas Huth <thuth@redhat.com>
15
Message-id: 20200121095205.26323-5-thuth@redhat.com
16
Signed-off-by: Max Reitz <mreitz@redhat.com>
17
---
18
tests/qemu-iotests/127 | 2 ++
19
tests/qemu-iotests/267 | 2 ++
20
tests/qemu-iotests/common.rc | 14 ++++++++++++++
21
3 files changed, 18 insertions(+)
22
23
diff --git a/tests/qemu-iotests/127 b/tests/qemu-iotests/127
24
index XXXXXXX..XXXXXXX 100755
25
--- a/tests/qemu-iotests/127
26
+++ b/tests/qemu-iotests/127
27
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
28
_supported_fmt qcow2
29
_supported_proto file
30
31
+_require_devices virtio-scsi scsi-hd
32
+
33
IMG_SIZE=64K
34
35
_make_test_img $IMG_SIZE
36
diff --git a/tests/qemu-iotests/267 b/tests/qemu-iotests/267
37
index XXXXXXX..XXXXXXX 100755
38
--- a/tests/qemu-iotests/267
39
+++ b/tests/qemu-iotests/267
40
@@ -XXX,XX +XXX,XX @@ _require_drivers copy-on-read
41
# and generally impossible with external data files
42
_unsupported_imgopts 'refcount_bits=1[^0-9]' data_file
43
44
+_require_devices virtio-blk
45
+
46
do_run_qemu()
47
{
48
echo Testing: "$@"
49
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
50
index XXXXXXX..XXXXXXX 100644
51
--- a/tests/qemu-iotests/common.rc
52
+++ b/tests/qemu-iotests/common.rc
53
@@ -XXX,XX +XXX,XX @@ _require_large_file()
54
rm "$TEST_IMG"
55
}
56
57
+# Check that a set of devices is available in the QEMU binary
58
+#
59
+_require_devices()
60
+{
61
+ available=$($QEMU -M none -device help | \
62
+ grep ^name | sed -e 's/^name "//' -e 's/".*$//')
63
+ for device
64
+ do
65
+ if ! echo "$available" | grep -q "$device" ; then
66
+ _notrun "$device not available"
67
+ fi
68
+ done
69
+}
70
+
71
# make sure this script returns success
72
true
73
--
74
2.24.1
75
76
diff view generated by jsdifflib
Deleted patch
1
From: Thomas Huth <thuth@redhat.com>
2
1
3
We are going to enable some of the python-based tests in the "auto" group,
4
and these tests require virtio-blk to work properly. Running iotests
5
without virtio-blk likely does not make too much sense anyway, so instead
6
of adding a check for the availability of virtio-blk to each and every
7
test (which does not sound very appealing), let's rather add a check for
8
this a central spot in the "check" script instead (so that it is still
9
possible to run "make check" for qemu-system-tricore for example).
10
11
Signed-off-by: Thomas Huth <thuth@redhat.com>
12
Message-id: 20200121095205.26323-6-thuth@redhat.com
13
Signed-off-by: Max Reitz <mreitz@redhat.com>
14
---
15
tests/qemu-iotests/check | 12 ++++++++++--
16
1 file changed, 10 insertions(+), 2 deletions(-)
17
18
diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check
19
index XXXXXXX..XXXXXXX 100755
20
--- a/tests/qemu-iotests/check
21
+++ b/tests/qemu-iotests/check
22
@@ -XXX,XX +XXX,XX @@ fi
23
python_usable=false
24
if $PYTHON -c 'import sys; sys.exit(0 if sys.version_info >= (3,6) else 1)'
25
then
26
- python_usable=true
27
+ # Our python framework also requires virtio-blk
28
+ if "$QEMU_PROG" -M none -device help | grep -q virtio-blk >/dev/null 2>&1
29
+ then
30
+ python_usable=true
31
+ else
32
+ python_unusable_because="Missing virtio-blk in QEMU binary"
33
+ fi
34
+else
35
+ python_unusable_because="Unsupported Python version"
36
fi
37
38
default_machine=$($QEMU_PROG -machine help | sed -n '/(default)/ s/ .*//p')
39
@@ -XXX,XX +XXX,XX @@ do
40
run_command="$PYTHON $seq"
41
else
42
run_command="false"
43
- echo "Unsupported Python version" > $seq.notrun
44
+ echo "$python_unusable_because" > $seq.notrun
45
fi
46
else
47
run_command="./$seq"
48
--
49
2.24.1
50
51
diff view generated by jsdifflib
Deleted patch
1
From: Thomas Huth <thuth@redhat.com>
2
1
3
According to Kevin, tests 030, 040 and 041 are among the most valuable
4
tests that we have, so we should always run them if possible, even if
5
they take a little bit longer.
6
7
According to Max, it would be good to have a test for iothreads and
8
migration. 127 and 256 seem to be good candidates for iothreads. For
9
migration, let's enable 181 and 203 (which also tests iothreads).
10
(091 would be a good candidate for migration, too, but Alex Bennée
11
reported that this test fails on ZFS file systems, so it can't be
12
included yet)
13
14
Reviewed-by: Max Reitz <mreitz@redhat.com>
15
Signed-off-by: Thomas Huth <thuth@redhat.com>
16
Message-id: 20200121095205.26323-7-thuth@redhat.com
17
Signed-off-by: Max Reitz <mreitz@redhat.com>
18
---
19
tests/qemu-iotests/group | 14 +++++++-------
20
1 file changed, 7 insertions(+), 7 deletions(-)
21
22
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
23
index XXXXXXX..XXXXXXX 100644
24
--- a/tests/qemu-iotests/group
25
+++ b/tests/qemu-iotests/group
26
@@ -XXX,XX +XXX,XX @@
27
027 rw auto quick
28
028 rw backing quick
29
029 rw auto quick
30
-030 rw backing
31
+030 rw auto backing
32
031 rw auto quick
33
032 rw auto quick
34
033 rw auto quick
35
@@ -XXX,XX +XXX,XX @@
36
037 rw auto backing quick
37
038 rw auto backing quick
38
039 rw auto quick
39
-040 rw
40
-041 rw backing
41
+040 rw auto
42
+041 rw auto backing
43
042 rw auto quick
44
043 rw auto backing
45
044 rw
46
@@ -XXX,XX +XXX,XX @@
47
124 rw backing
48
125 rw
49
126 rw auto backing
50
-127 rw backing quick
51
+127 rw auto backing quick
52
128 rw quick
53
129 rw quick
54
130 rw quick
55
@@ -XXX,XX +XXX,XX @@
56
177 rw auto quick
57
178 img
58
179 rw auto quick
59
-181 rw migration
60
+181 rw auto migration
61
182 rw quick
62
183 rw migration
63
184 rw auto quick
64
@@ -XXX,XX +XXX,XX @@
65
200 rw
66
201 rw migration
67
202 rw quick
68
-203 rw migration
69
+203 rw auto migration
70
204 rw quick
71
205 rw quick
72
206 rw
73
@@ -XXX,XX +XXX,XX @@
74
253 rw quick
75
254 rw backing quick
76
255 rw quick
77
-256 rw quick
78
+256 rw auto quick
79
257 rw
80
258 rw quick
81
260 rw quick
82
--
83
2.24.1
84
85
diff view generated by jsdifflib
Deleted patch
1
From: Alberto Garcia <berto@igalia.com>
2
1
3
qcow2_alloc_cluster_offset() and qcow2_get_cluster_offset() always
4
return offsets that are cluster-aligned so don't just check that they
5
are sector-aligned.
6
7
The check in qcow2_co_preadv_task() is also replaced by an assertion
8
for the same reason.
9
10
Signed-off-by: Alberto Garcia <berto@igalia.com>
11
Reviewed-by: Max Reitz <mreitz@redhat.com>
12
Message-id: 558ba339965f858bede4c73ce3f50f0c0493597d.1579374329.git.berto@igalia.com
13
Signed-off-by: Max Reitz <mreitz@redhat.com>
14
---
15
block/qcow2.c | 9 +++------
16
1 file changed, 3 insertions(+), 6 deletions(-)
17
18
diff --git a/block/qcow2.c b/block/qcow2.c
19
index XXXXXXX..XXXXXXX 100644
20
--- a/block/qcow2.c
21
+++ b/block/qcow2.c
22
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_co_preadv_task(BlockDriverState *bs,
23
offset, bytes, qiov, qiov_offset);
24
25
case QCOW2_CLUSTER_NORMAL:
26
- if ((file_cluster_offset & 511) != 0) {
27
- return -EIO;
28
- }
29
-
30
+ assert(offset_into_cluster(s, file_cluster_offset) == 0);
31
if (bs->encrypted) {
32
return qcow2_co_preadv_encrypted(bs, file_cluster_offset,
33
offset, bytes, qiov, qiov_offset);
34
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_co_pwritev_part(
35
goto out_locked;
36
}
37
38
- assert((cluster_offset & 511) == 0);
39
+ assert(offset_into_cluster(s, cluster_offset) == 0);
40
41
ret = qcow2_pre_write_overlap_check(bs, 0,
42
cluster_offset + offset_in_cluster,
43
@@ -XXX,XX +XXX,XX @@ qcow2_co_copy_range_to(BlockDriverState *bs,
44
goto fail;
45
}
46
47
- assert((cluster_offset & 511) == 0);
48
+ assert(offset_into_cluster(s, cluster_offset) == 0);
49
50
ret = qcow2_pre_write_overlap_check(bs, 0,
51
cluster_offset + offset_in_cluster, cur_bytes, true);
52
--
53
2.24.1
54
55
diff view generated by jsdifflib
1
From: Alberto Garcia <berto@igalia.com>
1
From: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
2
2
3
qemu-img's convert_co_copy_range() operates at the sector level and
3
Add testcase which checks that allocations during copy-on-read are
4
block_copy() operates at the cluster level so this condition is always
4
performed on the subcluster basis when subclusters are enabled in target
5
true, but it is not necessary to restrict this here, so let's leave it
5
image.
6
to the driver implementation return an error if there is any.
7
6
8
Signed-off-by: Alberto Garcia <berto@igalia.com>
7
This testcase also triggers the following assert with previous commit
9
Message-id: a4264aaee656910c84161a2965f7a501437379ca.1579374329.git.berto@igalia.com
8
not being applied, so we check that as well:
10
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
qemu-io: ../block/io.c:1236: bdrv_co_do_copy_on_readv: Assertion `skip_bytes < pnum' failed.
11
12
Reviewed-by: Eric Blake <eblake@redhat.com>
13
Reviewed-by: Denis V. Lunev <den@openvz.org>
14
Signed-off-by: Andrey Drobyshev <andrey.drobyshev@virtuozzo.com>
15
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
16
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
17
Message-ID: <20230711172553.234055-4-andrey.drobyshev@virtuozzo.com>
12
---
18
---
13
block/qcow2.c | 4 ----
19
tests/qemu-iotests/197 | 29 +++++++++++++++++++++++++++++
14
1 file changed, 4 deletions(-)
20
tests/qemu-iotests/197.out | 24 ++++++++++++++++++++++++
21
2 files changed, 53 insertions(+)
15
22
16
diff --git a/block/qcow2.c b/block/qcow2.c
23
diff --git a/tests/qemu-iotests/197 b/tests/qemu-iotests/197
24
index XXXXXXX..XXXXXXX 100755
25
--- a/tests/qemu-iotests/197
26
+++ b/tests/qemu-iotests/197
27
@@ -XXX,XX +XXX,XX @@ $QEMU_IO -f qcow2 -C -c 'read 0 1024' "$TEST_WRAP" | _filter_qemu_io
28
$QEMU_IO -f qcow2 -c map "$TEST_WRAP"
29
_check_test_img
30
31
+echo
32
+echo '=== Copy-on-read with subclusters ==='
33
+echo
34
+
35
+# Create base and top images 64K (1 cluster) each. Make subclusters enabled
36
+# for the top image
37
+_make_test_img 64K
38
+IMGPROTO=file IMGFMT=qcow2 TEST_IMG_FILE="$TEST_WRAP" \
39
+ _make_test_img --no-opts -o extended_l2=true -F "$IMGFMT" -b "$TEST_IMG" \
40
+ 64K | _filter_img_create
41
+
42
+$QEMU_IO -c "write -P 0xaa 0 64k" "$TEST_IMG" | _filter_qemu_io
43
+
44
+# Allocate individual subclusters in the top image, and not the whole cluster
45
+$QEMU_IO -c "write -P 0xbb 28K 2K" -c "write -P 0xcc 34K 2K" "$TEST_WRAP" \
46
+ | _filter_qemu_io
47
+
48
+# Only 2 subclusters should be allocated in the top image at this point
49
+$QEMU_IMG map "$TEST_WRAP" | _filter_qemu_img_map
50
+
51
+# Actual copy-on-read operation
52
+$QEMU_IO -C -c "read -P 0xaa 30K 4K" "$TEST_WRAP" | _filter_qemu_io
53
+
54
+# And here we should have 4 subclusters allocated right in the middle of the
55
+# top image. Make sure the whole cluster remains unallocated
56
+$QEMU_IMG map "$TEST_WRAP" | _filter_qemu_img_map
57
+
58
+_check_test_img
59
+
60
# success, all done
61
echo '*** done'
62
status=0
63
diff --git a/tests/qemu-iotests/197.out b/tests/qemu-iotests/197.out
17
index XXXXXXX..XXXXXXX 100644
64
index XXXXXXX..XXXXXXX 100644
18
--- a/block/qcow2.c
65
--- a/tests/qemu-iotests/197.out
19
+++ b/block/qcow2.c
66
+++ b/tests/qemu-iotests/197.out
20
@@ -XXX,XX +XXX,XX @@ qcow2_co_copy_range_from(BlockDriverState *bs,
67
@@ -XXX,XX +XXX,XX @@ read 1024/1024 bytes at offset 0
21
case QCOW2_CLUSTER_NORMAL:
68
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
22
child = s->data_file;
69
1 KiB (0x400) bytes allocated at offset 0 bytes (0x0)
23
copy_offset += offset_into_cluster(s, src_offset);
70
No errors were found on the image.
24
- if ((copy_offset & 511) != 0) {
71
+
25
- ret = -EIO;
72
+=== Copy-on-read with subclusters ===
26
- goto out;
73
+
27
- }
74
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536
28
break;
75
+Formatting 'TEST_DIR/t.wrap.IMGFMT', fmt=IMGFMT size=65536 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT
29
76
+wrote 65536/65536 bytes at offset 0
30
default:
77
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
78
+wrote 2048/2048 bytes at offset 28672
79
+2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
80
+wrote 2048/2048 bytes at offset 34816
81
+2 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
82
+Offset Length File
83
+0 0x7000 TEST_DIR/t.IMGFMT
84
+0x7000 0x800 TEST_DIR/t.wrap.IMGFMT
85
+0x7800 0x1000 TEST_DIR/t.IMGFMT
86
+0x8800 0x800 TEST_DIR/t.wrap.IMGFMT
87
+0x9000 0x7000 TEST_DIR/t.IMGFMT
88
+read 4096/4096 bytes at offset 30720
89
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
90
+Offset Length File
91
+0 0x7000 TEST_DIR/t.IMGFMT
92
+0x7000 0x2000 TEST_DIR/t.wrap.IMGFMT
93
+0x9000 0x7000 TEST_DIR/t.IMGFMT
94
+No errors were found on the image.
95
*** done
31
--
96
--
32
2.24.1
97
2.41.0
33
34
diff view generated by jsdifflib