diff mbox series

[RFC,18/25] hw/cxl/device: Add a memory device (8.2.8.5)

Message ID 20201111054724.794888-19-ben.widawsky@intel.com
State New
Headers show
Series Introduce CXL 2.0 Emulation | expand

Commit Message

Ben Widawsky Nov. 11, 2020, 5:47 a.m. UTC
A CXL memory device (AKA Type 3) is a CXL component that contains some
combination of volatile and persistent memory. It also implements the
previously defined mailbox interface as well as the memory device
firmware interface.

The following example will create a 256M device in a 512M window:

-object "memory-backend-file,id=cxl-mem1,share,mem-path=cxl-type3,size=512M"
-device "cxl-type3,bus=rp0,memdev=cxl-mem1,id=cxl-pmem0,size=256M"

Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
---
 hw/core/numa.c           |   3 +
 hw/i386/pc.c             |   1 +
 hw/mem/Kconfig           |   5 +
 hw/mem/cxl_type3.c       | 262 +++++++++++++++++++++++++++++++++++++++
 hw/mem/meson.build       |   1 +
 hw/pci/pcie.c            |  30 +++++
 include/hw/cxl/cxl.h     |   2 +
 include/hw/cxl/cxl_pci.h |  22 ++++
 include/hw/pci/pci_ids.h |   1 +
 monitor/hmp-cmds.c       |  15 +++
 qapi/machine.json        |   1 +
 11 files changed, 343 insertions(+)
 create mode 100644 hw/mem/cxl_type3.c

Comments

Eric Blake Nov. 12, 2020, 6:37 p.m. UTC | #1
On 11/10/20 11:47 PM, Ben Widawsky wrote:
> A CXL memory device (AKA Type 3) is a CXL component that contains some
> combination of volatile and persistent memory. It also implements the
> previously defined mailbox interface as well as the memory device
> firmware interface.
> 
> The following example will create a 256M device in a 512M window:
> 
> -object "memory-backend-file,id=cxl-mem1,share,mem-path=cxl-type3,size=512M"
> -device "cxl-type3,bus=rp0,memdev=cxl-mem1,id=cxl-pmem0,size=256M"
> 
> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> ---

