@samitouri / QOSamiQemu / commits / 42feb94b79

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 */