All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v4 0/8] ARM virt: ACPI memory hotplug support
@ 2019-04-09 10:29 ` Shameer Kolothum
  0 siblings, 0 replies; 62+ messages in thread
From: Shameer Kolothum @ 2019-04-09 10:29 UTC (permalink / raw)
  To: qemu-devel, qemu-arm, eric.auger, imammedo
  Cc: peter.maydell, shannon.zhaosl, sameo, sebastien.boeuf, xuwei5,
	lersek, ard.biesheuvel, linuxarm

This series is an attempt to provide device memory hotplug support 
on ARM virt platform. This is based on Eric's recent works here[1]
and carries some of the pc-dimm related patches dropped from his
series.

The kernel support for arm64 memory hot add was added recently by
Robin and hence the guest kernel should be => 5.0-rc1.

NVDIM support is not included currently as we still have an unresolved
issue while hot adding NVDIMM[2]. However NVDIMM cold plug patches
can be included, but not done for now, for keeping it simple.

This makes use of GED device to sent hotplug ACPI events to the
Guest. GED code is based on Nemu. Thanks to the efforts of Samuel and
Sebastien to add the hardware-reduced support to Nemu using GED
device[3]. (Please shout if I got the author/signed-off wrong for
those patches or missed any names).

This is sanity tested on a HiSilicon ARM64 platform and appreciate
any further testing.

Thanks,
Shameer

[1] https://patchwork.kernel.org/cover/10837565/
[2] https://patchwork.kernel.org/cover/10783589/
[3] https://github.com/intel/nemu/blob/topic/virt-x86/hw/acpi/ged.c

v3 --> v4
Addressed comments from Igor and Eric,
-Renamed "virt-acpi" to "acpi-ged".
-Changed ged device parent to TYPE_DEVICE.
-Introduced DT memory node property "hotpluggable" to resolve device
 memory being treated as early boot memory issue(patch #7).
-Combined patches #3 and #9 from v3 into #3.

v2 --> v3

Addressed comments from Igor and Eric,
-Made virt acpi device platform independent and moved
 to hw/acpi/generic_event_device.c
-Moved ged specific code into hw/acpi/generic_event_device.c
-Introduced an opt-in feature "fdt" to resolve device-memory being
 treated as early boot memory.
-Dropped patch #1 from v2.

RFC --> v2

-Use GED device instead of GPIO for ACPI hotplug events.
-Removed NVDIMM support for now.
-Includes dropped patches from Eric's v9 series.

Eric Auger (1):
  hw/arm/virt: Add memory hotplug framework

Samuel Ortiz (2):
  hw/acpi: Do not create memory hotplug method when handler is not
    defined
  hw/acpi: Add ACPI Generic Event Device Support

Shameer Kolothum (5):
  hw/acpi: Make ACPI IO address space configurable
  hw/arm/virt: Enable device memory cold/hot plug with ACPI boot
  hw/arm/virt-acpi-build: Add PC-DIMM in SRAT
  hw/arm/boot: Add "hotpluggable" property to DT memory node
  hw/arm/boot: Expose the PC-DIMM nodes in the DT

 default-configs/arm-softmmu.mak        |   5 +
 hw/acpi/Kconfig                        |   4 +
 hw/acpi/Makefile.objs                  |   1 +
 hw/acpi/generic_event_device.c         | 311 +++++++++++++++++++++++++++++++++
 hw/acpi/memory_hotplug.c               |  35 ++--
 hw/arm/boot.c                          |  64 ++++++-
 hw/arm/virt-acpi-build.c               |  18 ++
 hw/arm/virt.c                          | 102 ++++++++++-
 hw/i386/acpi-build.c                   |   3 +-
 include/hw/acpi/generic_event_device.h | 121 +++++++++++++
 include/hw/acpi/memory_hotplug.h       |   9 +-
 include/hw/arm/virt.h                  |   4 +
 12 files changed, 654 insertions(+), 23 deletions(-)
 create mode 100644 hw/acpi/generic_event_device.c
 create mode 100644 include/hw/acpi/generic_event_device.h

-- 
2.7.4

^ permalink raw reply	[flat|nested] 62+ messages in thread

* [Qemu-devel] [PATCH v4 0/8] ARM virt: ACPI memory hotplug support
@ 2019-04-09 10:29 ` Shameer Kolothum
  0 siblings, 0 replies; 62+ messages in thread
From: Shameer Kolothum @ 2019-04-09 10:29 UTC (permalink / raw)
  To: qemu-devel, qemu-arm, eric.auger, imammedo
  Cc: peter.maydell, sameo, ard.biesheuvel, linuxarm, xuwei5,
	shannon.zhaosl, sebastien.boeuf, lersek

This series is an attempt to provide device memory hotplug support 
on ARM virt platform. This is based on Eric's recent works here[1]
and carries some of the pc-dimm related patches dropped from his
series.

The kernel support for arm64 memory hot add was added recently by
Robin and hence the guest kernel should be => 5.0-rc1.

NVDIM support is not included currently as we still have an unresolved
issue while hot adding NVDIMM[2]. However NVDIMM cold plug patches
can be included, but not done for now, for keeping it simple.

This makes use of GED device to sent hotplug ACPI events to the
Guest. GED code is based on Nemu. Thanks to the efforts of Samuel and
Sebastien to add the hardware-reduced support to Nemu using GED
device[3]. (Please shout if I got the author/signed-off wrong for
those patches or missed any names).

This is sanity tested on a HiSilicon ARM64 platform and appreciate
any further testing.

Thanks,
Shameer

[1] https://patchwork.kernel.org/cover/10837565/
[2] https://patchwork.kernel.org/cover/10783589/
[3] https://github.com/intel/nemu/blob/topic/virt-x86/hw/acpi/ged.c

v3 --> v4
Addressed comments from Igor and Eric,
-Renamed "virt-acpi" to "acpi-ged".
-Changed ged device parent to TYPE_DEVICE.
-Introduced DT memory node property "hotpluggable" to resolve device
 memory being treated as early boot memory issue(patch #7).
-Combined patches #3 and #9 from v3 into #3.

v2 --> v3

Addressed comments from Igor and Eric,
-Made virt acpi device platform independent and moved
 to hw/acpi/generic_event_device.c
-Moved ged specific code into hw/acpi/generic_event_device.c
-Introduced an opt-in feature "fdt" to resolve device-memory being
 treated as early boot memory.
-Dropped patch #1 from v2.

RFC --> v2

-Use GED device instead of GPIO for ACPI hotplug events.
-Removed NVDIMM support for now.
-Includes dropped patches from Eric's v9 series.

Eric Auger (1):
  hw/arm/virt: Add memory hotplug framework

Samuel Ortiz (2):
  hw/acpi: Do not create memory hotplug method when handler is not
    defined
  hw/acpi: Add ACPI Generic Event Device Support

Shameer Kolothum (5):
  hw/acpi: Make ACPI IO address space configurable
  hw/arm/virt: Enable device memory cold/hot plug with ACPI boot
  hw/arm/virt-acpi-build: Add PC-DIMM in SRAT
  hw/arm/boot: Add "hotpluggable" property to DT memory node
  hw/arm/boot: Expose the PC-DIMM nodes in the DT

 default-configs/arm-softmmu.mak        |   5 +
 hw/acpi/Kconfig                        |   4 +
 hw/acpi/Makefile.objs                  |   1 +
 hw/acpi/generic_event_device.c         | 311 +++++++++++++++++++++++++++++++++
 hw/acpi/memory_hotplug.c               |  35 ++--
 hw/arm/boot.c                          |  64 ++++++-
 hw/arm/virt-acpi-build.c               |  18 ++
 hw/arm/virt.c                          | 102 ++++++++++-
 hw/i386/acpi-build.c                   |   3 +-
 include/hw/acpi/generic_event_device.h | 121 +++++++++++++
 include/hw/acpi/memory_hotplug.h       |   9 +-
 include/hw/arm/virt.h                  |   4 +
 12 files changed, 654 insertions(+), 23 deletions(-)
 create mode 100644 hw/acpi/generic_event_device.c
 create mode 100644 include/hw/acpi/generic_event_device.h

-- 
2.7.4




^ permalink raw reply	[flat|nested] 62+ messages in thread

* [Qemu-devel] [PATCH v4 1/8] hw/acpi: Make ACPI IO address space configurable
@ 2019-04-09 10:29   ` Shameer Kolothum
  0 siblings, 0 replies; 62+ messages in thread
From: Shameer Kolothum @ 2019-04-09 10:29 UTC (permalink / raw)
  To: qemu-devel, qemu-arm, eric.auger, imammedo
  Cc: peter.maydell, shannon.zhaosl, sameo, sebastien.boeuf, xuwei5,
	lersek, ard.biesheuvel, linuxarm

This is in preparation for adding support for ARM64 platforms
where it doesn't use port mapped IO for ACPI IO space.

Also move few MEMORY_* definitions to header so that other memory
hotplug event signalling mechanisms (eg. Generic Event Device on
HW-reduced acpi platforms) can use the same from their respective
event handler code.

Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
Reviewed-by: Eric Auger <eric.auger@redhat.com>
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
---
 hw/acpi/memory_hotplug.c         | 25 ++++++++++++++-----------
 hw/i386/acpi-build.c             |  3 ++-
 include/hw/acpi/memory_hotplug.h |  9 +++++++--
 3 files changed, 23 insertions(+), 14 deletions(-)

diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c
index 297812d..c724f5f 100644
--- a/hw/acpi/memory_hotplug.c
+++ b/hw/acpi/memory_hotplug.c
@@ -29,12 +29,9 @@
 #define MEMORY_SLOT_PROXIMITY_METHOD "MPXM"
 #define MEMORY_SLOT_EJECT_METHOD     "MEJ0"
 #define MEMORY_SLOT_NOTIFY_METHOD    "MTFY"
-#define MEMORY_SLOT_SCAN_METHOD      "MSCN"
 #define MEMORY_HOTPLUG_DEVICE        "MHPD"
-#define MEMORY_HOTPLUG_IO_LEN         24
-#define MEMORY_DEVICES_CONTAINER     "\\_SB.MHPC"
 
-static uint16_t memhp_io_base;
+static hwaddr memhp_io_base;
 
 static ACPIOSTInfo *acpi_memory_device_status(int slot, MemStatus *mdev)
 {
@@ -209,7 +206,7 @@ static const MemoryRegionOps acpi_memory_hotplug_ops = {
 };
 
 void acpi_memory_hotplug_init(MemoryRegion *as, Object *owner,
-                              MemHotplugState *state, uint16_t io_base)
+                              MemHotplugState *state, hwaddr io_base)
 {
     MachineState *machine = MACHINE(qdev_get_machine());
 
@@ -342,7 +339,8 @@ const VMStateDescription vmstate_memory_hotplug = {
 
 void build_memory_hotplug_aml(Aml *table, uint32_t nr_mem,
                               const char *res_root,
-                              const char *event_handler_method)
+                              const char *event_handler_method,
+                              AmlRegionSpace rs)
 {
     int i;
     Aml *ifctx;
@@ -365,14 +363,19 @@ void build_memory_hotplug_aml(Aml *table, uint32_t nr_mem,
             aml_name_decl("_UID", aml_string("Memory hotplug resources")));
 
         crs = aml_resource_template();
-        aml_append(crs,
-            aml_io(AML_DECODE16, memhp_io_base, memhp_io_base, 0,
-                   MEMORY_HOTPLUG_IO_LEN)
-        );
+        if (rs == AML_SYSTEM_IO) {
+            aml_append(crs,
+                aml_io(AML_DECODE16, memhp_io_base, memhp_io_base, 0,
+                       MEMORY_HOTPLUG_IO_LEN)
+            );
+        } else {
+            aml_append(crs, aml_memory32_fixed(memhp_io_base,
+                            MEMORY_HOTPLUG_IO_LEN, AML_READ_WRITE));
+        }
         aml_append(mem_ctrl_dev, aml_name_decl("_CRS", crs));
 
         aml_append(mem_ctrl_dev, aml_operation_region(
-            MEMORY_HOTPLUG_IO_REGION, AML_SYSTEM_IO,
+            MEMORY_HOTPLUG_IO_REGION, rs,
             aml_int(memhp_io_base), MEMORY_HOTPLUG_IO_LEN)
         );
 
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 416da31..6d6de44 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -1852,7 +1852,8 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
         build_cpus_aml(dsdt, machine, opts, pm->cpu_hp_io_base,
                        "\\_SB.PCI0", "\\_GPE._E02");
     }
-    build_memory_hotplug_aml(dsdt, nr_mem, "\\_SB.PCI0", "\\_GPE._E03");
+    build_memory_hotplug_aml(dsdt, nr_mem, "\\_SB.PCI0",
+                             "\\_GPE._E03", AML_SYSTEM_IO);
 
     scope =  aml_scope("_GPE");
     {
diff --git a/include/hw/acpi/memory_hotplug.h b/include/hw/acpi/memory_hotplug.h
index 77c6576..e3a4b89 100644
--- a/include/hw/acpi/memory_hotplug.h
+++ b/include/hw/acpi/memory_hotplug.h
@@ -5,6 +5,10 @@
 #include "hw/acpi/acpi.h"
 #include "hw/acpi/aml-build.h"
 
+#define MEMORY_SLOT_SCAN_METHOD      "MSCN"
+#define MEMORY_DEVICES_CONTAINER     "\\_SB.MHPC"
+#define MEMORY_HOTPLUG_IO_LEN         24
+
 /**
  * MemStatus:
  * @is_removing: the memory device in slot has been requested to be ejected.
@@ -29,7 +33,7 @@ typedef struct MemHotplugState {
 } MemHotplugState;
 
 void acpi_memory_hotplug_init(MemoryRegion *as, Object *owner,
-                              MemHotplugState *state, uint16_t io_base);
+                              MemHotplugState *state, hwaddr io_base);
 
 void acpi_memory_plug_cb(HotplugHandler *hotplug_dev, MemHotplugState *mem_st,
                          DeviceState *dev, Error **errp);
@@ -48,5 +52,6 @@ void acpi_memory_ospm_status(MemHotplugState *mem_st, ACPIOSTInfoList ***list);
 
 void build_memory_hotplug_aml(Aml *table, uint32_t nr_mem,
                               const char *res_root,
-                              const char *event_handler_method);
+                              const char *event_handler_method,
+                              AmlRegionSpace rs);
 #endif
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [Qemu-devel] [PATCH v4 1/8] hw/acpi: Make ACPI IO address space configurable
@ 2019-04-09 10:29   ` Shameer Kolothum
  0 siblings, 0 replies; 62+ messages in thread
From: Shameer Kolothum @ 2019-04-09 10:29 UTC (permalink / raw)
  To: qemu-devel, qemu-arm, eric.auger, imammedo
  Cc: peter.maydell, sameo, ard.biesheuvel, linuxarm, xuwei5,
	shannon.zhaosl, sebastien.boeuf, lersek

This is in preparation for adding support for ARM64 platforms
where it doesn't use port mapped IO for ACPI IO space.

Also move few MEMORY_* definitions to header so that other memory
hotplug event signalling mechanisms (eg. Generic Event Device on
HW-reduced acpi platforms) can use the same from their respective
event handler code.

Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
Reviewed-by: Eric Auger <eric.auger@redhat.com>
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
---
 hw/acpi/memory_hotplug.c         | 25 ++++++++++++++-----------
 hw/i386/acpi-build.c             |  3 ++-
 include/hw/acpi/memory_hotplug.h |  9 +++++++--
 3 files changed, 23 insertions(+), 14 deletions(-)

diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c
index 297812d..c724f5f 100644
--- a/hw/acpi/memory_hotplug.c
+++ b/hw/acpi/memory_hotplug.c
@@ -29,12 +29,9 @@
 #define MEMORY_SLOT_PROXIMITY_METHOD "MPXM"
 #define MEMORY_SLOT_EJECT_METHOD     "MEJ0"
 #define MEMORY_SLOT_NOTIFY_METHOD    "MTFY"
-#define MEMORY_SLOT_SCAN_METHOD      "MSCN"
 #define MEMORY_HOTPLUG_DEVICE        "MHPD"
-#define MEMORY_HOTPLUG_IO_LEN         24
-#define MEMORY_DEVICES_CONTAINER     "\\_SB.MHPC"
 
-static uint16_t memhp_io_base;
+static hwaddr memhp_io_base;
 
 static ACPIOSTInfo *acpi_memory_device_status(int slot, MemStatus *mdev)
 {
@@ -209,7 +206,7 @@ static const MemoryRegionOps acpi_memory_hotplug_ops = {
 };
 
 void acpi_memory_hotplug_init(MemoryRegion *as, Object *owner,
-                              MemHotplugState *state, uint16_t io_base)
+                              MemHotplugState *state, hwaddr io_base)
 {
     MachineState *machine = MACHINE(qdev_get_machine());
 
@@ -342,7 +339,8 @@ const VMStateDescription vmstate_memory_hotplug = {
 
 void build_memory_hotplug_aml(Aml *table, uint32_t nr_mem,
                               const char *res_root,
-                              const char *event_handler_method)
+                              const char *event_handler_method,
+                              AmlRegionSpace rs)
 {
     int i;
     Aml *ifctx;
@@ -365,14 +363,19 @@ void build_memory_hotplug_aml(Aml *table, uint32_t nr_mem,
             aml_name_decl("_UID", aml_string("Memory hotplug resources")));
 
         crs = aml_resource_template();
-        aml_append(crs,
-            aml_io(AML_DECODE16, memhp_io_base, memhp_io_base, 0,
-                   MEMORY_HOTPLUG_IO_LEN)
-        );
+        if (rs == AML_SYSTEM_IO) {
+            aml_append(crs,
+                aml_io(AML_DECODE16, memhp_io_base, memhp_io_base, 0,
+                       MEMORY_HOTPLUG_IO_LEN)
+            );
+        } else {
+            aml_append(crs, aml_memory32_fixed(memhp_io_base,
+                            MEMORY_HOTPLUG_IO_LEN, AML_READ_WRITE));
+        }
         aml_append(mem_ctrl_dev, aml_name_decl("_CRS", crs));
 
         aml_append(mem_ctrl_dev, aml_operation_region(
-            MEMORY_HOTPLUG_IO_REGION, AML_SYSTEM_IO,
+            MEMORY_HOTPLUG_IO_REGION, rs,
             aml_int(memhp_io_base), MEMORY_HOTPLUG_IO_LEN)
         );
 
diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c
index 416da31..6d6de44 100644
--- a/hw/i386/acpi-build.c
+++ b/hw/i386/acpi-build.c
@@ -1852,7 +1852,8 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
         build_cpus_aml(dsdt, machine, opts, pm->cpu_hp_io_base,
                        "\\_SB.PCI0", "\\_GPE._E02");
     }
-    build_memory_hotplug_aml(dsdt, nr_mem, "\\_SB.PCI0", "\\_GPE._E03");
+    build_memory_hotplug_aml(dsdt, nr_mem, "\\_SB.PCI0",
+                             "\\_GPE._E03", AML_SYSTEM_IO);
 
     scope =  aml_scope("_GPE");
     {
diff --git a/include/hw/acpi/memory_hotplug.h b/include/hw/acpi/memory_hotplug.h
index 77c6576..e3a4b89 100644
--- a/include/hw/acpi/memory_hotplug.h
+++ b/include/hw/acpi/memory_hotplug.h
@@ -5,6 +5,10 @@
 #include "hw/acpi/acpi.h"
 #include "hw/acpi/aml-build.h"
 
+#define MEMORY_SLOT_SCAN_METHOD      "MSCN"
+#define MEMORY_DEVICES_CONTAINER     "\\_SB.MHPC"
+#define MEMORY_HOTPLUG_IO_LEN         24
+
 /**
  * MemStatus:
  * @is_removing: the memory device in slot has been requested to be ejected.
@@ -29,7 +33,7 @@ typedef struct MemHotplugState {
 } MemHotplugState;
 
 void acpi_memory_hotplug_init(MemoryRegion *as, Object *owner,
-                              MemHotplugState *state, uint16_t io_base);
+                              MemHotplugState *state, hwaddr io_base);
 
 void acpi_memory_plug_cb(HotplugHandler *hotplug_dev, MemHotplugState *mem_st,
                          DeviceState *dev, Error **errp);
@@ -48,5 +52,6 @@ void acpi_memory_ospm_status(MemHotplugState *mem_st, ACPIOSTInfoList ***list);
 
 void build_memory_hotplug_aml(Aml *table, uint32_t nr_mem,
                               const char *res_root,
-                              const char *event_handler_method);
+                              const char *event_handler_method,
+                              AmlRegionSpace rs);
 #endif
-- 
2.7.4




^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [Qemu-devel] [PATCH v4 2/8] hw/acpi: Do not create memory hotplug method when handler is not defined
@ 2019-04-09 10:29   ` Shameer Kolothum
  0 siblings, 0 replies; 62+ messages in thread
From: Shameer Kolothum @ 2019-04-09 10:29 UTC (permalink / raw)
  To: qemu-devel, qemu-arm, eric.auger, imammedo
  Cc: peter.maydell, shannon.zhaosl, sameo, sebastien.boeuf, xuwei5,
	lersek, ard.biesheuvel, linuxarm

From: Samuel Ortiz <sameo@linux.intel.com>

With Hardware-reduced ACPI, the GED device will manage ACPI
hotplug entirely. As a consequence, make the memory specific
events AML generation optional. The code will only be added
when the method name is not NULL.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
Reviewed-by: Eric Auger <eric.auger@redhat.com>
---
 hw/acpi/memory_hotplug.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c
index c724f5f..b74a5e2 100644
--- a/hw/acpi/memory_hotplug.c
+++ b/hw/acpi/memory_hotplug.c
@@ -719,10 +719,12 @@ void build_memory_hotplug_aml(Aml *table, uint32_t nr_mem,
     }
     aml_append(table, dev_container);
 
-    method = aml_method(event_handler_method, 0, AML_NOTSERIALIZED);
-    aml_append(method,
-        aml_call0(MEMORY_DEVICES_CONTAINER "." MEMORY_SLOT_SCAN_METHOD));
-    aml_append(table, method);
+    if (event_handler_method) {
+        method = aml_method(event_handler_method, 0, AML_NOTSERIALIZED);
+        aml_append(method,
+                   aml_call0(MEMORY_DEVICES_CONTAINER "." MEMORY_SLOT_SCAN_METHOD));
+        aml_append(table, method);
+    }
 
     g_free(mhp_res_path);
 }
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [Qemu-devel] [PATCH v4 2/8] hw/acpi: Do not create memory hotplug method when handler is not defined
@ 2019-04-09 10:29   ` Shameer Kolothum
  0 siblings, 0 replies; 62+ messages in thread
From: Shameer Kolothum @ 2019-04-09 10:29 UTC (permalink / raw)
  To: qemu-devel, qemu-arm, eric.auger, imammedo
  Cc: peter.maydell, sameo, ard.biesheuvel, linuxarm, xuwei5,
	shannon.zhaosl, sebastien.boeuf, lersek

From: Samuel Ortiz <sameo@linux.intel.com>

With Hardware-reduced ACPI, the GED device will manage ACPI
hotplug entirely. As a consequence, make the memory specific
events AML generation optional. The code will only be added
when the method name is not NULL.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
Reviewed-by: Eric Auger <eric.auger@redhat.com>
---
 hw/acpi/memory_hotplug.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c
index c724f5f..b74a5e2 100644
--- a/hw/acpi/memory_hotplug.c
+++ b/hw/acpi/memory_hotplug.c
@@ -719,10 +719,12 @@ void build_memory_hotplug_aml(Aml *table, uint32_t nr_mem,
     }
     aml_append(table, dev_container);
 
-    method = aml_method(event_handler_method, 0, AML_NOTSERIALIZED);
-    aml_append(method,
-        aml_call0(MEMORY_DEVICES_CONTAINER "." MEMORY_SLOT_SCAN_METHOD));
-    aml_append(table, method);
+    if (event_handler_method) {
+        method = aml_method(event_handler_method, 0, AML_NOTSERIALIZED);
+        aml_append(method,
+                   aml_call0(MEMORY_DEVICES_CONTAINER "." MEMORY_SLOT_SCAN_METHOD));
+        aml_append(table, method);
+    }
 
     g_free(mhp_res_path);
 }
-- 
2.7.4




^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
@ 2019-04-09 10:29   ` Shameer Kolothum
  0 siblings, 0 replies; 62+ messages in thread
From: Shameer Kolothum @ 2019-04-09 10:29 UTC (permalink / raw)
  To: qemu-devel, qemu-arm, eric.auger, imammedo
  Cc: peter.maydell, shannon.zhaosl, sameo, sebastien.boeuf, xuwei5,
	lersek, ard.biesheuvel, linuxarm

From: Samuel Ortiz <sameo@linux.intel.com>

The ACPI Generic Event Device (GED) is a hardware-reduced specific
device[ACPI v6.1 Section 5.6.9] that handles all platform events,
including the hotplug ones.This patch generates the AML code that
defines GEDs.

Platforms need to specify their own GedEvent array to describe what
kind of events they want to support through GED.  Also this uses a
a single interrupt for the  GED device, relying on IO memory region
to communicate the type of device affected by the interrupt. This
way, we can support up to 32 events with a unique interrupt.

This supports only memory hotplug for now.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
---
 hw/acpi/Kconfig                        |   4 +
 hw/acpi/Makefile.objs                  |   1 +
 hw/acpi/generic_event_device.c         | 311 +++++++++++++++++++++++++++++++++
 include/hw/acpi/generic_event_device.h | 121 +++++++++++++
 4 files changed, 437 insertions(+)
 create mode 100644 hw/acpi/generic_event_device.c
 create mode 100644 include/hw/acpi/generic_event_device.h

diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig
index eca3bee..01a8b41 100644
--- a/hw/acpi/Kconfig
+++ b/hw/acpi/Kconfig
@@ -27,3 +27,7 @@ config ACPI_VMGENID
     bool
     default y
     depends on PC
+
+config ACPI_HW_REDUCED
+    bool
+    depends on ACPI
diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
index 2d46e37..b753232 100644
--- a/hw/acpi/Makefile.objs
+++ b/hw/acpi/Makefile.objs
@@ -6,6 +6,7 @@ common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o
 common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu.o
 common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
 common-obj-$(CONFIG_ACPI_VMGENID) += vmgenid.o
+common-obj-$(CONFIG_ACPI_HW_REDUCED) += generic_event_device.o
 common-obj-$(call lnot,$(CONFIG_ACPI_X86)) += acpi-stub.o
 
 common-obj-y += acpi_interface.o
diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c
new file mode 100644
index 0000000..856ca04
--- /dev/null
+++ b/hw/acpi/generic_event_device.c
@@ -0,0 +1,311 @@
+/*
+ *
+ * Copyright (c) 2018 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "exec/address-spaces.h"
+#include "hw/sysbus.h"
+#include "hw/acpi/acpi.h"
+#include "hw/acpi/generic_event_device.h"
+#include "hw/mem/pc-dimm.h"
+
+static Aml *ged_event_aml(const GedEvent *event)
+{
+
+    if (!event) {
+        return NULL;
+    }
+
+    switch (event->event) {
+    case GED_MEMORY_HOTPLUG:
+        /* We run a complete memory SCAN when getting a memory hotplug event */
+        return aml_call0(MEMORY_DEVICES_CONTAINER "." MEMORY_SLOT_SCAN_METHOD);
+    default:
+        break;
+    }
+
+    return NULL;
+}
+
+/*
+ * The ACPI Generic Event Device (GED) is a hardware-reduced specific
+ * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
+ * including the hotplug ones. Platforms need to specify their own
+ * GedEvent array to describe what kind of events they want to support
+ * through GED. This routine uses a single interrupt for the GED device,
+ * relying on IO memory region to communicate the type of device
+ * affected by the interrupt. This way, we can support up to 32 events
+ * with a unique interrupt.
+ */
+void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev,
+                   uint32_t ged_irq, AmlRegionSpace rs)
+{
+    AcpiGedState *s = ACPI_GED(hotplug_dev);
+    GedEvent *ged_events = s->ged_events;
+    Aml *crs = aml_resource_template();
+    Aml *evt, *field;
+    Aml *dev = aml_device("%s", name);
+    Aml *irq_sel = aml_local(0);
+    Aml *isel = aml_name(AML_GED_IRQ_SEL);
+    uint32_t i;
+
+    if (!s->ged_base || !ged_events || !s->ged_events_size) {
+        return;
+    }
+
+    /* _CRS interrupt */
+    aml_append(crs, aml_interrupt(AML_CONSUMER, AML_EDGE, AML_ACTIVE_HIGH,
+                                  AML_EXCLUSIVE, &ged_irq, 1));
+    /*
+     * For each GED event we:
+     * - Add an interrupt to the CRS section.
+     * - Add a conditional block for each event, inside a while loop.
+     *   This is semantically equivalent to a switch/case implementation.
+     */
+    evt = aml_method("_EVT", 1, AML_SERIALIZED);
+    {
+        Aml *ged_aml;
+        Aml *if_ctx;
+
+        /* Local0 = ISEL */
+        aml_append(evt, aml_store(isel, irq_sel));
+
+        /*
+         * Here we want to call a method for each supported GED event type.
+         * The resulting ASL code looks like:
+         *
+         * Local0 = ISEL
+         * If ((Local0 & irq0) == irq0)
+         * {
+         *     MethodEvent0()
+         * }
+         *
+         * If ((Local0 & irq1) == irq1)
+         * {
+         *     MethodEvent1()
+         * }
+         * ...
+         */
+
+        for (i = 0; i < s->ged_events_size; i++) {
+            ged_aml = ged_event_aml(&ged_events[i]);
+            if (!ged_aml) {
+                continue;
+            }
+
+            /* If ((Local1 == irq))*/
+            if_ctx = aml_if(aml_equal(aml_and(irq_sel,
+                                      aml_int(ged_events[i].selector), NULL),
+                                      aml_int(ged_events[i].selector)));
+            {
+                /* AML for this specific type of event */
+                aml_append(if_ctx, ged_aml);
+            }
+
+            /*
+             * We append the first "if" to the "while" context.
+             * Other "if"s will be "elseif"s.
+             */
+            aml_append(evt, if_ctx);
+        }
+    }
+
+    aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0013")));
+    aml_append(dev, aml_name_decl("_UID", aml_string(GED_DEVICE)));
+    aml_append(dev, aml_name_decl("_CRS", crs));
+
+    /* Append IO region */
+    aml_append(dev, aml_operation_region(AML_GED_IRQ_REG, rs,
+               aml_int(s->ged_base + ACPI_GED_IRQ_SEL_OFFSET),
+               ACPI_GED_IRQ_SEL_LEN));
+    field = aml_field(AML_GED_IRQ_REG, AML_DWORD_ACC, AML_NOLOCK,
+                      AML_WRITE_AS_ZEROS);
+    aml_append(field, aml_named_field(AML_GED_IRQ_SEL,
+                                      ACPI_GED_IRQ_SEL_LEN * 8));
+    aml_append(dev, field);
+
+    /* Append _EVT method */
+    aml_append(dev, evt);
+
+    aml_append(table, dev);
+}
+
+/* Memory read by the GED _EVT AML dynamic method */
+static uint64_t ged_read(void *opaque, hwaddr addr, unsigned size)
+{
+    uint64_t val = 0;
+    GEDState *ged_st = opaque;
+
+    switch (addr) {
+    case ACPI_GED_IRQ_SEL_OFFSET:
+        /* Read the selector value and reset it */
+        qemu_mutex_lock(&ged_st->lock);
+        val = ged_st->sel;
+        ged_st->sel = 0;
+        qemu_mutex_unlock(&ged_st->lock);
+        break;
+    default:
+        break;
+    }
+
+    return val;
+}
+
+/* Nothing is expected to be written to the GED memory region */
+static void ged_write(void *opaque, hwaddr addr, uint64_t data,
+                      unsigned int size)
+{
+}
+
+static const MemoryRegionOps ged_ops = {
+    .read = ged_read,
+    .write = ged_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static void acpi_ged_event(GEDState *ged_st, uint32_t ged_irq_sel)
+{
+    /*
+     * Set the GED IRQ selector to the expected device type value. This
+     * way, the ACPI method will be able to trigger the right code based
+     * on a unique IRQ.
+     */
+    qemu_mutex_lock(&ged_st->lock);
+    ged_st->sel = ged_irq_sel;
+    qemu_mutex_unlock(&ged_st->lock);
+
+    /* Trigger the event by sending an interrupt to the guest. */
+    qemu_irq_pulse(ged_st->gsi[ged_st->irq]);
+}
+
+static void acpi_ged_init(MemoryRegion *as, DeviceState *dev, GEDState *ged_st)
+{
+    AcpiGedState *s = ACPI_GED(dev);
+
+    assert(s->ged_base);
+
+    ged_st->irq = s->ged_irq;
+    ged_st->gsi = s->gsi;
+    qemu_mutex_init(&ged_st->lock);
+    memory_region_init_io(&ged_st->io, OBJECT(dev), &ged_ops, ged_st,
+                          "acpi-ged-event", ACPI_GED_REG_LEN);
+    memory_region_add_subregion(as, s->ged_base, &ged_st->io);
+}
+
+static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
+                                    DeviceState *dev, Error **errp)
+{
+    AcpiGedState *s = ACPI_GED(hotplug_dev);
+
+    if (s->memhp_state.is_enabled &&
+        object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+            acpi_memory_plug_cb(hotplug_dev, &s->memhp_state,
+                                dev, errp);
+    } else {
+        error_setg(errp, "virt: device plug request for unsupported device"
+                   " type: %s", object_get_typename(OBJECT(dev)));
+    }
+}
+
+static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
+{
+    AcpiGedState *s = ACPI_GED(adev);
+    uint32_t sel;
+
+    if (ev & ACPI_MEMORY_HOTPLUG_STATUS) {
+        sel = ACPI_GED_IRQ_SEL_MEM;
+    } else {
+        /* Unknown event. Return without generating interrupt. */
+        return;
+    }
+
+    /*
+     * We inject the hotplug interrupt. The IRQ selector will make
+     * the difference from the ACPI table.
+     */
+    acpi_ged_event(&s->ged_state, sel);
+}
+
+static void acpi_ged_device_realize(DeviceState *dev, Error **errp)
+{
+    AcpiGedState *s = ACPI_GED(dev);
+
+    if (s->memhp_state.is_enabled) {
+        acpi_memory_hotplug_init(get_system_memory(), OBJECT(dev),
+                                 &s->memhp_state,
+                                 s->memhp_base);
+        acpi_ged_init(get_system_memory(), dev, &s->ged_state);
+    }
+}
+
+static Property acpi_ged_properties[] = {
+    /*
+     * Memory hotplug base address is a property of GED here,
+     * because GED handles memory hotplug event and MEMORY_HOTPLUG_DEVICE
+     * gets initialized when GED device is realized.
+     */
+    DEFINE_PROP_UINT64("memhp-base", AcpiGedState, memhp_base, 0),
+    DEFINE_PROP_BOOL("memory-hotplug-support", AcpiGedState,
+                     memhp_state.is_enabled, true),
+    DEFINE_PROP_PTR("gsi", AcpiGedState, gsi),
+    DEFINE_PROP_UINT64("ged-base", AcpiGedState, ged_base, 0),
+    DEFINE_PROP_UINT32("ged-irq", AcpiGedState, ged_irq, 0),
+    DEFINE_PROP_PTR("ged-events", AcpiGedState, ged_events),
+    DEFINE_PROP_UINT32("ged-events-size", AcpiGedState, ged_events_size, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void acpi_ged_class_init(ObjectClass *class, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(class);
+    HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class);
+    AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class);
+
+    dc->desc = "ACPI";
+    dc->props = acpi_ged_properties;
+    dc->realize = acpi_ged_device_realize;
+
+    /* Reason: pointer properties "gsi" and "ged_events" */
+    dc->user_creatable = false;
+
+    hc->plug = acpi_ged_device_plug_cb;
+
+    adevc->send_event = acpi_ged_send_event;
+}
+
+static const TypeInfo acpi_ged_info = {
+    .name          = TYPE_ACPI_GED,
+    .parent        = TYPE_DEVICE,
+    .instance_size = sizeof(AcpiGedState),
+    .class_init    = acpi_ged_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_HOTPLUG_HANDLER },
+        { TYPE_ACPI_DEVICE_IF },
+        { }
+    }
+};
+
+static void acpi_ged_register_types(void)
+{
+    type_register_static(&acpi_ged_info);
+}
+
+type_init(acpi_ged_register_types)
diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h
new file mode 100644
index 0000000..9c840d8
--- /dev/null
+++ b/include/hw/acpi/generic_event_device.h
@@ -0,0 +1,121 @@
+/*
+ *
+ * Copyright (c) 2018 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * The ACPI Generic Event Device (GED) is a hardware-reduced specific
+ * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
+ * including the hotplug ones. Generic Event Device allows platforms
+ * to handle interrupts in ACPI ASL statements. It follows a very
+ * similar approach like the _EVT method from GPIO events. All
+ * interrupts are listed in  _CRS and the handler is written in _EVT
+ * method. Here, we use a single interrupt for the GED device, relying
+ * on IO memory region to communicate the type of device affected by
+ * the interrupt. This way, we can support up to 32 events with a
+ * unique interrupt.
+ *
+ * Here is an example.
+ *
+ * Device (\_SB.GED)
+ * {
+ *     Name (_HID, "ACPI0013")
+ *     Name (_UID, Zero)
+ *     Name (_CRS, ResourceTemplate ()
+ *     {
+ *         Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, )
+ *         {
+ *              0x00000029,
+ *         }
+ *     })
+ *     OperationRegion (IREG, SystemMemory, 0x09080000, 0x04)
+ *     Field (IREG, DWordAcc, NoLock, WriteAsZeros)
+ *     {
+ *         ISEL,   32
+ *     }
+ *
+ *     Method (_EVT, 1, Serialized)  // _EVT: Event
+ *     {
+ *         Local0 = ISEL // ISEL = IO memory region which specifies the
+ *                       // device type.
+ *         If (((Local0 & irq0) == irq0))
+ *         {
+ *             MethodEvent0()
+ *         }
+ *         ElseIf ((Local0 & irq1) == irq1)
+ *         {
+ *             MethodEvent1()
+ *         }
+ *         ...
+ *     }
+ * }
+ *
+ */
+
+#ifndef HW_ACPI_GED_H
+#define HW_ACPI_GED_H
+
+#include "hw/acpi/memory_hotplug.h"
+
+#define TYPE_ACPI_GED "acpi-ged"
+#define ACPI_GED(obj) \
+    OBJECT_CHECK(AcpiGedState, (obj), TYPE_ACPI_GED)
+
+#define ACPI_GED_IRQ_SEL_OFFSET 0x0
+#define ACPI_GED_IRQ_SEL_LEN    0x4
+#define ACPI_GED_IRQ_SEL_MEM    0x1
+#define ACPI_GED_REG_LEN        0x4
+
+#define GED_DEVICE      "GED"
+#define AML_GED_IRQ_REG "IREG"
+#define AML_GED_IRQ_SEL "ISEL"
+
+typedef enum {
+    GED_MEMORY_HOTPLUG = 1,
+} GedEventType;
+
+/*
+ * Platforms need to specify their own GedEvent array
+ * to describe what kind of events they want to support
+ * through GED.
+ */
+typedef struct GedEvent {
+    uint32_t     selector;
+    GedEventType event;
+} GedEvent;
+
+typedef struct GEDState {
+    MemoryRegion io;
+    uint32_t     sel;
+    uint32_t     irq;
+    qemu_irq     *gsi;
+    QemuMutex    lock;
+} GEDState;
+
+
+typedef struct AcpiGedState {
+    DeviceClass parent_obj;
+    MemHotplugState memhp_state;
+    hwaddr memhp_base;
+    void *gsi;
+    hwaddr ged_base;
+    GEDState ged_state;
+    uint32_t ged_irq;
+    void *ged_events;
+    uint32_t ged_events_size;
+} AcpiGedState;
+
+void build_ged_aml(Aml *table, const char* name, HotplugHandler *hotplug_dev,
+                   uint32_t ged_irq, AmlRegionSpace rs);
+
+#endif
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
@ 2019-04-09 10:29   ` Shameer Kolothum
  0 siblings, 0 replies; 62+ messages in thread
From: Shameer Kolothum @ 2019-04-09 10:29 UTC (permalink / raw)
  To: qemu-devel, qemu-arm, eric.auger, imammedo
  Cc: peter.maydell, sameo, ard.biesheuvel, linuxarm, xuwei5,
	shannon.zhaosl, sebastien.boeuf, lersek

From: Samuel Ortiz <sameo@linux.intel.com>

The ACPI Generic Event Device (GED) is a hardware-reduced specific
device[ACPI v6.1 Section 5.6.9] that handles all platform events,
including the hotplug ones.This patch generates the AML code that
defines GEDs.

Platforms need to specify their own GedEvent array to describe what
kind of events they want to support through GED.  Also this uses a
a single interrupt for the  GED device, relying on IO memory region
to communicate the type of device affected by the interrupt. This
way, we can support up to 32 events with a unique interrupt.

This supports only memory hotplug for now.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
---
 hw/acpi/Kconfig                        |   4 +
 hw/acpi/Makefile.objs                  |   1 +
 hw/acpi/generic_event_device.c         | 311 +++++++++++++++++++++++++++++++++
 include/hw/acpi/generic_event_device.h | 121 +++++++++++++
 4 files changed, 437 insertions(+)
 create mode 100644 hw/acpi/generic_event_device.c
 create mode 100644 include/hw/acpi/generic_event_device.h

diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig
index eca3bee..01a8b41 100644
--- a/hw/acpi/Kconfig
+++ b/hw/acpi/Kconfig
@@ -27,3 +27,7 @@ config ACPI_VMGENID
     bool
     default y
     depends on PC
+
+config ACPI_HW_REDUCED
+    bool
+    depends on ACPI
diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
index 2d46e37..b753232 100644
--- a/hw/acpi/Makefile.objs
+++ b/hw/acpi/Makefile.objs
@@ -6,6 +6,7 @@ common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o
 common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu.o
 common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
 common-obj-$(CONFIG_ACPI_VMGENID) += vmgenid.o
+common-obj-$(CONFIG_ACPI_HW_REDUCED) += generic_event_device.o
 common-obj-$(call lnot,$(CONFIG_ACPI_X86)) += acpi-stub.o
 
 common-obj-y += acpi_interface.o
diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c
new file mode 100644
index 0000000..856ca04
--- /dev/null
+++ b/hw/acpi/generic_event_device.c
@@ -0,0 +1,311 @@
+/*
+ *
+ * Copyright (c) 2018 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "exec/address-spaces.h"
+#include "hw/sysbus.h"
+#include "hw/acpi/acpi.h"
+#include "hw/acpi/generic_event_device.h"
+#include "hw/mem/pc-dimm.h"
+
+static Aml *ged_event_aml(const GedEvent *event)
+{
+
+    if (!event) {
+        return NULL;
+    }
+
+    switch (event->event) {
+    case GED_MEMORY_HOTPLUG:
+        /* We run a complete memory SCAN when getting a memory hotplug event */
+        return aml_call0(MEMORY_DEVICES_CONTAINER "." MEMORY_SLOT_SCAN_METHOD);
+    default:
+        break;
+    }
+
+    return NULL;
+}
+
+/*
+ * The ACPI Generic Event Device (GED) is a hardware-reduced specific
+ * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
+ * including the hotplug ones. Platforms need to specify their own
+ * GedEvent array to describe what kind of events they want to support
+ * through GED. This routine uses a single interrupt for the GED device,
+ * relying on IO memory region to communicate the type of device
+ * affected by the interrupt. This way, we can support up to 32 events
+ * with a unique interrupt.
+ */
+void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev,
+                   uint32_t ged_irq, AmlRegionSpace rs)
+{
+    AcpiGedState *s = ACPI_GED(hotplug_dev);
+    GedEvent *ged_events = s->ged_events;
+    Aml *crs = aml_resource_template();
+    Aml *evt, *field;
+    Aml *dev = aml_device("%s", name);
+    Aml *irq_sel = aml_local(0);
+    Aml *isel = aml_name(AML_GED_IRQ_SEL);
+    uint32_t i;
+
+    if (!s->ged_base || !ged_events || !s->ged_events_size) {
+        return;
+    }
+
+    /* _CRS interrupt */
+    aml_append(crs, aml_interrupt(AML_CONSUMER, AML_EDGE, AML_ACTIVE_HIGH,
+                                  AML_EXCLUSIVE, &ged_irq, 1));
+    /*
+     * For each GED event we:
+     * - Add an interrupt to the CRS section.
+     * - Add a conditional block for each event, inside a while loop.
+     *   This is semantically equivalent to a switch/case implementation.
+     */
+    evt = aml_method("_EVT", 1, AML_SERIALIZED);
+    {
+        Aml *ged_aml;
+        Aml *if_ctx;
+
+        /* Local0 = ISEL */
+        aml_append(evt, aml_store(isel, irq_sel));
+
+        /*
+         * Here we want to call a method for each supported GED event type.
+         * The resulting ASL code looks like:
+         *
+         * Local0 = ISEL
+         * If ((Local0 & irq0) == irq0)
+         * {
+         *     MethodEvent0()
+         * }
+         *
+         * If ((Local0 & irq1) == irq1)
+         * {
+         *     MethodEvent1()
+         * }
+         * ...
+         */
+
+        for (i = 0; i < s->ged_events_size; i++) {
+            ged_aml = ged_event_aml(&ged_events[i]);
+            if (!ged_aml) {
+                continue;
+            }
+
+            /* If ((Local1 == irq))*/
+            if_ctx = aml_if(aml_equal(aml_and(irq_sel,
+                                      aml_int(ged_events[i].selector), NULL),
+                                      aml_int(ged_events[i].selector)));
+            {
+                /* AML for this specific type of event */
+                aml_append(if_ctx, ged_aml);
+            }
+
+            /*
+             * We append the first "if" to the "while" context.
+             * Other "if"s will be "elseif"s.
+             */
+            aml_append(evt, if_ctx);
+        }
+    }
+
+    aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0013")));
+    aml_append(dev, aml_name_decl("_UID", aml_string(GED_DEVICE)));
+    aml_append(dev, aml_name_decl("_CRS", crs));
+
+    /* Append IO region */
+    aml_append(dev, aml_operation_region(AML_GED_IRQ_REG, rs,
+               aml_int(s->ged_base + ACPI_GED_IRQ_SEL_OFFSET),
+               ACPI_GED_IRQ_SEL_LEN));
+    field = aml_field(AML_GED_IRQ_REG, AML_DWORD_ACC, AML_NOLOCK,
+                      AML_WRITE_AS_ZEROS);
+    aml_append(field, aml_named_field(AML_GED_IRQ_SEL,
+                                      ACPI_GED_IRQ_SEL_LEN * 8));
+    aml_append(dev, field);
+
+    /* Append _EVT method */
+    aml_append(dev, evt);
+
+    aml_append(table, dev);
+}
+
+/* Memory read by the GED _EVT AML dynamic method */
+static uint64_t ged_read(void *opaque, hwaddr addr, unsigned size)
+{
+    uint64_t val = 0;
+    GEDState *ged_st = opaque;
+
+    switch (addr) {
+    case ACPI_GED_IRQ_SEL_OFFSET:
+        /* Read the selector value and reset it */
+        qemu_mutex_lock(&ged_st->lock);
+        val = ged_st->sel;
+        ged_st->sel = 0;
+        qemu_mutex_unlock(&ged_st->lock);
+        break;
+    default:
+        break;
+    }
+
+    return val;
+}
+
+/* Nothing is expected to be written to the GED memory region */
+static void ged_write(void *opaque, hwaddr addr, uint64_t data,
+                      unsigned int size)
+{
+}
+
+static const MemoryRegionOps ged_ops = {
+    .read = ged_read,
+    .write = ged_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static void acpi_ged_event(GEDState *ged_st, uint32_t ged_irq_sel)
+{
+    /*
+     * Set the GED IRQ selector to the expected device type value. This
+     * way, the ACPI method will be able to trigger the right code based
+     * on a unique IRQ.
+     */
+    qemu_mutex_lock(&ged_st->lock);
+    ged_st->sel = ged_irq_sel;
+    qemu_mutex_unlock(&ged_st->lock);
+
+    /* Trigger the event by sending an interrupt to the guest. */
+    qemu_irq_pulse(ged_st->gsi[ged_st->irq]);
+}
+
+static void acpi_ged_init(MemoryRegion *as, DeviceState *dev, GEDState *ged_st)
+{
+    AcpiGedState *s = ACPI_GED(dev);
+
+    assert(s->ged_base);
+
+    ged_st->irq = s->ged_irq;
+    ged_st->gsi = s->gsi;
+    qemu_mutex_init(&ged_st->lock);
+    memory_region_init_io(&ged_st->io, OBJECT(dev), &ged_ops, ged_st,
+                          "acpi-ged-event", ACPI_GED_REG_LEN);
+    memory_region_add_subregion(as, s->ged_base, &ged_st->io);
+}
+
+static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
+                                    DeviceState *dev, Error **errp)
+{
+    AcpiGedState *s = ACPI_GED(hotplug_dev);
+
+    if (s->memhp_state.is_enabled &&
+        object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+            acpi_memory_plug_cb(hotplug_dev, &s->memhp_state,
+                                dev, errp);
+    } else {
+        error_setg(errp, "virt: device plug request for unsupported device"
+                   " type: %s", object_get_typename(OBJECT(dev)));
+    }
+}
+
+static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
+{
+    AcpiGedState *s = ACPI_GED(adev);
+    uint32_t sel;
+
+    if (ev & ACPI_MEMORY_HOTPLUG_STATUS) {
+        sel = ACPI_GED_IRQ_SEL_MEM;
+    } else {
+        /* Unknown event. Return without generating interrupt. */
+        return;
+    }
+
+    /*
+     * We inject the hotplug interrupt. The IRQ selector will make
+     * the difference from the ACPI table.
+     */
+    acpi_ged_event(&s->ged_state, sel);
+}
+
+static void acpi_ged_device_realize(DeviceState *dev, Error **errp)
+{
+    AcpiGedState *s = ACPI_GED(dev);
+
+    if (s->memhp_state.is_enabled) {
+        acpi_memory_hotplug_init(get_system_memory(), OBJECT(dev),
+                                 &s->memhp_state,
+                                 s->memhp_base);
+        acpi_ged_init(get_system_memory(), dev, &s->ged_state);
+    }
+}
+
+static Property acpi_ged_properties[] = {
+    /*
+     * Memory hotplug base address is a property of GED here,
+     * because GED handles memory hotplug event and MEMORY_HOTPLUG_DEVICE
+     * gets initialized when GED device is realized.
+     */
+    DEFINE_PROP_UINT64("memhp-base", AcpiGedState, memhp_base, 0),
+    DEFINE_PROP_BOOL("memory-hotplug-support", AcpiGedState,
+                     memhp_state.is_enabled, true),
+    DEFINE_PROP_PTR("gsi", AcpiGedState, gsi),
+    DEFINE_PROP_UINT64("ged-base", AcpiGedState, ged_base, 0),
+    DEFINE_PROP_UINT32("ged-irq", AcpiGedState, ged_irq, 0),
+    DEFINE_PROP_PTR("ged-events", AcpiGedState, ged_events),
+    DEFINE_PROP_UINT32("ged-events-size", AcpiGedState, ged_events_size, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void acpi_ged_class_init(ObjectClass *class, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(class);
+    HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class);
+    AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class);
+
+    dc->desc = "ACPI";
+    dc->props = acpi_ged_properties;
+    dc->realize = acpi_ged_device_realize;
+
+    /* Reason: pointer properties "gsi" and "ged_events" */
+    dc->user_creatable = false;
+
+    hc->plug = acpi_ged_device_plug_cb;
+
+    adevc->send_event = acpi_ged_send_event;
+}
+
+static const TypeInfo acpi_ged_info = {
+    .name          = TYPE_ACPI_GED,
+    .parent        = TYPE_DEVICE,
+    .instance_size = sizeof(AcpiGedState),
+    .class_init    = acpi_ged_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_HOTPLUG_HANDLER },
+        { TYPE_ACPI_DEVICE_IF },
+        { }
+    }
+};
+
+static void acpi_ged_register_types(void)
+{
+    type_register_static(&acpi_ged_info);
+}
+
+type_init(acpi_ged_register_types)
diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h
new file mode 100644
index 0000000..9c840d8
--- /dev/null
+++ b/include/hw/acpi/generic_event_device.h
@@ -0,0 +1,121 @@
+/*
+ *
+ * Copyright (c) 2018 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * The ACPI Generic Event Device (GED) is a hardware-reduced specific
+ * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
+ * including the hotplug ones. Generic Event Device allows platforms
+ * to handle interrupts in ACPI ASL statements. It follows a very
+ * similar approach like the _EVT method from GPIO events. All
+ * interrupts are listed in  _CRS and the handler is written in _EVT
+ * method. Here, we use a single interrupt for the GED device, relying
+ * on IO memory region to communicate the type of device affected by
+ * the interrupt. This way, we can support up to 32 events with a
+ * unique interrupt.
+ *
+ * Here is an example.
+ *
+ * Device (\_SB.GED)
+ * {
+ *     Name (_HID, "ACPI0013")
+ *     Name (_UID, Zero)
+ *     Name (_CRS, ResourceTemplate ()
+ *     {
+ *         Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, )
+ *         {
+ *              0x00000029,
+ *         }
+ *     })
+ *     OperationRegion (IREG, SystemMemory, 0x09080000, 0x04)
+ *     Field (IREG, DWordAcc, NoLock, WriteAsZeros)
+ *     {
+ *         ISEL,   32
+ *     }
+ *
+ *     Method (_EVT, 1, Serialized)  // _EVT: Event
+ *     {
+ *         Local0 = ISEL // ISEL = IO memory region which specifies the
+ *                       // device type.
+ *         If (((Local0 & irq0) == irq0))
+ *         {
+ *             MethodEvent0()
+ *         }
+ *         ElseIf ((Local0 & irq1) == irq1)
+ *         {
+ *             MethodEvent1()
+ *         }
+ *         ...
+ *     }
+ * }
+ *
+ */
+
+#ifndef HW_ACPI_GED_H
+#define HW_ACPI_GED_H
+
+#include "hw/acpi/memory_hotplug.h"
+
+#define TYPE_ACPI_GED "acpi-ged"
+#define ACPI_GED(obj) \
+    OBJECT_CHECK(AcpiGedState, (obj), TYPE_ACPI_GED)
+
+#define ACPI_GED_IRQ_SEL_OFFSET 0x0
+#define ACPI_GED_IRQ_SEL_LEN    0x4
+#define ACPI_GED_IRQ_SEL_MEM    0x1
+#define ACPI_GED_REG_LEN        0x4
+
+#define GED_DEVICE      "GED"
+#define AML_GED_IRQ_REG "IREG"
+#define AML_GED_IRQ_SEL "ISEL"
+
+typedef enum {
+    GED_MEMORY_HOTPLUG = 1,
+} GedEventType;
+
+/*
+ * Platforms need to specify their own GedEvent array
+ * to describe what kind of events they want to support
+ * through GED.
+ */
+typedef struct GedEvent {
+    uint32_t     selector;
+    GedEventType event;
+} GedEvent;
+
+typedef struct GEDState {
+    MemoryRegion io;
+    uint32_t     sel;
+    uint32_t     irq;
+    qemu_irq     *gsi;
+    QemuMutex    lock;
+} GEDState;
+
+
+typedef struct AcpiGedState {
+    DeviceClass parent_obj;
+    MemHotplugState memhp_state;
+    hwaddr memhp_base;
+    void *gsi;
+    hwaddr ged_base;
+    GEDState ged_state;
+    uint32_t ged_irq;
+    void *ged_events;
+    uint32_t ged_events_size;
+} AcpiGedState;
+
+void build_ged_aml(Aml *table, const char* name, HotplugHandler *hotplug_dev,
+                   uint32_t ged_irq, AmlRegionSpace rs);
+
+#endif
-- 
2.7.4




^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [Qemu-devel] [PATCH v4 4/8] hw/arm/virt: Add memory hotplug framework
@ 2019-04-09 10:29   ` Shameer Kolothum
  0 siblings, 0 replies; 62+ messages in thread
From: Shameer Kolothum @ 2019-04-09 10:29 UTC (permalink / raw)
  To: qemu-devel, qemu-arm, eric.auger, imammedo
  Cc: peter.maydell, shannon.zhaosl, sameo, sebastien.boeuf, xuwei5,
	lersek, ard.biesheuvel, linuxarm

From: Eric Auger <eric.auger@redhat.com>

This patch adds the memory hot-plug/hot-unplug infrastructure
in machvirt. The device memory is not yet exposed to the Guest
either though DT or ACPI and hence both cold/hot plug of memory
is explicitly disabled for now.

Signed-off-by: Eric Auger <eric.auger@redhat.com>
Signed-off-by: Kwangwoo Lee <kwangwoo.lee@sk.com>
Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
---
 default-configs/arm-softmmu.mak |  3 +++
 hw/arm/virt.c                   | 45 ++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index 613d19a..9f4b803 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -160,3 +160,6 @@ CONFIG_MUSICPAL=y
 
 # for realview and versatilepb
 CONFIG_LSI_SCSI_PCI=y
+
+CONFIG_MEM_DEVICE=y
+CONFIG_DIMM=y
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index ce2664a..da516b3 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -61,6 +61,8 @@
 #include "hw/arm/smmuv3.h"
 #include "hw/acpi/acpi.h"
 #include "target/arm/internals.h"
+#include "hw/mem/pc-dimm.h"
+#include "hw/mem/nvdimm.h"
 
 #define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \
     static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
@@ -1806,6 +1808,34 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms)
     return ms->possible_cpus;
 }
 
+static void virt_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
+                                 Error **errp)
+{
+    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+        error_setg(errp, "memory cold/hot plug is not yet supported");
+        return;
+    }
+
+    pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), NULL, errp);
+}
+
+static void virt_memory_plug(HotplugHandler *hotplug_dev,
+                             DeviceState *dev, Error **errp)
+{
+    VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
+
+    pc_dimm_plug(PC_DIMM(dev), MACHINE(vms), NULL);
+
+}
+
+static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
+                                            DeviceState *dev, Error **errp)
+{
+    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+        virt_memory_pre_plug(hotplug_dev, dev, errp);
+    }
+}
+
 static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev,
                                         DeviceState *dev, Error **errp)
 {
@@ -1817,12 +1847,23 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev,
                                      SYS_BUS_DEVICE(dev));
         }
     }
+    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+        virt_memory_plug(hotplug_dev, dev, errp);
+    }
+}
+
+static void virt_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev,
+                                          DeviceState *dev, Error **errp)
+{
+    error_setg(errp, "device unplug request for unsupported device"
+               " type: %s", object_get_typename(OBJECT(dev)));
 }
 
 static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine,
                                                         DeviceState *dev)
 {
-    if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE)) {
+    if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE) ||
+       (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM))) {
         return HOTPLUG_HANDLER(machine);
     }
 
@@ -1886,7 +1927,9 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
     mc->kvm_type = virt_kvm_type;
     assert(!mc->get_hotplug_handler);
     mc->get_hotplug_handler = virt_machine_get_hotplug_handler;
+    hc->pre_plug = virt_machine_device_pre_plug_cb;
     hc->plug = virt_machine_device_plug_cb;
+    hc->unplug_request = virt_machine_device_unplug_request_cb;
 }
 
 static void virt_instance_init(Object *obj)
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [Qemu-devel] [PATCH v4 4/8] hw/arm/virt: Add memory hotplug framework
@ 2019-04-09 10:29   ` Shameer Kolothum
  0 siblings, 0 replies; 62+ messages in thread
From: Shameer Kolothum @ 2019-04-09 10:29 UTC (permalink / raw)
  To: qemu-devel, qemu-arm, eric.auger, imammedo
  Cc: peter.maydell, sameo, ard.biesheuvel, linuxarm, xuwei5,
	shannon.zhaosl, sebastien.boeuf, lersek

From: Eric Auger <eric.auger@redhat.com>

This patch adds the memory hot-plug/hot-unplug infrastructure
in machvirt. The device memory is not yet exposed to the Guest
either though DT or ACPI and hence both cold/hot plug of memory
is explicitly disabled for now.

Signed-off-by: Eric Auger <eric.auger@redhat.com>
Signed-off-by: Kwangwoo Lee <kwangwoo.lee@sk.com>
Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
---
 default-configs/arm-softmmu.mak |  3 +++
 hw/arm/virt.c                   | 45 ++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index 613d19a..9f4b803 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -160,3 +160,6 @@ CONFIG_MUSICPAL=y
 
 # for realview and versatilepb
 CONFIG_LSI_SCSI_PCI=y
+
+CONFIG_MEM_DEVICE=y
+CONFIG_DIMM=y
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index ce2664a..da516b3 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -61,6 +61,8 @@
 #include "hw/arm/smmuv3.h"
 #include "hw/acpi/acpi.h"
 #include "target/arm/internals.h"
+#include "hw/mem/pc-dimm.h"
+#include "hw/mem/nvdimm.h"
 
 #define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \
     static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
@@ -1806,6 +1808,34 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms)
     return ms->possible_cpus;
 }
 
+static void virt_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
+                                 Error **errp)
+{
+    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+        error_setg(errp, "memory cold/hot plug is not yet supported");
+        return;
+    }
+
+    pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), NULL, errp);
+}
+
+static void virt_memory_plug(HotplugHandler *hotplug_dev,
+                             DeviceState *dev, Error **errp)
+{
+    VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
+
+    pc_dimm_plug(PC_DIMM(dev), MACHINE(vms), NULL);
+
+}
+
+static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
+                                            DeviceState *dev, Error **errp)
+{
+    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+        virt_memory_pre_plug(hotplug_dev, dev, errp);
+    }
+}
+
 static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev,
                                         DeviceState *dev, Error **errp)
 {
@@ -1817,12 +1847,23 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev,
                                      SYS_BUS_DEVICE(dev));
         }
     }
+    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+        virt_memory_plug(hotplug_dev, dev, errp);
+    }
+}
+
+static void virt_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev,
+                                          DeviceState *dev, Error **errp)
+{
+    error_setg(errp, "device unplug request for unsupported device"
+               " type: %s", object_get_typename(OBJECT(dev)));
 }
 
 static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine,
                                                         DeviceState *dev)
 {
-    if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE)) {
+    if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE) ||
+       (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM))) {
         return HOTPLUG_HANDLER(machine);
     }
 
@@ -1886,7 +1927,9 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
     mc->kvm_type = virt_kvm_type;
     assert(!mc->get_hotplug_handler);
     mc->get_hotplug_handler = virt_machine_get_hotplug_handler;
+    hc->pre_plug = virt_machine_device_pre_plug_cb;
     hc->plug = virt_machine_device_plug_cb;
+    hc->unplug_request = virt_machine_device_unplug_request_cb;
 }
 
 static void virt_instance_init(Object *obj)
-- 
2.7.4




^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [Qemu-devel] [PATCH v4 5/8] hw/arm/virt: Enable device memory cold/hot plug with ACPI boot
@ 2019-04-09 10:29   ` Shameer Kolothum
  0 siblings, 0 replies; 62+ messages in thread
From: Shameer Kolothum @ 2019-04-09 10:29 UTC (permalink / raw)
  To: qemu-devel, qemu-arm, eric.auger, imammedo
  Cc: peter.maydell, shannon.zhaosl, sameo, sebastien.boeuf, xuwei5,
	lersek, ard.biesheuvel, linuxarm

This initializes the GED device with base memory and irq, configures
ged memory hotplug event and builds the corresponding aml code. GED
irq routing to Guest is also enabled. With this, both hot and cold
plug of device memory is enabled now for Guest with ACPI boot.

Memory cold plug support with Guest DT boot is not yet supported.

Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
---
 default-configs/arm-softmmu.mak |  2 ++
 hw/arm/virt-acpi-build.c        |  9 ++++++
 hw/arm/virt.c                   | 61 +++++++++++++++++++++++++++++++++++++++--
 include/hw/arm/virt.h           |  4 +++
 4 files changed, 73 insertions(+), 3 deletions(-)

diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index 9f4b803..c9a9b34 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -163,3 +163,5 @@ CONFIG_LSI_SCSI_PCI=y
 
 CONFIG_MEM_DEVICE=y
 CONFIG_DIMM=y
+CONFIG_ACPI_MEMORY_HOTPLUG=y
+CONFIG_ACPI_HW_REDUCED=y
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index bf9c0bc..1ad394b 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -40,6 +40,8 @@
 #include "hw/loader.h"
 #include "hw/hw.h"
 #include "hw/acpi/aml-build.h"
+#include "hw/acpi/memory_hotplug.h"
+#include "hw/acpi/generic_event_device.h"
 #include "hw/pci/pcie_host.h"
 #include "hw/pci/pci.h"
 #include "hw/arm/virt.h"
@@ -727,6 +729,7 @@ static void
 build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
 {
     Aml *scope, *dsdt;
+    MachineState *ms = MACHINE(vms);
     const MemMapEntry *memmap = vms->memmap;
     const int *irqmap = vms->irqmap;
 
@@ -753,6 +756,12 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
                        (irqmap[VIRT_GPIO] + ARM_SPI_BASE));
     acpi_dsdt_add_power_button(scope);
 
+    build_ged_aml(scope, "\\_SB."GED_DEVICE, HOTPLUG_HANDLER(vms->acpi_dev),
+                  irqmap[VIRT_ACPI_GED] + ARM_SPI_BASE, AML_SYSTEM_MEMORY);
+
+    build_memory_hotplug_aml(scope, ms->ram_slots, "\\_SB", NULL,
+                             AML_SYSTEM_MEMORY);
+
     aml_append(dsdt, scope);
 
     /* copy AML table into ACPI tables blob and patch header there */
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index da516b3..8179b3e 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -63,6 +63,7 @@
 #include "target/arm/internals.h"
 #include "hw/mem/pc-dimm.h"
 #include "hw/mem/nvdimm.h"
+#include "hw/acpi/generic_event_device.h"
 
 #define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \
     static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
@@ -133,6 +134,8 @@ static const MemMapEntry base_memmap[] = {
     [VIRT_GPIO] =               { 0x09030000, 0x00001000 },
     [VIRT_SECURE_UART] =        { 0x09040000, 0x00001000 },
     [VIRT_SMMU] =               { 0x09050000, 0x00020000 },
+    [VIRT_PCDIMM_ACPI] =        { 0x09070000, MEMORY_HOTPLUG_IO_LEN },
+    [VIRT_ACPI_GED] =           { 0x09080000, ACPI_GED_REG_LEN },
     [VIRT_MMIO] =               { 0x0a000000, 0x00000200 },
     /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
     [VIRT_PLATFORM_BUS] =       { 0x0c000000, 0x02000000 },
@@ -168,6 +171,7 @@ static const int a15irqmap[] = {
     [VIRT_PCIE] = 3, /* ... to 6 */
     [VIRT_GPIO] = 7,
     [VIRT_SECURE_UART] = 8,
+    [VIRT_ACPI_GED] = 9,
     [VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */
     [VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */
     [VIRT_SMMU] = 74,    /* ...to 74 + NUM_SMMU_IRQS - 1 */
@@ -183,6 +187,13 @@ static const char *valid_cpus[] = {
     ARM_CPU_TYPE_NAME("max"),
 };
 
+static GedEvent ged_events[] = {
+    {
+        .selector = ACPI_GED_IRQ_SEL_MEM,
+        .event    = GED_MEMORY_HOTPLUG,
+    },
+};
+
 static bool cpu_type_valid(const char *cpu)
 {
     int i;
@@ -516,6 +527,26 @@ static void fdt_add_pmu_nodes(const VirtMachineState *vms)
     }
 }
 
+static inline DeviceState *create_acpi_ged(VirtMachineState *vms)
+{
+    DeviceState *dev;
+
+    dev = DEVICE(object_new(TYPE_ACPI_GED));
+    qdev_prop_set_uint64(dev, "memhp-base",
+                         vms->memmap[VIRT_PCDIMM_ACPI].base);
+    qdev_prop_set_ptr(dev, "gsi", vms->gsi);
+    qdev_prop_set_uint64(dev, "ged-base", vms->memmap[VIRT_ACPI_GED].base);
+    qdev_prop_set_uint32(dev, "ged-irq", vms->irqmap[VIRT_ACPI_GED]);
+    qdev_prop_set_ptr(dev, "ged-events", ged_events);
+    qdev_prop_set_uint32(dev, "ged-events-size", ARRAY_SIZE(ged_events));
+
+    object_property_add_child(qdev_get_machine(), "acpi-ged",
+                              OBJECT(dev), NULL);
+    qdev_init_nofail(dev);
+
+    return dev;
+}
+
 static void create_its(VirtMachineState *vms, DeviceState *gicdev)
 {
     const char *itsclass = its_class_name();
@@ -555,6 +586,12 @@ static void create_v2m(VirtMachineState *vms, qemu_irq *pic)
     fdt_add_v2m_gic_node(vms);
 }
 
+static void virt_gsi_handler(void *opaque, int n, int level)
+{
+    qemu_irq *gic_irq = opaque;
+    qemu_set_irq(gic_irq[n], level);
+}
+
 static void create_gic(VirtMachineState *vms, qemu_irq *pic)
 {
     /* We create a standalone GIC */
@@ -670,6 +707,8 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic)
         pic[i] = qdev_get_gpio_in(gicdev, i);
     }
 
+    vms->gsi = qemu_allocate_irqs(virt_gsi_handler, pic, NUM_IRQS);
+
     fdt_add_gic_node(vms);
 
     if (type == 3 && vms->its) {
@@ -1418,7 +1457,7 @@ static void machvirt_init(MachineState *machine)
     VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(machine);
     MachineClass *mc = MACHINE_GET_CLASS(machine);
     const CPUArchIdList *possible_cpus;
-    qemu_irq pic[NUM_IRQS];
+    qemu_irq *pic;
     MemoryRegion *sysmem = get_system_memory();
     MemoryRegion *secure_sysmem = NULL;
     int n, virt_max_cpus;
@@ -1614,6 +1653,7 @@ static void machvirt_init(MachineState *machine)
 
     create_flash(vms, sysmem, secure_sysmem ? secure_sysmem : sysmem);
 
+    pic = g_new0(qemu_irq, NUM_IRQS);
     create_gic(vms, pic);
 
     fdt_add_pmu_nodes(vms);
@@ -1644,6 +1684,8 @@ static void machvirt_init(MachineState *machine)
 
     create_platform_bus(vms, pic);
 
+    vms->acpi_dev = create_acpi_ged(vms);
+
     vms->bootinfo.ram_size = machine->ram_size;
     vms->bootinfo.kernel_filename = machine->kernel_filename;
     vms->bootinfo.kernel_cmdline = machine->kernel_cmdline;
@@ -1811,21 +1853,34 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms)
 static void virt_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
                                  Error **errp)
 {
-    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
-        error_setg(errp, "memory cold/hot plug is not yet supported");
+    VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
+    const bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM);
+
+    if (is_nvdimm) {
+        error_setg(errp, "nvdimm is not yet supported");
         return;
     }
 
+    if (!vms->acpi_dev) {
+        error_setg(errp, "memory hotplug is not enabled: missing acpi device");
+        return;
+    }
+
+    hotplug_handler_pre_plug(HOTPLUG_HANDLER(vms->acpi_dev), dev, errp);
+
     pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), NULL, errp);
 }
 
 static void virt_memory_plug(HotplugHandler *hotplug_dev,
                              DeviceState *dev, Error **errp)
 {
+    HotplugHandlerClass *hhc;
     VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
 
     pc_dimm_plug(PC_DIMM(dev), MACHINE(vms), NULL);
 
+    hhc = HOTPLUG_HANDLER_GET_CLASS(vms->acpi_dev);
+    hhc->plug(HOTPLUG_HANDLER(vms->acpi_dev), dev, NULL);
 }
 
 static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index 507517c..9c6b0b1 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -77,6 +77,8 @@ enum {
     VIRT_GPIO,
     VIRT_SECURE_UART,
     VIRT_SECURE_MEM,
+    VIRT_PCDIMM_ACPI,
+    VIRT_ACPI_GED,
     VIRT_LOWMEMMAP_LAST,
 };
 
@@ -132,6 +134,8 @@ typedef struct {
     uint32_t iommu_phandle;
     int psci_conduit;
     hwaddr highest_gpa;
+    DeviceState *acpi_dev;
+    qemu_irq *gsi;
 } VirtMachineState;
 
 #define VIRT_ECAM_ID(high) (high ? VIRT_HIGH_PCIE_ECAM : VIRT_PCIE_ECAM)
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [Qemu-devel] [PATCH v4 5/8] hw/arm/virt: Enable device memory cold/hot plug with ACPI boot
@ 2019-04-09 10:29   ` Shameer Kolothum
  0 siblings, 0 replies; 62+ messages in thread
From: Shameer Kolothum @ 2019-04-09 10:29 UTC (permalink / raw)
  To: qemu-devel, qemu-arm, eric.auger, imammedo
  Cc: peter.maydell, sameo, ard.biesheuvel, linuxarm, xuwei5,
	shannon.zhaosl, sebastien.boeuf, lersek

This initializes the GED device with base memory and irq, configures
ged memory hotplug event and builds the corresponding aml code. GED
irq routing to Guest is also enabled. With this, both hot and cold
plug of device memory is enabled now for Guest with ACPI boot.

Memory cold plug support with Guest DT boot is not yet supported.

Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
---
 default-configs/arm-softmmu.mak |  2 ++
 hw/arm/virt-acpi-build.c        |  9 ++++++
 hw/arm/virt.c                   | 61 +++++++++++++++++++++++++++++++++++++++--
 include/hw/arm/virt.h           |  4 +++
 4 files changed, 73 insertions(+), 3 deletions(-)

diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index 9f4b803..c9a9b34 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -163,3 +163,5 @@ CONFIG_LSI_SCSI_PCI=y
 
 CONFIG_MEM_DEVICE=y
 CONFIG_DIMM=y
+CONFIG_ACPI_MEMORY_HOTPLUG=y
+CONFIG_ACPI_HW_REDUCED=y
diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index bf9c0bc..1ad394b 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -40,6 +40,8 @@
 #include "hw/loader.h"
 #include "hw/hw.h"
 #include "hw/acpi/aml-build.h"
+#include "hw/acpi/memory_hotplug.h"
+#include "hw/acpi/generic_event_device.h"
 #include "hw/pci/pcie_host.h"
 #include "hw/pci/pci.h"
 #include "hw/arm/virt.h"
@@ -727,6 +729,7 @@ static void
 build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
 {
     Aml *scope, *dsdt;
+    MachineState *ms = MACHINE(vms);
     const MemMapEntry *memmap = vms->memmap;
     const int *irqmap = vms->irqmap;
 
@@ -753,6 +756,12 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
                        (irqmap[VIRT_GPIO] + ARM_SPI_BASE));
     acpi_dsdt_add_power_button(scope);
 
+    build_ged_aml(scope, "\\_SB."GED_DEVICE, HOTPLUG_HANDLER(vms->acpi_dev),
+                  irqmap[VIRT_ACPI_GED] + ARM_SPI_BASE, AML_SYSTEM_MEMORY);
+
+    build_memory_hotplug_aml(scope, ms->ram_slots, "\\_SB", NULL,
+                             AML_SYSTEM_MEMORY);
+
     aml_append(dsdt, scope);
 
     /* copy AML table into ACPI tables blob and patch header there */
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index da516b3..8179b3e 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -63,6 +63,7 @@
 #include "target/arm/internals.h"
 #include "hw/mem/pc-dimm.h"
 #include "hw/mem/nvdimm.h"
+#include "hw/acpi/generic_event_device.h"
 
 #define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \
     static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
@@ -133,6 +134,8 @@ static const MemMapEntry base_memmap[] = {
     [VIRT_GPIO] =               { 0x09030000, 0x00001000 },
     [VIRT_SECURE_UART] =        { 0x09040000, 0x00001000 },
     [VIRT_SMMU] =               { 0x09050000, 0x00020000 },
+    [VIRT_PCDIMM_ACPI] =        { 0x09070000, MEMORY_HOTPLUG_IO_LEN },
+    [VIRT_ACPI_GED] =           { 0x09080000, ACPI_GED_REG_LEN },
     [VIRT_MMIO] =               { 0x0a000000, 0x00000200 },
     /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
     [VIRT_PLATFORM_BUS] =       { 0x0c000000, 0x02000000 },
@@ -168,6 +171,7 @@ static const int a15irqmap[] = {
     [VIRT_PCIE] = 3, /* ... to 6 */
     [VIRT_GPIO] = 7,
     [VIRT_SECURE_UART] = 8,
+    [VIRT_ACPI_GED] = 9,
     [VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */
     [VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */
     [VIRT_SMMU] = 74,    /* ...to 74 + NUM_SMMU_IRQS - 1 */
@@ -183,6 +187,13 @@ static const char *valid_cpus[] = {
     ARM_CPU_TYPE_NAME("max"),
 };
 
+static GedEvent ged_events[] = {
+    {
+        .selector = ACPI_GED_IRQ_SEL_MEM,
+        .event    = GED_MEMORY_HOTPLUG,
+    },
+};
+
 static bool cpu_type_valid(const char *cpu)
 {
     int i;
@@ -516,6 +527,26 @@ static void fdt_add_pmu_nodes(const VirtMachineState *vms)
     }
 }
 
+static inline DeviceState *create_acpi_ged(VirtMachineState *vms)
+{
+    DeviceState *dev;
+
+    dev = DEVICE(object_new(TYPE_ACPI_GED));
+    qdev_prop_set_uint64(dev, "memhp-base",
+                         vms->memmap[VIRT_PCDIMM_ACPI].base);
+    qdev_prop_set_ptr(dev, "gsi", vms->gsi);
+    qdev_prop_set_uint64(dev, "ged-base", vms->memmap[VIRT_ACPI_GED].base);
+    qdev_prop_set_uint32(dev, "ged-irq", vms->irqmap[VIRT_ACPI_GED]);
+    qdev_prop_set_ptr(dev, "ged-events", ged_events);
+    qdev_prop_set_uint32(dev, "ged-events-size", ARRAY_SIZE(ged_events));
+
+    object_property_add_child(qdev_get_machine(), "acpi-ged",
+                              OBJECT(dev), NULL);
+    qdev_init_nofail(dev);
+
+    return dev;
+}
+
 static void create_its(VirtMachineState *vms, DeviceState *gicdev)
 {
     const char *itsclass = its_class_name();
@@ -555,6 +586,12 @@ static void create_v2m(VirtMachineState *vms, qemu_irq *pic)
     fdt_add_v2m_gic_node(vms);
 }
 
+static void virt_gsi_handler(void *opaque, int n, int level)
+{
+    qemu_irq *gic_irq = opaque;
+    qemu_set_irq(gic_irq[n], level);
+}
+
 static void create_gic(VirtMachineState *vms, qemu_irq *pic)
 {
     /* We create a standalone GIC */
@@ -670,6 +707,8 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic)
         pic[i] = qdev_get_gpio_in(gicdev, i);
     }
 
+    vms->gsi = qemu_allocate_irqs(virt_gsi_handler, pic, NUM_IRQS);
+
     fdt_add_gic_node(vms);
 
     if (type == 3 && vms->its) {
@@ -1418,7 +1457,7 @@ static void machvirt_init(MachineState *machine)
     VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(machine);
     MachineClass *mc = MACHINE_GET_CLASS(machine);
     const CPUArchIdList *possible_cpus;
-    qemu_irq pic[NUM_IRQS];
+    qemu_irq *pic;
     MemoryRegion *sysmem = get_system_memory();
     MemoryRegion *secure_sysmem = NULL;
     int n, virt_max_cpus;
@@ -1614,6 +1653,7 @@ static void machvirt_init(MachineState *machine)
 
     create_flash(vms, sysmem, secure_sysmem ? secure_sysmem : sysmem);
 
+    pic = g_new0(qemu_irq, NUM_IRQS);
     create_gic(vms, pic);
 
     fdt_add_pmu_nodes(vms);
@@ -1644,6 +1684,8 @@ static void machvirt_init(MachineState *machine)
 
     create_platform_bus(vms, pic);
 
+    vms->acpi_dev = create_acpi_ged(vms);
+
     vms->bootinfo.ram_size = machine->ram_size;
     vms->bootinfo.kernel_filename = machine->kernel_filename;
     vms->bootinfo.kernel_cmdline = machine->kernel_cmdline;
@@ -1811,21 +1853,34 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms)
 static void virt_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
                                  Error **errp)
 {
-    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
-        error_setg(errp, "memory cold/hot plug is not yet supported");
+    VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
+    const bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM);
+
+    if (is_nvdimm) {
+        error_setg(errp, "nvdimm is not yet supported");
         return;
     }
 
+    if (!vms->acpi_dev) {
+        error_setg(errp, "memory hotplug is not enabled: missing acpi device");
+        return;
+    }
+
+    hotplug_handler_pre_plug(HOTPLUG_HANDLER(vms->acpi_dev), dev, errp);
+
     pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), NULL, errp);
 }
 
 static void virt_memory_plug(HotplugHandler *hotplug_dev,
                              DeviceState *dev, Error **errp)
 {
+    HotplugHandlerClass *hhc;
     VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
 
     pc_dimm_plug(PC_DIMM(dev), MACHINE(vms), NULL);
 
+    hhc = HOTPLUG_HANDLER_GET_CLASS(vms->acpi_dev);
+    hhc->plug(HOTPLUG_HANDLER(vms->acpi_dev), dev, NULL);
 }
 
 static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index 507517c..9c6b0b1 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -77,6 +77,8 @@ enum {
     VIRT_GPIO,
     VIRT_SECURE_UART,
     VIRT_SECURE_MEM,
+    VIRT_PCDIMM_ACPI,
+    VIRT_ACPI_GED,
     VIRT_LOWMEMMAP_LAST,
 };
 
@@ -132,6 +134,8 @@ typedef struct {
     uint32_t iommu_phandle;
     int psci_conduit;
     hwaddr highest_gpa;
+    DeviceState *acpi_dev;
+    qemu_irq *gsi;
 } VirtMachineState;
 
 #define VIRT_ECAM_ID(high) (high ? VIRT_HIGH_PCIE_ECAM : VIRT_PCIE_ECAM)
-- 
2.7.4




^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [Qemu-devel] [PATCH v4 6/8] hw/arm/virt-acpi-build: Add PC-DIMM in SRAT
@ 2019-04-09 10:29   ` Shameer Kolothum
  0 siblings, 0 replies; 62+ messages in thread
From: Shameer Kolothum @ 2019-04-09 10:29 UTC (permalink / raw)
  To: qemu-devel, qemu-arm, eric.auger, imammedo
  Cc: peter.maydell, shannon.zhaosl, sameo, sebastien.boeuf, xuwei5,
	lersek, ard.biesheuvel, linuxarm

Generate Memory Affinity Structures for PC-DIMM ranges.

Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
Signed-off-by: Eric Auger <eric.auger@redhat.com>
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
---
 hw/arm/virt-acpi-build.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index 1ad394b..1b9a9b9 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -518,6 +518,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
     int i, srat_start;
     uint64_t mem_base;
     MachineClass *mc = MACHINE_GET_CLASS(vms);
+    MachineState *ms = MACHINE(vms);
     const CPUArchIdList *cpu_list = mc->possible_cpu_arch_ids(MACHINE(vms));
 
     srat_start = table_data->len;
@@ -543,6 +544,14 @@ build_srat(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
         }
     }
 
+    if (ms->device_memory) {
+        numamem = acpi_data_push(table_data, sizeof *numamem);
+        build_srat_memory(numamem, ms->device_memory->base,
+                          memory_region_size(&ms->device_memory->mr),
+                          nb_numa_nodes - 1,
+                          MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED);
+    }
+
     build_header(linker, table_data, (void *)(table_data->data + srat_start),
                  "SRAT", table_data->len - srat_start, 3, NULL, NULL);
 }
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [Qemu-devel] [PATCH v4 6/8] hw/arm/virt-acpi-build: Add PC-DIMM in SRAT
@ 2019-04-09 10:29   ` Shameer Kolothum
  0 siblings, 0 replies; 62+ messages in thread
From: Shameer Kolothum @ 2019-04-09 10:29 UTC (permalink / raw)
  To: qemu-devel, qemu-arm, eric.auger, imammedo
  Cc: peter.maydell, sameo, ard.biesheuvel, linuxarm, xuwei5,
	shannon.zhaosl, sebastien.boeuf, lersek

Generate Memory Affinity Structures for PC-DIMM ranges.

Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
Signed-off-by: Eric Auger <eric.auger@redhat.com>
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
---
 hw/arm/virt-acpi-build.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
index 1ad394b..1b9a9b9 100644
--- a/hw/arm/virt-acpi-build.c
+++ b/hw/arm/virt-acpi-build.c
@@ -518,6 +518,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
     int i, srat_start;
     uint64_t mem_base;
     MachineClass *mc = MACHINE_GET_CLASS(vms);
+    MachineState *ms = MACHINE(vms);
     const CPUArchIdList *cpu_list = mc->possible_cpu_arch_ids(MACHINE(vms));
 
     srat_start = table_data->len;
@@ -543,6 +544,14 @@ build_srat(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
         }
     }
 
+    if (ms->device_memory) {
+        numamem = acpi_data_push(table_data, sizeof *numamem);
+        build_srat_memory(numamem, ms->device_memory->base,
+                          memory_region_size(&ms->device_memory->mr),
+                          nb_numa_nodes - 1,
+                          MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED);
+    }
+
     build_header(linker, table_data, (void *)(table_data->data + srat_start),
                  "SRAT", table_data->len - srat_start, 3, NULL, NULL);
 }
-- 
2.7.4




^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [Qemu-devel] [PATCH v4 7/8] hw/arm/boot: Add "hotpluggable" property to DT memory node
@ 2019-04-09 10:29   ` Shameer Kolothum
  0 siblings, 0 replies; 62+ messages in thread
From: Shameer Kolothum @ 2019-04-09 10:29 UTC (permalink / raw)
  To: qemu-devel, qemu-arm, eric.auger, imammedo
  Cc: peter.maydell, shannon.zhaosl, sameo, sebastien.boeuf, xuwei5,
	lersek, ard.biesheuvel, linuxarm

Currently firmware has no way of differentiating the memory
nodes and as a result device memory nodes might get exposed
to Guest Kernel via UEFI GetMemoryMap() as early boot memory
even if Guest boots with ACPI. This will prevent hot-unplug
of the device memory. To address this, an additional
"hotpluggable" property is introduced here similar to
the MEM_AFFINITY_HOTPLUGGABLE flag used in ACPI SRAT table.

Firmware can now check for "hotpluggable" property in the
memory node and treat them differently.

This is in preparation of exposing device memory nodes in DT.

Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
---
 hw/arm/boot.c | 22 +++++++++++++++++++---
 1 file changed, 19 insertions(+), 3 deletions(-)

diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index a830655..8c840ba 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -425,7 +425,7 @@ static void set_kernel_args_old(const struct arm_boot_info *info,
 
 static int fdt_add_memory_node(void *fdt, uint32_t acells, hwaddr mem_base,
                                uint32_t scells, hwaddr mem_len,
-                               int numa_node_id)
+                               int numa_node_id, bool hotplug)
 {
     char *nodename;
     int ret;
@@ -444,6 +444,22 @@ static int fdt_add_memory_node(void *fdt, uint32_t acells, hwaddr mem_base,
         ret = qemu_fdt_setprop_cell(fdt, nodename,
                                     "numa-node-id", numa_node_id);
     }
+
+    /*
+     * Firmware has no way of differentiating the memory nodes currently
+     * and in the case of device memory nodes this is a problem if Guest
+     * boots with ACPI as these nodes might get exposed to Guest Kernel
+     * via UEFI GetMemoryMap() as early boot memory. This will prevent
+     * the memory being hot-unpluggable. To address this, an additional
+     * "hotpluggable" property is introduced here, similar to the
+     * MEM_AFFINITY_HOTPLUGGABLE flag used in ACPI SRAT table.
+     * Firmware can now check for "hotpluggable" property in the memory
+     * node and treat them differently.
+     */
+
+    if (hotplug) {
+        ret = qemu_fdt_setprop(fdt, nodename, "hotpluggable", NULL, 0);
+    }
 out:
     g_free(nodename);
     return ret;
@@ -602,7 +618,7 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
         for (i = 0; i < nb_numa_nodes; i++) {
             mem_len = numa_info[i].node_mem;
             rc = fdt_add_memory_node(fdt, acells, mem_base,
-                                     scells, mem_len, i);
+                                     scells, mem_len, i, false);
             if (rc < 0) {
                 fprintf(stderr, "couldn't add /memory@%"PRIx64" node\n",
                         mem_base);
@@ -613,7 +629,7 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
         }
     } else {
         rc = fdt_add_memory_node(fdt, acells, binfo->loader_start,
-                                 scells, binfo->ram_size, -1);
+                                 scells, binfo->ram_size, -1, false);
         if (rc < 0) {
             fprintf(stderr, "couldn't add /memory@%"PRIx64" node\n",
                     binfo->loader_start);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [Qemu-devel] [PATCH v4 7/8] hw/arm/boot: Add "hotpluggable" property to DT memory node
@ 2019-04-09 10:29   ` Shameer Kolothum
  0 siblings, 0 replies; 62+ messages in thread
From: Shameer Kolothum @ 2019-04-09 10:29 UTC (permalink / raw)
  To: qemu-devel, qemu-arm, eric.auger, imammedo
  Cc: peter.maydell, sameo, ard.biesheuvel, linuxarm, xuwei5,
	shannon.zhaosl, sebastien.boeuf, lersek

Currently firmware has no way of differentiating the memory
nodes and as a result device memory nodes might get exposed
to Guest Kernel via UEFI GetMemoryMap() as early boot memory
even if Guest boots with ACPI. This will prevent hot-unplug
of the device memory. To address this, an additional
"hotpluggable" property is introduced here similar to
the MEM_AFFINITY_HOTPLUGGABLE flag used in ACPI SRAT table.

Firmware can now check for "hotpluggable" property in the
memory node and treat them differently.

This is in preparation of exposing device memory nodes in DT.

Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
---
 hw/arm/boot.c | 22 +++++++++++++++++++---
 1 file changed, 19 insertions(+), 3 deletions(-)

diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index a830655..8c840ba 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -425,7 +425,7 @@ static void set_kernel_args_old(const struct arm_boot_info *info,
 
 static int fdt_add_memory_node(void *fdt, uint32_t acells, hwaddr mem_base,
                                uint32_t scells, hwaddr mem_len,
-                               int numa_node_id)
+                               int numa_node_id, bool hotplug)
 {
     char *nodename;
     int ret;
@@ -444,6 +444,22 @@ static int fdt_add_memory_node(void *fdt, uint32_t acells, hwaddr mem_base,
         ret = qemu_fdt_setprop_cell(fdt, nodename,
                                     "numa-node-id", numa_node_id);
     }
+
+    /*
+     * Firmware has no way of differentiating the memory nodes currently
+     * and in the case of device memory nodes this is a problem if Guest
+     * boots with ACPI as these nodes might get exposed to Guest Kernel
+     * via UEFI GetMemoryMap() as early boot memory. This will prevent
+     * the memory being hot-unpluggable. To address this, an additional
+     * "hotpluggable" property is introduced here, similar to the
+     * MEM_AFFINITY_HOTPLUGGABLE flag used in ACPI SRAT table.
+     * Firmware can now check for "hotpluggable" property in the memory
+     * node and treat them differently.
+     */
+
+    if (hotplug) {
+        ret = qemu_fdt_setprop(fdt, nodename, "hotpluggable", NULL, 0);
+    }
 out:
     g_free(nodename);
     return ret;
@@ -602,7 +618,7 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
         for (i = 0; i < nb_numa_nodes; i++) {
             mem_len = numa_info[i].node_mem;
             rc = fdt_add_memory_node(fdt, acells, mem_base,
-                                     scells, mem_len, i);
+                                     scells, mem_len, i, false);
             if (rc < 0) {
                 fprintf(stderr, "couldn't add /memory@%"PRIx64" node\n",
                         mem_base);
@@ -613,7 +629,7 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
         }
     } else {
         rc = fdt_add_memory_node(fdt, acells, binfo->loader_start,
-                                 scells, binfo->ram_size, -1);
+                                 scells, binfo->ram_size, -1, false);
         if (rc < 0) {
             fprintf(stderr, "couldn't add /memory@%"PRIx64" node\n",
                     binfo->loader_start);
-- 
2.7.4




^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [Qemu-devel] [PATCH v4 8/8] hw/arm/boot: Expose the PC-DIMM nodes in the DT
@ 2019-04-09 10:29   ` Shameer Kolothum
  0 siblings, 0 replies; 62+ messages in thread
From: Shameer Kolothum @ 2019-04-09 10:29 UTC (permalink / raw)
  To: qemu-devel, qemu-arm, eric.auger, imammedo
  Cc: peter.maydell, shannon.zhaosl, sameo, sebastien.boeuf, xuwei5,
	lersek, ard.biesheuvel, linuxarm

This patch adds memory nodes corresponding to PC-DIMM regions.
This will enable support for cold plugged device memory for Guests
with DT boot.

Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
Signed-off-by: Eric Auger <eric.auger@redhat.com>
---
 hw/arm/boot.c | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index 8c840ba..150e1ed 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -19,6 +19,7 @@
 #include "sysemu/numa.h"
 #include "hw/boards.h"
 #include "hw/loader.h"
+#include "hw/mem/memory-device.h"
 #include "elf.h"
 #include "sysemu/device_tree.h"
 #include "qemu/config-file.h"
@@ -538,6 +539,41 @@ static void fdt_add_psci_node(void *fdt)
     qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn);
 }
 
+static int fdt_add_hotpluggable_memory_nodes(void *fdt,
+                                             uint32_t acells, uint32_t scells) {
+    MemoryDeviceInfoList *info, *info_list = qmp_memory_device_list();
+    MemoryDeviceInfo *mi;
+    int ret = 0;
+
+    for (info = info_list; info != NULL; info = info->next) {
+        mi = info->value;
+        switch (mi->type) {
+        case MEMORY_DEVICE_INFO_KIND_DIMM:
+        {
+            PCDIMMDeviceInfo *di = mi->u.dimm.data;
+
+            ret = fdt_add_memory_node(fdt, acells, di->addr, scells,
+                                      di->size, di->node, true);
+            if (ret) {
+                fprintf(stderr,
+                        "couldn't add PCDIMM /memory@%"PRIx64" node\n",
+                        di->addr);
+                goto out;
+            }
+            break;
+        }
+        default:
+            fprintf(stderr, "%s memory nodes are not yet supported\n",
+                    MemoryDeviceInfoKind_str(mi->type));
+            ret = -ENOENT;
+            goto out;
+        }
+    }
+out:
+    qapi_free_MemoryDeviceInfoList(info_list);
+    return ret;
+}
+
 int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
                  hwaddr addr_limit, AddressSpace *as)
 {
@@ -637,6 +673,12 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
         }
     }
 
+    rc = fdt_add_hotpluggable_memory_nodes(fdt, acells, scells);
+    if (rc < 0) {
+        fprintf(stderr, "couldn't add hotpluggable memory nodes\n");
+        goto fail;
+    }
+
     rc = fdt_path_offset(fdt, "/chosen");
     if (rc < 0) {
         qemu_fdt_add_subnode(fdt, "/chosen");
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [Qemu-devel] [PATCH v4 8/8] hw/arm/boot: Expose the PC-DIMM nodes in the DT
@ 2019-04-09 10:29   ` Shameer Kolothum
  0 siblings, 0 replies; 62+ messages in thread
From: Shameer Kolothum @ 2019-04-09 10:29 UTC (permalink / raw)
  To: qemu-devel, qemu-arm, eric.auger, imammedo
  Cc: peter.maydell, sameo, ard.biesheuvel, linuxarm, xuwei5,
	shannon.zhaosl, sebastien.boeuf, lersek

This patch adds memory nodes corresponding to PC-DIMM regions.
This will enable support for cold plugged device memory for Guests
with DT boot.

Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
Signed-off-by: Eric Auger <eric.auger@redhat.com>
---
 hw/arm/boot.c | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index 8c840ba..150e1ed 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -19,6 +19,7 @@
 #include "sysemu/numa.h"
 #include "hw/boards.h"
 #include "hw/loader.h"
+#include "hw/mem/memory-device.h"
 #include "elf.h"
 #include "sysemu/device_tree.h"
 #include "qemu/config-file.h"
@@ -538,6 +539,41 @@ static void fdt_add_psci_node(void *fdt)
     qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn);
 }
 
+static int fdt_add_hotpluggable_memory_nodes(void *fdt,
+                                             uint32_t acells, uint32_t scells) {
+    MemoryDeviceInfoList *info, *info_list = qmp_memory_device_list();
+    MemoryDeviceInfo *mi;
+    int ret = 0;
+
+    for (info = info_list; info != NULL; info = info->next) {
+        mi = info->value;
+        switch (mi->type) {
+        case MEMORY_DEVICE_INFO_KIND_DIMM:
+        {
+            PCDIMMDeviceInfo *di = mi->u.dimm.data;
+
+            ret = fdt_add_memory_node(fdt, acells, di->addr, scells,
+                                      di->size, di->node, true);
+            if (ret) {
+                fprintf(stderr,
+                        "couldn't add PCDIMM /memory@%"PRIx64" node\n",
+                        di->addr);
+                goto out;
+            }
+            break;
+        }
+        default:
+            fprintf(stderr, "%s memory nodes are not yet supported\n",
+                    MemoryDeviceInfoKind_str(mi->type));
+            ret = -ENOENT;
+            goto out;
+        }
+    }
+out:
+    qapi_free_MemoryDeviceInfoList(info_list);
+    return ret;
+}
+
 int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
                  hwaddr addr_limit, AddressSpace *as)
 {
@@ -637,6 +673,12 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
         }
     }
 
+    rc = fdt_add_hotpluggable_memory_nodes(fdt, acells, scells);
+    if (rc < 0) {
+        fprintf(stderr, "couldn't add hotpluggable memory nodes\n");
+        goto fail;
+    }
+
     rc = fdt_path_offset(fdt, "/chosen");
     if (rc < 0) {
         qemu_fdt_add_subnode(fdt, "/chosen");
-- 
2.7.4




^ permalink raw reply related	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 8/8] hw/arm/boot: Expose the PC-DIMM nodes in the DT
@ 2019-04-09 15:08     ` Laszlo Ersek
  0 siblings, 0 replies; 62+ messages in thread
From: Laszlo Ersek @ 2019-04-09 15:08 UTC (permalink / raw)
  To: Shameer Kolothum, qemu-devel, qemu-arm, eric.auger, imammedo
  Cc: peter.maydell, shannon.zhaosl, sameo, sebastien.boeuf, xuwei5,
	ard.biesheuvel, linuxarm

On 04/09/19 12:29, Shameer Kolothum wrote:
> This patch adds memory nodes corresponding to PC-DIMM regions.
> This will enable support for cold plugged device memory for Guests
> with DT boot.
> 
> Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> Signed-off-by: Eric Auger <eric.auger@redhat.com>
> ---
>  hw/arm/boot.c | 42 ++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 42 insertions(+)
> 
> diff --git a/hw/arm/boot.c b/hw/arm/boot.c
> index 8c840ba..150e1ed 100644
> --- a/hw/arm/boot.c
> +++ b/hw/arm/boot.c
> @@ -19,6 +19,7 @@
>  #include "sysemu/numa.h"
>  #include "hw/boards.h"
>  #include "hw/loader.h"
> +#include "hw/mem/memory-device.h"
>  #include "elf.h"
>  #include "sysemu/device_tree.h"
>  #include "qemu/config-file.h"
> @@ -538,6 +539,41 @@ static void fdt_add_psci_node(void *fdt)
>      qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn);
>  }
>  
> +static int fdt_add_hotpluggable_memory_nodes(void *fdt,
> +                                             uint32_t acells, uint32_t scells) {
> +    MemoryDeviceInfoList *info, *info_list = qmp_memory_device_list();
> +    MemoryDeviceInfo *mi;
> +    int ret = 0;
> +
> +    for (info = info_list; info != NULL; info = info->next) {
> +        mi = info->value;
> +        switch (mi->type) {
> +        case MEMORY_DEVICE_INFO_KIND_DIMM:
> +        {
> +            PCDIMMDeviceInfo *di = mi->u.dimm.data;
> +
> +            ret = fdt_add_memory_node(fdt, acells, di->addr, scells,
> +                                      di->size, di->node, true);
> +            if (ret) {
> +                fprintf(stderr,
> +                        "couldn't add PCDIMM /memory@%"PRIx64" node\n",
> +                        di->addr);
> +                goto out;
> +            }
> +            break;
> +        }
> +        default:
> +            fprintf(stderr, "%s memory nodes are not yet supported\n",
> +                    MemoryDeviceInfoKind_str(mi->type));
> +            ret = -ENOENT;
> +            goto out;
> +        }
> +    }
> +out:
> +    qapi_free_MemoryDeviceInfoList(info_list);
> +    return ret;
> +}
> +
>  int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
>                   hwaddr addr_limit, AddressSpace *as)
>  {
> @@ -637,6 +673,12 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
>          }
>      }
>  
> +    rc = fdt_add_hotpluggable_memory_nodes(fdt, acells, scells);
> +    if (rc < 0) {
> +        fprintf(stderr, "couldn't add hotpluggable memory nodes\n");
> +        goto fail;
> +    }
> +
>      rc = fdt_path_offset(fdt, "/chosen");
>      if (rc < 0) {
>          qemu_fdt_add_subnode(fdt, "/chosen");
> 


Given patches #7 and #8, as I understand them, the firmware cannot distinguish hotpluggable & present, from hotpluggable & absent. The firmware can only skip both hotpluggable cases. That's fine in that the firmware will hog neither type -- but is that OK for the OS as well, for both ACPI boot and DT boot?

Consider in particular the "hotpluggable & present, ACPI boot" case. Assuming we modify the firmware to skip "hotpluggable" altogether, the UEFI memmap will not include the range despite it being present at boot. Presumably, ACPI will refer to the range somehow, however. Will that not confuse the OS?

When Igor raised this earlier, I suggested that hotpluggable-and-present should be added by the firmware, but also allocated immediately, as EfiBootServicesData type memory. This will prevent other drivers in the firmware from allocating AcpiNVS or Reserved chunks from the same memory range, the UEFI memmap will contain the range as EfiBootServicesData, and then the OS can release that allocation in one go early during boot.

But this really has to be clarified from the Linux kernel's expectations. Please formalize all of the following cases:

OS boot (DT/ACPI)  hotpluggable & ...  GetMemoryMap() should report as  DT/ACPI should report as
-----------------  ------------------  -------------------------------  ------------------------
DT                 present             ?                                ?
DT                 absent              ?                                ?
ACPI               present             ?                                ?
ACPI               absent              ?                                ?

Again, this table is dictated by Linux.

Thanks
Laszlo

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 8/8] hw/arm/boot: Expose the PC-DIMM nodes in the DT
@ 2019-04-09 15:08     ` Laszlo Ersek
  0 siblings, 0 replies; 62+ messages in thread
From: Laszlo Ersek @ 2019-04-09 15:08 UTC (permalink / raw)
  To: Shameer Kolothum, qemu-devel, qemu-arm, eric.auger, imammedo
  Cc: peter.maydell, sameo, ard.biesheuvel, linuxarm, xuwei5,
	shannon.zhaosl, sebastien.boeuf

On 04/09/19 12:29, Shameer Kolothum wrote:
> This patch adds memory nodes corresponding to PC-DIMM regions.
> This will enable support for cold plugged device memory for Guests
> with DT boot.
> 
> Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> Signed-off-by: Eric Auger <eric.auger@redhat.com>
> ---
>  hw/arm/boot.c | 42 ++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 42 insertions(+)
> 
> diff --git a/hw/arm/boot.c b/hw/arm/boot.c
> index 8c840ba..150e1ed 100644
> --- a/hw/arm/boot.c
> +++ b/hw/arm/boot.c
> @@ -19,6 +19,7 @@
>  #include "sysemu/numa.h"
>  #include "hw/boards.h"
>  #include "hw/loader.h"
> +#include "hw/mem/memory-device.h"
>  #include "elf.h"
>  #include "sysemu/device_tree.h"
>  #include "qemu/config-file.h"
> @@ -538,6 +539,41 @@ static void fdt_add_psci_node(void *fdt)
>      qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn);
>  }
>  
> +static int fdt_add_hotpluggable_memory_nodes(void *fdt,
> +                                             uint32_t acells, uint32_t scells) {
> +    MemoryDeviceInfoList *info, *info_list = qmp_memory_device_list();
> +    MemoryDeviceInfo *mi;
> +    int ret = 0;
> +
> +    for (info = info_list; info != NULL; info = info->next) {
> +        mi = info->value;
> +        switch (mi->type) {
> +        case MEMORY_DEVICE_INFO_KIND_DIMM:
> +        {
> +            PCDIMMDeviceInfo *di = mi->u.dimm.data;
> +
> +            ret = fdt_add_memory_node(fdt, acells, di->addr, scells,
> +                                      di->size, di->node, true);
> +            if (ret) {
> +                fprintf(stderr,
> +                        "couldn't add PCDIMM /memory@%"PRIx64" node\n",
> +                        di->addr);
> +                goto out;
> +            }
> +            break;
> +        }
> +        default:
> +            fprintf(stderr, "%s memory nodes are not yet supported\n",
> +                    MemoryDeviceInfoKind_str(mi->type));
> +            ret = -ENOENT;
> +            goto out;
> +        }
> +    }
> +out:
> +    qapi_free_MemoryDeviceInfoList(info_list);
> +    return ret;
> +}
> +
>  int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
>                   hwaddr addr_limit, AddressSpace *as)
>  {
> @@ -637,6 +673,12 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
>          }
>      }
>  
> +    rc = fdt_add_hotpluggable_memory_nodes(fdt, acells, scells);
> +    if (rc < 0) {
> +        fprintf(stderr, "couldn't add hotpluggable memory nodes\n");
> +        goto fail;
> +    }
> +
>      rc = fdt_path_offset(fdt, "/chosen");
>      if (rc < 0) {
>          qemu_fdt_add_subnode(fdt, "/chosen");
> 


Given patches #7 and #8, as I understand them, the firmware cannot distinguish hotpluggable & present, from hotpluggable & absent. The firmware can only skip both hotpluggable cases. That's fine in that the firmware will hog neither type -- but is that OK for the OS as well, for both ACPI boot and DT boot?

Consider in particular the "hotpluggable & present, ACPI boot" case. Assuming we modify the firmware to skip "hotpluggable" altogether, the UEFI memmap will not include the range despite it being present at boot. Presumably, ACPI will refer to the range somehow, however. Will that not confuse the OS?

When Igor raised this earlier, I suggested that hotpluggable-and-present should be added by the firmware, but also allocated immediately, as EfiBootServicesData type memory. This will prevent other drivers in the firmware from allocating AcpiNVS or Reserved chunks from the same memory range, the UEFI memmap will contain the range as EfiBootServicesData, and then the OS can release that allocation in one go early during boot.

But this really has to be clarified from the Linux kernel's expectations. Please formalize all of the following cases:

OS boot (DT/ACPI)  hotpluggable & ...  GetMemoryMap() should report as  DT/ACPI should report as
-----------------  ------------------  -------------------------------  ------------------------
DT                 present             ?                                ?
DT                 absent              ?                                ?
ACPI               present             ?                                ?
ACPI               absent              ?                                ?

Again, this table is dictated by Linux.

Thanks
Laszlo


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 8/8] hw/arm/boot: Expose the PC-DIMM nodes in the DT
@ 2019-04-10  8:49       ` Shameerali Kolothum Thodi
  0 siblings, 0 replies; 62+ messages in thread
From: Shameerali Kolothum Thodi @ 2019-04-10  8:49 UTC (permalink / raw)
  To: Laszlo Ersek, qemu-devel, qemu-arm, eric.auger, imammedo
  Cc: peter.maydell, shannon.zhaosl, sameo, sebastien.boeuf, xuwei (O),
	ard.biesheuvel, Linuxarm


> -----Original Message-----
> From: Laszlo Ersek [mailto:lersek@redhat.com]
> Sent: 09 April 2019 16:09
> To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>;
> qemu-devel@nongnu.org; qemu-arm@nongnu.org; eric.auger@redhat.com;
> imammedo@redhat.com
> Cc: peter.maydell@linaro.org; shannon.zhaosl@gmail.com;
> sameo@linux.intel.com; sebastien.boeuf@intel.com; xuwei (O)
> <xuwei5@huawei.com>; ard.biesheuvel@linaro.org; Linuxarm
> <linuxarm@huawei.com>
> Subject: Re: [PATCH v4 8/8] hw/arm/boot: Expose the PC-DIMM nodes in the
> DT
> 
> On 04/09/19 12:29, Shameer Kolothum wrote:
> > This patch adds memory nodes corresponding to PC-DIMM regions.
> > This will enable support for cold plugged device memory for Guests
> > with DT boot.
> >
> > Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> > Signed-off-by: Eric Auger <eric.auger@redhat.com>
> > ---
> >  hw/arm/boot.c | 42 ++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 42 insertions(+)
> >
> > diff --git a/hw/arm/boot.c b/hw/arm/boot.c
> > index 8c840ba..150e1ed 100644
> > --- a/hw/arm/boot.c
> > +++ b/hw/arm/boot.c
> > @@ -19,6 +19,7 @@
> >  #include "sysemu/numa.h"
> >  #include "hw/boards.h"
> >  #include "hw/loader.h"
> > +#include "hw/mem/memory-device.h"
> >  #include "elf.h"
> >  #include "sysemu/device_tree.h"
> >  #include "qemu/config-file.h"
> > @@ -538,6 +539,41 @@ static void fdt_add_psci_node(void *fdt)
> >      qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn);
> >  }
> >
> > +static int fdt_add_hotpluggable_memory_nodes(void *fdt,
> > +                                             uint32_t acells,
> uint32_t scells) {
> > +    MemoryDeviceInfoList *info, *info_list = qmp_memory_device_list();
> > +    MemoryDeviceInfo *mi;
> > +    int ret = 0;
> > +
> > +    for (info = info_list; info != NULL; info = info->next) {
> > +        mi = info->value;
> > +        switch (mi->type) {
> > +        case MEMORY_DEVICE_INFO_KIND_DIMM:
> > +        {
> > +            PCDIMMDeviceInfo *di = mi->u.dimm.data;
> > +
> > +            ret = fdt_add_memory_node(fdt, acells, di->addr, scells,
> > +                                      di->size, di->node, true);
> > +            if (ret) {
> > +                fprintf(stderr,
> > +                        "couldn't add PCDIMM /memory@%"PRIx64"
> node\n",
> > +                        di->addr);
> > +                goto out;
> > +            }
> > +            break;
> > +        }
> > +        default:
> > +            fprintf(stderr, "%s memory nodes are not yet supported\n",
> > +                    MemoryDeviceInfoKind_str(mi->type));
> > +            ret = -ENOENT;
> > +            goto out;
> > +        }
> > +    }
> > +out:
> > +    qapi_free_MemoryDeviceInfoList(info_list);
> > +    return ret;
> > +}
> > +
> >  int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
> >                   hwaddr addr_limit, AddressSpace *as)
> >  {
> > @@ -637,6 +673,12 @@ int arm_load_dtb(hwaddr addr, const struct
> arm_boot_info *binfo,
> >          }
> >      }
> >
> > +    rc = fdt_add_hotpluggable_memory_nodes(fdt, acells, scells);
> > +    if (rc < 0) {
> > +        fprintf(stderr, "couldn't add hotpluggable memory nodes\n");
> > +        goto fail;
> > +    }
> > +
> >      rc = fdt_path_offset(fdt, "/chosen");
> >      if (rc < 0) {
> >          qemu_fdt_add_subnode(fdt, "/chosen");
> >
> 
> 
> Given patches #7 and #8, as I understand them, the firmware cannot
> distinguish hotpluggable & present, from hotpluggable & absent. The firmware
> can only skip both hotpluggable cases. That's fine in that the firmware will hog
> neither type -- but is that OK for the OS as well, for both ACPI boot and DT
> boot?

Right. This only handles the hotpluggable-and-present condition.

> Consider in particular the "hotpluggable & present, ACPI boot" case. Assuming
> we modify the firmware to skip "hotpluggable" altogether, the UEFI memmap
> will not include the range despite it being present at boot. Presumably, ACPI
> will refer to the range somehow, however. Will that not confuse the OS?

From my testing so far, without patches #7 and #8(ie, no UEFI memmap entry),
ACPI boots fine. I think ACPI only relies on aml and SRAT. 
 
> When Igor raised this earlier, I suggested that hotpluggable-and-present
> should be added by the firmware, but also allocated immediately, as
> EfiBootServicesData type memory. This will prevent other drivers in the
> firmware from allocating AcpiNVS or Reserved chunks from the same memory
> range, the UEFI memmap will contain the range as EfiBootServicesData, and
> then the OS can release that allocation in one go early during boot.

Ok. Agree that hotpluggable-and-present case it may make sense to make use of
that memory rather than just hiding it altogether.

On another note, Does hotpluggable-and-absent case make any valid use case for
a DT boot? I am not sure how we can hot-add memory in the case of DT boot later.
I have not verified the sysfs probe interface yet and there are discussions of dropping
that interface altogether.

> But this really has to be clarified from the Linux kernel's expectations. Please
> formalize all of the following cases:

Sure. I will wait for suggestions here and work on it.

Thanks,
Shameer

> OS boot (DT/ACPI)  hotpluggable & ...  GetMemoryMap() should report as
> DT/ACPI should report as
> -----------------  ------------------  -------------------------------  ------------------------
> DT
> present             ?                                ?
> DT
> absent              ?                                ?
> ACPI
> present             ?                                ?
> ACPI
> absent              ?                                ?
> 
> Again, this table is dictated by Linux.
> 
> Thanks
> Laszlo

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 8/8] hw/arm/boot: Expose the PC-DIMM nodes in the DT
@ 2019-04-10  8:49       ` Shameerali Kolothum Thodi
  0 siblings, 0 replies; 62+ messages in thread
From: Shameerali Kolothum Thodi @ 2019-04-10  8:49 UTC (permalink / raw)
  To: Laszlo Ersek, qemu-devel, qemu-arm, eric.auger, imammedo
  Cc: peter.maydell, sameo, ard.biesheuvel, Linuxarm, shannon.zhaosl,
	sebastien.boeuf, xuwei (O)


> -----Original Message-----
> From: Laszlo Ersek [mailto:lersek@redhat.com]
> Sent: 09 April 2019 16:09
> To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>;
> qemu-devel@nongnu.org; qemu-arm@nongnu.org; eric.auger@redhat.com;
> imammedo@redhat.com
> Cc: peter.maydell@linaro.org; shannon.zhaosl@gmail.com;
> sameo@linux.intel.com; sebastien.boeuf@intel.com; xuwei (O)
> <xuwei5@huawei.com>; ard.biesheuvel@linaro.org; Linuxarm
> <linuxarm@huawei.com>
> Subject: Re: [PATCH v4 8/8] hw/arm/boot: Expose the PC-DIMM nodes in the
> DT
> 
> On 04/09/19 12:29, Shameer Kolothum wrote:
> > This patch adds memory nodes corresponding to PC-DIMM regions.
> > This will enable support for cold plugged device memory for Guests
> > with DT boot.
> >
> > Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> > Signed-off-by: Eric Auger <eric.auger@redhat.com>
> > ---
> >  hw/arm/boot.c | 42 ++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 42 insertions(+)
> >
> > diff --git a/hw/arm/boot.c b/hw/arm/boot.c
> > index 8c840ba..150e1ed 100644
> > --- a/hw/arm/boot.c
> > +++ b/hw/arm/boot.c
> > @@ -19,6 +19,7 @@
> >  #include "sysemu/numa.h"
> >  #include "hw/boards.h"
> >  #include "hw/loader.h"
> > +#include "hw/mem/memory-device.h"
> >  #include "elf.h"
> >  #include "sysemu/device_tree.h"
> >  #include "qemu/config-file.h"
> > @@ -538,6 +539,41 @@ static void fdt_add_psci_node(void *fdt)
> >      qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn);
> >  }
> >
> > +static int fdt_add_hotpluggable_memory_nodes(void *fdt,
> > +                                             uint32_t acells,
> uint32_t scells) {
> > +    MemoryDeviceInfoList *info, *info_list = qmp_memory_device_list();
> > +    MemoryDeviceInfo *mi;
> > +    int ret = 0;
> > +
> > +    for (info = info_list; info != NULL; info = info->next) {
> > +        mi = info->value;
> > +        switch (mi->type) {
> > +        case MEMORY_DEVICE_INFO_KIND_DIMM:
> > +        {
> > +            PCDIMMDeviceInfo *di = mi->u.dimm.data;
> > +
> > +            ret = fdt_add_memory_node(fdt, acells, di->addr, scells,
> > +                                      di->size, di->node, true);
> > +            if (ret) {
> > +                fprintf(stderr,
> > +                        "couldn't add PCDIMM /memory@%"PRIx64"
> node\n",
> > +                        di->addr);
> > +                goto out;
> > +            }
> > +            break;
> > +        }
> > +        default:
> > +            fprintf(stderr, "%s memory nodes are not yet supported\n",
> > +                    MemoryDeviceInfoKind_str(mi->type));
> > +            ret = -ENOENT;
> > +            goto out;
> > +        }
> > +    }
> > +out:
> > +    qapi_free_MemoryDeviceInfoList(info_list);
> > +    return ret;
> > +}
> > +
> >  int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
> >                   hwaddr addr_limit, AddressSpace *as)
> >  {
> > @@ -637,6 +673,12 @@ int arm_load_dtb(hwaddr addr, const struct
> arm_boot_info *binfo,
> >          }
> >      }
> >
> > +    rc = fdt_add_hotpluggable_memory_nodes(fdt, acells, scells);
> > +    if (rc < 0) {
> > +        fprintf(stderr, "couldn't add hotpluggable memory nodes\n");
> > +        goto fail;
> > +    }
> > +
> >      rc = fdt_path_offset(fdt, "/chosen");
> >      if (rc < 0) {
> >          qemu_fdt_add_subnode(fdt, "/chosen");
> >
> 
> 
> Given patches #7 and #8, as I understand them, the firmware cannot
> distinguish hotpluggable & present, from hotpluggable & absent. The firmware
> can only skip both hotpluggable cases. That's fine in that the firmware will hog
> neither type -- but is that OK for the OS as well, for both ACPI boot and DT
> boot?

Right. This only handles the hotpluggable-and-present condition.

> Consider in particular the "hotpluggable & present, ACPI boot" case. Assuming
> we modify the firmware to skip "hotpluggable" altogether, the UEFI memmap
> will not include the range despite it being present at boot. Presumably, ACPI
> will refer to the range somehow, however. Will that not confuse the OS?

From my testing so far, without patches #7 and #8(ie, no UEFI memmap entry),
ACPI boots fine. I think ACPI only relies on aml and SRAT. 
 
> When Igor raised this earlier, I suggested that hotpluggable-and-present
> should be added by the firmware, but also allocated immediately, as
> EfiBootServicesData type memory. This will prevent other drivers in the
> firmware from allocating AcpiNVS or Reserved chunks from the same memory
> range, the UEFI memmap will contain the range as EfiBootServicesData, and
> then the OS can release that allocation in one go early during boot.

Ok. Agree that hotpluggable-and-present case it may make sense to make use of
that memory rather than just hiding it altogether.

On another note, Does hotpluggable-and-absent case make any valid use case for
a DT boot? I am not sure how we can hot-add memory in the case of DT boot later.
I have not verified the sysfs probe interface yet and there are discussions of dropping
that interface altogether.

> But this really has to be clarified from the Linux kernel's expectations. Please
> formalize all of the following cases:

Sure. I will wait for suggestions here and work on it.

Thanks,
Shameer

> OS boot (DT/ACPI)  hotpluggable & ...  GetMemoryMap() should report as
> DT/ACPI should report as
> -----------------  ------------------  -------------------------------  ------------------------
> DT
> present             ?                                ?
> DT
> absent              ?                                ?
> ACPI
> present             ?                                ?
> ACPI
> absent              ?                                ?
> 
> Again, this table is dictated by Linux.
> 
> Thanks
> Laszlo

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
@ 2019-04-30 15:49     ` Auger Eric
  0 siblings, 0 replies; 62+ messages in thread
From: Auger Eric @ 2019-04-30 15:49 UTC (permalink / raw)
  To: Shameer Kolothum, qemu-devel, qemu-arm, imammedo
  Cc: peter.maydell, shannon.zhaosl, sameo, sebastien.boeuf, xuwei5,
	lersek, ard.biesheuvel, linuxarm

Hi Shameer,

On 4/9/19 12:29 PM, Shameer Kolothum wrote:
> From: Samuel Ortiz <sameo@linux.intel.com>
> 
> The ACPI Generic Event Device (GED) is a hardware-reduced specific
> device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> including the hotplug ones.This patch generates the AML code that
> defines GEDs.
> 
> Platforms need to specify their own GedEvent array to describe what
> kind of events they want to support through GED.  Also this uses a
> a single interrupt for the  GED device, relying on IO memory region
> to communicate the type of device affected by the interrupt. This
> way, we can support up to 32 events with a unique interrupt.
> 
> This supports only memory hotplug for now.
> 
> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
> Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
> Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> ---
>  hw/acpi/Kconfig                        |   4 +
>  hw/acpi/Makefile.objs                  |   1 +
>  hw/acpi/generic_event_device.c         | 311 +++++++++++++++++++++++++++++++++
>  include/hw/acpi/generic_event_device.h | 121 +++++++++++++
>  4 files changed, 437 insertions(+)
>  create mode 100644 hw/acpi/generic_event_device.c
>  create mode 100644 include/hw/acpi/generic_event_device.h
> 
> diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig
> index eca3bee..01a8b41 100644
> --- a/hw/acpi/Kconfig
> +++ b/hw/acpi/Kconfig
> @@ -27,3 +27,7 @@ config ACPI_VMGENID
>      bool
>      default y
>      depends on PC
> +
> +config ACPI_HW_REDUCED
> +    bool
> +    depends on ACPI
> diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
> index 2d46e37..b753232 100644
> --- a/hw/acpi/Makefile.objs
> +++ b/hw/acpi/Makefile.objs
> @@ -6,6 +6,7 @@ common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o
>  common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu.o
>  common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
>  common-obj-$(CONFIG_ACPI_VMGENID) += vmgenid.o
> +common-obj-$(CONFIG_ACPI_HW_REDUCED) += generic_event_device.o
>  common-obj-$(call lnot,$(CONFIG_ACPI_X86)) += acpi-stub.o
>  
>  common-obj-y += acpi_interface.o
> diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c
> new file mode 100644
> index 0000000..856ca04
> --- /dev/null
> +++ b/hw/acpi/generic_event_device.c
> @@ -0,0 +1,311 @@
> +/*
> + *
> + * Copyright (c) 2018 Intel Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
I am not sure we need below statements: see hw/misc/armsse-mhu.c for a
recent added file.

> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "exec/address-spaces.h"
> +#include "hw/sysbus.h"
> +#include "hw/acpi/acpi.h"
> +#include "hw/acpi/generic_event_device.h"
> +#include "hw/mem/pc-dimm.h"
> +
> +static Aml *ged_event_aml(const GedEvent *event)
> +{
> +
> +    if (!event) {
> +        return NULL;
> +    }
> +
> +    switch (event->event) {
> +    case GED_MEMORY_HOTPLUG:
> +        /* We run a complete memory SCAN when getting a memory hotplug event */
> +        return aml_call0(MEMORY_DEVICES_CONTAINER "." MEMORY_SLOT_SCAN_METHOD);
> +    default:
> +        break;
> +    }
> +
> +    return NULL;
> +}
> +
> +/*
> + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
> + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> + * including the hotplug ones. Platforms need to specify their own
> + * GedEvent array to describe what kind of events they want to support
> + * through GED. This routine uses a single interrupt for the GED device,
> + * relying on IO memory region to communicate the type of device
> + * affected by the interrupt. This way, we can support up to 32 events
> + * with a unique interrupt.
> + */
> +void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev,
> +                   uint32_t ged_irq, AmlRegionSpace rs)
> +{
> +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> +    GedEvent *ged_events = s->ged_events;
> +    Aml *crs = aml_resource_template();
> +    Aml *evt, *field;
> +    Aml *dev = aml_device("%s", name);
> +    Aml *irq_sel = aml_local(0);
> +    Aml *isel = aml_name(AML_GED_IRQ_SEL);
> +    uint32_t i;
> +
> +    if (!s->ged_base || !ged_events || !s->ged_events_size) {
> +        return;
> +    }
> +
> +    /* _CRS interrupt */
> +    aml_append(crs, aml_interrupt(AML_CONSUMER, AML_EDGE, AML_ACTIVE_HIGH,
> +                                  AML_EXCLUSIVE, &ged_irq, 1));
> +    /*
> +     * For each GED event we:
> +     * - Add an interrupt to the CRS section.
> +     * - Add a conditional block for each event, inside a while loop.
> +     *   This is semantically equivalent to a switch/case implementation.
> +     */
> +    evt = aml_method("_EVT", 1, AML_SERIALIZED);
> +    {
> +        Aml *ged_aml;
> +        Aml *if_ctx;
> +
> +        /* Local0 = ISEL */
> +        aml_append(evt, aml_store(isel, irq_sel));
> +
> +        /*
> +         * Here we want to call a method for each supported GED event type.
> +         * The resulting ASL code looks like:
> +         *
> +         * Local0 = ISEL
> +         * If ((Local0 & irq0) == irq0)
> +         * {
> +         *     MethodEvent0()
> +         * }
> +         *
> +         * If ((Local0 & irq1) == irq1)
> +         * {
> +         *     MethodEvent1()
> +         * }
> +         * ...
> +         */
> +
> +        for (i = 0; i < s->ged_events_size; i++) {
> +            ged_aml = ged_event_aml(&ged_events[i]);
> +            if (!ged_aml) {
> +                continue;
> +            }
> +
> +            /* If ((Local1 == irq))*/
> +            if_ctx = aml_if(aml_equal(aml_and(irq_sel,
> +                                      aml_int(ged_events[i].selector), NULL),
> +                                      aml_int(ged_events[i].selector)));
> +            {
> +                /* AML for this specific type of event */
> +                aml_append(if_ctx, ged_aml);
> +            }
> +
> +            /*
> +             * We append the first "if" to the "while" context.
> +             * Other "if"s will be "elseif"s.
Are we sure about that:
in hw/acpi/nvdimm.c nvdimm_build_common_dsm() I can see an aml_else() is
added inbetween aml_if's?
> +             */
> +            aml_append(evt, if_ctx);
> +        }
> +    }
> +
> +    aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0013")));
> +    aml_append(dev, aml_name_decl("_UID", aml_string(GED_DEVICE)));
> +    aml_append(dev, aml_name_decl("_CRS", crs));
> +
> +    /* Append IO region */
> +    aml_append(dev, aml_operation_region(AML_GED_IRQ_REG, rs,
> +               aml_int(s->ged_base + ACPI_GED_IRQ_SEL_OFFSET),
> +               ACPI_GED_IRQ_SEL_LEN));
> +    field = aml_field(AML_GED_IRQ_REG, AML_DWORD_ACC, AML_NOLOCK,
> +                      AML_WRITE_AS_ZEROS);
> +    aml_append(field, aml_named_field(AML_GED_IRQ_SEL,
> +                                      ACPI_GED_IRQ_SEL_LEN * 8));
nit s/8/BITS_PER_BYTE as done in nvdimm.c.
> +    aml_append(dev, field);
> +
> +    /* Append _EVT method */
> +    aml_append(dev, evt);
> +
> +    aml_append(table, dev);
> +}
> +
> +/* Memory read by the GED _EVT AML dynamic method */
> +static uint64_t ged_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    uint64_t val = 0;
> +    GEDState *ged_st = opaque;
> +
> +    switch (addr) {
> +    case ACPI_GED_IRQ_SEL_OFFSET:
> +        /* Read the selector value and reset it */
> +        qemu_mutex_lock(&ged_st->lock);
> +        val = ged_st->sel;
> +        ged_st->sel = 0;
> +        qemu_mutex_unlock(&ged_st->lock);
> +        break;
> +    default:
> +        break;
> +    }
> +
> +    return val;
> +}
> +
> +/* Nothing is expected to be written to the GED memory region */
> +static void ged_write(void *opaque, hwaddr addr, uint64_t data,
> +                      unsigned int size)
> +{
> +}
> +
> +static const MemoryRegionOps ged_ops = {
> +    .read = ged_read,
> +    .write = ged_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +};
> +
> +static void acpi_ged_event(GEDState *ged_st, uint32_t ged_irq_sel)
> +{
> +    /*
I would personally inline that code in void acpi_ged_send_event()
> +     * Set the GED IRQ selector to the expected device type value. This
> +     * way, the ACPI method will be able to trigger the right code based
> +     * on a unique IRQ.
> +     */
> +    qemu_mutex_lock(&ged_st->lock);
> +    ged_st->sel = ged_irq_sel;
> +    qemu_mutex_unlock(&ged_st->lock);
> +
> +    /* Trigger the event by sending an interrupt to the guest. */
> +    qemu_irq_pulse(ged_st->gsi[ged_st->irq]);
I don't really understand why we need this array. Doesn't The GED use a
single IRQ. So why can't you store the precise qemu_irq pointer directly
through the propery (DEFINE_PROP_PTR("gsi", AcpiGedState, gsi))?
> +}
> +
> +static void acpi_ged_init(MemoryRegion *as, DeviceState *dev, GEDState *ged_st)
> +{
> +    AcpiGedState *s = ACPI_GED(dev);
> +
> +    assert(s->ged_base);
> +
> +    ged_st->irq = s->ged_irq;
> +    ged_st->gsi = s->gsi;
> +    qemu_mutex_init(&ged_st->lock);
> +    memory_region_init_io(&ged_st->io, OBJECT(dev), &ged_ops, ged_st,
> +                          "acpi-ged-event", ACPI_GED_REG_LEN);
> +    memory_region_add_subregion(as, s->ged_base, &ged_st->io);
> +}
> +
> +static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
> +                                    DeviceState *dev, Error **errp)
> +{
> +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> +
> +    if (s->memhp_state.is_enabled &&
> +        object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> +            acpi_memory_plug_cb(hotplug_dev, &s->memhp_state,
> +                                dev, errp);
> +    } else {
> +        error_setg(errp, "virt: device plug request for unsupported device"
> +                   " type: %s", object_get_typename(OBJECT(dev)));
> +    }
> +}
> +
> +static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
> +{
> +    AcpiGedState *s = ACPI_GED(adev);
> +    uint32_t sel;
> +
> +    if (ev & ACPI_MEMORY_HOTPLUG_STATUS) {
> +        sel = ACPI_GED_IRQ_SEL_MEM;
> +    } else {
> +        /* Unknown event. Return without generating interrupt. */
> +        return;
> +    }
> +
> +    /*
> +     * We inject the hotplug interrupt. The IRQ selector will make
> +     * the difference from the ACPI table.
> +     */
> +    acpi_ged_event(&s->ged_state, sel);
> +}
> +
> +static void acpi_ged_device_realize(DeviceState *dev, Error **errp)
> +{
> +    AcpiGedState *s = ACPI_GED(dev);
> +
> +    if (s->memhp_state.is_enabled) {
> +        acpi_memory_hotplug_init(get_system_memory(), OBJECT(dev),
> +                                 &s->memhp_state,
> +                                 s->memhp_base);
> +        acpi_ged_init(get_system_memory(), dev, &s->ged_state);
> +    }
> +}
> +
> +static Property acpi_ged_properties[] = {
> +    /*
> +     * Memory hotplug base address is a property of GED here,
> +     * because GED handles memory hotplug event and MEMORY_HOTPLUG_DEVICE
> +     * gets initialized when GED device is realized.
> +     */
> +    DEFINE_PROP_UINT64("memhp-base", AcpiGedState, memhp_base, 0),
> +    DEFINE_PROP_BOOL("memory-hotplug-support", AcpiGedState,
> +                     memhp_state.is_enabled, true),
> +    DEFINE_PROP_PTR("gsi", AcpiGedState, gsi),
> +    DEFINE_PROP_UINT64("ged-base", AcpiGedState, ged_base, 0),
> +    DEFINE_PROP_UINT32("ged-irq", AcpiGedState, ged_irq, 0),
> +    DEFINE_PROP_PTR("ged-events", AcpiGedState, ged_events),
> +    DEFINE_PROP_UINT32("ged-events-size", AcpiGedState, ged_events_size, 0),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void acpi_ged_class_init(ObjectClass *class, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(class);
> +    HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class);
> +    AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class);
> +
> +    dc->desc = "ACPI";
> +    dc->props = acpi_ged_properties;
> +    dc->realize = acpi_ged_device_realize;
> +
> +    /* Reason: pointer properties "gsi" and "ged_events" */
> +    dc->user_creatable = false;
> +
> +    hc->plug = acpi_ged_device_plug_cb;
> +
> +    adevc->send_event = acpi_ged_send_event;
> +}
> +
> +static const TypeInfo acpi_ged_info = {
> +    .name          = TYPE_ACPI_GED,
> +    .parent        = TYPE_DEVICE,
> +    .instance_size = sizeof(AcpiGedState),
> +    .class_init    = acpi_ged_class_init,
> +    .interfaces = (InterfaceInfo[]) {
> +        { TYPE_HOTPLUG_HANDLER },
> +        { TYPE_ACPI_DEVICE_IF },
> +        { }
> +    }
> +};
> +
> +static void acpi_ged_register_types(void)
> +{
> +    type_register_static(&acpi_ged_info);
> +}
> +
> +type_init(acpi_ged_register_types)
> diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h
> new file mode 100644
> index 0000000..9c840d8
> --- /dev/null
> +++ b/include/hw/acpi/generic_event_device.h
> @@ -0,0 +1,121 @@
> +/*
> + *
> + * Copyright (c) 2018 Intel Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
> + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> + * including the hotplug ones. Generic Event Device allows platforms
> + * to handle interrupts in ACPI ASL statements. It follows a very
> + * similar approach like the _EVT method from GPIO events. All
> + * interrupts are listed in  _CRS and the handler is written in _EVT> + * method. Here, we use a single interrupt for the GED device, relying
> + * on IO memory region to communicate the type of device affected by
> + * the interrupt. This way, we can support up to 32 events with a
> + * unique interrupt.
> + *
> + * Here is an example.
> + *
> + * Device (\_SB.GED)
> + * {
> + *     Name (_HID, "ACPI0013")
> + *     Name (_UID, Zero)
> + *     Name (_CRS, ResourceTemplate ()
> + *     {
> + *         Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, )
> + *         {
> + *              0x00000029,
> + *         }
> + *     })
> + *     OperationRegion (IREG, SystemMemory, 0x09080000, 0x04)
> + *     Field (IREG, DWordAcc, NoLock, WriteAsZeros)
> + *     {
> + *         ISEL,   32
> + *     }
> + *
> + *     Method (_EVT, 1, Serialized)  // _EVT: Event
> + *     {
> + *         Local0 = ISEL // ISEL = IO memory region which specifies the
> + *                       // device type.
> + *         If (((Local0 & irq0) == irq0))
> + *         {
> + *             MethodEvent0()
> + *         }
> + *         ElseIf ((Local0 & irq1) == irq1)
> + *         {
> + *             MethodEvent1()
> + *         }
> + *         ...
> + *     }
> + * }
> + *
> + */
> +
> +#ifndef HW_ACPI_GED_H
> +#define HW_ACPI_GED_H
> +
> +#include "hw/acpi/memory_hotplug.h"
> +
> +#define TYPE_ACPI_GED "acpi-ged"
> +#define ACPI_GED(obj) \
> +    OBJECT_CHECK(AcpiGedState, (obj), TYPE_ACPI_GED)
> +
> +#define ACPI_GED_IRQ_SEL_OFFSET 0x0
> +#define ACPI_GED_IRQ_SEL_LEN    0x4
> +#define ACPI_GED_IRQ_SEL_MEM    0x1
> +#define ACPI_GED_REG_LEN        0x4
> +
> +#define GED_DEVICE      "GED"
> +#define AML_GED_IRQ_REG "IREG"
> +#define AML_GED_IRQ_SEL "ISEL"
> +
> +typedef enum {
> +    GED_MEMORY_HOTPLUG = 1,
> +} GedEventType;
> +
> +/*
> + * Platforms need to specify their own GedEvent array
> + * to describe what kind of events they want to support
> + * through GED.
> + */
> +typedef struct GedEvent {
> +    uint32_t     selector;
> +    GedEventType event;
> +} GedEvent;
> +
> +typedef struct GEDState {
> +    MemoryRegion io;
> +    uint32_t     sel;
> +    uint32_t     irq;
> +    qemu_irq     *gsi;
so I am not sure why with need gsi + irq.
> +    QemuMutex    lock;
> +} GEDState;
> +
> +
> +typedef struct AcpiGedState {
> +    DeviceClass parent_obj;
> +    MemHotplugState memhp_state;
> +    hwaddr memhp_base;
> +    void *gsi;
> +    hwaddr ged_base;
> +    GEDState ged_state;
> +    uint32_t ged_irq;
> +    void *ged_events;
> +    uint32_t ged_events_size;
> +} AcpiGedState;
> +
> +void build_ged_aml(Aml *table, const char* name, HotplugHandler *hotplug_dev,
> +                   uint32_t ged_irq, AmlRegionSpace rs);
> +
> +#endif
> 
Besides, this looks good to me.

Thanks

Eric

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
@ 2019-04-30 15:49     ` Auger Eric
  0 siblings, 0 replies; 62+ messages in thread
From: Auger Eric @ 2019-04-30 15:49 UTC (permalink / raw)
  To: Shameer Kolothum, qemu-devel, qemu-arm, imammedo
  Cc: peter.maydell, sameo, ard.biesheuvel, linuxarm, xuwei5,
	shannon.zhaosl, sebastien.boeuf, lersek

Hi Shameer,

On 4/9/19 12:29 PM, Shameer Kolothum wrote:
> From: Samuel Ortiz <sameo@linux.intel.com>
> 
> The ACPI Generic Event Device (GED) is a hardware-reduced specific
> device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> including the hotplug ones.This patch generates the AML code that
> defines GEDs.
> 
> Platforms need to specify their own GedEvent array to describe what
> kind of events they want to support through GED.  Also this uses a
> a single interrupt for the  GED device, relying on IO memory region
> to communicate the type of device affected by the interrupt. This
> way, we can support up to 32 events with a unique interrupt.
> 
> This supports only memory hotplug for now.
> 
> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
> Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
> Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> ---
>  hw/acpi/Kconfig                        |   4 +
>  hw/acpi/Makefile.objs                  |   1 +
>  hw/acpi/generic_event_device.c         | 311 +++++++++++++++++++++++++++++++++
>  include/hw/acpi/generic_event_device.h | 121 +++++++++++++
>  4 files changed, 437 insertions(+)
>  create mode 100644 hw/acpi/generic_event_device.c
>  create mode 100644 include/hw/acpi/generic_event_device.h
> 
> diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig
> index eca3bee..01a8b41 100644
> --- a/hw/acpi/Kconfig
> +++ b/hw/acpi/Kconfig
> @@ -27,3 +27,7 @@ config ACPI_VMGENID
>      bool
>      default y
>      depends on PC
> +
> +config ACPI_HW_REDUCED
> +    bool
> +    depends on ACPI
> diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
> index 2d46e37..b753232 100644
> --- a/hw/acpi/Makefile.objs
> +++ b/hw/acpi/Makefile.objs
> @@ -6,6 +6,7 @@ common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o
>  common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu.o
>  common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
>  common-obj-$(CONFIG_ACPI_VMGENID) += vmgenid.o
> +common-obj-$(CONFIG_ACPI_HW_REDUCED) += generic_event_device.o
>  common-obj-$(call lnot,$(CONFIG_ACPI_X86)) += acpi-stub.o
>  
>  common-obj-y += acpi_interface.o
> diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c
> new file mode 100644
> index 0000000..856ca04
> --- /dev/null
> +++ b/hw/acpi/generic_event_device.c
> @@ -0,0 +1,311 @@
> +/*
> + *
> + * Copyright (c) 2018 Intel Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
I am not sure we need below statements: see hw/misc/armsse-mhu.c for a
recent added file.

> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "exec/address-spaces.h"
> +#include "hw/sysbus.h"
> +#include "hw/acpi/acpi.h"
> +#include "hw/acpi/generic_event_device.h"
> +#include "hw/mem/pc-dimm.h"
> +
> +static Aml *ged_event_aml(const GedEvent *event)
> +{
> +
> +    if (!event) {
> +        return NULL;
> +    }
> +
> +    switch (event->event) {
> +    case GED_MEMORY_HOTPLUG:
> +        /* We run a complete memory SCAN when getting a memory hotplug event */
> +        return aml_call0(MEMORY_DEVICES_CONTAINER "." MEMORY_SLOT_SCAN_METHOD);
> +    default:
> +        break;
> +    }
> +
> +    return NULL;
> +}
> +
> +/*
> + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
> + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> + * including the hotplug ones. Platforms need to specify their own
> + * GedEvent array to describe what kind of events they want to support
> + * through GED. This routine uses a single interrupt for the GED device,
> + * relying on IO memory region to communicate the type of device
> + * affected by the interrupt. This way, we can support up to 32 events
> + * with a unique interrupt.
> + */
> +void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev,
> +                   uint32_t ged_irq, AmlRegionSpace rs)
> +{
> +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> +    GedEvent *ged_events = s->ged_events;
> +    Aml *crs = aml_resource_template();
> +    Aml *evt, *field;
> +    Aml *dev = aml_device("%s", name);
> +    Aml *irq_sel = aml_local(0);
> +    Aml *isel = aml_name(AML_GED_IRQ_SEL);
> +    uint32_t i;
> +
> +    if (!s->ged_base || !ged_events || !s->ged_events_size) {
> +        return;
> +    }
> +
> +    /* _CRS interrupt */
> +    aml_append(crs, aml_interrupt(AML_CONSUMER, AML_EDGE, AML_ACTIVE_HIGH,
> +                                  AML_EXCLUSIVE, &ged_irq, 1));
> +    /*
> +     * For each GED event we:
> +     * - Add an interrupt to the CRS section.
> +     * - Add a conditional block for each event, inside a while loop.
> +     *   This is semantically equivalent to a switch/case implementation.
> +     */
> +    evt = aml_method("_EVT", 1, AML_SERIALIZED);
> +    {
> +        Aml *ged_aml;
> +        Aml *if_ctx;
> +
> +        /* Local0 = ISEL */
> +        aml_append(evt, aml_store(isel, irq_sel));
> +
> +        /*
> +         * Here we want to call a method for each supported GED event type.
> +         * The resulting ASL code looks like:
> +         *
> +         * Local0 = ISEL
> +         * If ((Local0 & irq0) == irq0)
> +         * {
> +         *     MethodEvent0()
> +         * }
> +         *
> +         * If ((Local0 & irq1) == irq1)
> +         * {
> +         *     MethodEvent1()
> +         * }
> +         * ...
> +         */
> +
> +        for (i = 0; i < s->ged_events_size; i++) {
> +            ged_aml = ged_event_aml(&ged_events[i]);
> +            if (!ged_aml) {
> +                continue;
> +            }
> +
> +            /* If ((Local1 == irq))*/
> +            if_ctx = aml_if(aml_equal(aml_and(irq_sel,
> +                                      aml_int(ged_events[i].selector), NULL),
> +                                      aml_int(ged_events[i].selector)));
> +            {
> +                /* AML for this specific type of event */
> +                aml_append(if_ctx, ged_aml);
> +            }
> +
> +            /*
> +             * We append the first "if" to the "while" context.
> +             * Other "if"s will be "elseif"s.
Are we sure about that:
in hw/acpi/nvdimm.c nvdimm_build_common_dsm() I can see an aml_else() is
added inbetween aml_if's?
> +             */
> +            aml_append(evt, if_ctx);
> +        }
> +    }
> +
> +    aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0013")));
> +    aml_append(dev, aml_name_decl("_UID", aml_string(GED_DEVICE)));
> +    aml_append(dev, aml_name_decl("_CRS", crs));
> +
> +    /* Append IO region */
> +    aml_append(dev, aml_operation_region(AML_GED_IRQ_REG, rs,
> +               aml_int(s->ged_base + ACPI_GED_IRQ_SEL_OFFSET),
> +               ACPI_GED_IRQ_SEL_LEN));
> +    field = aml_field(AML_GED_IRQ_REG, AML_DWORD_ACC, AML_NOLOCK,
> +                      AML_WRITE_AS_ZEROS);
> +    aml_append(field, aml_named_field(AML_GED_IRQ_SEL,
> +                                      ACPI_GED_IRQ_SEL_LEN * 8));
nit s/8/BITS_PER_BYTE as done in nvdimm.c.
> +    aml_append(dev, field);
> +
> +    /* Append _EVT method */
> +    aml_append(dev, evt);
> +
> +    aml_append(table, dev);
> +}
> +
> +/* Memory read by the GED _EVT AML dynamic method */
> +static uint64_t ged_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    uint64_t val = 0;
> +    GEDState *ged_st = opaque;
> +
> +    switch (addr) {
> +    case ACPI_GED_IRQ_SEL_OFFSET:
> +        /* Read the selector value and reset it */
> +        qemu_mutex_lock(&ged_st->lock);
> +        val = ged_st->sel;
> +        ged_st->sel = 0;
> +        qemu_mutex_unlock(&ged_st->lock);
> +        break;
> +    default:
> +        break;
> +    }
> +
> +    return val;
> +}
> +
> +/* Nothing is expected to be written to the GED memory region */
> +static void ged_write(void *opaque, hwaddr addr, uint64_t data,
> +                      unsigned int size)
> +{
> +}
> +
> +static const MemoryRegionOps ged_ops = {
> +    .read = ged_read,
> +    .write = ged_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +};
> +
> +static void acpi_ged_event(GEDState *ged_st, uint32_t ged_irq_sel)
> +{
> +    /*
I would personally inline that code in void acpi_ged_send_event()
> +     * Set the GED IRQ selector to the expected device type value. This
> +     * way, the ACPI method will be able to trigger the right code based
> +     * on a unique IRQ.
> +     */
> +    qemu_mutex_lock(&ged_st->lock);
> +    ged_st->sel = ged_irq_sel;
> +    qemu_mutex_unlock(&ged_st->lock);
> +
> +    /* Trigger the event by sending an interrupt to the guest. */
> +    qemu_irq_pulse(ged_st->gsi[ged_st->irq]);
I don't really understand why we need this array. Doesn't The GED use a
single IRQ. So why can't you store the precise qemu_irq pointer directly
through the propery (DEFINE_PROP_PTR("gsi", AcpiGedState, gsi))?
> +}
> +
> +static void acpi_ged_init(MemoryRegion *as, DeviceState *dev, GEDState *ged_st)
> +{
> +    AcpiGedState *s = ACPI_GED(dev);
> +
> +    assert(s->ged_base);
> +
> +    ged_st->irq = s->ged_irq;
> +    ged_st->gsi = s->gsi;
> +    qemu_mutex_init(&ged_st->lock);
> +    memory_region_init_io(&ged_st->io, OBJECT(dev), &ged_ops, ged_st,
> +                          "acpi-ged-event", ACPI_GED_REG_LEN);
> +    memory_region_add_subregion(as, s->ged_base, &ged_st->io);
> +}
> +
> +static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
> +                                    DeviceState *dev, Error **errp)
> +{
> +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> +
> +    if (s->memhp_state.is_enabled &&
> +        object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> +            acpi_memory_plug_cb(hotplug_dev, &s->memhp_state,
> +                                dev, errp);
> +    } else {
> +        error_setg(errp, "virt: device plug request for unsupported device"
> +                   " type: %s", object_get_typename(OBJECT(dev)));
> +    }
> +}
> +
> +static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
> +{
> +    AcpiGedState *s = ACPI_GED(adev);
> +    uint32_t sel;
> +
> +    if (ev & ACPI_MEMORY_HOTPLUG_STATUS) {
> +        sel = ACPI_GED_IRQ_SEL_MEM;
> +    } else {
> +        /* Unknown event. Return without generating interrupt. */
> +        return;
> +    }
> +
> +    /*
> +     * We inject the hotplug interrupt. The IRQ selector will make
> +     * the difference from the ACPI table.
> +     */
> +    acpi_ged_event(&s->ged_state, sel);
> +}
> +
> +static void acpi_ged_device_realize(DeviceState *dev, Error **errp)
> +{
> +    AcpiGedState *s = ACPI_GED(dev);
> +
> +    if (s->memhp_state.is_enabled) {
> +        acpi_memory_hotplug_init(get_system_memory(), OBJECT(dev),
> +                                 &s->memhp_state,
> +                                 s->memhp_base);
> +        acpi_ged_init(get_system_memory(), dev, &s->ged_state);
> +    }
> +}
> +
> +static Property acpi_ged_properties[] = {
> +    /*
> +     * Memory hotplug base address is a property of GED here,
> +     * because GED handles memory hotplug event and MEMORY_HOTPLUG_DEVICE
> +     * gets initialized when GED device is realized.
> +     */
> +    DEFINE_PROP_UINT64("memhp-base", AcpiGedState, memhp_base, 0),
> +    DEFINE_PROP_BOOL("memory-hotplug-support", AcpiGedState,
> +                     memhp_state.is_enabled, true),
> +    DEFINE_PROP_PTR("gsi", AcpiGedState, gsi),
> +    DEFINE_PROP_UINT64("ged-base", AcpiGedState, ged_base, 0),
> +    DEFINE_PROP_UINT32("ged-irq", AcpiGedState, ged_irq, 0),
> +    DEFINE_PROP_PTR("ged-events", AcpiGedState, ged_events),
> +    DEFINE_PROP_UINT32("ged-events-size", AcpiGedState, ged_events_size, 0),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void acpi_ged_class_init(ObjectClass *class, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(class);
> +    HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class);
> +    AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class);
> +
> +    dc->desc = "ACPI";
> +    dc->props = acpi_ged_properties;
> +    dc->realize = acpi_ged_device_realize;
> +
> +    /* Reason: pointer properties "gsi" and "ged_events" */
> +    dc->user_creatable = false;
> +
> +    hc->plug = acpi_ged_device_plug_cb;
> +
> +    adevc->send_event = acpi_ged_send_event;
> +}
> +
> +static const TypeInfo acpi_ged_info = {
> +    .name          = TYPE_ACPI_GED,
> +    .parent        = TYPE_DEVICE,
> +    .instance_size = sizeof(AcpiGedState),
> +    .class_init    = acpi_ged_class_init,
> +    .interfaces = (InterfaceInfo[]) {
> +        { TYPE_HOTPLUG_HANDLER },
> +        { TYPE_ACPI_DEVICE_IF },
> +        { }
> +    }
> +};
> +
> +static void acpi_ged_register_types(void)
> +{
> +    type_register_static(&acpi_ged_info);
> +}
> +
> +type_init(acpi_ged_register_types)
> diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h
> new file mode 100644
> index 0000000..9c840d8
> --- /dev/null
> +++ b/include/hw/acpi/generic_event_device.h
> @@ -0,0 +1,121 @@
> +/*
> + *
> + * Copyright (c) 2018 Intel Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
> + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> + * including the hotplug ones. Generic Event Device allows platforms
> + * to handle interrupts in ACPI ASL statements. It follows a very
> + * similar approach like the _EVT method from GPIO events. All
> + * interrupts are listed in  _CRS and the handler is written in _EVT> + * method. Here, we use a single interrupt for the GED device, relying
> + * on IO memory region to communicate the type of device affected by
> + * the interrupt. This way, we can support up to 32 events with a
> + * unique interrupt.
> + *
> + * Here is an example.
> + *
> + * Device (\_SB.GED)
> + * {
> + *     Name (_HID, "ACPI0013")
> + *     Name (_UID, Zero)
> + *     Name (_CRS, ResourceTemplate ()
> + *     {
> + *         Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, )
> + *         {
> + *              0x00000029,
> + *         }
> + *     })
> + *     OperationRegion (IREG, SystemMemory, 0x09080000, 0x04)
> + *     Field (IREG, DWordAcc, NoLock, WriteAsZeros)
> + *     {
> + *         ISEL,   32
> + *     }
> + *
> + *     Method (_EVT, 1, Serialized)  // _EVT: Event
> + *     {
> + *         Local0 = ISEL // ISEL = IO memory region which specifies the
> + *                       // device type.
> + *         If (((Local0 & irq0) == irq0))
> + *         {
> + *             MethodEvent0()
> + *         }
> + *         ElseIf ((Local0 & irq1) == irq1)
> + *         {
> + *             MethodEvent1()
> + *         }
> + *         ...
> + *     }
> + * }
> + *
> + */
> +
> +#ifndef HW_ACPI_GED_H
> +#define HW_ACPI_GED_H
> +
> +#include "hw/acpi/memory_hotplug.h"
> +
> +#define TYPE_ACPI_GED "acpi-ged"
> +#define ACPI_GED(obj) \
> +    OBJECT_CHECK(AcpiGedState, (obj), TYPE_ACPI_GED)
> +
> +#define ACPI_GED_IRQ_SEL_OFFSET 0x0
> +#define ACPI_GED_IRQ_SEL_LEN    0x4
> +#define ACPI_GED_IRQ_SEL_MEM    0x1
> +#define ACPI_GED_REG_LEN        0x4
> +
> +#define GED_DEVICE      "GED"
> +#define AML_GED_IRQ_REG "IREG"
> +#define AML_GED_IRQ_SEL "ISEL"
> +
> +typedef enum {
> +    GED_MEMORY_HOTPLUG = 1,
> +} GedEventType;
> +
> +/*
> + * Platforms need to specify their own GedEvent array
> + * to describe what kind of events they want to support
> + * through GED.
> + */
> +typedef struct GedEvent {
> +    uint32_t     selector;
> +    GedEventType event;
> +} GedEvent;
> +
> +typedef struct GEDState {
> +    MemoryRegion io;
> +    uint32_t     sel;
> +    uint32_t     irq;
> +    qemu_irq     *gsi;
so I am not sure why with need gsi + irq.
> +    QemuMutex    lock;
> +} GEDState;
> +
> +
> +typedef struct AcpiGedState {
> +    DeviceClass parent_obj;
> +    MemHotplugState memhp_state;
> +    hwaddr memhp_base;
> +    void *gsi;
> +    hwaddr ged_base;
> +    GEDState ged_state;
> +    uint32_t ged_irq;
> +    void *ged_events;
> +    uint32_t ged_events_size;
> +} AcpiGedState;
> +
> +void build_ged_aml(Aml *table, const char* name, HotplugHandler *hotplug_dev,
> +                   uint32_t ged_irq, AmlRegionSpace rs);
> +
> +#endif
> 
Besides, this looks good to me.

Thanks

Eric


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 5/8] hw/arm/virt: Enable device memory cold/hot plug with ACPI boot
@ 2019-04-30 16:34     ` Auger Eric
  0 siblings, 0 replies; 62+ messages in thread
From: Auger Eric @ 2019-04-30 16:34 UTC (permalink / raw)
  To: Shameer Kolothum, qemu-devel, qemu-arm, imammedo
  Cc: peter.maydell, shannon.zhaosl, sameo, sebastien.boeuf, xuwei5,
	lersek, ard.biesheuvel, linuxarm

Hi Shameer,

On 4/9/19 12:29 PM, Shameer Kolothum wrote:
> This initializes the GED device with base memory and irq, configures
> ged memory hotplug event and builds the corresponding aml code. GED
> irq routing to Guest is also enabled. With this, both hot and cold
> plug of device memory is enabled now for Guest with ACPI boot.
> 
> Memory cold plug support with Guest DT boot is not yet supported.
> 
> Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
Individual history logs may be helpful to follow the changes (change in
MMIO reggion size, ...)
> ---
>  default-configs/arm-softmmu.mak |  2 ++
>  hw/arm/virt-acpi-build.c        |  9 ++++++
>  hw/arm/virt.c                   | 61 +++++++++++++++++++++++++++++++++++++++--
>  include/hw/arm/virt.h           |  4 +++
>  4 files changed, 73 insertions(+), 3 deletions(-)
> 
> diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
> index 9f4b803..c9a9b34 100644
> --- a/default-configs/arm-softmmu.mak
> +++ b/default-configs/arm-softmmu.mak
> @@ -163,3 +163,5 @@ CONFIG_LSI_SCSI_PCI=y
>  
>  CONFIG_MEM_DEVICE=y
>  CONFIG_DIMM=y
> +CONFIG_ACPI_MEMORY_HOTPLUG=y
> +CONFIG_ACPI_HW_REDUCED=y
> diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
> index bf9c0bc..1ad394b 100644
> --- a/hw/arm/virt-acpi-build.c
> +++ b/hw/arm/virt-acpi-build.c
> @@ -40,6 +40,8 @@
>  #include "hw/loader.h"
>  #include "hw/hw.h"
>  #include "hw/acpi/aml-build.h"
> +#include "hw/acpi/memory_hotplug.h"
> +#include "hw/acpi/generic_event_device.h"
>  #include "hw/pci/pcie_host.h"
>  #include "hw/pci/pci.h"
>  #include "hw/arm/virt.h"
> @@ -727,6 +729,7 @@ static void
>  build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
>  {
>      Aml *scope, *dsdt;
> +    MachineState *ms = MACHINE(vms);
>      const MemMapEntry *memmap = vms->memmap;
>      const int *irqmap = vms->irqmap;
>  
> @@ -753,6 +756,12 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
>                         (irqmap[VIRT_GPIO] + ARM_SPI_BASE));
>      acpi_dsdt_add_power_button(scope);
>  
> +    build_ged_aml(scope, "\\_SB."GED_DEVICE, HOTPLUG_HANDLER(vms->acpi_dev),
> +                  irqmap[VIRT_ACPI_GED] + ARM_SPI_BASE, AML_SYSTEM_MEMORY);
> +
> +    build_memory_hotplug_aml(scope, ms->ram_slots, "\\_SB", NULL,
> +                             AML_SYSTEM_MEMORY);
> +
>      aml_append(dsdt, scope);
>  
>      /* copy AML table into ACPI tables blob and patch header there */
> diff --git a/hw/arm/virt.c b/hw/arm/virt.c
> index da516b3..8179b3e 100644
> --- a/hw/arm/virt.c
> +++ b/hw/arm/virt.c
> @@ -63,6 +63,7 @@
>  #include "target/arm/internals.h"
>  #include "hw/mem/pc-dimm.h"
>  #include "hw/mem/nvdimm.h"
> +#include "hw/acpi/generic_event_device.h"
>  
>  #define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \
>      static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
> @@ -133,6 +134,8 @@ static const MemMapEntry base_memmap[] = {
>      [VIRT_GPIO] =               { 0x09030000, 0x00001000 },
>      [VIRT_SECURE_UART] =        { 0x09040000, 0x00001000 },
>      [VIRT_SMMU] =               { 0x09050000, 0x00020000 },
> +    [VIRT_PCDIMM_ACPI] =        { 0x09070000, MEMORY_HOTPLUG_IO_LEN },
> +    [VIRT_ACPI_GED] =           { 0x09080000, ACPI_GED_REG_LEN },>      [VIRT_MMIO] =               { 0x0a000000, 0x00000200 },
>      /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
>      [VIRT_PLATFORM_BUS] =       { 0x0c000000, 0x02000000 },
> @@ -168,6 +171,7 @@ static const int a15irqmap[] = {
>      [VIRT_PCIE] = 3, /* ... to 6 */
>      [VIRT_GPIO] = 7,
>      [VIRT_SECURE_UART] = 8,
> +    [VIRT_ACPI_GED] = 9,
>      [VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */
>      [VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */
>      [VIRT_SMMU] = 74,    /* ...to 74 + NUM_SMMU_IRQS - 1 */
> @@ -183,6 +187,13 @@ static const char *valid_cpus[] = {
>      ARM_CPU_TYPE_NAME("max"),
>  };
>  
> +static GedEvent ged_events[] = {
> +    {
> +        .selector = ACPI_GED_IRQ_SEL_MEM,
> +        .event    = GED_MEMORY_HOTPLUG,
> +    },
> +};
> +
>  static bool cpu_type_valid(const char *cpu)
>  {
>      int i;
> @@ -516,6 +527,26 @@ static void fdt_add_pmu_nodes(const VirtMachineState *vms)
>      }
>  }
>  
> +static inline DeviceState *create_acpi_ged(VirtMachineState *vms)
> +{
> +    DeviceState *dev;
> +
> +    dev = DEVICE(object_new(TYPE_ACPI_GED));
> +    qdev_prop_set_uint64(dev, "memhp-base",
> +                         vms->memmap[VIRT_PCDIMM_ACPI].base);
> +    qdev_prop_set_ptr(dev, "gsi", vms->gsi);
> +    qdev_prop_set_uint64(dev, "ged-base", vms->memmap[VIRT_ACPI_GED].base);
> +    qdev_prop_set_uint32(dev, "ged-irq", vms->irqmap[VIRT_ACPI_GED]);
so as commented earlier I wonder why we can directly pass the right gsi.
> +    qdev_prop_set_ptr(dev, "ged-events", ged_events);
> +    qdev_prop_set_uint32(dev, "ged-events-size", ARRAY_SIZE(ged_events));
> +
> +    object_property_add_child(qdev_get_machine(), "acpi-ged",
> +                              OBJECT(dev), NULL);
> +    qdev_init_nofail(dev);
> +
> +    return dev;
> +}
> +
>  static void create_its(VirtMachineState *vms, DeviceState *gicdev)
>  {
>      const char *itsclass = its_class_name();
> @@ -555,6 +586,12 @@ static void create_v2m(VirtMachineState *vms, qemu_irq *pic)
>      fdt_add_v2m_gic_node(vms);
>  }
>  
> +static void virt_gsi_handler(void *opaque, int n, int level)
> +{
> +    qemu_irq *gic_irq = opaque;
> +    qemu_set_irq(gic_irq[n], level);
In https://patchwork.kernel.org/patch/10863285/, I asked why this gsi
routing was necessary. Please forgive me if I missed your answer but I
still don't get why it is necessary.
> +}
> +
>  static void create_gic(VirtMachineState *vms, qemu_irq *pic)
>  {
>      /* We create a standalone GIC */
> @@ -670,6 +707,8 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic)
>          pic[i] = qdev_get_gpio_in(gicdev, i);
>      }
>  
> +    vms->gsi = qemu_allocate_irqs(virt_gsi_handler, pic, NUM_IRQS);
> +
>      fdt_add_gic_node(vms);
>  
>      if (type == 3 && vms->its) {
> @@ -1418,7 +1457,7 @@ static void machvirt_init(MachineState *machine)
>      VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(machine);
>      MachineClass *mc = MACHINE_GET_CLASS(machine);
>      const CPUArchIdList *possible_cpus;
> -    qemu_irq pic[NUM_IRQS];
> +    qemu_irq *pic;
>      MemoryRegion *sysmem = get_system_memory();
>      MemoryRegion *secure_sysmem = NULL;
>      int n, virt_max_cpus;
> @@ -1614,6 +1653,7 @@ static void machvirt_init(MachineState *machine)
>  
>      create_flash(vms, sysmem, secure_sysmem ? secure_sysmem : sysmem);
>  
> +    pic = g_new0(qemu_irq, NUM_IRQS);
>      create_gic(vms, pic);
>  
>      fdt_add_pmu_nodes(vms);
> @@ -1644,6 +1684,8 @@ static void machvirt_init(MachineState *machine)
>  
>      create_platform_bus(vms, pic);
>  
> +    vms->acpi_dev = create_acpi_ged(vms);
> +
>      vms->bootinfo.ram_size = machine->ram_size;
>      vms->bootinfo.kernel_filename = machine->kernel_filename;
>      vms->bootinfo.kernel_cmdline = machine->kernel_cmdline;
> @@ -1811,21 +1853,34 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms)
>  static void virt_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
>                                   Error **errp)
>  {
> -    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> -        error_setg(errp, "memory cold/hot plug is not yet supported");
> +    VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
> +    const bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM);
> +
> +    if (is_nvdimm) {
> +        error_setg(errp, "nvdimm is not yet supported");
>          return;
>      }
>  
> +    if (!vms->acpi_dev) {
> +        error_setg(errp, "memory hotplug is not enabled: missing acpi device");
> +        return;
> +    }
> +
> +    hotplug_handler_pre_plug(HOTPLUG_HANDLER(vms->acpi_dev), dev, errp);
> +
>      pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), NULL, errp);
>  }
>  
>  static void virt_memory_plug(HotplugHandler *hotplug_dev,
>                               DeviceState *dev, Error **errp)
>  {
> +    HotplugHandlerClass *hhc;
>      VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
>  
>      pc_dimm_plug(PC_DIMM(dev), MACHINE(vms), NULL);
>  
> +    hhc = HOTPLUG_HANDLER_GET_CLASS(vms->acpi_dev);
> +    hhc->plug(HOTPLUG_HANDLER(vms->acpi_dev), dev, NULL);
>  }
>  
>  static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
> diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
> index 507517c..9c6b0b1 100644
> --- a/include/hw/arm/virt.h
> +++ b/include/hw/arm/virt.h
> @@ -77,6 +77,8 @@ enum {
>      VIRT_GPIO,
>      VIRT_SECURE_UART,
>      VIRT_SECURE_MEM,
> +    VIRT_PCDIMM_ACPI,
> +    VIRT_ACPI_GED,
>      VIRT_LOWMEMMAP_LAST,
>  };
>  
> @@ -132,6 +134,8 @@ typedef struct {
>      uint32_t iommu_phandle;
>      int psci_conduit;
>      hwaddr highest_gpa;
> +    DeviceState *acpi_dev;
> +    qemu_irq *gsi;
>  } VirtMachineState;
>  
>  #define VIRT_ECAM_ID(high) (high ? VIRT_HIGH_PCIE_ECAM : VIRT_PCIE_ECAM)
Thanks

Eric
> 

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 5/8] hw/arm/virt: Enable device memory cold/hot plug with ACPI boot
@ 2019-04-30 16:34     ` Auger Eric
  0 siblings, 0 replies; 62+ messages in thread
From: Auger Eric @ 2019-04-30 16:34 UTC (permalink / raw)
  To: Shameer Kolothum, qemu-devel, qemu-arm, imammedo
  Cc: peter.maydell, sameo, ard.biesheuvel, linuxarm, xuwei5,
	shannon.zhaosl, sebastien.boeuf, lersek

Hi Shameer,

On 4/9/19 12:29 PM, Shameer Kolothum wrote:
> This initializes the GED device with base memory and irq, configures
> ged memory hotplug event and builds the corresponding aml code. GED
> irq routing to Guest is also enabled. With this, both hot and cold
> plug of device memory is enabled now for Guest with ACPI boot.
> 
> Memory cold plug support with Guest DT boot is not yet supported.
> 
> Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
Individual history logs may be helpful to follow the changes (change in
MMIO reggion size, ...)
> ---
>  default-configs/arm-softmmu.mak |  2 ++
>  hw/arm/virt-acpi-build.c        |  9 ++++++
>  hw/arm/virt.c                   | 61 +++++++++++++++++++++++++++++++++++++++--
>  include/hw/arm/virt.h           |  4 +++
>  4 files changed, 73 insertions(+), 3 deletions(-)
> 
> diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
> index 9f4b803..c9a9b34 100644
> --- a/default-configs/arm-softmmu.mak
> +++ b/default-configs/arm-softmmu.mak
> @@ -163,3 +163,5 @@ CONFIG_LSI_SCSI_PCI=y
>  
>  CONFIG_MEM_DEVICE=y
>  CONFIG_DIMM=y
> +CONFIG_ACPI_MEMORY_HOTPLUG=y
> +CONFIG_ACPI_HW_REDUCED=y
> diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
> index bf9c0bc..1ad394b 100644
> --- a/hw/arm/virt-acpi-build.c
> +++ b/hw/arm/virt-acpi-build.c
> @@ -40,6 +40,8 @@
>  #include "hw/loader.h"
>  #include "hw/hw.h"
>  #include "hw/acpi/aml-build.h"
> +#include "hw/acpi/memory_hotplug.h"
> +#include "hw/acpi/generic_event_device.h"
>  #include "hw/pci/pcie_host.h"
>  #include "hw/pci/pci.h"
>  #include "hw/arm/virt.h"
> @@ -727,6 +729,7 @@ static void
>  build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
>  {
>      Aml *scope, *dsdt;
> +    MachineState *ms = MACHINE(vms);
>      const MemMapEntry *memmap = vms->memmap;
>      const int *irqmap = vms->irqmap;
>  
> @@ -753,6 +756,12 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
>                         (irqmap[VIRT_GPIO] + ARM_SPI_BASE));
>      acpi_dsdt_add_power_button(scope);
>  
> +    build_ged_aml(scope, "\\_SB."GED_DEVICE, HOTPLUG_HANDLER(vms->acpi_dev),
> +                  irqmap[VIRT_ACPI_GED] + ARM_SPI_BASE, AML_SYSTEM_MEMORY);
> +
> +    build_memory_hotplug_aml(scope, ms->ram_slots, "\\_SB", NULL,
> +                             AML_SYSTEM_MEMORY);
> +
>      aml_append(dsdt, scope);
>  
>      /* copy AML table into ACPI tables blob and patch header there */
> diff --git a/hw/arm/virt.c b/hw/arm/virt.c
> index da516b3..8179b3e 100644
> --- a/hw/arm/virt.c
> +++ b/hw/arm/virt.c
> @@ -63,6 +63,7 @@
>  #include "target/arm/internals.h"
>  #include "hw/mem/pc-dimm.h"
>  #include "hw/mem/nvdimm.h"
> +#include "hw/acpi/generic_event_device.h"
>  
>  #define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \
>      static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
> @@ -133,6 +134,8 @@ static const MemMapEntry base_memmap[] = {
>      [VIRT_GPIO] =               { 0x09030000, 0x00001000 },
>      [VIRT_SECURE_UART] =        { 0x09040000, 0x00001000 },
>      [VIRT_SMMU] =               { 0x09050000, 0x00020000 },
> +    [VIRT_PCDIMM_ACPI] =        { 0x09070000, MEMORY_HOTPLUG_IO_LEN },
> +    [VIRT_ACPI_GED] =           { 0x09080000, ACPI_GED_REG_LEN },>      [VIRT_MMIO] =               { 0x0a000000, 0x00000200 },
>      /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
>      [VIRT_PLATFORM_BUS] =       { 0x0c000000, 0x02000000 },
> @@ -168,6 +171,7 @@ static const int a15irqmap[] = {
>      [VIRT_PCIE] = 3, /* ... to 6 */
>      [VIRT_GPIO] = 7,
>      [VIRT_SECURE_UART] = 8,
> +    [VIRT_ACPI_GED] = 9,
>      [VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */
>      [VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */
>      [VIRT_SMMU] = 74,    /* ...to 74 + NUM_SMMU_IRQS - 1 */
> @@ -183,6 +187,13 @@ static const char *valid_cpus[] = {
>      ARM_CPU_TYPE_NAME("max"),
>  };
>  
> +static GedEvent ged_events[] = {
> +    {
> +        .selector = ACPI_GED_IRQ_SEL_MEM,
> +        .event    = GED_MEMORY_HOTPLUG,
> +    },
> +};
> +
>  static bool cpu_type_valid(const char *cpu)
>  {
>      int i;
> @@ -516,6 +527,26 @@ static void fdt_add_pmu_nodes(const VirtMachineState *vms)
>      }
>  }
>  
> +static inline DeviceState *create_acpi_ged(VirtMachineState *vms)
> +{
> +    DeviceState *dev;
> +
> +    dev = DEVICE(object_new(TYPE_ACPI_GED));
> +    qdev_prop_set_uint64(dev, "memhp-base",
> +                         vms->memmap[VIRT_PCDIMM_ACPI].base);
> +    qdev_prop_set_ptr(dev, "gsi", vms->gsi);
> +    qdev_prop_set_uint64(dev, "ged-base", vms->memmap[VIRT_ACPI_GED].base);
> +    qdev_prop_set_uint32(dev, "ged-irq", vms->irqmap[VIRT_ACPI_GED]);
so as commented earlier I wonder why we can directly pass the right gsi.
> +    qdev_prop_set_ptr(dev, "ged-events", ged_events);
> +    qdev_prop_set_uint32(dev, "ged-events-size", ARRAY_SIZE(ged_events));
> +
> +    object_property_add_child(qdev_get_machine(), "acpi-ged",
> +                              OBJECT(dev), NULL);
> +    qdev_init_nofail(dev);
> +
> +    return dev;
> +}
> +
>  static void create_its(VirtMachineState *vms, DeviceState *gicdev)
>  {
>      const char *itsclass = its_class_name();
> @@ -555,6 +586,12 @@ static void create_v2m(VirtMachineState *vms, qemu_irq *pic)
>      fdt_add_v2m_gic_node(vms);
>  }
>  
> +static void virt_gsi_handler(void *opaque, int n, int level)
> +{
> +    qemu_irq *gic_irq = opaque;
> +    qemu_set_irq(gic_irq[n], level);
In https://patchwork.kernel.org/patch/10863285/, I asked why this gsi
routing was necessary. Please forgive me if I missed your answer but I
still don't get why it is necessary.
> +}
> +
>  static void create_gic(VirtMachineState *vms, qemu_irq *pic)
>  {
>      /* We create a standalone GIC */
> @@ -670,6 +707,8 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic)
>          pic[i] = qdev_get_gpio_in(gicdev, i);
>      }
>  
> +    vms->gsi = qemu_allocate_irqs(virt_gsi_handler, pic, NUM_IRQS);
> +
>      fdt_add_gic_node(vms);
>  
>      if (type == 3 && vms->its) {
> @@ -1418,7 +1457,7 @@ static void machvirt_init(MachineState *machine)
>      VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(machine);
>      MachineClass *mc = MACHINE_GET_CLASS(machine);
>      const CPUArchIdList *possible_cpus;
> -    qemu_irq pic[NUM_IRQS];
> +    qemu_irq *pic;
>      MemoryRegion *sysmem = get_system_memory();
>      MemoryRegion *secure_sysmem = NULL;
>      int n, virt_max_cpus;
> @@ -1614,6 +1653,7 @@ static void machvirt_init(MachineState *machine)
>  
>      create_flash(vms, sysmem, secure_sysmem ? secure_sysmem : sysmem);
>  
> +    pic = g_new0(qemu_irq, NUM_IRQS);
>      create_gic(vms, pic);
>  
>      fdt_add_pmu_nodes(vms);
> @@ -1644,6 +1684,8 @@ static void machvirt_init(MachineState *machine)
>  
>      create_platform_bus(vms, pic);
>  
> +    vms->acpi_dev = create_acpi_ged(vms);
> +
>      vms->bootinfo.ram_size = machine->ram_size;
>      vms->bootinfo.kernel_filename = machine->kernel_filename;
>      vms->bootinfo.kernel_cmdline = machine->kernel_cmdline;
> @@ -1811,21 +1853,34 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms)
>  static void virt_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
>                                   Error **errp)
>  {
> -    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> -        error_setg(errp, "memory cold/hot plug is not yet supported");
> +    VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
> +    const bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM);
> +
> +    if (is_nvdimm) {
> +        error_setg(errp, "nvdimm is not yet supported");
>          return;
>      }
>  
> +    if (!vms->acpi_dev) {
> +        error_setg(errp, "memory hotplug is not enabled: missing acpi device");
> +        return;
> +    }
> +
> +    hotplug_handler_pre_plug(HOTPLUG_HANDLER(vms->acpi_dev), dev, errp);
> +
>      pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), NULL, errp);
>  }
>  
>  static void virt_memory_plug(HotplugHandler *hotplug_dev,
>                               DeviceState *dev, Error **errp)
>  {
> +    HotplugHandlerClass *hhc;
>      VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
>  
>      pc_dimm_plug(PC_DIMM(dev), MACHINE(vms), NULL);
>  
> +    hhc = HOTPLUG_HANDLER_GET_CLASS(vms->acpi_dev);
> +    hhc->plug(HOTPLUG_HANDLER(vms->acpi_dev), dev, NULL);
>  }
>  
>  static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
> diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
> index 507517c..9c6b0b1 100644
> --- a/include/hw/arm/virt.h
> +++ b/include/hw/arm/virt.h
> @@ -77,6 +77,8 @@ enum {
>      VIRT_GPIO,
>      VIRT_SECURE_UART,
>      VIRT_SECURE_MEM,
> +    VIRT_PCDIMM_ACPI,
> +    VIRT_ACPI_GED,
>      VIRT_LOWMEMMAP_LAST,
>  };
>  
> @@ -132,6 +134,8 @@ typedef struct {
>      uint32_t iommu_phandle;
>      int psci_conduit;
>      hwaddr highest_gpa;
> +    DeviceState *acpi_dev;
> +    qemu_irq *gsi;
>  } VirtMachineState;
>  
>  #define VIRT_ECAM_ID(high) (high ? VIRT_HIGH_PCIE_ECAM : VIRT_PCIE_ECAM)
Thanks

Eric
> 


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
@ 2019-05-01 10:40       ` Shameerali Kolothum Thodi
  0 siblings, 0 replies; 62+ messages in thread
From: Shameerali Kolothum Thodi @ 2019-05-01 10:40 UTC (permalink / raw)
  To: Auger Eric, qemu-devel, qemu-arm, imammedo
  Cc: peter.maydell, shannon.zhaosl, sameo, sebastien.boeuf, xuwei (O),
	lersek, ard.biesheuvel, Linuxarm

Hi Eric,

> -----Original Message-----
> From: Auger Eric [mailto:eric.auger@redhat.com]
> Sent: 30 April 2019 16:50
> To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>;
> qemu-devel@nongnu.org; qemu-arm@nongnu.org; imammedo@redhat.com
> Cc: peter.maydell@linaro.org; shannon.zhaosl@gmail.com;
> sameo@linux.intel.com; sebastien.boeuf@intel.com; xuwei (O)
> <xuwei5@huawei.com>; lersek@redhat.com; ard.biesheuvel@linaro.org;
> Linuxarm <linuxarm@huawei.com>
> Subject: Re: [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
> 
> Hi Shameer,
> 
> On 4/9/19 12:29 PM, Shameer Kolothum wrote:
> > From: Samuel Ortiz <sameo@linux.intel.com>
> >
> > The ACPI Generic Event Device (GED) is a hardware-reduced specific
> > device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> > including the hotplug ones.This patch generates the AML code that
> > defines GEDs.
> >
> > Platforms need to specify their own GedEvent array to describe what
> > kind of events they want to support through GED.  Also this uses a
> > a single interrupt for the  GED device, relying on IO memory region
> > to communicate the type of device affected by the interrupt. This
> > way, we can support up to 32 events with a unique interrupt.
> >
> > This supports only memory hotplug for now.
> >
> > Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
> > Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
> > Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> > ---
> >  hw/acpi/Kconfig                        |   4 +
> >  hw/acpi/Makefile.objs                  |   1 +
> >  hw/acpi/generic_event_device.c         | 311
> +++++++++++++++++++++++++++++++++
> >  include/hw/acpi/generic_event_device.h | 121 +++++++++++++
> >  4 files changed, 437 insertions(+)
> >  create mode 100644 hw/acpi/generic_event_device.c
> >  create mode 100644 include/hw/acpi/generic_event_device.h
> >
> > diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig
> > index eca3bee..01a8b41 100644
> > --- a/hw/acpi/Kconfig
> > +++ b/hw/acpi/Kconfig
> > @@ -27,3 +27,7 @@ config ACPI_VMGENID
> >      bool
> >      default y
> >      depends on PC
> > +
> > +config ACPI_HW_REDUCED
> > +    bool
> > +    depends on ACPI
> > diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
> > index 2d46e37..b753232 100644
> > --- a/hw/acpi/Makefile.objs
> > +++ b/hw/acpi/Makefile.objs
> > @@ -6,6 +6,7 @@ common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) +=
> memory_hotplug.o
> >  common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu.o
> >  common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
> >  common-obj-$(CONFIG_ACPI_VMGENID) += vmgenid.o
> > +common-obj-$(CONFIG_ACPI_HW_REDUCED) += generic_event_device.o
> >  common-obj-$(call lnot,$(CONFIG_ACPI_X86)) += acpi-stub.o
> >
> >  common-obj-y += acpi_interface.o
> > diff --git a/hw/acpi/generic_event_device.c
> b/hw/acpi/generic_event_device.c
> > new file mode 100644
> > index 0000000..856ca04
> > --- /dev/null
> > +++ b/hw/acpi/generic_event_device.c
> > @@ -0,0 +1,311 @@
> > +/*
> > + *
> > + * Copyright (c) 2018 Intel Corporation
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2 or later, as published by the Free Software Foundation.
> I am not sure we need below statements: see hw/misc/armsse-mhu.c for a
> recent added file.

Ok. I will get rid of this.
 
> > + *
> > + * This program is distributed in the hope it will be useful, but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of
> MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
> License for
> > + * more details.
> > + *
> > + * You should have received a copy of the GNU General Public License along
> with
> > + * this program.  If not, see <http://www.gnu.org/licenses/>.
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +#include "qapi/error.h"
> > +#include "exec/address-spaces.h"
> > +#include "hw/sysbus.h"
> > +#include "hw/acpi/acpi.h"
> > +#include "hw/acpi/generic_event_device.h"
> > +#include "hw/mem/pc-dimm.h"
> > +
> > +static Aml *ged_event_aml(const GedEvent *event)
> > +{
> > +
> > +    if (!event) {
> > +        return NULL;
> > +    }
> > +
> > +    switch (event->event) {
> > +    case GED_MEMORY_HOTPLUG:
> > +        /* We run a complete memory SCAN when getting a memory
> hotplug event */
> > +        return aml_call0(MEMORY_DEVICES_CONTAINER "."
> MEMORY_SLOT_SCAN_METHOD);
> > +    default:
> > +        break;
> > +    }
> > +
> > +    return NULL;
> > +}
> > +
> > +/*
> > + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
> > + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> > + * including the hotplug ones. Platforms need to specify their own
> > + * GedEvent array to describe what kind of events they want to support
> > + * through GED. This routine uses a single interrupt for the GED device,
> > + * relying on IO memory region to communicate the type of device
> > + * affected by the interrupt. This way, we can support up to 32 events
> > + * with a unique interrupt.
> > + */
> > +void build_ged_aml(Aml *table, const char *name, HotplugHandler
> *hotplug_dev,
> > +                   uint32_t ged_irq, AmlRegionSpace rs)
> > +{
> > +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> > +    GedEvent *ged_events = s->ged_events;
> > +    Aml *crs = aml_resource_template();
> > +    Aml *evt, *field;
> > +    Aml *dev = aml_device("%s", name);
> > +    Aml *irq_sel = aml_local(0);
> > +    Aml *isel = aml_name(AML_GED_IRQ_SEL);
> > +    uint32_t i;
> > +
> > +    if (!s->ged_base || !ged_events || !s->ged_events_size) {
> > +        return;
> > +    }
> > +
> > +    /* _CRS interrupt */
> > +    aml_append(crs, aml_interrupt(AML_CONSUMER, AML_EDGE,
> AML_ACTIVE_HIGH,
> > +                                  AML_EXCLUSIVE, &ged_irq, 1));
> > +    /*
> > +     * For each GED event we:
> > +     * - Add an interrupt to the CRS section.
> > +     * - Add a conditional block for each event, inside a while loop.
> > +     *   This is semantically equivalent to a switch/case implementation.
> > +     */
> > +    evt = aml_method("_EVT", 1, AML_SERIALIZED);
> > +    {
> > +        Aml *ged_aml;
> > +        Aml *if_ctx;
> > +
> > +        /* Local0 = ISEL */
> > +        aml_append(evt, aml_store(isel, irq_sel));
> > +
> > +        /*
> > +         * Here we want to call a method for each supported GED event
> type.
> > +         * The resulting ASL code looks like:
> > +         *
> > +         * Local0 = ISEL
> > +         * If ((Local0 & irq0) == irq0)
> > +         * {
> > +         *     MethodEvent0()
> > +         * }
> > +         *
> > +         * If ((Local0 & irq1) == irq1)
> > +         * {
> > +         *     MethodEvent1()
> > +         * }
> > +         * ...
> > +         */
> > +
> > +        for (i = 0; i < s->ged_events_size; i++) {
> > +            ged_aml = ged_event_aml(&ged_events[i]);
> > +            if (!ged_aml) {
> > +                continue;
> > +            }
> > +
> > +            /* If ((Local1 == irq))*/
> > +            if_ctx = aml_if(aml_equal(aml_and(irq_sel,
> > +
> aml_int(ged_events[i].selector), NULL),
> > +
> aml_int(ged_events[i].selector)));
> > +            {
> > +                /* AML for this specific type of event */
> > +                aml_append(if_ctx, ged_aml);
> > +            }
> > +
> > +            /*
> > +             * We append the first "if" to the "while" context.
> > +             * Other "if"s will be "elseif"s.
> Are we sure about that:
> in hw/acpi/nvdimm.c nvdimm_build_common_dsm() I can see an aml_else() is
> added inbetween aml_if's?

Right. I think the comment here is misleading. This will be as mentioned in the
comment block above,

 If ((Local0 & irq0) == irq0)
 {
     MethodEvent0()
 }

 If ((Local0 & irq1) == irq1)
 {
     MethodEvent1()
 }

 And this should do the job. Do we really need ElseIf block here?

> > +             */
> > +            aml_append(evt, if_ctx);
> > +        }
> > +    }
> > +
> > +    aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0013")));
> > +    aml_append(dev, aml_name_decl("_UID", aml_string(GED_DEVICE)));
> > +    aml_append(dev, aml_name_decl("_CRS", crs));
> > +
> > +    /* Append IO region */
> > +    aml_append(dev, aml_operation_region(AML_GED_IRQ_REG, rs,
> > +               aml_int(s->ged_base + ACPI_GED_IRQ_SEL_OFFSET),
> > +               ACPI_GED_IRQ_SEL_LEN));
> > +    field = aml_field(AML_GED_IRQ_REG, AML_DWORD_ACC,
> AML_NOLOCK,
> > +                      AML_WRITE_AS_ZEROS);
> > +    aml_append(field, aml_named_field(AML_GED_IRQ_SEL,
> > +                                      ACPI_GED_IRQ_SEL_LEN *
> 8));
> nit s/8/BITS_PER_BYTE as done in nvdimm.c.

Ok.

> > +    aml_append(dev, field);
> > +
> > +    /* Append _EVT method */
> > +    aml_append(dev, evt);
> > +
> > +    aml_append(table, dev);
> > +}
> > +
> > +/* Memory read by the GED _EVT AML dynamic method */
> > +static uint64_t ged_read(void *opaque, hwaddr addr, unsigned size)
> > +{
> > +    uint64_t val = 0;
> > +    GEDState *ged_st = opaque;
> > +
> > +    switch (addr) {
> > +    case ACPI_GED_IRQ_SEL_OFFSET:
> > +        /* Read the selector value and reset it */
> > +        qemu_mutex_lock(&ged_st->lock);
> > +        val = ged_st->sel;
> > +        ged_st->sel = 0;
> > +        qemu_mutex_unlock(&ged_st->lock);
> > +        break;
> > +    default:
> > +        break;
> > +    }
> > +
> > +    return val;
> > +}
> > +
> > +/* Nothing is expected to be written to the GED memory region */
> > +static void ged_write(void *opaque, hwaddr addr, uint64_t data,
> > +                      unsigned int size)
> > +{
> > +}
> > +
> > +static const MemoryRegionOps ged_ops = {
> > +    .read = ged_read,
> > +    .write = ged_write,
> > +    .endianness = DEVICE_LITTLE_ENDIAN,
> > +    .valid = {
> > +        .min_access_size = 4,
> > +        .max_access_size = 4,
> > +    },
> > +};
> > +
> > +static void acpi_ged_event(GEDState *ged_st, uint32_t ged_irq_sel)
> > +{
> > +    /*
> I would personally inline that code in void acpi_ged_send_event()

Ok.

> > +     * Set the GED IRQ selector to the expected device type value. This
> > +     * way, the ACPI method will be able to trigger the right code based
> > +     * on a unique IRQ.
> > +     */
> > +    qemu_mutex_lock(&ged_st->lock);
> > +    ged_st->sel = ged_irq_sel;
> > +    qemu_mutex_unlock(&ged_st->lock);
> > +
> > +    /* Trigger the event by sending an interrupt to the guest. */
> > +    qemu_irq_pulse(ged_st->gsi[ged_st->irq]);
> I don't really understand why we need this array. Doesn't The GED use a
> single IRQ. So why can't you store the precise qemu_irq pointer directly
> through the propery (DEFINE_PROP_PTR("gsi", AcpiGedState, gsi))?

Yes. You have raised this point previously as well. Sorry, I missed it when I
reworked the GED type from SYS_BUS_DEVICE to TYPE_DEVICE. I think
we can get rid of this array and the GED/gsi routing done in virt.c.


> > +}
> > +
> > +static void acpi_ged_init(MemoryRegion *as, DeviceState *dev, GEDState
> *ged_st)
> > +{
> > +    AcpiGedState *s = ACPI_GED(dev);
> > +
> > +    assert(s->ged_base);
> > +
> > +    ged_st->irq = s->ged_irq;
> > +    ged_st->gsi = s->gsi;
> > +    qemu_mutex_init(&ged_st->lock);
> > +    memory_region_init_io(&ged_st->io, OBJECT(dev), &ged_ops, ged_st,
> > +                          "acpi-ged-event", ACPI_GED_REG_LEN);
> > +    memory_region_add_subregion(as, s->ged_base, &ged_st->io);
> > +}
> > +
> > +static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
> > +                                    DeviceState *dev, Error **errp)
> > +{
> > +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> > +
> > +    if (s->memhp_state.is_enabled &&
> > +        object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> > +            acpi_memory_plug_cb(hotplug_dev, &s->memhp_state,
> > +                                dev, errp);
> > +    } else {
> > +        error_setg(errp, "virt: device plug request for unsupported
> device"
> > +                   " type: %s", object_get_typename(OBJECT(dev)));
> > +    }
> > +}
> > +
> > +static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits
> ev)
> > +{
> > +    AcpiGedState *s = ACPI_GED(adev);
> > +    uint32_t sel;
> > +
> > +    if (ev & ACPI_MEMORY_HOTPLUG_STATUS) {
> > +        sel = ACPI_GED_IRQ_SEL_MEM;
> > +    } else {
> > +        /* Unknown event. Return without generating interrupt. */
> > +        return;
> > +    }
> > +
> > +    /*
> > +     * We inject the hotplug interrupt. The IRQ selector will make
> > +     * the difference from the ACPI table.
> > +     */
> > +    acpi_ged_event(&s->ged_state, sel);
> > +}
> > +
> > +static void acpi_ged_device_realize(DeviceState *dev, Error **errp)
> > +{
> > +    AcpiGedState *s = ACPI_GED(dev);
> > +
> > +    if (s->memhp_state.is_enabled) {
> > +        acpi_memory_hotplug_init(get_system_memory(), OBJECT(dev),
> > +                                 &s->memhp_state,
> > +                                 s->memhp_base);
> > +        acpi_ged_init(get_system_memory(), dev, &s->ged_state);
> > +    }
> > +}
> > +
> > +static Property acpi_ged_properties[] = {
> > +    /*
> > +     * Memory hotplug base address is a property of GED here,
> > +     * because GED handles memory hotplug event and
> MEMORY_HOTPLUG_DEVICE
> > +     * gets initialized when GED device is realized.
> > +     */
> > +    DEFINE_PROP_UINT64("memhp-base", AcpiGedState, memhp_base,
> 0),
> > +    DEFINE_PROP_BOOL("memory-hotplug-support", AcpiGedState,
> > +                     memhp_state.is_enabled, true),
> > +    DEFINE_PROP_PTR("gsi", AcpiGedState, gsi),
> > +    DEFINE_PROP_UINT64("ged-base", AcpiGedState, ged_base, 0),
> > +    DEFINE_PROP_UINT32("ged-irq", AcpiGedState, ged_irq, 0),
> > +    DEFINE_PROP_PTR("ged-events", AcpiGedState, ged_events),
> > +    DEFINE_PROP_UINT32("ged-events-size", AcpiGedState,
> ged_events_size, 0),
> > +    DEFINE_PROP_END_OF_LIST(),
> > +};
> > +
> > +static void acpi_ged_class_init(ObjectClass *class, void *data)
> > +{
> > +    DeviceClass *dc = DEVICE_CLASS(class);
> > +    HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class);
> > +    AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class);
> > +
> > +    dc->desc = "ACPI";
> > +    dc->props = acpi_ged_properties;
> > +    dc->realize = acpi_ged_device_realize;
> > +
> > +    /* Reason: pointer properties "gsi" and "ged_events" */
> > +    dc->user_creatable = false;
> > +
> > +    hc->plug = acpi_ged_device_plug_cb;
> > +
> > +    adevc->send_event = acpi_ged_send_event;
> > +}
> > +
> > +static const TypeInfo acpi_ged_info = {
> > +    .name          = TYPE_ACPI_GED,
> > +    .parent        = TYPE_DEVICE,
> > +    .instance_size = sizeof(AcpiGedState),
> > +    .class_init    = acpi_ged_class_init,
> > +    .interfaces = (InterfaceInfo[]) {
> > +        { TYPE_HOTPLUG_HANDLER },
> > +        { TYPE_ACPI_DEVICE_IF },
> > +        { }
> > +    }
> > +};
> > +
> > +static void acpi_ged_register_types(void)
> > +{
> > +    type_register_static(&acpi_ged_info);
> > +}
> > +
> > +type_init(acpi_ged_register_types)
> > diff --git a/include/hw/acpi/generic_event_device.h
> b/include/hw/acpi/generic_event_device.h
> > new file mode 100644
> > index 0000000..9c840d8
> > --- /dev/null
> > +++ b/include/hw/acpi/generic_event_device.h
> > @@ -0,0 +1,121 @@
> > +/*
> > + *
> > + * Copyright (c) 2018 Intel Corporation
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2 or later, as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope it will be useful, but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of
> MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
> License for
> > + * more details.
> > + *
> > + * You should have received a copy of the GNU General Public License along
> with
> > + * this program.  If not, see <http://www.gnu.org/licenses/>.
> > + *
> > + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
> > + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> > + * including the hotplug ones. Generic Event Device allows platforms
> > + * to handle interrupts in ACPI ASL statements. It follows a very
> > + * similar approach like the _EVT method from GPIO events. All
> > + * interrupts are listed in  _CRS and the handler is written in _EVT> + *
> method. Here, we use a single interrupt for the GED device, relying
> > + * on IO memory region to communicate the type of device affected by
> > + * the interrupt. This way, we can support up to 32 events with a
> > + * unique interrupt.
> > + *
> > + * Here is an example.
> > + *
> > + * Device (\_SB.GED)
> > + * {
> > + *     Name (_HID, "ACPI0013")
> > + *     Name (_UID, Zero)
> > + *     Name (_CRS, ResourceTemplate ()
> > + *     {
> > + *         Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, )
> > + *         {
> > + *              0x00000029,
> > + *         }
> > + *     })
> > + *     OperationRegion (IREG, SystemMemory, 0x09080000, 0x04)
> > + *     Field (IREG, DWordAcc, NoLock, WriteAsZeros)
> > + *     {
> > + *         ISEL,   32
> > + *     }
> > + *
> > + *     Method (_EVT, 1, Serialized)  // _EVT: Event
> > + *     {
> > + *         Local0 = ISEL // ISEL = IO memory region which specifies the
> > + *                       // device type.
> > + *         If (((Local0 & irq0) == irq0))
> > + *         {
> > + *             MethodEvent0()
> > + *         }
> > + *         ElseIf ((Local0 & irq1) == irq1)
> > + *         {
> > + *             MethodEvent1()
> > + *         }
> > + *         ...
> > + *     }
> > + * }
> > + *
> > + */
> > +
> > +#ifndef HW_ACPI_GED_H
> > +#define HW_ACPI_GED_H
> > +
> > +#include "hw/acpi/memory_hotplug.h"
> > +
> > +#define TYPE_ACPI_GED "acpi-ged"
> > +#define ACPI_GED(obj) \
> > +    OBJECT_CHECK(AcpiGedState, (obj), TYPE_ACPI_GED)
> > +
> > +#define ACPI_GED_IRQ_SEL_OFFSET 0x0
> > +#define ACPI_GED_IRQ_SEL_LEN    0x4
> > +#define ACPI_GED_IRQ_SEL_MEM    0x1
> > +#define ACPI_GED_REG_LEN        0x4
> > +
> > +#define GED_DEVICE      "GED"
> > +#define AML_GED_IRQ_REG "IREG"
> > +#define AML_GED_IRQ_SEL "ISEL"
> > +
> > +typedef enum {
> > +    GED_MEMORY_HOTPLUG = 1,
> > +} GedEventType;
> > +
> > +/*
> > + * Platforms need to specify their own GedEvent array
> > + * to describe what kind of events they want to support
> > + * through GED.
> > + */
> > +typedef struct GedEvent {
> > +    uint32_t     selector;
> > +    GedEventType event;
> > +} GedEvent;
> > +
> > +typedef struct GEDState {
> > +    MemoryRegion io;
> > +    uint32_t     sel;
> > +    uint32_t     irq;
> > +    qemu_irq     *gsi;
> so I am not sure why with need gsi + irq.
> > +    QemuMutex    lock;
> > +} GEDState;
> > +
> > +
> > +typedef struct AcpiGedState {
> > +    DeviceClass parent_obj;
> > +    MemHotplugState memhp_state;
> > +    hwaddr memhp_base;
> > +    void *gsi;
> > +    hwaddr ged_base;
> > +    GEDState ged_state;
> > +    uint32_t ged_irq;
> > +    void *ged_events;
> > +    uint32_t ged_events_size;
> > +} AcpiGedState;
> > +
> > +void build_ged_aml(Aml *table, const char* name, HotplugHandler
> *hotplug_dev,
> > +                   uint32_t ged_irq, AmlRegionSpace rs);
> > +
> > +#endif
> >
> Besides, this looks good to me.
> 

Thanks,
Shameer

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
@ 2019-05-01 10:40       ` Shameerali Kolothum Thodi
  0 siblings, 0 replies; 62+ messages in thread
From: Shameerali Kolothum Thodi @ 2019-05-01 10:40 UTC (permalink / raw)
  To: Auger Eric, qemu-devel, qemu-arm, imammedo
  Cc: peter.maydell, sameo, ard.biesheuvel, Linuxarm, shannon.zhaosl,
	sebastien.boeuf, xuwei (O),
	lersek

Hi Eric,

> -----Original Message-----
> From: Auger Eric [mailto:eric.auger@redhat.com]
> Sent: 30 April 2019 16:50
> To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>;
> qemu-devel@nongnu.org; qemu-arm@nongnu.org; imammedo@redhat.com
> Cc: peter.maydell@linaro.org; shannon.zhaosl@gmail.com;
> sameo@linux.intel.com; sebastien.boeuf@intel.com; xuwei (O)
> <xuwei5@huawei.com>; lersek@redhat.com; ard.biesheuvel@linaro.org;
> Linuxarm <linuxarm@huawei.com>
> Subject: Re: [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
> 
> Hi Shameer,
> 
> On 4/9/19 12:29 PM, Shameer Kolothum wrote:
> > From: Samuel Ortiz <sameo@linux.intel.com>
> >
> > The ACPI Generic Event Device (GED) is a hardware-reduced specific
> > device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> > including the hotplug ones.This patch generates the AML code that
> > defines GEDs.
> >
> > Platforms need to specify their own GedEvent array to describe what
> > kind of events they want to support through GED.  Also this uses a
> > a single interrupt for the  GED device, relying on IO memory region
> > to communicate the type of device affected by the interrupt. This
> > way, we can support up to 32 events with a unique interrupt.
> >
> > This supports only memory hotplug for now.
> >
> > Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
> > Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
> > Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> > ---
> >  hw/acpi/Kconfig                        |   4 +
> >  hw/acpi/Makefile.objs                  |   1 +
> >  hw/acpi/generic_event_device.c         | 311
> +++++++++++++++++++++++++++++++++
> >  include/hw/acpi/generic_event_device.h | 121 +++++++++++++
> >  4 files changed, 437 insertions(+)
> >  create mode 100644 hw/acpi/generic_event_device.c
> >  create mode 100644 include/hw/acpi/generic_event_device.h
> >
> > diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig
> > index eca3bee..01a8b41 100644
> > --- a/hw/acpi/Kconfig
> > +++ b/hw/acpi/Kconfig
> > @@ -27,3 +27,7 @@ config ACPI_VMGENID
> >      bool
> >      default y
> >      depends on PC
> > +
> > +config ACPI_HW_REDUCED
> > +    bool
> > +    depends on ACPI
> > diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
> > index 2d46e37..b753232 100644
> > --- a/hw/acpi/Makefile.objs
> > +++ b/hw/acpi/Makefile.objs
> > @@ -6,6 +6,7 @@ common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) +=
> memory_hotplug.o
> >  common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu.o
> >  common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
> >  common-obj-$(CONFIG_ACPI_VMGENID) += vmgenid.o
> > +common-obj-$(CONFIG_ACPI_HW_REDUCED) += generic_event_device.o
> >  common-obj-$(call lnot,$(CONFIG_ACPI_X86)) += acpi-stub.o
> >
> >  common-obj-y += acpi_interface.o
> > diff --git a/hw/acpi/generic_event_device.c
> b/hw/acpi/generic_event_device.c
> > new file mode 100644
> > index 0000000..856ca04
> > --- /dev/null
> > +++ b/hw/acpi/generic_event_device.c
> > @@ -0,0 +1,311 @@
> > +/*
> > + *
> > + * Copyright (c) 2018 Intel Corporation
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2 or later, as published by the Free Software Foundation.
> I am not sure we need below statements: see hw/misc/armsse-mhu.c for a
> recent added file.

Ok. I will get rid of this.
 
> > + *
> > + * This program is distributed in the hope it will be useful, but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of
> MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
> License for
> > + * more details.
> > + *
> > + * You should have received a copy of the GNU General Public License along
> with
> > + * this program.  If not, see <http://www.gnu.org/licenses/>.
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +#include "qapi/error.h"
> > +#include "exec/address-spaces.h"
> > +#include "hw/sysbus.h"
> > +#include "hw/acpi/acpi.h"
> > +#include "hw/acpi/generic_event_device.h"
> > +#include "hw/mem/pc-dimm.h"
> > +
> > +static Aml *ged_event_aml(const GedEvent *event)
> > +{
> > +
> > +    if (!event) {
> > +        return NULL;
> > +    }
> > +
> > +    switch (event->event) {
> > +    case GED_MEMORY_HOTPLUG:
> > +        /* We run a complete memory SCAN when getting a memory
> hotplug event */
> > +        return aml_call0(MEMORY_DEVICES_CONTAINER "."
> MEMORY_SLOT_SCAN_METHOD);
> > +    default:
> > +        break;
> > +    }
> > +
> > +    return NULL;
> > +}
> > +
> > +/*
> > + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
> > + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> > + * including the hotplug ones. Platforms need to specify their own
> > + * GedEvent array to describe what kind of events they want to support
> > + * through GED. This routine uses a single interrupt for the GED device,
> > + * relying on IO memory region to communicate the type of device
> > + * affected by the interrupt. This way, we can support up to 32 events
> > + * with a unique interrupt.
> > + */
> > +void build_ged_aml(Aml *table, const char *name, HotplugHandler
> *hotplug_dev,
> > +                   uint32_t ged_irq, AmlRegionSpace rs)
> > +{
> > +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> > +    GedEvent *ged_events = s->ged_events;
> > +    Aml *crs = aml_resource_template();
> > +    Aml *evt, *field;
> > +    Aml *dev = aml_device("%s", name);
> > +    Aml *irq_sel = aml_local(0);
> > +    Aml *isel = aml_name(AML_GED_IRQ_SEL);
> > +    uint32_t i;
> > +
> > +    if (!s->ged_base || !ged_events || !s->ged_events_size) {
> > +        return;
> > +    }
> > +
> > +    /* _CRS interrupt */
> > +    aml_append(crs, aml_interrupt(AML_CONSUMER, AML_EDGE,
> AML_ACTIVE_HIGH,
> > +                                  AML_EXCLUSIVE, &ged_irq, 1));
> > +    /*
> > +     * For each GED event we:
> > +     * - Add an interrupt to the CRS section.
> > +     * - Add a conditional block for each event, inside a while loop.
> > +     *   This is semantically equivalent to a switch/case implementation.
> > +     */
> > +    evt = aml_method("_EVT", 1, AML_SERIALIZED);
> > +    {
> > +        Aml *ged_aml;
> > +        Aml *if_ctx;
> > +
> > +        /* Local0 = ISEL */
> > +        aml_append(evt, aml_store(isel, irq_sel));
> > +
> > +        /*
> > +         * Here we want to call a method for each supported GED event
> type.
> > +         * The resulting ASL code looks like:
> > +         *
> > +         * Local0 = ISEL
> > +         * If ((Local0 & irq0) == irq0)
> > +         * {
> > +         *     MethodEvent0()
> > +         * }
> > +         *
> > +         * If ((Local0 & irq1) == irq1)
> > +         * {
> > +         *     MethodEvent1()
> > +         * }
> > +         * ...
> > +         */
> > +
> > +        for (i = 0; i < s->ged_events_size; i++) {
> > +            ged_aml = ged_event_aml(&ged_events[i]);
> > +            if (!ged_aml) {
> > +                continue;
> > +            }
> > +
> > +            /* If ((Local1 == irq))*/
> > +            if_ctx = aml_if(aml_equal(aml_and(irq_sel,
> > +
> aml_int(ged_events[i].selector), NULL),
> > +
> aml_int(ged_events[i].selector)));
> > +            {
> > +                /* AML for this specific type of event */
> > +                aml_append(if_ctx, ged_aml);
> > +            }
> > +
> > +            /*
> > +             * We append the first "if" to the "while" context.
> > +             * Other "if"s will be "elseif"s.
> Are we sure about that:
> in hw/acpi/nvdimm.c nvdimm_build_common_dsm() I can see an aml_else() is
> added inbetween aml_if's?

Right. I think the comment here is misleading. This will be as mentioned in the
comment block above,

 If ((Local0 & irq0) == irq0)
 {
     MethodEvent0()
 }

 If ((Local0 & irq1) == irq1)
 {
     MethodEvent1()
 }

 And this should do the job. Do we really need ElseIf block here?

> > +             */
> > +            aml_append(evt, if_ctx);
> > +        }
> > +    }
> > +
> > +    aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0013")));
> > +    aml_append(dev, aml_name_decl("_UID", aml_string(GED_DEVICE)));
> > +    aml_append(dev, aml_name_decl("_CRS", crs));
> > +
> > +    /* Append IO region */
> > +    aml_append(dev, aml_operation_region(AML_GED_IRQ_REG, rs,
> > +               aml_int(s->ged_base + ACPI_GED_IRQ_SEL_OFFSET),
> > +               ACPI_GED_IRQ_SEL_LEN));
> > +    field = aml_field(AML_GED_IRQ_REG, AML_DWORD_ACC,
> AML_NOLOCK,
> > +                      AML_WRITE_AS_ZEROS);
> > +    aml_append(field, aml_named_field(AML_GED_IRQ_SEL,
> > +                                      ACPI_GED_IRQ_SEL_LEN *
> 8));
> nit s/8/BITS_PER_BYTE as done in nvdimm.c.

Ok.

> > +    aml_append(dev, field);
> > +
> > +    /* Append _EVT method */
> > +    aml_append(dev, evt);
> > +
> > +    aml_append(table, dev);
> > +}
> > +
> > +/* Memory read by the GED _EVT AML dynamic method */
> > +static uint64_t ged_read(void *opaque, hwaddr addr, unsigned size)
> > +{
> > +    uint64_t val = 0;
> > +    GEDState *ged_st = opaque;
> > +
> > +    switch (addr) {
> > +    case ACPI_GED_IRQ_SEL_OFFSET:
> > +        /* Read the selector value and reset it */
> > +        qemu_mutex_lock(&ged_st->lock);
> > +        val = ged_st->sel;
> > +        ged_st->sel = 0;
> > +        qemu_mutex_unlock(&ged_st->lock);
> > +        break;
> > +    default:
> > +        break;
> > +    }
> > +
> > +    return val;
> > +}
> > +
> > +/* Nothing is expected to be written to the GED memory region */
> > +static void ged_write(void *opaque, hwaddr addr, uint64_t data,
> > +                      unsigned int size)
> > +{
> > +}
> > +
> > +static const MemoryRegionOps ged_ops = {
> > +    .read = ged_read,
> > +    .write = ged_write,
> > +    .endianness = DEVICE_LITTLE_ENDIAN,
> > +    .valid = {
> > +        .min_access_size = 4,
> > +        .max_access_size = 4,
> > +    },
> > +};
> > +
> > +static void acpi_ged_event(GEDState *ged_st, uint32_t ged_irq_sel)
> > +{
> > +    /*
> I would personally inline that code in void acpi_ged_send_event()

Ok.

> > +     * Set the GED IRQ selector to the expected device type value. This
> > +     * way, the ACPI method will be able to trigger the right code based
> > +     * on a unique IRQ.
> > +     */
> > +    qemu_mutex_lock(&ged_st->lock);
> > +    ged_st->sel = ged_irq_sel;
> > +    qemu_mutex_unlock(&ged_st->lock);
> > +
> > +    /* Trigger the event by sending an interrupt to the guest. */
> > +    qemu_irq_pulse(ged_st->gsi[ged_st->irq]);
> I don't really understand why we need this array. Doesn't The GED use a
> single IRQ. So why can't you store the precise qemu_irq pointer directly
> through the propery (DEFINE_PROP_PTR("gsi", AcpiGedState, gsi))?

Yes. You have raised this point previously as well. Sorry, I missed it when I
reworked the GED type from SYS_BUS_DEVICE to TYPE_DEVICE. I think
we can get rid of this array and the GED/gsi routing done in virt.c.


> > +}
> > +
> > +static void acpi_ged_init(MemoryRegion *as, DeviceState *dev, GEDState
> *ged_st)
> > +{
> > +    AcpiGedState *s = ACPI_GED(dev);
> > +
> > +    assert(s->ged_base);
> > +
> > +    ged_st->irq = s->ged_irq;
> > +    ged_st->gsi = s->gsi;
> > +    qemu_mutex_init(&ged_st->lock);
> > +    memory_region_init_io(&ged_st->io, OBJECT(dev), &ged_ops, ged_st,
> > +                          "acpi-ged-event", ACPI_GED_REG_LEN);
> > +    memory_region_add_subregion(as, s->ged_base, &ged_st->io);
> > +}
> > +
> > +static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
> > +                                    DeviceState *dev, Error **errp)
> > +{
> > +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> > +
> > +    if (s->memhp_state.is_enabled &&
> > +        object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> > +            acpi_memory_plug_cb(hotplug_dev, &s->memhp_state,
> > +                                dev, errp);
> > +    } else {
> > +        error_setg(errp, "virt: device plug request for unsupported
> device"
> > +                   " type: %s", object_get_typename(OBJECT(dev)));
> > +    }
> > +}
> > +
> > +static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits
> ev)
> > +{
> > +    AcpiGedState *s = ACPI_GED(adev);
> > +    uint32_t sel;
> > +
> > +    if (ev & ACPI_MEMORY_HOTPLUG_STATUS) {
> > +        sel = ACPI_GED_IRQ_SEL_MEM;
> > +    } else {
> > +        /* Unknown event. Return without generating interrupt. */
> > +        return;
> > +    }
> > +
> > +    /*
> > +     * We inject the hotplug interrupt. The IRQ selector will make
> > +     * the difference from the ACPI table.
> > +     */
> > +    acpi_ged_event(&s->ged_state, sel);
> > +}
> > +
> > +static void acpi_ged_device_realize(DeviceState *dev, Error **errp)
> > +{
> > +    AcpiGedState *s = ACPI_GED(dev);
> > +
> > +    if (s->memhp_state.is_enabled) {
> > +        acpi_memory_hotplug_init(get_system_memory(), OBJECT(dev),
> > +                                 &s->memhp_state,
> > +                                 s->memhp_base);
> > +        acpi_ged_init(get_system_memory(), dev, &s->ged_state);
> > +    }
> > +}
> > +
> > +static Property acpi_ged_properties[] = {
> > +    /*
> > +     * Memory hotplug base address is a property of GED here,
> > +     * because GED handles memory hotplug event and
> MEMORY_HOTPLUG_DEVICE
> > +     * gets initialized when GED device is realized.
> > +     */
> > +    DEFINE_PROP_UINT64("memhp-base", AcpiGedState, memhp_base,
> 0),
> > +    DEFINE_PROP_BOOL("memory-hotplug-support", AcpiGedState,
> > +                     memhp_state.is_enabled, true),
> > +    DEFINE_PROP_PTR("gsi", AcpiGedState, gsi),
> > +    DEFINE_PROP_UINT64("ged-base", AcpiGedState, ged_base, 0),
> > +    DEFINE_PROP_UINT32("ged-irq", AcpiGedState, ged_irq, 0),
> > +    DEFINE_PROP_PTR("ged-events", AcpiGedState, ged_events),
> > +    DEFINE_PROP_UINT32("ged-events-size", AcpiGedState,
> ged_events_size, 0),
> > +    DEFINE_PROP_END_OF_LIST(),
> > +};
> > +
> > +static void acpi_ged_class_init(ObjectClass *class, void *data)
> > +{
> > +    DeviceClass *dc = DEVICE_CLASS(class);
> > +    HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class);
> > +    AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class);
> > +
> > +    dc->desc = "ACPI";
> > +    dc->props = acpi_ged_properties;
> > +    dc->realize = acpi_ged_device_realize;
> > +
> > +    /* Reason: pointer properties "gsi" and "ged_events" */
> > +    dc->user_creatable = false;
> > +
> > +    hc->plug = acpi_ged_device_plug_cb;
> > +
> > +    adevc->send_event = acpi_ged_send_event;
> > +}
> > +
> > +static const TypeInfo acpi_ged_info = {
> > +    .name          = TYPE_ACPI_GED,
> > +    .parent        = TYPE_DEVICE,
> > +    .instance_size = sizeof(AcpiGedState),
> > +    .class_init    = acpi_ged_class_init,
> > +    .interfaces = (InterfaceInfo[]) {
> > +        { TYPE_HOTPLUG_HANDLER },
> > +        { TYPE_ACPI_DEVICE_IF },
> > +        { }
> > +    }
> > +};
> > +
> > +static void acpi_ged_register_types(void)
> > +{
> > +    type_register_static(&acpi_ged_info);
> > +}
> > +
> > +type_init(acpi_ged_register_types)
> > diff --git a/include/hw/acpi/generic_event_device.h
> b/include/hw/acpi/generic_event_device.h
> > new file mode 100644
> > index 0000000..9c840d8
> > --- /dev/null
> > +++ b/include/hw/acpi/generic_event_device.h
> > @@ -0,0 +1,121 @@
> > +/*
> > + *
> > + * Copyright (c) 2018 Intel Corporation
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2 or later, as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope it will be useful, but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of
> MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
> License for
> > + * more details.
> > + *
> > + * You should have received a copy of the GNU General Public License along
> with
> > + * this program.  If not, see <http://www.gnu.org/licenses/>.
> > + *
> > + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
> > + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> > + * including the hotplug ones. Generic Event Device allows platforms
> > + * to handle interrupts in ACPI ASL statements. It follows a very
> > + * similar approach like the _EVT method from GPIO events. All
> > + * interrupts are listed in  _CRS and the handler is written in _EVT> + *
> method. Here, we use a single interrupt for the GED device, relying
> > + * on IO memory region to communicate the type of device affected by
> > + * the interrupt. This way, we can support up to 32 events with a
> > + * unique interrupt.
> > + *
> > + * Here is an example.
> > + *
> > + * Device (\_SB.GED)
> > + * {
> > + *     Name (_HID, "ACPI0013")
> > + *     Name (_UID, Zero)
> > + *     Name (_CRS, ResourceTemplate ()
> > + *     {
> > + *         Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, )
> > + *         {
> > + *              0x00000029,
> > + *         }
> > + *     })
> > + *     OperationRegion (IREG, SystemMemory, 0x09080000, 0x04)
> > + *     Field (IREG, DWordAcc, NoLock, WriteAsZeros)
> > + *     {
> > + *         ISEL,   32
> > + *     }
> > + *
> > + *     Method (_EVT, 1, Serialized)  // _EVT: Event
> > + *     {
> > + *         Local0 = ISEL // ISEL = IO memory region which specifies the
> > + *                       // device type.
> > + *         If (((Local0 & irq0) == irq0))
> > + *         {
> > + *             MethodEvent0()
> > + *         }
> > + *         ElseIf ((Local0 & irq1) == irq1)
> > + *         {
> > + *             MethodEvent1()
> > + *         }
> > + *         ...
> > + *     }
> > + * }
> > + *
> > + */
> > +
> > +#ifndef HW_ACPI_GED_H
> > +#define HW_ACPI_GED_H
> > +
> > +#include "hw/acpi/memory_hotplug.h"
> > +
> > +#define TYPE_ACPI_GED "acpi-ged"
> > +#define ACPI_GED(obj) \
> > +    OBJECT_CHECK(AcpiGedState, (obj), TYPE_ACPI_GED)
> > +
> > +#define ACPI_GED_IRQ_SEL_OFFSET 0x0
> > +#define ACPI_GED_IRQ_SEL_LEN    0x4
> > +#define ACPI_GED_IRQ_SEL_MEM    0x1
> > +#define ACPI_GED_REG_LEN        0x4
> > +
> > +#define GED_DEVICE      "GED"
> > +#define AML_GED_IRQ_REG "IREG"
> > +#define AML_GED_IRQ_SEL "ISEL"
> > +
> > +typedef enum {
> > +    GED_MEMORY_HOTPLUG = 1,
> > +} GedEventType;
> > +
> > +/*
> > + * Platforms need to specify their own GedEvent array
> > + * to describe what kind of events they want to support
> > + * through GED.
> > + */
> > +typedef struct GedEvent {
> > +    uint32_t     selector;
> > +    GedEventType event;
> > +} GedEvent;
> > +
> > +typedef struct GEDState {
> > +    MemoryRegion io;
> > +    uint32_t     sel;
> > +    uint32_t     irq;
> > +    qemu_irq     *gsi;
> so I am not sure why with need gsi + irq.
> > +    QemuMutex    lock;
> > +} GEDState;
> > +
> > +
> > +typedef struct AcpiGedState {
> > +    DeviceClass parent_obj;
> > +    MemHotplugState memhp_state;
> > +    hwaddr memhp_base;
> > +    void *gsi;
> > +    hwaddr ged_base;
> > +    GEDState ged_state;
> > +    uint32_t ged_irq;
> > +    void *ged_events;
> > +    uint32_t ged_events_size;
> > +} AcpiGedState;
> > +
> > +void build_ged_aml(Aml *table, const char* name, HotplugHandler
> *hotplug_dev,
> > +                   uint32_t ged_irq, AmlRegionSpace rs);
> > +
> > +#endif
> >
> Besides, this looks good to me.
> 

Thanks,
Shameer

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 5/8] hw/arm/virt: Enable device memory cold/hot plug with ACPI boot
@ 2019-05-01 10:49       ` Shameerali Kolothum Thodi
  0 siblings, 0 replies; 62+ messages in thread
From: Shameerali Kolothum Thodi @ 2019-05-01 10:49 UTC (permalink / raw)
  To: Auger Eric, qemu-devel, qemu-arm, imammedo
  Cc: peter.maydell, shannon.zhaosl, sameo, sebastien.boeuf, xuwei (O),
	lersek, ard.biesheuvel, Linuxarm

Hi Eric,

> -----Original Message-----
> From: Auger Eric [mailto:eric.auger@redhat.com]
> Sent: 30 April 2019 17:34
> To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>;
> qemu-devel@nongnu.org; qemu-arm@nongnu.org; imammedo@redhat.com
> Cc: peter.maydell@linaro.org; shannon.zhaosl@gmail.com;
> sameo@linux.intel.com; sebastien.boeuf@intel.com; xuwei (O)
> <xuwei5@huawei.com>; lersek@redhat.com; ard.biesheuvel@linaro.org;
> Linuxarm <linuxarm@huawei.com>
> Subject: Re: [PATCH v4 5/8] hw/arm/virt: Enable device memory cold/hot plug
> with ACPI boot
> 
> Hi Shameer,
> 
> On 4/9/19 12:29 PM, Shameer Kolothum wrote:
> > This initializes the GED device with base memory and irq, configures
> > ged memory hotplug event and builds the corresponding aml code. GED
> > irq routing to Guest is also enabled. With this, both hot and cold
> > plug of device memory is enabled now for Guest with ACPI boot.
> >
> > Memory cold plug support with Guest DT boot is not yet supported.
> >
> > Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> Individual history logs may be helpful to follow the changes (change in
> MMIO reggion size, ...)

Ok. Noted.

> > ---
> >  default-configs/arm-softmmu.mak |  2 ++
> >  hw/arm/virt-acpi-build.c        |  9 ++++++
> >  hw/arm/virt.c                   | 61
> +++++++++++++++++++++++++++++++++++++++--
> >  include/hw/arm/virt.h           |  4 +++
> >  4 files changed, 73 insertions(+), 3 deletions(-)
> >
> > diff --git a/default-configs/arm-softmmu.mak
> b/default-configs/arm-softmmu.mak
> > index 9f4b803..c9a9b34 100644
> > --- a/default-configs/arm-softmmu.mak
> > +++ b/default-configs/arm-softmmu.mak
> > @@ -163,3 +163,5 @@ CONFIG_LSI_SCSI_PCI=y
> >
> >  CONFIG_MEM_DEVICE=y
> >  CONFIG_DIMM=y
> > +CONFIG_ACPI_MEMORY_HOTPLUG=y
> > +CONFIG_ACPI_HW_REDUCED=y
> > diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
> > index bf9c0bc..1ad394b 100644
> > --- a/hw/arm/virt-acpi-build.c
> > +++ b/hw/arm/virt-acpi-build.c
> > @@ -40,6 +40,8 @@
> >  #include "hw/loader.h"
> >  #include "hw/hw.h"
> >  #include "hw/acpi/aml-build.h"
> > +#include "hw/acpi/memory_hotplug.h"
> > +#include "hw/acpi/generic_event_device.h"
> >  #include "hw/pci/pcie_host.h"
> >  #include "hw/pci/pci.h"
> >  #include "hw/arm/virt.h"
> > @@ -727,6 +729,7 @@ static void
> >  build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
> >  {
> >      Aml *scope, *dsdt;
> > +    MachineState *ms = MACHINE(vms);
> >      const MemMapEntry *memmap = vms->memmap;
> >      const int *irqmap = vms->irqmap;
> >
> > @@ -753,6 +756,12 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
> VirtMachineState *vms)
> >                         (irqmap[VIRT_GPIO] + ARM_SPI_BASE));
> >      acpi_dsdt_add_power_button(scope);
> >
> > +    build_ged_aml(scope, "\\_SB."GED_DEVICE,
> HOTPLUG_HANDLER(vms->acpi_dev),
> > +                  irqmap[VIRT_ACPI_GED] + ARM_SPI_BASE,
> AML_SYSTEM_MEMORY);
> > +
> > +    build_memory_hotplug_aml(scope, ms->ram_slots, "\\_SB", NULL,
> > +                             AML_SYSTEM_MEMORY);
> > +
> >      aml_append(dsdt, scope);
> >
> >      /* copy AML table into ACPI tables blob and patch header there */
> > diff --git a/hw/arm/virt.c b/hw/arm/virt.c
> > index da516b3..8179b3e 100644
> > --- a/hw/arm/virt.c
> > +++ b/hw/arm/virt.c
> > @@ -63,6 +63,7 @@
> >  #include "target/arm/internals.h"
> >  #include "hw/mem/pc-dimm.h"
> >  #include "hw/mem/nvdimm.h"
> > +#include "hw/acpi/generic_event_device.h"
> >
> >  #define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \
> >      static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
> > @@ -133,6 +134,8 @@ static const MemMapEntry base_memmap[] = {
> >      [VIRT_GPIO] =               { 0x09030000, 0x00001000 },
> >      [VIRT_SECURE_UART] =        { 0x09040000, 0x00001000 },
> >      [VIRT_SMMU] =               { 0x09050000, 0x00020000 },
> > +    [VIRT_PCDIMM_ACPI] =        { 0x09070000,
> MEMORY_HOTPLUG_IO_LEN },
> > +    [VIRT_ACPI_GED] =           { 0x09080000, ACPI_GED_REG_LEN },>
> [VIRT_MMIO] =               { 0x0a000000, 0x00000200 },
> >      /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that
> size */
> >      [VIRT_PLATFORM_BUS] =       { 0x0c000000, 0x02000000 },
> > @@ -168,6 +171,7 @@ static const int a15irqmap[] = {
> >      [VIRT_PCIE] = 3, /* ... to 6 */
> >      [VIRT_GPIO] = 7,
> >      [VIRT_SECURE_UART] = 8,
> > +    [VIRT_ACPI_GED] = 9,
> >      [VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */
> >      [VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */
> >      [VIRT_SMMU] = 74,    /* ...to 74 + NUM_SMMU_IRQS - 1 */
> > @@ -183,6 +187,13 @@ static const char *valid_cpus[] = {
> >      ARM_CPU_TYPE_NAME("max"),
> >  };
> >
> > +static GedEvent ged_events[] = {
> > +    {
> > +        .selector = ACPI_GED_IRQ_SEL_MEM,
> > +        .event    = GED_MEMORY_HOTPLUG,
> > +    },
> > +};
> > +
> >  static bool cpu_type_valid(const char *cpu)
> >  {
> >      int i;
> > @@ -516,6 +527,26 @@ static void fdt_add_pmu_nodes(const
> VirtMachineState *vms)
> >      }
> >  }
> >
> > +static inline DeviceState *create_acpi_ged(VirtMachineState *vms)
> > +{
> > +    DeviceState *dev;
> > +
> > +    dev = DEVICE(object_new(TYPE_ACPI_GED));
> > +    qdev_prop_set_uint64(dev, "memhp-base",
> > +                         vms->memmap[VIRT_PCDIMM_ACPI].base);
> > +    qdev_prop_set_ptr(dev, "gsi", vms->gsi);
> > +    qdev_prop_set_uint64(dev, "ged-base",
> vms->memmap[VIRT_ACPI_GED].base);
> > +    qdev_prop_set_uint32(dev, "ged-irq", vms->irqmap[VIRT_ACPI_GED]);
> so as commented earlier I wonder why we can directly pass the right gsi.

Ok.

> > +    qdev_prop_set_ptr(dev, "ged-events", ged_events);
> > +    qdev_prop_set_uint32(dev, "ged-events-size",
> ARRAY_SIZE(ged_events));
> > +
> > +    object_property_add_child(qdev_get_machine(), "acpi-ged",
> > +                              OBJECT(dev), NULL);
> > +    qdev_init_nofail(dev);
> > +
> > +    return dev;
> > +}
> > +
> >  static void create_its(VirtMachineState *vms, DeviceState *gicdev)
> >  {
> >      const char *itsclass = its_class_name();
> > @@ -555,6 +586,12 @@ static void create_v2m(VirtMachineState *vms,
> qemu_irq *pic)
> >      fdt_add_v2m_gic_node(vms);
> >  }
> >
> > +static void virt_gsi_handler(void *opaque, int n, int level)
> > +{
> > +    qemu_irq *gic_irq = opaque;
> > +    qemu_set_irq(gic_irq[n], level);
> In https://patchwork.kernel.org/patch/10863285/, I asked why this gsi
> routing was necessary. Please forgive me if I missed your answer but I
> still don't get why it is necessary.

Yes, you did. As I commented in 3/8, I missed this for v4. I don’t think we need this
routing If we pass the right qemu_irq directly. I will change this in next revision.

Thanks,
Shameer

> > +}
> > +
> >  static void create_gic(VirtMachineState *vms, qemu_irq *pic)
> >  {
> >      /* We create a standalone GIC */
> > @@ -670,6 +707,8 @@ static void create_gic(VirtMachineState *vms,
> qemu_irq *pic)
> >          pic[i] = qdev_get_gpio_in(gicdev, i);
> >      }
> >
> > +    vms->gsi = qemu_allocate_irqs(virt_gsi_handler, pic, NUM_IRQS);
> > +
> >      fdt_add_gic_node(vms);
> >
> >      if (type == 3 && vms->its) {
> > @@ -1418,7 +1457,7 @@ static void machvirt_init(MachineState *machine)
> >      VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(machine);
> >      MachineClass *mc = MACHINE_GET_CLASS(machine);
> >      const CPUArchIdList *possible_cpus;
> > -    qemu_irq pic[NUM_IRQS];
> > +    qemu_irq *pic;
> >      MemoryRegion *sysmem = get_system_memory();
> >      MemoryRegion *secure_sysmem = NULL;
> >      int n, virt_max_cpus;
> > @@ -1614,6 +1653,7 @@ static void machvirt_init(MachineState *machine)
> >
> >      create_flash(vms, sysmem, secure_sysmem ? secure_sysmem :
> sysmem);
> >
> > +    pic = g_new0(qemu_irq, NUM_IRQS);
> >      create_gic(vms, pic);
> >
> >      fdt_add_pmu_nodes(vms);
> > @@ -1644,6 +1684,8 @@ static void machvirt_init(MachineState *machine)
> >
> >      create_platform_bus(vms, pic);
> >
> > +    vms->acpi_dev = create_acpi_ged(vms);
> > +
> >      vms->bootinfo.ram_size = machine->ram_size;
> >      vms->bootinfo.kernel_filename = machine->kernel_filename;
> >      vms->bootinfo.kernel_cmdline = machine->kernel_cmdline;
> > @@ -1811,21 +1853,34 @@ static const CPUArchIdList
> *virt_possible_cpu_arch_ids(MachineState *ms)
> >  static void virt_memory_pre_plug(HotplugHandler *hotplug_dev,
> DeviceState *dev,
> >                                   Error **errp)
> >  {
> > -    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> > -        error_setg(errp, "memory cold/hot plug is not yet supported");
> > +    VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
> > +    const bool is_nvdimm = object_dynamic_cast(OBJECT(dev),
> TYPE_NVDIMM);
> > +
> > +    if (is_nvdimm) {
> > +        error_setg(errp, "nvdimm is not yet supported");
> >          return;
> >      }
> >
> > +    if (!vms->acpi_dev) {
> > +        error_setg(errp, "memory hotplug is not enabled: missing acpi
> device");
> > +        return;
> > +    }
> > +
> > +    hotplug_handler_pre_plug(HOTPLUG_HANDLER(vms->acpi_dev), dev,
> errp);
> > +
> >      pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), NULL,
> errp);
> >  }
> >
> >  static void virt_memory_plug(HotplugHandler *hotplug_dev,
> >                               DeviceState *dev, Error **errp)
> >  {
> > +    HotplugHandlerClass *hhc;
> >      VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
> >
> >      pc_dimm_plug(PC_DIMM(dev), MACHINE(vms), NULL);
> >
> > +    hhc = HOTPLUG_HANDLER_GET_CLASS(vms->acpi_dev);
> > +    hhc->plug(HOTPLUG_HANDLER(vms->acpi_dev), dev, NULL);
> >  }
> >
> >  static void virt_machine_device_pre_plug_cb(HotplugHandler
> *hotplug_dev,
> > diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
> > index 507517c..9c6b0b1 100644
> > --- a/include/hw/arm/virt.h
> > +++ b/include/hw/arm/virt.h
> > @@ -77,6 +77,8 @@ enum {
> >      VIRT_GPIO,
> >      VIRT_SECURE_UART,
> >      VIRT_SECURE_MEM,
> > +    VIRT_PCDIMM_ACPI,
> > +    VIRT_ACPI_GED,
> >      VIRT_LOWMEMMAP_LAST,
> >  };
> >
> > @@ -132,6 +134,8 @@ typedef struct {
> >      uint32_t iommu_phandle;
> >      int psci_conduit;
> >      hwaddr highest_gpa;
> > +    DeviceState *acpi_dev;
> > +    qemu_irq *gsi;
> >  } VirtMachineState;
> >
> >  #define VIRT_ECAM_ID(high) (high ? VIRT_HIGH_PCIE_ECAM :
> VIRT_PCIE_ECAM)
> Thanks
> 
> Eric
> >

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 5/8] hw/arm/virt: Enable device memory cold/hot plug with ACPI boot
@ 2019-05-01 10:49       ` Shameerali Kolothum Thodi
  0 siblings, 0 replies; 62+ messages in thread
From: Shameerali Kolothum Thodi @ 2019-05-01 10:49 UTC (permalink / raw)
  To: Auger Eric, qemu-devel, qemu-arm, imammedo
  Cc: peter.maydell, sameo, ard.biesheuvel, Linuxarm, shannon.zhaosl,
	sebastien.boeuf, xuwei (O),
	lersek

Hi Eric,

> -----Original Message-----
> From: Auger Eric [mailto:eric.auger@redhat.com]
> Sent: 30 April 2019 17:34
> To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>;
> qemu-devel@nongnu.org; qemu-arm@nongnu.org; imammedo@redhat.com
> Cc: peter.maydell@linaro.org; shannon.zhaosl@gmail.com;
> sameo@linux.intel.com; sebastien.boeuf@intel.com; xuwei (O)
> <xuwei5@huawei.com>; lersek@redhat.com; ard.biesheuvel@linaro.org;
> Linuxarm <linuxarm@huawei.com>
> Subject: Re: [PATCH v4 5/8] hw/arm/virt: Enable device memory cold/hot plug
> with ACPI boot
> 
> Hi Shameer,
> 
> On 4/9/19 12:29 PM, Shameer Kolothum wrote:
> > This initializes the GED device with base memory and irq, configures
> > ged memory hotplug event and builds the corresponding aml code. GED
> > irq routing to Guest is also enabled. With this, both hot and cold
> > plug of device memory is enabled now for Guest with ACPI boot.
> >
> > Memory cold plug support with Guest DT boot is not yet supported.
> >
> > Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> Individual history logs may be helpful to follow the changes (change in
> MMIO reggion size, ...)

Ok. Noted.

> > ---
> >  default-configs/arm-softmmu.mak |  2 ++
> >  hw/arm/virt-acpi-build.c        |  9 ++++++
> >  hw/arm/virt.c                   | 61
> +++++++++++++++++++++++++++++++++++++++--
> >  include/hw/arm/virt.h           |  4 +++
> >  4 files changed, 73 insertions(+), 3 deletions(-)
> >
> > diff --git a/default-configs/arm-softmmu.mak
> b/default-configs/arm-softmmu.mak
> > index 9f4b803..c9a9b34 100644
> > --- a/default-configs/arm-softmmu.mak
> > +++ b/default-configs/arm-softmmu.mak
> > @@ -163,3 +163,5 @@ CONFIG_LSI_SCSI_PCI=y
> >
> >  CONFIG_MEM_DEVICE=y
> >  CONFIG_DIMM=y
> > +CONFIG_ACPI_MEMORY_HOTPLUG=y
> > +CONFIG_ACPI_HW_REDUCED=y
> > diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
> > index bf9c0bc..1ad394b 100644
> > --- a/hw/arm/virt-acpi-build.c
> > +++ b/hw/arm/virt-acpi-build.c
> > @@ -40,6 +40,8 @@
> >  #include "hw/loader.h"
> >  #include "hw/hw.h"
> >  #include "hw/acpi/aml-build.h"
> > +#include "hw/acpi/memory_hotplug.h"
> > +#include "hw/acpi/generic_event_device.h"
> >  #include "hw/pci/pcie_host.h"
> >  #include "hw/pci/pci.h"
> >  #include "hw/arm/virt.h"
> > @@ -727,6 +729,7 @@ static void
> >  build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
> >  {
> >      Aml *scope, *dsdt;
> > +    MachineState *ms = MACHINE(vms);
> >      const MemMapEntry *memmap = vms->memmap;
> >      const int *irqmap = vms->irqmap;
> >
> > @@ -753,6 +756,12 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
> VirtMachineState *vms)
> >                         (irqmap[VIRT_GPIO] + ARM_SPI_BASE));
> >      acpi_dsdt_add_power_button(scope);
> >
> > +    build_ged_aml(scope, "\\_SB."GED_DEVICE,
> HOTPLUG_HANDLER(vms->acpi_dev),
> > +                  irqmap[VIRT_ACPI_GED] + ARM_SPI_BASE,
> AML_SYSTEM_MEMORY);
> > +
> > +    build_memory_hotplug_aml(scope, ms->ram_slots, "\\_SB", NULL,
> > +                             AML_SYSTEM_MEMORY);
> > +
> >      aml_append(dsdt, scope);
> >
> >      /* copy AML table into ACPI tables blob and patch header there */
> > diff --git a/hw/arm/virt.c b/hw/arm/virt.c
> > index da516b3..8179b3e 100644
> > --- a/hw/arm/virt.c
> > +++ b/hw/arm/virt.c
> > @@ -63,6 +63,7 @@
> >  #include "target/arm/internals.h"
> >  #include "hw/mem/pc-dimm.h"
> >  #include "hw/mem/nvdimm.h"
> > +#include "hw/acpi/generic_event_device.h"
> >
> >  #define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \
> >      static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
> > @@ -133,6 +134,8 @@ static const MemMapEntry base_memmap[] = {
> >      [VIRT_GPIO] =               { 0x09030000, 0x00001000 },
> >      [VIRT_SECURE_UART] =        { 0x09040000, 0x00001000 },
> >      [VIRT_SMMU] =               { 0x09050000, 0x00020000 },
> > +    [VIRT_PCDIMM_ACPI] =        { 0x09070000,
> MEMORY_HOTPLUG_IO_LEN },
> > +    [VIRT_ACPI_GED] =           { 0x09080000, ACPI_GED_REG_LEN },>
> [VIRT_MMIO] =               { 0x0a000000, 0x00000200 },
> >      /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that
> size */
> >      [VIRT_PLATFORM_BUS] =       { 0x0c000000, 0x02000000 },
> > @@ -168,6 +171,7 @@ static const int a15irqmap[] = {
> >      [VIRT_PCIE] = 3, /* ... to 6 */
> >      [VIRT_GPIO] = 7,
> >      [VIRT_SECURE_UART] = 8,
> > +    [VIRT_ACPI_GED] = 9,
> >      [VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */
> >      [VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */
> >      [VIRT_SMMU] = 74,    /* ...to 74 + NUM_SMMU_IRQS - 1 */
> > @@ -183,6 +187,13 @@ static const char *valid_cpus[] = {
> >      ARM_CPU_TYPE_NAME("max"),
> >  };
> >
> > +static GedEvent ged_events[] = {
> > +    {
> > +        .selector = ACPI_GED_IRQ_SEL_MEM,
> > +        .event    = GED_MEMORY_HOTPLUG,
> > +    },
> > +};
> > +
> >  static bool cpu_type_valid(const char *cpu)
> >  {
> >      int i;
> > @@ -516,6 +527,26 @@ static void fdt_add_pmu_nodes(const
> VirtMachineState *vms)
> >      }
> >  }
> >
> > +static inline DeviceState *create_acpi_ged(VirtMachineState *vms)
> > +{
> > +    DeviceState *dev;
> > +
> > +    dev = DEVICE(object_new(TYPE_ACPI_GED));
> > +    qdev_prop_set_uint64(dev, "memhp-base",
> > +                         vms->memmap[VIRT_PCDIMM_ACPI].base);
> > +    qdev_prop_set_ptr(dev, "gsi", vms->gsi);
> > +    qdev_prop_set_uint64(dev, "ged-base",
> vms->memmap[VIRT_ACPI_GED].base);
> > +    qdev_prop_set_uint32(dev, "ged-irq", vms->irqmap[VIRT_ACPI_GED]);
> so as commented earlier I wonder why we can directly pass the right gsi.

Ok.

> > +    qdev_prop_set_ptr(dev, "ged-events", ged_events);
> > +    qdev_prop_set_uint32(dev, "ged-events-size",
> ARRAY_SIZE(ged_events));
> > +
> > +    object_property_add_child(qdev_get_machine(), "acpi-ged",
> > +                              OBJECT(dev), NULL);
> > +    qdev_init_nofail(dev);
> > +
> > +    return dev;
> > +}
> > +
> >  static void create_its(VirtMachineState *vms, DeviceState *gicdev)
> >  {
> >      const char *itsclass = its_class_name();
> > @@ -555,6 +586,12 @@ static void create_v2m(VirtMachineState *vms,
> qemu_irq *pic)
> >      fdt_add_v2m_gic_node(vms);
> >  }
> >
> > +static void virt_gsi_handler(void *opaque, int n, int level)
> > +{
> > +    qemu_irq *gic_irq = opaque;
> > +    qemu_set_irq(gic_irq[n], level);
> In https://patchwork.kernel.org/patch/10863285/, I asked why this gsi
> routing was necessary. Please forgive me if I missed your answer but I
> still don't get why it is necessary.

Yes, you did. As I commented in 3/8, I missed this for v4. I don’t think we need this
routing If we pass the right qemu_irq directly. I will change this in next revision.

Thanks,
Shameer

> > +}
> > +
> >  static void create_gic(VirtMachineState *vms, qemu_irq *pic)
> >  {
> >      /* We create a standalone GIC */
> > @@ -670,6 +707,8 @@ static void create_gic(VirtMachineState *vms,
> qemu_irq *pic)
> >          pic[i] = qdev_get_gpio_in(gicdev, i);
> >      }
> >
> > +    vms->gsi = qemu_allocate_irqs(virt_gsi_handler, pic, NUM_IRQS);
> > +
> >      fdt_add_gic_node(vms);
> >
> >      if (type == 3 && vms->its) {
> > @@ -1418,7 +1457,7 @@ static void machvirt_init(MachineState *machine)
> >      VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(machine);
> >      MachineClass *mc = MACHINE_GET_CLASS(machine);
> >      const CPUArchIdList *possible_cpus;
> > -    qemu_irq pic[NUM_IRQS];
> > +    qemu_irq *pic;
> >      MemoryRegion *sysmem = get_system_memory();
> >      MemoryRegion *secure_sysmem = NULL;
> >      int n, virt_max_cpus;
> > @@ -1614,6 +1653,7 @@ static void machvirt_init(MachineState *machine)
> >
> >      create_flash(vms, sysmem, secure_sysmem ? secure_sysmem :
> sysmem);
> >
> > +    pic = g_new0(qemu_irq, NUM_IRQS);
> >      create_gic(vms, pic);
> >
> >      fdt_add_pmu_nodes(vms);
> > @@ -1644,6 +1684,8 @@ static void machvirt_init(MachineState *machine)
> >
> >      create_platform_bus(vms, pic);
> >
> > +    vms->acpi_dev = create_acpi_ged(vms);
> > +
> >      vms->bootinfo.ram_size = machine->ram_size;
> >      vms->bootinfo.kernel_filename = machine->kernel_filename;
> >      vms->bootinfo.kernel_cmdline = machine->kernel_cmdline;
> > @@ -1811,21 +1853,34 @@ static const CPUArchIdList
> *virt_possible_cpu_arch_ids(MachineState *ms)
> >  static void virt_memory_pre_plug(HotplugHandler *hotplug_dev,
> DeviceState *dev,
> >                                   Error **errp)
> >  {
> > -    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> > -        error_setg(errp, "memory cold/hot plug is not yet supported");
> > +    VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
> > +    const bool is_nvdimm = object_dynamic_cast(OBJECT(dev),
> TYPE_NVDIMM);
> > +
> > +    if (is_nvdimm) {
> > +        error_setg(errp, "nvdimm is not yet supported");
> >          return;
> >      }
> >
> > +    if (!vms->acpi_dev) {
> > +        error_setg(errp, "memory hotplug is not enabled: missing acpi
> device");
> > +        return;
> > +    }
> > +
> > +    hotplug_handler_pre_plug(HOTPLUG_HANDLER(vms->acpi_dev), dev,
> errp);
> > +
> >      pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), NULL,
> errp);
> >  }
> >
> >  static void virt_memory_plug(HotplugHandler *hotplug_dev,
> >                               DeviceState *dev, Error **errp)
> >  {
> > +    HotplugHandlerClass *hhc;
> >      VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
> >
> >      pc_dimm_plug(PC_DIMM(dev), MACHINE(vms), NULL);
> >
> > +    hhc = HOTPLUG_HANDLER_GET_CLASS(vms->acpi_dev);
> > +    hhc->plug(HOTPLUG_HANDLER(vms->acpi_dev), dev, NULL);
> >  }
> >
> >  static void virt_machine_device_pre_plug_cb(HotplugHandler
> *hotplug_dev,
> > diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
> > index 507517c..9c6b0b1 100644
> > --- a/include/hw/arm/virt.h
> > +++ b/include/hw/arm/virt.h
> > @@ -77,6 +77,8 @@ enum {
> >      VIRT_GPIO,
> >      VIRT_SECURE_UART,
> >      VIRT_SECURE_MEM,
> > +    VIRT_PCDIMM_ACPI,
> > +    VIRT_ACPI_GED,
> >      VIRT_LOWMEMMAP_LAST,
> >  };
> >
> > @@ -132,6 +134,8 @@ typedef struct {
> >      uint32_t iommu_phandle;
> >      int psci_conduit;
> >      hwaddr highest_gpa;
> > +    DeviceState *acpi_dev;
> > +    qemu_irq *gsi;
> >  } VirtMachineState;
> >
> >  #define VIRT_ECAM_ID(high) (high ? VIRT_HIGH_PCIE_ECAM :
> VIRT_PCIE_ECAM)
> Thanks
> 
> Eric
> >

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
@ 2019-05-01 11:10     ` Ard Biesheuvel
  0 siblings, 0 replies; 62+ messages in thread
From: Ard Biesheuvel @ 2019-05-01 11:10 UTC (permalink / raw)
  To: Shameer Kolothum
  Cc: QEMU Developers, qemu-arm, Auger Eric, Igor Mammedov,
	Peter Maydell, shannon.zhaosl, sameo, sebastien.boeuf, Wei Xu,
	Laszlo Ersek, Linuxarm

On Tue, 9 Apr 2019 at 12:31, Shameer Kolothum
<shameerali.kolothum.thodi@huawei.com> wrote:
>
> From: Samuel Ortiz <sameo@linux.intel.com>
>
> The ACPI Generic Event Device (GED) is a hardware-reduced specific
> device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> including the hotplug ones.This patch generates the AML code that
> defines GEDs.
>
> Platforms need to specify their own GedEvent array to describe what
> kind of events they want to support through GED.  Also this uses a
> a single interrupt for the  GED device, relying on IO memory region
> to communicate the type of device affected by the interrupt. This
> way, we can support up to 32 events with a unique interrupt.
>
> This supports only memory hotplug for now.
>
> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
> Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
> Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>

Apologies if this question has been raised before, but do we really
need a separate device for this? We already handle the power button
via _AEI/_Exx on the GPIO device, and I think we should be able to add
additional events using that interface, rather than have two event
signalling methods/devices on the same platform.



> ---
>  hw/acpi/Kconfig                        |   4 +
>  hw/acpi/Makefile.objs                  |   1 +
>  hw/acpi/generic_event_device.c         | 311 +++++++++++++++++++++++++++++++++
>  include/hw/acpi/generic_event_device.h | 121 +++++++++++++
>  4 files changed, 437 insertions(+)
>  create mode 100644 hw/acpi/generic_event_device.c
>  create mode 100644 include/hw/acpi/generic_event_device.h
>
> diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig
> index eca3bee..01a8b41 100644
> --- a/hw/acpi/Kconfig
> +++ b/hw/acpi/Kconfig
> @@ -27,3 +27,7 @@ config ACPI_VMGENID
>      bool
>      default y
>      depends on PC
> +
> +config ACPI_HW_REDUCED
> +    bool
> +    depends on ACPI
> diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
> index 2d46e37..b753232 100644
> --- a/hw/acpi/Makefile.objs
> +++ b/hw/acpi/Makefile.objs
> @@ -6,6 +6,7 @@ common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o
>  common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu.o
>  common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
>  common-obj-$(CONFIG_ACPI_VMGENID) += vmgenid.o
> +common-obj-$(CONFIG_ACPI_HW_REDUCED) += generic_event_device.o
>  common-obj-$(call lnot,$(CONFIG_ACPI_X86)) += acpi-stub.o
>
>  common-obj-y += acpi_interface.o
> diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c
> new file mode 100644
> index 0000000..856ca04
> --- /dev/null
> +++ b/hw/acpi/generic_event_device.c
> @@ -0,0 +1,311 @@
> +/*
> + *
> + * Copyright (c) 2018 Intel Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "exec/address-spaces.h"
> +#include "hw/sysbus.h"
> +#include "hw/acpi/acpi.h"
> +#include "hw/acpi/generic_event_device.h"
> +#include "hw/mem/pc-dimm.h"
> +
> +static Aml *ged_event_aml(const GedEvent *event)
> +{
> +
> +    if (!event) {
> +        return NULL;
> +    }
> +
> +    switch (event->event) {
> +    case GED_MEMORY_HOTPLUG:
> +        /* We run a complete memory SCAN when getting a memory hotplug event */
> +        return aml_call0(MEMORY_DEVICES_CONTAINER "." MEMORY_SLOT_SCAN_METHOD);
> +    default:
> +        break;
> +    }
> +
> +    return NULL;
> +}
> +
> +/*
> + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
> + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> + * including the hotplug ones. Platforms need to specify their own
> + * GedEvent array to describe what kind of events they want to support
> + * through GED. This routine uses a single interrupt for the GED device,
> + * relying on IO memory region to communicate the type of device
> + * affected by the interrupt. This way, we can support up to 32 events
> + * with a unique interrupt.
> + */
> +void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev,
> +                   uint32_t ged_irq, AmlRegionSpace rs)
> +{
> +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> +    GedEvent *ged_events = s->ged_events;
> +    Aml *crs = aml_resource_template();
> +    Aml *evt, *field;
> +    Aml *dev = aml_device("%s", name);
> +    Aml *irq_sel = aml_local(0);
> +    Aml *isel = aml_name(AML_GED_IRQ_SEL);
> +    uint32_t i;
> +
> +    if (!s->ged_base || !ged_events || !s->ged_events_size) {
> +        return;
> +    }
> +
> +    /* _CRS interrupt */
> +    aml_append(crs, aml_interrupt(AML_CONSUMER, AML_EDGE, AML_ACTIVE_HIGH,
> +                                  AML_EXCLUSIVE, &ged_irq, 1));
> +    /*
> +     * For each GED event we:
> +     * - Add an interrupt to the CRS section.
> +     * - Add a conditional block for each event, inside a while loop.
> +     *   This is semantically equivalent to a switch/case implementation.
> +     */
> +    evt = aml_method("_EVT", 1, AML_SERIALIZED);
> +    {
> +        Aml *ged_aml;
> +        Aml *if_ctx;
> +
> +        /* Local0 = ISEL */
> +        aml_append(evt, aml_store(isel, irq_sel));
> +
> +        /*
> +         * Here we want to call a method for each supported GED event type.
> +         * The resulting ASL code looks like:
> +         *
> +         * Local0 = ISEL
> +         * If ((Local0 & irq0) == irq0)
> +         * {
> +         *     MethodEvent0()
> +         * }
> +         *
> +         * If ((Local0 & irq1) == irq1)
> +         * {
> +         *     MethodEvent1()
> +         * }
> +         * ...
> +         */
> +
> +        for (i = 0; i < s->ged_events_size; i++) {
> +            ged_aml = ged_event_aml(&ged_events[i]);
> +            if (!ged_aml) {
> +                continue;
> +            }
> +
> +            /* If ((Local1 == irq))*/
> +            if_ctx = aml_if(aml_equal(aml_and(irq_sel,
> +                                      aml_int(ged_events[i].selector), NULL),
> +                                      aml_int(ged_events[i].selector)));
> +            {
> +                /* AML for this specific type of event */
> +                aml_append(if_ctx, ged_aml);
> +            }
> +
> +            /*
> +             * We append the first "if" to the "while" context.
> +             * Other "if"s will be "elseif"s.
> +             */
> +            aml_append(evt, if_ctx);
> +        }
> +    }
> +
> +    aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0013")));
> +    aml_append(dev, aml_name_decl("_UID", aml_string(GED_DEVICE)));
> +    aml_append(dev, aml_name_decl("_CRS", crs));
> +
> +    /* Append IO region */
> +    aml_append(dev, aml_operation_region(AML_GED_IRQ_REG, rs,
> +               aml_int(s->ged_base + ACPI_GED_IRQ_SEL_OFFSET),
> +               ACPI_GED_IRQ_SEL_LEN));
> +    field = aml_field(AML_GED_IRQ_REG, AML_DWORD_ACC, AML_NOLOCK,
> +                      AML_WRITE_AS_ZEROS);
> +    aml_append(field, aml_named_field(AML_GED_IRQ_SEL,
> +                                      ACPI_GED_IRQ_SEL_LEN * 8));
> +    aml_append(dev, field);
> +
> +    /* Append _EVT method */
> +    aml_append(dev, evt);
> +
> +    aml_append(table, dev);
> +}
> +
> +/* Memory read by the GED _EVT AML dynamic method */
> +static uint64_t ged_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    uint64_t val = 0;
> +    GEDState *ged_st = opaque;
> +
> +    switch (addr) {
> +    case ACPI_GED_IRQ_SEL_OFFSET:
> +        /* Read the selector value and reset it */
> +        qemu_mutex_lock(&ged_st->lock);
> +        val = ged_st->sel;
> +        ged_st->sel = 0;
> +        qemu_mutex_unlock(&ged_st->lock);
> +        break;
> +    default:
> +        break;
> +    }
> +
> +    return val;
> +}
> +
> +/* Nothing is expected to be written to the GED memory region */
> +static void ged_write(void *opaque, hwaddr addr, uint64_t data,
> +                      unsigned int size)
> +{
> +}
> +
> +static const MemoryRegionOps ged_ops = {
> +    .read = ged_read,
> +    .write = ged_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +};
> +
> +static void acpi_ged_event(GEDState *ged_st, uint32_t ged_irq_sel)
> +{
> +    /*
> +     * Set the GED IRQ selector to the expected device type value. This
> +     * way, the ACPI method will be able to trigger the right code based
> +     * on a unique IRQ.
> +     */
> +    qemu_mutex_lock(&ged_st->lock);
> +    ged_st->sel = ged_irq_sel;
> +    qemu_mutex_unlock(&ged_st->lock);
> +
> +    /* Trigger the event by sending an interrupt to the guest. */
> +    qemu_irq_pulse(ged_st->gsi[ged_st->irq]);
> +}
> +
> +static void acpi_ged_init(MemoryRegion *as, DeviceState *dev, GEDState *ged_st)
> +{
> +    AcpiGedState *s = ACPI_GED(dev);
> +
> +    assert(s->ged_base);
> +
> +    ged_st->irq = s->ged_irq;
> +    ged_st->gsi = s->gsi;
> +    qemu_mutex_init(&ged_st->lock);
> +    memory_region_init_io(&ged_st->io, OBJECT(dev), &ged_ops, ged_st,
> +                          "acpi-ged-event", ACPI_GED_REG_LEN);
> +    memory_region_add_subregion(as, s->ged_base, &ged_st->io);
> +}
> +
> +static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
> +                                    DeviceState *dev, Error **errp)
> +{
> +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> +
> +    if (s->memhp_state.is_enabled &&
> +        object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> +            acpi_memory_plug_cb(hotplug_dev, &s->memhp_state,
> +                                dev, errp);
> +    } else {
> +        error_setg(errp, "virt: device plug request for unsupported device"
> +                   " type: %s", object_get_typename(OBJECT(dev)));
> +    }
> +}
> +
> +static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
> +{
> +    AcpiGedState *s = ACPI_GED(adev);
> +    uint32_t sel;
> +
> +    if (ev & ACPI_MEMORY_HOTPLUG_STATUS) {
> +        sel = ACPI_GED_IRQ_SEL_MEM;
> +    } else {
> +        /* Unknown event. Return without generating interrupt. */
> +        return;
> +    }
> +
> +    /*
> +     * We inject the hotplug interrupt. The IRQ selector will make
> +     * the difference from the ACPI table.
> +     */
> +    acpi_ged_event(&s->ged_state, sel);
> +}
> +
> +static void acpi_ged_device_realize(DeviceState *dev, Error **errp)
> +{
> +    AcpiGedState *s = ACPI_GED(dev);
> +
> +    if (s->memhp_state.is_enabled) {
> +        acpi_memory_hotplug_init(get_system_memory(), OBJECT(dev),
> +                                 &s->memhp_state,
> +                                 s->memhp_base);
> +        acpi_ged_init(get_system_memory(), dev, &s->ged_state);
> +    }
> +}
> +
> +static Property acpi_ged_properties[] = {
> +    /*
> +     * Memory hotplug base address is a property of GED here,
> +     * because GED handles memory hotplug event and MEMORY_HOTPLUG_DEVICE
> +     * gets initialized when GED device is realized.
> +     */
> +    DEFINE_PROP_UINT64("memhp-base", AcpiGedState, memhp_base, 0),
> +    DEFINE_PROP_BOOL("memory-hotplug-support", AcpiGedState,
> +                     memhp_state.is_enabled, true),
> +    DEFINE_PROP_PTR("gsi", AcpiGedState, gsi),
> +    DEFINE_PROP_UINT64("ged-base", AcpiGedState, ged_base, 0),
> +    DEFINE_PROP_UINT32("ged-irq", AcpiGedState, ged_irq, 0),
> +    DEFINE_PROP_PTR("ged-events", AcpiGedState, ged_events),
> +    DEFINE_PROP_UINT32("ged-events-size", AcpiGedState, ged_events_size, 0),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void acpi_ged_class_init(ObjectClass *class, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(class);
> +    HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class);
> +    AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class);
> +
> +    dc->desc = "ACPI";
> +    dc->props = acpi_ged_properties;
> +    dc->realize = acpi_ged_device_realize;
> +
> +    /* Reason: pointer properties "gsi" and "ged_events" */
> +    dc->user_creatable = false;
> +
> +    hc->plug = acpi_ged_device_plug_cb;
> +
> +    adevc->send_event = acpi_ged_send_event;
> +}
> +
> +static const TypeInfo acpi_ged_info = {
> +    .name          = TYPE_ACPI_GED,
> +    .parent        = TYPE_DEVICE,
> +    .instance_size = sizeof(AcpiGedState),
> +    .class_init    = acpi_ged_class_init,
> +    .interfaces = (InterfaceInfo[]) {
> +        { TYPE_HOTPLUG_HANDLER },
> +        { TYPE_ACPI_DEVICE_IF },
> +        { }
> +    }
> +};
> +
> +static void acpi_ged_register_types(void)
> +{
> +    type_register_static(&acpi_ged_info);
> +}
> +
> +type_init(acpi_ged_register_types)
> diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h
> new file mode 100644
> index 0000000..9c840d8
> --- /dev/null
> +++ b/include/hw/acpi/generic_event_device.h
> @@ -0,0 +1,121 @@
> +/*
> + *
> + * Copyright (c) 2018 Intel Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
> + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> + * including the hotplug ones. Generic Event Device allows platforms
> + * to handle interrupts in ACPI ASL statements. It follows a very
> + * similar approach like the _EVT method from GPIO events. All
> + * interrupts are listed in  _CRS and the handler is written in _EVT
> + * method. Here, we use a single interrupt for the GED device, relying
> + * on IO memory region to communicate the type of device affected by
> + * the interrupt. This way, we can support up to 32 events with a
> + * unique interrupt.
> + *
> + * Here is an example.
> + *
> + * Device (\_SB.GED)
> + * {
> + *     Name (_HID, "ACPI0013")
> + *     Name (_UID, Zero)
> + *     Name (_CRS, ResourceTemplate ()
> + *     {
> + *         Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, )
> + *         {
> + *              0x00000029,
> + *         }
> + *     })
> + *     OperationRegion (IREG, SystemMemory, 0x09080000, 0x04)
> + *     Field (IREG, DWordAcc, NoLock, WriteAsZeros)
> + *     {
> + *         ISEL,   32
> + *     }
> + *
> + *     Method (_EVT, 1, Serialized)  // _EVT: Event
> + *     {
> + *         Local0 = ISEL // ISEL = IO memory region which specifies the
> + *                       // device type.
> + *         If (((Local0 & irq0) == irq0))
> + *         {
> + *             MethodEvent0()
> + *         }
> + *         ElseIf ((Local0 & irq1) == irq1)
> + *         {
> + *             MethodEvent1()
> + *         }
> + *         ...
> + *     }
> + * }
> + *
> + */
> +
> +#ifndef HW_ACPI_GED_H
> +#define HW_ACPI_GED_H
> +
> +#include "hw/acpi/memory_hotplug.h"
> +
> +#define TYPE_ACPI_GED "acpi-ged"
> +#define ACPI_GED(obj) \
> +    OBJECT_CHECK(AcpiGedState, (obj), TYPE_ACPI_GED)
> +
> +#define ACPI_GED_IRQ_SEL_OFFSET 0x0
> +#define ACPI_GED_IRQ_SEL_LEN    0x4
> +#define ACPI_GED_IRQ_SEL_MEM    0x1
> +#define ACPI_GED_REG_LEN        0x4
> +
> +#define GED_DEVICE      "GED"
> +#define AML_GED_IRQ_REG "IREG"
> +#define AML_GED_IRQ_SEL "ISEL"
> +
> +typedef enum {
> +    GED_MEMORY_HOTPLUG = 1,
> +} GedEventType;
> +
> +/*
> + * Platforms need to specify their own GedEvent array
> + * to describe what kind of events they want to support
> + * through GED.
> + */
> +typedef struct GedEvent {
> +    uint32_t     selector;
> +    GedEventType event;
> +} GedEvent;
> +
> +typedef struct GEDState {
> +    MemoryRegion io;
> +    uint32_t     sel;
> +    uint32_t     irq;
> +    qemu_irq     *gsi;
> +    QemuMutex    lock;
> +} GEDState;
> +
> +
> +typedef struct AcpiGedState {
> +    DeviceClass parent_obj;
> +    MemHotplugState memhp_state;
> +    hwaddr memhp_base;
> +    void *gsi;
> +    hwaddr ged_base;
> +    GEDState ged_state;
> +    uint32_t ged_irq;
> +    void *ged_events;
> +    uint32_t ged_events_size;
> +} AcpiGedState;
> +
> +void build_ged_aml(Aml *table, const char* name, HotplugHandler *hotplug_dev,
> +                   uint32_t ged_irq, AmlRegionSpace rs);
> +
> +#endif
> --
> 2.7.4
>
>

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
@ 2019-05-01 11:10     ` Ard Biesheuvel
  0 siblings, 0 replies; 62+ messages in thread
From: Ard Biesheuvel @ 2019-05-01 11:10 UTC (permalink / raw)
  To: Shameer Kolothum
  Cc: Peter Maydell, sameo, shannon.zhaosl, QEMU Developers, Wei Xu,
	Linuxarm, Auger Eric, qemu-arm, Igor Mammedov, sebastien.boeuf,
	Laszlo Ersek

On Tue, 9 Apr 2019 at 12:31, Shameer Kolothum
<shameerali.kolothum.thodi@huawei.com> wrote:
>
> From: Samuel Ortiz <sameo@linux.intel.com>
>
> The ACPI Generic Event Device (GED) is a hardware-reduced specific
> device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> including the hotplug ones.This patch generates the AML code that
> defines GEDs.
>
> Platforms need to specify their own GedEvent array to describe what
> kind of events they want to support through GED.  Also this uses a
> a single interrupt for the  GED device, relying on IO memory region
> to communicate the type of device affected by the interrupt. This
> way, we can support up to 32 events with a unique interrupt.
>
> This supports only memory hotplug for now.
>
> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
> Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
> Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>

Apologies if this question has been raised before, but do we really
need a separate device for this? We already handle the power button
via _AEI/_Exx on the GPIO device, and I think we should be able to add
additional events using that interface, rather than have two event
signalling methods/devices on the same platform.



> ---
>  hw/acpi/Kconfig                        |   4 +
>  hw/acpi/Makefile.objs                  |   1 +
>  hw/acpi/generic_event_device.c         | 311 +++++++++++++++++++++++++++++++++
>  include/hw/acpi/generic_event_device.h | 121 +++++++++++++
>  4 files changed, 437 insertions(+)
>  create mode 100644 hw/acpi/generic_event_device.c
>  create mode 100644 include/hw/acpi/generic_event_device.h
>
> diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig
> index eca3bee..01a8b41 100644
> --- a/hw/acpi/Kconfig
> +++ b/hw/acpi/Kconfig
> @@ -27,3 +27,7 @@ config ACPI_VMGENID
>      bool
>      default y
>      depends on PC
> +
> +config ACPI_HW_REDUCED
> +    bool
> +    depends on ACPI
> diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
> index 2d46e37..b753232 100644
> --- a/hw/acpi/Makefile.objs
> +++ b/hw/acpi/Makefile.objs
> @@ -6,6 +6,7 @@ common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o
>  common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu.o
>  common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
>  common-obj-$(CONFIG_ACPI_VMGENID) += vmgenid.o
> +common-obj-$(CONFIG_ACPI_HW_REDUCED) += generic_event_device.o
>  common-obj-$(call lnot,$(CONFIG_ACPI_X86)) += acpi-stub.o
>
>  common-obj-y += acpi_interface.o
> diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c
> new file mode 100644
> index 0000000..856ca04
> --- /dev/null
> +++ b/hw/acpi/generic_event_device.c
> @@ -0,0 +1,311 @@
> +/*
> + *
> + * Copyright (c) 2018 Intel Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "exec/address-spaces.h"
> +#include "hw/sysbus.h"
> +#include "hw/acpi/acpi.h"
> +#include "hw/acpi/generic_event_device.h"
> +#include "hw/mem/pc-dimm.h"
> +
> +static Aml *ged_event_aml(const GedEvent *event)
> +{
> +
> +    if (!event) {
> +        return NULL;
> +    }
> +
> +    switch (event->event) {
> +    case GED_MEMORY_HOTPLUG:
> +        /* We run a complete memory SCAN when getting a memory hotplug event */
> +        return aml_call0(MEMORY_DEVICES_CONTAINER "." MEMORY_SLOT_SCAN_METHOD);
> +    default:
> +        break;
> +    }
> +
> +    return NULL;
> +}
> +
> +/*
> + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
> + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> + * including the hotplug ones. Platforms need to specify their own
> + * GedEvent array to describe what kind of events they want to support
> + * through GED. This routine uses a single interrupt for the GED device,
> + * relying on IO memory region to communicate the type of device
> + * affected by the interrupt. This way, we can support up to 32 events
> + * with a unique interrupt.
> + */
> +void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev,
> +                   uint32_t ged_irq, AmlRegionSpace rs)
> +{
> +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> +    GedEvent *ged_events = s->ged_events;
> +    Aml *crs = aml_resource_template();
> +    Aml *evt, *field;
> +    Aml *dev = aml_device("%s", name);
> +    Aml *irq_sel = aml_local(0);
> +    Aml *isel = aml_name(AML_GED_IRQ_SEL);
> +    uint32_t i;
> +
> +    if (!s->ged_base || !ged_events || !s->ged_events_size) {
> +        return;
> +    }
> +
> +    /* _CRS interrupt */
> +    aml_append(crs, aml_interrupt(AML_CONSUMER, AML_EDGE, AML_ACTIVE_HIGH,
> +                                  AML_EXCLUSIVE, &ged_irq, 1));
> +    /*
> +     * For each GED event we:
> +     * - Add an interrupt to the CRS section.
> +     * - Add a conditional block for each event, inside a while loop.
> +     *   This is semantically equivalent to a switch/case implementation.
> +     */
> +    evt = aml_method("_EVT", 1, AML_SERIALIZED);
> +    {
> +        Aml *ged_aml;
> +        Aml *if_ctx;
> +
> +        /* Local0 = ISEL */
> +        aml_append(evt, aml_store(isel, irq_sel));
> +
> +        /*
> +         * Here we want to call a method for each supported GED event type.
> +         * The resulting ASL code looks like:
> +         *
> +         * Local0 = ISEL
> +         * If ((Local0 & irq0) == irq0)
> +         * {
> +         *     MethodEvent0()
> +         * }
> +         *
> +         * If ((Local0 & irq1) == irq1)
> +         * {
> +         *     MethodEvent1()
> +         * }
> +         * ...
> +         */
> +
> +        for (i = 0; i < s->ged_events_size; i++) {
> +            ged_aml = ged_event_aml(&ged_events[i]);
> +            if (!ged_aml) {
> +                continue;
> +            }
> +
> +            /* If ((Local1 == irq))*/
> +            if_ctx = aml_if(aml_equal(aml_and(irq_sel,
> +                                      aml_int(ged_events[i].selector), NULL),
> +                                      aml_int(ged_events[i].selector)));
> +            {
> +                /* AML for this specific type of event */
> +                aml_append(if_ctx, ged_aml);
> +            }
> +
> +            /*
> +             * We append the first "if" to the "while" context.
> +             * Other "if"s will be "elseif"s.
> +             */
> +            aml_append(evt, if_ctx);
> +        }
> +    }
> +
> +    aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0013")));
> +    aml_append(dev, aml_name_decl("_UID", aml_string(GED_DEVICE)));
> +    aml_append(dev, aml_name_decl("_CRS", crs));
> +
> +    /* Append IO region */
> +    aml_append(dev, aml_operation_region(AML_GED_IRQ_REG, rs,
> +               aml_int(s->ged_base + ACPI_GED_IRQ_SEL_OFFSET),
> +               ACPI_GED_IRQ_SEL_LEN));
> +    field = aml_field(AML_GED_IRQ_REG, AML_DWORD_ACC, AML_NOLOCK,
> +                      AML_WRITE_AS_ZEROS);
> +    aml_append(field, aml_named_field(AML_GED_IRQ_SEL,
> +                                      ACPI_GED_IRQ_SEL_LEN * 8));
> +    aml_append(dev, field);
> +
> +    /* Append _EVT method */
> +    aml_append(dev, evt);
> +
> +    aml_append(table, dev);
> +}
> +
> +/* Memory read by the GED _EVT AML dynamic method */
> +static uint64_t ged_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    uint64_t val = 0;
> +    GEDState *ged_st = opaque;
> +
> +    switch (addr) {
> +    case ACPI_GED_IRQ_SEL_OFFSET:
> +        /* Read the selector value and reset it */
> +        qemu_mutex_lock(&ged_st->lock);
> +        val = ged_st->sel;
> +        ged_st->sel = 0;
> +        qemu_mutex_unlock(&ged_st->lock);
> +        break;
> +    default:
> +        break;
> +    }
> +
> +    return val;
> +}
> +
> +/* Nothing is expected to be written to the GED memory region */
> +static void ged_write(void *opaque, hwaddr addr, uint64_t data,
> +                      unsigned int size)
> +{
> +}
> +
> +static const MemoryRegionOps ged_ops = {
> +    .read = ged_read,
> +    .write = ged_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +};
> +
> +static void acpi_ged_event(GEDState *ged_st, uint32_t ged_irq_sel)
> +{
> +    /*
> +     * Set the GED IRQ selector to the expected device type value. This
> +     * way, the ACPI method will be able to trigger the right code based
> +     * on a unique IRQ.
> +     */
> +    qemu_mutex_lock(&ged_st->lock);
> +    ged_st->sel = ged_irq_sel;
> +    qemu_mutex_unlock(&ged_st->lock);
> +
> +    /* Trigger the event by sending an interrupt to the guest. */
> +    qemu_irq_pulse(ged_st->gsi[ged_st->irq]);
> +}
> +
> +static void acpi_ged_init(MemoryRegion *as, DeviceState *dev, GEDState *ged_st)
> +{
> +    AcpiGedState *s = ACPI_GED(dev);
> +
> +    assert(s->ged_base);
> +
> +    ged_st->irq = s->ged_irq;
> +    ged_st->gsi = s->gsi;
> +    qemu_mutex_init(&ged_st->lock);
> +    memory_region_init_io(&ged_st->io, OBJECT(dev), &ged_ops, ged_st,
> +                          "acpi-ged-event", ACPI_GED_REG_LEN);
> +    memory_region_add_subregion(as, s->ged_base, &ged_st->io);
> +}
> +
> +static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
> +                                    DeviceState *dev, Error **errp)
> +{
> +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> +
> +    if (s->memhp_state.is_enabled &&
> +        object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> +            acpi_memory_plug_cb(hotplug_dev, &s->memhp_state,
> +                                dev, errp);
> +    } else {
> +        error_setg(errp, "virt: device plug request for unsupported device"
> +                   " type: %s", object_get_typename(OBJECT(dev)));
> +    }
> +}
> +
> +static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
> +{
> +    AcpiGedState *s = ACPI_GED(adev);
> +    uint32_t sel;
> +
> +    if (ev & ACPI_MEMORY_HOTPLUG_STATUS) {
> +        sel = ACPI_GED_IRQ_SEL_MEM;
> +    } else {
> +        /* Unknown event. Return without generating interrupt. */
> +        return;
> +    }
> +
> +    /*
> +     * We inject the hotplug interrupt. The IRQ selector will make
> +     * the difference from the ACPI table.
> +     */
> +    acpi_ged_event(&s->ged_state, sel);
> +}
> +
> +static void acpi_ged_device_realize(DeviceState *dev, Error **errp)
> +{
> +    AcpiGedState *s = ACPI_GED(dev);
> +
> +    if (s->memhp_state.is_enabled) {
> +        acpi_memory_hotplug_init(get_system_memory(), OBJECT(dev),
> +                                 &s->memhp_state,
> +                                 s->memhp_base);
> +        acpi_ged_init(get_system_memory(), dev, &s->ged_state);
> +    }
> +}
> +
> +static Property acpi_ged_properties[] = {
> +    /*
> +     * Memory hotplug base address is a property of GED here,
> +     * because GED handles memory hotplug event and MEMORY_HOTPLUG_DEVICE
> +     * gets initialized when GED device is realized.
> +     */
> +    DEFINE_PROP_UINT64("memhp-base", AcpiGedState, memhp_base, 0),
> +    DEFINE_PROP_BOOL("memory-hotplug-support", AcpiGedState,
> +                     memhp_state.is_enabled, true),
> +    DEFINE_PROP_PTR("gsi", AcpiGedState, gsi),
> +    DEFINE_PROP_UINT64("ged-base", AcpiGedState, ged_base, 0),
> +    DEFINE_PROP_UINT32("ged-irq", AcpiGedState, ged_irq, 0),
> +    DEFINE_PROP_PTR("ged-events", AcpiGedState, ged_events),
> +    DEFINE_PROP_UINT32("ged-events-size", AcpiGedState, ged_events_size, 0),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void acpi_ged_class_init(ObjectClass *class, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(class);
> +    HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class);
> +    AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class);
> +
> +    dc->desc = "ACPI";
> +    dc->props = acpi_ged_properties;
> +    dc->realize = acpi_ged_device_realize;
> +
> +    /* Reason: pointer properties "gsi" and "ged_events" */
> +    dc->user_creatable = false;
> +
> +    hc->plug = acpi_ged_device_plug_cb;
> +
> +    adevc->send_event = acpi_ged_send_event;
> +}
> +
> +static const TypeInfo acpi_ged_info = {
> +    .name          = TYPE_ACPI_GED,
> +    .parent        = TYPE_DEVICE,
> +    .instance_size = sizeof(AcpiGedState),
> +    .class_init    = acpi_ged_class_init,
> +    .interfaces = (InterfaceInfo[]) {
> +        { TYPE_HOTPLUG_HANDLER },
> +        { TYPE_ACPI_DEVICE_IF },
> +        { }
> +    }
> +};
> +
> +static void acpi_ged_register_types(void)
> +{
> +    type_register_static(&acpi_ged_info);
> +}
> +
> +type_init(acpi_ged_register_types)
> diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h
> new file mode 100644
> index 0000000..9c840d8
> --- /dev/null
> +++ b/include/hw/acpi/generic_event_device.h
> @@ -0,0 +1,121 @@
> +/*
> + *
> + * Copyright (c) 2018 Intel Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
> + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> + * including the hotplug ones. Generic Event Device allows platforms
> + * to handle interrupts in ACPI ASL statements. It follows a very
> + * similar approach like the _EVT method from GPIO events. All
> + * interrupts are listed in  _CRS and the handler is written in _EVT
> + * method. Here, we use a single interrupt for the GED device, relying
> + * on IO memory region to communicate the type of device affected by
> + * the interrupt. This way, we can support up to 32 events with a
> + * unique interrupt.
> + *
> + * Here is an example.
> + *
> + * Device (\_SB.GED)
> + * {
> + *     Name (_HID, "ACPI0013")
> + *     Name (_UID, Zero)
> + *     Name (_CRS, ResourceTemplate ()
> + *     {
> + *         Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, )
> + *         {
> + *              0x00000029,
> + *         }
> + *     })
> + *     OperationRegion (IREG, SystemMemory, 0x09080000, 0x04)
> + *     Field (IREG, DWordAcc, NoLock, WriteAsZeros)
> + *     {
> + *         ISEL,   32
> + *     }
> + *
> + *     Method (_EVT, 1, Serialized)  // _EVT: Event
> + *     {
> + *         Local0 = ISEL // ISEL = IO memory region which specifies the
> + *                       // device type.
> + *         If (((Local0 & irq0) == irq0))
> + *         {
> + *             MethodEvent0()
> + *         }
> + *         ElseIf ((Local0 & irq1) == irq1)
> + *         {
> + *             MethodEvent1()
> + *         }
> + *         ...
> + *     }
> + * }
> + *
> + */
> +
> +#ifndef HW_ACPI_GED_H
> +#define HW_ACPI_GED_H
> +
> +#include "hw/acpi/memory_hotplug.h"
> +
> +#define TYPE_ACPI_GED "acpi-ged"
> +#define ACPI_GED(obj) \
> +    OBJECT_CHECK(AcpiGedState, (obj), TYPE_ACPI_GED)
> +
> +#define ACPI_GED_IRQ_SEL_OFFSET 0x0
> +#define ACPI_GED_IRQ_SEL_LEN    0x4
> +#define ACPI_GED_IRQ_SEL_MEM    0x1
> +#define ACPI_GED_REG_LEN        0x4
> +
> +#define GED_DEVICE      "GED"
> +#define AML_GED_IRQ_REG "IREG"
> +#define AML_GED_IRQ_SEL "ISEL"
> +
> +typedef enum {
> +    GED_MEMORY_HOTPLUG = 1,
> +} GedEventType;
> +
> +/*
> + * Platforms need to specify their own GedEvent array
> + * to describe what kind of events they want to support
> + * through GED.
> + */
> +typedef struct GedEvent {
> +    uint32_t     selector;
> +    GedEventType event;
> +} GedEvent;
> +
> +typedef struct GEDState {
> +    MemoryRegion io;
> +    uint32_t     sel;
> +    uint32_t     irq;
> +    qemu_irq     *gsi;
> +    QemuMutex    lock;
> +} GEDState;
> +
> +
> +typedef struct AcpiGedState {
> +    DeviceClass parent_obj;
> +    MemHotplugState memhp_state;
> +    hwaddr memhp_base;
> +    void *gsi;
> +    hwaddr ged_base;
> +    GEDState ged_state;
> +    uint32_t ged_irq;
> +    void *ged_events;
> +    uint32_t ged_events_size;
> +} AcpiGedState;
> +
> +void build_ged_aml(Aml *table, const char* name, HotplugHandler *hotplug_dev,
> +                   uint32_t ged_irq, AmlRegionSpace rs);
> +
> +#endif
> --
> 2.7.4
>
>


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
@ 2019-05-01 11:25       ` Shameerali Kolothum Thodi
  0 siblings, 0 replies; 62+ messages in thread
From: Shameerali Kolothum Thodi @ 2019-05-01 11:25 UTC (permalink / raw)
  To: Ard Biesheuvel
  Cc: QEMU Developers, qemu-arm, Auger Eric, Igor Mammedov,
	Peter Maydell, shannon.zhaosl, sameo, sebastien.boeuf, xuwei (O),
	Laszlo Ersek, Linuxarm

Hi Ard,

> -----Original Message-----
> From: Ard Biesheuvel [mailto:ard.biesheuvel@linaro.org]
> Sent: 01 May 2019 12:10
> To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>
> Cc: QEMU Developers <qemu-devel@nongnu.org>; qemu-arm
> <qemu-arm@nongnu.org>; Auger Eric <eric.auger@redhat.com>; Igor
> Mammedov <imammedo@redhat.com>; Peter Maydell
> <peter.maydell@linaro.org>; shannon.zhaosl@gmail.com;
> sameo@linux.intel.com; sebastien.boeuf@intel.com; xuwei (O)
> <xuwei5@huawei.com>; Laszlo Ersek <lersek@redhat.com>; Linuxarm
> <linuxarm@huawei.com>
> Subject: Re: [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
> 
> On Tue, 9 Apr 2019 at 12:31, Shameer Kolothum
> <shameerali.kolothum.thodi@huawei.com> wrote:
> >
> > From: Samuel Ortiz <sameo@linux.intel.com>
> >
> > The ACPI Generic Event Device (GED) is a hardware-reduced specific
> > device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> > including the hotplug ones.This patch generates the AML code that
> > defines GEDs.
> >
> > Platforms need to specify their own GedEvent array to describe what
> > kind of events they want to support through GED.  Also this uses a
> > a single interrupt for the  GED device, relying on IO memory region
> > to communicate the type of device affected by the interrupt. This
> > way, we can support up to 32 events with a unique interrupt.
> >
> > This supports only memory hotplug for now.
> >
> > Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
> > Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
> > Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> 
> Apologies if this question has been raised before, but do we really
> need a separate device for this? We already handle the power button
> via _AEI/_Exx on the GPIO device, and I think we should be able to add
> additional events using that interface, rather than have two event
> signalling methods/devices on the same platform.

Right. The initial RFC was based on GPIO device[1] and later Igor commented
here[2] that,

" ARM boards were first to use ACPI hw-reduced profile so they picked up
available back then GPIO based way to deliver hotplug event, later spec
introduced Generic Event Device for that means to use with hw-reduced
profile, which NEMU implemented[1], so I'd use that rather than ad-hoc
GPIO mapping. I'd guess it will more compatible with various contemporary
guests and we could reuse the same code for both x86/arm virt boards) "

Thanks,
Shameer

[1]. https://patchwork.kernel.org/cover/10783589/
[2] http://patchwork.ozlabs.org/cover/1045604/

> 
> 
> > ---
> >  hw/acpi/Kconfig                        |   4 +
> >  hw/acpi/Makefile.objs                  |   1 +
> >  hw/acpi/generic_event_device.c         | 311
> +++++++++++++++++++++++++++++++++
> >  include/hw/acpi/generic_event_device.h | 121 +++++++++++++
> >  4 files changed, 437 insertions(+)
> >  create mode 100644 hw/acpi/generic_event_device.c
> >  create mode 100644 include/hw/acpi/generic_event_device.h
> >
> > diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig
> > index eca3bee..01a8b41 100644
> > --- a/hw/acpi/Kconfig
> > +++ b/hw/acpi/Kconfig
> > @@ -27,3 +27,7 @@ config ACPI_VMGENID
> >      bool
> >      default y
> >      depends on PC
> > +
> > +config ACPI_HW_REDUCED
> > +    bool
> > +    depends on ACPI
> > diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
> > index 2d46e37..b753232 100644
> > --- a/hw/acpi/Makefile.objs
> > +++ b/hw/acpi/Makefile.objs
> > @@ -6,6 +6,7 @@ common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) +=
> memory_hotplug.o
> >  common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu.o
> >  common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
> >  common-obj-$(CONFIG_ACPI_VMGENID) += vmgenid.o
> > +common-obj-$(CONFIG_ACPI_HW_REDUCED) += generic_event_device.o
> >  common-obj-$(call lnot,$(CONFIG_ACPI_X86)) += acpi-stub.o
> >
> >  common-obj-y += acpi_interface.o
> > diff --git a/hw/acpi/generic_event_device.c
> b/hw/acpi/generic_event_device.c
> > new file mode 100644
> > index 0000000..856ca04
> > --- /dev/null
> > +++ b/hw/acpi/generic_event_device.c
> > @@ -0,0 +1,311 @@
> > +/*
> > + *
> > + * Copyright (c) 2018 Intel Corporation
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2 or later, as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope it will be useful, but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of
> MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
> License for
> > + * more details.
> > + *
> > + * You should have received a copy of the GNU General Public License along
> with
> > + * this program.  If not, see <http://www.gnu.org/licenses/>.
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +#include "qapi/error.h"
> > +#include "exec/address-spaces.h"
> > +#include "hw/sysbus.h"
> > +#include "hw/acpi/acpi.h"
> > +#include "hw/acpi/generic_event_device.h"
> > +#include "hw/mem/pc-dimm.h"
> > +
> > +static Aml *ged_event_aml(const GedEvent *event)
> > +{
> > +
> > +    if (!event) {
> > +        return NULL;
> > +    }
> > +
> > +    switch (event->event) {
> > +    case GED_MEMORY_HOTPLUG:
> > +        /* We run a complete memory SCAN when getting a memory
> hotplug event */
> > +        return aml_call0(MEMORY_DEVICES_CONTAINER "."
> MEMORY_SLOT_SCAN_METHOD);
> > +    default:
> > +        break;
> > +    }
> > +
> > +    return NULL;
> > +}
> > +
> > +/*
> > + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
> > + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> > + * including the hotplug ones. Platforms need to specify their own
> > + * GedEvent array to describe what kind of events they want to support
> > + * through GED. This routine uses a single interrupt for the GED device,
> > + * relying on IO memory region to communicate the type of device
> > + * affected by the interrupt. This way, we can support up to 32 events
> > + * with a unique interrupt.
> > + */
> > +void build_ged_aml(Aml *table, const char *name, HotplugHandler
> *hotplug_dev,
> > +                   uint32_t ged_irq, AmlRegionSpace rs)
> > +{
> > +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> > +    GedEvent *ged_events = s->ged_events;
> > +    Aml *crs = aml_resource_template();
> > +    Aml *evt, *field;
> > +    Aml *dev = aml_device("%s", name);
> > +    Aml *irq_sel = aml_local(0);
> > +    Aml *isel = aml_name(AML_GED_IRQ_SEL);
> > +    uint32_t i;
> > +
> > +    if (!s->ged_base || !ged_events || !s->ged_events_size) {
> > +        return;
> > +    }
> > +
> > +    /* _CRS interrupt */
> > +    aml_append(crs, aml_interrupt(AML_CONSUMER, AML_EDGE,
> AML_ACTIVE_HIGH,
> > +                                  AML_EXCLUSIVE, &ged_irq, 1));
> > +    /*
> > +     * For each GED event we:
> > +     * - Add an interrupt to the CRS section.
> > +     * - Add a conditional block for each event, inside a while loop.
> > +     *   This is semantically equivalent to a switch/case implementation.
> > +     */
> > +    evt = aml_method("_EVT", 1, AML_SERIALIZED);
> > +    {
> > +        Aml *ged_aml;
> > +        Aml *if_ctx;
> > +
> > +        /* Local0 = ISEL */
> > +        aml_append(evt, aml_store(isel, irq_sel));
> > +
> > +        /*
> > +         * Here we want to call a method for each supported GED event
> type.
> > +         * The resulting ASL code looks like:
> > +         *
> > +         * Local0 = ISEL
> > +         * If ((Local0 & irq0) == irq0)
> > +         * {
> > +         *     MethodEvent0()
> > +         * }
> > +         *
> > +         * If ((Local0 & irq1) == irq1)
> > +         * {
> > +         *     MethodEvent1()
> > +         * }
> > +         * ...
> > +         */
> > +
> > +        for (i = 0; i < s->ged_events_size; i++) {
> > +            ged_aml = ged_event_aml(&ged_events[i]);
> > +            if (!ged_aml) {
> > +                continue;
> > +            }
> > +
> > +            /* If ((Local1 == irq))*/
> > +            if_ctx = aml_if(aml_equal(aml_and(irq_sel,
> > +
> aml_int(ged_events[i].selector), NULL),
> > +
> aml_int(ged_events[i].selector)));
> > +            {
> > +                /* AML for this specific type of event */
> > +                aml_append(if_ctx, ged_aml);
> > +            }
> > +
> > +            /*
> > +             * We append the first "if" to the "while" context.
> > +             * Other "if"s will be "elseif"s.
> > +             */
> > +            aml_append(evt, if_ctx);
> > +        }
> > +    }
> > +
> > +    aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0013")));
> > +    aml_append(dev, aml_name_decl("_UID", aml_string(GED_DEVICE)));
> > +    aml_append(dev, aml_name_decl("_CRS", crs));
> > +
> > +    /* Append IO region */
> > +    aml_append(dev, aml_operation_region(AML_GED_IRQ_REG, rs,
> > +               aml_int(s->ged_base + ACPI_GED_IRQ_SEL_OFFSET),
> > +               ACPI_GED_IRQ_SEL_LEN));
> > +    field = aml_field(AML_GED_IRQ_REG, AML_DWORD_ACC,
> AML_NOLOCK,
> > +                      AML_WRITE_AS_ZEROS);
> > +    aml_append(field, aml_named_field(AML_GED_IRQ_SEL,
> > +                                      ACPI_GED_IRQ_SEL_LEN *
> 8));
> > +    aml_append(dev, field);
> > +
> > +    /* Append _EVT method */
> > +    aml_append(dev, evt);
> > +
> > +    aml_append(table, dev);
> > +}
> > +
> > +/* Memory read by the GED _EVT AML dynamic method */
> > +static uint64_t ged_read(void *opaque, hwaddr addr, unsigned size)
> > +{
> > +    uint64_t val = 0;
> > +    GEDState *ged_st = opaque;
> > +
> > +    switch (addr) {
> > +    case ACPI_GED_IRQ_SEL_OFFSET:
> > +        /* Read the selector value and reset it */
> > +        qemu_mutex_lock(&ged_st->lock);
> > +        val = ged_st->sel;
> > +        ged_st->sel = 0;
> > +        qemu_mutex_unlock(&ged_st->lock);
> > +        break;
> > +    default:
> > +        break;
> > +    }
> > +
> > +    return val;
> > +}
> > +
> > +/* Nothing is expected to be written to the GED memory region */
> > +static void ged_write(void *opaque, hwaddr addr, uint64_t data,
> > +                      unsigned int size)
> > +{
> > +}
> > +
> > +static const MemoryRegionOps ged_ops = {
> > +    .read = ged_read,
> > +    .write = ged_write,
> > +    .endianness = DEVICE_LITTLE_ENDIAN,
> > +    .valid = {
> > +        .min_access_size = 4,
> > +        .max_access_size = 4,
> > +    },
> > +};
> > +
> > +static void acpi_ged_event(GEDState *ged_st, uint32_t ged_irq_sel)
> > +{
> > +    /*
> > +     * Set the GED IRQ selector to the expected device type value. This
> > +     * way, the ACPI method will be able to trigger the right code based
> > +     * on a unique IRQ.
> > +     */
> > +    qemu_mutex_lock(&ged_st->lock);
> > +    ged_st->sel = ged_irq_sel;
> > +    qemu_mutex_unlock(&ged_st->lock);
> > +
> > +    /* Trigger the event by sending an interrupt to the guest. */
> > +    qemu_irq_pulse(ged_st->gsi[ged_st->irq]);
> > +}
> > +
> > +static void acpi_ged_init(MemoryRegion *as, DeviceState *dev, GEDState
> *ged_st)
> > +{
> > +    AcpiGedState *s = ACPI_GED(dev);
> > +
> > +    assert(s->ged_base);
> > +
> > +    ged_st->irq = s->ged_irq;
> > +    ged_st->gsi = s->gsi;
> > +    qemu_mutex_init(&ged_st->lock);
> > +    memory_region_init_io(&ged_st->io, OBJECT(dev), &ged_ops, ged_st,
> > +                          "acpi-ged-event", ACPI_GED_REG_LEN);
> > +    memory_region_add_subregion(as, s->ged_base, &ged_st->io);
> > +}
> > +
> > +static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
> > +                                    DeviceState *dev, Error **errp)
> > +{
> > +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> > +
> > +    if (s->memhp_state.is_enabled &&
> > +        object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> > +            acpi_memory_plug_cb(hotplug_dev, &s->memhp_state,
> > +                                dev, errp);
> > +    } else {
> > +        error_setg(errp, "virt: device plug request for unsupported
> device"
> > +                   " type: %s", object_get_typename(OBJECT(dev)));
> > +    }
> > +}
> > +
> > +static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits
> ev)
> > +{
> > +    AcpiGedState *s = ACPI_GED(adev);
> > +    uint32_t sel;
> > +
> > +    if (ev & ACPI_MEMORY_HOTPLUG_STATUS) {
> > +        sel = ACPI_GED_IRQ_SEL_MEM;
> > +    } else {
> > +        /* Unknown event. Return without generating interrupt. */
> > +        return;
> > +    }
> > +
> > +    /*
> > +     * We inject the hotplug interrupt. The IRQ selector will make
> > +     * the difference from the ACPI table.
> > +     */
> > +    acpi_ged_event(&s->ged_state, sel);
> > +}
> > +
> > +static void acpi_ged_device_realize(DeviceState *dev, Error **errp)
> > +{
> > +    AcpiGedState *s = ACPI_GED(dev);
> > +
> > +    if (s->memhp_state.is_enabled) {
> > +        acpi_memory_hotplug_init(get_system_memory(), OBJECT(dev),
> > +                                 &s->memhp_state,
> > +                                 s->memhp_base);
> > +        acpi_ged_init(get_system_memory(), dev, &s->ged_state);
> > +    }
> > +}
> > +
> > +static Property acpi_ged_properties[] = {
> > +    /*
> > +     * Memory hotplug base address is a property of GED here,
> > +     * because GED handles memory hotplug event and
> MEMORY_HOTPLUG_DEVICE
> > +     * gets initialized when GED device is realized.
> > +     */
> > +    DEFINE_PROP_UINT64("memhp-base", AcpiGedState, memhp_base,
> 0),
> > +    DEFINE_PROP_BOOL("memory-hotplug-support", AcpiGedState,
> > +                     memhp_state.is_enabled, true),
> > +    DEFINE_PROP_PTR("gsi", AcpiGedState, gsi),
> > +    DEFINE_PROP_UINT64("ged-base", AcpiGedState, ged_base, 0),
> > +    DEFINE_PROP_UINT32("ged-irq", AcpiGedState, ged_irq, 0),
> > +    DEFINE_PROP_PTR("ged-events", AcpiGedState, ged_events),
> > +    DEFINE_PROP_UINT32("ged-events-size", AcpiGedState,
> ged_events_size, 0),
> > +    DEFINE_PROP_END_OF_LIST(),
> > +};
> > +
> > +static void acpi_ged_class_init(ObjectClass *class, void *data)
> > +{
> > +    DeviceClass *dc = DEVICE_CLASS(class);
> > +    HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class);
> > +    AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class);
> > +
> > +    dc->desc = "ACPI";
> > +    dc->props = acpi_ged_properties;
> > +    dc->realize = acpi_ged_device_realize;
> > +
> > +    /* Reason: pointer properties "gsi" and "ged_events" */
> > +    dc->user_creatable = false;
> > +
> > +    hc->plug = acpi_ged_device_plug_cb;
> > +
> > +    adevc->send_event = acpi_ged_send_event;
> > +}
> > +
> > +static const TypeInfo acpi_ged_info = {
> > +    .name          = TYPE_ACPI_GED,
> > +    .parent        = TYPE_DEVICE,
> > +    .instance_size = sizeof(AcpiGedState),
> > +    .class_init    = acpi_ged_class_init,
> > +    .interfaces = (InterfaceInfo[]) {
> > +        { TYPE_HOTPLUG_HANDLER },
> > +        { TYPE_ACPI_DEVICE_IF },
> > +        { }
> > +    }
> > +};
> > +
> > +static void acpi_ged_register_types(void)
> > +{
> > +    type_register_static(&acpi_ged_info);
> > +}
> > +
> > +type_init(acpi_ged_register_types)
> > diff --git a/include/hw/acpi/generic_event_device.h
> b/include/hw/acpi/generic_event_device.h
> > new file mode 100644
> > index 0000000..9c840d8
> > --- /dev/null
> > +++ b/include/hw/acpi/generic_event_device.h
> > @@ -0,0 +1,121 @@
> > +/*
> > + *
> > + * Copyright (c) 2018 Intel Corporation
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2 or later, as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope it will be useful, but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of
> MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
> License for
> > + * more details.
> > + *
> > + * You should have received a copy of the GNU General Public License along
> with
> > + * this program.  If not, see <http://www.gnu.org/licenses/>.
> > + *
> > + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
> > + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> > + * including the hotplug ones. Generic Event Device allows platforms
> > + * to handle interrupts in ACPI ASL statements. It follows a very
> > + * similar approach like the _EVT method from GPIO events. All
> > + * interrupts are listed in  _CRS and the handler is written in _EVT
> > + * method. Here, we use a single interrupt for the GED device, relying
> > + * on IO memory region to communicate the type of device affected by
> > + * the interrupt. This way, we can support up to 32 events with a
> > + * unique interrupt.
> > + *
> > + * Here is an example.
> > + *
> > + * Device (\_SB.GED)
> > + * {
> > + *     Name (_HID, "ACPI0013")
> > + *     Name (_UID, Zero)
> > + *     Name (_CRS, ResourceTemplate ()
> > + *     {
> > + *         Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, )
> > + *         {
> > + *              0x00000029,
> > + *         }
> > + *     })
> > + *     OperationRegion (IREG, SystemMemory, 0x09080000, 0x04)
> > + *     Field (IREG, DWordAcc, NoLock, WriteAsZeros)
> > + *     {
> > + *         ISEL,   32
> > + *     }
> > + *
> > + *     Method (_EVT, 1, Serialized)  // _EVT: Event
> > + *     {
> > + *         Local0 = ISEL // ISEL = IO memory region which specifies the
> > + *                       // device type.
> > + *         If (((Local0 & irq0) == irq0))
> > + *         {
> > + *             MethodEvent0()
> > + *         }
> > + *         ElseIf ((Local0 & irq1) == irq1)
> > + *         {
> > + *             MethodEvent1()
> > + *         }
> > + *         ...
> > + *     }
> > + * }
> > + *
> > + */
> > +
> > +#ifndef HW_ACPI_GED_H
> > +#define HW_ACPI_GED_H
> > +
> > +#include "hw/acpi/memory_hotplug.h"
> > +
> > +#define TYPE_ACPI_GED "acpi-ged"
> > +#define ACPI_GED(obj) \
> > +    OBJECT_CHECK(AcpiGedState, (obj), TYPE_ACPI_GED)
> > +
> > +#define ACPI_GED_IRQ_SEL_OFFSET 0x0
> > +#define ACPI_GED_IRQ_SEL_LEN    0x4
> > +#define ACPI_GED_IRQ_SEL_MEM    0x1
> > +#define ACPI_GED_REG_LEN        0x4
> > +
> > +#define GED_DEVICE      "GED"
> > +#define AML_GED_IRQ_REG "IREG"
> > +#define AML_GED_IRQ_SEL "ISEL"
> > +
> > +typedef enum {
> > +    GED_MEMORY_HOTPLUG = 1,
> > +} GedEventType;
> > +
> > +/*
> > + * Platforms need to specify their own GedEvent array
> > + * to describe what kind of events they want to support
> > + * through GED.
> > + */
> > +typedef struct GedEvent {
> > +    uint32_t     selector;
> > +    GedEventType event;
> > +} GedEvent;
> > +
> > +typedef struct GEDState {
> > +    MemoryRegion io;
> > +    uint32_t     sel;
> > +    uint32_t     irq;
> > +    qemu_irq     *gsi;
> > +    QemuMutex    lock;
> > +} GEDState;
> > +
> > +
> > +typedef struct AcpiGedState {
> > +    DeviceClass parent_obj;
> > +    MemHotplugState memhp_state;
> > +    hwaddr memhp_base;
> > +    void *gsi;
> > +    hwaddr ged_base;
> > +    GEDState ged_state;
> > +    uint32_t ged_irq;
> > +    void *ged_events;
> > +    uint32_t ged_events_size;
> > +} AcpiGedState;
> > +
> > +void build_ged_aml(Aml *table, const char* name, HotplugHandler
> *hotplug_dev,
> > +                   uint32_t ged_irq, AmlRegionSpace rs);
> > +
> > +#endif
> > --
> > 2.7.4
> >
> >

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
@ 2019-05-01 11:25       ` Shameerali Kolothum Thodi
  0 siblings, 0 replies; 62+ messages in thread
From: Shameerali Kolothum Thodi @ 2019-05-01 11:25 UTC (permalink / raw)
  To: Ard Biesheuvel
  Cc: Peter Maydell, sameo, shannon.zhaosl, QEMU Developers, Linuxarm,
	Auger Eric, qemu-arm, xuwei (O),
	Igor Mammedov, sebastien.boeuf, Laszlo Ersek

Hi Ard,

> -----Original Message-----
> From: Ard Biesheuvel [mailto:ard.biesheuvel@linaro.org]
> Sent: 01 May 2019 12:10
> To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>
> Cc: QEMU Developers <qemu-devel@nongnu.org>; qemu-arm
> <qemu-arm@nongnu.org>; Auger Eric <eric.auger@redhat.com>; Igor
> Mammedov <imammedo@redhat.com>; Peter Maydell
> <peter.maydell@linaro.org>; shannon.zhaosl@gmail.com;
> sameo@linux.intel.com; sebastien.boeuf@intel.com; xuwei (O)
> <xuwei5@huawei.com>; Laszlo Ersek <lersek@redhat.com>; Linuxarm
> <linuxarm@huawei.com>
> Subject: Re: [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
> 
> On Tue, 9 Apr 2019 at 12:31, Shameer Kolothum
> <shameerali.kolothum.thodi@huawei.com> wrote:
> >
> > From: Samuel Ortiz <sameo@linux.intel.com>
> >
> > The ACPI Generic Event Device (GED) is a hardware-reduced specific
> > device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> > including the hotplug ones.This patch generates the AML code that
> > defines GEDs.
> >
> > Platforms need to specify their own GedEvent array to describe what
> > kind of events they want to support through GED.  Also this uses a
> > a single interrupt for the  GED device, relying on IO memory region
> > to communicate the type of device affected by the interrupt. This
> > way, we can support up to 32 events with a unique interrupt.
> >
> > This supports only memory hotplug for now.
> >
> > Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
> > Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
> > Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> 
> Apologies if this question has been raised before, but do we really
> need a separate device for this? We already handle the power button
> via _AEI/_Exx on the GPIO device, and I think we should be able to add
> additional events using that interface, rather than have two event
> signalling methods/devices on the same platform.

Right. The initial RFC was based on GPIO device[1] and later Igor commented
here[2] that,

" ARM boards were first to use ACPI hw-reduced profile so they picked up
available back then GPIO based way to deliver hotplug event, later spec
introduced Generic Event Device for that means to use with hw-reduced
profile, which NEMU implemented[1], so I'd use that rather than ad-hoc
GPIO mapping. I'd guess it will more compatible with various contemporary
guests and we could reuse the same code for both x86/arm virt boards) "

Thanks,
Shameer

[1]. https://patchwork.kernel.org/cover/10783589/
[2] http://patchwork.ozlabs.org/cover/1045604/

> 
> 
> > ---
> >  hw/acpi/Kconfig                        |   4 +
> >  hw/acpi/Makefile.objs                  |   1 +
> >  hw/acpi/generic_event_device.c         | 311
> +++++++++++++++++++++++++++++++++
> >  include/hw/acpi/generic_event_device.h | 121 +++++++++++++
> >  4 files changed, 437 insertions(+)
> >  create mode 100644 hw/acpi/generic_event_device.c
> >  create mode 100644 include/hw/acpi/generic_event_device.h
> >
> > diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig
> > index eca3bee..01a8b41 100644
> > --- a/hw/acpi/Kconfig
> > +++ b/hw/acpi/Kconfig
> > @@ -27,3 +27,7 @@ config ACPI_VMGENID
> >      bool
> >      default y
> >      depends on PC
> > +
> > +config ACPI_HW_REDUCED
> > +    bool
> > +    depends on ACPI
> > diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
> > index 2d46e37..b753232 100644
> > --- a/hw/acpi/Makefile.objs
> > +++ b/hw/acpi/Makefile.objs
> > @@ -6,6 +6,7 @@ common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) +=
> memory_hotplug.o
> >  common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu.o
> >  common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
> >  common-obj-$(CONFIG_ACPI_VMGENID) += vmgenid.o
> > +common-obj-$(CONFIG_ACPI_HW_REDUCED) += generic_event_device.o
> >  common-obj-$(call lnot,$(CONFIG_ACPI_X86)) += acpi-stub.o
> >
> >  common-obj-y += acpi_interface.o
> > diff --git a/hw/acpi/generic_event_device.c
> b/hw/acpi/generic_event_device.c
> > new file mode 100644
> > index 0000000..856ca04
> > --- /dev/null
> > +++ b/hw/acpi/generic_event_device.c
> > @@ -0,0 +1,311 @@
> > +/*
> > + *
> > + * Copyright (c) 2018 Intel Corporation
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2 or later, as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope it will be useful, but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of
> MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
> License for
> > + * more details.
> > + *
> > + * You should have received a copy of the GNU General Public License along
> with
> > + * this program.  If not, see <http://www.gnu.org/licenses/>.
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +#include "qapi/error.h"
> > +#include "exec/address-spaces.h"
> > +#include "hw/sysbus.h"
> > +#include "hw/acpi/acpi.h"
> > +#include "hw/acpi/generic_event_device.h"
> > +#include "hw/mem/pc-dimm.h"
> > +
> > +static Aml *ged_event_aml(const GedEvent *event)
> > +{
> > +
> > +    if (!event) {
> > +        return NULL;
> > +    }
> > +
> > +    switch (event->event) {
> > +    case GED_MEMORY_HOTPLUG:
> > +        /* We run a complete memory SCAN when getting a memory
> hotplug event */
> > +        return aml_call0(MEMORY_DEVICES_CONTAINER "."
> MEMORY_SLOT_SCAN_METHOD);
> > +    default:
> > +        break;
> > +    }
> > +
> > +    return NULL;
> > +}
> > +
> > +/*
> > + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
> > + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> > + * including the hotplug ones. Platforms need to specify their own
> > + * GedEvent array to describe what kind of events they want to support
> > + * through GED. This routine uses a single interrupt for the GED device,
> > + * relying on IO memory region to communicate the type of device
> > + * affected by the interrupt. This way, we can support up to 32 events
> > + * with a unique interrupt.
> > + */
> > +void build_ged_aml(Aml *table, const char *name, HotplugHandler
> *hotplug_dev,
> > +                   uint32_t ged_irq, AmlRegionSpace rs)
> > +{
> > +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> > +    GedEvent *ged_events = s->ged_events;
> > +    Aml *crs = aml_resource_template();
> > +    Aml *evt, *field;
> > +    Aml *dev = aml_device("%s", name);
> > +    Aml *irq_sel = aml_local(0);
> > +    Aml *isel = aml_name(AML_GED_IRQ_SEL);
> > +    uint32_t i;
> > +
> > +    if (!s->ged_base || !ged_events || !s->ged_events_size) {
> > +        return;
> > +    }
> > +
> > +    /* _CRS interrupt */
> > +    aml_append(crs, aml_interrupt(AML_CONSUMER, AML_EDGE,
> AML_ACTIVE_HIGH,
> > +                                  AML_EXCLUSIVE, &ged_irq, 1));
> > +    /*
> > +     * For each GED event we:
> > +     * - Add an interrupt to the CRS section.
> > +     * - Add a conditional block for each event, inside a while loop.
> > +     *   This is semantically equivalent to a switch/case implementation.
> > +     */
> > +    evt = aml_method("_EVT", 1, AML_SERIALIZED);
> > +    {
> > +        Aml *ged_aml;
> > +        Aml *if_ctx;
> > +
> > +        /* Local0 = ISEL */
> > +        aml_append(evt, aml_store(isel, irq_sel));
> > +
> > +        /*
> > +         * Here we want to call a method for each supported GED event
> type.
> > +         * The resulting ASL code looks like:
> > +         *
> > +         * Local0 = ISEL
> > +         * If ((Local0 & irq0) == irq0)
> > +         * {
> > +         *     MethodEvent0()
> > +         * }
> > +         *
> > +         * If ((Local0 & irq1) == irq1)
> > +         * {
> > +         *     MethodEvent1()
> > +         * }
> > +         * ...
> > +         */
> > +
> > +        for (i = 0; i < s->ged_events_size; i++) {
> > +            ged_aml = ged_event_aml(&ged_events[i]);
> > +            if (!ged_aml) {
> > +                continue;
> > +            }
> > +
> > +            /* If ((Local1 == irq))*/
> > +            if_ctx = aml_if(aml_equal(aml_and(irq_sel,
> > +
> aml_int(ged_events[i].selector), NULL),
> > +
> aml_int(ged_events[i].selector)));
> > +            {
> > +                /* AML for this specific type of event */
> > +                aml_append(if_ctx, ged_aml);
> > +            }
> > +
> > +            /*
> > +             * We append the first "if" to the "while" context.
> > +             * Other "if"s will be "elseif"s.
> > +             */
> > +            aml_append(evt, if_ctx);
> > +        }
> > +    }
> > +
> > +    aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0013")));
> > +    aml_append(dev, aml_name_decl("_UID", aml_string(GED_DEVICE)));
> > +    aml_append(dev, aml_name_decl("_CRS", crs));
> > +
> > +    /* Append IO region */
> > +    aml_append(dev, aml_operation_region(AML_GED_IRQ_REG, rs,
> > +               aml_int(s->ged_base + ACPI_GED_IRQ_SEL_OFFSET),
> > +               ACPI_GED_IRQ_SEL_LEN));
> > +    field = aml_field(AML_GED_IRQ_REG, AML_DWORD_ACC,
> AML_NOLOCK,
> > +                      AML_WRITE_AS_ZEROS);
> > +    aml_append(field, aml_named_field(AML_GED_IRQ_SEL,
> > +                                      ACPI_GED_IRQ_SEL_LEN *
> 8));
> > +    aml_append(dev, field);
> > +
> > +    /* Append _EVT method */
> > +    aml_append(dev, evt);
> > +
> > +    aml_append(table, dev);
> > +}
> > +
> > +/* Memory read by the GED _EVT AML dynamic method */
> > +static uint64_t ged_read(void *opaque, hwaddr addr, unsigned size)
> > +{
> > +    uint64_t val = 0;
> > +    GEDState *ged_st = opaque;
> > +
> > +    switch (addr) {
> > +    case ACPI_GED_IRQ_SEL_OFFSET:
> > +        /* Read the selector value and reset it */
> > +        qemu_mutex_lock(&ged_st->lock);
> > +        val = ged_st->sel;
> > +        ged_st->sel = 0;
> > +        qemu_mutex_unlock(&ged_st->lock);
> > +        break;
> > +    default:
> > +        break;
> > +    }
> > +
> > +    return val;
> > +}
> > +
> > +/* Nothing is expected to be written to the GED memory region */
> > +static void ged_write(void *opaque, hwaddr addr, uint64_t data,
> > +                      unsigned int size)
> > +{
> > +}
> > +
> > +static const MemoryRegionOps ged_ops = {
> > +    .read = ged_read,
> > +    .write = ged_write,
> > +    .endianness = DEVICE_LITTLE_ENDIAN,
> > +    .valid = {
> > +        .min_access_size = 4,
> > +        .max_access_size = 4,
> > +    },
> > +};
> > +
> > +static void acpi_ged_event(GEDState *ged_st, uint32_t ged_irq_sel)
> > +{
> > +    /*
> > +     * Set the GED IRQ selector to the expected device type value. This
> > +     * way, the ACPI method will be able to trigger the right code based
> > +     * on a unique IRQ.
> > +     */
> > +    qemu_mutex_lock(&ged_st->lock);
> > +    ged_st->sel = ged_irq_sel;
> > +    qemu_mutex_unlock(&ged_st->lock);
> > +
> > +    /* Trigger the event by sending an interrupt to the guest. */
> > +    qemu_irq_pulse(ged_st->gsi[ged_st->irq]);
> > +}
> > +
> > +static void acpi_ged_init(MemoryRegion *as, DeviceState *dev, GEDState
> *ged_st)
> > +{
> > +    AcpiGedState *s = ACPI_GED(dev);
> > +
> > +    assert(s->ged_base);
> > +
> > +    ged_st->irq = s->ged_irq;
> > +    ged_st->gsi = s->gsi;
> > +    qemu_mutex_init(&ged_st->lock);
> > +    memory_region_init_io(&ged_st->io, OBJECT(dev), &ged_ops, ged_st,
> > +                          "acpi-ged-event", ACPI_GED_REG_LEN);
> > +    memory_region_add_subregion(as, s->ged_base, &ged_st->io);
> > +}
> > +
> > +static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
> > +                                    DeviceState *dev, Error **errp)
> > +{
> > +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> > +
> > +    if (s->memhp_state.is_enabled &&
> > +        object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> > +            acpi_memory_plug_cb(hotplug_dev, &s->memhp_state,
> > +                                dev, errp);
> > +    } else {
> > +        error_setg(errp, "virt: device plug request for unsupported
> device"
> > +                   " type: %s", object_get_typename(OBJECT(dev)));
> > +    }
> > +}
> > +
> > +static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits
> ev)
> > +{
> > +    AcpiGedState *s = ACPI_GED(adev);
> > +    uint32_t sel;
> > +
> > +    if (ev & ACPI_MEMORY_HOTPLUG_STATUS) {
> > +        sel = ACPI_GED_IRQ_SEL_MEM;
> > +    } else {
> > +        /* Unknown event. Return without generating interrupt. */
> > +        return;
> > +    }
> > +
> > +    /*
> > +     * We inject the hotplug interrupt. The IRQ selector will make
> > +     * the difference from the ACPI table.
> > +     */
> > +    acpi_ged_event(&s->ged_state, sel);
> > +}
> > +
> > +static void acpi_ged_device_realize(DeviceState *dev, Error **errp)
> > +{
> > +    AcpiGedState *s = ACPI_GED(dev);
> > +
> > +    if (s->memhp_state.is_enabled) {
> > +        acpi_memory_hotplug_init(get_system_memory(), OBJECT(dev),
> > +                                 &s->memhp_state,
> > +                                 s->memhp_base);
> > +        acpi_ged_init(get_system_memory(), dev, &s->ged_state);
> > +    }
> > +}
> > +
> > +static Property acpi_ged_properties[] = {
> > +    /*
> > +     * Memory hotplug base address is a property of GED here,
> > +     * because GED handles memory hotplug event and
> MEMORY_HOTPLUG_DEVICE
> > +     * gets initialized when GED device is realized.
> > +     */
> > +    DEFINE_PROP_UINT64("memhp-base", AcpiGedState, memhp_base,
> 0),
> > +    DEFINE_PROP_BOOL("memory-hotplug-support", AcpiGedState,
> > +                     memhp_state.is_enabled, true),
> > +    DEFINE_PROP_PTR("gsi", AcpiGedState, gsi),
> > +    DEFINE_PROP_UINT64("ged-base", AcpiGedState, ged_base, 0),
> > +    DEFINE_PROP_UINT32("ged-irq", AcpiGedState, ged_irq, 0),
> > +    DEFINE_PROP_PTR("ged-events", AcpiGedState, ged_events),
> > +    DEFINE_PROP_UINT32("ged-events-size", AcpiGedState,
> ged_events_size, 0),
> > +    DEFINE_PROP_END_OF_LIST(),
> > +};
> > +
> > +static void acpi_ged_class_init(ObjectClass *class, void *data)
> > +{
> > +    DeviceClass *dc = DEVICE_CLASS(class);
> > +    HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class);
> > +    AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class);
> > +
> > +    dc->desc = "ACPI";
> > +    dc->props = acpi_ged_properties;
> > +    dc->realize = acpi_ged_device_realize;
> > +
> > +    /* Reason: pointer properties "gsi" and "ged_events" */
> > +    dc->user_creatable = false;
> > +
> > +    hc->plug = acpi_ged_device_plug_cb;
> > +
> > +    adevc->send_event = acpi_ged_send_event;
> > +}
> > +
> > +static const TypeInfo acpi_ged_info = {
> > +    .name          = TYPE_ACPI_GED,
> > +    .parent        = TYPE_DEVICE,
> > +    .instance_size = sizeof(AcpiGedState),
> > +    .class_init    = acpi_ged_class_init,
> > +    .interfaces = (InterfaceInfo[]) {
> > +        { TYPE_HOTPLUG_HANDLER },
> > +        { TYPE_ACPI_DEVICE_IF },
> > +        { }
> > +    }
> > +};
> > +
> > +static void acpi_ged_register_types(void)
> > +{
> > +    type_register_static(&acpi_ged_info);
> > +}
> > +
> > +type_init(acpi_ged_register_types)
> > diff --git a/include/hw/acpi/generic_event_device.h
> b/include/hw/acpi/generic_event_device.h
> > new file mode 100644
> > index 0000000..9c840d8
> > --- /dev/null
> > +++ b/include/hw/acpi/generic_event_device.h
> > @@ -0,0 +1,121 @@
> > +/*
> > + *
> > + * Copyright (c) 2018 Intel Corporation
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2 or later, as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope it will be useful, but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of
> MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
> License for
> > + * more details.
> > + *
> > + * You should have received a copy of the GNU General Public License along
> with
> > + * this program.  If not, see <http://www.gnu.org/licenses/>.
> > + *
> > + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
> > + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> > + * including the hotplug ones. Generic Event Device allows platforms
> > + * to handle interrupts in ACPI ASL statements. It follows a very
> > + * similar approach like the _EVT method from GPIO events. All
> > + * interrupts are listed in  _CRS and the handler is written in _EVT
> > + * method. Here, we use a single interrupt for the GED device, relying
> > + * on IO memory region to communicate the type of device affected by
> > + * the interrupt. This way, we can support up to 32 events with a
> > + * unique interrupt.
> > + *
> > + * Here is an example.
> > + *
> > + * Device (\_SB.GED)
> > + * {
> > + *     Name (_HID, "ACPI0013")
> > + *     Name (_UID, Zero)
> > + *     Name (_CRS, ResourceTemplate ()
> > + *     {
> > + *         Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, )
> > + *         {
> > + *              0x00000029,
> > + *         }
> > + *     })
> > + *     OperationRegion (IREG, SystemMemory, 0x09080000, 0x04)
> > + *     Field (IREG, DWordAcc, NoLock, WriteAsZeros)
> > + *     {
> > + *         ISEL,   32
> > + *     }
> > + *
> > + *     Method (_EVT, 1, Serialized)  // _EVT: Event
> > + *     {
> > + *         Local0 = ISEL // ISEL = IO memory region which specifies the
> > + *                       // device type.
> > + *         If (((Local0 & irq0) == irq0))
> > + *         {
> > + *             MethodEvent0()
> > + *         }
> > + *         ElseIf ((Local0 & irq1) == irq1)
> > + *         {
> > + *             MethodEvent1()
> > + *         }
> > + *         ...
> > + *     }
> > + * }
> > + *
> > + */
> > +
> > +#ifndef HW_ACPI_GED_H
> > +#define HW_ACPI_GED_H
> > +
> > +#include "hw/acpi/memory_hotplug.h"
> > +
> > +#define TYPE_ACPI_GED "acpi-ged"
> > +#define ACPI_GED(obj) \
> > +    OBJECT_CHECK(AcpiGedState, (obj), TYPE_ACPI_GED)
> > +
> > +#define ACPI_GED_IRQ_SEL_OFFSET 0x0
> > +#define ACPI_GED_IRQ_SEL_LEN    0x4
> > +#define ACPI_GED_IRQ_SEL_MEM    0x1
> > +#define ACPI_GED_REG_LEN        0x4
> > +
> > +#define GED_DEVICE      "GED"
> > +#define AML_GED_IRQ_REG "IREG"
> > +#define AML_GED_IRQ_SEL "ISEL"
> > +
> > +typedef enum {
> > +    GED_MEMORY_HOTPLUG = 1,
> > +} GedEventType;
> > +
> > +/*
> > + * Platforms need to specify their own GedEvent array
> > + * to describe what kind of events they want to support
> > + * through GED.
> > + */
> > +typedef struct GedEvent {
> > +    uint32_t     selector;
> > +    GedEventType event;
> > +} GedEvent;
> > +
> > +typedef struct GEDState {
> > +    MemoryRegion io;
> > +    uint32_t     sel;
> > +    uint32_t     irq;
> > +    qemu_irq     *gsi;
> > +    QemuMutex    lock;
> > +} GEDState;
> > +
> > +
> > +typedef struct AcpiGedState {
> > +    DeviceClass parent_obj;
> > +    MemHotplugState memhp_state;
> > +    hwaddr memhp_base;
> > +    void *gsi;
> > +    hwaddr ged_base;
> > +    GEDState ged_state;
> > +    uint32_t ged_irq;
> > +    void *ged_events;
> > +    uint32_t ged_events_size;
> > +} AcpiGedState;
> > +
> > +void build_ged_aml(Aml *table, const char* name, HotplugHandler
> *hotplug_dev,
> > +                   uint32_t ged_irq, AmlRegionSpace rs);
> > +
> > +#endif
> > --
> > 2.7.4
> >
> >

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
@ 2019-05-02  7:10         ` Auger Eric
  0 siblings, 0 replies; 62+ messages in thread
From: Auger Eric @ 2019-05-02  7:10 UTC (permalink / raw)
  To: Shameerali Kolothum Thodi, qemu-devel, qemu-arm, imammedo
  Cc: peter.maydell, shannon.zhaosl, sameo, sebastien.boeuf, xuwei (O),
	lersek, ard.biesheuvel, Linuxarm

Hi Shameer,

On 5/1/19 12:40 PM, Shameerali Kolothum Thodi wrote:
> Hi Eric,
> 
>> -----Original Message-----
>> From: Auger Eric [mailto:eric.auger@redhat.com]
>> Sent: 30 April 2019 16:50
>> To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>;
>> qemu-devel@nongnu.org; qemu-arm@nongnu.org; imammedo@redhat.com
>> Cc: peter.maydell@linaro.org; shannon.zhaosl@gmail.com;
>> sameo@linux.intel.com; sebastien.boeuf@intel.com; xuwei (O)
>> <xuwei5@huawei.com>; lersek@redhat.com; ard.biesheuvel@linaro.org;
>> Linuxarm <linuxarm@huawei.com>
>> Subject: Re: [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
>>
>> Hi Shameer,
>>
>> On 4/9/19 12:29 PM, Shameer Kolothum wrote:
>>> From: Samuel Ortiz <sameo@linux.intel.com>
>>>
>>> The ACPI Generic Event Device (GED) is a hardware-reduced specific
>>> device[ACPI v6.1 Section 5.6.9] that handles all platform events,
>>> including the hotplug ones.This patch generates the AML code that
>>> defines GEDs.
>>>
>>> Platforms need to specify their own GedEvent array to describe what
>>> kind of events they want to support through GED.  Also this uses a
>>> a single interrupt for the  GED device, relying on IO memory region
>>> to communicate the type of device affected by the interrupt. This
>>> way, we can support up to 32 events with a unique interrupt.
>>>
>>> This supports only memory hotplug for now.
>>>
>>> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
>>> Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
>>> Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
>>> ---
>>>  hw/acpi/Kconfig                        |   4 +
>>>  hw/acpi/Makefile.objs                  |   1 +
>>>  hw/acpi/generic_event_device.c         | 311
>> +++++++++++++++++++++++++++++++++
>>>  include/hw/acpi/generic_event_device.h | 121 +++++++++++++
>>>  4 files changed, 437 insertions(+)
>>>  create mode 100644 hw/acpi/generic_event_device.c
>>>  create mode 100644 include/hw/acpi/generic_event_device.h
>>>
>>> diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig
>>> index eca3bee..01a8b41 100644
>>> --- a/hw/acpi/Kconfig
>>> +++ b/hw/acpi/Kconfig
>>> @@ -27,3 +27,7 @@ config ACPI_VMGENID
>>>      bool
>>>      default y
>>>      depends on PC
>>> +
>>> +config ACPI_HW_REDUCED
>>> +    bool
>>> +    depends on ACPI
>>> diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
>>> index 2d46e37..b753232 100644
>>> --- a/hw/acpi/Makefile.objs
>>> +++ b/hw/acpi/Makefile.objs
>>> @@ -6,6 +6,7 @@ common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) +=
>> memory_hotplug.o
>>>  common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu.o
>>>  common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
>>>  common-obj-$(CONFIG_ACPI_VMGENID) += vmgenid.o
>>> +common-obj-$(CONFIG_ACPI_HW_REDUCED) += generic_event_device.o
>>>  common-obj-$(call lnot,$(CONFIG_ACPI_X86)) += acpi-stub.o
>>>
>>>  common-obj-y += acpi_interface.o
>>> diff --git a/hw/acpi/generic_event_device.c
>> b/hw/acpi/generic_event_device.c
>>> new file mode 100644
>>> index 0000000..856ca04
>>> --- /dev/null
>>> +++ b/hw/acpi/generic_event_device.c
>>> @@ -0,0 +1,311 @@
>>> +/*
>>> + *
>>> + * Copyright (c) 2018 Intel Corporation
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms and conditions of the GNU General Public License,
>>> + * version 2 or later, as published by the Free Software Foundation.
>> I am not sure we need below statements: see hw/misc/armsse-mhu.c for a
>> recent added file.
> 
> Ok. I will get rid of this.
>  
>>> + *
>>> + * This program is distributed in the hope it will be useful, but WITHOUT
>>> + * ANY WARRANTY; without even the implied warranty of
>> MERCHANTABILITY or
>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
>> License for
>>> + * more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License along
>> with
>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>> + */
>>> +
>>> +#include "qemu/osdep.h"
>>> +#include "qapi/error.h"
>>> +#include "exec/address-spaces.h"
>>> +#include "hw/sysbus.h"
>>> +#include "hw/acpi/acpi.h"
>>> +#include "hw/acpi/generic_event_device.h"
>>> +#include "hw/mem/pc-dimm.h"
>>> +
>>> +static Aml *ged_event_aml(const GedEvent *event)
>>> +{
>>> +
>>> +    if (!event) {
>>> +        return NULL;
>>> +    }
>>> +
>>> +    switch (event->event) {
>>> +    case GED_MEMORY_HOTPLUG:
>>> +        /* We run a complete memory SCAN when getting a memory
>> hotplug event */
>>> +        return aml_call0(MEMORY_DEVICES_CONTAINER "."
>> MEMORY_SLOT_SCAN_METHOD);
>>> +    default:
>>> +        break;
>>> +    }
>>> +
>>> +    return NULL;
>>> +}
>>> +
>>> +/*
>>> + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
>>> + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
>>> + * including the hotplug ones. Platforms need to specify their own
>>> + * GedEvent array to describe what kind of events they want to support
>>> + * through GED. This routine uses a single interrupt for the GED device,
>>> + * relying on IO memory region to communicate the type of device
>>> + * affected by the interrupt. This way, we can support up to 32 events
>>> + * with a unique interrupt.
>>> + */
>>> +void build_ged_aml(Aml *table, const char *name, HotplugHandler
>> *hotplug_dev,
>>> +                   uint32_t ged_irq, AmlRegionSpace rs)
>>> +{
>>> +    AcpiGedState *s = ACPI_GED(hotplug_dev);
>>> +    GedEvent *ged_events = s->ged_events;
>>> +    Aml *crs = aml_resource_template();
>>> +    Aml *evt, *field;
>>> +    Aml *dev = aml_device("%s", name);
>>> +    Aml *irq_sel = aml_local(0);
>>> +    Aml *isel = aml_name(AML_GED_IRQ_SEL);
>>> +    uint32_t i;
>>> +
>>> +    if (!s->ged_base || !ged_events || !s->ged_events_size) {
>>> +        return;
>>> +    }
>>> +
>>> +    /* _CRS interrupt */
>>> +    aml_append(crs, aml_interrupt(AML_CONSUMER, AML_EDGE,
>> AML_ACTIVE_HIGH,
>>> +                                  AML_EXCLUSIVE, &ged_irq, 1));
>>> +    /*
>>> +     * For each GED event we:
>>> +     * - Add an interrupt to the CRS section.
>>> +     * - Add a conditional block for each event, inside a while loop.
>>> +     *   This is semantically equivalent to a switch/case implementation.
>>> +     */
>>> +    evt = aml_method("_EVT", 1, AML_SERIALIZED);
>>> +    {
>>> +        Aml *ged_aml;
>>> +        Aml *if_ctx;
>>> +
>>> +        /* Local0 = ISEL */
>>> +        aml_append(evt, aml_store(isel, irq_sel));
>>> +
>>> +        /*
>>> +         * Here we want to call a method for each supported GED event
>> type.
>>> +         * The resulting ASL code looks like:
>>> +         *
>>> +         * Local0 = ISEL
>>> +         * If ((Local0 & irq0) == irq0)
>>> +         * {
>>> +         *     MethodEvent0()
>>> +         * }
>>> +         *
>>> +         * If ((Local0 & irq1) == irq1)
>>> +         * {
>>> +         *     MethodEvent1()
>>> +         * }
>>> +         * ...
>>> +         */
>>> +
>>> +        for (i = 0; i < s->ged_events_size; i++) {
>>> +            ged_aml = ged_event_aml(&ged_events[i]);
>>> +            if (!ged_aml) {
>>> +                continue;
>>> +            }
>>> +
>>> +            /* If ((Local1 == irq))*/
>>> +            if_ctx = aml_if(aml_equal(aml_and(irq_sel,
>>> +
>> aml_int(ged_events[i].selector), NULL),
>>> +
>> aml_int(ged_events[i].selector)));
>>> +            {
>>> +                /* AML for this specific type of event */
>>> +                aml_append(if_ctx, ged_aml);
>>> +            }
>>> +
>>> +            /*
>>> +             * We append the first "if" to the "while" context.
>>> +             * Other "if"s will be "elseif"s.
>> Are we sure about that:
>> in hw/acpi/nvdimm.c nvdimm_build_common_dsm() I can see an aml_else() is
>> added inbetween aml_if's?
> 
> Right. I think the comment here is misleading. This will be as mentioned in the
> comment block above,
> 
>  If ((Local0 & irq0) == irq0)
>  {
>      MethodEvent0()
>  }
> 
>  If ((Local0 & irq1) == irq1)
>  {
>      MethodEvent1()
>  }
> 
>  And this should do the job. Do we really need ElseIf block here?
I don't think so, as long as handling several set bits is allowed. So
let's remove the comment then.

Thanks

Eric
> 
>>> +             */
>>> +            aml_append(evt, if_ctx);
>>> +        }
>>> +    }
>>> +
>>> +    aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0013")));
>>> +    aml_append(dev, aml_name_decl("_UID", aml_string(GED_DEVICE)));
>>> +    aml_append(dev, aml_name_decl("_CRS", crs));
>>> +
>>> +    /* Append IO region */
>>> +    aml_append(dev, aml_operation_region(AML_GED_IRQ_REG, rs,
>>> +               aml_int(s->ged_base + ACPI_GED_IRQ_SEL_OFFSET),
>>> +               ACPI_GED_IRQ_SEL_LEN));
>>> +    field = aml_field(AML_GED_IRQ_REG, AML_DWORD_ACC,
>> AML_NOLOCK,
>>> +                      AML_WRITE_AS_ZEROS);
>>> +    aml_append(field, aml_named_field(AML_GED_IRQ_SEL,
>>> +                                      ACPI_GED_IRQ_SEL_LEN *
>> 8));
>> nit s/8/BITS_PER_BYTE as done in nvdimm.c.
> 
> Ok.
> 
>>> +    aml_append(dev, field);
>>> +
>>> +    /* Append _EVT method */
>>> +    aml_append(dev, evt);
>>> +
>>> +    aml_append(table, dev);
>>> +}
>>> +
>>> +/* Memory read by the GED _EVT AML dynamic method */
>>> +static uint64_t ged_read(void *opaque, hwaddr addr, unsigned size)
>>> +{
>>> +    uint64_t val = 0;
>>> +    GEDState *ged_st = opaque;
>>> +
>>> +    switch (addr) {
>>> +    case ACPI_GED_IRQ_SEL_OFFSET:
>>> +        /* Read the selector value and reset it */
>>> +        qemu_mutex_lock(&ged_st->lock);
>>> +        val = ged_st->sel;
>>> +        ged_st->sel = 0;
>>> +        qemu_mutex_unlock(&ged_st->lock);
>>> +        break;
>>> +    default:
>>> +        break;
>>> +    }
>>> +
>>> +    return val;
>>> +}
>>> +
>>> +/* Nothing is expected to be written to the GED memory region */
>>> +static void ged_write(void *opaque, hwaddr addr, uint64_t data,
>>> +                      unsigned int size)
>>> +{
>>> +}
>>> +
>>> +static const MemoryRegionOps ged_ops = {
>>> +    .read = ged_read,
>>> +    .write = ged_write,
>>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>>> +    .valid = {
>>> +        .min_access_size = 4,
>>> +        .max_access_size = 4,
>>> +    },
>>> +};
>>> +
>>> +static void acpi_ged_event(GEDState *ged_st, uint32_t ged_irq_sel)
>>> +{
>>> +    /*
>> I would personally inline that code in void acpi_ged_send_event()
> 
> Ok.
> 
>>> +     * Set the GED IRQ selector to the expected device type value. This
>>> +     * way, the ACPI method will be able to trigger the right code based
>>> +     * on a unique IRQ.
>>> +     */
>>> +    qemu_mutex_lock(&ged_st->lock);
>>> +    ged_st->sel = ged_irq_sel;
>>> +    qemu_mutex_unlock(&ged_st->lock);
>>> +
>>> +    /* Trigger the event by sending an interrupt to the guest. */
>>> +    qemu_irq_pulse(ged_st->gsi[ged_st->irq]);
>> I don't really understand why we need this array. Doesn't The GED use a
>> single IRQ. So why can't you store the precise qemu_irq pointer directly
>> through the propery (DEFINE_PROP_PTR("gsi", AcpiGedState, gsi))?
> 
> Yes. You have raised this point previously as well. Sorry, I missed it when I
> reworked the GED type from SYS_BUS_DEVICE to TYPE_DEVICE. I think
> we can get rid of this array and the GED/gsi routing done in virt.c.
> 
> 
>>> +}
>>> +
>>> +static void acpi_ged_init(MemoryRegion *as, DeviceState *dev, GEDState
>> *ged_st)
>>> +{
>>> +    AcpiGedState *s = ACPI_GED(dev);
>>> +
>>> +    assert(s->ged_base);
>>> +
>>> +    ged_st->irq = s->ged_irq;
>>> +    ged_st->gsi = s->gsi;
>>> +    qemu_mutex_init(&ged_st->lock);
>>> +    memory_region_init_io(&ged_st->io, OBJECT(dev), &ged_ops, ged_st,
>>> +                          "acpi-ged-event", ACPI_GED_REG_LEN);
>>> +    memory_region_add_subregion(as, s->ged_base, &ged_st->io);
>>> +}
>>> +
>>> +static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
>>> +                                    DeviceState *dev, Error **errp)
>>> +{
>>> +    AcpiGedState *s = ACPI_GED(hotplug_dev);
>>> +
>>> +    if (s->memhp_state.is_enabled &&
>>> +        object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
>>> +            acpi_memory_plug_cb(hotplug_dev, &s->memhp_state,
>>> +                                dev, errp);
>>> +    } else {
>>> +        error_setg(errp, "virt: device plug request for unsupported
>> device"
>>> +                   " type: %s", object_get_typename(OBJECT(dev)));
>>> +    }
>>> +}
>>> +
>>> +static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits
>> ev)
>>> +{
>>> +    AcpiGedState *s = ACPI_GED(adev);
>>> +    uint32_t sel;
>>> +
>>> +    if (ev & ACPI_MEMORY_HOTPLUG_STATUS) {
>>> +        sel = ACPI_GED_IRQ_SEL_MEM;
>>> +    } else {
>>> +        /* Unknown event. Return without generating interrupt. */
>>> +        return;
>>> +    }
>>> +
>>> +    /*
>>> +     * We inject the hotplug interrupt. The IRQ selector will make
>>> +     * the difference from the ACPI table.
>>> +     */
>>> +    acpi_ged_event(&s->ged_state, sel);
>>> +}
>>> +
>>> +static void acpi_ged_device_realize(DeviceState *dev, Error **errp)
>>> +{
>>> +    AcpiGedState *s = ACPI_GED(dev);
>>> +
>>> +    if (s->memhp_state.is_enabled) {
>>> +        acpi_memory_hotplug_init(get_system_memory(), OBJECT(dev),
>>> +                                 &s->memhp_state,
>>> +                                 s->memhp_base);
>>> +        acpi_ged_init(get_system_memory(), dev, &s->ged_state);
>>> +    }
>>> +}
>>> +
>>> +static Property acpi_ged_properties[] = {
>>> +    /*
>>> +     * Memory hotplug base address is a property of GED here,
>>> +     * because GED handles memory hotplug event and
>> MEMORY_HOTPLUG_DEVICE
>>> +     * gets initialized when GED device is realized.
>>> +     */
>>> +    DEFINE_PROP_UINT64("memhp-base", AcpiGedState, memhp_base,
>> 0),
>>> +    DEFINE_PROP_BOOL("memory-hotplug-support", AcpiGedState,
>>> +                     memhp_state.is_enabled, true),
>>> +    DEFINE_PROP_PTR("gsi", AcpiGedState, gsi),
>>> +    DEFINE_PROP_UINT64("ged-base", AcpiGedState, ged_base, 0),
>>> +    DEFINE_PROP_UINT32("ged-irq", AcpiGedState, ged_irq, 0),
>>> +    DEFINE_PROP_PTR("ged-events", AcpiGedState, ged_events),
>>> +    DEFINE_PROP_UINT32("ged-events-size", AcpiGedState,
>> ged_events_size, 0),
>>> +    DEFINE_PROP_END_OF_LIST(),
>>> +};
>>> +
>>> +static void acpi_ged_class_init(ObjectClass *class, void *data)
>>> +{
>>> +    DeviceClass *dc = DEVICE_CLASS(class);
>>> +    HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class);
>>> +    AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class);
>>> +
>>> +    dc->desc = "ACPI";
>>> +    dc->props = acpi_ged_properties;
>>> +    dc->realize = acpi_ged_device_realize;
>>> +
>>> +    /* Reason: pointer properties "gsi" and "ged_events" */
>>> +    dc->user_creatable = false;
>>> +
>>> +    hc->plug = acpi_ged_device_plug_cb;
>>> +
>>> +    adevc->send_event = acpi_ged_send_event;
>>> +}
>>> +
>>> +static const TypeInfo acpi_ged_info = {
>>> +    .name          = TYPE_ACPI_GED,
>>> +    .parent        = TYPE_DEVICE,
>>> +    .instance_size = sizeof(AcpiGedState),
>>> +    .class_init    = acpi_ged_class_init,
>>> +    .interfaces = (InterfaceInfo[]) {
>>> +        { TYPE_HOTPLUG_HANDLER },
>>> +        { TYPE_ACPI_DEVICE_IF },
>>> +        { }
>>> +    }
>>> +};
>>> +
>>> +static void acpi_ged_register_types(void)
>>> +{
>>> +    type_register_static(&acpi_ged_info);
>>> +}
>>> +
>>> +type_init(acpi_ged_register_types)
>>> diff --git a/include/hw/acpi/generic_event_device.h
>> b/include/hw/acpi/generic_event_device.h
>>> new file mode 100644
>>> index 0000000..9c840d8
>>> --- /dev/null
>>> +++ b/include/hw/acpi/generic_event_device.h
>>> @@ -0,0 +1,121 @@
>>> +/*
>>> + *
>>> + * Copyright (c) 2018 Intel Corporation
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms and conditions of the GNU General Public License,
>>> + * version 2 or later, as published by the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope it will be useful, but WITHOUT
>>> + * ANY WARRANTY; without even the implied warranty of
>> MERCHANTABILITY or
>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
>> License for
>>> + * more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License along
>> with
>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>> + *
>>> + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
>>> + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
>>> + * including the hotplug ones. Generic Event Device allows platforms
>>> + * to handle interrupts in ACPI ASL statements. It follows a very
>>> + * similar approach like the _EVT method from GPIO events. All
>>> + * interrupts are listed in  _CRS and the handler is written in _EVT> + *
>> method. Here, we use a single interrupt for the GED device, relying
>>> + * on IO memory region to communicate the type of device affected by
>>> + * the interrupt. This way, we can support up to 32 events with a
>>> + * unique interrupt.
>>> + *
>>> + * Here is an example.
>>> + *
>>> + * Device (\_SB.GED)
>>> + * {
>>> + *     Name (_HID, "ACPI0013")
>>> + *     Name (_UID, Zero)
>>> + *     Name (_CRS, ResourceTemplate ()
>>> + *     {
>>> + *         Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, )
>>> + *         {
>>> + *              0x00000029,
>>> + *         }
>>> + *     })
>>> + *     OperationRegion (IREG, SystemMemory, 0x09080000, 0x04)
>>> + *     Field (IREG, DWordAcc, NoLock, WriteAsZeros)
>>> + *     {
>>> + *         ISEL,   32
>>> + *     }
>>> + *
>>> + *     Method (_EVT, 1, Serialized)  // _EVT: Event
>>> + *     {
>>> + *         Local0 = ISEL // ISEL = IO memory region which specifies the
>>> + *                       // device type.
>>> + *         If (((Local0 & irq0) == irq0))
>>> + *         {
>>> + *             MethodEvent0()
>>> + *         }
>>> + *         ElseIf ((Local0 & irq1) == irq1)
>>> + *         {
>>> + *             MethodEvent1()
>>> + *         }
>>> + *         ...
>>> + *     }
>>> + * }
>>> + *
>>> + */
>>> +
>>> +#ifndef HW_ACPI_GED_H
>>> +#define HW_ACPI_GED_H
>>> +
>>> +#include "hw/acpi/memory_hotplug.h"
>>> +
>>> +#define TYPE_ACPI_GED "acpi-ged"
>>> +#define ACPI_GED(obj) \
>>> +    OBJECT_CHECK(AcpiGedState, (obj), TYPE_ACPI_GED)
>>> +
>>> +#define ACPI_GED_IRQ_SEL_OFFSET 0x0
>>> +#define ACPI_GED_IRQ_SEL_LEN    0x4
>>> +#define ACPI_GED_IRQ_SEL_MEM    0x1
>>> +#define ACPI_GED_REG_LEN        0x4
>>> +
>>> +#define GED_DEVICE      "GED"
>>> +#define AML_GED_IRQ_REG "IREG"
>>> +#define AML_GED_IRQ_SEL "ISEL"
>>> +
>>> +typedef enum {
>>> +    GED_MEMORY_HOTPLUG = 1,
>>> +} GedEventType;
>>> +
>>> +/*
>>> + * Platforms need to specify their own GedEvent array
>>> + * to describe what kind of events they want to support
>>> + * through GED.
>>> + */
>>> +typedef struct GedEvent {
>>> +    uint32_t     selector;
>>> +    GedEventType event;
>>> +} GedEvent;
>>> +
>>> +typedef struct GEDState {
>>> +    MemoryRegion io;
>>> +    uint32_t     sel;
>>> +    uint32_t     irq;
>>> +    qemu_irq     *gsi;
>> so I am not sure why with need gsi + irq.
>>> +    QemuMutex    lock;
>>> +} GEDState;
>>> +
>>> +
>>> +typedef struct AcpiGedState {
>>> +    DeviceClass parent_obj;
>>> +    MemHotplugState memhp_state;
>>> +    hwaddr memhp_base;
>>> +    void *gsi;
>>> +    hwaddr ged_base;
>>> +    GEDState ged_state;
>>> +    uint32_t ged_irq;
>>> +    void *ged_events;
>>> +    uint32_t ged_events_size;
>>> +} AcpiGedState;
>>> +
>>> +void build_ged_aml(Aml *table, const char* name, HotplugHandler
>> *hotplug_dev,
>>> +                   uint32_t ged_irq, AmlRegionSpace rs);
>>> +
>>> +#endif
>>>
>> Besides, this looks good to me.
>>
> 
> Thanks,
> Shameer
> 

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
@ 2019-05-02  7:10         ` Auger Eric
  0 siblings, 0 replies; 62+ messages in thread
From: Auger Eric @ 2019-05-02  7:10 UTC (permalink / raw)
  To: Shameerali Kolothum Thodi, qemu-devel, qemu-arm, imammedo
  Cc: peter.maydell, sameo, ard.biesheuvel, Linuxarm, shannon.zhaosl,
	sebastien.boeuf, xuwei (O),
	lersek

Hi Shameer,

On 5/1/19 12:40 PM, Shameerali Kolothum Thodi wrote:
> Hi Eric,
> 
>> -----Original Message-----
>> From: Auger Eric [mailto:eric.auger@redhat.com]
>> Sent: 30 April 2019 16:50
>> To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>;
>> qemu-devel@nongnu.org; qemu-arm@nongnu.org; imammedo@redhat.com
>> Cc: peter.maydell@linaro.org; shannon.zhaosl@gmail.com;
>> sameo@linux.intel.com; sebastien.boeuf@intel.com; xuwei (O)
>> <xuwei5@huawei.com>; lersek@redhat.com; ard.biesheuvel@linaro.org;
>> Linuxarm <linuxarm@huawei.com>
>> Subject: Re: [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
>>
>> Hi Shameer,
>>
>> On 4/9/19 12:29 PM, Shameer Kolothum wrote:
>>> From: Samuel Ortiz <sameo@linux.intel.com>
>>>
>>> The ACPI Generic Event Device (GED) is a hardware-reduced specific
>>> device[ACPI v6.1 Section 5.6.9] that handles all platform events,
>>> including the hotplug ones.This patch generates the AML code that
>>> defines GEDs.
>>>
>>> Platforms need to specify their own GedEvent array to describe what
>>> kind of events they want to support through GED.  Also this uses a
>>> a single interrupt for the  GED device, relying on IO memory region
>>> to communicate the type of device affected by the interrupt. This
>>> way, we can support up to 32 events with a unique interrupt.
>>>
>>> This supports only memory hotplug for now.
>>>
>>> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
>>> Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
>>> Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
>>> ---
>>>  hw/acpi/Kconfig                        |   4 +
>>>  hw/acpi/Makefile.objs                  |   1 +
>>>  hw/acpi/generic_event_device.c         | 311
>> +++++++++++++++++++++++++++++++++
>>>  include/hw/acpi/generic_event_device.h | 121 +++++++++++++
>>>  4 files changed, 437 insertions(+)
>>>  create mode 100644 hw/acpi/generic_event_device.c
>>>  create mode 100644 include/hw/acpi/generic_event_device.h
>>>
>>> diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig
>>> index eca3bee..01a8b41 100644
>>> --- a/hw/acpi/Kconfig
>>> +++ b/hw/acpi/Kconfig
>>> @@ -27,3 +27,7 @@ config ACPI_VMGENID
>>>      bool
>>>      default y
>>>      depends on PC
>>> +
>>> +config ACPI_HW_REDUCED
>>> +    bool
>>> +    depends on ACPI
>>> diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
>>> index 2d46e37..b753232 100644
>>> --- a/hw/acpi/Makefile.objs
>>> +++ b/hw/acpi/Makefile.objs
>>> @@ -6,6 +6,7 @@ common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) +=
>> memory_hotplug.o
>>>  common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu.o
>>>  common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
>>>  common-obj-$(CONFIG_ACPI_VMGENID) += vmgenid.o
>>> +common-obj-$(CONFIG_ACPI_HW_REDUCED) += generic_event_device.o
>>>  common-obj-$(call lnot,$(CONFIG_ACPI_X86)) += acpi-stub.o
>>>
>>>  common-obj-y += acpi_interface.o
>>> diff --git a/hw/acpi/generic_event_device.c
>> b/hw/acpi/generic_event_device.c
>>> new file mode 100644
>>> index 0000000..856ca04
>>> --- /dev/null
>>> +++ b/hw/acpi/generic_event_device.c
>>> @@ -0,0 +1,311 @@
>>> +/*
>>> + *
>>> + * Copyright (c) 2018 Intel Corporation
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms and conditions of the GNU General Public License,
>>> + * version 2 or later, as published by the Free Software Foundation.
>> I am not sure we need below statements: see hw/misc/armsse-mhu.c for a
>> recent added file.
> 
> Ok. I will get rid of this.
>  
>>> + *
>>> + * This program is distributed in the hope it will be useful, but WITHOUT
>>> + * ANY WARRANTY; without even the implied warranty of
>> MERCHANTABILITY or
>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
>> License for
>>> + * more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License along
>> with
>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>> + */
>>> +
>>> +#include "qemu/osdep.h"
>>> +#include "qapi/error.h"
>>> +#include "exec/address-spaces.h"
>>> +#include "hw/sysbus.h"
>>> +#include "hw/acpi/acpi.h"
>>> +#include "hw/acpi/generic_event_device.h"
>>> +#include "hw/mem/pc-dimm.h"
>>> +
>>> +static Aml *ged_event_aml(const GedEvent *event)
>>> +{
>>> +
>>> +    if (!event) {
>>> +        return NULL;
>>> +    }
>>> +
>>> +    switch (event->event) {
>>> +    case GED_MEMORY_HOTPLUG:
>>> +        /* We run a complete memory SCAN when getting a memory
>> hotplug event */
>>> +        return aml_call0(MEMORY_DEVICES_CONTAINER "."
>> MEMORY_SLOT_SCAN_METHOD);
>>> +    default:
>>> +        break;
>>> +    }
>>> +
>>> +    return NULL;
>>> +}
>>> +
>>> +/*
>>> + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
>>> + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
>>> + * including the hotplug ones. Platforms need to specify their own
>>> + * GedEvent array to describe what kind of events they want to support
>>> + * through GED. This routine uses a single interrupt for the GED device,
>>> + * relying on IO memory region to communicate the type of device
>>> + * affected by the interrupt. This way, we can support up to 32 events
>>> + * with a unique interrupt.
>>> + */
>>> +void build_ged_aml(Aml *table, const char *name, HotplugHandler
>> *hotplug_dev,
>>> +                   uint32_t ged_irq, AmlRegionSpace rs)
>>> +{
>>> +    AcpiGedState *s = ACPI_GED(hotplug_dev);
>>> +    GedEvent *ged_events = s->ged_events;
>>> +    Aml *crs = aml_resource_template();
>>> +    Aml *evt, *field;
>>> +    Aml *dev = aml_device("%s", name);
>>> +    Aml *irq_sel = aml_local(0);
>>> +    Aml *isel = aml_name(AML_GED_IRQ_SEL);
>>> +    uint32_t i;
>>> +
>>> +    if (!s->ged_base || !ged_events || !s->ged_events_size) {
>>> +        return;
>>> +    }
>>> +
>>> +    /* _CRS interrupt */
>>> +    aml_append(crs, aml_interrupt(AML_CONSUMER, AML_EDGE,
>> AML_ACTIVE_HIGH,
>>> +                                  AML_EXCLUSIVE, &ged_irq, 1));
>>> +    /*
>>> +     * For each GED event we:
>>> +     * - Add an interrupt to the CRS section.
>>> +     * - Add a conditional block for each event, inside a while loop.
>>> +     *   This is semantically equivalent to a switch/case implementation.
>>> +     */
>>> +    evt = aml_method("_EVT", 1, AML_SERIALIZED);
>>> +    {
>>> +        Aml *ged_aml;
>>> +        Aml *if_ctx;
>>> +
>>> +        /* Local0 = ISEL */
>>> +        aml_append(evt, aml_store(isel, irq_sel));
>>> +
>>> +        /*
>>> +         * Here we want to call a method for each supported GED event
>> type.
>>> +         * The resulting ASL code looks like:
>>> +         *
>>> +         * Local0 = ISEL
>>> +         * If ((Local0 & irq0) == irq0)
>>> +         * {
>>> +         *     MethodEvent0()
>>> +         * }
>>> +         *
>>> +         * If ((Local0 & irq1) == irq1)
>>> +         * {
>>> +         *     MethodEvent1()
>>> +         * }
>>> +         * ...
>>> +         */
>>> +
>>> +        for (i = 0; i < s->ged_events_size; i++) {
>>> +            ged_aml = ged_event_aml(&ged_events[i]);
>>> +            if (!ged_aml) {
>>> +                continue;
>>> +            }
>>> +
>>> +            /* If ((Local1 == irq))*/
>>> +            if_ctx = aml_if(aml_equal(aml_and(irq_sel,
>>> +
>> aml_int(ged_events[i].selector), NULL),
>>> +
>> aml_int(ged_events[i].selector)));
>>> +            {
>>> +                /* AML for this specific type of event */
>>> +                aml_append(if_ctx, ged_aml);
>>> +            }
>>> +
>>> +            /*
>>> +             * We append the first "if" to the "while" context.
>>> +             * Other "if"s will be "elseif"s.
>> Are we sure about that:
>> in hw/acpi/nvdimm.c nvdimm_build_common_dsm() I can see an aml_else() is
>> added inbetween aml_if's?
> 
> Right. I think the comment here is misleading. This will be as mentioned in the
> comment block above,
> 
>  If ((Local0 & irq0) == irq0)
>  {
>      MethodEvent0()
>  }
> 
>  If ((Local0 & irq1) == irq1)
>  {
>      MethodEvent1()
>  }
> 
>  And this should do the job. Do we really need ElseIf block here?
I don't think so, as long as handling several set bits is allowed. So
let's remove the comment then.

Thanks

Eric
> 
>>> +             */
>>> +            aml_append(evt, if_ctx);
>>> +        }
>>> +    }
>>> +
>>> +    aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0013")));
>>> +    aml_append(dev, aml_name_decl("_UID", aml_string(GED_DEVICE)));
>>> +    aml_append(dev, aml_name_decl("_CRS", crs));
>>> +
>>> +    /* Append IO region */
>>> +    aml_append(dev, aml_operation_region(AML_GED_IRQ_REG, rs,
>>> +               aml_int(s->ged_base + ACPI_GED_IRQ_SEL_OFFSET),
>>> +               ACPI_GED_IRQ_SEL_LEN));
>>> +    field = aml_field(AML_GED_IRQ_REG, AML_DWORD_ACC,
>> AML_NOLOCK,
>>> +                      AML_WRITE_AS_ZEROS);
>>> +    aml_append(field, aml_named_field(AML_GED_IRQ_SEL,
>>> +                                      ACPI_GED_IRQ_SEL_LEN *
>> 8));
>> nit s/8/BITS_PER_BYTE as done in nvdimm.c.
> 
> Ok.
> 
>>> +    aml_append(dev, field);
>>> +
>>> +    /* Append _EVT method */
>>> +    aml_append(dev, evt);
>>> +
>>> +    aml_append(table, dev);
>>> +}
>>> +
>>> +/* Memory read by the GED _EVT AML dynamic method */
>>> +static uint64_t ged_read(void *opaque, hwaddr addr, unsigned size)
>>> +{
>>> +    uint64_t val = 0;
>>> +    GEDState *ged_st = opaque;
>>> +
>>> +    switch (addr) {
>>> +    case ACPI_GED_IRQ_SEL_OFFSET:
>>> +        /* Read the selector value and reset it */
>>> +        qemu_mutex_lock(&ged_st->lock);
>>> +        val = ged_st->sel;
>>> +        ged_st->sel = 0;
>>> +        qemu_mutex_unlock(&ged_st->lock);
>>> +        break;
>>> +    default:
>>> +        break;
>>> +    }
>>> +
>>> +    return val;
>>> +}
>>> +
>>> +/* Nothing is expected to be written to the GED memory region */
>>> +static void ged_write(void *opaque, hwaddr addr, uint64_t data,
>>> +                      unsigned int size)
>>> +{
>>> +}
>>> +
>>> +static const MemoryRegionOps ged_ops = {
>>> +    .read = ged_read,
>>> +    .write = ged_write,
>>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>>> +    .valid = {
>>> +        .min_access_size = 4,
>>> +        .max_access_size = 4,
>>> +    },
>>> +};
>>> +
>>> +static void acpi_ged_event(GEDState *ged_st, uint32_t ged_irq_sel)
>>> +{
>>> +    /*
>> I would personally inline that code in void acpi_ged_send_event()
> 
> Ok.
> 
>>> +     * Set the GED IRQ selector to the expected device type value. This
>>> +     * way, the ACPI method will be able to trigger the right code based
>>> +     * on a unique IRQ.
>>> +     */
>>> +    qemu_mutex_lock(&ged_st->lock);
>>> +    ged_st->sel = ged_irq_sel;
>>> +    qemu_mutex_unlock(&ged_st->lock);
>>> +
>>> +    /* Trigger the event by sending an interrupt to the guest. */
>>> +    qemu_irq_pulse(ged_st->gsi[ged_st->irq]);
>> I don't really understand why we need this array. Doesn't The GED use a
>> single IRQ. So why can't you store the precise qemu_irq pointer directly
>> through the propery (DEFINE_PROP_PTR("gsi", AcpiGedState, gsi))?
> 
> Yes. You have raised this point previously as well. Sorry, I missed it when I
> reworked the GED type from SYS_BUS_DEVICE to TYPE_DEVICE. I think
> we can get rid of this array and the GED/gsi routing done in virt.c.
> 
> 
>>> +}
>>> +
>>> +static void acpi_ged_init(MemoryRegion *as, DeviceState *dev, GEDState
>> *ged_st)
>>> +{
>>> +    AcpiGedState *s = ACPI_GED(dev);
>>> +
>>> +    assert(s->ged_base);
>>> +
>>> +    ged_st->irq = s->ged_irq;
>>> +    ged_st->gsi = s->gsi;
>>> +    qemu_mutex_init(&ged_st->lock);
>>> +    memory_region_init_io(&ged_st->io, OBJECT(dev), &ged_ops, ged_st,
>>> +                          "acpi-ged-event", ACPI_GED_REG_LEN);
>>> +    memory_region_add_subregion(as, s->ged_base, &ged_st->io);
>>> +}
>>> +
>>> +static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
>>> +                                    DeviceState *dev, Error **errp)
>>> +{
>>> +    AcpiGedState *s = ACPI_GED(hotplug_dev);
>>> +
>>> +    if (s->memhp_state.is_enabled &&
>>> +        object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
>>> +            acpi_memory_plug_cb(hotplug_dev, &s->memhp_state,
>>> +                                dev, errp);
>>> +    } else {
>>> +        error_setg(errp, "virt: device plug request for unsupported
>> device"
>>> +                   " type: %s", object_get_typename(OBJECT(dev)));
>>> +    }
>>> +}
>>> +
>>> +static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits
>> ev)
>>> +{
>>> +    AcpiGedState *s = ACPI_GED(adev);
>>> +    uint32_t sel;
>>> +
>>> +    if (ev & ACPI_MEMORY_HOTPLUG_STATUS) {
>>> +        sel = ACPI_GED_IRQ_SEL_MEM;
>>> +    } else {
>>> +        /* Unknown event. Return without generating interrupt. */
>>> +        return;
>>> +    }
>>> +
>>> +    /*
>>> +     * We inject the hotplug interrupt. The IRQ selector will make
>>> +     * the difference from the ACPI table.
>>> +     */
>>> +    acpi_ged_event(&s->ged_state, sel);
>>> +}
>>> +
>>> +static void acpi_ged_device_realize(DeviceState *dev, Error **errp)
>>> +{
>>> +    AcpiGedState *s = ACPI_GED(dev);
>>> +
>>> +    if (s->memhp_state.is_enabled) {
>>> +        acpi_memory_hotplug_init(get_system_memory(), OBJECT(dev),
>>> +                                 &s->memhp_state,
>>> +                                 s->memhp_base);
>>> +        acpi_ged_init(get_system_memory(), dev, &s->ged_state);
>>> +    }
>>> +}
>>> +
>>> +static Property acpi_ged_properties[] = {
>>> +    /*
>>> +     * Memory hotplug base address is a property of GED here,
>>> +     * because GED handles memory hotplug event and
>> MEMORY_HOTPLUG_DEVICE
>>> +     * gets initialized when GED device is realized.
>>> +     */
>>> +    DEFINE_PROP_UINT64("memhp-base", AcpiGedState, memhp_base,
>> 0),
>>> +    DEFINE_PROP_BOOL("memory-hotplug-support", AcpiGedState,
>>> +                     memhp_state.is_enabled, true),
>>> +    DEFINE_PROP_PTR("gsi", AcpiGedState, gsi),
>>> +    DEFINE_PROP_UINT64("ged-base", AcpiGedState, ged_base, 0),
>>> +    DEFINE_PROP_UINT32("ged-irq", AcpiGedState, ged_irq, 0),
>>> +    DEFINE_PROP_PTR("ged-events", AcpiGedState, ged_events),
>>> +    DEFINE_PROP_UINT32("ged-events-size", AcpiGedState,
>> ged_events_size, 0),
>>> +    DEFINE_PROP_END_OF_LIST(),
>>> +};
>>> +
>>> +static void acpi_ged_class_init(ObjectClass *class, void *data)
>>> +{
>>> +    DeviceClass *dc = DEVICE_CLASS(class);
>>> +    HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class);
>>> +    AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class);
>>> +
>>> +    dc->desc = "ACPI";
>>> +    dc->props = acpi_ged_properties;
>>> +    dc->realize = acpi_ged_device_realize;
>>> +
>>> +    /* Reason: pointer properties "gsi" and "ged_events" */
>>> +    dc->user_creatable = false;
>>> +
>>> +    hc->plug = acpi_ged_device_plug_cb;
>>> +
>>> +    adevc->send_event = acpi_ged_send_event;
>>> +}
>>> +
>>> +static const TypeInfo acpi_ged_info = {
>>> +    .name          = TYPE_ACPI_GED,
>>> +    .parent        = TYPE_DEVICE,
>>> +    .instance_size = sizeof(AcpiGedState),
>>> +    .class_init    = acpi_ged_class_init,
>>> +    .interfaces = (InterfaceInfo[]) {
>>> +        { TYPE_HOTPLUG_HANDLER },
>>> +        { TYPE_ACPI_DEVICE_IF },
>>> +        { }
>>> +    }
>>> +};
>>> +
>>> +static void acpi_ged_register_types(void)
>>> +{
>>> +    type_register_static(&acpi_ged_info);
>>> +}
>>> +
>>> +type_init(acpi_ged_register_types)
>>> diff --git a/include/hw/acpi/generic_event_device.h
>> b/include/hw/acpi/generic_event_device.h
>>> new file mode 100644
>>> index 0000000..9c840d8
>>> --- /dev/null
>>> +++ b/include/hw/acpi/generic_event_device.h
>>> @@ -0,0 +1,121 @@
>>> +/*
>>> + *
>>> + * Copyright (c) 2018 Intel Corporation
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms and conditions of the GNU General Public License,
>>> + * version 2 or later, as published by the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope it will be useful, but WITHOUT
>>> + * ANY WARRANTY; without even the implied warranty of
>> MERCHANTABILITY or
>>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
>> License for
>>> + * more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License along
>> with
>>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>>> + *
>>> + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
>>> + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
>>> + * including the hotplug ones. Generic Event Device allows platforms
>>> + * to handle interrupts in ACPI ASL statements. It follows a very
>>> + * similar approach like the _EVT method from GPIO events. All
>>> + * interrupts are listed in  _CRS and the handler is written in _EVT> + *
>> method. Here, we use a single interrupt for the GED device, relying
>>> + * on IO memory region to communicate the type of device affected by
>>> + * the interrupt. This way, we can support up to 32 events with a
>>> + * unique interrupt.
>>> + *
>>> + * Here is an example.
>>> + *
>>> + * Device (\_SB.GED)
>>> + * {
>>> + *     Name (_HID, "ACPI0013")
>>> + *     Name (_UID, Zero)
>>> + *     Name (_CRS, ResourceTemplate ()
>>> + *     {
>>> + *         Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, )
>>> + *         {
>>> + *              0x00000029,
>>> + *         }
>>> + *     })
>>> + *     OperationRegion (IREG, SystemMemory, 0x09080000, 0x04)
>>> + *     Field (IREG, DWordAcc, NoLock, WriteAsZeros)
>>> + *     {
>>> + *         ISEL,   32
>>> + *     }
>>> + *
>>> + *     Method (_EVT, 1, Serialized)  // _EVT: Event
>>> + *     {
>>> + *         Local0 = ISEL // ISEL = IO memory region which specifies the
>>> + *                       // device type.
>>> + *         If (((Local0 & irq0) == irq0))
>>> + *         {
>>> + *             MethodEvent0()
>>> + *         }
>>> + *         ElseIf ((Local0 & irq1) == irq1)
>>> + *         {
>>> + *             MethodEvent1()
>>> + *         }
>>> + *         ...
>>> + *     }
>>> + * }
>>> + *
>>> + */
>>> +
>>> +#ifndef HW_ACPI_GED_H
>>> +#define HW_ACPI_GED_H
>>> +
>>> +#include "hw/acpi/memory_hotplug.h"
>>> +
>>> +#define TYPE_ACPI_GED "acpi-ged"
>>> +#define ACPI_GED(obj) \
>>> +    OBJECT_CHECK(AcpiGedState, (obj), TYPE_ACPI_GED)
>>> +
>>> +#define ACPI_GED_IRQ_SEL_OFFSET 0x0
>>> +#define ACPI_GED_IRQ_SEL_LEN    0x4
>>> +#define ACPI_GED_IRQ_SEL_MEM    0x1
>>> +#define ACPI_GED_REG_LEN        0x4
>>> +
>>> +#define GED_DEVICE      "GED"
>>> +#define AML_GED_IRQ_REG "IREG"
>>> +#define AML_GED_IRQ_SEL "ISEL"
>>> +
>>> +typedef enum {
>>> +    GED_MEMORY_HOTPLUG = 1,
>>> +} GedEventType;
>>> +
>>> +/*
>>> + * Platforms need to specify their own GedEvent array
>>> + * to describe what kind of events they want to support
>>> + * through GED.
>>> + */
>>> +typedef struct GedEvent {
>>> +    uint32_t     selector;
>>> +    GedEventType event;
>>> +} GedEvent;
>>> +
>>> +typedef struct GEDState {
>>> +    MemoryRegion io;
>>> +    uint32_t     sel;
>>> +    uint32_t     irq;
>>> +    qemu_irq     *gsi;
>> so I am not sure why with need gsi + irq.
>>> +    QemuMutex    lock;
>>> +} GEDState;
>>> +
>>> +
>>> +typedef struct AcpiGedState {
>>> +    DeviceClass parent_obj;
>>> +    MemHotplugState memhp_state;
>>> +    hwaddr memhp_base;
>>> +    void *gsi;
>>> +    hwaddr ged_base;
>>> +    GEDState ged_state;
>>> +    uint32_t ged_irq;
>>> +    void *ged_events;
>>> +    uint32_t ged_events_size;
>>> +} AcpiGedState;
>>> +
>>> +void build_ged_aml(Aml *table, const char* name, HotplugHandler
>> *hotplug_dev,
>>> +                   uint32_t ged_irq, AmlRegionSpace rs);
>>> +
>>> +#endif
>>>
>> Besides, this looks good to me.
>>
> 
> Thanks,
> Shameer
> 


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
@ 2019-05-02  7:22         ` Ard Biesheuvel
  0 siblings, 0 replies; 62+ messages in thread
From: Ard Biesheuvel @ 2019-05-02  7:22 UTC (permalink / raw)
  To: Shameerali Kolothum Thodi
  Cc: QEMU Developers, qemu-arm, Auger Eric, Igor Mammedov,
	Peter Maydell, shannon.zhaosl, sameo, sebastien.boeuf, xuwei (O),
	Laszlo Ersek, Linuxarm

On Wed, 1 May 2019 at 13:25, Shameerali Kolothum Thodi
<shameerali.kolothum.thodi@huawei.com> wrote:
>
> Hi Ard,
>
> > -----Original Message-----
> > From: Ard Biesheuvel [mailto:ard.biesheuvel@linaro.org]
> > Sent: 01 May 2019 12:10
> > To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>
> > Cc: QEMU Developers <qemu-devel@nongnu.org>; qemu-arm
> > <qemu-arm@nongnu.org>; Auger Eric <eric.auger@redhat.com>; Igor
> > Mammedov <imammedo@redhat.com>; Peter Maydell
> > <peter.maydell@linaro.org>; shannon.zhaosl@gmail.com;
> > sameo@linux.intel.com; sebastien.boeuf@intel.com; xuwei (O)
> > <xuwei5@huawei.com>; Laszlo Ersek <lersek@redhat.com>; Linuxarm
> > <linuxarm@huawei.com>
> > Subject: Re: [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
> >
> > On Tue, 9 Apr 2019 at 12:31, Shameer Kolothum
> > <shameerali.kolothum.thodi@huawei.com> wrote:
> > >
> > > From: Samuel Ortiz <sameo@linux.intel.com>
> > >
> > > The ACPI Generic Event Device (GED) is a hardware-reduced specific
> > > device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> > > including the hotplug ones.This patch generates the AML code that
> > > defines GEDs.
> > >
> > > Platforms need to specify their own GedEvent array to describe what
> > > kind of events they want to support through GED.  Also this uses a
> > > a single interrupt for the  GED device, relying on IO memory region
> > > to communicate the type of device affected by the interrupt. This
> > > way, we can support up to 32 events with a unique interrupt.
> > >
> > > This supports only memory hotplug for now.
> > >
> > > Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
> > > Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
> > > Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> >
> > Apologies if this question has been raised before, but do we really
> > need a separate device for this? We already handle the power button
> > via _AEI/_Exx on the GPIO device, and I think we should be able to add
> > additional events using that interface, rather than have two event
> > signalling methods/devices on the same platform.
>
> Right. The initial RFC was based on GPIO device[1] and later Igor commented
> here[2] that,
>
> " ARM boards were first to use ACPI hw-reduced profile so they picked up
> available back then GPIO based way to deliver hotplug event, later spec
> introduced Generic Event Device for that means to use with hw-reduced
> profile, which NEMU implemented[1], so I'd use that rather than ad-hoc
> GPIO mapping. I'd guess it will more compatible with various contemporary
> guests and we could reuse the same code for both x86/arm virt boards) "
>

On mach-virt, we already use the GPIO controller for the ACPI event
involving the power button, so by aligning with virt-x86, we end up in
the opposite situation for mach-virt.

The problem with the GED device is that it only supports GSI
interrupts, while the GPIO device supports arbitrary interrupts (such
as GPIO signalled ones). This means that mach-virt will be stuck with
having two different methods of signalling ACPI events, unless we
rewire the power button not to use a GPIO interrupt but use a GSI
directly.

In general, I think the ACPI event delivery mechanism doesn't really
have to be aligned: the ACPI event is ultimately converted into a AML
notification to the right device, and how we end up there can remain
platform specific.

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
@ 2019-05-02  7:22         ` Ard Biesheuvel
  0 siblings, 0 replies; 62+ messages in thread
From: Ard Biesheuvel @ 2019-05-02  7:22 UTC (permalink / raw)
  To: Shameerali Kolothum Thodi
  Cc: Peter Maydell, sameo, shannon.zhaosl, QEMU Developers, Linuxarm,
	Auger Eric, qemu-arm, xuwei (O),
	Igor Mammedov, sebastien.boeuf, Laszlo Ersek

On Wed, 1 May 2019 at 13:25, Shameerali Kolothum Thodi
<shameerali.kolothum.thodi@huawei.com> wrote:
>
> Hi Ard,
>
> > -----Original Message-----
> > From: Ard Biesheuvel [mailto:ard.biesheuvel@linaro.org]
> > Sent: 01 May 2019 12:10
> > To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>
> > Cc: QEMU Developers <qemu-devel@nongnu.org>; qemu-arm
> > <qemu-arm@nongnu.org>; Auger Eric <eric.auger@redhat.com>; Igor
> > Mammedov <imammedo@redhat.com>; Peter Maydell
> > <peter.maydell@linaro.org>; shannon.zhaosl@gmail.com;
> > sameo@linux.intel.com; sebastien.boeuf@intel.com; xuwei (O)
> > <xuwei5@huawei.com>; Laszlo Ersek <lersek@redhat.com>; Linuxarm
> > <linuxarm@huawei.com>
> > Subject: Re: [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
> >
> > On Tue, 9 Apr 2019 at 12:31, Shameer Kolothum
> > <shameerali.kolothum.thodi@huawei.com> wrote:
> > >
> > > From: Samuel Ortiz <sameo@linux.intel.com>
> > >
> > > The ACPI Generic Event Device (GED) is a hardware-reduced specific
> > > device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> > > including the hotplug ones.This patch generates the AML code that
> > > defines GEDs.
> > >
> > > Platforms need to specify their own GedEvent array to describe what
> > > kind of events they want to support through GED.  Also this uses a
> > > a single interrupt for the  GED device, relying on IO memory region
> > > to communicate the type of device affected by the interrupt. This
> > > way, we can support up to 32 events with a unique interrupt.
> > >
> > > This supports only memory hotplug for now.
> > >
> > > Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
> > > Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
> > > Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> >
> > Apologies if this question has been raised before, but do we really
> > need a separate device for this? We already handle the power button
> > via _AEI/_Exx on the GPIO device, and I think we should be able to add
> > additional events using that interface, rather than have two event
> > signalling methods/devices on the same platform.
>
> Right. The initial RFC was based on GPIO device[1] and later Igor commented
> here[2] that,
>
> " ARM boards were first to use ACPI hw-reduced profile so they picked up
> available back then GPIO based way to deliver hotplug event, later spec
> introduced Generic Event Device for that means to use with hw-reduced
> profile, which NEMU implemented[1], so I'd use that rather than ad-hoc
> GPIO mapping. I'd guess it will more compatible with various contemporary
> guests and we could reuse the same code for both x86/arm virt boards) "
>

On mach-virt, we already use the GPIO controller for the ACPI event
involving the power button, so by aligning with virt-x86, we end up in
the opposite situation for mach-virt.

The problem with the GED device is that it only supports GSI
interrupts, while the GPIO device supports arbitrary interrupts (such
as GPIO signalled ones). This means that mach-virt will be stuck with
having two different methods of signalling ACPI events, unless we
rewire the power button not to use a GPIO interrupt but use a GSI
directly.

In general, I think the ACPI event delivery mechanism doesn't really
have to be aligned: the ACPI event is ultimately converted into a AML
notification to the right device, and how we end up there can remain
platform specific.


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
@ 2019-05-02 15:24           ` Igor Mammedov
  0 siblings, 0 replies; 62+ messages in thread
From: Igor Mammedov @ 2019-05-02 15:24 UTC (permalink / raw)
  To: Ard Biesheuvel
  Cc: Shameerali Kolothum Thodi, Peter Maydell, sameo, shannon.zhaosl,
	QEMU Developers, Linuxarm, Auger Eric, qemu-arm, xuwei (O),
	sebastien.boeuf, Laszlo Ersek

On Thu, 2 May 2019 09:22:35 +0200
Ard Biesheuvel <ard.biesheuvel@linaro.org> wrote:

> On Wed, 1 May 2019 at 13:25, Shameerali Kolothum Thodi
> <shameerali.kolothum.thodi@huawei.com> wrote:
> >
> > Hi Ard,
> >
> > > -----Original Message-----
> > > From: Ard Biesheuvel [mailto:ard.biesheuvel@linaro.org]
> > > Sent: 01 May 2019 12:10
> > > To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>
> > > Cc: QEMU Developers <qemu-devel@nongnu.org>; qemu-arm
> > > <qemu-arm@nongnu.org>; Auger Eric <eric.auger@redhat.com>; Igor
> > > Mammedov <imammedo@redhat.com>; Peter Maydell
> > > <peter.maydell@linaro.org>; shannon.zhaosl@gmail.com;
> > > sameo@linux.intel.com; sebastien.boeuf@intel.com; xuwei (O)
> > > <xuwei5@huawei.com>; Laszlo Ersek <lersek@redhat.com>; Linuxarm
> > > <linuxarm@huawei.com>
> > > Subject: Re: [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
> > >
> > > On Tue, 9 Apr 2019 at 12:31, Shameer Kolothum
> > > <shameerali.kolothum.thodi@huawei.com> wrote:
> > > >
> > > > From: Samuel Ortiz <sameo@linux.intel.com>
> > > >
> > > > The ACPI Generic Event Device (GED) is a hardware-reduced specific
> > > > device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> > > > including the hotplug ones.This patch generates the AML code that
> > > > defines GEDs.
> > > >
> > > > Platforms need to specify their own GedEvent array to describe what
> > > > kind of events they want to support through GED.  Also this uses a
> > > > a single interrupt for the  GED device, relying on IO memory region
> > > > to communicate the type of device affected by the interrupt. This
> > > > way, we can support up to 32 events with a unique interrupt.
> > > >
> > > > This supports only memory hotplug for now.
> > > >
> > > > Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
> > > > Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
> > > > Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> > >
> > > Apologies if this question has been raised before, but do we really
> > > need a separate device for this? We already handle the power button
> > > via _AEI/_Exx on the GPIO device, and I think we should be able to add
> > > additional events using that interface, rather than have two event
> > > signalling methods/devices on the same platform.
> >
> > Right. The initial RFC was based on GPIO device[1] and later Igor commented
> > here[2] that,
> >
> > " ARM boards were first to use ACPI hw-reduced profile so they picked up
> > available back then GPIO based way to deliver hotplug event, later spec
> > introduced Generic Event Device for that means to use with hw-reduced
> > profile, which NEMU implemented[1], so I'd use that rather than ad-hoc
> > GPIO mapping. I'd guess it will more compatible with various contemporary
> > guests and we could reuse the same code for both x86/arm virt boards) "
> >
> 
> On mach-virt, we already use the GPIO controller for the ACPI event
> involving the power button, so by aligning with virt-x86, we end up in
> the opposite situation for mach-virt.
> 
> The problem with the GED device is that it only supports GSI
> interrupts, while the GPIO device supports arbitrary interrupts (such
> as GPIO signalled ones). This means that mach-virt will be stuck with
> having two different methods of signalling ACPI events, unless we
> rewire the power button not to use a GPIO interrupt but use a GSI
> directly.

we can rewire power button then.


> In general, I think the ACPI event delivery mechanism doesn't really
> have to be aligned: the ACPI event is ultimately converted into a AML
> notification to the right device, and how we end up there can remain
> platform specific.

Reasoning for using GED is to reduce code duplication with x86
and not creating zoo of different approached (if it could be avoided).

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
@ 2019-05-02 15:24           ` Igor Mammedov
  0 siblings, 0 replies; 62+ messages in thread
From: Igor Mammedov @ 2019-05-02 15:24 UTC (permalink / raw)
  To: Ard Biesheuvel
  Cc: Peter Maydell, sameo, Auger Eric, QEMU Developers,
	Shameerali Kolothum Thodi, Linuxarm, shannon.zhaosl, qemu-arm,
	xuwei (O),
	sebastien.boeuf, Laszlo Ersek

On Thu, 2 May 2019 09:22:35 +0200
Ard Biesheuvel <ard.biesheuvel@linaro.org> wrote:

> On Wed, 1 May 2019 at 13:25, Shameerali Kolothum Thodi
> <shameerali.kolothum.thodi@huawei.com> wrote:
> >
> > Hi Ard,
> >
> > > -----Original Message-----
> > > From: Ard Biesheuvel [mailto:ard.biesheuvel@linaro.org]
> > > Sent: 01 May 2019 12:10
> > > To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>
> > > Cc: QEMU Developers <qemu-devel@nongnu.org>; qemu-arm
> > > <qemu-arm@nongnu.org>; Auger Eric <eric.auger@redhat.com>; Igor
> > > Mammedov <imammedo@redhat.com>; Peter Maydell
> > > <peter.maydell@linaro.org>; shannon.zhaosl@gmail.com;
> > > sameo@linux.intel.com; sebastien.boeuf@intel.com; xuwei (O)
> > > <xuwei5@huawei.com>; Laszlo Ersek <lersek@redhat.com>; Linuxarm
> > > <linuxarm@huawei.com>
> > > Subject: Re: [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
> > >
> > > On Tue, 9 Apr 2019 at 12:31, Shameer Kolothum
> > > <shameerali.kolothum.thodi@huawei.com> wrote:
> > > >
> > > > From: Samuel Ortiz <sameo@linux.intel.com>
> > > >
> > > > The ACPI Generic Event Device (GED) is a hardware-reduced specific
> > > > device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> > > > including the hotplug ones.This patch generates the AML code that
> > > > defines GEDs.
> > > >
> > > > Platforms need to specify their own GedEvent array to describe what
> > > > kind of events they want to support through GED.  Also this uses a
> > > > a single interrupt for the  GED device, relying on IO memory region
> > > > to communicate the type of device affected by the interrupt. This
> > > > way, we can support up to 32 events with a unique interrupt.
> > > >
> > > > This supports only memory hotplug for now.
> > > >
> > > > Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
> > > > Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
> > > > Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> > >
> > > Apologies if this question has been raised before, but do we really
> > > need a separate device for this? We already handle the power button
> > > via _AEI/_Exx on the GPIO device, and I think we should be able to add
> > > additional events using that interface, rather than have two event
> > > signalling methods/devices on the same platform.
> >
> > Right. The initial RFC was based on GPIO device[1] and later Igor commented
> > here[2] that,
> >
> > " ARM boards were first to use ACPI hw-reduced profile so they picked up
> > available back then GPIO based way to deliver hotplug event, later spec
> > introduced Generic Event Device for that means to use with hw-reduced
> > profile, which NEMU implemented[1], so I'd use that rather than ad-hoc
> > GPIO mapping. I'd guess it will more compatible with various contemporary
> > guests and we could reuse the same code for both x86/arm virt boards) "
> >
> 
> On mach-virt, we already use the GPIO controller for the ACPI event
> involving the power button, so by aligning with virt-x86, we end up in
> the opposite situation for mach-virt.
> 
> The problem with the GED device is that it only supports GSI
> interrupts, while the GPIO device supports arbitrary interrupts (such
> as GPIO signalled ones). This means that mach-virt will be stuck with
> having two different methods of signalling ACPI events, unless we
> rewire the power button not to use a GPIO interrupt but use a GSI
> directly.

we can rewire power button then.


> In general, I think the ACPI event delivery mechanism doesn't really
> have to be aligned: the ACPI event is ultimately converted into a AML
> notification to the right device, and how we end up there can remain
> platform specific.

Reasoning for using GED is to reduce code duplication with x86
and not creating zoo of different approached (if it could be avoided).


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
@ 2019-05-02 16:12     ` Igor Mammedov
  0 siblings, 0 replies; 62+ messages in thread
From: Igor Mammedov @ 2019-05-02 16:12 UTC (permalink / raw)
  To: Shameer Kolothum
  Cc: qemu-devel, qemu-arm, eric.auger, peter.maydell, shannon.zhaosl,
	sameo, sebastien.boeuf, xuwei5, lersek, ard.biesheuvel, linuxarm

On Tue, 9 Apr 2019 11:29:30 +0100
Shameer Kolothum <shameerali.kolothum.thodi@huawei.com> wrote:

> From: Samuel Ortiz <sameo@linux.intel.com>
> 
> The ACPI Generic Event Device (GED) is a hardware-reduced specific
> device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> including the hotplug ones.This patch generates the AML code that
> defines GEDs.
> 
> Platforms need to specify their own GedEvent array to describe what
> kind of events they want to support through GED.  Also this uses a
> a single interrupt for the  GED device, relying on IO memory region
> to communicate the type of device affected by the interrupt. This
> way, we can support up to 32 events with a unique interrupt.
> 
> This supports only memory hotplug for now.
> 
> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
> Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
> Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> ---
>  hw/acpi/Kconfig                        |   4 +
>  hw/acpi/Makefile.objs                  |   1 +
>  hw/acpi/generic_event_device.c         | 311 +++++++++++++++++++++++++++++++++
>  include/hw/acpi/generic_event_device.h | 121 +++++++++++++
>  4 files changed, 437 insertions(+)
>  create mode 100644 hw/acpi/generic_event_device.c
>  create mode 100644 include/hw/acpi/generic_event_device.h
> 
> diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig
> index eca3bee..01a8b41 100644
> --- a/hw/acpi/Kconfig
> +++ b/hw/acpi/Kconfig
> @@ -27,3 +27,7 @@ config ACPI_VMGENID
>      bool
>      default y
>      depends on PC
> +
> +config ACPI_HW_REDUCED
> +    bool
> +    depends on ACPI
> diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
> index 2d46e37..b753232 100644
> --- a/hw/acpi/Makefile.objs
> +++ b/hw/acpi/Makefile.objs
> @@ -6,6 +6,7 @@ common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o
>  common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu.o
>  common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
>  common-obj-$(CONFIG_ACPI_VMGENID) += vmgenid.o
> +common-obj-$(CONFIG_ACPI_HW_REDUCED) += generic_event_device.o
>  common-obj-$(call lnot,$(CONFIG_ACPI_X86)) += acpi-stub.o
>  
>  common-obj-y += acpi_interface.o
> diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c
> new file mode 100644
> index 0000000..856ca04
> --- /dev/null
> +++ b/hw/acpi/generic_event_device.c
> @@ -0,0 +1,311 @@
> +/*
> + *
> + * Copyright (c) 2018 Intel Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "exec/address-spaces.h"
> +#include "hw/sysbus.h"
> +#include "hw/acpi/acpi.h"
> +#include "hw/acpi/generic_event_device.h"
> +#include "hw/mem/pc-dimm.h"
> +
> +static Aml *ged_event_aml(const GedEvent *event)
> +{
> +
> +    if (!event) {
In general, I prefer to check condition for calling something before doing call.
This way one can see in caller why and what is called, which is more clear.

> +        return NULL;
> +    }
> +
> +    switch (event->event) {
> +    case GED_MEMORY_HOTPLUG:
> +        /* We run a complete memory SCAN when getting a memory hotplug event */
> +        return aml_call0(MEMORY_DEVICES_CONTAINER "." MEMORY_SLOT_SCAN_METHOD);
> +    default:
> +        break;
> +    }
> +
> +    return NULL;
> +}
> +
> +/*
> + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
> + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> + * including the hotplug ones. Platforms need to specify their own
> + * GedEvent array to describe what kind of events they want to support
> + * through GED. This routine uses a single interrupt for the GED device,
> + * relying on IO memory region to communicate the type of device
> + * affected by the interrupt. This way, we can support up to 32 events
> + * with a unique interrupt.
> + */
> +void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev,
> +                   uint32_t ged_irq, AmlRegionSpace rs)
> +{
> +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> +    GedEvent *ged_events = s->ged_events;
> +    Aml *crs = aml_resource_template();
> +    Aml *evt, *field;
> +    Aml *dev = aml_device("%s", name);
> +    Aml *irq_sel = aml_local(0);
> +    Aml *isel = aml_name(AML_GED_IRQ_SEL);
> +    uint32_t i;
> +
> +    if (!s->ged_base || !ged_events || !s->ged_events_size) {
I'd move it up to the caller, it would be obvious right there when
function should be called.

> +        return;
> +    }
> +
> +    /* _CRS interrupt */
> +    aml_append(crs, aml_interrupt(AML_CONSUMER, AML_EDGE, AML_ACTIVE_HIGH,
> +                                  AML_EXCLUSIVE, &ged_irq, 1));
> +    /*
> +     * For each GED event we:
> +     * - Add an interrupt to the CRS section.
> +     * - Add a conditional block for each event, inside a while loop.
> +     *   This is semantically equivalent to a switch/case implementation.
> +     */
> +    evt = aml_method("_EVT", 1, AML_SERIALIZED);
> +    {
> +        Aml *ged_aml;
> +        Aml *if_ctx;
> +
> +        /* Local0 = ISEL */
> +        aml_append(evt, aml_store(isel, irq_sel));
> +
> +        /*
> +         * Here we want to call a method for each supported GED event type.
> +         * The resulting ASL code looks like:
> +         *
> +         * Local0 = ISEL
> +         * If ((Local0 & irq0) == irq0)
> +         * {
> +         *     MethodEvent0()
> +         * }
> +         *
> +         * If ((Local0 & irq1) == irq1)
> +         * {
> +         *     MethodEvent1()
> +         * }
> +         * ...
> +         */
Well, I'm confused.
do we actually use multiple IRQs or we use only one + MMIO for event type?

> +        for (i = 0; i < s->ged_events_size; i++) {

> +            ged_aml = ged_event_aml(&ged_events[i]);
> +            if (!ged_aml) {
> +                continue;
> +            }
I'd get rid of ged_event_aml replace it with more 'switch':
   for (i,...)
       if_ctx = aml_if(...)
       switch (event) 
          case GED_MEMORY_HOTPLUG:
               aml_append(if_ctx, aml_call0(MEMORY_DEVICES_CONTAINER "." MEMORY_SLOT_SCAN_METHOD))
               break
          default:
               about(); // make sure that a newly added events have a handler

> +
> +            /* If ((Local1 == irq))*/
> +            if_ctx = aml_if(aml_equal(aml_and(irq_sel,
> +                                      aml_int(ged_events[i].selector), NULL),
> +                                      aml_int(ged_events[i].selector)));
> +            {
> +                /* AML for this specific type of event */
> +                aml_append(if_ctx), ged_aml);
> +            }
> +
> +            /*
> +             * We append the first "if" to the "while" context.
> +             * Other "if"s will be "elseif"s.
> +             */
> +            aml_append(evt, if_ctx);
> +        }
> +    }
> +

> +    aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0013")));
> +    aml_append(dev, aml_name_decl("_UID", aml_string(GED_DEVICE)));
> +    aml_append(dev, aml_name_decl("_CRS", crs));
> +
> +    /* Append IO region */
> +    aml_append(dev, aml_operation_region(AML_GED_IRQ_REG, rs,
> +               aml_int(s->ged_base + ACPI_GED_IRQ_SEL_OFFSET),
> +               ACPI_GED_IRQ_SEL_LEN));
> +    field = aml_field(AML_GED_IRQ_REG, AML_DWORD_ACC, AML_NOLOCK,
> +                      AML_WRITE_AS_ZEROS);
> +    aml_append(field, aml_named_field(AML_GED_IRQ_SEL,
> +                                      ACPI_GED_IRQ_SEL_LEN * 8));
> +    aml_append(dev, field);

I'd move it up above EVT() method, so it would be clear from the begging
for what device we are building AML


> +    /* Append _EVT method */
> +    aml_append(dev, evt);
> +
> +    aml_append(table, dev);
> +}
> +
> +/* Memory read by the GED _EVT AML dynamic method */
> +static uint64_t ged_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    uint64_t val = 0;
> +    GEDState *ged_st = opaque;
> +
> +    switch (addr) {
> +    case ACPI_GED_IRQ_SEL_OFFSET:
> +        /* Read the selector value and reset it */
> +        qemu_mutex_lock(&ged_st->lock);
> +        val = ged_st->sel;
> +        ged_st->sel = 0;
> +        qemu_mutex_unlock(&ged_st->lock);
> +        break;
> +    default:
> +        break;
> +    }
> +
> +    return val;
> +}
> +
> +/* Nothing is expected to be written to the GED memory region */
> +static void ged_write(void *opaque, hwaddr addr, uint64_t data,
> +                      unsigned int size)
> +{
> +}
> +
> +static const MemoryRegionOps ged_ops = {
> +    .read = ged_read,
> +    .write = ged_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +};
> +
> +static void acpi_ged_event(GEDState *ged_st, uint32_t ged_irq_sel)
> +{
> +    /*
> +     * Set the GED IRQ selector to the expected device type value. This
> +     * way, the ACPI method will be able to trigger the right code based
> +     * on a unique IRQ.
> +     */
> +    qemu_mutex_lock(&ged_st->lock);
> +    ged_st->sel = ged_irq_sel;
what if there are 2 events with different sel value and 2nd event happens
before guesr read the first one?

> +    qemu_mutex_unlock(&ged_st->lock);
> +
> +    /* Trigger the event by sending an interrupt to the guest. */
> +    qemu_irq_pulse(ged_st->gsi[ged_st->irq]);
> +}
> +
> +static void acpi_ged_init(MemoryRegion *as, DeviceState *dev, GEDState *ged_st)
> +{
> +    AcpiGedState *s = ACPI_GED(dev);
> +
> +    assert(s->ged_base);
> +
> +    ged_st->irq = s->ged_irq;
> +    ged_st->gsi = s->gsi;
> +    qemu_mutex_init(&ged_st->lock);
> +    memory_region_init_io(&ged_st->io, OBJECT(dev), &ged_ops, ged_st,
> +                          "acpi-ged-event", ACPI_GED_REG_LEN);
> +    memory_region_add_subregion(as, s->ged_base, &ged_st->io);
> +}
> +
> +static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
> +                                    DeviceState *dev, Error **errp)
> +{
> +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> +
> +    if (s->memhp_state.is_enabled &&
> +        object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> +            acpi_memory_plug_cb(hotplug_dev, &s->memhp_state,
> +                                dev, errp);
> +    } else {
> +        error_setg(errp, "virt: device plug request for unsupported device"
> +                   " type: %s", object_get_typename(OBJECT(dev)));
> +    }
> +}
> +
> +static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
> +{
> +    AcpiGedState *s = ACPI_GED(adev);
> +    uint32_t sel;
> +
> +    if (ev & ACPI_MEMORY_HOTPLUG_STATUS) {
> +        sel = ACPI_GED_IRQ_SEL_MEM;
> +    } else {
> +        /* Unknown event. Return without generating interrupt. */
> +        return;
> +    }
> +
> +    /*
> +     * We inject the hotplug interrupt. The IRQ selector will make
> +     * the difference from the ACPI table.
> +     */
> +    acpi_ged_event(&s->ged_state, sel);
> +}
> +
> +static void acpi_ged_device_realize(DeviceState *dev, Error **errp)
> +{
> +    AcpiGedState *s = ACPI_GED(dev);
> +
> +    if (s->memhp_state.is_enabled) {
> +        acpi_memory_hotplug_init(get_system_memory(), OBJECT(dev),
> +                                 &s->memhp_state,
> +                                 s->memhp_base);
> +        acpi_ged_init(get_system_memory(), dev, &s->ged_state);
> +    }
> +}
> +
> +static Property acpi_ged_properties[] = {
> +    /*
> +     * Memory hotplug base address is a property of GED here,
> +     * because GED handles memory hotplug event and MEMORY_HOTPLUG_DEVICE
> +     * gets initialized when GED device is realized.
> +     */
> +    DEFINE_PROP_UINT64("memhp-base", AcpiGedState, memhp_base, 0),
> +    DEFINE_PROP_BOOL("memory-hotplug-support", AcpiGedState,
> +                     memhp_state.is_enabled, true),
> +    DEFINE_PROP_PTR("gsi", AcpiGedState, gsi),

PTR shouldn't be used in new code, look at object_property_add_link() & co


> +    DEFINE_PROP_UINT64("ged-base", AcpiGedState, ged_base, 0),
> +    DEFINE_PROP_UINT32("ged-irq", AcpiGedState, ged_irq, 0),
> +    DEFINE_PROP_PTR("ged-events", AcpiGedState, ged_events),
> +    DEFINE_PROP_UINT32("ged-events-size", AcpiGedState, ged_events_size, 0),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void acpi_ged_class_init(ObjectClass *class, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(class);
> +    HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class);
> +    AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class);
> +
> +    dc->desc = "ACPI";
> +    dc->props = acpi_ged_properties;
> +    dc->realize = acpi_ged_device_realize;
> +
> +    /* Reason: pointer properties "gsi" and "ged_events" */
> +    dc->user_creatable = false;
> +
> +    hc->plug = acpi_ged_device_plug_cb;
> +
> +    adevc->send_event = acpi_ged_send_event;
> +}
> +
> +static const TypeInfo acpi_ged_info = {
> +    .name          = TYPE_ACPI_GED,
> +    .parent        = TYPE_DEVICE,
> +    .instance_size = sizeof(AcpiGedState),
> +    .class_init    = acpi_ged_class_init,
> +    .interfaces = (InterfaceInfo[]) {
> +        { TYPE_HOTPLUG_HANDLER },
> +        { TYPE_ACPI_DEVICE_IF },
> +        { }
> +    }
> +};
> +
> +static void acpi_ged_register_types(void)
> +{
> +    type_register_static(&acpi_ged_info);
> +}
> +
> +type_init(acpi_ged_register_types)
> diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h
> new file mode 100644
> index 0000000..9c840d8
> --- /dev/null
> +++ b/include/hw/acpi/generic_event_device.h
> @@ -0,0 +1,121 @@
> +/*
> + *
> + * Copyright (c) 2018 Intel Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
> + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> + * including the hotplug ones. Generic Event Device allows platforms
> + * to handle interrupts in ACPI ASL statements. It follows a very
> + * similar approach like the _EVT method from GPIO events. All
> + * interrupts are listed in  _CRS and the handler is written in _EVT
> + * method. Here, we use a single interrupt for the GED device, relying
> + * on IO memory region to communicate the type of device affected by
> + * the interrupt. This way, we can support up to 32 events with a
> + * unique interrupt.
> + *
> + * Here is an example.
> + *
> + * Device (\_SB.GED)
> + * {
> + *     Name (_HID, "ACPI0013")
> + *     Name (_UID, Zero)
> + *     Name (_CRS, ResourceTemplate ()
> + *     {
> + *         Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, )
> + *         {
> + *              0x00000029,
> + *         }
> + *     })
> + *     OperationRegion (IREG, SystemMemory, 0x09080000, 0x04)
> + *     Field (IREG, DWordAcc, NoLock, WriteAsZeros)
> + *     {
> + *         ISEL,   32
> + *     }
> + *
> + *     Method (_EVT, 1, Serialized)  // _EVT: Event
> + *     {
> + *         Local0 = ISEL // ISEL = IO memory region which specifies the
> + *                       // device type.
> + *         If (((Local0 & irq0) == irq0))
> + *         {
> + *             MethodEvent0()
> + *         }
> + *         ElseIf ((Local0 & irq1) == irq1)
> + *         {
> + *             MethodEvent1()
> + *         }
> + *         ...
> + *     }
> + * }
> + *
> + */
> +
> +#ifndef HW_ACPI_GED_H
> +#define HW_ACPI_GED_H
> +
> +#include "hw/acpi/memory_hotplug.h"
> +
> +#define TYPE_ACPI_GED "acpi-ged"
> +#define ACPI_GED(obj) \
> +    OBJECT_CHECK(AcpiGedState, (obj), TYPE_ACPI_GED)
> +
> +#define ACPI_GED_IRQ_SEL_OFFSET 0x0
> +#define ACPI_GED_IRQ_SEL_LEN    0x4
> +#define ACPI_GED_IRQ_SEL_MEM    0x1
> +#define ACPI_GED_REG_LEN        0x4
> +
> +#define GED_DEVICE      "GED"
> +#define AML_GED_IRQ_REG "IREG"
> +#define AML_GED_IRQ_SEL "ISEL"
> +
> +typedef enum {
> +    GED_MEMORY_HOTPLUG = 1,
> +} GedEventType;
> +
> +/*
> + * Platforms need to specify their own GedEvent array
> + * to describe what kind of events they want to support
> + * through GED.
> + */
> +typedef struct GedEvent {
> +    uint32_t     selector;
> +    GedEventType event;
> +} GedEvent;
> +
> +typedef struct GEDState {
> +    MemoryRegion io;
> +    uint32_t     sel;

do we need to migrate this during migration?

> +    uint32_t     irq;
> +    qemu_irq     *gsi;
> +    QemuMutex    lock;
> +} GEDState;
> +
> +
> +typedef struct AcpiGedState {
> +    DeviceClass parent_obj;
> +    MemHotplugState memhp_state;
> +    hwaddr memhp_base;
> +    void *gsi;
> +    hwaddr ged_base;
> +    GEDState ged_state;
> +    uint32_t ged_irq;
> +    void *ged_events;
> +    uint32_t ged_events_size;
> +} AcpiGedState;
> +
> +void build_ged_aml(Aml *table, const char* name, HotplugHandler *hotplug_dev,
> +                   uint32_t ged_irq, AmlRegionSpace rs);
> +
> +#endif

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
@ 2019-05-02 16:12     ` Igor Mammedov
  0 siblings, 0 replies; 62+ messages in thread
From: Igor Mammedov @ 2019-05-02 16:12 UTC (permalink / raw)
  To: Shameer Kolothum
  Cc: peter.maydell, sameo, shannon.zhaosl, ard.biesheuvel, qemu-devel,
	xuwei5, linuxarm, eric.auger, qemu-arm, sebastien.boeuf, lersek

On Tue, 9 Apr 2019 11:29:30 +0100
Shameer Kolothum <shameerali.kolothum.thodi@huawei.com> wrote:

> From: Samuel Ortiz <sameo@linux.intel.com>
> 
> The ACPI Generic Event Device (GED) is a hardware-reduced specific
> device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> including the hotplug ones.This patch generates the AML code that
> defines GEDs.
> 
> Platforms need to specify their own GedEvent array to describe what
> kind of events they want to support through GED.  Also this uses a
> a single interrupt for the  GED device, relying on IO memory region
> to communicate the type of device affected by the interrupt. This
> way, we can support up to 32 events with a unique interrupt.
> 
> This supports only memory hotplug for now.
> 
> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
> Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
> Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> ---
>  hw/acpi/Kconfig                        |   4 +
>  hw/acpi/Makefile.objs                  |   1 +
>  hw/acpi/generic_event_device.c         | 311 +++++++++++++++++++++++++++++++++
>  include/hw/acpi/generic_event_device.h | 121 +++++++++++++
>  4 files changed, 437 insertions(+)
>  create mode 100644 hw/acpi/generic_event_device.c
>  create mode 100644 include/hw/acpi/generic_event_device.h
> 
> diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig
> index eca3bee..01a8b41 100644
> --- a/hw/acpi/Kconfig
> +++ b/hw/acpi/Kconfig
> @@ -27,3 +27,7 @@ config ACPI_VMGENID
>      bool
>      default y
>      depends on PC
> +
> +config ACPI_HW_REDUCED
> +    bool
> +    depends on ACPI
> diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
> index 2d46e37..b753232 100644
> --- a/hw/acpi/Makefile.objs
> +++ b/hw/acpi/Makefile.objs
> @@ -6,6 +6,7 @@ common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o
>  common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu.o
>  common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
>  common-obj-$(CONFIG_ACPI_VMGENID) += vmgenid.o
> +common-obj-$(CONFIG_ACPI_HW_REDUCED) += generic_event_device.o
>  common-obj-$(call lnot,$(CONFIG_ACPI_X86)) += acpi-stub.o
>  
>  common-obj-y += acpi_interface.o
> diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c
> new file mode 100644
> index 0000000..856ca04
> --- /dev/null
> +++ b/hw/acpi/generic_event_device.c
> @@ -0,0 +1,311 @@
> +/*
> + *
> + * Copyright (c) 2018 Intel Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "exec/address-spaces.h"
> +#include "hw/sysbus.h"
> +#include "hw/acpi/acpi.h"
> +#include "hw/acpi/generic_event_device.h"
> +#include "hw/mem/pc-dimm.h"
> +
> +static Aml *ged_event_aml(const GedEvent *event)
> +{
> +
> +    if (!event) {
In general, I prefer to check condition for calling something before doing call.
This way one can see in caller why and what is called, which is more clear.

> +        return NULL;
> +    }
> +
> +    switch (event->event) {
> +    case GED_MEMORY_HOTPLUG:
> +        /* We run a complete memory SCAN when getting a memory hotplug event */
> +        return aml_call0(MEMORY_DEVICES_CONTAINER "." MEMORY_SLOT_SCAN_METHOD);
> +    default:
> +        break;
> +    }
> +
> +    return NULL;
> +}
> +
> +/*
> + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
> + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> + * including the hotplug ones. Platforms need to specify their own
> + * GedEvent array to describe what kind of events they want to support
> + * through GED. This routine uses a single interrupt for the GED device,
> + * relying on IO memory region to communicate the type of device
> + * affected by the interrupt. This way, we can support up to 32 events
> + * with a unique interrupt.
> + */
> +void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev,
> +                   uint32_t ged_irq, AmlRegionSpace rs)
> +{
> +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> +    GedEvent *ged_events = s->ged_events;
> +    Aml *crs = aml_resource_template();
> +    Aml *evt, *field;
> +    Aml *dev = aml_device("%s", name);
> +    Aml *irq_sel = aml_local(0);
> +    Aml *isel = aml_name(AML_GED_IRQ_SEL);
> +    uint32_t i;
> +
> +    if (!s->ged_base || !ged_events || !s->ged_events_size) {
I'd move it up to the caller, it would be obvious right there when
function should be called.

> +        return;
> +    }
> +
> +    /* _CRS interrupt */
> +    aml_append(crs, aml_interrupt(AML_CONSUMER, AML_EDGE, AML_ACTIVE_HIGH,
> +                                  AML_EXCLUSIVE, &ged_irq, 1));
> +    /*
> +     * For each GED event we:
> +     * - Add an interrupt to the CRS section.
> +     * - Add a conditional block for each event, inside a while loop.
> +     *   This is semantically equivalent to a switch/case implementation.
> +     */
> +    evt = aml_method("_EVT", 1, AML_SERIALIZED);
> +    {
> +        Aml *ged_aml;
> +        Aml *if_ctx;
> +
> +        /* Local0 = ISEL */
> +        aml_append(evt, aml_store(isel, irq_sel));
> +
> +        /*
> +         * Here we want to call a method for each supported GED event type.
> +         * The resulting ASL code looks like:
> +         *
> +         * Local0 = ISEL
> +         * If ((Local0 & irq0) == irq0)
> +         * {
> +         *     MethodEvent0()
> +         * }
> +         *
> +         * If ((Local0 & irq1) == irq1)
> +         * {
> +         *     MethodEvent1()
> +         * }
> +         * ...
> +         */
Well, I'm confused.
do we actually use multiple IRQs or we use only one + MMIO for event type?

> +        for (i = 0; i < s->ged_events_size; i++) {

> +            ged_aml = ged_event_aml(&ged_events[i]);
> +            if (!ged_aml) {
> +                continue;
> +            }
I'd get rid of ged_event_aml replace it with more 'switch':
   for (i,...)
       if_ctx = aml_if(...)
       switch (event) 
          case GED_MEMORY_HOTPLUG:
               aml_append(if_ctx, aml_call0(MEMORY_DEVICES_CONTAINER "." MEMORY_SLOT_SCAN_METHOD))
               break
          default:
               about(); // make sure that a newly added events have a handler

> +
> +            /* If ((Local1 == irq))*/
> +            if_ctx = aml_if(aml_equal(aml_and(irq_sel,
> +                                      aml_int(ged_events[i].selector), NULL),
> +                                      aml_int(ged_events[i].selector)));
> +            {
> +                /* AML for this specific type of event */
> +                aml_append(if_ctx), ged_aml);
> +            }
> +
> +            /*
> +             * We append the first "if" to the "while" context.
> +             * Other "if"s will be "elseif"s.
> +             */
> +            aml_append(evt, if_ctx);
> +        }
> +    }
> +

> +    aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0013")));
> +    aml_append(dev, aml_name_decl("_UID", aml_string(GED_DEVICE)));
> +    aml_append(dev, aml_name_decl("_CRS", crs));
> +
> +    /* Append IO region */
> +    aml_append(dev, aml_operation_region(AML_GED_IRQ_REG, rs,
> +               aml_int(s->ged_base + ACPI_GED_IRQ_SEL_OFFSET),
> +               ACPI_GED_IRQ_SEL_LEN));
> +    field = aml_field(AML_GED_IRQ_REG, AML_DWORD_ACC, AML_NOLOCK,
> +                      AML_WRITE_AS_ZEROS);
> +    aml_append(field, aml_named_field(AML_GED_IRQ_SEL,
> +                                      ACPI_GED_IRQ_SEL_LEN * 8));
> +    aml_append(dev, field);

I'd move it up above EVT() method, so it would be clear from the begging
for what device we are building AML


> +    /* Append _EVT method */
> +    aml_append(dev, evt);
> +
> +    aml_append(table, dev);
> +}
> +
> +/* Memory read by the GED _EVT AML dynamic method */
> +static uint64_t ged_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    uint64_t val = 0;
> +    GEDState *ged_st = opaque;
> +
> +    switch (addr) {
> +    case ACPI_GED_IRQ_SEL_OFFSET:
> +        /* Read the selector value and reset it */
> +        qemu_mutex_lock(&ged_st->lock);
> +        val = ged_st->sel;
> +        ged_st->sel = 0;
> +        qemu_mutex_unlock(&ged_st->lock);
> +        break;
> +    default:
> +        break;
> +    }
> +
> +    return val;
> +}
> +
> +/* Nothing is expected to be written to the GED memory region */
> +static void ged_write(void *opaque, hwaddr addr, uint64_t data,
> +                      unsigned int size)
> +{
> +}
> +
> +static const MemoryRegionOps ged_ops = {
> +    .read = ged_read,
> +    .write = ged_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +};
> +
> +static void acpi_ged_event(GEDState *ged_st, uint32_t ged_irq_sel)
> +{
> +    /*
> +     * Set the GED IRQ selector to the expected device type value. This
> +     * way, the ACPI method will be able to trigger the right code based
> +     * on a unique IRQ.
> +     */
> +    qemu_mutex_lock(&ged_st->lock);
> +    ged_st->sel = ged_irq_sel;
what if there are 2 events with different sel value and 2nd event happens
before guesr read the first one?

> +    qemu_mutex_unlock(&ged_st->lock);
> +
> +    /* Trigger the event by sending an interrupt to the guest. */
> +    qemu_irq_pulse(ged_st->gsi[ged_st->irq]);
> +}
> +
> +static void acpi_ged_init(MemoryRegion *as, DeviceState *dev, GEDState *ged_st)
> +{
> +    AcpiGedState *s = ACPI_GED(dev);
> +
> +    assert(s->ged_base);
> +
> +    ged_st->irq = s->ged_irq;
> +    ged_st->gsi = s->gsi;
> +    qemu_mutex_init(&ged_st->lock);
> +    memory_region_init_io(&ged_st->io, OBJECT(dev), &ged_ops, ged_st,
> +                          "acpi-ged-event", ACPI_GED_REG_LEN);
> +    memory_region_add_subregion(as, s->ged_base, &ged_st->io);
> +}
> +
> +static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
> +                                    DeviceState *dev, Error **errp)
> +{
> +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> +
> +    if (s->memhp_state.is_enabled &&
> +        object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> +            acpi_memory_plug_cb(hotplug_dev, &s->memhp_state,
> +                                dev, errp);
> +    } else {
> +        error_setg(errp, "virt: device plug request for unsupported device"
> +                   " type: %s", object_get_typename(OBJECT(dev)));
> +    }
> +}
> +
> +static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
> +{
> +    AcpiGedState *s = ACPI_GED(adev);
> +    uint32_t sel;
> +
> +    if (ev & ACPI_MEMORY_HOTPLUG_STATUS) {
> +        sel = ACPI_GED_IRQ_SEL_MEM;
> +    } else {
> +        /* Unknown event. Return without generating interrupt. */
> +        return;
> +    }
> +
> +    /*
> +     * We inject the hotplug interrupt. The IRQ selector will make
> +     * the difference from the ACPI table.
> +     */
> +    acpi_ged_event(&s->ged_state, sel);
> +}
> +
> +static void acpi_ged_device_realize(DeviceState *dev, Error **errp)
> +{
> +    AcpiGedState *s = ACPI_GED(dev);
> +
> +    if (s->memhp_state.is_enabled) {
> +        acpi_memory_hotplug_init(get_system_memory(), OBJECT(dev),
> +                                 &s->memhp_state,
> +                                 s->memhp_base);
> +        acpi_ged_init(get_system_memory(), dev, &s->ged_state);
> +    }
> +}
> +
> +static Property acpi_ged_properties[] = {
> +    /*
> +     * Memory hotplug base address is a property of GED here,
> +     * because GED handles memory hotplug event and MEMORY_HOTPLUG_DEVICE
> +     * gets initialized when GED device is realized.
> +     */
> +    DEFINE_PROP_UINT64("memhp-base", AcpiGedState, memhp_base, 0),
> +    DEFINE_PROP_BOOL("memory-hotplug-support", AcpiGedState,
> +                     memhp_state.is_enabled, true),
> +    DEFINE_PROP_PTR("gsi", AcpiGedState, gsi),

PTR shouldn't be used in new code, look at object_property_add_link() & co


> +    DEFINE_PROP_UINT64("ged-base", AcpiGedState, ged_base, 0),
> +    DEFINE_PROP_UINT32("ged-irq", AcpiGedState, ged_irq, 0),
> +    DEFINE_PROP_PTR("ged-events", AcpiGedState, ged_events),
> +    DEFINE_PROP_UINT32("ged-events-size", AcpiGedState, ged_events_size, 0),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void acpi_ged_class_init(ObjectClass *class, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(class);
> +    HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class);
> +    AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class);
> +
> +    dc->desc = "ACPI";
> +    dc->props = acpi_ged_properties;
> +    dc->realize = acpi_ged_device_realize;
> +
> +    /* Reason: pointer properties "gsi" and "ged_events" */
> +    dc->user_creatable = false;
> +
> +    hc->plug = acpi_ged_device_plug_cb;
> +
> +    adevc->send_event = acpi_ged_send_event;
> +}
> +
> +static const TypeInfo acpi_ged_info = {
> +    .name          = TYPE_ACPI_GED,
> +    .parent        = TYPE_DEVICE,
> +    .instance_size = sizeof(AcpiGedState),
> +    .class_init    = acpi_ged_class_init,
> +    .interfaces = (InterfaceInfo[]) {
> +        { TYPE_HOTPLUG_HANDLER },
> +        { TYPE_ACPI_DEVICE_IF },
> +        { }
> +    }
> +};
> +
> +static void acpi_ged_register_types(void)
> +{
> +    type_register_static(&acpi_ged_info);
> +}
> +
> +type_init(acpi_ged_register_types)
> diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h
> new file mode 100644
> index 0000000..9c840d8
> --- /dev/null
> +++ b/include/hw/acpi/generic_event_device.h
> @@ -0,0 +1,121 @@
> +/*
> + *
> + * Copyright (c) 2018 Intel Corporation
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2 or later, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
> + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> + * including the hotplug ones. Generic Event Device allows platforms
> + * to handle interrupts in ACPI ASL statements. It follows a very
> + * similar approach like the _EVT method from GPIO events. All
> + * interrupts are listed in  _CRS and the handler is written in _EVT
> + * method. Here, we use a single interrupt for the GED device, relying
> + * on IO memory region to communicate the type of device affected by
> + * the interrupt. This way, we can support up to 32 events with a
> + * unique interrupt.
> + *
> + * Here is an example.
> + *
> + * Device (\_SB.GED)
> + * {
> + *     Name (_HID, "ACPI0013")
> + *     Name (_UID, Zero)
> + *     Name (_CRS, ResourceTemplate ()
> + *     {
> + *         Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, )
> + *         {
> + *              0x00000029,
> + *         }
> + *     })
> + *     OperationRegion (IREG, SystemMemory, 0x09080000, 0x04)
> + *     Field (IREG, DWordAcc, NoLock, WriteAsZeros)
> + *     {
> + *         ISEL,   32
> + *     }
> + *
> + *     Method (_EVT, 1, Serialized)  // _EVT: Event
> + *     {
> + *         Local0 = ISEL // ISEL = IO memory region which specifies the
> + *                       // device type.
> + *         If (((Local0 & irq0) == irq0))
> + *         {
> + *             MethodEvent0()
> + *         }
> + *         ElseIf ((Local0 & irq1) == irq1)
> + *         {
> + *             MethodEvent1()
> + *         }
> + *         ...
> + *     }
> + * }
> + *
> + */
> +
> +#ifndef HW_ACPI_GED_H
> +#define HW_ACPI_GED_H
> +
> +#include "hw/acpi/memory_hotplug.h"
> +
> +#define TYPE_ACPI_GED "acpi-ged"
> +#define ACPI_GED(obj) \
> +    OBJECT_CHECK(AcpiGedState, (obj), TYPE_ACPI_GED)
> +
> +#define ACPI_GED_IRQ_SEL_OFFSET 0x0
> +#define ACPI_GED_IRQ_SEL_LEN    0x4
> +#define ACPI_GED_IRQ_SEL_MEM    0x1
> +#define ACPI_GED_REG_LEN        0x4
> +
> +#define GED_DEVICE      "GED"
> +#define AML_GED_IRQ_REG "IREG"
> +#define AML_GED_IRQ_SEL "ISEL"
> +
> +typedef enum {
> +    GED_MEMORY_HOTPLUG = 1,
> +} GedEventType;
> +
> +/*
> + * Platforms need to specify their own GedEvent array
> + * to describe what kind of events they want to support
> + * through GED.
> + */
> +typedef struct GedEvent {
> +    uint32_t     selector;
> +    GedEventType event;
> +} GedEvent;
> +
> +typedef struct GEDState {
> +    MemoryRegion io;
> +    uint32_t     sel;

do we need to migrate this during migration?

> +    uint32_t     irq;
> +    qemu_irq     *gsi;
> +    QemuMutex    lock;
> +} GEDState;
> +
> +
> +typedef struct AcpiGedState {
> +    DeviceClass parent_obj;
> +    MemHotplugState memhp_state;
> +    hwaddr memhp_base;
> +    void *gsi;
> +    hwaddr ged_base;
> +    GEDState ged_state;
> +    uint32_t ged_irq;
> +    void *ged_events;
> +    uint32_t ged_events_size;
> +} AcpiGedState;
> +
> +void build_ged_aml(Aml *table, const char* name, HotplugHandler *hotplug_dev,
> +                   uint32_t ged_irq, AmlRegionSpace rs);
> +
> +#endif



^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 4/8] hw/arm/virt: Add memory hotplug framework
@ 2019-05-02 16:19     ` Igor Mammedov
  0 siblings, 0 replies; 62+ messages in thread
From: Igor Mammedov @ 2019-05-02 16:19 UTC (permalink / raw)
  To: Shameer Kolothum
  Cc: qemu-devel, qemu-arm, eric.auger, peter.maydell, shannon.zhaosl,
	sameo, sebastien.boeuf, xuwei5, lersek, ard.biesheuvel, linuxarm

On Tue, 9 Apr 2019 11:29:31 +0100
Shameer Kolothum <shameerali.kolothum.thodi@huawei.com> wrote:

> From: Eric Auger <eric.auger@redhat.com>
> 
> This patch adds the memory hot-plug/hot-unplug infrastructure
> in machvirt. The device memory is not yet exposed to the Guest
> either though DT or ACPI and hence both cold/hot plug of memory
s/though/through/

> is explicitly disabled for now.
> 
> Signed-off-by: Eric Auger <eric.auger@redhat.com>
> Signed-off-by: Kwangwoo Lee <kwangwoo.lee@sk.com>
> Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> ---
>  default-configs/arm-softmmu.mak |  3 +++
>  hw/arm/virt.c                   | 45 ++++++++++++++++++++++++++++++++++++++++-
>  2 files changed, 47 insertions(+), 1 deletion(-)
> 
> diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
> index 613d19a..9f4b803 100644
> --- a/default-configs/arm-softmmu.mak
> +++ b/default-configs/arm-softmmu.mak
> @@ -160,3 +160,6 @@ CONFIG_MUSICPAL=y
>  
>  # for realview and versatilepb
>  CONFIG_LSI_SCSI_PCI=y
> +
> +CONFIG_MEM_DEVICE=y
> +CONFIG_DIMM=y
> diff --git a/hw/arm/virt.c b/hw/arm/virt.c
> index ce2664a..da516b3 100644
> --- a/hw/arm/virt.c
> +++ b/hw/arm/virt.c
> @@ -61,6 +61,8 @@
>  #include "hw/arm/smmuv3.h"
>  #include "hw/acpi/acpi.h"
>  #include "target/arm/internals.h"
> +#include "hw/mem/pc-dimm.h"
> +#include "hw/mem/nvdimm.h"
>  
>  #define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \
>      static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
> @@ -1806,6 +1808,34 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms)
>      return ms->possible_cpus;
>  }
>  
> +static void virt_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
> +                                 Error **errp)
> +{
> +    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> +        error_setg(errp, "memory cold/hot plug is not yet supported");
> +        return;
> +    }
add comment here why it's needed.

> +
> +    pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), NULL, errp);
maybe before calling this there probably should be check if acpi is enabled.

not sure if arm/virt board honors -no-acpi CLI option.

> +}
> +
> +static void virt_memory_plug(HotplugHandler *hotplug_dev,
> +                             DeviceState *dev, Error **errp)
> +{
> +    VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
> +
> +    pc_dimm_plug(PC_DIMM(dev), MACHINE(vms), NULL);
> +
> +}
> +
> +static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
> +                                            DeviceState *dev, Error **errp)
> +{
> +    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> +        virt_memory_pre_plug(hotplug_dev, dev, errp);
> +    }
> +}
> +
>  static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev,
>                                          DeviceState *dev, Error **errp)
>  {
> @@ -1817,12 +1847,23 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev,
>                                       SYS_BUS_DEVICE(dev));
>          }
>      }
> +    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> +        virt_memory_plug(hotplug_dev, dev, errp);
> +    }
> +}
> +
> +static void virt_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev,
> +                                          DeviceState *dev, Error **errp)
> +{
> +    error_setg(errp, "device unplug request for unsupported device"
> +               " type: %s", object_get_typename(OBJECT(dev)));
>  }
>  
>  static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine,
>                                                          DeviceState *dev)
>  {
> -    if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE)) {
> +    if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE) ||
> +       (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM))) {
>          return HOTPLUG_HANDLER(machine);
>      }
>  
> @@ -1886,7 +1927,9 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
>      mc->kvm_type = virt_kvm_type;
>      assert(!mc->get_hotplug_handler);
>      mc->get_hotplug_handler = virt_machine_get_hotplug_handler;
> +    hc->pre_plug = virt_machine_device_pre_plug_cb;
>      hc->plug = virt_machine_device_plug_cb;
> +    hc->unplug_request = virt_machine_device_unplug_request_cb;
>  }
>  
>  static void virt_instance_init(Object *obj)

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 4/8] hw/arm/virt: Add memory hotplug framework
@ 2019-05-02 16:19     ` Igor Mammedov
  0 siblings, 0 replies; 62+ messages in thread
From: Igor Mammedov @ 2019-05-02 16:19 UTC (permalink / raw)
  To: Shameer Kolothum
  Cc: peter.maydell, sameo, shannon.zhaosl, ard.biesheuvel, qemu-devel,
	xuwei5, linuxarm, eric.auger, qemu-arm, sebastien.boeuf, lersek

On Tue, 9 Apr 2019 11:29:31 +0100
Shameer Kolothum <shameerali.kolothum.thodi@huawei.com> wrote:

> From: Eric Auger <eric.auger@redhat.com>
> 
> This patch adds the memory hot-plug/hot-unplug infrastructure
> in machvirt. The device memory is not yet exposed to the Guest
> either though DT or ACPI and hence both cold/hot plug of memory
s/though/through/

> is explicitly disabled for now.
> 
> Signed-off-by: Eric Auger <eric.auger@redhat.com>
> Signed-off-by: Kwangwoo Lee <kwangwoo.lee@sk.com>
> Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> ---
>  default-configs/arm-softmmu.mak |  3 +++
>  hw/arm/virt.c                   | 45 ++++++++++++++++++++++++++++++++++++++++-
>  2 files changed, 47 insertions(+), 1 deletion(-)
> 
> diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
> index 613d19a..9f4b803 100644
> --- a/default-configs/arm-softmmu.mak
> +++ b/default-configs/arm-softmmu.mak
> @@ -160,3 +160,6 @@ CONFIG_MUSICPAL=y
>  
>  # for realview and versatilepb
>  CONFIG_LSI_SCSI_PCI=y
> +
> +CONFIG_MEM_DEVICE=y
> +CONFIG_DIMM=y
> diff --git a/hw/arm/virt.c b/hw/arm/virt.c
> index ce2664a..da516b3 100644
> --- a/hw/arm/virt.c
> +++ b/hw/arm/virt.c
> @@ -61,6 +61,8 @@
>  #include "hw/arm/smmuv3.h"
>  #include "hw/acpi/acpi.h"
>  #include "target/arm/internals.h"
> +#include "hw/mem/pc-dimm.h"
> +#include "hw/mem/nvdimm.h"
>  
>  #define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \
>      static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
> @@ -1806,6 +1808,34 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms)
>      return ms->possible_cpus;
>  }
>  
> +static void virt_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
> +                                 Error **errp)
> +{
> +    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> +        error_setg(errp, "memory cold/hot plug is not yet supported");
> +        return;
> +    }
add comment here why it's needed.

> +
> +    pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), NULL, errp);
maybe before calling this there probably should be check if acpi is enabled.

not sure if arm/virt board honors -no-acpi CLI option.

> +}
> +
> +static void virt_memory_plug(HotplugHandler *hotplug_dev,
> +                             DeviceState *dev, Error **errp)
> +{
> +    VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
> +
> +    pc_dimm_plug(PC_DIMM(dev), MACHINE(vms), NULL);
> +
> +}
> +
> +static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
> +                                            DeviceState *dev, Error **errp)
> +{
> +    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> +        virt_memory_pre_plug(hotplug_dev, dev, errp);
> +    }
> +}
> +
>  static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev,
>                                          DeviceState *dev, Error **errp)
>  {
> @@ -1817,12 +1847,23 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev,
>                                       SYS_BUS_DEVICE(dev));
>          }
>      }
> +    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> +        virt_memory_plug(hotplug_dev, dev, errp);
> +    }
> +}
> +
> +static void virt_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev,
> +                                          DeviceState *dev, Error **errp)
> +{
> +    error_setg(errp, "device unplug request for unsupported device"
> +               " type: %s", object_get_typename(OBJECT(dev)));
>  }
>  
>  static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine,
>                                                          DeviceState *dev)
>  {
> -    if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE)) {
> +    if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE) ||
> +       (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM))) {
>          return HOTPLUG_HANDLER(machine);
>      }
>  
> @@ -1886,7 +1927,9 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
>      mc->kvm_type = virt_kvm_type;
>      assert(!mc->get_hotplug_handler);
>      mc->get_hotplug_handler = virt_machine_get_hotplug_handler;
> +    hc->pre_plug = virt_machine_device_pre_plug_cb;
>      hc->plug = virt_machine_device_plug_cb;
> +    hc->unplug_request = virt_machine_device_unplug_request_cb;
>  }
>  
>  static void virt_instance_init(Object *obj)



^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
@ 2019-05-03 12:45       ` Shameerali Kolothum Thodi
  0 siblings, 0 replies; 62+ messages in thread
From: Shameerali Kolothum Thodi @ 2019-05-03 12:45 UTC (permalink / raw)
  To: Igor Mammedov
  Cc: qemu-devel, qemu-arm, eric.auger, peter.maydell, shannon.zhaosl,
	sameo, sebastien.boeuf, xuwei (O),
	lersek, ard.biesheuvel, Linuxarm

Hi Igor,

> -----Original Message-----
> From: Igor Mammedov [mailto:imammedo@redhat.com]
> Sent: 02 May 2019 17:13
> To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>
> Cc: qemu-devel@nongnu.org; qemu-arm@nongnu.org;
> eric.auger@redhat.com; peter.maydell@linaro.org;
> shannon.zhaosl@gmail.com; sameo@linux.intel.com;
> sebastien.boeuf@intel.com; xuwei (O) <xuwei5@huawei.com>;
> lersek@redhat.com; ard.biesheuvel@linaro.org; Linuxarm
> <linuxarm@huawei.com>
> Subject: Re: [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
> 
> On Tue, 9 Apr 2019 11:29:30 +0100
> Shameer Kolothum <shameerali.kolothum.thodi@huawei.com> wrote:
> 
> > From: Samuel Ortiz <sameo@linux.intel.com>
> >
> > The ACPI Generic Event Device (GED) is a hardware-reduced specific
> > device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> > including the hotplug ones.This patch generates the AML code that
> > defines GEDs.
> >
> > Platforms need to specify their own GedEvent array to describe what
> > kind of events they want to support through GED.  Also this uses a
> > a single interrupt for the  GED device, relying on IO memory region
> > to communicate the type of device affected by the interrupt. This
> > way, we can support up to 32 events with a unique interrupt.
> >
> > This supports only memory hotplug for now.
> >
> > Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
> > Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
> > Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> > ---
> >  hw/acpi/Kconfig                        |   4 +
> >  hw/acpi/Makefile.objs                  |   1 +
> >  hw/acpi/generic_event_device.c         | 311
> +++++++++++++++++++++++++++++++++
> >  include/hw/acpi/generic_event_device.h | 121 +++++++++++++
> >  4 files changed, 437 insertions(+)
> >  create mode 100644 hw/acpi/generic_event_device.c
> >  create mode 100644 include/hw/acpi/generic_event_device.h
> >
> > diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig
> > index eca3bee..01a8b41 100644
> > --- a/hw/acpi/Kconfig
> > +++ b/hw/acpi/Kconfig
> > @@ -27,3 +27,7 @@ config ACPI_VMGENID
> >      bool
> >      default y
> >      depends on PC
> > +
> > +config ACPI_HW_REDUCED
> > +    bool
> > +    depends on ACPI
> > diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
> > index 2d46e37..b753232 100644
> > --- a/hw/acpi/Makefile.objs
> > +++ b/hw/acpi/Makefile.objs
> > @@ -6,6 +6,7 @@ common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) +=
> memory_hotplug.o
> >  common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu.o
> >  common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
> >  common-obj-$(CONFIG_ACPI_VMGENID) += vmgenid.o
> > +common-obj-$(CONFIG_ACPI_HW_REDUCED) += generic_event_device.o
> >  common-obj-$(call lnot,$(CONFIG_ACPI_X86)) += acpi-stub.o
> >
> >  common-obj-y += acpi_interface.o
> > diff --git a/hw/acpi/generic_event_device.c
> b/hw/acpi/generic_event_device.c
> > new file mode 100644
> > index 0000000..856ca04
> > --- /dev/null
> > +++ b/hw/acpi/generic_event_device.c
> > @@ -0,0 +1,311 @@
> > +/*
> > + *
> > + * Copyright (c) 2018 Intel Corporation
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2 or later, as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope it will be useful, but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of
> MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
> License for
> > + * more details.
> > + *
> > + * You should have received a copy of the GNU General Public License along
> with
> > + * this program.  If not, see <http://www.gnu.org/licenses/>.
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +#include "qapi/error.h"
> > +#include "exec/address-spaces.h"
> > +#include "hw/sysbus.h"
> > +#include "hw/acpi/acpi.h"
> > +#include "hw/acpi/generic_event_device.h"
> > +#include "hw/mem/pc-dimm.h"
> > +
> > +static Aml *ged_event_aml(const GedEvent *event)
> > +{
> > +
> > +    if (!event) {
> In general, I prefer to check condition for calling something before doing call.
> This way one can see in caller why and what is called, which is more clear.

Ok. I will move it then.

> 
> > +        return NULL;
> > +    }
> > +
> > +    switch (event->event) {
> > +    case GED_MEMORY_HOTPLUG:
> > +        /* We run a complete memory SCAN when getting a memory
> hotplug event */
> > +        return aml_call0(MEMORY_DEVICES_CONTAINER "."
> MEMORY_SLOT_SCAN_METHOD);
> > +    default:
> > +        break;
> > +    }
> > +
> > +    return NULL;
> > +}
> > +
> > +/*
> > + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
> > + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> > + * including the hotplug ones. Platforms need to specify their own
> > + * GedEvent array to describe what kind of events they want to support
> > + * through GED. This routine uses a single interrupt for the GED device,
> > + * relying on IO memory region to communicate the type of device
> > + * affected by the interrupt. This way, we can support up to 32 events
> > + * with a unique interrupt.
> > + */
> > +void build_ged_aml(Aml *table, const char *name, HotplugHandler
> *hotplug_dev,
> > +                   uint32_t ged_irq, AmlRegionSpace rs)
> > +{
> > +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> > +    GedEvent *ged_events = s->ged_events;
> > +    Aml *crs = aml_resource_template();
> > +    Aml *evt, *field;
> > +    Aml *dev = aml_device("%s", name);
> > +    Aml *irq_sel = aml_local(0);
> > +    Aml *isel = aml_name(AML_GED_IRQ_SEL);
> > +    uint32_t i;
> > +
> > +    if (!s->ged_base || !ged_events || !s->ged_events_size) {
> I'd move it up to the caller, it would be obvious right there when
> function should be called.

Ok. 
 
> > +        return;
> > +    }
> > +
> > +    /* _CRS interrupt */
> > +    aml_append(crs, aml_interrupt(AML_CONSUMER, AML_EDGE,
> AML_ACTIVE_HIGH,
> > +                                  AML_EXCLUSIVE, &ged_irq, 1));
> > +    /*
> > +     * For each GED event we:
> > +     * - Add an interrupt to the CRS section.
> > +     * - Add a conditional block for each event, inside a while loop.
> > +     *   This is semantically equivalent to a switch/case implementation.
> > +     */
> > +    evt = aml_method("_EVT", 1, AML_SERIALIZED);
> > +    {
> > +        Aml *ged_aml;
> > +        Aml *if_ctx;
> > +
> > +        /* Local0 = ISEL */
> > +        aml_append(evt, aml_store(isel, irq_sel));
> > +
> > +        /*
> > +         * Here we want to call a method for each supported GED event
> type.
> > +         * The resulting ASL code looks like:
> > +         *
> > +         * Local0 = ISEL
> > +         * If ((Local0 & irq0) == irq0)
> > +         * {
> > +         *     MethodEvent0()
> > +         * }
> > +         *
> > +         * If ((Local0 & irq1) == irq1)
> > +         * {
> > +         *     MethodEvent1()
> > +         * }
> > +         * ...
> > +         */
> Well, I'm confused.
> do we actually use multiple IRQs or we use only one + MMIO for event type?

It is one irq + MMIO. I will change the comment block something like this,

    Local0 = ISEL
    If ((Local0 & One) == One)
    {
        MethodEvent1()
    }

    If ((Local0 & 0x02) == 0x02)
    {
        MethodEvent2()
    }
    ...

> 
> > +        for (i = 0; i < s->ged_events_size; i++) {
> 
> > +            ged_aml = ged_event_aml(&ged_events[i]);
> > +            if (!ged_aml) {
> > +                continue;
> > +            }
> I'd get rid of ged_event_aml replace it with more 'switch':
>    for (i,...)
>        if_ctx = aml_if(...)
>        switch (event)
>           case GED_MEMORY_HOTPLUG:
>                aml_append(if_ctx,
> aml_call0(MEMORY_DEVICES_CONTAINER "."
> MEMORY_SLOT_SCAN_METHOD))
>                break
>           default:
>                about(); // make sure that a newly added events have a
> handler

Ok. I will change this.

> 
> > +
> > +            /* If ((Local1 == irq))*/
> > +            if_ctx = aml_if(aml_equal(aml_and(irq_sel,
> > +
> aml_int(ged_events[i].selector), NULL),
> > +
> aml_int(ged_events[i].selector)));
> > +            {
> > +                /* AML for this specific type of event */
> > +                aml_append(if_ctx), ged_aml);
> > +            }
> > +
> > +            /*
> > +             * We append the first "if" to the "while" context.
> > +             * Other "if"s will be "elseif"s.
> > +             */
> > +            aml_append(evt, if_ctx);
> > +        }
> > +    }
> > +
> 
> > +    aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0013")));
> > +    aml_append(dev, aml_name_decl("_UID", aml_string(GED_DEVICE)));
> > +    aml_append(dev, aml_name_decl("_CRS", crs));
> > +
> > +    /* Append IO region */
> > +    aml_append(dev, aml_operation_region(AML_GED_IRQ_REG, rs,
> > +               aml_int(s->ged_base + ACPI_GED_IRQ_SEL_OFFSET),
> > +               ACPI_GED_IRQ_SEL_LEN));
> > +    field = aml_field(AML_GED_IRQ_REG, AML_DWORD_ACC,
> AML_NOLOCK,
> > +                      AML_WRITE_AS_ZEROS);
> > +    aml_append(field, aml_named_field(AML_GED_IRQ_SEL,
> > +                                      ACPI_GED_IRQ_SEL_LEN *
> 8));
> > +    aml_append(dev, field);
> 
> I'd move it up above EVT() method, so it would be clear from the begging
> for what device we are building AML

Ok.
 
> 
> > +    /* Append _EVT method */
> > +    aml_append(dev, evt);
> > +
> > +    aml_append(table, dev);
> > +}
> > +
> > +/* Memory read by the GED _EVT AML dynamic method */
> > +static uint64_t ged_read(void *opaque, hwaddr addr, unsigned size)
> > +{
> > +    uint64_t val = 0;
> > +    GEDState *ged_st = opaque;
> > +
> > +    switch (addr) {
> > +    case ACPI_GED_IRQ_SEL_OFFSET:
> > +        /* Read the selector value and reset it */
> > +        qemu_mutex_lock(&ged_st->lock);
> > +        val = ged_st->sel;
> > +        ged_st->sel = 0;
> > +        qemu_mutex_unlock(&ged_st->lock);
> > +        break;
> > +    default:
> > +        break;
> > +    }
> > +
> > +    return val;
> > +}
> > +
> > +/* Nothing is expected to be written to the GED memory region */
> > +static void ged_write(void *opaque, hwaddr addr, uint64_t data,
> > +                      unsigned int size)
> > +{
> > +}
> > +
> > +static const MemoryRegionOps ged_ops = {
> > +    .read = ged_read,
> > +    .write = ged_write,
> > +    .endianness = DEVICE_LITTLE_ENDIAN,
> > +    .valid = {
> > +        .min_access_size = 4,
> > +        .max_access_size = 4,
> > +    },
> > +};
> > +
> > +static void acpi_ged_event(GEDState *ged_st, uint32_t ged_irq_sel)
> > +{
> > +    /*
> > +     * Set the GED IRQ selector to the expected device type value. This
> > +     * way, the ACPI method will be able to trigger the right code based
> > +     * on a unique IRQ.
> > +     */
> > +    qemu_mutex_lock(&ged_st->lock);
> > +    ged_st->sel = ged_irq_sel;
> what if there are 2 events with different sel value and 2nd event happens
> before guesr read the first one?

This was previously,
   ged_st->sel |= ged_irq_sel;

and was changed based on the assumption that there won't be any multiple
events. But I think the scenario above is right. I will change it back so that 
we won't overwrite.

> 
> > +    qemu_mutex_unlock(&ged_st->lock);
> > +
> > +    /* Trigger the event by sending an interrupt to the guest. */
> > +    qemu_irq_pulse(ged_st->gsi[ged_st->irq]);
> > +}
> > +
> > +static void acpi_ged_init(MemoryRegion *as, DeviceState *dev, GEDState
> *ged_st)
> > +{
> > +    AcpiGedState *s = ACPI_GED(dev);
> > +
> > +    assert(s->ged_base);
> > +
> > +    ged_st->irq = s->ged_irq;
> > +    ged_st->gsi = s->gsi;
> > +    qemu_mutex_init(&ged_st->lock);
> > +    memory_region_init_io(&ged_st->io, OBJECT(dev), &ged_ops, ged_st,
> > +                          "acpi-ged-event", ACPI_GED_REG_LEN);
> > +    memory_region_add_subregion(as, s->ged_base, &ged_st->io);
> > +}
> > +
> > +static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
> > +                                    DeviceState *dev, Error **errp)
> > +{
> > +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> > +
> > +    if (s->memhp_state.is_enabled &&
> > +        object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> > +            acpi_memory_plug_cb(hotplug_dev, &s->memhp_state,
> > +                                dev, errp);
> > +    } else {
> > +        error_setg(errp, "virt: device plug request for unsupported
> device"
> > +                   " type: %s", object_get_typename(OBJECT(dev)));
> > +    }
> > +}
> > +
> > +static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits
> ev)
> > +{
> > +    AcpiGedState *s = ACPI_GED(adev);
> > +    uint32_t sel;
> > +
> > +    if (ev & ACPI_MEMORY_HOTPLUG_STATUS) {
> > +        sel = ACPI_GED_IRQ_SEL_MEM;
> > +    } else {
> > +        /* Unknown event. Return without generating interrupt. */
> > +        return;
> > +    }
> > +
> > +    /*
> > +     * We inject the hotplug interrupt. The IRQ selector will make
> > +     * the difference from the ACPI table.
> > +     */
> > +    acpi_ged_event(&s->ged_state, sel);
> > +}
> > +
> > +static void acpi_ged_device_realize(DeviceState *dev, Error **errp)
> > +{
> > +    AcpiGedState *s = ACPI_GED(dev);
> > +
> > +    if (s->memhp_state.is_enabled) {
> > +        acpi_memory_hotplug_init(get_system_memory(), OBJECT(dev),
> > +                                 &s->memhp_state,
> > +                                 s->memhp_base);
> > +        acpi_ged_init(get_system_memory(), dev, &s->ged_state);
> > +    }
> > +}
> > +
> > +static Property acpi_ged_properties[] = {
> > +    /*
> > +     * Memory hotplug base address is a property of GED here,
> > +     * because GED handles memory hotplug event and
> MEMORY_HOTPLUG_DEVICE
> > +     * gets initialized when GED device is realized.
> > +     */
> > +    DEFINE_PROP_UINT64("memhp-base", AcpiGedState, memhp_base,
> 0),
> > +    DEFINE_PROP_BOOL("memory-hotplug-support", AcpiGedState,
> > +                     memhp_state.is_enabled, true),
> > +    DEFINE_PROP_PTR("gsi", AcpiGedState, gsi),
> 
> PTR shouldn't be used in new code, look at object_property_add_link() & co

Ok. I will take a look at that.
 
> 
> > +    DEFINE_PROP_UINT64("ged-base", AcpiGedState, ged_base, 0),
> > +    DEFINE_PROP_UINT32("ged-irq", AcpiGedState, ged_irq, 0),
> > +    DEFINE_PROP_PTR("ged-events", AcpiGedState, ged_events),
> > +    DEFINE_PROP_UINT32("ged-events-size", AcpiGedState,
> ged_events_size, 0),
> > +    DEFINE_PROP_END_OF_LIST(),
> > +};
> > +
> > +static void acpi_ged_class_init(ObjectClass *class, void *data)
> > +{
> > +    DeviceClass *dc = DEVICE_CLASS(class);
> > +    HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class);
> > +    AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class);
> > +
> > +    dc->desc = "ACPI";
> > +    dc->props = acpi_ged_properties;
> > +    dc->realize = acpi_ged_device_realize;
> > +
> > +    /* Reason: pointer properties "gsi" and "ged_events" */
> > +    dc->user_creatable = false;
> > +
> > +    hc->plug = acpi_ged_device_plug_cb;
> > +
> > +    adevc->send_event = acpi_ged_send_event;
> > +}
> > +
> > +static const TypeInfo acpi_ged_info = {
> > +    .name          = TYPE_ACPI_GED,
> > +    .parent        = TYPE_DEVICE,
> > +    .instance_size = sizeof(AcpiGedState),
> > +    .class_init    = acpi_ged_class_init,
> > +    .interfaces = (InterfaceInfo[]) {
> > +        { TYPE_HOTPLUG_HANDLER },
> > +        { TYPE_ACPI_DEVICE_IF },
> > +        { }
> > +    }
> > +};
> > +
> > +static void acpi_ged_register_types(void)
> > +{
> > +    type_register_static(&acpi_ged_info);
> > +}
> > +
> > +type_init(acpi_ged_register_types)
> > diff --git a/include/hw/acpi/generic_event_device.h
> b/include/hw/acpi/generic_event_device.h
> > new file mode 100644
> > index 0000000..9c840d8
> > --- /dev/null
> > +++ b/include/hw/acpi/generic_event_device.h
> > @@ -0,0 +1,121 @@
> > +/*
> > + *
> > + * Copyright (c) 2018 Intel Corporation
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2 or later, as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope it will be useful, but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of
> MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
> License for
> > + * more details.
> > + *
> > + * You should have received a copy of the GNU General Public License along
> with
> > + * this program.  If not, see <http://www.gnu.org/licenses/>.
> > + *
> > + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
> > + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> > + * including the hotplug ones. Generic Event Device allows platforms
> > + * to handle interrupts in ACPI ASL statements. It follows a very
> > + * similar approach like the _EVT method from GPIO events. All
> > + * interrupts are listed in  _CRS and the handler is written in _EVT
> > + * method. Here, we use a single interrupt for the GED device, relying
> > + * on IO memory region to communicate the type of device affected by
> > + * the interrupt. This way, we can support up to 32 events with a
> > + * unique interrupt.
> > + *
> > + * Here is an example.
> > + *
> > + * Device (\_SB.GED)
> > + * {
> > + *     Name (_HID, "ACPI0013")
> > + *     Name (_UID, Zero)
> > + *     Name (_CRS, ResourceTemplate ()
> > + *     {
> > + *         Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, )
> > + *         {
> > + *              0x00000029,
> > + *         }
> > + *     })
> > + *     OperationRegion (IREG, SystemMemory, 0x09080000, 0x04)
> > + *     Field (IREG, DWordAcc, NoLock, WriteAsZeros)
> > + *     {
> > + *         ISEL,   32
> > + *     }
> > + *
> > + *     Method (_EVT, 1, Serialized)  // _EVT: Event
> > + *     {
> > + *         Local0 = ISEL // ISEL = IO memory region which specifies the
> > + *                       // device type.
> > + *         If (((Local0 & irq0) == irq0))
> > + *         {
> > + *             MethodEvent0()
> > + *         }
> > + *         ElseIf ((Local0 & irq1) == irq1)
> > + *         {
> > + *             MethodEvent1()
> > + *         }
> > + *         ...
> > + *     }
> > + * }
> > + *
> > + */
> > +
> > +#ifndef HW_ACPI_GED_H
> > +#define HW_ACPI_GED_H
> > +
> > +#include "hw/acpi/memory_hotplug.h"
> > +
> > +#define TYPE_ACPI_GED "acpi-ged"
> > +#define ACPI_GED(obj) \
> > +    OBJECT_CHECK(AcpiGedState, (obj), TYPE_ACPI_GED)
> > +
> > +#define ACPI_GED_IRQ_SEL_OFFSET 0x0
> > +#define ACPI_GED_IRQ_SEL_LEN    0x4
> > +#define ACPI_GED_IRQ_SEL_MEM    0x1
> > +#define ACPI_GED_REG_LEN        0x4
> > +
> > +#define GED_DEVICE      "GED"
> > +#define AML_GED_IRQ_REG "IREG"
> > +#define AML_GED_IRQ_SEL "ISEL"
> > +
> > +typedef enum {
> > +    GED_MEMORY_HOTPLUG = 1,
> > +} GedEventType;
> > +
> > +/*
> > + * Platforms need to specify their own GedEvent array
> > + * to describe what kind of events they want to support
> > + * through GED.
> > + */
> > +typedef struct GedEvent {
> > +    uint32_t     selector;
> > +    GedEventType event;
> > +} GedEvent;
> > +
> > +typedef struct GEDState {
> > +    MemoryRegion io;
> > +    uint32_t     sel;
> 
> do we need to migrate this during migration?

TBH, I don't know and currently this series doesn't address migration 
as we don't have any VMStateDescription and friends. Is this something 
we can sort later?

Thanks,
Shameer

> > +    uint32_t     irq;
> > +    qemu_irq     *gsi;
> > +    QemuMutex    lock;
> > +} GEDState;
> > +
> > +
> > +typedef struct AcpiGedState {
> > +    DeviceClass parent_obj;
> > +    MemHotplugState memhp_state;
> > +    hwaddr memhp_base;
> > +    void *gsi;
> > +    hwaddr ged_base;
> > +    GEDState ged_state;
> > +    uint32_t ged_irq;
> > +    void *ged_events;
> > +    uint32_t ged_events_size;
> > +} AcpiGedState;
> > +
> > +void build_ged_aml(Aml *table, const char* name, HotplugHandler
> *hotplug_dev,
> > +                   uint32_t ged_irq, AmlRegionSpace rs);
> > +
> > +#endif

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
@ 2019-05-03 12:45       ` Shameerali Kolothum Thodi
  0 siblings, 0 replies; 62+ messages in thread
From: Shameerali Kolothum Thodi @ 2019-05-03 12:45 UTC (permalink / raw)
  To: Igor Mammedov
  Cc: peter.maydell, sameo, shannon.zhaosl, ard.biesheuvel, qemu-devel,
	Linuxarm, eric.auger, qemu-arm, xuwei (O),
	sebastien.boeuf, lersek

Hi Igor,

> -----Original Message-----
> From: Igor Mammedov [mailto:imammedo@redhat.com]
> Sent: 02 May 2019 17:13
> To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>
> Cc: qemu-devel@nongnu.org; qemu-arm@nongnu.org;
> eric.auger@redhat.com; peter.maydell@linaro.org;
> shannon.zhaosl@gmail.com; sameo@linux.intel.com;
> sebastien.boeuf@intel.com; xuwei (O) <xuwei5@huawei.com>;
> lersek@redhat.com; ard.biesheuvel@linaro.org; Linuxarm
> <linuxarm@huawei.com>
> Subject: Re: [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
> 
> On Tue, 9 Apr 2019 11:29:30 +0100
> Shameer Kolothum <shameerali.kolothum.thodi@huawei.com> wrote:
> 
> > From: Samuel Ortiz <sameo@linux.intel.com>
> >
> > The ACPI Generic Event Device (GED) is a hardware-reduced specific
> > device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> > including the hotplug ones.This patch generates the AML code that
> > defines GEDs.
> >
> > Platforms need to specify their own GedEvent array to describe what
> > kind of events they want to support through GED.  Also this uses a
> > a single interrupt for the  GED device, relying on IO memory region
> > to communicate the type of device affected by the interrupt. This
> > way, we can support up to 32 events with a unique interrupt.
> >
> > This supports only memory hotplug for now.
> >
> > Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
> > Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
> > Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> > ---
> >  hw/acpi/Kconfig                        |   4 +
> >  hw/acpi/Makefile.objs                  |   1 +
> >  hw/acpi/generic_event_device.c         | 311
> +++++++++++++++++++++++++++++++++
> >  include/hw/acpi/generic_event_device.h | 121 +++++++++++++
> >  4 files changed, 437 insertions(+)
> >  create mode 100644 hw/acpi/generic_event_device.c
> >  create mode 100644 include/hw/acpi/generic_event_device.h
> >
> > diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig
> > index eca3bee..01a8b41 100644
> > --- a/hw/acpi/Kconfig
> > +++ b/hw/acpi/Kconfig
> > @@ -27,3 +27,7 @@ config ACPI_VMGENID
> >      bool
> >      default y
> >      depends on PC
> > +
> > +config ACPI_HW_REDUCED
> > +    bool
> > +    depends on ACPI
> > diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
> > index 2d46e37..b753232 100644
> > --- a/hw/acpi/Makefile.objs
> > +++ b/hw/acpi/Makefile.objs
> > @@ -6,6 +6,7 @@ common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) +=
> memory_hotplug.o
> >  common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu.o
> >  common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
> >  common-obj-$(CONFIG_ACPI_VMGENID) += vmgenid.o
> > +common-obj-$(CONFIG_ACPI_HW_REDUCED) += generic_event_device.o
> >  common-obj-$(call lnot,$(CONFIG_ACPI_X86)) += acpi-stub.o
> >
> >  common-obj-y += acpi_interface.o
> > diff --git a/hw/acpi/generic_event_device.c
> b/hw/acpi/generic_event_device.c
> > new file mode 100644
> > index 0000000..856ca04
> > --- /dev/null
> > +++ b/hw/acpi/generic_event_device.c
> > @@ -0,0 +1,311 @@
> > +/*
> > + *
> > + * Copyright (c) 2018 Intel Corporation
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2 or later, as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope it will be useful, but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of
> MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
> License for
> > + * more details.
> > + *
> > + * You should have received a copy of the GNU General Public License along
> with
> > + * this program.  If not, see <http://www.gnu.org/licenses/>.
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +#include "qapi/error.h"
> > +#include "exec/address-spaces.h"
> > +#include "hw/sysbus.h"
> > +#include "hw/acpi/acpi.h"
> > +#include "hw/acpi/generic_event_device.h"
> > +#include "hw/mem/pc-dimm.h"
> > +
> > +static Aml *ged_event_aml(const GedEvent *event)
> > +{
> > +
> > +    if (!event) {
> In general, I prefer to check condition for calling something before doing call.
> This way one can see in caller why and what is called, which is more clear.

Ok. I will move it then.

> 
> > +        return NULL;
> > +    }
> > +
> > +    switch (event->event) {
> > +    case GED_MEMORY_HOTPLUG:
> > +        /* We run a complete memory SCAN when getting a memory
> hotplug event */
> > +        return aml_call0(MEMORY_DEVICES_CONTAINER "."
> MEMORY_SLOT_SCAN_METHOD);
> > +    default:
> > +        break;
> > +    }
> > +
> > +    return NULL;
> > +}
> > +
> > +/*
> > + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
> > + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> > + * including the hotplug ones. Platforms need to specify their own
> > + * GedEvent array to describe what kind of events they want to support
> > + * through GED. This routine uses a single interrupt for the GED device,
> > + * relying on IO memory region to communicate the type of device
> > + * affected by the interrupt. This way, we can support up to 32 events
> > + * with a unique interrupt.
> > + */
> > +void build_ged_aml(Aml *table, const char *name, HotplugHandler
> *hotplug_dev,
> > +                   uint32_t ged_irq, AmlRegionSpace rs)
> > +{
> > +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> > +    GedEvent *ged_events = s->ged_events;
> > +    Aml *crs = aml_resource_template();
> > +    Aml *evt, *field;
> > +    Aml *dev = aml_device("%s", name);
> > +    Aml *irq_sel = aml_local(0);
> > +    Aml *isel = aml_name(AML_GED_IRQ_SEL);
> > +    uint32_t i;
> > +
> > +    if (!s->ged_base || !ged_events || !s->ged_events_size) {
> I'd move it up to the caller, it would be obvious right there when
> function should be called.

Ok. 
 
> > +        return;
> > +    }
> > +
> > +    /* _CRS interrupt */
> > +    aml_append(crs, aml_interrupt(AML_CONSUMER, AML_EDGE,
> AML_ACTIVE_HIGH,
> > +                                  AML_EXCLUSIVE, &ged_irq, 1));
> > +    /*
> > +     * For each GED event we:
> > +     * - Add an interrupt to the CRS section.
> > +     * - Add a conditional block for each event, inside a while loop.
> > +     *   This is semantically equivalent to a switch/case implementation.
> > +     */
> > +    evt = aml_method("_EVT", 1, AML_SERIALIZED);
> > +    {
> > +        Aml *ged_aml;
> > +        Aml *if_ctx;
> > +
> > +        /* Local0 = ISEL */
> > +        aml_append(evt, aml_store(isel, irq_sel));
> > +
> > +        /*
> > +         * Here we want to call a method for each supported GED event
> type.
> > +         * The resulting ASL code looks like:
> > +         *
> > +         * Local0 = ISEL
> > +         * If ((Local0 & irq0) == irq0)
> > +         * {
> > +         *     MethodEvent0()
> > +         * }
> > +         *
> > +         * If ((Local0 & irq1) == irq1)
> > +         * {
> > +         *     MethodEvent1()
> > +         * }
> > +         * ...
> > +         */
> Well, I'm confused.
> do we actually use multiple IRQs or we use only one + MMIO for event type?

It is one irq + MMIO. I will change the comment block something like this,

    Local0 = ISEL
    If ((Local0 & One) == One)
    {
        MethodEvent1()
    }

    If ((Local0 & 0x02) == 0x02)
    {
        MethodEvent2()
    }
    ...

> 
> > +        for (i = 0; i < s->ged_events_size; i++) {
> 
> > +            ged_aml = ged_event_aml(&ged_events[i]);
> > +            if (!ged_aml) {
> > +                continue;
> > +            }
> I'd get rid of ged_event_aml replace it with more 'switch':
>    for (i,...)
>        if_ctx = aml_if(...)
>        switch (event)
>           case GED_MEMORY_HOTPLUG:
>                aml_append(if_ctx,
> aml_call0(MEMORY_DEVICES_CONTAINER "."
> MEMORY_SLOT_SCAN_METHOD))
>                break
>           default:
>                about(); // make sure that a newly added events have a
> handler

Ok. I will change this.

> 
> > +
> > +            /* If ((Local1 == irq))*/
> > +            if_ctx = aml_if(aml_equal(aml_and(irq_sel,
> > +
> aml_int(ged_events[i].selector), NULL),
> > +
> aml_int(ged_events[i].selector)));
> > +            {
> > +                /* AML for this specific type of event */
> > +                aml_append(if_ctx), ged_aml);
> > +            }
> > +
> > +            /*
> > +             * We append the first "if" to the "while" context.
> > +             * Other "if"s will be "elseif"s.
> > +             */
> > +            aml_append(evt, if_ctx);
> > +        }
> > +    }
> > +
> 
> > +    aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0013")));
> > +    aml_append(dev, aml_name_decl("_UID", aml_string(GED_DEVICE)));
> > +    aml_append(dev, aml_name_decl("_CRS", crs));
> > +
> > +    /* Append IO region */
> > +    aml_append(dev, aml_operation_region(AML_GED_IRQ_REG, rs,
> > +               aml_int(s->ged_base + ACPI_GED_IRQ_SEL_OFFSET),
> > +               ACPI_GED_IRQ_SEL_LEN));
> > +    field = aml_field(AML_GED_IRQ_REG, AML_DWORD_ACC,
> AML_NOLOCK,
> > +                      AML_WRITE_AS_ZEROS);
> > +    aml_append(field, aml_named_field(AML_GED_IRQ_SEL,
> > +                                      ACPI_GED_IRQ_SEL_LEN *
> 8));
> > +    aml_append(dev, field);
> 
> I'd move it up above EVT() method, so it would be clear from the begging
> for what device we are building AML

Ok.
 
> 
> > +    /* Append _EVT method */
> > +    aml_append(dev, evt);
> > +
> > +    aml_append(table, dev);
> > +}
> > +
> > +/* Memory read by the GED _EVT AML dynamic method */
> > +static uint64_t ged_read(void *opaque, hwaddr addr, unsigned size)
> > +{
> > +    uint64_t val = 0;
> > +    GEDState *ged_st = opaque;
> > +
> > +    switch (addr) {
> > +    case ACPI_GED_IRQ_SEL_OFFSET:
> > +        /* Read the selector value and reset it */
> > +        qemu_mutex_lock(&ged_st->lock);
> > +        val = ged_st->sel;
> > +        ged_st->sel = 0;
> > +        qemu_mutex_unlock(&ged_st->lock);
> > +        break;
> > +    default:
> > +        break;
> > +    }
> > +
> > +    return val;
> > +}
> > +
> > +/* Nothing is expected to be written to the GED memory region */
> > +static void ged_write(void *opaque, hwaddr addr, uint64_t data,
> > +                      unsigned int size)
> > +{
> > +}
> > +
> > +static const MemoryRegionOps ged_ops = {
> > +    .read = ged_read,
> > +    .write = ged_write,
> > +    .endianness = DEVICE_LITTLE_ENDIAN,
> > +    .valid = {
> > +        .min_access_size = 4,
> > +        .max_access_size = 4,
> > +    },
> > +};
> > +
> > +static void acpi_ged_event(GEDState *ged_st, uint32_t ged_irq_sel)
> > +{
> > +    /*
> > +     * Set the GED IRQ selector to the expected device type value. This
> > +     * way, the ACPI method will be able to trigger the right code based
> > +     * on a unique IRQ.
> > +     */
> > +    qemu_mutex_lock(&ged_st->lock);
> > +    ged_st->sel = ged_irq_sel;
> what if there are 2 events with different sel value and 2nd event happens
> before guesr read the first one?

This was previously,
   ged_st->sel |= ged_irq_sel;

and was changed based on the assumption that there won't be any multiple
events. But I think the scenario above is right. I will change it back so that 
we won't overwrite.

> 
> > +    qemu_mutex_unlock(&ged_st->lock);
> > +
> > +    /* Trigger the event by sending an interrupt to the guest. */
> > +    qemu_irq_pulse(ged_st->gsi[ged_st->irq]);
> > +}
> > +
> > +static void acpi_ged_init(MemoryRegion *as, DeviceState *dev, GEDState
> *ged_st)
> > +{
> > +    AcpiGedState *s = ACPI_GED(dev);
> > +
> > +    assert(s->ged_base);
> > +
> > +    ged_st->irq = s->ged_irq;
> > +    ged_st->gsi = s->gsi;
> > +    qemu_mutex_init(&ged_st->lock);
> > +    memory_region_init_io(&ged_st->io, OBJECT(dev), &ged_ops, ged_st,
> > +                          "acpi-ged-event", ACPI_GED_REG_LEN);
> > +    memory_region_add_subregion(as, s->ged_base, &ged_st->io);
> > +}
> > +
> > +static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
> > +                                    DeviceState *dev, Error **errp)
> > +{
> > +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> > +
> > +    if (s->memhp_state.is_enabled &&
> > +        object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> > +            acpi_memory_plug_cb(hotplug_dev, &s->memhp_state,
> > +                                dev, errp);
> > +    } else {
> > +        error_setg(errp, "virt: device plug request for unsupported
> device"
> > +                   " type: %s", object_get_typename(OBJECT(dev)));
> > +    }
> > +}
> > +
> > +static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits
> ev)
> > +{
> > +    AcpiGedState *s = ACPI_GED(adev);
> > +    uint32_t sel;
> > +
> > +    if (ev & ACPI_MEMORY_HOTPLUG_STATUS) {
> > +        sel = ACPI_GED_IRQ_SEL_MEM;
> > +    } else {
> > +        /* Unknown event. Return without generating interrupt. */
> > +        return;
> > +    }
> > +
> > +    /*
> > +     * We inject the hotplug interrupt. The IRQ selector will make
> > +     * the difference from the ACPI table.
> > +     */
> > +    acpi_ged_event(&s->ged_state, sel);
> > +}
> > +
> > +static void acpi_ged_device_realize(DeviceState *dev, Error **errp)
> > +{
> > +    AcpiGedState *s = ACPI_GED(dev);
> > +
> > +    if (s->memhp_state.is_enabled) {
> > +        acpi_memory_hotplug_init(get_system_memory(), OBJECT(dev),
> > +                                 &s->memhp_state,
> > +                                 s->memhp_base);
> > +        acpi_ged_init(get_system_memory(), dev, &s->ged_state);
> > +    }
> > +}
> > +
> > +static Property acpi_ged_properties[] = {
> > +    /*
> > +     * Memory hotplug base address is a property of GED here,
> > +     * because GED handles memory hotplug event and
> MEMORY_HOTPLUG_DEVICE
> > +     * gets initialized when GED device is realized.
> > +     */
> > +    DEFINE_PROP_UINT64("memhp-base", AcpiGedState, memhp_base,
> 0),
> > +    DEFINE_PROP_BOOL("memory-hotplug-support", AcpiGedState,
> > +                     memhp_state.is_enabled, true),
> > +    DEFINE_PROP_PTR("gsi", AcpiGedState, gsi),
> 
> PTR shouldn't be used in new code, look at object_property_add_link() & co

Ok. I will take a look at that.
 
> 
> > +    DEFINE_PROP_UINT64("ged-base", AcpiGedState, ged_base, 0),
> > +    DEFINE_PROP_UINT32("ged-irq", AcpiGedState, ged_irq, 0),
> > +    DEFINE_PROP_PTR("ged-events", AcpiGedState, ged_events),
> > +    DEFINE_PROP_UINT32("ged-events-size", AcpiGedState,
> ged_events_size, 0),
> > +    DEFINE_PROP_END_OF_LIST(),
> > +};
> > +
> > +static void acpi_ged_class_init(ObjectClass *class, void *data)
> > +{
> > +    DeviceClass *dc = DEVICE_CLASS(class);
> > +    HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class);
> > +    AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class);
> > +
> > +    dc->desc = "ACPI";
> > +    dc->props = acpi_ged_properties;
> > +    dc->realize = acpi_ged_device_realize;
> > +
> > +    /* Reason: pointer properties "gsi" and "ged_events" */
> > +    dc->user_creatable = false;
> > +
> > +    hc->plug = acpi_ged_device_plug_cb;
> > +
> > +    adevc->send_event = acpi_ged_send_event;
> > +}
> > +
> > +static const TypeInfo acpi_ged_info = {
> > +    .name          = TYPE_ACPI_GED,
> > +    .parent        = TYPE_DEVICE,
> > +    .instance_size = sizeof(AcpiGedState),
> > +    .class_init    = acpi_ged_class_init,
> > +    .interfaces = (InterfaceInfo[]) {
> > +        { TYPE_HOTPLUG_HANDLER },
> > +        { TYPE_ACPI_DEVICE_IF },
> > +        { }
> > +    }
> > +};
> > +
> > +static void acpi_ged_register_types(void)
> > +{
> > +    type_register_static(&acpi_ged_info);
> > +}
> > +
> > +type_init(acpi_ged_register_types)
> > diff --git a/include/hw/acpi/generic_event_device.h
> b/include/hw/acpi/generic_event_device.h
> > new file mode 100644
> > index 0000000..9c840d8
> > --- /dev/null
> > +++ b/include/hw/acpi/generic_event_device.h
> > @@ -0,0 +1,121 @@
> > +/*
> > + *
> > + * Copyright (c) 2018 Intel Corporation
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2 or later, as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope it will be useful, but WITHOUT
> > + * ANY WARRANTY; without even the implied warranty of
> MERCHANTABILITY or
> > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
> License for
> > + * more details.
> > + *
> > + * You should have received a copy of the GNU General Public License along
> with
> > + * this program.  If not, see <http://www.gnu.org/licenses/>.
> > + *
> > + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
> > + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> > + * including the hotplug ones. Generic Event Device allows platforms
> > + * to handle interrupts in ACPI ASL statements. It follows a very
> > + * similar approach like the _EVT method from GPIO events. All
> > + * interrupts are listed in  _CRS and the handler is written in _EVT
> > + * method. Here, we use a single interrupt for the GED device, relying
> > + * on IO memory region to communicate the type of device affected by
> > + * the interrupt. This way, we can support up to 32 events with a
> > + * unique interrupt.
> > + *
> > + * Here is an example.
> > + *
> > + * Device (\_SB.GED)
> > + * {
> > + *     Name (_HID, "ACPI0013")
> > + *     Name (_UID, Zero)
> > + *     Name (_CRS, ResourceTemplate ()
> > + *     {
> > + *         Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, )
> > + *         {
> > + *              0x00000029,
> > + *         }
> > + *     })
> > + *     OperationRegion (IREG, SystemMemory, 0x09080000, 0x04)
> > + *     Field (IREG, DWordAcc, NoLock, WriteAsZeros)
> > + *     {
> > + *         ISEL,   32
> > + *     }
> > + *
> > + *     Method (_EVT, 1, Serialized)  // _EVT: Event
> > + *     {
> > + *         Local0 = ISEL // ISEL = IO memory region which specifies the
> > + *                       // device type.
> > + *         If (((Local0 & irq0) == irq0))
> > + *         {
> > + *             MethodEvent0()
> > + *         }
> > + *         ElseIf ((Local0 & irq1) == irq1)
> > + *         {
> > + *             MethodEvent1()
> > + *         }
> > + *         ...
> > + *     }
> > + * }
> > + *
> > + */
> > +
> > +#ifndef HW_ACPI_GED_H
> > +#define HW_ACPI_GED_H
> > +
> > +#include "hw/acpi/memory_hotplug.h"
> > +
> > +#define TYPE_ACPI_GED "acpi-ged"
> > +#define ACPI_GED(obj) \
> > +    OBJECT_CHECK(AcpiGedState, (obj), TYPE_ACPI_GED)
> > +
> > +#define ACPI_GED_IRQ_SEL_OFFSET 0x0
> > +#define ACPI_GED_IRQ_SEL_LEN    0x4
> > +#define ACPI_GED_IRQ_SEL_MEM    0x1
> > +#define ACPI_GED_REG_LEN        0x4
> > +
> > +#define GED_DEVICE      "GED"
> > +#define AML_GED_IRQ_REG "IREG"
> > +#define AML_GED_IRQ_SEL "ISEL"
> > +
> > +typedef enum {
> > +    GED_MEMORY_HOTPLUG = 1,
> > +} GedEventType;
> > +
> > +/*
> > + * Platforms need to specify their own GedEvent array
> > + * to describe what kind of events they want to support
> > + * through GED.
> > + */
> > +typedef struct GedEvent {
> > +    uint32_t     selector;
> > +    GedEventType event;
> > +} GedEvent;
> > +
> > +typedef struct GEDState {
> > +    MemoryRegion io;
> > +    uint32_t     sel;
> 
> do we need to migrate this during migration?

TBH, I don't know and currently this series doesn't address migration 
as we don't have any VMStateDescription and friends. Is this something 
we can sort later?

Thanks,
Shameer

> > +    uint32_t     irq;
> > +    qemu_irq     *gsi;
> > +    QemuMutex    lock;
> > +} GEDState;
> > +
> > +
> > +typedef struct AcpiGedState {
> > +    DeviceClass parent_obj;
> > +    MemHotplugState memhp_state;
> > +    hwaddr memhp_base;
> > +    void *gsi;
> > +    hwaddr ged_base;
> > +    GEDState ged_state;
> > +    uint32_t ged_irq;
> > +    void *ged_events;
> > +    uint32_t ged_events_size;
> > +} AcpiGedState;
> > +
> > +void build_ged_aml(Aml *table, const char* name, HotplugHandler
> *hotplug_dev,
> > +                   uint32_t ged_irq, AmlRegionSpace rs);
> > +
> > +#endif



^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 4/8] hw/arm/virt: Add memory hotplug framework
@ 2019-05-03 12:47       ` Shameerali Kolothum Thodi
  0 siblings, 0 replies; 62+ messages in thread
From: Shameerali Kolothum Thodi @ 2019-05-03 12:47 UTC (permalink / raw)
  To: Igor Mammedov
  Cc: qemu-devel, qemu-arm, eric.auger, peter.maydell, shannon.zhaosl,
	sameo, sebastien.boeuf, xuwei (O),
	lersek, ard.biesheuvel, Linuxarm



> -----Original Message-----
> From: Igor Mammedov [mailto:imammedo@redhat.com]
> Sent: 02 May 2019 17:19
> To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>
> Cc: qemu-devel@nongnu.org; qemu-arm@nongnu.org;
> eric.auger@redhat.com; peter.maydell@linaro.org;
> shannon.zhaosl@gmail.com; sameo@linux.intel.com;
> sebastien.boeuf@intel.com; xuwei (O) <xuwei5@huawei.com>;
> lersek@redhat.com; ard.biesheuvel@linaro.org; Linuxarm
> <linuxarm@huawei.com>
> Subject: Re: [PATCH v4 4/8] hw/arm/virt: Add memory hotplug framework
> 
> On Tue, 9 Apr 2019 11:29:31 +0100
> Shameer Kolothum <shameerali.kolothum.thodi@huawei.com> wrote:
> 
> > From: Eric Auger <eric.auger@redhat.com>
> >
> > This patch adds the memory hot-plug/hot-unplug infrastructure
> > in machvirt. The device memory is not yet exposed to the Guest
> > either though DT or ACPI and hence both cold/hot plug of memory
> s/though/through/

Sure.

> > is explicitly disabled for now.
> >
> > Signed-off-by: Eric Auger <eric.auger@redhat.com>
> > Signed-off-by: Kwangwoo Lee <kwangwoo.lee@sk.com>
> > Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> > ---
> >  default-configs/arm-softmmu.mak |  3 +++
> >  hw/arm/virt.c                   | 45
> ++++++++++++++++++++++++++++++++++++++++-
> >  2 files changed, 47 insertions(+), 1 deletion(-)
> >
> > diff --git a/default-configs/arm-softmmu.mak
> b/default-configs/arm-softmmu.mak
> > index 613d19a..9f4b803 100644
> > --- a/default-configs/arm-softmmu.mak
> > +++ b/default-configs/arm-softmmu.mak
> > @@ -160,3 +160,6 @@ CONFIG_MUSICPAL=y
> >
> >  # for realview and versatilepb
> >  CONFIG_LSI_SCSI_PCI=y
> > +
> > +CONFIG_MEM_DEVICE=y
> > +CONFIG_DIMM=y
> > diff --git a/hw/arm/virt.c b/hw/arm/virt.c
> > index ce2664a..da516b3 100644
> > --- a/hw/arm/virt.c
> > +++ b/hw/arm/virt.c
> > @@ -61,6 +61,8 @@
> >  #include "hw/arm/smmuv3.h"
> >  #include "hw/acpi/acpi.h"
> >  #include "target/arm/internals.h"
> > +#include "hw/mem/pc-dimm.h"
> > +#include "hw/mem/nvdimm.h"
> >
> >  #define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \
> >      static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
> > @@ -1806,6 +1808,34 @@ static const CPUArchIdList
> *virt_possible_cpu_arch_ids(MachineState *ms)
> >      return ms->possible_cpus;
> >  }
> >
> > +static void virt_memory_pre_plug(HotplugHandler *hotplug_dev,
> DeviceState *dev,
> > +                                 Error **errp)
> > +{
> > +    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> > +        error_setg(errp, "memory cold/hot plug is not yet supported");
> > +        return;
> > +    }
> add comment here why it's needed.

Ok.

> 
> > +
> > +    pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), NULL,
> errp);
> maybe before calling this there probably should be check if acpi is enabled.
> 
> not sure if arm/virt board honors -no-acpi CLI option.

Ok. I will check this

Thanks,
Shameer
 
> > +}
> > +
> > +static void virt_memory_plug(HotplugHandler *hotplug_dev,
> > +                             DeviceState *dev, Error **errp)
> > +{
> > +    VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
> > +
> > +    pc_dimm_plug(PC_DIMM(dev), MACHINE(vms), NULL);
> > +
> > +}
> > +
> > +static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
> > +                                            DeviceState *dev,
> Error **errp)
> > +{
> > +    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> > +        virt_memory_pre_plug(hotplug_dev, dev, errp);
> > +    }
> > +}
> > +
> >  static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev,
> >                                          DeviceState *dev, Error
> **errp)
> >  {
> > @@ -1817,12 +1847,23 @@ static void
> virt_machine_device_plug_cb(HotplugHandler *hotplug_dev,
> >                                       SYS_BUS_DEVICE(dev));
> >          }
> >      }
> > +    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> > +        virt_memory_plug(hotplug_dev, dev, errp);
> > +    }
> > +}
> > +
> > +static void virt_machine_device_unplug_request_cb(HotplugHandler
> *hotplug_dev,
> > +                                          DeviceState *dev, Error
> **errp)
> > +{
> > +    error_setg(errp, "device unplug request for unsupported device"
> > +               " type: %s", object_get_typename(OBJECT(dev)));
> >  }
> >
> >  static HotplugHandler *virt_machine_get_hotplug_handler(MachineState
> *machine,
> >
> DeviceState *dev)
> >  {
> > -    if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE)) {
> > +    if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE) ||
> > +       (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM))) {
> >          return HOTPLUG_HANDLER(machine);
> >      }
> >
> > @@ -1886,7 +1927,9 @@ static void virt_machine_class_init(ObjectClass
> *oc, void *data)
> >      mc->kvm_type = virt_kvm_type;
> >      assert(!mc->get_hotplug_handler);
> >      mc->get_hotplug_handler = virt_machine_get_hotplug_handler;
> > +    hc->pre_plug = virt_machine_device_pre_plug_cb;
> >      hc->plug = virt_machine_device_plug_cb;
> > +    hc->unplug_request = virt_machine_device_unplug_request_cb;
> >  }
> >
> >  static void virt_instance_init(Object *obj)

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 4/8] hw/arm/virt: Add memory hotplug framework
@ 2019-05-03 12:47       ` Shameerali Kolothum Thodi
  0 siblings, 0 replies; 62+ messages in thread
From: Shameerali Kolothum Thodi @ 2019-05-03 12:47 UTC (permalink / raw)
  To: Igor Mammedov
  Cc: peter.maydell, sameo, shannon.zhaosl, ard.biesheuvel, qemu-devel,
	Linuxarm, eric.auger, qemu-arm, xuwei (O),
	sebastien.boeuf, lersek



> -----Original Message-----
> From: Igor Mammedov [mailto:imammedo@redhat.com]
> Sent: 02 May 2019 17:19
> To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>
> Cc: qemu-devel@nongnu.org; qemu-arm@nongnu.org;
> eric.auger@redhat.com; peter.maydell@linaro.org;
> shannon.zhaosl@gmail.com; sameo@linux.intel.com;
> sebastien.boeuf@intel.com; xuwei (O) <xuwei5@huawei.com>;
> lersek@redhat.com; ard.biesheuvel@linaro.org; Linuxarm
> <linuxarm@huawei.com>
> Subject: Re: [PATCH v4 4/8] hw/arm/virt: Add memory hotplug framework
> 
> On Tue, 9 Apr 2019 11:29:31 +0100
> Shameer Kolothum <shameerali.kolothum.thodi@huawei.com> wrote:
> 
> > From: Eric Auger <eric.auger@redhat.com>
> >
> > This patch adds the memory hot-plug/hot-unplug infrastructure
> > in machvirt. The device memory is not yet exposed to the Guest
> > either though DT or ACPI and hence both cold/hot plug of memory
> s/though/through/

Sure.

> > is explicitly disabled for now.
> >
> > Signed-off-by: Eric Auger <eric.auger@redhat.com>
> > Signed-off-by: Kwangwoo Lee <kwangwoo.lee@sk.com>
> > Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> > ---
> >  default-configs/arm-softmmu.mak |  3 +++
> >  hw/arm/virt.c                   | 45
> ++++++++++++++++++++++++++++++++++++++++-
> >  2 files changed, 47 insertions(+), 1 deletion(-)
> >
> > diff --git a/default-configs/arm-softmmu.mak
> b/default-configs/arm-softmmu.mak
> > index 613d19a..9f4b803 100644
> > --- a/default-configs/arm-softmmu.mak
> > +++ b/default-configs/arm-softmmu.mak
> > @@ -160,3 +160,6 @@ CONFIG_MUSICPAL=y
> >
> >  # for realview and versatilepb
> >  CONFIG_LSI_SCSI_PCI=y
> > +
> > +CONFIG_MEM_DEVICE=y
> > +CONFIG_DIMM=y
> > diff --git a/hw/arm/virt.c b/hw/arm/virt.c
> > index ce2664a..da516b3 100644
> > --- a/hw/arm/virt.c
> > +++ b/hw/arm/virt.c
> > @@ -61,6 +61,8 @@
> >  #include "hw/arm/smmuv3.h"
> >  #include "hw/acpi/acpi.h"
> >  #include "target/arm/internals.h"
> > +#include "hw/mem/pc-dimm.h"
> > +#include "hw/mem/nvdimm.h"
> >
> >  #define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \
> >      static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
> > @@ -1806,6 +1808,34 @@ static const CPUArchIdList
> *virt_possible_cpu_arch_ids(MachineState *ms)
> >      return ms->possible_cpus;
> >  }
> >
> > +static void virt_memory_pre_plug(HotplugHandler *hotplug_dev,
> DeviceState *dev,
> > +                                 Error **errp)
> > +{
> > +    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> > +        error_setg(errp, "memory cold/hot plug is not yet supported");
> > +        return;
> > +    }
> add comment here why it's needed.

Ok.

> 
> > +
> > +    pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), NULL,
> errp);
> maybe before calling this there probably should be check if acpi is enabled.
> 
> not sure if arm/virt board honors -no-acpi CLI option.

Ok. I will check this

Thanks,
Shameer
 
> > +}
> > +
> > +static void virt_memory_plug(HotplugHandler *hotplug_dev,
> > +                             DeviceState *dev, Error **errp)
> > +{
> > +    VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
> > +
> > +    pc_dimm_plug(PC_DIMM(dev), MACHINE(vms), NULL);
> > +
> > +}
> > +
> > +static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
> > +                                            DeviceState *dev,
> Error **errp)
> > +{
> > +    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> > +        virt_memory_pre_plug(hotplug_dev, dev, errp);
> > +    }
> > +}
> > +
> >  static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev,
> >                                          DeviceState *dev, Error
> **errp)
> >  {
> > @@ -1817,12 +1847,23 @@ static void
> virt_machine_device_plug_cb(HotplugHandler *hotplug_dev,
> >                                       SYS_BUS_DEVICE(dev));
> >          }
> >      }
> > +    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> > +        virt_memory_plug(hotplug_dev, dev, errp);
> > +    }
> > +}
> > +
> > +static void virt_machine_device_unplug_request_cb(HotplugHandler
> *hotplug_dev,
> > +                                          DeviceState *dev, Error
> **errp)
> > +{
> > +    error_setg(errp, "device unplug request for unsupported device"
> > +               " type: %s", object_get_typename(OBJECT(dev)));
> >  }
> >
> >  static HotplugHandler *virt_machine_get_hotplug_handler(MachineState
> *machine,
> >
> DeviceState *dev)
> >  {
> > -    if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE)) {
> > +    if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE) ||
> > +       (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM))) {
> >          return HOTPLUG_HANDLER(machine);
> >      }
> >
> > @@ -1886,7 +1927,9 @@ static void virt_machine_class_init(ObjectClass
> *oc, void *data)
> >      mc->kvm_type = virt_kvm_type;
> >      assert(!mc->get_hotplug_handler);
> >      mc->get_hotplug_handler = virt_machine_get_hotplug_handler;
> > +    hc->pre_plug = virt_machine_device_pre_plug_cb;
> >      hc->plug = virt_machine_device_plug_cb;
> > +    hc->unplug_request = virt_machine_device_unplug_request_cb;
> >  }
> >
> >  static void virt_instance_init(Object *obj)



^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 8/8] hw/arm/boot: Expose the PC-DIMM nodes in the DT
@ 2019-05-03 13:35         ` Shameerali Kolothum Thodi
  0 siblings, 0 replies; 62+ messages in thread
From: Shameerali Kolothum Thodi @ 2019-05-03 13:35 UTC (permalink / raw)
  To: Laszlo Ersek, qemu-devel, qemu-arm, eric.auger, imammedo
  Cc: peter.maydell, sameo, ard.biesheuvel, Linuxarm, shannon.zhaosl,
	sebastien.boeuf, xuwei (O)



> -----Original Message-----
> From: Linuxarm [mailto:linuxarm-bounces@huawei.com] On Behalf Of
> Shameerali Kolothum Thodi
> Sent: 10 April 2019 09:49
> To: Laszlo Ersek <lersek@redhat.com>; qemu-devel@nongnu.org;
> qemu-arm@nongnu.org; eric.auger@redhat.com; imammedo@redhat.com
> Cc: peter.maydell@linaro.org; sameo@linux.intel.com;
> ard.biesheuvel@linaro.org; Linuxarm <linuxarm@huawei.com>;
> shannon.zhaosl@gmail.com; sebastien.boeuf@intel.com; xuwei (O)
> <xuwei5@huawei.com>
> Subject: RE: [PATCH v4 8/8] hw/arm/boot: Expose the PC-DIMM nodes in the
> DT
> 
> 
> > -----Original Message-----
> > From: Laszlo Ersek [mailto:lersek@redhat.com]
> > Sent: 09 April 2019 16:09
> > To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>;
> > qemu-devel@nongnu.org; qemu-arm@nongnu.org; eric.auger@redhat.com;
> > imammedo@redhat.com
> > Cc: peter.maydell@linaro.org; shannon.zhaosl@gmail.com;
> > sameo@linux.intel.com; sebastien.boeuf@intel.com; xuwei (O)
> > <xuwei5@huawei.com>; ard.biesheuvel@linaro.org; Linuxarm
> > <linuxarm@huawei.com>
> > Subject: Re: [PATCH v4 8/8] hw/arm/boot: Expose the PC-DIMM nodes in the
> > DT
> >
> > On 04/09/19 12:29, Shameer Kolothum wrote:
> > > This patch adds memory nodes corresponding to PC-DIMM regions.
> > > This will enable support for cold plugged device memory for Guests
> > > with DT boot.
> > >
> > > Signed-off-by: Shameer Kolothum
> <shameerali.kolothum.thodi@huawei.com>
> > > Signed-off-by: Eric Auger <eric.auger@redhat.com>
> > > ---
> > >  hw/arm/boot.c | 42 ++++++++++++++++++++++++++++++++++++++++++
> > >  1 file changed, 42 insertions(+)
> > >
> > > diff --git a/hw/arm/boot.c b/hw/arm/boot.c
> > > index 8c840ba..150e1ed 100644
> > > --- a/hw/arm/boot.c
> > > +++ b/hw/arm/boot.c
> > > @@ -19,6 +19,7 @@
> > >  #include "sysemu/numa.h"
> > >  #include "hw/boards.h"
> > >  #include "hw/loader.h"
> > > +#include "hw/mem/memory-device.h"
> > >  #include "elf.h"
> > >  #include "sysemu/device_tree.h"
> > >  #include "qemu/config-file.h"
> > > @@ -538,6 +539,41 @@ static void fdt_add_psci_node(void *fdt)
> > >      qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn);
> > >  }
> > >
> > > +static int fdt_add_hotpluggable_memory_nodes(void *fdt,
> > > +                                             uint32_t acells,
> > uint32_t scells) {
> > > +    MemoryDeviceInfoList *info, *info_list = qmp_memory_device_list();
> > > +    MemoryDeviceInfo *mi;
> > > +    int ret = 0;
> > > +
> > > +    for (info = info_list; info != NULL; info = info->next) {
> > > +        mi = info->value;
> > > +        switch (mi->type) {
> > > +        case MEMORY_DEVICE_INFO_KIND_DIMM:
> > > +        {
> > > +            PCDIMMDeviceInfo *di = mi->u.dimm.data;
> > > +
> > > +            ret = fdt_add_memory_node(fdt, acells, di->addr, scells,
> > > +                                      di->size, di->node, true);
> > > +            if (ret) {
> > > +                fprintf(stderr,
> > > +                        "couldn't add PCDIMM
> /memory@%"PRIx64"
> > node\n",
> > > +                        di->addr);
> > > +                goto out;
> > > +            }
> > > +            break;
> > > +        }
> > > +        default:
> > > +            fprintf(stderr, "%s memory nodes are not yet supported\n",
> > > +                    MemoryDeviceInfoKind_str(mi->type));
> > > +            ret = -ENOENT;
> > > +            goto out;
> > > +        }
> > > +    }
> > > +out:
> > > +    qapi_free_MemoryDeviceInfoList(info_list);
> > > +    return ret;
> > > +}
> > > +
> > >  int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
> > >                   hwaddr addr_limit, AddressSpace *as)
> > >  {
> > > @@ -637,6 +673,12 @@ int arm_load_dtb(hwaddr addr, const struct
> > arm_boot_info *binfo,
> > >          }
> > >      }
> > >
> > > +    rc = fdt_add_hotpluggable_memory_nodes(fdt, acells, scells);
> > > +    if (rc < 0) {
> > > +        fprintf(stderr, "couldn't add hotpluggable memory nodes\n");
> > > +        goto fail;
> > > +    }
> > > +
> > >      rc = fdt_path_offset(fdt, "/chosen");
> > >      if (rc < 0) {
> > >          qemu_fdt_add_subnode(fdt, "/chosen");
> > >
> >
> >
> > Given patches #7 and #8, as I understand them, the firmware cannot
> > distinguish hotpluggable & present, from hotpluggable & absent. The
> firmware
> > can only skip both hotpluggable cases. That's fine in that the firmware will
> hog
> > neither type -- but is that OK for the OS as well, for both ACPI boot and DT
> > boot?
> 
> Right. This only handles the hotpluggable-and-present condition.
> 
> > Consider in particular the "hotpluggable & present, ACPI boot" case.
> Assuming
> > we modify the firmware to skip "hotpluggable" altogether, the UEFI memmap
> > will not include the range despite it being present at boot. Presumably, ACPI
> > will refer to the range somehow, however. Will that not confuse the OS?
> 
> From my testing so far, without patches #7 and #8(ie, no UEFI memmap entry),
> ACPI boots fine. I think ACPI only relies on aml and SRAT.
> 
> > When Igor raised this earlier, I suggested that hotpluggable-and-present
> > should be added by the firmware, but also allocated immediately, as
> > EfiBootServicesData type memory. This will prevent other drivers in the
> > firmware from allocating AcpiNVS or Reserved chunks from the same memory
> > range, the UEFI memmap will contain the range as EfiBootServicesData, and
> > then the OS can release that allocation in one go early during boot.
> 
> Ok. Agree that hotpluggable-and-present case it may make sense to make use
> of
> that memory rather than just hiding it altogether.
> 
> On another note, Does hotpluggable-and-absent case make any valid use case
> for
> a DT boot? I am not sure how we can hot-add memory in the case of DT boot
> later.
> I have not verified the sysfs probe interface yet and there are discussions of
> dropping
> that interface altogether.
> 
> > But this really has to be clarified from the Linux kernel's expectations. Please
> > formalize all of the following cases:
> 
> Sure. I will wait for suggestions here and work on it.

To continue the discussion on this, this is my proposal,

OS boot (DT/ACPI)  hotpluggable & ...  GetMemoryMap() should report as  DT/ACPI should report as
-----------------  ------------------  -------------------------------  ------------------------
DT                present           Normal memory             Report as normal DT memory node

DT                absent            Not reported                  Not reported.

ACPI              present            EfiBootServicesData memory  SRAT specifies the memory region
                                                               and device memory discovered
                                                               during Guest boot up through AML.

ACPI              absent             Not reported               SRAT specifies the memory region.
                                                               Supports hot add later.

Not sure I got all the scenarios correct here, please take a look and let me know.

Thanks,
Shameer

> 
> Thanks,
> Shameer
> 
> > OS boot (DT/ACPI)  hotpluggable & ...  GetMemoryMap() should report as
> > DT/ACPI should report as
> > -----------------  ------------------  -------------------------------  ------------------------
> > DT
> > present             ?                                ?
> > DT
> > absent              ?                                ?
> > ACPI
> > present             ?                                ?
> > ACPI
> > absent              ?                                ?
> >
> > Again, this table is dictated by Linux.
> >
> > Thanks
> > Laszlo
> _______________________________________________
> Linuxarm mailing list
> Linuxarm@huawei.com
> http://hulk.huawei.com/mailman/listinfo/linuxarm

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 8/8] hw/arm/boot: Expose the PC-DIMM nodes in the DT
@ 2019-05-03 13:35         ` Shameerali Kolothum Thodi
  0 siblings, 0 replies; 62+ messages in thread
From: Shameerali Kolothum Thodi @ 2019-05-03 13:35 UTC (permalink / raw)
  To: Laszlo Ersek, qemu-devel, qemu-arm, eric.auger, imammedo
  Cc: peter.maydell, sameo, ard.biesheuvel, Linuxarm, shannon.zhaosl,
	sebastien.boeuf, xuwei (O)



> -----Original Message-----
> From: Linuxarm [mailto:linuxarm-bounces@huawei.com] On Behalf Of
> Shameerali Kolothum Thodi
> Sent: 10 April 2019 09:49
> To: Laszlo Ersek <lersek@redhat.com>; qemu-devel@nongnu.org;
> qemu-arm@nongnu.org; eric.auger@redhat.com; imammedo@redhat.com
> Cc: peter.maydell@linaro.org; sameo@linux.intel.com;
> ard.biesheuvel@linaro.org; Linuxarm <linuxarm@huawei.com>;
> shannon.zhaosl@gmail.com; sebastien.boeuf@intel.com; xuwei (O)
> <xuwei5@huawei.com>
> Subject: RE: [PATCH v4 8/8] hw/arm/boot: Expose the PC-DIMM nodes in the
> DT
> 
> 
> > -----Original Message-----
> > From: Laszlo Ersek [mailto:lersek@redhat.com]
> > Sent: 09 April 2019 16:09
> > To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>;
> > qemu-devel@nongnu.org; qemu-arm@nongnu.org; eric.auger@redhat.com;
> > imammedo@redhat.com
> > Cc: peter.maydell@linaro.org; shannon.zhaosl@gmail.com;
> > sameo@linux.intel.com; sebastien.boeuf@intel.com; xuwei (O)
> > <xuwei5@huawei.com>; ard.biesheuvel@linaro.org; Linuxarm
> > <linuxarm@huawei.com>
> > Subject: Re: [PATCH v4 8/8] hw/arm/boot: Expose the PC-DIMM nodes in the
> > DT
> >
> > On 04/09/19 12:29, Shameer Kolothum wrote:
> > > This patch adds memory nodes corresponding to PC-DIMM regions.
> > > This will enable support for cold plugged device memory for Guests
> > > with DT boot.
> > >
> > > Signed-off-by: Shameer Kolothum
> <shameerali.kolothum.thodi@huawei.com>
> > > Signed-off-by: Eric Auger <eric.auger@redhat.com>
> > > ---
> > >  hw/arm/boot.c | 42 ++++++++++++++++++++++++++++++++++++++++++
> > >  1 file changed, 42 insertions(+)
> > >
> > > diff --git a/hw/arm/boot.c b/hw/arm/boot.c
> > > index 8c840ba..150e1ed 100644
> > > --- a/hw/arm/boot.c
> > > +++ b/hw/arm/boot.c
> > > @@ -19,6 +19,7 @@
> > >  #include "sysemu/numa.h"
> > >  #include "hw/boards.h"
> > >  #include "hw/loader.h"
> > > +#include "hw/mem/memory-device.h"
> > >  #include "elf.h"
> > >  #include "sysemu/device_tree.h"
> > >  #include "qemu/config-file.h"
> > > @@ -538,6 +539,41 @@ static void fdt_add_psci_node(void *fdt)
> > >      qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn);
> > >  }
> > >
> > > +static int fdt_add_hotpluggable_memory_nodes(void *fdt,
> > > +                                             uint32_t acells,
> > uint32_t scells) {
> > > +    MemoryDeviceInfoList *info, *info_list = qmp_memory_device_list();
> > > +    MemoryDeviceInfo *mi;
> > > +    int ret = 0;
> > > +
> > > +    for (info = info_list; info != NULL; info = info->next) {
> > > +        mi = info->value;
> > > +        switch (mi->type) {
> > > +        case MEMORY_DEVICE_INFO_KIND_DIMM:
> > > +        {
> > > +            PCDIMMDeviceInfo *di = mi->u.dimm.data;
> > > +
> > > +            ret = fdt_add_memory_node(fdt, acells, di->addr, scells,
> > > +                                      di->size, di->node, true);
> > > +            if (ret) {
> > > +                fprintf(stderr,
> > > +                        "couldn't add PCDIMM
> /memory@%"PRIx64"
> > node\n",
> > > +                        di->addr);
> > > +                goto out;
> > > +            }
> > > +            break;
> > > +        }
> > > +        default:
> > > +            fprintf(stderr, "%s memory nodes are not yet supported\n",
> > > +                    MemoryDeviceInfoKind_str(mi->type));
> > > +            ret = -ENOENT;
> > > +            goto out;
> > > +        }
> > > +    }
> > > +out:
> > > +    qapi_free_MemoryDeviceInfoList(info_list);
> > > +    return ret;
> > > +}
> > > +
> > >  int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
> > >                   hwaddr addr_limit, AddressSpace *as)
> > >  {
> > > @@ -637,6 +673,12 @@ int arm_load_dtb(hwaddr addr, const struct
> > arm_boot_info *binfo,
> > >          }
> > >      }
> > >
> > > +    rc = fdt_add_hotpluggable_memory_nodes(fdt, acells, scells);
> > > +    if (rc < 0) {
> > > +        fprintf(stderr, "couldn't add hotpluggable memory nodes\n");
> > > +        goto fail;
> > > +    }
> > > +
> > >      rc = fdt_path_offset(fdt, "/chosen");
> > >      if (rc < 0) {
> > >          qemu_fdt_add_subnode(fdt, "/chosen");
> > >
> >
> >
> > Given patches #7 and #8, as I understand them, the firmware cannot
> > distinguish hotpluggable & present, from hotpluggable & absent. The
> firmware
> > can only skip both hotpluggable cases. That's fine in that the firmware will
> hog
> > neither type -- but is that OK for the OS as well, for both ACPI boot and DT
> > boot?
> 
> Right. This only handles the hotpluggable-and-present condition.
> 
> > Consider in particular the "hotpluggable & present, ACPI boot" case.
> Assuming
> > we modify the firmware to skip "hotpluggable" altogether, the UEFI memmap
> > will not include the range despite it being present at boot. Presumably, ACPI
> > will refer to the range somehow, however. Will that not confuse the OS?
> 
> From my testing so far, without patches #7 and #8(ie, no UEFI memmap entry),
> ACPI boots fine. I think ACPI only relies on aml and SRAT.
> 
> > When Igor raised this earlier, I suggested that hotpluggable-and-present
> > should be added by the firmware, but also allocated immediately, as
> > EfiBootServicesData type memory. This will prevent other drivers in the
> > firmware from allocating AcpiNVS or Reserved chunks from the same memory
> > range, the UEFI memmap will contain the range as EfiBootServicesData, and
> > then the OS can release that allocation in one go early during boot.
> 
> Ok. Agree that hotpluggable-and-present case it may make sense to make use
> of
> that memory rather than just hiding it altogether.
> 
> On another note, Does hotpluggable-and-absent case make any valid use case
> for
> a DT boot? I am not sure how we can hot-add memory in the case of DT boot
> later.
> I have not verified the sysfs probe interface yet and there are discussions of
> dropping
> that interface altogether.
> 
> > But this really has to be clarified from the Linux kernel's expectations. Please
> > formalize all of the following cases:
> 
> Sure. I will wait for suggestions here and work on it.

To continue the discussion on this, this is my proposal,

OS boot (DT/ACPI)  hotpluggable & ...  GetMemoryMap() should report as  DT/ACPI should report as
-----------------  ------------------  -------------------------------  ------------------------
DT                present           Normal memory             Report as normal DT memory node

DT                absent            Not reported                  Not reported.

ACPI              present            EfiBootServicesData memory  SRAT specifies the memory region
                                                               and device memory discovered
                                                               during Guest boot up through AML.

ACPI              absent             Not reported               SRAT specifies the memory region.
                                                               Supports hot add later.

Not sure I got all the scenarios correct here, please take a look and let me know.

Thanks,
Shameer

> 
> Thanks,
> Shameer
> 
> > OS boot (DT/ACPI)  hotpluggable & ...  GetMemoryMap() should report as
> > DT/ACPI should report as
> > -----------------  ------------------  -------------------------------  ------------------------
> > DT
> > present             ?                                ?
> > DT
> > absent              ?                                ?
> > ACPI
> > present             ?                                ?
> > ACPI
> > absent              ?                                ?
> >
> > Again, this table is dictated by Linux.
> >
> > Thanks
> > Laszlo
> _______________________________________________
> Linuxarm mailing list
> Linuxarm@huawei.com
> http://hulk.huawei.com/mailman/listinfo/linuxarm


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 8/8] hw/arm/boot: Expose the PC-DIMM nodes in the DT
@ 2019-05-03 14:13           ` Laszlo Ersek
  0 siblings, 0 replies; 62+ messages in thread
From: Laszlo Ersek @ 2019-05-03 14:13 UTC (permalink / raw)
  To: Shameerali Kolothum Thodi, qemu-devel, qemu-arm, eric.auger, imammedo
  Cc: peter.maydell, sameo, ard.biesheuvel, Linuxarm, shannon.zhaosl,
	sebastien.boeuf, xuwei (O)

Hi Shameer,

On 05/03/19 15:35, Shameerali Kolothum Thodi wrote:
> 
> 
>> -----Original Message-----
>> From: Linuxarm [mailto:linuxarm-bounces@huawei.com] On Behalf Of
>> Shameerali Kolothum Thodi
>> Sent: 10 April 2019 09:49
>> To: Laszlo Ersek <lersek@redhat.com>; qemu-devel@nongnu.org;
>> qemu-arm@nongnu.org; eric.auger@redhat.com; imammedo@redhat.com
>> Cc: peter.maydell@linaro.org; sameo@linux.intel.com;
>> ard.biesheuvel@linaro.org; Linuxarm <linuxarm@huawei.com>;
>> shannon.zhaosl@gmail.com; sebastien.boeuf@intel.com; xuwei (O)
>> <xuwei5@huawei.com>
>> Subject: RE: [PATCH v4 8/8] hw/arm/boot: Expose the PC-DIMM nodes in the
>> DT
>>
>>
>>> -----Original Message-----
>>> From: Laszlo Ersek [mailto:lersek@redhat.com]
>>> Sent: 09 April 2019 16:09
>>> To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>;
>>> qemu-devel@nongnu.org; qemu-arm@nongnu.org; eric.auger@redhat.com;
>>> imammedo@redhat.com
>>> Cc: peter.maydell@linaro.org; shannon.zhaosl@gmail.com;
>>> sameo@linux.intel.com; sebastien.boeuf@intel.com; xuwei (O)
>>> <xuwei5@huawei.com>; ard.biesheuvel@linaro.org; Linuxarm
>>> <linuxarm@huawei.com>
>>> Subject: Re: [PATCH v4 8/8] hw/arm/boot: Expose the PC-DIMM nodes in the
>>> DT
>>>
>>> On 04/09/19 12:29, Shameer Kolothum wrote:
>>>> This patch adds memory nodes corresponding to PC-DIMM regions.
>>>> This will enable support for cold plugged device memory for Guests
>>>> with DT boot.
>>>>
>>>> Signed-off-by: Shameer Kolothum
>> <shameerali.kolothum.thodi@huawei.com>
>>>> Signed-off-by: Eric Auger <eric.auger@redhat.com>
>>>> ---
>>>>  hw/arm/boot.c | 42 ++++++++++++++++++++++++++++++++++++++++++
>>>>  1 file changed, 42 insertions(+)
>>>>
>>>> diff --git a/hw/arm/boot.c b/hw/arm/boot.c
>>>> index 8c840ba..150e1ed 100644
>>>> --- a/hw/arm/boot.c
>>>> +++ b/hw/arm/boot.c
>>>> @@ -19,6 +19,7 @@
>>>>  #include "sysemu/numa.h"
>>>>  #include "hw/boards.h"
>>>>  #include "hw/loader.h"
>>>> +#include "hw/mem/memory-device.h"
>>>>  #include "elf.h"
>>>>  #include "sysemu/device_tree.h"
>>>>  #include "qemu/config-file.h"
>>>> @@ -538,6 +539,41 @@ static void fdt_add_psci_node(void *fdt)
>>>>      qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn);
>>>>  }
>>>>
>>>> +static int fdt_add_hotpluggable_memory_nodes(void *fdt,
>>>> +                                             uint32_t acells,
>>> uint32_t scells) {
>>>> +    MemoryDeviceInfoList *info, *info_list = qmp_memory_device_list();
>>>> +    MemoryDeviceInfo *mi;
>>>> +    int ret = 0;
>>>> +
>>>> +    for (info = info_list; info != NULL; info = info->next) {
>>>> +        mi = info->value;
>>>> +        switch (mi->type) {
>>>> +        case MEMORY_DEVICE_INFO_KIND_DIMM:
>>>> +        {
>>>> +            PCDIMMDeviceInfo *di = mi->u.dimm.data;
>>>> +
>>>> +            ret = fdt_add_memory_node(fdt, acells, di->addr, scells,
>>>> +                                      di->size, di->node, true);
>>>> +            if (ret) {
>>>> +                fprintf(stderr,
>>>> +                        "couldn't add PCDIMM
>> /memory@%"PRIx64"
>>> node\n",
>>>> +                        di->addr);
>>>> +                goto out;
>>>> +            }
>>>> +            break;
>>>> +        }
>>>> +        default:
>>>> +            fprintf(stderr, "%s memory nodes are not yet supported\n",
>>>> +                    MemoryDeviceInfoKind_str(mi->type));
>>>> +            ret = -ENOENT;
>>>> +            goto out;
>>>> +        }
>>>> +    }
>>>> +out:
>>>> +    qapi_free_MemoryDeviceInfoList(info_list);
>>>> +    return ret;
>>>> +}
>>>> +
>>>>  int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
>>>>                   hwaddr addr_limit, AddressSpace *as)
>>>>  {
>>>> @@ -637,6 +673,12 @@ int arm_load_dtb(hwaddr addr, const struct
>>> arm_boot_info *binfo,
>>>>          }
>>>>      }
>>>>
>>>> +    rc = fdt_add_hotpluggable_memory_nodes(fdt, acells, scells);
>>>> +    if (rc < 0) {
>>>> +        fprintf(stderr, "couldn't add hotpluggable memory nodes\n");
>>>> +        goto fail;
>>>> +    }
>>>> +
>>>>      rc = fdt_path_offset(fdt, "/chosen");
>>>>      if (rc < 0) {
>>>>          qemu_fdt_add_subnode(fdt, "/chosen");
>>>>
>>>
>>>
>>> Given patches #7 and #8, as I understand them, the firmware cannot
>>> distinguish hotpluggable & present, from hotpluggable & absent. The
>> firmware
>>> can only skip both hotpluggable cases. That's fine in that the firmware will
>> hog
>>> neither type -- but is that OK for the OS as well, for both ACPI boot and DT
>>> boot?
>>
>> Right. This only handles the hotpluggable-and-present condition.
>>
>>> Consider in particular the "hotpluggable & present, ACPI boot" case.
>> Assuming
>>> we modify the firmware to skip "hotpluggable" altogether, the UEFI memmap
>>> will not include the range despite it being present at boot. Presumably, ACPI
>>> will refer to the range somehow, however. Will that not confuse the OS?
>>
>> From my testing so far, without patches #7 and #8(ie, no UEFI memmap entry),
>> ACPI boots fine. I think ACPI only relies on aml and SRAT.
>>
>>> When Igor raised this earlier, I suggested that hotpluggable-and-present
>>> should be added by the firmware, but also allocated immediately, as
>>> EfiBootServicesData type memory. This will prevent other drivers in the
>>> firmware from allocating AcpiNVS or Reserved chunks from the same memory
>>> range, the UEFI memmap will contain the range as EfiBootServicesData, and
>>> then the OS can release that allocation in one go early during boot.
>>
>> Ok. Agree that hotpluggable-and-present case it may make sense to make use
>> of
>> that memory rather than just hiding it altogether.
>>
>> On another note, Does hotpluggable-and-absent case make any valid use case
>> for
>> a DT boot? I am not sure how we can hot-add memory in the case of DT boot
>> later.
>> I have not verified the sysfs probe interface yet and there are discussions of
>> dropping
>> that interface altogether.
>>
>>> But this really has to be clarified from the Linux kernel's expectations. Please
>>> formalize all of the following cases:
>>
>> Sure. I will wait for suggestions here and work on it.
> 
> To continue the discussion on this, this is my proposal,
> 
> [...]

I didn't miss your last update, on 10 April. The reason I didn't respond
then was that, the table that you create here, needs to be approved by
Linux developers. In other words, the table should summarize how Linux
expects DT/ACPI to look, for the given use cases. It's not something
that I can comment on. The requirements come from Linux, and we should
attempt (in QEMU and the fw) to satisfy them.

If those use cases / requirements haven't been *designed* in Linux, in
the first place, then the discussion belongs even more on a kernel
development list. (I really can't say what Linux *should* expect, and
even if I had input on that, discussing it *just* on qemu-devel would be
futile.)

I mean, considering ACPI and the UEFI memmap at least, can we take
examples from the physical world (I guess x86 too)? What does Linux (and
maybe Windows) expect wrt. hotpluggable memory areas, in ACPI and in the
UEFI memmap?

I find it hard to believe that these are such use cases that we have to
*invent* now. It seems more likely that OSes already handle these use
cases, they have expectations, and we should *collect* them.

Thanks
Laszlo

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 8/8] hw/arm/boot: Expose the PC-DIMM nodes in the DT
@ 2019-05-03 14:13           ` Laszlo Ersek
  0 siblings, 0 replies; 62+ messages in thread
From: Laszlo Ersek @ 2019-05-03 14:13 UTC (permalink / raw)
  To: Shameerali Kolothum Thodi, qemu-devel, qemu-arm, eric.auger, imammedo
  Cc: peter.maydell, sameo, ard.biesheuvel, Linuxarm, shannon.zhaosl,
	sebastien.boeuf, xuwei (O)

Hi Shameer,

On 05/03/19 15:35, Shameerali Kolothum Thodi wrote:
> 
> 
>> -----Original Message-----
>> From: Linuxarm [mailto:linuxarm-bounces@huawei.com] On Behalf Of
>> Shameerali Kolothum Thodi
>> Sent: 10 April 2019 09:49
>> To: Laszlo Ersek <lersek@redhat.com>; qemu-devel@nongnu.org;
>> qemu-arm@nongnu.org; eric.auger@redhat.com; imammedo@redhat.com
>> Cc: peter.maydell@linaro.org; sameo@linux.intel.com;
>> ard.biesheuvel@linaro.org; Linuxarm <linuxarm@huawei.com>;
>> shannon.zhaosl@gmail.com; sebastien.boeuf@intel.com; xuwei (O)
>> <xuwei5@huawei.com>
>> Subject: RE: [PATCH v4 8/8] hw/arm/boot: Expose the PC-DIMM nodes in the
>> DT
>>
>>
>>> -----Original Message-----
>>> From: Laszlo Ersek [mailto:lersek@redhat.com]
>>> Sent: 09 April 2019 16:09
>>> To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>;
>>> qemu-devel@nongnu.org; qemu-arm@nongnu.org; eric.auger@redhat.com;
>>> imammedo@redhat.com
>>> Cc: peter.maydell@linaro.org; shannon.zhaosl@gmail.com;
>>> sameo@linux.intel.com; sebastien.boeuf@intel.com; xuwei (O)
>>> <xuwei5@huawei.com>; ard.biesheuvel@linaro.org; Linuxarm
>>> <linuxarm@huawei.com>
>>> Subject: Re: [PATCH v4 8/8] hw/arm/boot: Expose the PC-DIMM nodes in the
>>> DT
>>>
>>> On 04/09/19 12:29, Shameer Kolothum wrote:
>>>> This patch adds memory nodes corresponding to PC-DIMM regions.
>>>> This will enable support for cold plugged device memory for Guests
>>>> with DT boot.
>>>>
>>>> Signed-off-by: Shameer Kolothum
>> <shameerali.kolothum.thodi@huawei.com>
>>>> Signed-off-by: Eric Auger <eric.auger@redhat.com>
>>>> ---
>>>>  hw/arm/boot.c | 42 ++++++++++++++++++++++++++++++++++++++++++
>>>>  1 file changed, 42 insertions(+)
>>>>
>>>> diff --git a/hw/arm/boot.c b/hw/arm/boot.c
>>>> index 8c840ba..150e1ed 100644
>>>> --- a/hw/arm/boot.c
>>>> +++ b/hw/arm/boot.c
>>>> @@ -19,6 +19,7 @@
>>>>  #include "sysemu/numa.h"
>>>>  #include "hw/boards.h"
>>>>  #include "hw/loader.h"
>>>> +#include "hw/mem/memory-device.h"
>>>>  #include "elf.h"
>>>>  #include "sysemu/device_tree.h"
>>>>  #include "qemu/config-file.h"
>>>> @@ -538,6 +539,41 @@ static void fdt_add_psci_node(void *fdt)
>>>>      qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn);
>>>>  }
>>>>
>>>> +static int fdt_add_hotpluggable_memory_nodes(void *fdt,
>>>> +                                             uint32_t acells,
>>> uint32_t scells) {
>>>> +    MemoryDeviceInfoList *info, *info_list = qmp_memory_device_list();
>>>> +    MemoryDeviceInfo *mi;
>>>> +    int ret = 0;
>>>> +
>>>> +    for (info = info_list; info != NULL; info = info->next) {
>>>> +        mi = info->value;
>>>> +        switch (mi->type) {
>>>> +        case MEMORY_DEVICE_INFO_KIND_DIMM:
>>>> +        {
>>>> +            PCDIMMDeviceInfo *di = mi->u.dimm.data;
>>>> +
>>>> +            ret = fdt_add_memory_node(fdt, acells, di->addr, scells,
>>>> +                                      di->size, di->node, true);
>>>> +            if (ret) {
>>>> +                fprintf(stderr,
>>>> +                        "couldn't add PCDIMM
>> /memory@%"PRIx64"
>>> node\n",
>>>> +                        di->addr);
>>>> +                goto out;
>>>> +            }
>>>> +            break;
>>>> +        }
>>>> +        default:
>>>> +            fprintf(stderr, "%s memory nodes are not yet supported\n",
>>>> +                    MemoryDeviceInfoKind_str(mi->type));
>>>> +            ret = -ENOENT;
>>>> +            goto out;
>>>> +        }
>>>> +    }
>>>> +out:
>>>> +    qapi_free_MemoryDeviceInfoList(info_list);
>>>> +    return ret;
>>>> +}
>>>> +
>>>>  int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
>>>>                   hwaddr addr_limit, AddressSpace *as)
>>>>  {
>>>> @@ -637,6 +673,12 @@ int arm_load_dtb(hwaddr addr, const struct
>>> arm_boot_info *binfo,
>>>>          }
>>>>      }
>>>>
>>>> +    rc = fdt_add_hotpluggable_memory_nodes(fdt, acells, scells);
>>>> +    if (rc < 0) {
>>>> +        fprintf(stderr, "couldn't add hotpluggable memory nodes\n");
>>>> +        goto fail;
>>>> +    }
>>>> +
>>>>      rc = fdt_path_offset(fdt, "/chosen");
>>>>      if (rc < 0) {
>>>>          qemu_fdt_add_subnode(fdt, "/chosen");
>>>>
>>>
>>>
>>> Given patches #7 and #8, as I understand them, the firmware cannot
>>> distinguish hotpluggable & present, from hotpluggable & absent. The
>> firmware
>>> can only skip both hotpluggable cases. That's fine in that the firmware will
>> hog
>>> neither type -- but is that OK for the OS as well, for both ACPI boot and DT
>>> boot?
>>
>> Right. This only handles the hotpluggable-and-present condition.
>>
>>> Consider in particular the "hotpluggable & present, ACPI boot" case.
>> Assuming
>>> we modify the firmware to skip "hotpluggable" altogether, the UEFI memmap
>>> will not include the range despite it being present at boot. Presumably, ACPI
>>> will refer to the range somehow, however. Will that not confuse the OS?
>>
>> From my testing so far, without patches #7 and #8(ie, no UEFI memmap entry),
>> ACPI boots fine. I think ACPI only relies on aml and SRAT.
>>
>>> When Igor raised this earlier, I suggested that hotpluggable-and-present
>>> should be added by the firmware, but also allocated immediately, as
>>> EfiBootServicesData type memory. This will prevent other drivers in the
>>> firmware from allocating AcpiNVS or Reserved chunks from the same memory
>>> range, the UEFI memmap will contain the range as EfiBootServicesData, and
>>> then the OS can release that allocation in one go early during boot.
>>
>> Ok. Agree that hotpluggable-and-present case it may make sense to make use
>> of
>> that memory rather than just hiding it altogether.
>>
>> On another note, Does hotpluggable-and-absent case make any valid use case
>> for
>> a DT boot? I am not sure how we can hot-add memory in the case of DT boot
>> later.
>> I have not verified the sysfs probe interface yet and there are discussions of
>> dropping
>> that interface altogether.
>>
>>> But this really has to be clarified from the Linux kernel's expectations. Please
>>> formalize all of the following cases:
>>
>> Sure. I will wait for suggestions here and work on it.
> 
> To continue the discussion on this, this is my proposal,
> 
> [...]

I didn't miss your last update, on 10 April. The reason I didn't respond
then was that, the table that you create here, needs to be approved by
Linux developers. In other words, the table should summarize how Linux
expects DT/ACPI to look, for the given use cases. It's not something
that I can comment on. The requirements come from Linux, and we should
attempt (in QEMU and the fw) to satisfy them.

If those use cases / requirements haven't been *designed* in Linux, in
the first place, then the discussion belongs even more on a kernel
development list. (I really can't say what Linux *should* expect, and
even if I had input on that, discussing it *just* on qemu-devel would be
futile.)

I mean, considering ACPI and the UEFI memmap at least, can we take
examples from the physical world (I guess x86 too)? What does Linux (and
maybe Windows) expect wrt. hotpluggable memory areas, in ACPI and in the
UEFI memmap?

I find it hard to believe that these are such use cases that we have to
*invent* now. It seems more likely that OSes already handle these use
cases, they have expectations, and we should *collect* them.

Thanks
Laszlo


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
@ 2019-05-03 15:10         ` Igor Mammedov
  0 siblings, 0 replies; 62+ messages in thread
From: Igor Mammedov @ 2019-05-03 15:10 UTC (permalink / raw)
  To: Shameerali Kolothum Thodi
  Cc: qemu-devel, qemu-arm, eric.auger, peter.maydell, shannon.zhaosl,
	sameo, sebastien.boeuf, xuwei (O),
	lersek, ard.biesheuvel, Linuxarm

On Fri, 3 May 2019 12:45:52 +0000
Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com> wrote:

> Hi Igor,
> 
> > -----Original Message-----
> > From: Igor Mammedov [mailto:imammedo@redhat.com]
> > Sent: 02 May 2019 17:13
> > To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>
> > Cc: qemu-devel@nongnu.org; qemu-arm@nongnu.org;
> > eric.auger@redhat.com; peter.maydell@linaro.org;
> > shannon.zhaosl@gmail.com; sameo@linux.intel.com;
> > sebastien.boeuf@intel.com; xuwei (O) <xuwei5@huawei.com>;
> > lersek@redhat.com; ard.biesheuvel@linaro.org; Linuxarm
> > <linuxarm@huawei.com>
> > Subject: Re: [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
> > 
> > On Tue, 9 Apr 2019 11:29:30 +0100
> > Shameer Kolothum <shameerali.kolothum.thodi@huawei.com> wrote:
> > 
> > > From: Samuel Ortiz <sameo@linux.intel.com>
> > >
> > > The ACPI Generic Event Device (GED) is a hardware-reduced specific
> > > device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> > > including the hotplug ones.This patch generates the AML code that
> > > defines GEDs.
> > >
> > > Platforms need to specify their own GedEvent array to describe what
> > > kind of events they want to support through GED.  Also this uses a
> > > a single interrupt for the  GED device, relying on IO memory region
> > > to communicate the type of device affected by the interrupt. This
> > > way, we can support up to 32 events with a unique interrupt.
> > >
> > > This supports only memory hotplug for now.
> > >
> > > Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
> > > Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
> > > Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> > > ---
> > >  hw/acpi/Kconfig                        |   4 +
> > >  hw/acpi/Makefile.objs                  |   1 +
> > >  hw/acpi/generic_event_device.c         | 311
> > +++++++++++++++++++++++++++++++++
> > >  include/hw/acpi/generic_event_device.h | 121 +++++++++++++
> > >  4 files changed, 437 insertions(+)
> > >  create mode 100644 hw/acpi/generic_event_device.c
> > >  create mode 100644 include/hw/acpi/generic_event_device.h
> > >
> > > diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig
> > > index eca3bee..01a8b41 100644
> > > --- a/hw/acpi/Kconfig
> > > +++ b/hw/acpi/Kconfig
> > > @@ -27,3 +27,7 @@ config ACPI_VMGENID
> > >      bool
> > >      default y
> > >      depends on PC
> > > +
> > > +config ACPI_HW_REDUCED
> > > +    bool
> > > +    depends on ACPI
> > > diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
> > > index 2d46e37..b753232 100644
> > > --- a/hw/acpi/Makefile.objs
> > > +++ b/hw/acpi/Makefile.objs
> > > @@ -6,6 +6,7 @@ common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) +=
> > memory_hotplug.o
> > >  common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu.o
> > >  common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
> > >  common-obj-$(CONFIG_ACPI_VMGENID) += vmgenid.o
> > > +common-obj-$(CONFIG_ACPI_HW_REDUCED) += generic_event_device.o
> > >  common-obj-$(call lnot,$(CONFIG_ACPI_X86)) += acpi-stub.o
> > >
> > >  common-obj-y += acpi_interface.o
> > > diff --git a/hw/acpi/generic_event_device.c
> > b/hw/acpi/generic_event_device.c
> > > new file mode 100644
> > > index 0000000..856ca04
> > > --- /dev/null
> > > +++ b/hw/acpi/generic_event_device.c
> > > @@ -0,0 +1,311 @@
> > > +/*
> > > + *
> > > + * Copyright (c) 2018 Intel Corporation
> > > + *
> > > + * This program is free software; you can redistribute it and/or modify it
> > > + * under the terms and conditions of the GNU General Public License,
> > > + * version 2 or later, as published by the Free Software Foundation.
> > > + *
> > > + * This program is distributed in the hope it will be useful, but WITHOUT
> > > + * ANY WARRANTY; without even the implied warranty of
> > MERCHANTABILITY or
> > > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
> > License for
> > > + * more details.
> > > + *
> > > + * You should have received a copy of the GNU General Public License along
> > with
> > > + * this program.  If not, see <http://www.gnu.org/licenses/>.
> > > + */
> > > +
> > > +#include "qemu/osdep.h"
> > > +#include "qapi/error.h"
> > > +#include "exec/address-spaces.h"
> > > +#include "hw/sysbus.h"
> > > +#include "hw/acpi/acpi.h"
> > > +#include "hw/acpi/generic_event_device.h"
> > > +#include "hw/mem/pc-dimm.h"
> > > +
> > > +static Aml *ged_event_aml(const GedEvent *event)
> > > +{
> > > +
> > > +    if (!event) {
> > In general, I prefer to check condition for calling something before doing call.
> > This way one can see in caller why and what is called, which is more clear.
> 
> Ok. I will move it then.
> 
> > 
> > > +        return NULL;
> > > +    }
> > > +
> > > +    switch (event->event) {
> > > +    case GED_MEMORY_HOTPLUG:
> > > +        /* We run a complete memory SCAN when getting a memory
> > hotplug event */
> > > +        return aml_call0(MEMORY_DEVICES_CONTAINER "."
> > MEMORY_SLOT_SCAN_METHOD);
> > > +    default:
> > > +        break;
> > > +    }
> > > +
> > > +    return NULL;
> > > +}
> > > +
> > > +/*
> > > + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
> > > + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> > > + * including the hotplug ones. Platforms need to specify their own
> > > + * GedEvent array to describe what kind of events they want to support
> > > + * through GED. This routine uses a single interrupt for the GED device,
> > > + * relying on IO memory region to communicate the type of device
> > > + * affected by the interrupt. This way, we can support up to 32 events
> > > + * with a unique interrupt.
> > > + */
> > > +void build_ged_aml(Aml *table, const char *name, HotplugHandler
> > *hotplug_dev,
> > > +                   uint32_t ged_irq, AmlRegionSpace rs)
> > > +{
> > > +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> > > +    GedEvent *ged_events = s->ged_events;
> > > +    Aml *crs = aml_resource_template();
> > > +    Aml *evt, *field;
> > > +    Aml *dev = aml_device("%s", name);
> > > +    Aml *irq_sel = aml_local(0);
> > > +    Aml *isel = aml_name(AML_GED_IRQ_SEL);
> > > +    uint32_t i;
> > > +
> > > +    if (!s->ged_base || !ged_events || !s->ged_events_size) {
> > I'd move it up to the caller, it would be obvious right there when
> > function should be called.
> 
> Ok. 
>  
> > > +        return;
> > > +    }
> > > +
> > > +    /* _CRS interrupt */
> > > +    aml_append(crs, aml_interrupt(AML_CONSUMER, AML_EDGE,
> > AML_ACTIVE_HIGH,
> > > +                                  AML_EXCLUSIVE, &ged_irq, 1));
> > > +    /*
> > > +     * For each GED event we:
> > > +     * - Add an interrupt to the CRS section.
> > > +     * - Add a conditional block for each event, inside a while loop.
> > > +     *   This is semantically equivalent to a switch/case implementation.
> > > +     */
> > > +    evt = aml_method("_EVT", 1, AML_SERIALIZED);
> > > +    {
> > > +        Aml *ged_aml;
> > > +        Aml *if_ctx;
> > > +
> > > +        /* Local0 = ISEL */
> > > +        aml_append(evt, aml_store(isel, irq_sel));
> > > +
> > > +        /*
> > > +         * Here we want to call a method for each supported GED event
> > type.
> > > +         * The resulting ASL code looks like:
> > > +         *
> > > +         * Local0 = ISEL
> > > +         * If ((Local0 & irq0) == irq0)
> > > +         * {
> > > +         *     MethodEvent0()
> > > +         * }
> > > +         *
> > > +         * If ((Local0 & irq1) == irq1)
> > > +         * {
> > > +         *     MethodEvent1()
> > > +         * }
> > > +         * ...
> > > +         */
> > Well, I'm confused.
> > do we actually use multiple IRQs or we use only one + MMIO for event type?
> 
> It is one irq + MMIO. I will change the comment block something like this,

change corresponding variable names as well

> 
>     Local0 = ISEL
>     If ((Local0 & One) == One)
>     {
>         MethodEvent1()
>     }
> 
>     If ((Local0 & 0x02) == 0x02)
>     {
>         MethodEvent2()
>     }
>     ...
> 
> > 
> > > +        for (i = 0; i < s->ged_events_size; i++) {
> > 
> > > +            ged_aml = ged_event_aml(&ged_events[i]);
> > > +            if (!ged_aml) {
> > > +                continue;
> > > +            }
> > I'd get rid of ged_event_aml replace it with more 'switch':
> >    for (i,...)
> >        if_ctx = aml_if(...)
> >        switch (event)
> >           case GED_MEMORY_HOTPLUG:
> >                aml_append(if_ctx,
> > aml_call0(MEMORY_DEVICES_CONTAINER "."
> > MEMORY_SLOT_SCAN_METHOD))
> >                break
> >           default:
> >                about(); // make sure that a newly added events have a
> > handler
> 
> Ok. I will change this.
> 
> > 
> > > +
> > > +            /* If ((Local1 == irq))*/
> > > +            if_ctx = aml_if(aml_equal(aml_and(irq_sel,
> > > +
> > aml_int(ged_events[i].selector), NULL),
> > > +
> > aml_int(ged_events[i].selector)));
> > > +            {
> > > +                /* AML for this specific type of event */
> > > +                aml_append(if_ctx), ged_aml);
> > > +            }
> > > +
> > > +            /*
> > > +             * We append the first "if" to the "while" context.
> > > +             * Other "if"s will be "elseif"s.
> > > +             */
> > > +            aml_append(evt, if_ctx);
> > > +        }
> > > +    }
> > > +
> > 
> > > +    aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0013")));
> > > +    aml_append(dev, aml_name_decl("_UID", aml_string(GED_DEVICE)));
> > > +    aml_append(dev, aml_name_decl("_CRS", crs));
> > > +
> > > +    /* Append IO region */
> > > +    aml_append(dev, aml_operation_region(AML_GED_IRQ_REG, rs,
> > > +               aml_int(s->ged_base + ACPI_GED_IRQ_SEL_OFFSET),
> > > +               ACPI_GED_IRQ_SEL_LEN));
> > > +    field = aml_field(AML_GED_IRQ_REG, AML_DWORD_ACC,
> > AML_NOLOCK,
> > > +                      AML_WRITE_AS_ZEROS);
> > > +    aml_append(field, aml_named_field(AML_GED_IRQ_SEL,
> > > +                                      ACPI_GED_IRQ_SEL_LEN *
> > 8));
> > > +    aml_append(dev, field);
> > 
> > I'd move it up above EVT() method, so it would be clear from the begging
> > for what device we are building AML
> 
> Ok.
>  
> > 
> > > +    /* Append _EVT method */
> > > +    aml_append(dev, evt);
> > > +
> > > +    aml_append(table, dev);
> > > +}
> > > +
> > > +/* Memory read by the GED _EVT AML dynamic method */
> > > +static uint64_t ged_read(void *opaque, hwaddr addr, unsigned size)
> > > +{
> > > +    uint64_t val = 0;
> > > +    GEDState *ged_st = opaque;
> > > +
> > > +    switch (addr) {
> > > +    case ACPI_GED_IRQ_SEL_OFFSET:
> > > +        /* Read the selector value and reset it */
> > > +        qemu_mutex_lock(&ged_st->lock);
> > > +        val = ged_st->sel;
> > > +        ged_st->sel = 0;
> > > +        qemu_mutex_unlock(&ged_st->lock);
> > > +        break;
> > > +    default:
> > > +        break;
> > > +    }
> > > +
> > > +    return val;
> > > +}
> > > +
> > > +/* Nothing is expected to be written to the GED memory region */
> > > +static void ged_write(void *opaque, hwaddr addr, uint64_t data,
> > > +                      unsigned int size)
> > > +{
> > > +}
> > > +
> > > +static const MemoryRegionOps ged_ops = {
> > > +    .read = ged_read,
> > > +    .write = ged_write,
> > > +    .endianness = DEVICE_LITTLE_ENDIAN,
> > > +    .valid = {
> > > +        .min_access_size = 4,
> > > +        .max_access_size = 4,
> > > +    },
> > > +};
> > > +
> > > +static void acpi_ged_event(GEDState *ged_st, uint32_t ged_irq_sel)
> > > +{
> > > +    /*
> > > +     * Set the GED IRQ selector to the expected device type value. This
> > > +     * way, the ACPI method will be able to trigger the right code based
> > > +     * on a unique IRQ.
> > > +     */
> > > +    qemu_mutex_lock(&ged_st->lock);
> > > +    ged_st->sel = ged_irq_sel;
> > what if there are 2 events with different sel value and 2nd event happens
> > before guesr read the first one?
> 
> This was previously,
>    ged_st->sel |= ged_irq_sel;
> 
> and was changed based on the assumption that there won't be any multiple
> events. But I think the scenario above is right. I will change it back so that 
> we won't overwrite.
so it was bitmap, then handling it as a DWORD in AML could be wrong
and AML part probably should be changed to accommodate it.

> 
> > 
> > > +    qemu_mutex_unlock(&ged_st->lock);
> > > +
> > > +    /* Trigger the event by sending an interrupt to the guest. */
> > > +    qemu_irq_pulse(ged_st->gsi[ged_st->irq]);
> > > +}
> > > +
> > > +static void acpi_ged_init(MemoryRegion *as, DeviceState *dev, GEDState
> > *ged_st)
> > > +{
> > > +    AcpiGedState *s = ACPI_GED(dev);
> > > +
> > > +    assert(s->ged_base);
> > > +
> > > +    ged_st->irq = s->ged_irq;
> > > +    ged_st->gsi = s->gsi;
> > > +    qemu_mutex_init(&ged_st->lock);
> > > +    memory_region_init_io(&ged_st->io, OBJECT(dev), &ged_ops, ged_st,
> > > +                          "acpi-ged-event", ACPI_GED_REG_LEN);
> > > +    memory_region_add_subregion(as, s->ged_base, &ged_st->io);
> > > +}
> > > +
> > > +static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
> > > +                                    DeviceState *dev, Error **errp)
> > > +{
> > > +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> > > +
> > > +    if (s->memhp_state.is_enabled &&
> > > +        object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> > > +            acpi_memory_plug_cb(hotplug_dev, &s->memhp_state,
> > > +                                dev, errp);
> > > +    } else {
> > > +        error_setg(errp, "virt: device plug request for unsupported
> > device"
> > > +                   " type: %s", object_get_typename(OBJECT(dev)));
> > > +    }
> > > +}
> > > +
> > > +static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits
> > ev)
> > > +{
> > > +    AcpiGedState *s = ACPI_GED(adev);
> > > +    uint32_t sel;
> > > +
> > > +    if (ev & ACPI_MEMORY_HOTPLUG_STATUS) {
> > > +        sel = ACPI_GED_IRQ_SEL_MEM;
> > > +    } else {
> > > +        /* Unknown event. Return without generating interrupt. */
> > > +        return;
> > > +    }
> > > +
> > > +    /*
> > > +     * We inject the hotplug interrupt. The IRQ selector will make
> > > +     * the difference from the ACPI table.
> > > +     */
> > > +    acpi_ged_event(&s->ged_state, sel);
> > > +}
> > > +
> > > +static void acpi_ged_device_realize(DeviceState *dev, Error **errp)
> > > +{
> > > +    AcpiGedState *s = ACPI_GED(dev);
> > > +
> > > +    if (s->memhp_state.is_enabled) {
> > > +        acpi_memory_hotplug_init(get_system_memory(), OBJECT(dev),
> > > +                                 &s->memhp_state,
> > > +                                 s->memhp_base);
> > > +        acpi_ged_init(get_system_memory(), dev, &s->ged_state);
> > > +    }
> > > +}
> > > +
> > > +static Property acpi_ged_properties[] = {
> > > +    /*
> > > +     * Memory hotplug base address is a property of GED here,
> > > +     * because GED handles memory hotplug event and
> > MEMORY_HOTPLUG_DEVICE
> > > +     * gets initialized when GED device is realized.
> > > +     */
> > > +    DEFINE_PROP_UINT64("memhp-base", AcpiGedState, memhp_base,
> > 0),
> > > +    DEFINE_PROP_BOOL("memory-hotplug-support", AcpiGedState,
> > > +                     memhp_state.is_enabled, true),
> > > +    DEFINE_PROP_PTR("gsi", AcpiGedState, gsi),
> > 
> > PTR shouldn't be used in new code, look at object_property_add_link() & co
> 
> Ok. I will take a look at that.
>  
> > 
> > > +    DEFINE_PROP_UINT64("ged-base", AcpiGedState, ged_base, 0),
> > > +    DEFINE_PROP_UINT32("ged-irq", AcpiGedState, ged_irq, 0),
> > > +    DEFINE_PROP_PTR("ged-events", AcpiGedState, ged_events),
> > > +    DEFINE_PROP_UINT32("ged-events-size", AcpiGedState,
> > ged_events_size, 0),
> > > +    DEFINE_PROP_END_OF_LIST(),
> > > +};
> > > +
> > > +static void acpi_ged_class_init(ObjectClass *class, void *data)
> > > +{
> > > +    DeviceClass *dc = DEVICE_CLASS(class);
> > > +    HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class);
> > > +    AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class);
> > > +
> > > +    dc->desc = "ACPI";
> > > +    dc->props = acpi_ged_properties;
> > > +    dc->realize = acpi_ged_device_realize;
> > > +
> > > +    /* Reason: pointer properties "gsi" and "ged_events" */
> > > +    dc->user_creatable = false;
> > > +
> > > +    hc->plug = acpi_ged_device_plug_cb;
> > > +
> > > +    adevc->send_event = acpi_ged_send_event;
> > > +}
> > > +
> > > +static const TypeInfo acpi_ged_info = {
> > > +    .name          = TYPE_ACPI_GED,
> > > +    .parent        = TYPE_DEVICE,
> > > +    .instance_size = sizeof(AcpiGedState),
> > > +    .class_init    = acpi_ged_class_init,
> > > +    .interfaces = (InterfaceInfo[]) {
> > > +        { TYPE_HOTPLUG_HANDLER },
> > > +        { TYPE_ACPI_DEVICE_IF },
> > > +        { }
> > > +    }
> > > +};
> > > +
> > > +static void acpi_ged_register_types(void)
> > > +{
> > > +    type_register_static(&acpi_ged_info);
> > > +}
> > > +
> > > +type_init(acpi_ged_register_types)
> > > diff --git a/include/hw/acpi/generic_event_device.h
> > b/include/hw/acpi/generic_event_device.h
> > > new file mode 100644
> > > index 0000000..9c840d8
> > > --- /dev/null
> > > +++ b/include/hw/acpi/generic_event_device.h
> > > @@ -0,0 +1,121 @@
> > > +/*
> > > + *
> > > + * Copyright (c) 2018 Intel Corporation
> > > + *
> > > + * This program is free software; you can redistribute it and/or modify it
> > > + * under the terms and conditions of the GNU General Public License,
> > > + * version 2 or later, as published by the Free Software Foundation.
> > > + *
> > > + * This program is distributed in the hope it will be useful, but WITHOUT
> > > + * ANY WARRANTY; without even the implied warranty of
> > MERCHANTABILITY or
> > > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
> > License for
> > > + * more details.
> > > + *
> > > + * You should have received a copy of the GNU General Public License along
> > with
> > > + * this program.  If not, see <http://www.gnu.org/licenses/>.
> > > + *
> > > + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
> > > + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> > > + * including the hotplug ones. Generic Event Device allows platforms
> > > + * to handle interrupts in ACPI ASL statements. It follows a very
> > > + * similar approach like the _EVT method from GPIO events. All
> > > + * interrupts are listed in  _CRS and the handler is written in _EVT
> > > + * method. Here, we use a single interrupt for the GED device, relying
> > > + * on IO memory region to communicate the type of device affected by
> > > + * the interrupt. This way, we can support up to 32 events with a
> > > + * unique interrupt.
> > > + *
> > > + * Here is an example.
> > > + *
> > > + * Device (\_SB.GED)
> > > + * {
> > > + *     Name (_HID, "ACPI0013")
> > > + *     Name (_UID, Zero)
> > > + *     Name (_CRS, ResourceTemplate ()
> > > + *     {
> > > + *         Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, )
> > > + *         {
> > > + *              0x00000029,
> > > + *         }
> > > + *     })
> > > + *     OperationRegion (IREG, SystemMemory, 0x09080000, 0x04)
> > > + *     Field (IREG, DWordAcc, NoLock, WriteAsZeros)
> > > + *     {
> > > + *         ISEL,   32
> > > + *     }
> > > + *
> > > + *     Method (_EVT, 1, Serialized)  // _EVT: Event
> > > + *     {
> > > + *         Local0 = ISEL // ISEL = IO memory region which specifies the
> > > + *                       // device type.
> > > + *         If (((Local0 & irq0) == irq0))
> > > + *         {
> > > + *             MethodEvent0()
> > > + *         }
> > > + *         ElseIf ((Local0 & irq1) == irq1)
> > > + *         {
> > > + *             MethodEvent1()
> > > + *         }
> > > + *         ...
> > > + *     }
> > > + * }
> > > + *
> > > + */
> > > +
> > > +#ifndef HW_ACPI_GED_H
> > > +#define HW_ACPI_GED_H
> > > +
> > > +#include "hw/acpi/memory_hotplug.h"
> > > +
> > > +#define TYPE_ACPI_GED "acpi-ged"
> > > +#define ACPI_GED(obj) \
> > > +    OBJECT_CHECK(AcpiGedState, (obj), TYPE_ACPI_GED)
> > > +
> > > +#define ACPI_GED_IRQ_SEL_OFFSET 0x0
> > > +#define ACPI_GED_IRQ_SEL_LEN    0x4
> > > +#define ACPI_GED_IRQ_SEL_MEM    0x1
> > > +#define ACPI_GED_REG_LEN        0x4
> > > +
> > > +#define GED_DEVICE      "GED"
> > > +#define AML_GED_IRQ_REG "IREG"
> > > +#define AML_GED_IRQ_SEL "ISEL"
> > > +
> > > +typedef enum {
> > > +    GED_MEMORY_HOTPLUG = 1,
> > > +} GedEventType;
> > > +
> > > +/*
> > > + * Platforms need to specify their own GedEvent array
> > > + * to describe what kind of events they want to support
> > > + * through GED.
> > > + */
> > > +typedef struct GedEvent {
> > > +    uint32_t     selector;
> > > +    GedEventType event;
> > > +} GedEvent;
> > > +
> > > +typedef struct GEDState {
> > > +    MemoryRegion io;
> > > +    uint32_t     sel;
> > 
> > do we need to migrate this during migration?
> 
> TBH, I don't know and currently this series doesn't address migration 
> as we don't have any VMStateDescription and friends. Is this something 
> we can sort later?

It probably should be implemented, otherwise we will have to deal with hard to debug bug reports
when users will try to migrate.

Alternative hack could be that, enabling memory hotplug should put migration blocker,
but that's probably the same effort as adding proper VMStateDescription

> 
> Thanks,
> Shameer
> 
> > > +    uint32_t     irq;
> > > +    qemu_irq     *gsi;
> > > +    QemuMutex    lock;
> > > +} GEDState;
> > > +
> > > +
> > > +typedef struct AcpiGedState {
> > > +    DeviceClass parent_obj;
> > > +    MemHotplugState memhp_state;
> > > +    hwaddr memhp_base;
> > > +    void *gsi;
> > > +    hwaddr ged_base;
> > > +    GEDState ged_state;
> > > +    uint32_t ged_irq;
> > > +    void *ged_events;
> > > +    uint32_t ged_events_size;
> > > +} AcpiGedState;
> > > +
> > > +void build_ged_aml(Aml *table, const char* name, HotplugHandler
> > *hotplug_dev,
> > > +                   uint32_t ged_irq, AmlRegionSpace rs);
> > > +
> > > +#endif
> 

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
@ 2019-05-03 15:10         ` Igor Mammedov
  0 siblings, 0 replies; 62+ messages in thread
From: Igor Mammedov @ 2019-05-03 15:10 UTC (permalink / raw)
  To: Shameerali Kolothum Thodi
  Cc: peter.maydell, sameo, shannon.zhaosl, ard.biesheuvel, qemu-devel,
	Linuxarm, eric.auger, qemu-arm, xuwei (O),
	sebastien.boeuf, lersek

On Fri, 3 May 2019 12:45:52 +0000
Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com> wrote:

> Hi Igor,
> 
> > -----Original Message-----
> > From: Igor Mammedov [mailto:imammedo@redhat.com]
> > Sent: 02 May 2019 17:13
> > To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>
> > Cc: qemu-devel@nongnu.org; qemu-arm@nongnu.org;
> > eric.auger@redhat.com; peter.maydell@linaro.org;
> > shannon.zhaosl@gmail.com; sameo@linux.intel.com;
> > sebastien.boeuf@intel.com; xuwei (O) <xuwei5@huawei.com>;
> > lersek@redhat.com; ard.biesheuvel@linaro.org; Linuxarm
> > <linuxarm@huawei.com>
> > Subject: Re: [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
> > 
> > On Tue, 9 Apr 2019 11:29:30 +0100
> > Shameer Kolothum <shameerali.kolothum.thodi@huawei.com> wrote:
> > 
> > > From: Samuel Ortiz <sameo@linux.intel.com>
> > >
> > > The ACPI Generic Event Device (GED) is a hardware-reduced specific
> > > device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> > > including the hotplug ones.This patch generates the AML code that
> > > defines GEDs.
> > >
> > > Platforms need to specify their own GedEvent array to describe what
> > > kind of events they want to support through GED.  Also this uses a
> > > a single interrupt for the  GED device, relying on IO memory region
> > > to communicate the type of device affected by the interrupt. This
> > > way, we can support up to 32 events with a unique interrupt.
> > >
> > > This supports only memory hotplug for now.
> > >
> > > Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
> > > Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
> > > Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> > > ---
> > >  hw/acpi/Kconfig                        |   4 +
> > >  hw/acpi/Makefile.objs                  |   1 +
> > >  hw/acpi/generic_event_device.c         | 311
> > +++++++++++++++++++++++++++++++++
> > >  include/hw/acpi/generic_event_device.h | 121 +++++++++++++
> > >  4 files changed, 437 insertions(+)
> > >  create mode 100644 hw/acpi/generic_event_device.c
> > >  create mode 100644 include/hw/acpi/generic_event_device.h
> > >
> > > diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig
> > > index eca3bee..01a8b41 100644
> > > --- a/hw/acpi/Kconfig
> > > +++ b/hw/acpi/Kconfig
> > > @@ -27,3 +27,7 @@ config ACPI_VMGENID
> > >      bool
> > >      default y
> > >      depends on PC
> > > +
> > > +config ACPI_HW_REDUCED
> > > +    bool
> > > +    depends on ACPI
> > > diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
> > > index 2d46e37..b753232 100644
> > > --- a/hw/acpi/Makefile.objs
> > > +++ b/hw/acpi/Makefile.objs
> > > @@ -6,6 +6,7 @@ common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) +=
> > memory_hotplug.o
> > >  common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu.o
> > >  common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o
> > >  common-obj-$(CONFIG_ACPI_VMGENID) += vmgenid.o
> > > +common-obj-$(CONFIG_ACPI_HW_REDUCED) += generic_event_device.o
> > >  common-obj-$(call lnot,$(CONFIG_ACPI_X86)) += acpi-stub.o
> > >
> > >  common-obj-y += acpi_interface.o
> > > diff --git a/hw/acpi/generic_event_device.c
> > b/hw/acpi/generic_event_device.c
> > > new file mode 100644
> > > index 0000000..856ca04
> > > --- /dev/null
> > > +++ b/hw/acpi/generic_event_device.c
> > > @@ -0,0 +1,311 @@
> > > +/*
> > > + *
> > > + * Copyright (c) 2018 Intel Corporation
> > > + *
> > > + * This program is free software; you can redistribute it and/or modify it
> > > + * under the terms and conditions of the GNU General Public License,
> > > + * version 2 or later, as published by the Free Software Foundation.
> > > + *
> > > + * This program is distributed in the hope it will be useful, but WITHOUT
> > > + * ANY WARRANTY; without even the implied warranty of
> > MERCHANTABILITY or
> > > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
> > License for
> > > + * more details.
> > > + *
> > > + * You should have received a copy of the GNU General Public License along
> > with
> > > + * this program.  If not, see <http://www.gnu.org/licenses/>.
> > > + */
> > > +
> > > +#include "qemu/osdep.h"
> > > +#include "qapi/error.h"
> > > +#include "exec/address-spaces.h"
> > > +#include "hw/sysbus.h"
> > > +#include "hw/acpi/acpi.h"
> > > +#include "hw/acpi/generic_event_device.h"
> > > +#include "hw/mem/pc-dimm.h"
> > > +
> > > +static Aml *ged_event_aml(const GedEvent *event)
> > > +{
> > > +
> > > +    if (!event) {
> > In general, I prefer to check condition for calling something before doing call.
> > This way one can see in caller why and what is called, which is more clear.
> 
> Ok. I will move it then.
> 
> > 
> > > +        return NULL;
> > > +    }
> > > +
> > > +    switch (event->event) {
> > > +    case GED_MEMORY_HOTPLUG:
> > > +        /* We run a complete memory SCAN when getting a memory
> > hotplug event */
> > > +        return aml_call0(MEMORY_DEVICES_CONTAINER "."
> > MEMORY_SLOT_SCAN_METHOD);
> > > +    default:
> > > +        break;
> > > +    }
> > > +
> > > +    return NULL;
> > > +}
> > > +
> > > +/*
> > > + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
> > > + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> > > + * including the hotplug ones. Platforms need to specify their own
> > > + * GedEvent array to describe what kind of events they want to support
> > > + * through GED. This routine uses a single interrupt for the GED device,
> > > + * relying on IO memory region to communicate the type of device
> > > + * affected by the interrupt. This way, we can support up to 32 events
> > > + * with a unique interrupt.
> > > + */
> > > +void build_ged_aml(Aml *table, const char *name, HotplugHandler
> > *hotplug_dev,
> > > +                   uint32_t ged_irq, AmlRegionSpace rs)
> > > +{
> > > +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> > > +    GedEvent *ged_events = s->ged_events;
> > > +    Aml *crs = aml_resource_template();
> > > +    Aml *evt, *field;
> > > +    Aml *dev = aml_device("%s", name);
> > > +    Aml *irq_sel = aml_local(0);
> > > +    Aml *isel = aml_name(AML_GED_IRQ_SEL);
> > > +    uint32_t i;
> > > +
> > > +    if (!s->ged_base || !ged_events || !s->ged_events_size) {
> > I'd move it up to the caller, it would be obvious right there when
> > function should be called.
> 
> Ok. 
>  
> > > +        return;
> > > +    }
> > > +
> > > +    /* _CRS interrupt */
> > > +    aml_append(crs, aml_interrupt(AML_CONSUMER, AML_EDGE,
> > AML_ACTIVE_HIGH,
> > > +                                  AML_EXCLUSIVE, &ged_irq, 1));
> > > +    /*
> > > +     * For each GED event we:
> > > +     * - Add an interrupt to the CRS section.
> > > +     * - Add a conditional block for each event, inside a while loop.
> > > +     *   This is semantically equivalent to a switch/case implementation.
> > > +     */
> > > +    evt = aml_method("_EVT", 1, AML_SERIALIZED);
> > > +    {
> > > +        Aml *ged_aml;
> > > +        Aml *if_ctx;
> > > +
> > > +        /* Local0 = ISEL */
> > > +        aml_append(evt, aml_store(isel, irq_sel));
> > > +
> > > +        /*
> > > +         * Here we want to call a method for each supported GED event
> > type.
> > > +         * The resulting ASL code looks like:
> > > +         *
> > > +         * Local0 = ISEL
> > > +         * If ((Local0 & irq0) == irq0)
> > > +         * {
> > > +         *     MethodEvent0()
> > > +         * }
> > > +         *
> > > +         * If ((Local0 & irq1) == irq1)
> > > +         * {
> > > +         *     MethodEvent1()
> > > +         * }
> > > +         * ...
> > > +         */
> > Well, I'm confused.
> > do we actually use multiple IRQs or we use only one + MMIO for event type?
> 
> It is one irq + MMIO. I will change the comment block something like this,

change corresponding variable names as well

> 
>     Local0 = ISEL
>     If ((Local0 & One) == One)
>     {
>         MethodEvent1()
>     }
> 
>     If ((Local0 & 0x02) == 0x02)
>     {
>         MethodEvent2()
>     }
>     ...
> 
> > 
> > > +        for (i = 0; i < s->ged_events_size; i++) {
> > 
> > > +            ged_aml = ged_event_aml(&ged_events[i]);
> > > +            if (!ged_aml) {
> > > +                continue;
> > > +            }
> > I'd get rid of ged_event_aml replace it with more 'switch':
> >    for (i,...)
> >        if_ctx = aml_if(...)
> >        switch (event)
> >           case GED_MEMORY_HOTPLUG:
> >                aml_append(if_ctx,
> > aml_call0(MEMORY_DEVICES_CONTAINER "."
> > MEMORY_SLOT_SCAN_METHOD))
> >                break
> >           default:
> >                about(); // make sure that a newly added events have a
> > handler
> 
> Ok. I will change this.
> 
> > 
> > > +
> > > +            /* If ((Local1 == irq))*/
> > > +            if_ctx = aml_if(aml_equal(aml_and(irq_sel,
> > > +
> > aml_int(ged_events[i].selector), NULL),
> > > +
> > aml_int(ged_events[i].selector)));
> > > +            {
> > > +                /* AML for this specific type of event */
> > > +                aml_append(if_ctx), ged_aml);
> > > +            }
> > > +
> > > +            /*
> > > +             * We append the first "if" to the "while" context.
> > > +             * Other "if"s will be "elseif"s.
> > > +             */
> > > +            aml_append(evt, if_ctx);
> > > +        }
> > > +    }
> > > +
> > 
> > > +    aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0013")));
> > > +    aml_append(dev, aml_name_decl("_UID", aml_string(GED_DEVICE)));
> > > +    aml_append(dev, aml_name_decl("_CRS", crs));
> > > +
> > > +    /* Append IO region */
> > > +    aml_append(dev, aml_operation_region(AML_GED_IRQ_REG, rs,
> > > +               aml_int(s->ged_base + ACPI_GED_IRQ_SEL_OFFSET),
> > > +               ACPI_GED_IRQ_SEL_LEN));
> > > +    field = aml_field(AML_GED_IRQ_REG, AML_DWORD_ACC,
> > AML_NOLOCK,
> > > +                      AML_WRITE_AS_ZEROS);
> > > +    aml_append(field, aml_named_field(AML_GED_IRQ_SEL,
> > > +                                      ACPI_GED_IRQ_SEL_LEN *
> > 8));
> > > +    aml_append(dev, field);
> > 
> > I'd move it up above EVT() method, so it would be clear from the begging
> > for what device we are building AML
> 
> Ok.
>  
> > 
> > > +    /* Append _EVT method */
> > > +    aml_append(dev, evt);
> > > +
> > > +    aml_append(table, dev);
> > > +}
> > > +
> > > +/* Memory read by the GED _EVT AML dynamic method */
> > > +static uint64_t ged_read(void *opaque, hwaddr addr, unsigned size)
> > > +{
> > > +    uint64_t val = 0;
> > > +    GEDState *ged_st = opaque;
> > > +
> > > +    switch (addr) {
> > > +    case ACPI_GED_IRQ_SEL_OFFSET:
> > > +        /* Read the selector value and reset it */
> > > +        qemu_mutex_lock(&ged_st->lock);
> > > +        val = ged_st->sel;
> > > +        ged_st->sel = 0;
> > > +        qemu_mutex_unlock(&ged_st->lock);
> > > +        break;
> > > +    default:
> > > +        break;
> > > +    }
> > > +
> > > +    return val;
> > > +}
> > > +
> > > +/* Nothing is expected to be written to the GED memory region */
> > > +static void ged_write(void *opaque, hwaddr addr, uint64_t data,
> > > +                      unsigned int size)
> > > +{
> > > +}
> > > +
> > > +static const MemoryRegionOps ged_ops = {
> > > +    .read = ged_read,
> > > +    .write = ged_write,
> > > +    .endianness = DEVICE_LITTLE_ENDIAN,
> > > +    .valid = {
> > > +        .min_access_size = 4,
> > > +        .max_access_size = 4,
> > > +    },
> > > +};
> > > +
> > > +static void acpi_ged_event(GEDState *ged_st, uint32_t ged_irq_sel)
> > > +{
> > > +    /*
> > > +     * Set the GED IRQ selector to the expected device type value. This
> > > +     * way, the ACPI method will be able to trigger the right code based
> > > +     * on a unique IRQ.
> > > +     */
> > > +    qemu_mutex_lock(&ged_st->lock);
> > > +    ged_st->sel = ged_irq_sel;
> > what if there are 2 events with different sel value and 2nd event happens
> > before guesr read the first one?
> 
> This was previously,
>    ged_st->sel |= ged_irq_sel;
> 
> and was changed based on the assumption that there won't be any multiple
> events. But I think the scenario above is right. I will change it back so that 
> we won't overwrite.
so it was bitmap, then handling it as a DWORD in AML could be wrong
and AML part probably should be changed to accommodate it.

> 
> > 
> > > +    qemu_mutex_unlock(&ged_st->lock);
> > > +
> > > +    /* Trigger the event by sending an interrupt to the guest. */
> > > +    qemu_irq_pulse(ged_st->gsi[ged_st->irq]);
> > > +}
> > > +
> > > +static void acpi_ged_init(MemoryRegion *as, DeviceState *dev, GEDState
> > *ged_st)
> > > +{
> > > +    AcpiGedState *s = ACPI_GED(dev);
> > > +
> > > +    assert(s->ged_base);
> > > +
> > > +    ged_st->irq = s->ged_irq;
> > > +    ged_st->gsi = s->gsi;
> > > +    qemu_mutex_init(&ged_st->lock);
> > > +    memory_region_init_io(&ged_st->io, OBJECT(dev), &ged_ops, ged_st,
> > > +                          "acpi-ged-event", ACPI_GED_REG_LEN);
> > > +    memory_region_add_subregion(as, s->ged_base, &ged_st->io);
> > > +}
> > > +
> > > +static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
> > > +                                    DeviceState *dev, Error **errp)
> > > +{
> > > +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> > > +
> > > +    if (s->memhp_state.is_enabled &&
> > > +        object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> > > +            acpi_memory_plug_cb(hotplug_dev, &s->memhp_state,
> > > +                                dev, errp);
> > > +    } else {
> > > +        error_setg(errp, "virt: device plug request for unsupported
> > device"
> > > +                   " type: %s", object_get_typename(OBJECT(dev)));
> > > +    }
> > > +}
> > > +
> > > +static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits
> > ev)
> > > +{
> > > +    AcpiGedState *s = ACPI_GED(adev);
> > > +    uint32_t sel;
> > > +
> > > +    if (ev & ACPI_MEMORY_HOTPLUG_STATUS) {
> > > +        sel = ACPI_GED_IRQ_SEL_MEM;
> > > +    } else {
> > > +        /* Unknown event. Return without generating interrupt. */
> > > +        return;
> > > +    }
> > > +
> > > +    /*
> > > +     * We inject the hotplug interrupt. The IRQ selector will make
> > > +     * the difference from the ACPI table.
> > > +     */
> > > +    acpi_ged_event(&s->ged_state, sel);
> > > +}
> > > +
> > > +static void acpi_ged_device_realize(DeviceState *dev, Error **errp)
> > > +{
> > > +    AcpiGedState *s = ACPI_GED(dev);
> > > +
> > > +    if (s->memhp_state.is_enabled) {
> > > +        acpi_memory_hotplug_init(get_system_memory(), OBJECT(dev),
> > > +                                 &s->memhp_state,
> > > +                                 s->memhp_base);
> > > +        acpi_ged_init(get_system_memory(), dev, &s->ged_state);
> > > +    }
> > > +}
> > > +
> > > +static Property acpi_ged_properties[] = {
> > > +    /*
> > > +     * Memory hotplug base address is a property of GED here,
> > > +     * because GED handles memory hotplug event and
> > MEMORY_HOTPLUG_DEVICE
> > > +     * gets initialized when GED device is realized.
> > > +     */
> > > +    DEFINE_PROP_UINT64("memhp-base", AcpiGedState, memhp_base,
> > 0),
> > > +    DEFINE_PROP_BOOL("memory-hotplug-support", AcpiGedState,
> > > +                     memhp_state.is_enabled, true),
> > > +    DEFINE_PROP_PTR("gsi", AcpiGedState, gsi),
> > 
> > PTR shouldn't be used in new code, look at object_property_add_link() & co
> 
> Ok. I will take a look at that.
>  
> > 
> > > +    DEFINE_PROP_UINT64("ged-base", AcpiGedState, ged_base, 0),
> > > +    DEFINE_PROP_UINT32("ged-irq", AcpiGedState, ged_irq, 0),
> > > +    DEFINE_PROP_PTR("ged-events", AcpiGedState, ged_events),
> > > +    DEFINE_PROP_UINT32("ged-events-size", AcpiGedState,
> > ged_events_size, 0),
> > > +    DEFINE_PROP_END_OF_LIST(),
> > > +};
> > > +
> > > +static void acpi_ged_class_init(ObjectClass *class, void *data)
> > > +{
> > > +    DeviceClass *dc = DEVICE_CLASS(class);
> > > +    HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class);
> > > +    AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class);
> > > +
> > > +    dc->desc = "ACPI";
> > > +    dc->props = acpi_ged_properties;
> > > +    dc->realize = acpi_ged_device_realize;
> > > +
> > > +    /* Reason: pointer properties "gsi" and "ged_events" */
> > > +    dc->user_creatable = false;
> > > +
> > > +    hc->plug = acpi_ged_device_plug_cb;
> > > +
> > > +    adevc->send_event = acpi_ged_send_event;
> > > +}
> > > +
> > > +static const TypeInfo acpi_ged_info = {
> > > +    .name          = TYPE_ACPI_GED,
> > > +    .parent        = TYPE_DEVICE,
> > > +    .instance_size = sizeof(AcpiGedState),
> > > +    .class_init    = acpi_ged_class_init,
> > > +    .interfaces = (InterfaceInfo[]) {
> > > +        { TYPE_HOTPLUG_HANDLER },
> > > +        { TYPE_ACPI_DEVICE_IF },
> > > +        { }
> > > +    }
> > > +};
> > > +
> > > +static void acpi_ged_register_types(void)
> > > +{
> > > +    type_register_static(&acpi_ged_info);
> > > +}
> > > +
> > > +type_init(acpi_ged_register_types)
> > > diff --git a/include/hw/acpi/generic_event_device.h
> > b/include/hw/acpi/generic_event_device.h
> > > new file mode 100644
> > > index 0000000..9c840d8
> > > --- /dev/null
> > > +++ b/include/hw/acpi/generic_event_device.h
> > > @@ -0,0 +1,121 @@
> > > +/*
> > > + *
> > > + * Copyright (c) 2018 Intel Corporation
> > > + *
> > > + * This program is free software; you can redistribute it and/or modify it
> > > + * under the terms and conditions of the GNU General Public License,
> > > + * version 2 or later, as published by the Free Software Foundation.
> > > + *
> > > + * This program is distributed in the hope it will be useful, but WITHOUT
> > > + * ANY WARRANTY; without even the implied warranty of
> > MERCHANTABILITY or
> > > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
> > License for
> > > + * more details.
> > > + *
> > > + * You should have received a copy of the GNU General Public License along
> > with
> > > + * this program.  If not, see <http://www.gnu.org/licenses/>.
> > > + *
> > > + * The ACPI Generic Event Device (GED) is a hardware-reduced specific
> > > + * device[ACPI v6.1 Section 5.6.9] that handles all platform events,
> > > + * including the hotplug ones. Generic Event Device allows platforms
> > > + * to handle interrupts in ACPI ASL statements. It follows a very
> > > + * similar approach like the _EVT method from GPIO events. All
> > > + * interrupts are listed in  _CRS and the handler is written in _EVT
> > > + * method. Here, we use a single interrupt for the GED device, relying
> > > + * on IO memory region to communicate the type of device affected by
> > > + * the interrupt. This way, we can support up to 32 events with a
> > > + * unique interrupt.
> > > + *
> > > + * Here is an example.
> > > + *
> > > + * Device (\_SB.GED)
> > > + * {
> > > + *     Name (_HID, "ACPI0013")
> > > + *     Name (_UID, Zero)
> > > + *     Name (_CRS, ResourceTemplate ()
> > > + *     {
> > > + *         Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, )
> > > + *         {
> > > + *              0x00000029,
> > > + *         }
> > > + *     })
> > > + *     OperationRegion (IREG, SystemMemory, 0x09080000, 0x04)
> > > + *     Field (IREG, DWordAcc, NoLock, WriteAsZeros)
> > > + *     {
> > > + *         ISEL,   32
> > > + *     }
> > > + *
> > > + *     Method (_EVT, 1, Serialized)  // _EVT: Event
> > > + *     {
> > > + *         Local0 = ISEL // ISEL = IO memory region which specifies the
> > > + *                       // device type.
> > > + *         If (((Local0 & irq0) == irq0))
> > > + *         {
> > > + *             MethodEvent0()
> > > + *         }
> > > + *         ElseIf ((Local0 & irq1) == irq1)
> > > + *         {
> > > + *             MethodEvent1()
> > > + *         }
> > > + *         ...
> > > + *     }
> > > + * }
> > > + *
> > > + */
> > > +
> > > +#ifndef HW_ACPI_GED_H
> > > +#define HW_ACPI_GED_H
> > > +
> > > +#include "hw/acpi/memory_hotplug.h"
> > > +
> > > +#define TYPE_ACPI_GED "acpi-ged"
> > > +#define ACPI_GED(obj) \
> > > +    OBJECT_CHECK(AcpiGedState, (obj), TYPE_ACPI_GED)
> > > +
> > > +#define ACPI_GED_IRQ_SEL_OFFSET 0x0
> > > +#define ACPI_GED_IRQ_SEL_LEN    0x4
> > > +#define ACPI_GED_IRQ_SEL_MEM    0x1
> > > +#define ACPI_GED_REG_LEN        0x4
> > > +
> > > +#define GED_DEVICE      "GED"
> > > +#define AML_GED_IRQ_REG "IREG"
> > > +#define AML_GED_IRQ_SEL "ISEL"
> > > +
> > > +typedef enum {
> > > +    GED_MEMORY_HOTPLUG = 1,
> > > +} GedEventType;
> > > +
> > > +/*
> > > + * Platforms need to specify their own GedEvent array
> > > + * to describe what kind of events they want to support
> > > + * through GED.
> > > + */
> > > +typedef struct GedEvent {
> > > +    uint32_t     selector;
> > > +    GedEventType event;
> > > +} GedEvent;
> > > +
> > > +typedef struct GEDState {
> > > +    MemoryRegion io;
> > > +    uint32_t     sel;
> > 
> > do we need to migrate this during migration?
> 
> TBH, I don't know and currently this series doesn't address migration 
> as we don't have any VMStateDescription and friends. Is this something 
> we can sort later?

It probably should be implemented, otherwise we will have to deal with hard to debug bug reports
when users will try to migrate.

Alternative hack could be that, enabling memory hotplug should put migration blocker,
but that's probably the same effort as adding proper VMStateDescription

> 
> Thanks,
> Shameer
> 
> > > +    uint32_t     irq;
> > > +    qemu_irq     *gsi;
> > > +    QemuMutex    lock;
> > > +} GEDState;
> > > +
> > > +
> > > +typedef struct AcpiGedState {
> > > +    DeviceClass parent_obj;
> > > +    MemHotplugState memhp_state;
> > > +    hwaddr memhp_base;
> > > +    void *gsi;
> > > +    hwaddr ged_base;
> > > +    GEDState ged_state;
> > > +    uint32_t ged_irq;
> > > +    void *ged_events;
> > > +    uint32_t ged_events_size;
> > > +} AcpiGedState;
> > > +
> > > +void build_ged_aml(Aml *table, const char* name, HotplugHandler
> > *hotplug_dev,
> > > +                   uint32_t ged_irq, AmlRegionSpace rs);
> > > +
> > > +#endif
> 



^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
  2019-05-03 15:10         ` Igor Mammedov
  (?)
@ 2019-05-07  9:01         ` Shameerali Kolothum Thodi
  2019-05-09 14:50           ` Igor Mammedov
  -1 siblings, 1 reply; 62+ messages in thread
From: Shameerali Kolothum Thodi @ 2019-05-07  9:01 UTC (permalink / raw)
  To: Igor Mammedov
  Cc: peter.maydell, sameo, shannon.zhaosl, ard.biesheuvel, qemu-devel,
	Linuxarm, eric.auger, qemu-arm, xuwei (O),
	sebastien.boeuf, lersek

Hi Igor,

> -----Original Message-----
> From: Igor Mammedov [mailto:imammedo@redhat.com]
> Sent: 03 May 2019 16:10
> To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>
> Cc: qemu-devel@nongnu.org; qemu-arm@nongnu.org;
> eric.auger@redhat.com; peter.maydell@linaro.org;
> shannon.zhaosl@gmail.com; sameo@linux.intel.com;
> sebastien.boeuf@intel.com; xuwei (O) <xuwei5@huawei.com>;
> lersek@redhat.com; ard.biesheuvel@linaro.org; Linuxarm
> <linuxarm@huawei.com>
> Subject: Re: [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support

[...]

> > > type.
> > > > +         * The resulting ASL code looks like:
> > > > +         *
> > > > +         * Local0 = ISEL
> > > > +         * If ((Local0 & irq0) == irq0)
> > > > +         * {
> > > > +         *     MethodEvent0()
> > > > +         * }
> > > > +         *
> > > > +         * If ((Local0 & irq1) == irq1)
> > > > +         * {
> > > > +         *     MethodEvent1()
> > > > +         * }
> > > > +         * ...
> > > > +         */
> > > Well, I'm confused.
> > > do we actually use multiple IRQs or we use only one + MMIO for event
> type?
> >
> > It is one irq + MMIO. I will change the comment block something like
> > this,
> 
> change corresponding variable names as well

Ok.

> >
> >     Local0 = ISEL
> >     If ((Local0 & One) == One)
> >     {
> >         MethodEvent1()
> >     }
> >
> >     If ((Local0 & 0x02) == 0x02)
> >     {
> >         MethodEvent2()
> >     }
> >     ...
> >
> > >
> > > > +        for (i = 0; i < s->ged_events_size; i++) {
> > >
> > > > +            ged_aml = ged_event_aml(&ged_events[i]);
> > > > +            if (!ged_aml) {
> > > > +                continue;
> > > > +            }
> > > I'd get rid of ged_event_aml replace it with more 'switch':
> > >    for (i,...)
> > >        if_ctx = aml_if(...)
> > >        switch (event)
> > >           case GED_MEMORY_HOTPLUG:
> > >                aml_append(if_ctx,
> > > aml_call0(MEMORY_DEVICES_CONTAINER "."
> > > MEMORY_SLOT_SCAN_METHOD))
> > >                break
> > >           default:
> > >                about(); // make sure that a newly added events have
> > > a handler
> >
> > Ok. I will change this.
> >
> > >
> > > > +
> > > > +            /* If ((Local1 == irq))*/
> > > > +            if_ctx = aml_if(aml_equal(aml_and(irq_sel,
> > > > +
> > > aml_int(ged_events[i].selector), NULL),
> > > > +
> > > aml_int(ged_events[i].selector)));
> > > > +            {
> > > > +                /* AML for this specific type of event */
> > > > +                aml_append(if_ctx), ged_aml);
> > > > +            }
> > > > +
> > > > +            /*
> > > > +             * We append the first "if" to the "while" context.
> > > > +             * Other "if"s will be "elseif"s.
> > > > +             */
> > > > +            aml_append(evt, if_ctx);
> > > > +        }
> > > > +    }
> > > > +
> > >
> > > > +    aml_append(dev, aml_name_decl("_HID",
> aml_string("ACPI0013")));
> > > > +    aml_append(dev, aml_name_decl("_UID",
> aml_string(GED_DEVICE)));
> > > > +    aml_append(dev, aml_name_decl("_CRS", crs));
> > > > +
> > > > +    /* Append IO region */
> > > > +    aml_append(dev, aml_operation_region(AML_GED_IRQ_REG, rs,
> > > > +               aml_int(s->ged_base + ACPI_GED_IRQ_SEL_OFFSET),
> > > > +               ACPI_GED_IRQ_SEL_LEN));
> > > > +    field = aml_field(AML_GED_IRQ_REG, AML_DWORD_ACC,
> > > AML_NOLOCK,
> > > > +                      AML_WRITE_AS_ZEROS);
> > > > +    aml_append(field, aml_named_field(AML_GED_IRQ_SEL,
> > > > +                                      ACPI_GED_IRQ_SEL_LEN
> *
> > > 8));
> > > > +    aml_append(dev, field);
> > >
> > > I'd move it up above EVT() method, so it would be clear from the
> > > begging for what device we are building AML
> >
> > Ok.
> >
> > >
> > > > +    /* Append _EVT method */
> > > > +    aml_append(dev, evt);
> > > > +
> > > > +    aml_append(table, dev);
> > > > +}
> > > > +
> > > > +/* Memory read by the GED _EVT AML dynamic method */ static
> > > > +uint64_t ged_read(void *opaque, hwaddr addr, unsigned size) {
> > > > +    uint64_t val = 0;
> > > > +    GEDState *ged_st = opaque;
> > > > +
> > > > +    switch (addr) {
> > > > +    case ACPI_GED_IRQ_SEL_OFFSET:
> > > > +        /* Read the selector value and reset it */
> > > > +        qemu_mutex_lock(&ged_st->lock);
> > > > +        val = ged_st->sel;
> > > > +        ged_st->sel = 0;
> > > > +        qemu_mutex_unlock(&ged_st->lock);
> > > > +        break;
> > > > +    default:
> > > > +        break;
> > > > +    }
> > > > +
> > > > +    return val;
> > > > +}
> > > > +
> > > > +/* Nothing is expected to be written to the GED memory region */
> > > > +static void ged_write(void *opaque, hwaddr addr, uint64_t data,
> > > > +                      unsigned int size) { }
> > > > +
> > > > +static const MemoryRegionOps ged_ops = {
> > > > +    .read = ged_read,
> > > > +    .write = ged_write,
> > > > +    .endianness = DEVICE_LITTLE_ENDIAN,
> > > > +    .valid = {
> > > > +        .min_access_size = 4,
> > > > +        .max_access_size = 4,
> > > > +    },
> > > > +};
> > > > +
> > > > +static void acpi_ged_event(GEDState *ged_st, uint32_t
> > > > +ged_irq_sel) {
> > > > +    /*
> > > > +     * Set the GED IRQ selector to the expected device type value. This
> > > > +     * way, the ACPI method will be able to trigger the right code based
> > > > +     * on a unique IRQ.
> > > > +     */
> > > > +    qemu_mutex_lock(&ged_st->lock);
> > > > +    ged_st->sel = ged_irq_sel;
> > > what if there are 2 events with different sel value and 2nd event
> > > happens before guesr read the first one?
> >
> > This was previously,
> >    ged_st->sel |= ged_irq_sel;
> >
> > and was changed based on the assumption that there won't be any
> > multiple events. But I think the scenario above is right. I will
> > change it back so that we won't overwrite.
> so it was bitmap, then handling it as a DWORD in AML could be wrong and AML
> part probably should be changed to accommodate it.

Hmm..I am slightly confused as the AML code already uses the len as 32 bits for this
register field,

    /* Append IO region */
    aml_append(dev, aml_operation_region(AML_GED_IRQ_REG, rs,
               aml_int(s->ged_base + ACPI_GED_IRQ_SEL_OFFSET),
               ACPI_GED_IRQ_SEL_LEN));
    field = aml_field(AML_GED_IRQ_REG, AML_DWORD_ACC, AML_NOLOCK,
                      AML_WRITE_AS_ZEROS);
    aml_append(field, aml_named_field(AML_GED_IRQ_SEL,
                                      ACPI_GED_IRQ_SEL_LEN * 8));
    aml_append(dev, field);

and event check logic is, 

     if_ctx = aml_if(aml_equal(aml_and(irq_sel,
                          aml_int(ged_events[i].selector), NULL),
                          aml_int(ged_events[i].selector)));

Not sure what is the concern here. Please let me know. 
            
> >
> > >
> > > > +    qemu_mutex_unlock(&ged_st->lock);
> > > > +
> > > > +    /* Trigger the event by sending an interrupt to the guest. */
> > > > +    qemu_irq_pulse(ged_st->gsi[ged_st->irq]);
> > > > +}
> > > > +
> > > > +static void acpi_ged_init(MemoryRegion *as, DeviceState *dev,
> > > > +GEDState
> > > *ged_st)
> > > > +{
> > > > +    AcpiGedState *s = ACPI_GED(dev);
> > > > +
> > > > +    assert(s->ged_base);
> > > > +
> > > > +    ged_st->irq = s->ged_irq;
> > > > +    ged_st->gsi = s->gsi;
> > > > +    qemu_mutex_init(&ged_st->lock);
> > > > +    memory_region_init_io(&ged_st->io, OBJECT(dev), &ged_ops,
> ged_st,
> > > > +                          "acpi-ged-event", ACPI_GED_REG_LEN);
> > > > +    memory_region_add_subregion(as, s->ged_base, &ged_st->io); }
> > > > +
> > > > +static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
> > > > +                                    DeviceState *dev, Error
> > > > +**errp) {
> > > > +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> > > > +
> > > > +    if (s->memhp_state.is_enabled &&
> > > > +        object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> > > > +            acpi_memory_plug_cb(hotplug_dev, &s->memhp_state,
> > > > +                                dev, errp);
> > > > +    } else {
> > > > +        error_setg(errp, "virt: device plug request for
> > > > + unsupported
> > > device"
> > > > +                   " type: %s",
> object_get_typename(OBJECT(dev)));
> > > > +    }
> > > > +}
> > > > +
> > > > +static void acpi_ged_send_event(AcpiDeviceIf *adev,
> > > > +AcpiEventStatusBits
> > > ev)
> > > > +{
> > > > +    AcpiGedState *s = ACPI_GED(adev);
> > > > +    uint32_t sel;
> > > > +
> > > > +    if (ev & ACPI_MEMORY_HOTPLUG_STATUS) {
> > > > +        sel = ACPI_GED_IRQ_SEL_MEM;
> > > > +    } else {
> > > > +        /* Unknown event. Return without generating interrupt. */
> > > > +        return;
> > > > +    }
> > > > +
> > > > +    /*
> > > > +     * We inject the hotplug interrupt. The IRQ selector will make
> > > > +     * the difference from the ACPI table.
> > > > +     */
> > > > +    acpi_ged_event(&s->ged_state, sel); }
> > > > +
> > > > +static void acpi_ged_device_realize(DeviceState *dev, Error
> > > > +**errp) {
> > > > +    AcpiGedState *s = ACPI_GED(dev);
> > > > +
> > > > +    if (s->memhp_state.is_enabled) {
> > > > +        acpi_memory_hotplug_init(get_system_memory(),
> OBJECT(dev),
> > > > +                                 &s->memhp_state,
> > > > +                                 s->memhp_base);
> > > > +        acpi_ged_init(get_system_memory(), dev, &s->ged_state);
> > > > +    }
> > > > +}
> > > > +
> > > > +static Property acpi_ged_properties[] = {
> > > > +    /*
> > > > +     * Memory hotplug base address is a property of GED here,
> > > > +     * because GED handles memory hotplug event and
> > > MEMORY_HOTPLUG_DEVICE
> > > > +     * gets initialized when GED device is realized.
> > > > +     */
> > > > +    DEFINE_PROP_UINT64("memhp-base", AcpiGedState,
> memhp_base,
> > > 0),
> > > > +    DEFINE_PROP_BOOL("memory-hotplug-support", AcpiGedState,
> > > > +                     memhp_state.is_enabled, true),
> > > > +    DEFINE_PROP_PTR("gsi", AcpiGedState, gsi),
> > >
> > > PTR shouldn't be used in new code, look at
> > > object_property_add_link() & co
> >
> > Ok. I will take a look at that.
> >
> > >
> > > > +    DEFINE_PROP_UINT64("ged-base", AcpiGedState, ged_base, 0),
> > > > +    DEFINE_PROP_UINT32("ged-irq", AcpiGedState, ged_irq, 0),
> > > > +    DEFINE_PROP_PTR("ged-events", AcpiGedState, ged_events),
> > > > +    DEFINE_PROP_UINT32("ged-events-size", AcpiGedState,
> > > ged_events_size, 0),
> > > > +    DEFINE_PROP_END_OF_LIST(),
> > > > +};
> > > > +
> > > > +static void acpi_ged_class_init(ObjectClass *class, void *data) {
> > > > +    DeviceClass *dc = DEVICE_CLASS(class);
> > > > +    HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class);
> > > > +    AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class);
> > > > +
> > > > +    dc->desc = "ACPI";
> > > > +    dc->props = acpi_ged_properties;
> > > > +    dc->realize = acpi_ged_device_realize;
> > > > +
> > > > +    /* Reason: pointer properties "gsi" and "ged_events" */
> > > > +    dc->user_creatable = false;
> > > > +
> > > > +    hc->plug = acpi_ged_device_plug_cb;
> > > > +
> > > > +    adevc->send_event = acpi_ged_send_event; }
> > > > +
> > > > +static const TypeInfo acpi_ged_info = {
> > > > +    .name          = TYPE_ACPI_GED,
> > > > +    .parent        = TYPE_DEVICE,
> > > > +    .instance_size = sizeof(AcpiGedState),
> > > > +    .class_init    = acpi_ged_class_init,
> > > > +    .interfaces = (InterfaceInfo[]) {
> > > > +        { TYPE_HOTPLUG_HANDLER },
> > > > +        { TYPE_ACPI_DEVICE_IF },
> > > > +        { }
> > > > +    }
> > > > +};
> > > > +
> > > > +static void acpi_ged_register_types(void) {
> > > > +    type_register_static(&acpi_ged_info);
> > > > +}
> > > > +
> > > > +type_init(acpi_ged_register_types)
> > > > diff --git a/include/hw/acpi/generic_event_device.h
> > > b/include/hw/acpi/generic_event_device.h
> > > > new file mode 100644
> > > > index 0000000..9c840d8
> > > > --- /dev/null
> > > > +++ b/include/hw/acpi/generic_event_device.h
> > > > @@ -0,0 +1,121 @@
> > > > +/*
> > > > + *
> > > > + * Copyright (c) 2018 Intel Corporation
> > > > + *
> > > > + * This program is free software; you can redistribute it and/or
> > > > +modify it
> > > > + * under the terms and conditions of the GNU General Public
> > > > +License,
> > > > + * version 2 or later, as published by the Free Software Foundation.
> > > > + *
> > > > + * This program is distributed in the hope it will be useful, but
> > > > +WITHOUT
> > > > + * ANY WARRANTY; without even the implied warranty of
> > > MERCHANTABILITY or
> > > > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
> > > License for
> > > > + * more details.
> > > > + *
> > > > + * You should have received a copy of the GNU General Public
> > > > + License along
> > > with
> > > > + * this program.  If not, see <http://www.gnu.org/licenses/>.
> > > > + *
> > > > + * The ACPI Generic Event Device (GED) is a hardware-reduced
> > > > + specific
> > > > + * device[ACPI v6.1 Section 5.6.9] that handles all platform
> > > > + events,
> > > > + * including the hotplug ones. Generic Event Device allows
> > > > + platforms
> > > > + * to handle interrupts in ACPI ASL statements. It follows a very
> > > > + * similar approach like the _EVT method from GPIO events. All
> > > > + * interrupts are listed in  _CRS and the handler is written in
> > > > + _EVT
> > > > + * method. Here, we use a single interrupt for the GED device,
> > > > + relying
> > > > + * on IO memory region to communicate the type of device affected
> > > > + by
> > > > + * the interrupt. This way, we can support up to 32 events with a
> > > > + * unique interrupt.
> > > > + *
> > > > + * Here is an example.
> > > > + *
> > > > + * Device (\_SB.GED)
> > > > + * {
> > > > + *     Name (_HID, "ACPI0013")
> > > > + *     Name (_UID, Zero)
> > > > + *     Name (_CRS, ResourceTemplate ()
> > > > + *     {
> > > > + *         Interrupt (ResourceConsumer, Edge, ActiveHigh,
> Exclusive, ,, )
> > > > + *         {
> > > > + *              0x00000029,
> > > > + *         }
> > > > + *     })
> > > > + *     OperationRegion (IREG, SystemMemory, 0x09080000, 0x04)
> > > > + *     Field (IREG, DWordAcc, NoLock, WriteAsZeros)
> > > > + *     {
> > > > + *         ISEL,   32
> > > > + *     }
> > > > + *
> > > > + *     Method (_EVT, 1, Serialized)  // _EVT: Event
> > > > + *     {
> > > > + *         Local0 = ISEL // ISEL = IO memory region which specifies the
> > > > + *                       // device type.
> > > > + *         If (((Local0 & irq0) == irq0))
> > > > + *         {
> > > > + *             MethodEvent0()
> > > > + *         }
> > > > + *         ElseIf ((Local0 & irq1) == irq1)
> > > > + *         {
> > > > + *             MethodEvent1()
> > > > + *         }
> > > > + *         ...
> > > > + *     }
> > > > + * }
> > > > + *
> > > > + */
> > > > +
> > > > +#ifndef HW_ACPI_GED_H
> > > > +#define HW_ACPI_GED_H
> > > > +
> > > > +#include "hw/acpi/memory_hotplug.h"
> > > > +
> > > > +#define TYPE_ACPI_GED "acpi-ged"
> > > > +#define ACPI_GED(obj) \
> > > > +    OBJECT_CHECK(AcpiGedState, (obj), TYPE_ACPI_GED)
> > > > +
> > > > +#define ACPI_GED_IRQ_SEL_OFFSET 0x0
> > > > +#define ACPI_GED_IRQ_SEL_LEN    0x4
> > > > +#define ACPI_GED_IRQ_SEL_MEM    0x1
> > > > +#define ACPI_GED_REG_LEN        0x4
> > > > +
> > > > +#define GED_DEVICE      "GED"
> > > > +#define AML_GED_IRQ_REG "IREG"
> > > > +#define AML_GED_IRQ_SEL "ISEL"
> > > > +
> > > > +typedef enum {
> > > > +    GED_MEMORY_HOTPLUG = 1,
> > > > +} GedEventType;
> > > > +
> > > > +/*
> > > > + * Platforms need to specify their own GedEvent array
> > > > + * to describe what kind of events they want to support
> > > > + * through GED.
> > > > + */
> > > > +typedef struct GedEvent {
> > > > +    uint32_t     selector;
> > > > +    GedEventType event;
> > > > +} GedEvent;
> > > > +
> > > > +typedef struct GEDState {
> > > > +    MemoryRegion io;
> > > > +    uint32_t     sel;
> > >
> > > do we need to migrate this during migration?
> >
> > TBH, I don't know and currently this series doesn't address migration
> > as we don't have any VMStateDescription and friends. Is this something
> > we can sort later?
> 
> It probably should be implemented, otherwise we will have to deal with hard to
> debug bug reports when users will try to migrate.
> 
> Alternative hack could be that, enabling memory hotplug should put migration
> blocker, but that's probably the same effort as adding proper
> VMStateDescription

Ok. I will take a look into VMStateDescription then.

Thanks,
Shameer

> >
> > Thanks,
> > Shameer
> >
> > > > +    uint32_t     irq;
> > > > +    qemu_irq     *gsi;
> > > > +    QemuMutex    lock;
> > > > +} GEDState;
> > > > +
> > > > +
> > > > +typedef struct AcpiGedState {
> > > > +    DeviceClass parent_obj;
> > > > +    MemHotplugState memhp_state;
> > > > +    hwaddr memhp_base;
> > > > +    void *gsi;
> > > > +    hwaddr ged_base;
> > > > +    GEDState ged_state;
> > > > +    uint32_t ged_irq;
> > > > +    void *ged_events;
> > > > +    uint32_t ged_events_size;
> > > > +} AcpiGedState;
> > > > +
> > > > +void build_ged_aml(Aml *table, const char* name, HotplugHandler
> > > *hotplug_dev,
> > > > +                   uint32_t ged_irq, AmlRegionSpace rs);
> > > > +
> > > > +#endif
> >



^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 8/8] hw/arm/boot: Expose the PC-DIMM nodes in the DT
  2019-05-03 14:13           ` Laszlo Ersek
  (?)
@ 2019-05-08 10:30           ` Shameerali Kolothum Thodi
  -1 siblings, 0 replies; 62+ messages in thread
From: Shameerali Kolothum Thodi @ 2019-05-08 10:30 UTC (permalink / raw)
  To: Laszlo Ersek, qemu-devel, qemu-arm, eric.auger, imammedo
  Cc: peter.maydell, sameo, ard.biesheuvel, Linuxarm, shannon.zhaosl,
	sebastien.boeuf, xuwei (O)

Hi Laszlo,

> -----Original Message-----
> From: Laszlo Ersek [mailto:lersek@redhat.com]
> Sent: 03 May 2019 15:14
> To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>;
> qemu-devel@nongnu.org; qemu-arm@nongnu.org; eric.auger@redhat.com;
> imammedo@redhat.com
> Cc: peter.maydell@linaro.org; sameo@linux.intel.com;
> ard.biesheuvel@linaro.org; Linuxarm <linuxarm@huawei.com>;
> shannon.zhaosl@gmail.com; sebastien.boeuf@intel.com; xuwei (O)
> <xuwei5@huawei.com>
> Subject: Re: [Qemu-devel] [PATCH v4 8/8] hw/arm/boot: Expose the PC-DIMM
> nodes in the DT
> 
> Hi Shameer,
> 
> On 05/03/19 15:35, Shameerali Kolothum Thodi wrote:
> >
> >
> >> -----Original Message-----
> >> From: Linuxarm [mailto:linuxarm-bounces@huawei.com] On Behalf Of
> >> Shameerali Kolothum Thodi
> >> Sent: 10 April 2019 09:49
> >> To: Laszlo Ersek <lersek@redhat.com>; qemu-devel@nongnu.org;
> >> qemu-arm@nongnu.org; eric.auger@redhat.com; imammedo@redhat.com
> >> Cc: peter.maydell@linaro.org; sameo@linux.intel.com;
> >> ard.biesheuvel@linaro.org; Linuxarm <linuxarm@huawei.com>;
> >> shannon.zhaosl@gmail.com; sebastien.boeuf@intel.com; xuwei (O)
> >> <xuwei5@huawei.com>
> >> Subject: RE: [PATCH v4 8/8] hw/arm/boot: Expose the PC-DIMM nodes in
> >> the DT
> >>
> >>
> >>> -----Original Message-----
> >>> From: Laszlo Ersek [mailto:lersek@redhat.com]
> >>> Sent: 09 April 2019 16:09
> >>> To: Shameerali Kolothum Thodi
> >>> <shameerali.kolothum.thodi@huawei.com>;
> >>> qemu-devel@nongnu.org; qemu-arm@nongnu.org;
> eric.auger@redhat.com;
> >>> imammedo@redhat.com
> >>> Cc: peter.maydell@linaro.org; shannon.zhaosl@gmail.com;
> >>> sameo@linux.intel.com; sebastien.boeuf@intel.com; xuwei (O)
> >>> <xuwei5@huawei.com>; ard.biesheuvel@linaro.org; Linuxarm
> >>> <linuxarm@huawei.com>
> >>> Subject: Re: [PATCH v4 8/8] hw/arm/boot: Expose the PC-DIMM nodes in
> >>> the DT
> >>>
> >>> On 04/09/19 12:29, Shameer Kolothum wrote:
> >>>> This patch adds memory nodes corresponding to PC-DIMM regions.
> >>>> This will enable support for cold plugged device memory for Guests
> >>>> with DT boot.
> >>>>
> >>>> Signed-off-by: Shameer Kolothum
> >> <shameerali.kolothum.thodi@huawei.com>
> >>>> Signed-off-by: Eric Auger <eric.auger@redhat.com>
> >>>> ---
> >>>>  hw/arm/boot.c | 42
> ++++++++++++++++++++++++++++++++++++++++++
> >>>>  1 file changed, 42 insertions(+)
> >>>>
> >>>> diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 8c840ba..150e1ed
> >>>> 100644
> >>>> --- a/hw/arm/boot.c
> >>>> +++ b/hw/arm/boot.c
> >>>> @@ -19,6 +19,7 @@
> >>>>  #include "sysemu/numa.h"
> >>>>  #include "hw/boards.h"
> >>>>  #include "hw/loader.h"
> >>>> +#include "hw/mem/memory-device.h"
> >>>>  #include "elf.h"
> >>>>  #include "sysemu/device_tree.h"
> >>>>  #include "qemu/config-file.h"
> >>>> @@ -538,6 +539,41 @@ static void fdt_add_psci_node(void *fdt)
> >>>>      qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn);  }
> >>>>
> >>>> +static int fdt_add_hotpluggable_memory_nodes(void *fdt,
> >>>> +                                             uint32_t acells,
> >>> uint32_t scells) {
> >>>> +    MemoryDeviceInfoList *info, *info_list =
> qmp_memory_device_list();
> >>>> +    MemoryDeviceInfo *mi;
> >>>> +    int ret = 0;
> >>>> +
> >>>> +    for (info = info_list; info != NULL; info = info->next) {
> >>>> +        mi = info->value;
> >>>> +        switch (mi->type) {
> >>>> +        case MEMORY_DEVICE_INFO_KIND_DIMM:
> >>>> +        {
> >>>> +            PCDIMMDeviceInfo *di = mi->u.dimm.data;
> >>>> +
> >>>> +            ret = fdt_add_memory_node(fdt, acells, di->addr, scells,
> >>>> +                                      di->size, di->node, true);
> >>>> +            if (ret) {
> >>>> +                fprintf(stderr,
> >>>> +                        "couldn't add PCDIMM
> >> /memory@%"PRIx64"
> >>> node\n",
> >>>> +                        di->addr);
> >>>> +                goto out;
> >>>> +            }
> >>>> +            break;
> >>>> +        }
> >>>> +        default:
> >>>> +            fprintf(stderr, "%s memory nodes are not yet
> supported\n",
> >>>> +                    MemoryDeviceInfoKind_str(mi->type));
> >>>> +            ret = -ENOENT;
> >>>> +            goto out;
> >>>> +        }
> >>>> +    }
> >>>> +out:
> >>>> +    qapi_free_MemoryDeviceInfoList(info_list);
> >>>> +    return ret;
> >>>> +}
> >>>> +
> >>>>  int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
> >>>>                   hwaddr addr_limit, AddressSpace *as)  { @@
> -637,6
> >>>> +673,12 @@ int arm_load_dtb(hwaddr addr, const struct
> >>> arm_boot_info *binfo,
> >>>>          }
> >>>>      }
> >>>>
> >>>> +    rc = fdt_add_hotpluggable_memory_nodes(fdt, acells, scells);
> >>>> +    if (rc < 0) {
> >>>> +        fprintf(stderr, "couldn't add hotpluggable memory nodes\n");
> >>>> +        goto fail;
> >>>> +    }
> >>>> +
> >>>>      rc = fdt_path_offset(fdt, "/chosen");
> >>>>      if (rc < 0) {
> >>>>          qemu_fdt_add_subnode(fdt, "/chosen");
> >>>>
> >>>
> >>>
> >>> Given patches #7 and #8, as I understand them, the firmware cannot
> >>> distinguish hotpluggable & present, from hotpluggable & absent. The
> >> firmware
> >>> can only skip both hotpluggable cases. That's fine in that the
> >>> firmware will
> >> hog
> >>> neither type -- but is that OK for the OS as well, for both ACPI
> >>> boot and DT boot?
> >>
> >> Right. This only handles the hotpluggable-and-present condition.
> >>
> >>> Consider in particular the "hotpluggable & present, ACPI boot" case.
> >> Assuming
> >>> we modify the firmware to skip "hotpluggable" altogether, the UEFI
> >>> memmap will not include the range despite it being present at boot.
> >>> Presumably, ACPI will refer to the range somehow, however. Will that not
> confuse the OS?
> >>
> >> From my testing so far, without patches #7 and #8(ie, no UEFI memmap
> >> entry), ACPI boots fine. I think ACPI only relies on aml and SRAT.
> >>
> >>> When Igor raised this earlier, I suggested that
> >>> hotpluggable-and-present should be added by the firmware, but also
> >>> allocated immediately, as EfiBootServicesData type memory. This will
> >>> prevent other drivers in the firmware from allocating AcpiNVS or
> >>> Reserved chunks from the same memory range, the UEFI memmap will
> >>> contain the range as EfiBootServicesData, and then the OS can release
> that allocation in one go early during boot.
> >>
> >> Ok. Agree that hotpluggable-and-present case it may make sense to
> >> make use of that memory rather than just hiding it altogether.
> >>
> >> On another note, Does hotpluggable-and-absent case make any valid use
> >> case for a DT boot? I am not sure how we can hot-add memory in the
> >> case of DT boot later.
> >> I have not verified the sysfs probe interface yet and there are
> >> discussions of dropping that interface altogether.
> >>
> >>> But this really has to be clarified from the Linux kernel's
> >>> expectations. Please formalize all of the following cases:
> >>
> >> Sure. I will wait for suggestions here and work on it.
> >
> > To continue the discussion on this, this is my proposal,
> >
> > [...]
> 
> I didn't miss your last update, on 10 April. The reason I didn't respond then was
> that, the table that you create here, needs to be approved by Linux developers.
> In other words, the table should summarize how Linux expects DT/ACPI to look,
> for the given use cases. It's not something that I can comment on. The
> requirements come from Linux, and we should attempt (in QEMU and the fw)
> to satisfy them.
> 
> If those use cases / requirements haven't been *designed* in Linux, in the first
> place, then the discussion belongs even more on a kernel development list. (I
> really can't say what Linux *should* expect, and even if I had input on that,
> discussing it *just* on qemu-devel would be
> futile.)
> 
> I mean, considering ACPI and the UEFI memmap at least, can we take
> examples from the physical world (I guess x86 too)? What does Linux (and
> maybe Windows) expect wrt. hotpluggable memory areas, in ACPI and in the
> UEFI memmap?
> 
> I find it hard to believe that these are such use cases that we have to
> *invent* now. It seems more likely that OSes already handle these use cases,
> they have expectations, and we should *collect* them.

Right. I have just sent out a mail seeking clarification on this to the wider Linux
community (CCd all of you as well) here,

http://lists.infradead.org/pipermail/linux-arm-kernel/2019-May/651360.html

Hope, we can come to a conclusion soon.

Thanks,
Shameer

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
  2019-05-07  9:01         ` Shameerali Kolothum Thodi
@ 2019-05-09 14:50           ` Igor Mammedov
  0 siblings, 0 replies; 62+ messages in thread
From: Igor Mammedov @ 2019-05-09 14:50 UTC (permalink / raw)
  To: Shameerali Kolothum Thodi
  Cc: peter.maydell, sameo, ard.biesheuvel, qemu-devel, Linuxarm,
	shannon.zhaosl, qemu-arm, eric.auger, xuwei (O),
	sebastien.boeuf, lersek

On Tue, 7 May 2019 09:01:43 +0000
Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com> wrote:

> Hi Igor,
> 
> > -----Original Message-----
> > From: Igor Mammedov [mailto:imammedo@redhat.com]
> > Sent: 03 May 2019 16:10
> > To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>
> > Cc: qemu-devel@nongnu.org; qemu-arm@nongnu.org;
> > eric.auger@redhat.com; peter.maydell@linaro.org;
> > shannon.zhaosl@gmail.com; sameo@linux.intel.com;
> > sebastien.boeuf@intel.com; xuwei (O) <xuwei5@huawei.com>;
> > lersek@redhat.com; ard.biesheuvel@linaro.org; Linuxarm
> > <linuxarm@huawei.com>
> > Subject: Re: [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
> 
> [...]
> 
> > > > type.
> > > > > +         * The resulting ASL code looks like:
> > > > > +         *
> > > > > +         * Local0 = ISEL
> > > > > +         * If ((Local0 & irq0) == irq0)
> > > > > +         * {
> > > > > +         *     MethodEvent0()
> > > > > +         * }
> > > > > +         *
> > > > > +         * If ((Local0 & irq1) == irq1)
> > > > > +         * {
> > > > > +         *     MethodEvent1()
> > > > > +         * }
> > > > > +         * ...
> > > > > +         */
> > > > Well, I'm confused.
> > > > do we actually use multiple IRQs or we use only one + MMIO for event
> > type?
> > >
> > > It is one irq + MMIO. I will change the comment block something like
> > > this,
> > 
> > change corresponding variable names as well
> 
> Ok.
> 
> > >
> > >     Local0 = ISEL
> > >     If ((Local0 & One) == One)
> > >     {
> > >         MethodEvent1()
> > >     }
> > >
> > >     If ((Local0 & 0x02) == 0x02)
> > >     {
> > >         MethodEvent2()
> > >     }
> > >     ...
> > >
> > > >
> > > > > +        for (i = 0; i < s->ged_events_size; i++) {
> > > >
> > > > > +            ged_aml = ged_event_aml(&ged_events[i]);
> > > > > +            if (!ged_aml) {
> > > > > +                continue;
> > > > > +            }
> > > > I'd get rid of ged_event_aml replace it with more 'switch':
> > > >    for (i,...)
> > > >        if_ctx = aml_if(...)
> > > >        switch (event)
> > > >           case GED_MEMORY_HOTPLUG:
> > > >                aml_append(if_ctx,
> > > > aml_call0(MEMORY_DEVICES_CONTAINER "."
> > > > MEMORY_SLOT_SCAN_METHOD))
> > > >                break
> > > >           default:
> > > >                about(); // make sure that a newly added events have
> > > > a handler
> > >
> > > Ok. I will change this.
> > >
> > > >
> > > > > +
> > > > > +            /* If ((Local1 == irq))*/
> > > > > +            if_ctx = aml_if(aml_equal(aml_and(irq_sel,
> > > > > +
> > > > aml_int(ged_events[i].selector), NULL),
> > > > > +
> > > > aml_int(ged_events[i].selector)));
> > > > > +            {
> > > > > +                /* AML for this specific type of event */
> > > > > +                aml_append(if_ctx), ged_aml);
> > > > > +            }
> > > > > +
> > > > > +            /*
> > > > > +             * We append the first "if" to the "while" context.
> > > > > +             * Other "if"s will be "elseif"s.
> > > > > +             */
> > > > > +            aml_append(evt, if_ctx);
> > > > > +        }
> > > > > +    }
> > > > > +
> > > >
> > > > > +    aml_append(dev, aml_name_decl("_HID",
> > aml_string("ACPI0013")));
> > > > > +    aml_append(dev, aml_name_decl("_UID",
> > aml_string(GED_DEVICE)));
> > > > > +    aml_append(dev, aml_name_decl("_CRS", crs));
> > > > > +
> > > > > +    /* Append IO region */
> > > > > +    aml_append(dev, aml_operation_region(AML_GED_IRQ_REG, rs,
> > > > > +               aml_int(s->ged_base + ACPI_GED_IRQ_SEL_OFFSET),
> > > > > +               ACPI_GED_IRQ_SEL_LEN));
> > > > > +    field = aml_field(AML_GED_IRQ_REG, AML_DWORD_ACC,
> > > > AML_NOLOCK,
> > > > > +                      AML_WRITE_AS_ZEROS);
> > > > > +    aml_append(field, aml_named_field(AML_GED_IRQ_SEL,
> > > > > +                                      ACPI_GED_IRQ_SEL_LEN
> > *
> > > > 8));
> > > > > +    aml_append(dev, field);
> > > >
> > > > I'd move it up above EVT() method, so it would be clear from the
> > > > begging for what device we are building AML
> > >
> > > Ok.
> > >
> > > >
> > > > > +    /* Append _EVT method */
> > > > > +    aml_append(dev, evt);
> > > > > +
> > > > > +    aml_append(table, dev);
> > > > > +}
> > > > > +
> > > > > +/* Memory read by the GED _EVT AML dynamic method */ static
> > > > > +uint64_t ged_read(void *opaque, hwaddr addr, unsigned size) {
> > > > > +    uint64_t val = 0;
> > > > > +    GEDState *ged_st = opaque;
> > > > > +
> > > > > +    switch (addr) {
> > > > > +    case ACPI_GED_IRQ_SEL_OFFSET:
> > > > > +        /* Read the selector value and reset it */
> > > > > +        qemu_mutex_lock(&ged_st->lock);
> > > > > +        val = ged_st->sel;
> > > > > +        ged_st->sel = 0;
> > > > > +        qemu_mutex_unlock(&ged_st->lock);
> > > > > +        break;
> > > > > +    default:
> > > > > +        break;
> > > > > +    }
> > > > > +
> > > > > +    return val;
> > > > > +}
> > > > > +
> > > > > +/* Nothing is expected to be written to the GED memory region */
> > > > > +static void ged_write(void *opaque, hwaddr addr, uint64_t data,
> > > > > +                      unsigned int size) { }
> > > > > +
> > > > > +static const MemoryRegionOps ged_ops = {
> > > > > +    .read = ged_read,
> > > > > +    .write = ged_write,
> > > > > +    .endianness = DEVICE_LITTLE_ENDIAN,
> > > > > +    .valid = {
> > > > > +        .min_access_size = 4,
> > > > > +        .max_access_size = 4,
> > > > > +    },
> > > > > +};
> > > > > +
> > > > > +static void acpi_ged_event(GEDState *ged_st, uint32_t
> > > > > +ged_irq_sel) {
> > > > > +    /*
> > > > > +     * Set the GED IRQ selector to the expected device type value. This
> > > > > +     * way, the ACPI method will be able to trigger the right code based
> > > > > +     * on a unique IRQ.
> > > > > +     */
> > > > > +    qemu_mutex_lock(&ged_st->lock);
> > > > > +    ged_st->sel = ged_irq_sel;
> > > > what if there are 2 events with different sel value and 2nd event
> > > > happens before guesr read the first one?
> > >
> > > This was previously,
> > >    ged_st->sel |= ged_irq_sel;
> > >
> > > and was changed based on the assumption that there won't be any
> > > multiple events. But I think the scenario above is right. I will
> > > change it back so that we won't overwrite.
> > so it was bitmap, then handling it as a DWORD in AML could be wrong and AML
> > part probably should be changed to accommodate it.
> 
> Hmm..I am slightly confused as the AML code already uses the len as 32 bits for this
> register field,
> 
>     /* Append IO region */
>     aml_append(dev, aml_operation_region(AML_GED_IRQ_REG, rs,
>                aml_int(s->ged_base + ACPI_GED_IRQ_SEL_OFFSET),
>                ACPI_GED_IRQ_SEL_LEN));
>     field = aml_field(AML_GED_IRQ_REG, AML_DWORD_ACC, AML_NOLOCK,
>                       AML_WRITE_AS_ZEROS);
>     aml_append(field, aml_named_field(AML_GED_IRQ_SEL,
>                                       ACPI_GED_IRQ_SEL_LEN * 8));
>     aml_append(dev, field);
> 
> and event check logic is, 
> 
>      if_ctx = aml_if(aml_equal(aml_and(irq_sel,
>                           aml_int(ged_events[i].selector), NULL),
>                           aml_int(ged_events[i].selector)));
> 
> Not sure what is the concern here. Please let me know. 

Looking at it second time, it seems I've been mistaken.
that 'if' should work.

one suggestion how to improve AML side,
instead of using  AML_GED_IRQ_REG DWORD + AND masking
you can use bit fields directly.
See how "aml_named_field(...,1)" is used for inspiration.


>             
> > >
> > > >
> > > > > +    qemu_mutex_unlock(&ged_st->lock);
> > > > > +
> > > > > +    /* Trigger the event by sending an interrupt to the guest. */
> > > > > +    qemu_irq_pulse(ged_st->gsi[ged_st->irq]);
> > > > > +}
> > > > > +
> > > > > +static void acpi_ged_init(MemoryRegion *as, DeviceState *dev,
> > > > > +GEDState
> > > > *ged_st)
> > > > > +{
> > > > > +    AcpiGedState *s = ACPI_GED(dev);
> > > > > +
> > > > > +    assert(s->ged_base);
> > > > > +
> > > > > +    ged_st->irq = s->ged_irq;
> > > > > +    ged_st->gsi = s->gsi;
> > > > > +    qemu_mutex_init(&ged_st->lock);
> > > > > +    memory_region_init_io(&ged_st->io, OBJECT(dev), &ged_ops,
> > ged_st,
> > > > > +                          "acpi-ged-event", ACPI_GED_REG_LEN);
> > > > > +    memory_region_add_subregion(as, s->ged_base, &ged_st->io); }
> > > > > +
> > > > > +static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev,
> > > > > +                                    DeviceState *dev, Error
> > > > > +**errp) {
> > > > > +    AcpiGedState *s = ACPI_GED(hotplug_dev);
> > > > > +
> > > > > +    if (s->memhp_state.is_enabled &&
> > > > > +        object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> > > > > +            acpi_memory_plug_cb(hotplug_dev, &s->memhp_state,
> > > > > +                                dev, errp);
> > > > > +    } else {
> > > > > +        error_setg(errp, "virt: device plug request for
> > > > > + unsupported
> > > > device"
> > > > > +                   " type: %s",
> > object_get_typename(OBJECT(dev)));
> > > > > +    }
> > > > > +}
> > > > > +
> > > > > +static void acpi_ged_send_event(AcpiDeviceIf *adev,
> > > > > +AcpiEventStatusBits
> > > > ev)
> > > > > +{
> > > > > +    AcpiGedState *s = ACPI_GED(adev);
> > > > > +    uint32_t sel;
> > > > > +
> > > > > +    if (ev & ACPI_MEMORY_HOTPLUG_STATUS) {
> > > > > +        sel = ACPI_GED_IRQ_SEL_MEM;
> > > > > +    } else {
> > > > > +        /* Unknown event. Return without generating interrupt. */
> > > > > +        return;
> > > > > +    }
> > > > > +
> > > > > +    /*
> > > > > +     * We inject the hotplug interrupt. The IRQ selector will make
> > > > > +     * the difference from the ACPI table.
> > > > > +     */
> > > > > +    acpi_ged_event(&s->ged_state, sel); }
> > > > > +
> > > > > +static void acpi_ged_device_realize(DeviceState *dev, Error
> > > > > +**errp) {
> > > > > +    AcpiGedState *s = ACPI_GED(dev);
> > > > > +
> > > > > +    if (s->memhp_state.is_enabled) {
> > > > > +        acpi_memory_hotplug_init(get_system_memory(),
> > OBJECT(dev),
> > > > > +                                 &s->memhp_state,
> > > > > +                                 s->memhp_base);
> > > > > +        acpi_ged_init(get_system_memory(), dev, &s->ged_state);
> > > > > +    }
> > > > > +}
> > > > > +
> > > > > +static Property acpi_ged_properties[] = {
> > > > > +    /*
> > > > > +     * Memory hotplug base address is a property of GED here,
> > > > > +     * because GED handles memory hotplug event and
> > > > MEMORY_HOTPLUG_DEVICE
> > > > > +     * gets initialized when GED device is realized.
> > > > > +     */
> > > > > +    DEFINE_PROP_UINT64("memhp-base", AcpiGedState,
> > memhp_base,
> > > > 0),
> > > > > +    DEFINE_PROP_BOOL("memory-hotplug-support", AcpiGedState,
> > > > > +                     memhp_state.is_enabled, true),
> > > > > +    DEFINE_PROP_PTR("gsi", AcpiGedState, gsi),
> > > >
> > > > PTR shouldn't be used in new code, look at
> > > > object_property_add_link() & co
> > >
> > > Ok. I will take a look at that.
> > >
> > > >
> > > > > +    DEFINE_PROP_UINT64("ged-base", AcpiGedState, ged_base, 0),
> > > > > +    DEFINE_PROP_UINT32("ged-irq", AcpiGedState, ged_irq, 0),
> > > > > +    DEFINE_PROP_PTR("ged-events", AcpiGedState, ged_events),
> > > > > +    DEFINE_PROP_UINT32("ged-events-size", AcpiGedState,
> > > > ged_events_size, 0),
> > > > > +    DEFINE_PROP_END_OF_LIST(),
> > > > > +};
> > > > > +
> > > > > +static void acpi_ged_class_init(ObjectClass *class, void *data) {
> > > > > +    DeviceClass *dc = DEVICE_CLASS(class);
> > > > > +    HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class);
> > > > > +    AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class);
> > > > > +
> > > > > +    dc->desc = "ACPI";
> > > > > +    dc->props = acpi_ged_properties;
> > > > > +    dc->realize = acpi_ged_device_realize;
> > > > > +
> > > > > +    /* Reason: pointer properties "gsi" and "ged_events" */
> > > > > +    dc->user_creatable = false;
> > > > > +
> > > > > +    hc->plug = acpi_ged_device_plug_cb;
> > > > > +
> > > > > +    adevc->send_event = acpi_ged_send_event; }
> > > > > +
> > > > > +static const TypeInfo acpi_ged_info = {
> > > > > +    .name          = TYPE_ACPI_GED,
> > > > > +    .parent        = TYPE_DEVICE,
> > > > > +    .instance_size = sizeof(AcpiGedState),
> > > > > +    .class_init    = acpi_ged_class_init,
> > > > > +    .interfaces = (InterfaceInfo[]) {
> > > > > +        { TYPE_HOTPLUG_HANDLER },
> > > > > +        { TYPE_ACPI_DEVICE_IF },
> > > > > +        { }
> > > > > +    }
> > > > > +};
> > > > > +
> > > > > +static void acpi_ged_register_types(void) {
> > > > > +    type_register_static(&acpi_ged_info);
> > > > > +}
> > > > > +
> > > > > +type_init(acpi_ged_register_types)
> > > > > diff --git a/include/hw/acpi/generic_event_device.h
> > > > b/include/hw/acpi/generic_event_device.h
> > > > > new file mode 100644
> > > > > index 0000000..9c840d8
> > > > > --- /dev/null
> > > > > +++ b/include/hw/acpi/generic_event_device.h
> > > > > @@ -0,0 +1,121 @@
> > > > > +/*
> > > > > + *
> > > > > + * Copyright (c) 2018 Intel Corporation
> > > > > + *
> > > > > + * This program is free software; you can redistribute it and/or
> > > > > +modify it
> > > > > + * under the terms and conditions of the GNU General Public
> > > > > +License,
> > > > > + * version 2 or later, as published by the Free Software Foundation.
> > > > > + *
> > > > > + * This program is distributed in the hope it will be useful, but
> > > > > +WITHOUT
> > > > > + * ANY WARRANTY; without even the implied warranty of
> > > > MERCHANTABILITY or
> > > > > + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
> > > > License for
> > > > > + * more details.
> > > > > + *
> > > > > + * You should have received a copy of the GNU General Public
> > > > > + License along
> > > > with
> > > > > + * this program.  If not, see <http://www.gnu.org/licenses/>.
> > > > > + *
> > > > > + * The ACPI Generic Event Device (GED) is a hardware-reduced
> > > > > + specific
> > > > > + * device[ACPI v6.1 Section 5.6.9] that handles all platform
> > > > > + events,
> > > > > + * including the hotplug ones. Generic Event Device allows
> > > > > + platforms
> > > > > + * to handle interrupts in ACPI ASL statements. It follows a very
> > > > > + * similar approach like the _EVT method from GPIO events. All
> > > > > + * interrupts are listed in  _CRS and the handler is written in
> > > > > + _EVT
> > > > > + * method. Here, we use a single interrupt for the GED device,
> > > > > + relying
> > > > > + * on IO memory region to communicate the type of device affected
> > > > > + by
> > > > > + * the interrupt. This way, we can support up to 32 events with a
> > > > > + * unique interrupt.
> > > > > + *
> > > > > + * Here is an example.
> > > > > + *
> > > > > + * Device (\_SB.GED)
> > > > > + * {
> > > > > + *     Name (_HID, "ACPI0013")
> > > > > + *     Name (_UID, Zero)
> > > > > + *     Name (_CRS, ResourceTemplate ()
> > > > > + *     {
> > > > > + *         Interrupt (ResourceConsumer, Edge, ActiveHigh,
> > Exclusive, ,, )
> > > > > + *         {
> > > > > + *              0x00000029,
> > > > > + *         }
> > > > > + *     })
> > > > > + *     OperationRegion (IREG, SystemMemory, 0x09080000, 0x04)
> > > > > + *     Field (IREG, DWordAcc, NoLock, WriteAsZeros)
> > > > > + *     {
> > > > > + *         ISEL,   32
> > > > > + *     }
> > > > > + *
> > > > > + *     Method (_EVT, 1, Serialized)  // _EVT: Event
> > > > > + *     {
> > > > > + *         Local0 = ISEL // ISEL = IO memory region which specifies the
> > > > > + *                       // device type.
> > > > > + *         If (((Local0 & irq0) == irq0))
> > > > > + *         {
> > > > > + *             MethodEvent0()
> > > > > + *         }
> > > > > + *         ElseIf ((Local0 & irq1) == irq1)
> > > > > + *         {
> > > > > + *             MethodEvent1()
> > > > > + *         }
> > > > > + *         ...
> > > > > + *     }
> > > > > + * }
> > > > > + *
> > > > > + */
> > > > > +
> > > > > +#ifndef HW_ACPI_GED_H
> > > > > +#define HW_ACPI_GED_H
> > > > > +
> > > > > +#include "hw/acpi/memory_hotplug.h"
> > > > > +
> > > > > +#define TYPE_ACPI_GED "acpi-ged"
> > > > > +#define ACPI_GED(obj) \
> > > > > +    OBJECT_CHECK(AcpiGedState, (obj), TYPE_ACPI_GED)
> > > > > +
> > > > > +#define ACPI_GED_IRQ_SEL_OFFSET 0x0
> > > > > +#define ACPI_GED_IRQ_SEL_LEN    0x4
> > > > > +#define ACPI_GED_IRQ_SEL_MEM    0x1
> > > > > +#define ACPI_GED_REG_LEN        0x4
> > > > > +
> > > > > +#define GED_DEVICE      "GED"
> > > > > +#define AML_GED_IRQ_REG "IREG"
> > > > > +#define AML_GED_IRQ_SEL "ISEL"
> > > > > +
> > > > > +typedef enum {
> > > > > +    GED_MEMORY_HOTPLUG = 1,
> > > > > +} GedEventType;
> > > > > +
> > > > > +/*
> > > > > + * Platforms need to specify their own GedEvent array
> > > > > + * to describe what kind of events they want to support
> > > > > + * through GED.
> > > > > + */
> > > > > +typedef struct GedEvent {
> > > > > +    uint32_t     selector;
> > > > > +    GedEventType event;
> > > > > +} GedEvent;
> > > > > +
> > > > > +typedef struct GEDState {
> > > > > +    MemoryRegion io;
> > > > > +    uint32_t     sel;
> > > >
> > > > do we need to migrate this during migration?
> > >
> > > TBH, I don't know and currently this series doesn't address migration
> > > as we don't have any VMStateDescription and friends. Is this something
> > > we can sort later?
> > 
> > It probably should be implemented, otherwise we will have to deal with hard to
> > debug bug reports when users will try to migrate.
> > 
> > Alternative hack could be that, enabling memory hotplug should put migration
> > blocker, but that's probably the same effort as adding proper
> > VMStateDescription
> 
> Ok. I will take a look into VMStateDescription then.
> 
> Thanks,
> Shameer
> 
> > >
> > > Thanks,
> > > Shameer
> > >
> > > > > +    uint32_t     irq;
> > > > > +    qemu_irq     *gsi;
> > > > > +    QemuMutex    lock;
> > > > > +} GEDState;
> > > > > +
> > > > > +
> > > > > +typedef struct AcpiGedState {
> > > > > +    DeviceClass parent_obj;
> > > > > +    MemHotplugState memhp_state;
> > > > > +    hwaddr memhp_base;
> > > > > +    void *gsi;
> > > > > +    hwaddr ged_base;
> > > > > +    GEDState ged_state;
> > > > > +    uint32_t ged_irq;
> > > > > +    void *ged_events;
> > > > > +    uint32_t ged_events_size;
> > > > > +} AcpiGedState;
> > > > > +
> > > > > +void build_ged_aml(Aml *table, const char* name, HotplugHandler
> > > > *hotplug_dev,
> > > > > +                   uint32_t ged_irq, AmlRegionSpace rs);
> > > > > +
> > > > > +#endif
> > >
> 
> 



^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 5/8] hw/arm/virt: Enable device memory cold/hot plug with ACPI boot
  2019-04-09 10:29   ` Shameer Kolothum
  (?)
  (?)
@ 2019-05-09 15:20   ` Igor Mammedov
  -1 siblings, 0 replies; 62+ messages in thread
From: Igor Mammedov @ 2019-05-09 15:20 UTC (permalink / raw)
  To: Shameer Kolothum
  Cc: peter.maydell, sameo, ard.biesheuvel, shannon.zhaosl, qemu-devel,
	xuwei5, linuxarm, eric.auger, qemu-arm, sebastien.boeuf, lersek

On Tue, 9 Apr 2019 11:29:32 +0100
Shameer Kolothum <shameerali.kolothum.thodi@huawei.com> wrote:

> This initializes the GED device with base memory and irq, configures
> ged memory hotplug event and builds the corresponding aml code. GED
> irq routing to Guest is also enabled. With this, both hot and cold
> plug of device memory is enabled now for Guest with ACPI boot.
> 
> Memory cold plug support with Guest DT boot is not yet supported.
> 
> Signed-off-by: Shameer Kolothum <shameerali.kolothum.thodi@huawei.com>
> ---
>  default-configs/arm-softmmu.mak |  2 ++
>  hw/arm/virt-acpi-build.c        |  9 ++++++
>  hw/arm/virt.c                   | 61 +++++++++++++++++++++++++++++++++++++++--
>  include/hw/arm/virt.h           |  4 +++
>  4 files changed, 73 insertions(+), 3 deletions(-)
> 
> diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
> index 9f4b803..c9a9b34 100644
> --- a/default-configs/arm-softmmu.mak
> +++ b/default-configs/arm-softmmu.mak
> @@ -163,3 +163,5 @@ CONFIG_LSI_SCSI_PCI=y
>  
>  CONFIG_MEM_DEVICE=y
>  CONFIG_DIMM=y
> +CONFIG_ACPI_MEMORY_HOTPLUG=y
> +CONFIG_ACPI_HW_REDUCED=y
> diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c
> index bf9c0bc..1ad394b 100644
> --- a/hw/arm/virt-acpi-build.c
> +++ b/hw/arm/virt-acpi-build.c
> @@ -40,6 +40,8 @@
>  #include "hw/loader.h"
>  #include "hw/hw.h"
>  #include "hw/acpi/aml-build.h"
> +#include "hw/acpi/memory_hotplug.h"
> +#include "hw/acpi/generic_event_device.h"
>  #include "hw/pci/pcie_host.h"
>  #include "hw/pci/pci.h"
>  #include "hw/arm/virt.h"
> @@ -727,6 +729,7 @@ static void
>  build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
>  {
>      Aml *scope, *dsdt;
> +    MachineState *ms = MACHINE(vms);
>      const MemMapEntry *memmap = vms->memmap;
>      const int *irqmap = vms->irqmap;
>  
> @@ -753,6 +756,12 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
>                         (irqmap[VIRT_GPIO] + ARM_SPI_BASE));
>      acpi_dsdt_add_power_button(scope);
>  
> +    build_ged_aml(scope, "\\_SB."GED_DEVICE, HOTPLUG_HANDLER(vms->acpi_dev),
> +                  irqmap[VIRT_ACPI_GED] + ARM_SPI_BASE, AML_SYSTEM_MEMORY);
> +
> +    build_memory_hotplug_aml(scope, ms->ram_slots, "\\_SB", NULL,
> +                             AML_SYSTEM_MEMORY);
>
are you sure we should add these parts if acpi_device not present or ms->ram_slots == 0 ?

>      aml_append(dsdt, scope);
>  
>      /* copy AML table into ACPI tables blob and patch header there */

[...]

> +
>  static void create_gic(VirtMachineState *vms, qemu_irq *pic)
>  {
>      /* We create a standalone GIC */
> @@ -670,6 +707,8 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic)
>          pic[i] = qdev_get_gpio_in(gicdev, i);
>      }
>  
> +    vms->gsi = qemu_allocate_irqs(virt_gsi_handler, pic, NUM_IRQS);

Why do we need this "gsi" to begin with?

> +
>      fdt_add_gic_node(vms);
>  
>      if (type == 3 && vms->its) {
> @@ -1418,7 +1457,7 @@ static void machvirt_init(MachineState *machine)
>      VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(machine);
>      MachineClass *mc = MACHINE_GET_CLASS(machine);
>      const CPUArchIdList *possible_cpus;
> -    qemu_irq pic[NUM_IRQS];
> +    qemu_irq *pic;
>      MemoryRegion *sysmem = get_system_memory();
>      MemoryRegion *secure_sysmem = NULL;
>      int n, virt_max_cpus;
> @@ -1614,6 +1653,7 @@ static void machvirt_init(MachineState *machine)
>  
>      create_flash(vms, sysmem, secure_sysmem ? secure_sysmem : sysmem);
>  
> +    pic = g_new0(qemu_irq, NUM_IRQS);
>      create_gic(vms, pic);
>  
>      fdt_add_pmu_nodes(vms);
> @@ -1644,6 +1684,8 @@ static void machvirt_init(MachineState *machine)
>  
>      create_platform_bus(vms, pic);
>  
> +    vms->acpi_dev = create_acpi_ged(vms);

what if we start QEMU with -no-acpi?

>      vms->bootinfo.ram_size = machine->ram_size;
>      vms->bootinfo.kernel_filename = machine->kernel_filename;
>      vms->bootinfo.kernel_cmdline = machine->kernel_cmdline;
> @@ -1811,21 +1853,34 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms)
>  static void virt_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
>                                   Error **errp)
>  {
> -    if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
> -        error_setg(errp, "memory cold/hot plug is not yet supported");
> +    VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
> +    const bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM);
> +
> +    if (is_nvdimm) {
> +        error_setg(errp, "nvdimm is not yet supported");
>          return;
>      }
>  
> +    if (!vms->acpi_dev) {
> +        error_setg(errp, "memory hotplug is not enabled: missing acpi device");
> +        return;
> +    }
> +
> +    hotplug_handler_pre_plug(HOTPLUG_HANDLER(vms->acpi_dev), dev, errp);
> +
>      pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), NULL, errp);
>  }
>  
>  static void virt_memory_plug(HotplugHandler *hotplug_dev,
>                               DeviceState *dev, Error **errp)
>  {
> +    HotplugHandlerClass *hhc;
>      VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
>  
>      pc_dimm_plug(PC_DIMM(dev), MACHINE(vms), NULL);
>  
> +    hhc = HOTPLUG_HANDLER_GET_CLASS(vms->acpi_dev);
> +    hhc->plug(HOTPLUG_HANDLER(vms->acpi_dev), dev, NULL);
>  }
>  
>  static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
> diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
> index 507517c..9c6b0b1 100644
> --- a/include/hw/arm/virt.h
> +++ b/include/hw/arm/virt.h
> @@ -77,6 +77,8 @@ enum {
>      VIRT_GPIO,
>      VIRT_SECURE_UART,
>      VIRT_SECURE_MEM,
> +    VIRT_PCDIMM_ACPI,
> +    VIRT_ACPI_GED,
>      VIRT_LOWMEMMAP_LAST,
>  };
>  
> @@ -132,6 +134,8 @@ typedef struct {
>      uint32_t iommu_phandle;
>      int psci_conduit;
>      hwaddr highest_gpa;
> +    DeviceState *acpi_dev;
> +    qemu_irq *gsi;
>  } VirtMachineState;
>  
>  #define VIRT_ECAM_ID(high) (high ? VIRT_HIGH_PCIE_ECAM : VIRT_PCIE_ECAM)



^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
  2019-05-02 16:12     ` Igor Mammedov
  (?)
  (?)
@ 2019-05-13 11:53     ` Shameerali Kolothum Thodi
  2019-05-13 17:00       ` Shameerali Kolothum Thodi
  -1 siblings, 1 reply; 62+ messages in thread
From: Shameerali Kolothum Thodi @ 2019-05-13 11:53 UTC (permalink / raw)
  To: Igor Mammedov
  Cc: peter.maydell, sameo, shannon.zhaosl, ard.biesheuvel, qemu-devel,
	Linuxarm, eric.auger, qemu-arm, xuwei (O),
	sebastien.boeuf, lersek

Hi Igor,

> -----Original Message-----
> From: Shameerali Kolothum Thodi
> Sent: 03 May 2019 13:46
> To: 'Igor Mammedov' <imammedo@redhat.com>
> Cc: qemu-devel@nongnu.org; qemu-arm@nongnu.org;
> eric.auger@redhat.com; peter.maydell@linaro.org;
> shannon.zhaosl@gmail.com; sameo@linux.intel.com;
> sebastien.boeuf@intel.com; xuwei (O) <xuwei5@huawei.com>;
> lersek@redhat.com; ard.biesheuvel@linaro.org; Linuxarm
> <linuxarm@huawei.com>
> Subject: RE: [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
> 
> Hi Igor,
> 
> > -----Original Message-----
> > From: Igor Mammedov [mailto:imammedo@redhat.com]
> > Sent: 02 May 2019 17:13
> > To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>
> > Cc: qemu-devel@nongnu.org; qemu-arm@nongnu.org;
> > eric.auger@redhat.com; peter.maydell@linaro.org;
> > shannon.zhaosl@gmail.com; sameo@linux.intel.com;
> > sebastien.boeuf@intel.com; xuwei (O) <xuwei5@huawei.com>;
> > lersek@redhat.com; ard.biesheuvel@linaro.org; Linuxarm
> > <linuxarm@huawei.com>
> > Subject: Re: [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
> >

[...]

> > > +}
> > > +
> > > +static Property acpi_ged_properties[] = {
> > > +    /*
> > > +     * Memory hotplug base address is a property of GED here,
> > > +     * because GED handles memory hotplug event and
> > MEMORY_HOTPLUG_DEVICE
> > > +     * gets initialized when GED device is realized.
> > > +     */
> > > +    DEFINE_PROP_UINT64("memhp-base", AcpiGedState, memhp_base,
> > 0),
> > > +    DEFINE_PROP_BOOL("memory-hotplug-support", AcpiGedState,
> > > +                     memhp_state.is_enabled, true),
> > > +    DEFINE_PROP_PTR("gsi", AcpiGedState, gsi),
> >
> > PTR shouldn't be used in new code, look at object_property_add_link() & co
> 
> Ok. I will take a look at that.

I attempted to remove _PROP_PTR for "ged-events" and use _PROP_LINK and
_set_link(),


diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c
index 856ca04c01..978c8e088e 100644
--- a/hw/acpi/generic_event_device.c
+++ b/hw/acpi/generic_event_device.c
@@ -268,7 +268,8 @@ static Property acpi_ged_properties[] = {
     DEFINE_PROP_PTR("gsi", AcpiGedState, gsi),
     DEFINE_PROP_UINT64("ged-base", AcpiGedState, ged_base, 0),
     DEFINE_PROP_UINT32("ged-irq", AcpiGedState, ged_irq, 0),
-    DEFINE_PROP_PTR("ged-events", AcpiGedState, ged_events),
+    DEFINE_PROP_LINK("ged-events", AcpiGedState, ged_events, TYPE_ACPI_GED,
+                     GedEvent *),
     DEFINE_PROP_UINT32("ged-events-size", AcpiGedState, ged_events_size, 0),
     DEFINE_PROP_END_OF_LIST(),
 };
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 8179b3e511..c89b7b7120 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -537,7 +537,8 @@ static inline DeviceState *create_acpi_ged(VirtMachineState *vms)
     qdev_prop_set_ptr(dev, "gsi", vms->gsi);
     qdev_prop_set_uint64(dev, "ged-base", vms->memmap[VIRT_ACPI_GED].base);
     qdev_prop_set_uint32(dev, "ged-irq", vms->irqmap[VIRT_ACPI_GED]);
-    qdev_prop_set_ptr(dev, "ged-events", ged_events);
+    object_property_set_link(OBJECT(dev), OBJECT(ged_events), "ged-events",
+                             &error_abort);
     qdev_prop_set_uint32(dev, "ged-events-size", ARRAY_SIZE(ged_events));
 
     object_property_add_child(qdev_get_machine(), "acpi-ged",
diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h
index 9c840d8064..588f4ecfba 100644
--- a/include/hw/acpi/generic_event_device.h
+++ b/include/hw/acpi/generic_event_device.h
@@ -111,7 +111,7 @@ typedef struct AcpiGedState {
     hwaddr ged_base;
     GEDState ged_state;
     uint32_t ged_irq;
-    void *ged_events;
+    GedEvent *ged_events;
     uint32_t ged_events_size;
 } AcpiGedState;


And with this I get,

Segmentation fault      (core dumped) ./qemu-system-aarch64-ged-v5
-machine virt, -cpu cortex-a57 -machine type=virt -nographic -smp 1 -m
4G,maxmem=8G,slots=10 -drive if=none,file=ubuntu-est-5.0,id=fs -device
virtio-blk-device,drive=fs -kernel Image_memhp_remove -bios
QEMU_EFI_Release.fd -object memory-backend-ram,id=mem1,size=1G -device
pc-dimm,id=dimm1,memdev=mem1 -numa node,nodeid=0 -append
"console=ttyAMA0 root=/dev/vda rw acpi=force movable_node"

It looks like struct pointer cannot be used directly and has to make a QOM object
for DEFINE_PROP_LINK use. Not sure there is an easy way for setting ptr property
using link() functions. Please let me know if there any reference implementation I
can take a look.

Appreciate your help,

Thanks,
Shameer



^ permalink raw reply related	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
  2019-05-13 11:53     ` Shameerali Kolothum Thodi
@ 2019-05-13 17:00       ` Shameerali Kolothum Thodi
  2019-05-17  8:41         ` Igor Mammedov
  0 siblings, 1 reply; 62+ messages in thread
From: Shameerali Kolothum Thodi @ 2019-05-13 17:00 UTC (permalink / raw)
  To: Igor Mammedov
  Cc: peter.maydell, sameo, ard.biesheuvel, qemu-devel, Linuxarm,
	shannon.zhaosl, qemu-arm, eric.auger, xuwei (O),
	sebastien.boeuf, lersek

> -----Original Message-----
> From: Igor Mammedov [mailto:imammedo@redhat.com]
> Sent: 13 May 2019 17:25
> To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>
> Subject: Re: [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
> 
> On Mon, 13 May 2019 11:53:38 +0000
> Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com> wrote:
> 
> > Hi Igor,
> >
> > > -----Original Message-----
> > > From: Shameerali Kolothum Thodi
> > > Sent: 03 May 2019 13:46
> > > To: 'Igor Mammedov' <imammedo@redhat.com>
> > > Cc: qemu-devel@nongnu.org; qemu-arm@nongnu.org;
> > > eric.auger@redhat.com; peter.maydell@linaro.org;
> > > shannon.zhaosl@gmail.com; sameo@linux.intel.com;
> > > sebastien.boeuf@intel.com; xuwei (O) <xuwei5@huawei.com>;
> > > lersek@redhat.com; ard.biesheuvel@linaro.org; Linuxarm
> > > <linuxarm@huawei.com>
> > > Subject: RE: [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device
> Support
> > >
> > > Hi Igor,
> > >
> > > > -----Original Message-----
> > > > From: Igor Mammedov [mailto:imammedo@redhat.com]
> > > > Sent: 02 May 2019 17:13
> > > > To: Shameerali Kolothum Thodi
> <shameerali.kolothum.thodi@huawei.com>
> > > > Cc: qemu-devel@nongnu.org; qemu-arm@nongnu.org;
> > > > eric.auger@redhat.com; peter.maydell@linaro.org;
> > > > shannon.zhaosl@gmail.com; sameo@linux.intel.com;
> > > > sebastien.boeuf@intel.com; xuwei (O) <xuwei5@huawei.com>;
> > > > lersek@redhat.com; ard.biesheuvel@linaro.org; Linuxarm
> > > > <linuxarm@huawei.com>
> > > > Subject: Re: [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device
> Support
> > > >
> >
> > [...]
> >
> > > > > +}
> > > > > +
> > > > > +static Property acpi_ged_properties[] = {
> > > > > +    /*
> > > > > +     * Memory hotplug base address is a property of GED here,
> > > > > +     * because GED handles memory hotplug event and
> > > > MEMORY_HOTPLUG_DEVICE
> > > > > +     * gets initialized when GED device is realized.
> > > > > +     */
> > > > > +    DEFINE_PROP_UINT64("memhp-base", AcpiGedState,
> memhp_base,
> > > > 0),
> > > > > +    DEFINE_PROP_BOOL("memory-hotplug-support", AcpiGedState,
> > > > > +                     memhp_state.is_enabled, true),
> > > > > +    DEFINE_PROP_PTR("gsi", AcpiGedState, gsi),
> > > >
> > > > PTR shouldn't be used in new code, look at object_property_add_link() &
> co
> > >
> > > Ok. I will take a look at that.
> >
> > I attempted to remove _PROP_PTR for "ged-events" and use _PROP_LINK
> and
> > _set_link(),
> >
> >
> > diff --git a/hw/acpi/generic_event_device.c
> b/hw/acpi/generic_event_device.c
> > index 856ca04c01..978c8e088e 100644
> > --- a/hw/acpi/generic_event_device.c
> > +++ b/hw/acpi/generic_event_device.c
> > @@ -268,7 +268,8 @@ static Property acpi_ged_properties[] = {
> >      DEFINE_PROP_PTR("gsi", AcpiGedState, gsi),
> >      DEFINE_PROP_UINT64("ged-base", AcpiGedState, ged_base, 0),
> >      DEFINE_PROP_UINT32("ged-irq", AcpiGedState, ged_irq, 0),
> > -    DEFINE_PROP_PTR("ged-events", AcpiGedState, ged_events),
> > +    DEFINE_PROP_LINK("ged-events", AcpiGedState, ged_events,
> TYPE_ACPI_GED,
> > +                     GedEvent *),
> >      DEFINE_PROP_UINT32("ged-events-size", AcpiGedState,
> ged_events_size, 0),
> >      DEFINE_PROP_END_OF_LIST(),
> >  };
> > diff --git a/hw/arm/virt.c b/hw/arm/virt.c
> > index 8179b3e511..c89b7b7120 100644
> > --- a/hw/arm/virt.c
> > +++ b/hw/arm/virt.c
> > @@ -537,7 +537,8 @@ static inline DeviceState
> *create_acpi_ged(VirtMachineState *vms)
> >      qdev_prop_set_ptr(dev, "gsi", vms->gsi);
> >      qdev_prop_set_uint64(dev, "ged-base",
> vms->memmap[VIRT_ACPI_GED].base);
> >      qdev_prop_set_uint32(dev, "ged-irq", vms->irqmap[VIRT_ACPI_GED]);
> > -    qdev_prop_set_ptr(dev, "ged-events", ged_events);
> > +    object_property_set_link(OBJECT(dev), OBJECT(ged_events),
> "ged-events",
> > +                             &error_abort);
> >      qdev_prop_set_uint32(dev, "ged-events-size",
> ARRAY_SIZE(ged_events));
> >
> >      object_property_add_child(qdev_get_machine(), "acpi-ged",
> > diff --git a/include/hw/acpi/generic_event_device.h
> b/include/hw/acpi/generic_event_device.h
> > index 9c840d8064..588f4ecfba 100644
> > --- a/include/hw/acpi/generic_event_device.h
> > +++ b/include/hw/acpi/generic_event_device.h
> > @@ -111,7 +111,7 @@ typedef struct AcpiGedState {
> >      hwaddr ged_base;
> >      GEDState ged_state;
> >      uint32_t ged_irq;
> > -    void *ged_events;
> > +    GedEvent *ged_events;
> >      uint32_t ged_events_size;
> >  } AcpiGedState;
> >
> >
> > And with this I get,
> >
> > Segmentation fault      (core dumped) ./qemu-system-aarch64-ged-v5
> > -machine virt, -cpu cortex-a57 -machine type=virt -nographic -smp 1 -m
> > 4G,maxmem=8G,slots=10 -drive if=none,file=ubuntu-est-5.0,id=fs -device
> > virtio-blk-device,drive=fs -kernel Image_memhp_remove -bios
> > QEMU_EFI_Release.fd -object memory-backend-ram,id=mem1,size=1G
> -device
> > pc-dimm,id=dimm1,memdev=mem1 -numa node,nodeid=0 -append
> > "console=ttyAMA0 root=/dev/vda rw acpi=force movable_node"
> >
> > It looks like struct pointer cannot be used directly and has to make a QOM
> object
> > for DEFINE_PROP_LINK use. Not sure there is an easy way for setting ptr
> property
> > using link() functions. Please let me know if there any reference
> implementation I
> > can take a look.
> 
> Simple 'struct' won't work with link, it has to be QOM object.
> 
> Question is do we still need GedEvent array?
> We handle only 1 irq and several event types, why not replace GedEvent
> with with a 32bit bitmap (1 bit per event type) and pass that to
> ged device from machine as 'int' property?

Right. That might solve the ged_events ptr issue. But we need to set the irq as
well for the GED device. Is there a way to set the irq directly for a TYPE_DEVICE
object? 

(I think Eric mentioned a way of setting it directly earlier but that time GED was 
a TYPE_SYS_BUS_DEVICE. May be that is a reason to go back to SYS_BUS_DEVICE )

Thanks,
Shameer

> >
> > Appreciate your help,
> >
> > Thanks,
> > Shameer
> >


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
  2019-05-13 17:00       ` Shameerali Kolothum Thodi
@ 2019-05-17  8:41         ` Igor Mammedov
  2019-05-17 10:31           ` Shameerali Kolothum Thodi
  0 siblings, 1 reply; 62+ messages in thread
From: Igor Mammedov @ 2019-05-17  8:41 UTC (permalink / raw)
  To: Shameerali Kolothum Thodi
  Cc: peter.maydell, sameo, ard.biesheuvel, qemu-devel, Linuxarm,
	shannon.zhaosl, qemu-arm, eric.auger, xuwei (O),
	sebastien.boeuf, lersek

On Mon, 13 May 2019 17:00:13 +0000
Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com> wrote:

> > -----Original Message-----
> > From: Igor Mammedov [mailto:imammedo@redhat.com]
> > Sent: 13 May 2019 17:25
> > To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>
> > Subject: Re: [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
> > 
> > On Mon, 13 May 2019 11:53:38 +0000
> > Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com> wrote:
> > 
> > > Hi Igor,
> > >
> > > > -----Original Message-----
> > > > From: Shameerali Kolothum Thodi
> > > > Sent: 03 May 2019 13:46
> > > > To: 'Igor Mammedov' <imammedo@redhat.com>
> > > > Cc: qemu-devel@nongnu.org; qemu-arm@nongnu.org;
> > > > eric.auger@redhat.com; peter.maydell@linaro.org;
> > > > shannon.zhaosl@gmail.com; sameo@linux.intel.com;
> > > > sebastien.boeuf@intel.com; xuwei (O) <xuwei5@huawei.com>;
> > > > lersek@redhat.com; ard.biesheuvel@linaro.org; Linuxarm
> > > > <linuxarm@huawei.com>
> > > > Subject: RE: [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device
> > Support
> > > >
> > > > Hi Igor,
> > > >
> > > > > -----Original Message-----
> > > > > From: Igor Mammedov [mailto:imammedo@redhat.com]
> > > > > Sent: 02 May 2019 17:13
> > > > > To: Shameerali Kolothum Thodi
> > <shameerali.kolothum.thodi@huawei.com>
> > > > > Cc: qemu-devel@nongnu.org; qemu-arm@nongnu.org;
> > > > > eric.auger@redhat.com; peter.maydell@linaro.org;
> > > > > shannon.zhaosl@gmail.com; sameo@linux.intel.com;
> > > > > sebastien.boeuf@intel.com; xuwei (O) <xuwei5@huawei.com>;
> > > > > lersek@redhat.com; ard.biesheuvel@linaro.org; Linuxarm
> > > > > <linuxarm@huawei.com>
> > > > > Subject: Re: [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device
> > Support
> > > > >
> > >
> > > [...]
> > >
> > > > > > +}
> > > > > > +
> > > > > > +static Property acpi_ged_properties[] = {
> > > > > > +    /*
> > > > > > +     * Memory hotplug base address is a property of GED here,
> > > > > > +     * because GED handles memory hotplug event and
> > > > > MEMORY_HOTPLUG_DEVICE
> > > > > > +     * gets initialized when GED device is realized.
> > > > > > +     */
> > > > > > +    DEFINE_PROP_UINT64("memhp-base", AcpiGedState,
> > memhp_base,
> > > > > 0),
> > > > > > +    DEFINE_PROP_BOOL("memory-hotplug-support", AcpiGedState,
> > > > > > +                     memhp_state.is_enabled, true),
> > > > > > +    DEFINE_PROP_PTR("gsi", AcpiGedState, gsi),
> > > > >
> > > > > PTR shouldn't be used in new code, look at object_property_add_link() &
> > co
> > > >
> > > > Ok. I will take a look at that.
> > >
> > > I attempted to remove _PROP_PTR for "ged-events" and use _PROP_LINK
> > and
> > > _set_link(),
> > >
> > >
> > > diff --git a/hw/acpi/generic_event_device.c
> > b/hw/acpi/generic_event_device.c
> > > index 856ca04c01..978c8e088e 100644
> > > --- a/hw/acpi/generic_event_device.c
> > > +++ b/hw/acpi/generic_event_device.c
> > > @@ -268,7 +268,8 @@ static Property acpi_ged_properties[] = {
> > >      DEFINE_PROP_PTR("gsi", AcpiGedState, gsi),
> > >      DEFINE_PROP_UINT64("ged-base", AcpiGedState, ged_base, 0),
> > >      DEFINE_PROP_UINT32("ged-irq", AcpiGedState, ged_irq, 0),
> > > -    DEFINE_PROP_PTR("ged-events", AcpiGedState, ged_events),
> > > +    DEFINE_PROP_LINK("ged-events", AcpiGedState, ged_events,
> > TYPE_ACPI_GED,
> > > +                     GedEvent *),
> > >      DEFINE_PROP_UINT32("ged-events-size", AcpiGedState,
> > ged_events_size, 0),
> > >      DEFINE_PROP_END_OF_LIST(),
> > >  };
> > > diff --git a/hw/arm/virt.c b/hw/arm/virt.c
> > > index 8179b3e511..c89b7b7120 100644
> > > --- a/hw/arm/virt.c
> > > +++ b/hw/arm/virt.c
> > > @@ -537,7 +537,8 @@ static inline DeviceState
> > *create_acpi_ged(VirtMachineState *vms)
> > >      qdev_prop_set_ptr(dev, "gsi", vms->gsi);
> > >      qdev_prop_set_uint64(dev, "ged-base",
> > vms->memmap[VIRT_ACPI_GED].base);
> > >      qdev_prop_set_uint32(dev, "ged-irq", vms->irqmap[VIRT_ACPI_GED]);
> > > -    qdev_prop_set_ptr(dev, "ged-events", ged_events);
> > > +    object_property_set_link(OBJECT(dev), OBJECT(ged_events),
> > "ged-events",
> > > +                             &error_abort);
> > >      qdev_prop_set_uint32(dev, "ged-events-size",
> > ARRAY_SIZE(ged_events));
> > >
> > >      object_property_add_child(qdev_get_machine(), "acpi-ged",
> > > diff --git a/include/hw/acpi/generic_event_device.h
> > b/include/hw/acpi/generic_event_device.h
> > > index 9c840d8064..588f4ecfba 100644
> > > --- a/include/hw/acpi/generic_event_device.h
> > > +++ b/include/hw/acpi/generic_event_device.h
> > > @@ -111,7 +111,7 @@ typedef struct AcpiGedState {
> > >      hwaddr ged_base;
> > >      GEDState ged_state;
> > >      uint32_t ged_irq;
> > > -    void *ged_events;
> > > +    GedEvent *ged_events;
> > >      uint32_t ged_events_size;
> > >  } AcpiGedState;
> > >
> > >
> > > And with this I get,
> > >
> > > Segmentation fault      (core dumped) ./qemu-system-aarch64-ged-v5
> > > -machine virt, -cpu cortex-a57 -machine type=virt -nographic -smp 1 -m
> > > 4G,maxmem=8G,slots=10 -drive if=none,file=ubuntu-est-5.0,id=fs -device
> > > virtio-blk-device,drive=fs -kernel Image_memhp_remove -bios
> > > QEMU_EFI_Release.fd -object memory-backend-ram,id=mem1,size=1G
> > -device
> > > pc-dimm,id=dimm1,memdev=mem1 -numa node,nodeid=0 -append
> > > "console=ttyAMA0 root=/dev/vda rw acpi=force movable_node"
> > >
> > > It looks like struct pointer cannot be used directly and has to make a QOM
> > object
> > > for DEFINE_PROP_LINK use. Not sure there is an easy way for setting ptr
> > property
> > > using link() functions. Please let me know if there any reference
> > implementation I
> > > can take a look.
> > 
> > Simple 'struct' won't work with link, it has to be QOM object.
> > 
> > Question is do we still need GedEvent array?
> > We handle only 1 irq and several event types, why not replace GedEvent
> > with with a 32bit bitmap (1 bit per event type) and pass that to
> > ged device from machine as 'int' property?
> 
> Right. That might solve the ged_events ptr issue. But we need to set the irq as
> well for the GED device. Is there a way to set the irq directly for a TYPE_DEVICE
> object? 
> 
> (I think Eric mentioned a way of setting it directly earlier but that time GED was 
> a TYPE_SYS_BUS_DEVICE. May be that is a reason to go back to SYS_BUS_DEVICE )
It's probably not necessary, I think I've found what you are looking for. Pls, take a loot at

hw/i386/pc_q35.c:
    for (i = 0; i < GSI_NUM_PINS; i++) {                                         
        qdev_connect_gpio_out_named(lpc_dev, ICH9_GPIO_GSI, i, pcms->gsi[i]);    
    } 


> 
> Thanks,
> Shameer
> 
> > >
> > > Appreciate your help,
> > >
> > > Thanks,
> > > Shameer
> > >



^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
  2019-05-17  8:41         ` Igor Mammedov
@ 2019-05-17 10:31           ` Shameerali Kolothum Thodi
  0 siblings, 0 replies; 62+ messages in thread
From: Shameerali Kolothum Thodi @ 2019-05-17 10:31 UTC (permalink / raw)
  To: Igor Mammedov
  Cc: peter.maydell, sameo, ard.biesheuvel, qemu-devel, Linuxarm,
	shannon.zhaosl, qemu-arm, eric.auger, xuwei (O),
	sebastien.boeuf, lersek



> -----Original Message-----
> From: Igor Mammedov [mailto:imammedo@redhat.com]
> Sent: 17 May 2019 09:41
> To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>
> Cc: peter.maydell@linaro.org; sameo@linux.intel.com;
> shannon.zhaosl@gmail.com; ard.biesheuvel@linaro.org;
> qemu-devel@nongnu.org; Linuxarm <linuxarm@huawei.com>;
> eric.auger@redhat.com; qemu-arm@nongnu.org; xuwei (O)
> <xuwei5@huawei.com>; sebastien.boeuf@intel.com; lersek@redhat.com
> Subject: Re: [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support
> 
> On Mon, 13 May 2019 17:00:13 +0000
> Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com> wrote:
> 
> > > -----Original Message-----
> > > From: Igor Mammedov [mailto:imammedo@redhat.com]
> > > Sent: 13 May 2019 17:25
> > > To: Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>
> > > Subject: Re: [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device
> > > Support
> > >
> > > On Mon, 13 May 2019 11:53:38 +0000
> > > Shameerali Kolothum Thodi <shameerali.kolothum.thodi@huawei.com>
> wrote:
> > >
> > > > Hi Igor,
> > > >
> > > > > -----Original Message-----
> > > > > From: Shameerali Kolothum Thodi
> > > > > Sent: 03 May 2019 13:46
> > > > > To: 'Igor Mammedov' <imammedo@redhat.com>
> > > > > Cc: qemu-devel@nongnu.org; qemu-arm@nongnu.org;
> > > > > eric.auger@redhat.com; peter.maydell@linaro.org;
> > > > > shannon.zhaosl@gmail.com; sameo@linux.intel.com;
> > > > > sebastien.boeuf@intel.com; xuwei (O) <xuwei5@huawei.com>;
> > > > > lersek@redhat.com; ard.biesheuvel@linaro.org; Linuxarm
> > > > > <linuxarm@huawei.com>
> > > > > Subject: RE: [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event
> > > > > Device
> > > Support
> > > > >
> > > > > Hi Igor,
> > > > >
> > > > > > -----Original Message-----
> > > > > > From: Igor Mammedov [mailto:imammedo@redhat.com]
> > > > > > Sent: 02 May 2019 17:13
> > > > > > To: Shameerali Kolothum Thodi
> > > <shameerali.kolothum.thodi@huawei.com>
> > > > > > Cc: qemu-devel@nongnu.org; qemu-arm@nongnu.org;
> > > > > > eric.auger@redhat.com; peter.maydell@linaro.org;
> > > > > > shannon.zhaosl@gmail.com; sameo@linux.intel.com;
> > > > > > sebastien.boeuf@intel.com; xuwei (O) <xuwei5@huawei.com>;
> > > > > > lersek@redhat.com; ard.biesheuvel@linaro.org; Linuxarm
> > > > > > <linuxarm@huawei.com>
> > > > > > Subject: Re: [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event
> > > > > > Device
> > > Support
> > > > > >
> > > >
> > > > [...]
> > > >
> > > > > > > +}
> > > > > > > +
> > > > > > > +static Property acpi_ged_properties[] = {
> > > > > > > +    /*
> > > > > > > +     * Memory hotplug base address is a property of GED here,
> > > > > > > +     * because GED handles memory hotplug event and
> > > > > > MEMORY_HOTPLUG_DEVICE
> > > > > > > +     * gets initialized when GED device is realized.
> > > > > > > +     */
> > > > > > > +    DEFINE_PROP_UINT64("memhp-base", AcpiGedState,
> > > memhp_base,
> > > > > > 0),
> > > > > > > +    DEFINE_PROP_BOOL("memory-hotplug-support",
> AcpiGedState,
> > > > > > > +                     memhp_state.is_enabled, true),
> > > > > > > +    DEFINE_PROP_PTR("gsi", AcpiGedState, gsi),
> > > > > >
> > > > > > PTR shouldn't be used in new code, look at
> > > > > > object_property_add_link() &
> > > co
> > > > >
> > > > > Ok. I will take a look at that.
> > > >
> > > > I attempted to remove _PROP_PTR for "ged-events" and use
> > > > _PROP_LINK
> > > and
> > > > _set_link(),
> > > >
> > > >
> > > > diff --git a/hw/acpi/generic_event_device.c
> > > b/hw/acpi/generic_event_device.c
> > > > index 856ca04c01..978c8e088e 100644
> > > > --- a/hw/acpi/generic_event_device.c
> > > > +++ b/hw/acpi/generic_event_device.c
> > > > @@ -268,7 +268,8 @@ static Property acpi_ged_properties[] = {
> > > >      DEFINE_PROP_PTR("gsi", AcpiGedState, gsi),
> > > >      DEFINE_PROP_UINT64("ged-base", AcpiGedState, ged_base, 0),
> > > >      DEFINE_PROP_UINT32("ged-irq", AcpiGedState, ged_irq, 0),
> > > > -    DEFINE_PROP_PTR("ged-events", AcpiGedState, ged_events),
> > > > +    DEFINE_PROP_LINK("ged-events", AcpiGedState, ged_events,
> > > TYPE_ACPI_GED,
> > > > +                     GedEvent *),
> > > >      DEFINE_PROP_UINT32("ged-events-size", AcpiGedState,
> > > ged_events_size, 0),
> > > >      DEFINE_PROP_END_OF_LIST(),
> > > >  };
> > > > diff --git a/hw/arm/virt.c b/hw/arm/virt.c index
> > > > 8179b3e511..c89b7b7120 100644
> > > > --- a/hw/arm/virt.c
> > > > +++ b/hw/arm/virt.c
> > > > @@ -537,7 +537,8 @@ static inline DeviceState
> > > *create_acpi_ged(VirtMachineState *vms)
> > > >      qdev_prop_set_ptr(dev, "gsi", vms->gsi);
> > > >      qdev_prop_set_uint64(dev, "ged-base",
> > > vms->memmap[VIRT_ACPI_GED].base);
> > > >      qdev_prop_set_uint32(dev, "ged-irq",
> vms->irqmap[VIRT_ACPI_GED]);
> > > > -    qdev_prop_set_ptr(dev, "ged-events", ged_events);
> > > > +    object_property_set_link(OBJECT(dev), OBJECT(ged_events),
> > > "ged-events",
> > > > +                             &error_abort);
> > > >      qdev_prop_set_uint32(dev, "ged-events-size",
> > > ARRAY_SIZE(ged_events));
> > > >
> > > >      object_property_add_child(qdev_get_machine(), "acpi-ged",
> > > > diff --git a/include/hw/acpi/generic_event_device.h
> > > b/include/hw/acpi/generic_event_device.h
> > > > index 9c840d8064..588f4ecfba 100644
> > > > --- a/include/hw/acpi/generic_event_device.h
> > > > +++ b/include/hw/acpi/generic_event_device.h
> > > > @@ -111,7 +111,7 @@ typedef struct AcpiGedState {
> > > >      hwaddr ged_base;
> > > >      GEDState ged_state;
> > > >      uint32_t ged_irq;
> > > > -    void *ged_events;
> > > > +    GedEvent *ged_events;
> > > >      uint32_t ged_events_size;
> > > >  } AcpiGedState;
> > > >
> > > >
> > > > And with this I get,
> > > >
> > > > Segmentation fault      (core dumped) ./qemu-system-aarch64-ged-v5
> > > > -machine virt, -cpu cortex-a57 -machine type=virt -nographic -smp
> > > > 1 -m
> > > > 4G,maxmem=8G,slots=10 -drive if=none,file=ubuntu-est-5.0,id=fs
> > > > -device virtio-blk-device,drive=fs -kernel Image_memhp_remove
> > > > -bios QEMU_EFI_Release.fd -object
> > > > memory-backend-ram,id=mem1,size=1G
> > > -device
> > > > pc-dimm,id=dimm1,memdev=mem1 -numa node,nodeid=0 -append
> > > > "console=ttyAMA0 root=/dev/vda rw acpi=force movable_node"
> > > >
> > > > It looks like struct pointer cannot be used directly and has to
> > > > make a QOM
> > > object
> > > > for DEFINE_PROP_LINK use. Not sure there is an easy way for
> > > > setting ptr
> > > property
> > > > using link() functions. Please let me know if there any reference
> > > implementation I
> > > > can take a look.
> > >
> > > Simple 'struct' won't work with link, it has to be QOM object.
> > >
> > > Question is do we still need GedEvent array?
> > > We handle only 1 irq and several event types, why not replace
> > > GedEvent with with a 32bit bitmap (1 bit per event type) and pass
> > > that to ged device from machine as 'int' property?
> >
> > Right. That might solve the ged_events ptr issue. But we need to set
> > the irq as well for the GED device. Is there a way to set the irq
> > directly for a TYPE_DEVICE object?
> >
> > (I think Eric mentioned a way of setting it directly earlier but that
> > time GED was a TYPE_SYS_BUS_DEVICE. May be that is a reason to go back
> > to SYS_BUS_DEVICE )
> It's probably not necessary, I think I've found what you are looking for. Pls, take
> a loot at
> 
> hw/i386/pc_q35.c:
>     for (i = 0; i < GSI_NUM_PINS; i++) {
>         qdev_connect_gpio_out_named(lpc_dev, ICH9_GPIO_GSI, i,
> pcms->gsi[i]);
>     }
> 

Cool!. Just tried and it works. Thanks for that.

I am going through other comments as well and hopefully will be able to
post a v5 soon.

Cheers,
Shameer

> >
> > Thanks,
> > Shameer
> >
> > > >
> > > > Appreciate your help,
> > > >
> > > > Thanks,
> > > > Shameer
> > > >



^ permalink raw reply	[flat|nested] 62+ messages in thread

end of thread, other threads:[~2019-05-17 11:07 UTC | newest]

Thread overview: 62+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-04-09 10:29 [Qemu-devel] [PATCH v4 0/8] ARM virt: ACPI memory hotplug support Shameer Kolothum
2019-04-09 10:29 ` Shameer Kolothum
2019-04-09 10:29 ` [Qemu-devel] [PATCH v4 1/8] hw/acpi: Make ACPI IO address space configurable Shameer Kolothum
2019-04-09 10:29   ` Shameer Kolothum
2019-04-09 10:29 ` [Qemu-devel] [PATCH v4 2/8] hw/acpi: Do not create memory hotplug method when handler is not defined Shameer Kolothum
2019-04-09 10:29   ` Shameer Kolothum
2019-04-09 10:29 ` [Qemu-devel] [PATCH v4 3/8] hw/acpi: Add ACPI Generic Event Device Support Shameer Kolothum
2019-04-09 10:29   ` Shameer Kolothum
2019-04-30 15:49   ` Auger Eric
2019-04-30 15:49     ` Auger Eric
2019-05-01 10:40     ` Shameerali Kolothum Thodi
2019-05-01 10:40       ` Shameerali Kolothum Thodi
2019-05-02  7:10       ` Auger Eric
2019-05-02  7:10         ` Auger Eric
2019-05-01 11:10   ` Ard Biesheuvel
2019-05-01 11:10     ` Ard Biesheuvel
2019-05-01 11:25     ` Shameerali Kolothum Thodi
2019-05-01 11:25       ` Shameerali Kolothum Thodi
2019-05-02  7:22       ` Ard Biesheuvel
2019-05-02  7:22         ` Ard Biesheuvel
2019-05-02 15:24         ` Igor Mammedov
2019-05-02 15:24           ` Igor Mammedov
2019-05-02 16:12   ` Igor Mammedov
2019-05-02 16:12     ` Igor Mammedov
2019-05-03 12:45     ` Shameerali Kolothum Thodi
2019-05-03 12:45       ` Shameerali Kolothum Thodi
2019-05-03 15:10       ` Igor Mammedov
2019-05-03 15:10         ` Igor Mammedov
2019-05-07  9:01         ` Shameerali Kolothum Thodi
2019-05-09 14:50           ` Igor Mammedov
2019-05-13 11:53     ` Shameerali Kolothum Thodi
2019-05-13 17:00       ` Shameerali Kolothum Thodi
2019-05-17  8:41         ` Igor Mammedov
2019-05-17 10:31           ` Shameerali Kolothum Thodi
2019-04-09 10:29 ` [Qemu-devel] [PATCH v4 4/8] hw/arm/virt: Add memory hotplug framework Shameer Kolothum
2019-04-09 10:29   ` Shameer Kolothum
2019-05-02 16:19   ` Igor Mammedov
2019-05-02 16:19     ` Igor Mammedov
2019-05-03 12:47     ` Shameerali Kolothum Thodi
2019-05-03 12:47       ` Shameerali Kolothum Thodi
2019-04-09 10:29 ` [Qemu-devel] [PATCH v4 5/8] hw/arm/virt: Enable device memory cold/hot plug with ACPI boot Shameer Kolothum
2019-04-09 10:29   ` Shameer Kolothum
2019-04-30 16:34   ` Auger Eric
2019-04-30 16:34     ` Auger Eric
2019-05-01 10:49     ` Shameerali Kolothum Thodi
2019-05-01 10:49       ` Shameerali Kolothum Thodi
2019-05-09 15:20   ` Igor Mammedov
2019-04-09 10:29 ` [Qemu-devel] [PATCH v4 6/8] hw/arm/virt-acpi-build: Add PC-DIMM in SRAT Shameer Kolothum
2019-04-09 10:29   ` Shameer Kolothum
2019-04-09 10:29 ` [Qemu-devel] [PATCH v4 7/8] hw/arm/boot: Add "hotpluggable" property to DT memory node Shameer Kolothum
2019-04-09 10:29   ` Shameer Kolothum
2019-04-09 10:29 ` [Qemu-devel] [PATCH v4 8/8] hw/arm/boot: Expose the PC-DIMM nodes in the DT Shameer Kolothum
2019-04-09 10:29   ` Shameer Kolothum
2019-04-09 15:08   ` Laszlo Ersek
2019-04-09 15:08     ` Laszlo Ersek
2019-04-10  8:49     ` Shameerali Kolothum Thodi
2019-04-10  8:49       ` Shameerali Kolothum Thodi
2019-05-03 13:35       ` Shameerali Kolothum Thodi
2019-05-03 13:35         ` Shameerali Kolothum Thodi
2019-05-03 14:13         ` Laszlo Ersek
2019-05-03 14:13           ` Laszlo Ersek
2019-05-08 10:30           ` Shameerali Kolothum Thodi

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.