> +++ b/qapi/machine.json
> @@ -1394,6 +1394,7 @@
>  { 'union': 'MemoryDeviceInfo',
>    'data': { 'dimm': 'PCDIMMDeviceInfo',
>              'nvdimm': 'PCDIMMDeviceInfo',
> +            'cxl': 'PCDIMMDeviceInfo',
>              'virtio-pmem': 'VirtioPMEMDeviceInfo',
>              'virtio-mem': 'VirtioMEMDeviceInfo'
>            }

Missing documentation of the new data type, and the fact that it will be
introduced in 6.0.  Also, Markus has been trying to get rid of so-called
"simple unions" in favor of "flat unions" - every time we modify an
existing simple union, it is worth asking if it is time to first flatten
this.
Markus Armbruster Nov. 13, 2020, 7:47 a.m. UTC | #2
Eric Blake <eblake@redhat.com> writes:

> On 11/10/20 11:47 PM, Ben Widawsky wrote:
>> A CXL memory device (AKA Type 3) is a CXL component that contains some
>> combination of volatile and persistent memory. It also implements the
>> previously defined mailbox interface as well as the memory device
>> firmware interface.
>> 
>> The following example will create a 256M device in a 512M window:
>> 
>> -object "memory-backend-file,id=cxl-mem1,share,mem-path=cxl-type3,size=512M"
>> -device "cxl-type3,bus=rp0,memdev=cxl-mem1,id=cxl-pmem0,size=256M"
>> 
>> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
>> ---
>
>> +++ b/qapi/machine.json
>> @@ -1394,6 +1394,7 @@
>>  { 'union': 'MemoryDeviceInfo',
>>    'data': { 'dimm': 'PCDIMMDeviceInfo',
>>              'nvdimm': 'PCDIMMDeviceInfo',
>> +            'cxl': 'PCDIMMDeviceInfo',
>>              'virtio-pmem': 'VirtioPMEMDeviceInfo',
>>              'virtio-mem': 'VirtioMEMDeviceInfo'
>>            }
>
> Missing documentation of the new data type, and the fact that it will be
> introduced in 6.0.

Old wish list item: improve the QAPI schema frontend to flag this.

>                     Also, Markus has been trying to get rid of so-called
> "simple unions" in favor of "flat unions" - every time we modify an
> existing simple union, it is worth asking if it is time to first flatten
> this.

0. Simple unions can be transformed into flat unions.  The
transformation can either preserve the nested wire format, or flatten
it.  See docs/devel/qapi-code-gen.txt "A simple union can always be
re-written as a flat union ..."

1. No new simple unions.

2. Existing simple unions that can be flattened without breaking
backward compatibility have long been flattened.

3. The remaining simple unions are part of QMP, where we need to
preserve the wire format.  We could turn them into flat union preserving
the wire format.  Only worthwhile if we kill simple unions and simplify
scripts/qapi/.  Opportunity to make the flat union syntax less
cumbersome.  Not done due to lack of time.

4. Kevin and I have been experimenting with ways to provide both flat
and nested wire format.  This would pave the way for orderly deprecation
of the nested wire format.  May not be practical for QMP output.
Ben Widawsky Nov. 25, 2020, 4:53 p.m. UTC | #3
On 20-11-13 08:47:59, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
> > On 11/10/20 11:47 PM, Ben Widawsky wrote:
> >> A CXL memory device (AKA Type 3) is a CXL component that contains some
> >> combination of volatile and persistent memory. It also implements the
> >> previously defined mailbox interface as well as the memory device
> >> firmware interface.
> >> 
> >> The following example will create a 256M device in a 512M window:
> >> 
> >> -object "memory-backend-file,id=cxl-mem1,share,mem-path=cxl-type3,size=512M"
> >> -device "cxl-type3,bus=rp0,memdev=cxl-mem1,id=cxl-pmem0,size=256M"
> >> 
> >> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> >> ---
> >
> >> +++ b/qapi/machine.json
> >> @@ -1394,6 +1394,7 @@
> >>  { 'union': 'MemoryDeviceInfo',
> >>    'data': { 'dimm': 'PCDIMMDeviceInfo',
> >>              'nvdimm': 'PCDIMMDeviceInfo',
> >> +            'cxl': 'PCDIMMDeviceInfo',
> >>              'virtio-pmem': 'VirtioPMEMDeviceInfo',
> >>              'virtio-mem': 'VirtioMEMDeviceInfo'
> >>            }
> >
> > Missing documentation of the new data type, and the fact that it will be
> > introduced in 6.0.
> 
> Old wish list item: improve the QAPI schema frontend to flag this.
> 

"Introduced in 6.0" - quite the optimist. Kidding aside, this is the area where
I could use some feedback. CXL Type 3 memory devices can contain both volatile
and persistent memory at the same time. As such, I think I'll need a new type to
represent that, but I'd love to know how best to accomplish that.

> >                     Also, Markus has been trying to get rid of so-called
> > "simple unions" in favor of "flat unions" - every time we modify an
> > existing simple union, it is worth asking if it is time to first flatten
> > this.
> 
> 0. Simple unions can be transformed into flat unions.  The
> transformation can either preserve the nested wire format, or flatten
> it.  See docs/devel/qapi-code-gen.txt "A simple union can always be
> re-written as a flat union ..."
> 
> 1. No new simple unions.
> 
> 2. Existing simple unions that can be flattened without breaking
> backward compatibility have long been flattened.
> 
> 3. The remaining simple unions are part of QMP, where we need to
> preserve the wire format.  We could turn them into flat union preserving
> the wire format.  Only worthwhile if we kill simple unions and simplify
> scripts/qapi/.  Opportunity to make the flat union syntax less
> cumbersome.  Not done due to lack of time.
> 
> 4. Kevin and I have been experimenting with ways to provide both flat
> and nested wire format.  This would pave the way for orderly deprecation
> of the nested wire format.  May not be practical for QMP output.
> 

So is there anything for me to do here?
Markus Armbruster Nov. 26, 2020, 6:36 a.m. UTC | #4
Ben Widawsky <ben.widawsky@intel.com> writes:

> On 20-11-13 08:47:59, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>> > On 11/10/20 11:47 PM, Ben Widawsky wrote:
>> >> A CXL memory device (AKA Type 3) is a CXL component that contains some
>> >> combination of volatile and persistent memory. It also implements the
>> >> previously defined mailbox interface as well as the memory device
>> >> firmware interface.
>> >> 
>> >> The following example will create a 256M device in a 512M window:
>> >> 
>> >> -object "memory-backend-file,id=cxl-mem1,share,mem-path=cxl-type3,size=512M"
>> >> -device "cxl-type3,bus=rp0,memdev=cxl-mem1,id=cxl-pmem0,size=256M"
>> >> 
>> >> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
>> >> ---
>> >
>> >> +++ b/qapi/machine.json
>> >> @@ -1394,6 +1394,7 @@
>> >>  { 'union': 'MemoryDeviceInfo',
>> >>    'data': { 'dimm': 'PCDIMMDeviceInfo',
>> >>              'nvdimm': 'PCDIMMDeviceInfo',
>> >> +            'cxl': 'PCDIMMDeviceInfo',
>> >>              'virtio-pmem': 'VirtioPMEMDeviceInfo',
>> >>              'virtio-mem': 'VirtioMEMDeviceInfo'
>> >>            }
>> >
>> > Missing documentation of the new data type, and the fact that it will be
>> > introduced in 6.0.
>> 
>> Old wish list item: improve the QAPI schema frontend to flag this.
>> 
>
> "Introduced in 6.0" - quite the optimist. Kidding aside, this is the area where
> I could use some feedback. CXL Type 3 memory devices can contain both volatile
> and persistent memory at the same time. As such, I think I'll need a new type to
> represent that, but I'd love to know how best to accomplish that.

We can help.  Tell us what information you want to provide in variant
'cxl'.  If it's a superset of an existing variant, give us just the
delta.

>> >                     Also, Markus has been trying to get rid of so-called
>> > "simple unions" in favor of "flat unions" - every time we modify an
>> > existing simple union, it is worth asking if it is time to first flatten
>> > this.
>> 
>> 0. Simple unions can be transformed into flat unions.  The
>> transformation can either preserve the nested wire format, or flatten
>> it.  See docs/devel/qapi-code-gen.txt "A simple union can always be
>> re-written as a flat union ..."
>> 
>> 1. No new simple unions.
>> 
>> 2. Existing simple unions that can be flattened without breaking
>> backward compatibility have long been flattened.
>> 
>> 3. The remaining simple unions are part of QMP, where we need to
>> preserve the wire format.  We could turn them into flat union preserving
>> the wire format.  Only worthwhile if we kill simple unions and simplify
>> scripts/qapi/.  Opportunity to make the flat union syntax less
>> cumbersome.  Not done due to lack of time.
>> 
>> 4. Kevin and I have been experimenting with ways to provide both flat
>> and nested wire format.  This would pave the way for orderly deprecation
>> of the nested wire format.  May not be practical for QMP output.
>> 
>
> So is there anything for me to do here?

No.  Extending an existing simple union is okay.

We should not add news ones.  We should think twice before we add new
uses of existing ones.
Ben Widawsky Nov. 30, 2020, 5:07 p.m. UTC | #5
On 20-11-26 07:36:23, Markus Armbruster wrote:
> Ben Widawsky <ben.widawsky@intel.com> writes:
> 
> > On 20-11-13 08:47:59, Markus Armbruster wrote:
> >> Eric Blake <eblake@redhat.com> writes:
> >> 
> >> > On 11/10/20 11:47 PM, Ben Widawsky wrote:
> >> >> A CXL memory device (AKA Type 3) is a CXL component that contains some
> >> >> combination of volatile and persistent memory. It also implements the
> >> >> previously defined mailbox interface as well as the memory device
> >> >> firmware interface.
> >> >> 
> >> >> The following example will create a 256M device in a 512M window:
> >> >> 
> >> >> -object "memory-backend-file,id=cxl-mem1,share,mem-path=cxl-type3,size=512M"
> >> >> -device "cxl-type3,bus=rp0,memdev=cxl-mem1,id=cxl-pmem0,size=256M"
> >> >> 
> >> >> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
> >> >> ---
> >> >
> >> >> +++ b/qapi/machine.json
> >> >> @@ -1394,6 +1394,7 @@
> >> >>  { 'union': 'MemoryDeviceInfo',
> >> >>    'data': { 'dimm': 'PCDIMMDeviceInfo',
> >> >>              'nvdimm': 'PCDIMMDeviceInfo',
> >> >> +            'cxl': 'PCDIMMDeviceInfo',
> >> >>              'virtio-pmem': 'VirtioPMEMDeviceInfo',
> >> >>              'virtio-mem': 'VirtioMEMDeviceInfo'
> >> >>            }
> >> >
> >> > Missing documentation of the new data type, and the fact that it will be
> >> > introduced in 6.0.
> >> 
> >> Old wish list item: improve the QAPI schema frontend to flag this.
> >> 
> >
> > "Introduced in 6.0" - quite the optimist. Kidding aside, this is the area where
> > I could use some feedback. CXL Type 3 memory devices can contain both volatile
> > and persistent memory at the same time. As such, I think I'll need a new type to
> > represent that, but I'd love to know how best to accomplish that.
> 
> We can help.  Tell us what information you want to provide in variant
> 'cxl'.  If it's a superset of an existing variant, give us just the
> delta.
> 

I'm not exactly sure what the best way to do this is in QEMU, so I'm not really
sure what to specify as the delta. A CXL memory device can have both volatile
and persistent memory. Currently when a CXL memory device implements the
TYPE_MEMORY_DEVICE interface. So I believe the shortest path is a
MemoryDeviceInfo that can have two memory devices associated with it, but I
don't know if that's the best path.


> >> >                     Also, Markus has been trying to get rid of so-called
> >> > "simple unions" in favor of "flat unions" - every time we modify an
> >> > existing simple union, it is worth asking if it is time to first flatten
> >> > this.
> >> 
> >> 0. Simple unions can be transformed into flat unions.  The
> >> transformation can either preserve the nested wire format, or flatten
> >> it.  See docs/devel/qapi-code-gen.txt "A simple union can always be
> >> re-written as a flat union ..."
> >> 
> >> 1. No new simple unions.
> >> 
> >> 2. Existing simple unions that can be flattened without breaking
> >> backward compatibility have long been flattened.
> >> 
> >> 3. The remaining simple unions are part of QMP, where we need to
> >> preserve the wire format.  We could turn them into flat union preserving
> >> the wire format.  Only worthwhile if we kill simple unions and simplify
> >> scripts/qapi/.  Opportunity to make the flat union syntax less
> >> cumbersome.  Not done due to lack of time.
> >> 
> >> 4. Kevin and I have been experimenting with ways to provide both flat
> >> and nested wire format.  This would pave the way for orderly deprecation
> >> of the nested wire format.  May not be practical for QMP output.
> >> 
> >
> > So is there anything for me to do here?
> 
> No.  Extending an existing simple union is okay.
> 
> We should not add news ones.  We should think twice before we add new
> uses of existing ones.
> 
>
Markus Armbruster Dec. 1, 2020, 5:06 p.m. UTC | #6
Ben Widawsky <ben@bwidawsk.net> writes:

> On 20-11-26 07:36:23, Markus Armbruster wrote:
>> Ben Widawsky <ben.widawsky@intel.com> writes:
>> 
>> > On 20-11-13 08:47:59, Markus Armbruster wrote:
>> >> Eric Blake <eblake@redhat.com> writes:
>> >> 
>> >> > On 11/10/20 11:47 PM, Ben Widawsky wrote:
>> >> >> A CXL memory device (AKA Type 3) is a CXL component that contains some
>> >> >> combination of volatile and persistent memory. It also implements the
>> >> >> previously defined mailbox interface as well as the memory device
>> >> >> firmware interface.
>> >> >> 
>> >> >> The following example will create a 256M device in a 512M window:
>> >> >> 
>> >> >> -object "memory-backend-file,id=cxl-mem1,share,mem-path=cxl-type3,size=512M"
>> >> >> -device "cxl-type3,bus=rp0,memdev=cxl-mem1,id=cxl-pmem0,size=256M"
>> >> >> 
>> >> >> Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
>> >> >> ---
>> >> >
>> >> >> +++ b/qapi/machine.json
>> >> >> @@ -1394,6 +1394,7 @@
>> >> >>  { 'union': 'MemoryDeviceInfo',
>> >> >>    'data': { 'dimm': 'PCDIMMDeviceInfo',
>> >> >>              'nvdimm': 'PCDIMMDeviceInfo',
>> >> >> +            'cxl': 'PCDIMMDeviceInfo',
>> >> >>              'virtio-pmem': 'VirtioPMEMDeviceInfo',
>> >> >>              'virtio-mem': 'VirtioMEMDeviceInfo'
>> >> >>            }
>> >> >
>> >> > Missing documentation of the new data type, and the fact that it will be
>> >> > introduced in 6.0.
>> >> 
>> >> Old wish list item: improve the QAPI schema frontend to flag this.
>> >> 
>> >
>> > "Introduced in 6.0" - quite the optimist. Kidding aside, this is the area where
>> > I could use some feedback. CXL Type 3 memory devices can contain both volatile
>> > and persistent memory at the same time. As such, I think I'll need a new type to
>> > represent that, but I'd love to know how best to accomplish that.
>> 
>> We can help.  Tell us what information you want to provide in variant
>> 'cxl'.  If it's a superset of an existing variant, give us just the
>> delta.
>> 
>
> I'm not exactly sure what the best way to do this is in QEMU, so I'm not really
> sure what to specify as the delta. A CXL memory device can have both volatile
> and persistent memory. Currently when a CXL memory device implements the
> TYPE_MEMORY_DEVICE interface. So I believe the shortest path is a
> MemoryDeviceInfo that can have two memory devices associated with it, but I
> don't know if that's the best path.

Perhaps a CXL device should contain two sub-devices implementing
TYPE_MEMORY_DEVICE.  Paolo, what do you think?

If yes, one of the existing variants fits the bill, I guess.

If no, I have more ramblings to offer.

query-memory-devices returns a MemoryDeviceInfo for each realized device
that implements interface TYPE_MEMORY_DEVICE.  The interface provides
callback ->fill_device_info() to fill in the MemoryDeviceInfo.  This is
its only use.

This means TYPE_MEMORY_DEVICE places no obvious constraints on how the
callbacks use MemoryDeviceInfo.  Each callback can pick whatever variant
it wants.  This also means *your* callback can pick a new one, which you
define however you want.

What if there are unobvious (and unwritten) constraints?

The existing variants overlap:

* All provide the device's ID: optional member @id

* All provide a physical address (base address, I supposed) and size,
  but the member names differ (argh!): @addr, @size vs. @memaddr, @size

* All provide the memory backend: member @memdev

The members that overlap by necessity (i.e. any conceivable
TYPE_MEMORY_DEVICE will have them) should be common members, not variant
members.  Requires converting the simple union to the equivalent flat
union.

Do these members overlap by necessity?  Paolo, Igor?

[...]
diff mbox series

Patch

diff --git a/hw/core/numa.c b/hw/core/numa.c
index 7c4dd4e68e..3ddeb23036 100644
--- a/hw/core/numa.c
+++ b/hw/core/numa.c
@@ -770,6 +770,9 @@  static void numa_stat_memory_devices(NumaNodeMem node_mem[])
                 node_mem[pcdimm_info->node].node_plugged_mem +=
                     pcdimm_info->size;
                 break;
+            case MEMORY_DEVICE_INFO_KIND_CXL:
+                /* FINISHME */
+                break;
             case MEMORY_DEVICE_INFO_KIND_VIRTIO_PMEM:
                 vpi = value->u.virtio_pmem.data;
                 /* TODO: once we support numa, assign to right node */
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 5e6c0023e0..ecfc497f71 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -79,6 +79,7 @@ 
 #include "acpi-build.h"
 #include "hw/mem/pc-dimm.h"
 #include "hw/mem/nvdimm.h"
+#include "hw/cxl/cxl.h"
 #include "qapi/error.h"
 #include "qapi/qapi-visit-common.h"
 #include "qapi/visitor.h"
diff --git a/hw/mem/Kconfig b/hw/mem/Kconfig
index a0ef2cf648..7d9d1ced3e 100644
--- a/hw/mem/Kconfig
+++ b/hw/mem/Kconfig
@@ -10,3 +10,8 @@  config NVDIMM
     default y
     depends on (PC || PSERIES || ARM_VIRT)
     select MEM_DEVICE
+
+config CXL_MEM_DEVICE
+    bool
+    default y if CXL
+    select MEM_DEVICE
diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c
new file mode 100644
index 0000000000..48c25922f3
--- /dev/null
+++ b/hw/mem/cxl_type3.c
@@ -0,0 +1,262 @@ 
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "qemu/error-report.h"
+#include "hw/mem/memory-device.h"
+#include "hw/mem/pc-dimm.h"
+#include "hw/pci/pci.h"
+#include "hw/qdev-properties.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/range.h"
+#include "qemu/rcu.h"
+#include "sysemu/hostmem.h"
+#include "hw/cxl/cxl.h"
+
+typedef struct cxl_type3_dev {
+    /* Private */
+    PCIDevice parent_obj;
+
+    /* Properties */
+    uint64_t size;
+    HostMemoryBackend *hostmem;
+
+    /* State */
+    CXLComponentState cxl_cstate;
+    CXLDeviceState cxl_dstate;
+} CXLType3Dev;
+
+#define CT3(obj) OBJECT_CHECK(CXLType3Dev, (obj), TYPE_CXL_TYPE3_DEV)
+
+static void build_dvsecs(CXLType3Dev *ct3d)
+{
+    CXLComponentState *cxl_cstate = &ct3d->cxl_cstate;
+    uint8_t *dvsec;
+
+    dvsec = (uint8_t *)&(struct dvsec_device){
+        .cap = 0x1e,
+        .ctrl = 0x6,
+        .status2 = 0x2,
+        .range1_size_hi = 0,
+        .range1_size_lo = (2 << 5) | (2 << 2) | 0x3 | ct3d->size,
+        .range1_base_hi = 0,
+        .range1_base_lo = 0,
+    };
+    cxl_component_create_dvsec(cxl_cstate, PCIE_CXL_DEVICE_DVSEC_LENGTH,
+                               PCIE_CXL_DEVICE_DVSEC,
+                               PCIE_CXL_DEVICE_DVSEC_REVID, dvsec);
+
+    dvsec = (uint8_t *)&(struct dvsec_register_locator){
+        .rsvd         = 0,
+        .reg0_base_lo = RBI_COMPONENT_REG | COMPONENT_REG_BAR_IDX,
+        .reg0_base_hi = 0,
+        .reg1_base_lo = RBI_CXL_DEVICE_REG | DEVICE_REG_BAR_IDX,
+        .reg1_base_hi = 0,
+    };
+    cxl_component_create_dvsec(cxl_cstate, REG_LOC_DVSEC_LENGTH, REG_LOC_DVSEC,
+                               REG_LOC_DVSEC_REVID, dvsec);
+}
+
+static void ct3_instance_init(Object *obj)
+{
+    /* MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(obj); */
+}
+
+static void ct3_finalize(Object *obj)
+{
+    CXLType3Dev *ct3d = CT3(obj);
+
+    g_free(ct3d->cxl_dstate.pmem);
+}
+
+static void cxl_setup_memory(CXLType3Dev *ct3d, Error **errp)
+{
+    MemoryRegionSection mrs;
+    MemoryRegion *mr;
+    uint64_t offset = 0;
+    size_t remaining_size;
+
+    if (!ct3d->hostmem) {
+        error_setg(errp, "memdev property must be set");
+        return;
+    }
+
+    /* FIXME: need to check mr is the host bridge's MR */
+    mr = host_memory_backend_get_memory(ct3d->hostmem);
+
+    /* Create our new subregion */
+    ct3d->cxl_dstate.pmem = g_new(MemoryRegion, 1);
+
+    /* Find the first free space in the window */
+    WITH_RCU_READ_LOCK_GUARD()
+    {
+        mrs = memory_region_find(mr, offset, 1);
+        while (mrs.mr && mrs.mr != mr) {
+            offset += memory_region_size(mrs.mr);
+            mrs = memory_region_find(mr, offset, 1);
+        }
+    }
+
+    remaining_size = memory_region_size(mr) - offset;
+    if (remaining_size < ct3d->size) {
+        g_free(ct3d->cxl_dstate.pmem);
+        error_setg(errp,
+                   "Not enough free space (%zd) required for device (%" PRId64  ")",
+                   remaining_size, ct3d->size);
+    }
+
+    /* Register our subregion as non-volatile */
+    memory_region_init_ram(ct3d->cxl_dstate.pmem, OBJECT(ct3d),
+                           "cxl_type3-memory", ct3d->size, errp);
+    memory_region_set_nonvolatile(ct3d->cxl_dstate.pmem, true);
+
+#ifdef SET_PMEM_PADDR
+    memory_region_add_subregion(mr, offset, ct3d->cxl_dstate.pmem);
+#endif
+}
+
+static MemoryRegion *cxl_md_get_memory_region(MemoryDeviceState *md,
+                                              Error **errp)
+{
+    CXLType3Dev *ct3d = CT3(md);
+
+    if (!ct3d->cxl_dstate.pmem) {
+        cxl_setup_memory(ct3d, errp);
+    }
+
+    return ct3d->cxl_dstate.pmem;
+}
+
+static void ct3_realize(PCIDevice *pci_dev, Error **errp)
+{
+    CXLType3Dev *ct3d = CT3(pci_dev);
+    CXLComponentState *cxl_cstate = &ct3d->cxl_cstate;
+    ComponentRegisters *regs = &cxl_cstate->crb;
+    MemoryRegion *mr = &regs->component_registers;
+    uint8_t *pci_conf = pci_dev->config;
+
+    if (!ct3d->cxl_dstate.pmem) {
+        cxl_setup_memory(ct3d, errp);
+    }
+
+    pci_config_set_prog_interface(pci_conf, 0x10);
+    pci_config_set_class(pci_conf, PCI_CLASS_MEMORY_CXL);
+
+    pcie_endpoint_cap_init(pci_dev, 0x80);
+    cxl_cstate->dvsec_offset = 0x100;
+
+    ct3d->cxl_cstate.pdev = pci_dev;
+    build_dvsecs(ct3d);
+
+    cxl_component_register_block_init(OBJECT(pci_dev), cxl_cstate,
+                                      TYPE_CXL_TYPE3_DEV);
+
+    pci_register_bar(
+        pci_dev, COMPONENT_REG_BAR_IDX,
+        PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64, mr);
+
+    cxl_device_register_block_init(OBJECT(pci_dev), &ct3d->cxl_dstate);
+    pci_register_bar(pci_dev, DEVICE_REG_BAR_IDX,
+                     PCI_BASE_ADDRESS_SPACE_MEMORY |
+                         PCI_BASE_ADDRESS_MEM_TYPE_64,
+                     &ct3d->cxl_dstate.device_registers);
+}
+
+static uint64_t cxl_md_get_addr(const MemoryDeviceState *md)
+{
+    CXLType3Dev *ct3d = CT3(md);
+
+    return memory_region_get_ram_addr(ct3d->cxl_dstate.pmem);
+}
+
+static void cxl_md_set_addr(MemoryDeviceState *md, uint64_t addr, Error **errp)
+{
+    object_property_set_uint(OBJECT(md), "paddr", addr, errp);
+}
+
+static void ct3d_reset(DeviceState *dev)
+{
+    CXLType3Dev *ct3d = CT3(dev);
+    uint32_t *reg_state = ct3d->cxl_cstate.crb.cache_mem_registers;
+
+    cxl_component_register_init_common(reg_state, CXL2_TYPE3_DEVICE);
+    cxl_device_register_init_common(&ct3d->cxl_dstate);
+}
+
+static Property ct3_props[] = {
+    DEFINE_PROP_SIZE("size", CXLType3Dev, size, -1),
+    DEFINE_PROP_LINK("memdev", CXLType3Dev, hostmem, TYPE_MEMORY_BACKEND,
+                     HostMemoryBackend *),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pc_dimm_md_fill_device_info(const MemoryDeviceState *md,
+                                        MemoryDeviceInfo *info)
+{
+    PCDIMMDeviceInfo *di = g_new0(PCDIMMDeviceInfo, 1);
+    const DeviceClass *dc = DEVICE_GET_CLASS(md);
+    const DeviceState *dev = DEVICE(md);
+    CXLType3Dev *ct3d = CT3(md);
+
+    if (dev->id) {
+        di->has_id = true;
+        di->id = g_strdup(dev->id);
+    }
+    di->hotplugged = dev->hotplugged;
+    di->hotpluggable = dc->hotpluggable;
+    di->addr = cxl_md_get_addr(md);
+    di->slot = 0;
+    di->node = 0;
+    di->size = memory_device_get_region_size(md, NULL);
+    di->memdev = object_get_canonical_path(OBJECT(ct3d->hostmem));
+
+
+    info->u.cxl.data = di;
+    info->type = MEMORY_DEVICE_INFO_KIND_CXL;
+}
+
+static void ct3_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
+    MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(oc);
+
+    pc->realize = ct3_realize;
+    pc->class_id = PCI_CLASS_STORAGE_EXPRESS;
+    pc->vendor_id = PCI_VENDOR_ID_INTEL;
+    pc->device_id = 0xd93; /* LVF for now */
+    pc->revision = 1;
+
+    set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
+    dc->desc = "CXL PMEM Device (Type 3)";
+    dc->reset = ct3d_reset;
+    device_class_set_props(dc, ct3_props);
+
+    mdc->get_memory_region = cxl_md_get_memory_region;
+    mdc->get_addr = cxl_md_get_addr;
+    mdc->fill_device_info = pc_dimm_md_fill_device_info;
+    mdc->get_plugged_size = memory_device_get_region_size;
+    mdc->set_addr = cxl_md_set_addr;
+}
+
+static const TypeInfo ct3d_info = {
+    .name = TYPE_CXL_TYPE3_DEV,
+    .parent = TYPE_PCI_DEVICE,
+    .class_init = ct3_class_init,
+    .instance_size = sizeof(CXLType3Dev),
+    .instance_init = ct3_instance_init,
+    .instance_finalize = ct3_finalize,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_MEMORY_DEVICE },
+        { INTERFACE_CXL_DEVICE },
+        { INTERFACE_PCIE_DEVICE },
+        {}
+    },
+};
+
+static void ct3d_registers(void)
+{
+    type_register_static(&ct3d_info);
+}
+
+type_init(ct3d_registers);
diff --git a/hw/mem/meson.build b/hw/mem/meson.build
index 0d22f2b572..d13c3ed117 100644
--- a/hw/mem/meson.build
+++ b/hw/mem/meson.build
@@ -3,5 +3,6 @@  mem_ss.add(files('memory-device.c'))
 mem_ss.add(when: 'CONFIG_DIMM', if_true: files('pc-dimm.c'))
 mem_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_mc.c'))
 mem_ss.add(when: 'CONFIG_NVDIMM', if_true: files('nvdimm.c'))
+mem_ss.add(when: 'CONFIG_CXL_MEM_DEVICE', if_true: files('cxl_type3.c'))
 
 softmmu_ss.add_all(when: 'CONFIG_MEM_DEVICE', if_true: mem_ss)
diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c
index d4010cf8f3..1ecf6f6a55 100644
--- a/hw/pci/pcie.c
+++ b/hw/pci/pcie.c
@@ -20,6 +20,7 @@ 
 
 #include "qemu/osdep.h"
 #include "qapi/error.h"
+#include "hw/mem/memory-device.h"
 #include "hw/pci/pci_bridge.h"
 #include "hw/pci/pcie.h"
 #include "hw/pci/msix.h"
@@ -27,6 +28,8 @@ 
 #include "hw/pci/pci_bus.h"
 #include "hw/pci/pcie_regs.h"
 #include "hw/pci/pcie_port.h"
+#include "hw/cxl/cxl.h"
+#include "hw/boards.h"
 #include "qemu/range.h"
 
 //#define DEBUG_PCIE
@@ -419,6 +422,28 @@  void pcie_cap_slot_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
     }
 
     pcie_cap_slot_plug_common(PCI_DEVICE(hotplug_dev), dev, errp);
+
+#ifdef CXL_MEM_DEVICE
+    /*
+     * FIXME:
+     * if (object_dynamic_cast(OBJECT(dev), TYPE_CXL_TYPE3_DEV)) {
+     *    HotplugHandler *hotplug_ctrl;
+     *   Error *local_err = NULL;
+     *  hotplug_ctrl = qdev_get_hotplug_handler(dev);
+     *  if (hotplug_ctrl) {
+     *      hotplug_handler_pre_plug(hotplug_ctrl, dev, &local_err);
+     *      if (local_err) {
+     *          error_propagate(errp, local_err);
+     *          return;
+     *      }
+     *  }
+     */
+
+    if (object_dynamic_cast(OBJECT(dev), TYPE_CXL_TYPE3_DEV)) {
+        memory_device_pre_plug(MEMORY_DEVICE(dev), MACHINE(qdev_get_machine()),
+                               NULL, errp);
+    }
+#endif
 }
 
 void pcie_cap_slot_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
@@ -455,6 +480,11 @@  void pcie_cap_slot_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
         pcie_cap_slot_event(hotplug_pdev,
                             PCI_EXP_HP_EV_PDC | PCI_EXP_HP_EV_ABP);
     }
+
+#ifdef CXL_MEM_DEVICE
+    if (object_dynamic_cast(OBJECT(dev), TYPE_CXL_TYPE3_DEV))
+        memory_device_plug(MEMORY_DEVICE(dev), MACHINE(qdev_get_machine()));
+#endif
 }
 
 void pcie_cap_slot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev,
