hw/loongarch: Support memory hotplug

Add hotplug/unplug interface for memory device.

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Acked-by: Song Gao <gaosong@loongson.cn>
Message-Id: <20220908094623.73051-9-yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
This commit is contained in:
Xiaojuan Yang 2022-08-25 11:36:59 +08:00 committed by Song Gao
parent 1cd5db2e88
commit c3da26f314
No known key found for this signature in database
GPG key ID: 40A2FFF239263EDF
3 changed files with 132 additions and 7 deletions

View file

@ -4,6 +4,7 @@ config LOONGARCH_VIRT
select PCI_EXPRESS_GENERIC_BRIDGE
imply VIRTIO_VGA
imply PCI_DEVICES
imply NVDIMM
select ISA_BUS
select SERIAL
select SERIAL_ISA
@ -18,3 +19,4 @@ config LOONGARCH_VIRT
select ACPI_PCI
select ACPI_HW_REDUCED
select FW_CFG_DMA
select DIMM

View file

@ -186,6 +186,12 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
build_srat_memory(table_data, VIRT_HIGHMEM_BASE, machine->ram_size - VIRT_LOWMEM_SIZE,
0, MEM_AFFINITY_ENABLED);
if (ms->device_memory) {
build_srat_memory(table_data, ms->device_memory->base,
memory_region_size(&ms->device_memory->mr),
0, MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED);
}
acpi_table_end(linker, &table);
}
@ -335,6 +341,25 @@ static void build_uart_device_aml(Aml *table)
aml_append(table, scope);
}
static void
build_la_ged_aml(Aml *dsdt, MachineState *machine)
{
uint32_t event;
LoongArchMachineState *lams = LOONGARCH_MACHINE(machine);
build_ged_aml(dsdt, "\\_SB."GED_DEVICE,
HOTPLUG_HANDLER(lams->acpi_ged),
VIRT_SCI_IRQ, AML_SYSTEM_MEMORY,
VIRT_GED_EVT_ADDR);
event = object_property_get_uint(OBJECT(lams->acpi_ged),
"ged-event", &error_abort);
if (event & ACPI_GED_MEM_HOTPLUG_EVT) {
build_memory_hotplug_aml(dsdt, machine->ram_slots, "\\_SB", NULL,
AML_SYSTEM_MEMORY,
VIRT_GED_MEM_ADDR);
}
}
/* build DSDT */
static void
build_dsdt(GArray *table_data, BIOSLinker *linker, MachineState *machine)
@ -364,12 +389,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, MachineState *machine)
build_gpex_pci0_int(dsdt);
build_uart_device_aml(dsdt);
if (lams->acpi_ged) {
build_ged_aml(dsdt, "\\_SB."GED_DEVICE,
HOTPLUG_HANDLER(lams->acpi_ged),
VIRT_SCI_IRQ, AML_SYSTEM_MEMORY,
VIRT_GED_EVT_ADDR);
}
build_la_ged_aml(dsdt, machine);
scope = aml_scope("\\_SB.PCI0");
/* Build PCI0._CRS */

View file

