hw/ufs: Separate the controller core from the PCI frontend
UfsHc is currently also the PCI device instance, tying common code to PCI-specific DMA and IRQ interfaces and preventing reuse by non-PCI frontends. Make UfsHc transport-independent and embed it in UfsPciState. Move the PCI-specific handling to ufs-pci.c, pass the owning DeviceState and DMA AddressSpace to the core, and record the core explicitly in UfsBus. Split the common implementation into CONFIG_UFS, selected by CONFIG_UFS_PCI. The user-visible "ufs" device and its properties remain unchanged. No functional change is intended. Signed-off-by: Jeuk Kim <jeuk20.kim@samsung.com>
Jeuk Kim committed
Aug 7, 2026 at 19:36 UTC
42feb94b790523f83c1d39141c59face0e327211
7 files changed
+153
-96
hw/ufs/Kconfig
+5
@@ -1,4 +1,9 @@
1
+config UFS
2
+ bool
3
+ select SCSI
4
+
5
config UFS_PCI
6
bool
7
default y if PCI_DEVICES
8
depends on PCI
9
+ select UFS
hw/ufs/lu.c
+1
-1
@@ -497,7 +497,7 @@ static void ufs_lu_realize(DeviceState *dev, Error **errp)
497
{
498
UfsLu *lu = DO_UPCAST(UfsLu, qdev, dev);
499
BusState *s = qdev_get_parent_bus(dev);
500
- UfsHc *u = UFS(s->parent);
500
+ UfsHc *u = UFS_BUS(s)->hc;
501
BlockBackend *blk = lu->conf.blk;
502
503
if (!ufs_lu_check_constraints(lu, errp)) {
hw/ufs/meson.build
+2
-1
@@ -1 +1,2 @@
1
-system_ss.add(when: 'CONFIG_UFS_PCI', if_true: files('ufs.c', 'lu.c'))
1
+system_ss.add(when: 'CONFIG_UFS', if_true: files('ufs.c', 'lu.c'))
2
+system_ss.add(when: 'CONFIG_UFS_PCI', if_true: files('ufs-pci.c'))
hw/ufs/trace-events
+2
-2
@@ -1,6 +1,6 @@
1
# ufs.c
2
-ufs_irq_raise(void) "INTx"
3
-ufs_irq_lower(void) "INTx"
2
+ufs_irq_raise(void) "IRQ"
3
+ufs_irq_lower(void) "IRQ"
4
ufs_mmio_read(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d"
5
ufs_mmio_write(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d"
6
ufs_process_db(uint32_t slot) "UTRLDBR slot %"PRIu32""
hw/ufs/ufs-pci.c
new
+112
@@ -0,0 +1,112 @@
1
+/*
2
+ * QEMU Universal Flash Storage (UFS) PCI Controller
3
+ *
4
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved.
5
+ *
6
+ * Written by Jeuk Kim <jeuk20.kim@samsung.com>
7
+ *
8
+ * SPDX-License-Identifier: GPL-2.0-or-later
9
+ */
10
+
11
+/**
12
+ * Usage
13
+ * -----
14
+ *
15
+ * Add options:
16
+ * -drive file=<file>,if=none,id=<drive_id>
17
+ * -device ufs,serial=<serial>,id=<bus_name>, \
18
+ * nutrs=<N[optional]>,nutmrs=<N[optional]>
19
+ * -device ufs-lu,drive=<drive_id>,bus=<bus_name>
20
+ */
21
+
22
+#include "qemu/osdep.h"
23
+#include "hw/core/irq.h"
24
+#include "hw/core/qdev-properties.h"
25
+#include "hw/pci/pci.h"
26
+#include "hw/pci/pci_device.h"
27
+#include "migration/vmstate.h"
28
+#include "ufs.h"
29
+
30
+#define TYPE_UFS_PCI "ufs"
31
+OBJECT_DECLARE_SIMPLE_TYPE(UfsPciState, UFS_PCI)
32
+
33
+struct UfsPciState {
34
+ PCIDevice parent_obj;
35
+ UfsHc ufs;
36
+};
37
+
38
+static void ufs_pci_realize(PCIDevice *pci_dev, Error **errp)
39
+{
40
+ UfsPciState *s = UFS_PCI(pci_dev);
41
+ UfsHc *u = &s->ufs;
42
+ uint8_t *pci_conf = pci_dev->config;
43
+
44
+ pci_conf[PCI_INTERRUPT_PIN] = 1;
45
+ pci_config_set_prog_interface(pci_conf, 0x1);
46
+ u->irq = pci_allocate_irq(pci_dev);
47
+ if (!ufs_realize(u, DEVICE(pci_dev), pci_get_address_space(pci_dev),
48
+ errp)) {
49
+ qemu_free_irq(u->irq);
50
+ u->irq = NULL;
51
+ return;
52
+ }
53
+
54
+ pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &u->iomem);
55
+}
56
+
57
+static void ufs_pci_exit(PCIDevice *pci_dev)
58
+{
59
+ UfsPciState *s = UFS_PCI(pci_dev);
60
+
61
+ ufs_unrealize(&s->ufs);
62
+ qemu_free_irq(s->ufs.irq);
63
+}
64
+
65
+static const Property ufs_pci_props[] = {
66
+ DEFINE_PROP_STRING("serial", UfsPciState, ufs.params.serial),
67
+ DEFINE_PROP_UINT8("nutrs", UfsPciState, ufs.params.nutrs, 32),
68
+ DEFINE_PROP_UINT8("nutmrs", UfsPciState, ufs.params.nutmrs, 8),
69
+ DEFINE_PROP_BOOL("mcq", UfsPciState, ufs.params.mcq, false),
70
+ DEFINE_PROP_UINT8("mcq-maxq", UfsPciState, ufs.params.mcq_maxq, 2),
71
+ DEFINE_PROP_UINT32("wb-max-size", UfsPciState, ufs.params.wb_max_size,
72
+ 0x400),
73
+ DEFINE_PROP_UINT32("wb-min-size", UfsPciState, ufs.params.wb_min_size,
74
+ 0x100),
75
+};
76
+
77
+static const VMStateDescription ufs_pci_vmstate = {
78
+ .name = "ufs",
79
+ .unmigratable = 1,
80
+};
81
+
82
+static void ufs_pci_class_init(ObjectClass *oc, const void *data)
83
+{
84
+ DeviceClass *dc = DEVICE_CLASS(oc);
85
+ PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
86
+
87
+ pc->realize = ufs_pci_realize;
88
+ pc->exit = ufs_pci_exit;
89
+ pc->vendor_id = PCI_VENDOR_ID_REDHAT;
90
+ pc->device_id = PCI_DEVICE_ID_REDHAT_UFS;
91
+ pc->class_id = PCI_CLASS_STORAGE_UFS;
92
+
93
+ set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
94
+ dc->desc = "Universal Flash Storage";
95
+ device_class_set_props(dc, ufs_pci_props);
96
+ dc->vmsd = &ufs_pci_vmstate;
97
+}
98
+
99
+static const TypeInfo ufs_pci_info = {
100
+ .name = TYPE_UFS_PCI,
101
+ .parent = TYPE_PCI_DEVICE,
102
+ .class_init = ufs_pci_class_init,
103
+ .instance_size = sizeof(UfsPciState),
104
+ .interfaces = (const InterfaceInfo[]){ { INTERFACE_PCIE_DEVICE }, {} },
105
+};
106
+
107
+static void ufs_pci_register_types(void)
108
+{
109
+ type_register_static(&ufs_pci_info);
110
+}
111
+
112
+type_init(ufs_pci_register_types)
hw/ufs/ufs.c
+22
-87
@@ -11,19 +11,10 @@
11
/**
12
* Reference Specs: https://www.jedec.org/, 4.1
13
*
14
- * Usage
15
- * -----
16
- *
17
- * Add options:
18
- * -drive file=<file>,if=none,id=<drive_id>
19
- * -device ufs,serial=<serial>,id=<bus_name>, \
20
- * nutrs=<N[optional]>,nutmrs=<N[optional]>
21
- * -device ufs-lu,drive=<drive_id>,bus=<bus_name>
14
*/
15
16
#include "qemu/osdep.h"
17
#include "qapi/error.h"
26
-#include "migration/vmstate.h"
18
#include "scsi/constants.h"
19
#include "hw/core/irq.h"
20
#include "trace.h"
@@ -102,7 +93,7 @@ static MemTxResult ufs_addr_read(UfsHc *u, hwaddr addr, void *buf, int size)
93
return MEMTX_DECODE_ERROR;
94
}
95
105
- return pci_dma_read(PCI_DEVICE(u), addr, buf, size);
96
+ return dma_memory_read(u->dma_as, addr, buf, size, MEMTXATTRS_UNSPECIFIED);
97
}
98
99
static MemTxResult ufs_addr_write(UfsHc *u, hwaddr addr, const void *buf,
@@ -117,7 +108,7 @@ static MemTxResult ufs_addr_write(UfsHc *u, hwaddr addr, const void *buf,
108
return MEMTX_DECODE_ERROR;
109
}
110
120
- return pci_dma_write(PCI_DEVICE(u), addr, buf, size);
111
+ return dma_memory_write(u->dma_as, addr, buf, size, MEMTXATTRS_UNSPECIFIED);
112
}
113
114
static inline hwaddr ufs_get_utrd_addr(UfsHc *u, uint32_t slot)
@@ -222,7 +213,7 @@ static MemTxResult ufs_dma_read_prdt(UfsRequest *req)
213
}
214
215
req->sg = g_malloc0(sizeof(QEMUSGList));
225
- pci_dma_sglist_init(req->sg, PCI_DEVICE(u), prdt_len);
216
+ qemu_sglist_init(req->sg, u->dev, prdt_len, u->dma_as);
217
req->data_len = 0;
218
219
for (uint16_t i = 0; i < prdt_len; ++i) {
@@ -317,14 +308,12 @@ static MemTxResult ufs_dma_write_upiu(UfsRequest *req)
308
309
static void ufs_irq_check(UfsHc *u)
310
{
320
- PCIDevice *pci = PCI_DEVICE(u);
321
-
311
if ((u->reg.is & UFS_INTR_MASK) & u->reg.ie) {
312
trace_ufs_irq_raise();
324
- pci_irq_assert(pci);
313
+ qemu_irq_raise(u->irq);
314
} else {
315
trace_ufs_irq_lower();
327
- pci_irq_deassert(pci);
316
+ qemu_irq_lower(u->irq);
317
}
318
}
319
@@ -596,7 +585,7 @@ static bool ufs_mcq_create_sq(UfsHc *u, uint8_t qid, uint32_t attr)
585
sq->size = qsize;
586
587
sq->bh = qemu_bh_new_guarded(ufs_mcq_process_sq, sq,
599
- &DEVICE(u)->mem_reentrancy_guard);
588
+ &u->dev->mem_reentrancy_guard);
589
sq->req = g_new0(UfsRequest, sq->size);
590
QTAILQ_INIT(&sq->req_list);
591
for (int i = 0; i < sq->size; i++) {
@@ -690,7 +679,7 @@ static bool ufs_mcq_create_cq(UfsHc *u, uint8_t qid, uint32_t attr)
679
cq->size = qsize;
680
681
cq->bh = qemu_bh_new_guarded(ufs_mcq_process_cq, cq,
693
- &DEVICE(u)->mem_reentrancy_guard);
682
+ &u->dev->mem_reentrancy_guard);
683
QTAILQ_INIT(&cq->req_list);
684
685
u->cq[qid] = cq;
@@ -2488,19 +2477,6 @@ static bool ufs_check_constraints(UfsHc *u, Error **errp)
2477
return true;
2478
}
2479
2491
-static void ufs_init_pci(UfsHc *u, PCIDevice *pci_dev)
2492
-{
2493
- uint8_t *pci_conf = pci_dev->config;
2494
-
2495
- pci_conf[PCI_INTERRUPT_PIN] = 1;
2496
- pci_config_set_prog_interface(pci_conf, 0x1);
2497
-
2498
- memory_region_init_io(&u->iomem, OBJECT(u), &ufs_mmio_ops, u, "ufs",
2499
- u->reg_size);
2500
- pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &u->iomem);
2501
- u->irq = pci_allocate_irq(pci_dev);
2502
-}
2503
-
2480
static void ufs_init_state(UfsHc *u)
2481
{
2482
u->req_list = g_new0(UfsRequest, u->params.nutrs);
@@ -2512,10 +2488,10 @@ static void ufs_init_state(UfsHc *u)
2488
u->req_list[i].state = UFS_REQUEST_IDLE;
2489
}
2490
2515
- u->doorbell_bh = qemu_bh_new_guarded(ufs_process_req, u,
2516
- &DEVICE(u)->mem_reentrancy_guard);
2517
- u->complete_bh = qemu_bh_new_guarded(ufs_sendback_req, u,
2518
- &DEVICE(u)->mem_reentrancy_guard);
2491
+ u->doorbell_bh =
2492
+ qemu_bh_new_guarded(ufs_process_req, u, &u->dev->mem_reentrancy_guard);
2493
+ u->complete_bh =
2494
+ qemu_bh_new_guarded(ufs_sendback_req, u, &u->dev->mem_reentrancy_guard);
2495
2496
if (u->params.mcq) {
2497
memset(u->sq, 0, sizeof(u->sq));
@@ -2689,35 +2665,35 @@ static void ufs_init_hc(UfsHc *u)
2665
timer_mod(&u->idle_timer, now + UFS_IDLE_TIMER_TICK);
2666
}
2667
2692
-static void ufs_realize(PCIDevice *pci_dev, Error **errp)
2668
+bool ufs_realize(UfsHc *u, DeviceState *dev, AddressSpace *dma_as, Error **errp)
2669
{
2694
- UfsHc *u = UFS(pci_dev);
2670
+ u->dev = dev;
2671
+ u->dma_as = dma_as;
2672
2673
if (!ufs_check_constraints(u, errp)) {
2697
- return;
2674
+ return false;
2675
}
2676
2700
- qbus_init(&u->bus, sizeof(UfsBus), TYPE_UFS_BUS, &pci_dev->qdev,
2701
- u->parent_obj.qdev.id);
2677
+ qbus_init(&u->bus, sizeof(UfsBus), TYPE_UFS_BUS, dev, dev->id);
2678
+ u->bus.hc = u;
2679
2680
ufs_init_state(u);
2681
ufs_init_hc(u);
2705
- ufs_init_pci(u, pci_dev);
2682
+ memory_region_init_io(&u->iomem, OBJECT(dev), &ufs_mmio_ops, u, "ufs",
2683
+ u->reg_size);
2684
2685
ufs_init_wlu(&u->report_wlu, UFS_UPIU_REPORT_LUNS_WLUN);
2686
ufs_init_wlu(&u->dev_wlu, UFS_UPIU_UFS_DEVICE_WLUN);
2687
ufs_init_wlu(&u->boot_wlu, UFS_UPIU_BOOT_WLUN);
2688
ufs_init_wlu(&u->rpmb_wlu, UFS_UPIU_RPMB_WLUN);
2689
+
2690
+ return true;
2691
}
2692
2713
-static void ufs_exit(PCIDevice *pci_dev)
2693
+void ufs_unrealize(UfsHc *u)
2694
{
2715
- UfsHc *u = UFS(pci_dev);
2716
-
2695
timer_del(&u->idle_timer);
2696
2719
- qemu_free_irq(u->irq);
2720
-
2697
qemu_bh_delete(u->doorbell_bh);
2698
qemu_bh_delete(u->complete_bh);
2699
@@ -2740,38 +2716,6 @@ static void ufs_exit(PCIDevice *pci_dev)
2716
}
2717
}
2718
2743
-static const Property ufs_props[] = {
2744
- DEFINE_PROP_STRING("serial", UfsHc, params.serial),
2745
- DEFINE_PROP_UINT8("nutrs", UfsHc, params.nutrs, 32),
2746
- DEFINE_PROP_UINT8("nutmrs", UfsHc, params.nutmrs, 8),
2747
- DEFINE_PROP_BOOL("mcq", UfsHc, params.mcq, false),
2748
- DEFINE_PROP_UINT8("mcq-maxq", UfsHc, params.mcq_maxq, 2),
2749
- DEFINE_PROP_UINT32("wb-max-size", UfsHc, params.wb_max_size, 0x400),
2750
- DEFINE_PROP_UINT32("wb-min-size", UfsHc, params.wb_min_size, 0x100),
2751
-};
2752
-
2753
-static const VMStateDescription ufs_vmstate = {
2754
- .name = "ufs",
2755
- .unmigratable = 1,
2756
-};
2757
-
2758
-static void ufs_class_init(ObjectClass *oc, const void *data)
2759
-{
2760
- DeviceClass *dc = DEVICE_CLASS(oc);
2761
- PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
2762
-
2763
- pc->realize = ufs_realize;
2764
- pc->exit = ufs_exit;
2765
- pc->vendor_id = PCI_VENDOR_ID_REDHAT;
2766
- pc->device_id = PCI_DEVICE_ID_REDHAT_UFS;
2767
- pc->class_id = PCI_CLASS_STORAGE_UFS;
2768
-
2769
- set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
2770
- dc->desc = "Universal Flash Storage";
2771
- device_class_set_props(dc, ufs_props);
2772
- dc->vmsd = &ufs_vmstate;
2773
-}
2774
-
2719
static bool ufs_bus_check_address(BusState *qbus, DeviceState *qdev,
2720
Error **errp)
2721
{
@@ -2798,14 +2742,6 @@ static void ufs_bus_class_init(ObjectClass *class, const void *data)
2742
bc->check_address = ufs_bus_check_address;
2743
}
2744
2801
-static const TypeInfo ufs_info = {
2802
- .name = TYPE_UFS,
2803
- .parent = TYPE_PCI_DEVICE,
2804
- .class_init = ufs_class_init,
2805
- .instance_size = sizeof(UfsHc),
2806
- .interfaces = (const InterfaceInfo[]){ { INTERFACE_PCIE_DEVICE }, {} },
2807
-};
2808
-
2745
static const TypeInfo ufs_bus_info = {
2746
.name = TYPE_UFS_BUS,
2747
.parent = TYPE_BUS,
@@ -2816,7 +2752,6 @@ static const TypeInfo ufs_bus_info = {
2752
2753
static void ufs_register_types(void)
2754
{
2819
- type_register_static(&ufs_info);
2755
type_register_static(&ufs_bus_info);
2756
}
2757
hw/ufs/ufs.h
+9
-5
@@ -11,9 +11,11 @@
11
#ifndef HW_UFS_UFS_H
12
#define HW_UFS_UFS_H
13
14
-#include "hw/pci/pci_device.h"
14
+#include "hw/core/qdev.h"
15
#include "hw/scsi/scsi.h"
16
#include "block/ufs.h"
17
+#include "scsi/constants.h"
18
+#include "system/dma.h"
19
20
#define UFS_MAX_LUS 32
21
#define UFS_MAX_MCQ_QNUM 32
@@ -27,6 +29,7 @@ typedef struct UfsBusClass {
29
30
typedef struct UfsBus {
31
BusState parent_bus;
32
+ struct UfsHc *hc;
33
} UfsBus;
34
35
#define TYPE_UFS_BUS "ufs-bus"
@@ -141,7 +144,8 @@ typedef struct UfsWb {
144
} UfsWb;
145
146
typedef struct UfsHc {
144
- PCIDevice parent_obj;
147
+ DeviceState *dev;
148
+ AddressSpace *dma_as;
149
UfsBus bus;
150
MemoryRegion iomem;
151
UfsReg reg;
@@ -268,9 +272,6 @@ static inline bool ufs_is_write_req(UfsRequest *req)
272
return (cmd == WRITE_6) || (cmd == WRITE_10) || (cmd == WRITE_16);
273
}
274
271
-#define TYPE_UFS "ufs"
272
-#define UFS(obj) OBJECT_CHECK(UfsHc, (obj), TYPE_UFS)
273
-
275
#define TYPE_UFS_LU "ufs-lu"
276
#define UFSLU(obj) OBJECT_CHECK(UfsLu, (obj), TYPE_UFS_LU)
277
@@ -302,4 +303,7 @@ void ufs_build_query_response(UfsRequest *req);
303
void ufs_complete_req(UfsRequest *req, UfsReqResult req_result);
304
void ufs_wb_update_avail_buffer(UfsHc *u);
305
void ufs_init_wlu(UfsLu *wlu, uint8_t wlun);
306
+bool ufs_realize(UfsHc *u, DeviceState *dev, AddressSpace *dma_as,
307
+ Error **errp);
308
+void ufs_unrealize(UfsHc *u);
309
#endif /* HW_UFS_UFS_H */