diff --git a/include/hw/cxl/cxl.h b/include/hw/cxl/cxl.h
index b1e5f4a8fa..809ed7de60 100644
--- a/include/hw/cxl/cxl.h
+++ b/include/hw/cxl/cxl.h
@@ -17,6 +17,8 @@ 
 #define COMPONENT_REG_BAR_IDX 0
 #define DEVICE_REG_BAR_IDX 2
 
+#define TYPE_CXL_TYPE3_DEV "cxl-type3"
+
 #define CXL_HOST_BASE 0xD0000000
 #define CXL_WINDOW_MAX 10
 
diff --git a/include/hw/cxl/cxl_pci.h b/include/hw/cxl/cxl_pci.h
index b403770424..7a9ce71612 100644
--- a/include/hw/cxl/cxl_pci.h
+++ b/include/hw/cxl/cxl_pci.h
@@ -63,6 +63,28 @@  _Static_assert(sizeof(struct dvsec_header) == 10,
  * CXL 2.0 Downstream Port: 3, 4, 7, 8
  */
 
+/* CXL 2.0 - 8.1.3 (ID 0001) */
+struct dvsec_device {
+    struct dvsec_header hdr;
+    uint16_t cap;
+    uint16_t ctrl;
+    uint16_t status;
+    uint16_t ctrl2;
+    uint16_t status2;
+    uint16_t lock;
+    uint16_t cap2;
+    uint32_t range1_size_hi;
+    uint32_t range1_size_lo;
+    uint32_t range1_base_hi;
+    uint32_t range1_base_lo;
+    uint32_t range2_size_hi;
+    uint32_t range2_size_lo;
+    uint32_t range2_base_hi;
+    uint32_t range2_base_lo;
+};
+_Static_assert(sizeof(struct dvsec_device) == 0x38,
+               "dvsec device size incorrect");
+
 /* CXL 2.0 - 8.1.5 (ID 0003) */
 struct dvsec_port {
     struct dvsec_header hdr;
diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h
index 11f8ab7149..76bf3ed590 100644
--- a/include/hw/pci/pci_ids.h
+++ b/include/hw/pci/pci_ids.h
@@ -53,6 +53,7 @@ 
 #define PCI_BASE_CLASS_MEMORY            0x05
 #define PCI_CLASS_MEMORY_RAM             0x0500
 #define PCI_CLASS_MEMORY_FLASH           0x0501
+#define PCI_CLASS_MEMORY_CXL             0x0502
 #define PCI_CLASS_MEMORY_OTHER           0x0580
 
 #define PCI_BASE_CLASS_BRIDGE            0x06
diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
index 56e9bad33d..8de1959c68 100644
--- a/monitor/hmp-cmds.c
+++ b/monitor/hmp-cmds.c
@@ -1880,6 +1880,21 @@  void hmp_info_memory_devices(Monitor *mon, const QDict *qdict)
                 monitor_printf(mon, "  hotpluggable: %s\n",
                                di->hotpluggable ? "true" : "false");
                 break;
+            case MEMORY_DEVICE_INFO_KIND_CXL:
+                di = value->u.cxl.data;
+                monitor_printf(mon, "Memory device [%s]: \"%s\"\n",
+                               MemoryDeviceInfoKind_str(value->type),
+                               di->id ? di->id : "");
+                monitor_printf(mon, "  addr: 0x%" PRIx64 "\n", di->addr);
+                monitor_printf(mon, "  slot: %" PRId64 "\n", di->slot);
+                monitor_printf(mon, "  node: %" PRId64 "\n", di->node);
+                monitor_printf(mon, "  size: %" PRIu64 "\n", di->size);
+                monitor_printf(mon, "  memdev: %s\n", di->memdev);
+                monitor_printf(mon, "  hotplugged: %s\n",
+                               di->hotplugged ? "true" : "false");
+                monitor_printf(mon, "  hotpluggable: %s\n",
+                               di->hotpluggable ? "true" : "false");
+                break;
             case MEMORY_DEVICE_INFO_KIND_VIRTIO_PMEM:
                 vpi = value->u.virtio_pmem.data;
                 monitor_printf(mon, "Memory device [%s]: \"%s\"\n",
diff --git a/qapi/machine.json b/qapi/machine.json
index 7c9a263778..c954950fdc 100644
--- a/qapi/machine.json
+++ b/qapi/machine.json
@@ -1394,6 +1394,7 @@ 
 { 'union': 'MemoryDeviceInfo',
   'data': { 'dimm': 'PCDIMMDeviceInfo',
             'nvdimm': 'PCDIMMDeviceInfo',
+            'cxl': 'PCDIMMDeviceInfo',
             'virtio-pmem': 'VirtioPMEMDeviceInfo',
             'virtio-mem': 'VirtioMEMDeviceInfo'
           }