@ -40,6 +40,7 @@
#include "hw/core/sysbus-fdt.h"
#include "hw/platform-bus.h"
#include "hw/display/ramfb.h"
#include "hw/mem/pc-dimm.h"
static void create_fdt(LoongArchMachineState *lams)
{
@ -718,6 +719,35 @@ static void loongarch_init(MachineState *machine)
machine->ram, offset, highram_size);
memory_region_add_subregion(address_space_mem, 0x90000000, &lams->highmem);
memmap_add_entry(0x90000000, highram_size, 1);
/* initialize device memory address space */
if (machine->ram_size < machine->maxram_size) {
machine->device_memory = g_malloc0(sizeof(*machine->device_memory));
ram_addr_t device_mem_size = machine->maxram_size - machine->ram_size;
if (machine->ram_slots > ACPI_MAX_RAM_SLOTS) {
error_report("unsupported amount of memory slots: %"PRIu64,
machine->ram_slots);
exit(EXIT_FAILURE);
}
if (QEMU_ALIGN_UP(machine->maxram_size,
TARGET_PAGE_SIZE) != machine->maxram_size) {
error_report("maximum memory size must by aligned to multiple of "
"%d bytes", TARGET_PAGE_SIZE);
exit(EXIT_FAILURE);
}
/* device memory base is the top of high memory address. */
machine->device_memory->base = 0x90000000 + highram_size;
machine->device_memory->base =
ROUND_UP(machine->device_memory->base, 1 * GiB);
memory_region_init(&machine->device_memory->mr, OBJECT(lams),
"device-memory", device_mem_size);
memory_region_add_subregion(address_space_mem, machine->device_memory->base,
&machine->device_memory->mr);
}
/* Add isa io region */
memory_region_init_alias(&lams->isa_io, NULL, "isa-io",
get_system_io(), 0, VIRT_ISA_IO_SIZE);
@ -804,6 +834,73 @@ static void loongarch_machine_initfn(Object *obj)
lams->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8);
}
static bool memhp_type_supported(DeviceState *dev)
{
/* we only support pc dimm now */
return object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) &&
!object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM);
}
static void virt_mem_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
Error **errp)
{
pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), NULL, errp);
}
static void virt_machine_device_pre_plug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
if (memhp_type_supported(dev)) {
virt_mem_pre_plug(hotplug_dev, dev, errp);
}
}
static void virt_mem_unplug_request(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
LoongArchMachineState *lams = LOONGARCH_MACHINE(hotplug_dev);
/* the acpi ged is always exist */
hotplug_handler_unplug_request(HOTPLUG_HANDLER(lams->acpi_ged), dev,
errp);
}
static void virt_machine_device_unplug_request(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
if (memhp_type_supported(dev)) {
virt_mem_unplug_request(hotplug_dev, dev, errp);
}
}
static void virt_mem_unplug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
LoongArchMachineState *lams = LOONGARCH_MACHINE(hotplug_dev);
hotplug_handler_unplug(HOTPLUG_HANDLER(lams->acpi_ged), dev, errp);
pc_dimm_unplug(PC_DIMM(dev), MACHINE(lams));
qdev_unrealize(dev);
}
static void virt_machine_device_unplug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
if (memhp_type_supported(dev)) {
virt_mem_unplug(hotplug_dev, dev, errp);
}
}
static void virt_mem_plug(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
LoongArchMachineState *lams = LOONGARCH_MACHINE(hotplug_dev);
pc_dimm_plug(PC_DIMM(dev), MACHINE(lams));
hotplug_handler_plug(HOTPLUG_HANDLER(lams->acpi_ged),
dev, &error_abort);
}
static void loongarch_machine_device_plug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
@ -815,6 +912,8 @@ static void loongarch_machine_device_plug_cb(HotplugHandler *hotplug_dev,
platform_bus_link_device(PLATFORM_BUS_DEVICE(lams->platform_bus_dev),
SYS_BUS_DEVICE(dev));
}
} else if (memhp_type_supported(dev)) {
virt_mem_plug(hotplug_dev, dev, errp);
}
}
@ -823,7 +922,8 @@ static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine,
{
MachineClass *mc = MACHINE_GET_CLASS(machine);
if (device_is_dynamic_sysbus(mc, dev)) {
if (device_is_dynamic_sysbus(mc, dev) ||
memhp_type_supported(dev)) {
return HOTPLUG_HANDLER(machine);
}
return NULL;
@ -847,6 +947,9 @@ static void loongarch_class_init(ObjectClass *oc, void *data)
mc->no_cdrom = 1;
mc->get_hotplug_handler = virt_machine_get_hotplug_handler;
hc->plug = loongarch_machine_device_plug_cb;
hc->pre_plug = virt_machine_device_pre_plug;
hc->unplug_request = virt_machine_device_unplug_request;
hc->unplug = virt_machine_device_unplug;
object_class_property_add(oc, "acpi", "OnOffAuto",
loongarch_get_acpi, loongarch_set_acpi,