xref: /openbmc/qemu/hw/acpi/memory_hotplug.c (revision 07a32d6b)
1 #include "hw/acpi/memory_hotplug.h"
2 #include "hw/acpi/pc-hotplug.h"
3 #include "hw/mem/pc-dimm.h"
4 #include "hw/boards.h"
5 #include "trace.h"
6 
7 static uint64_t acpi_memory_hotplug_read(void *opaque, hwaddr addr,
8                                          unsigned int size)
9 {
10     uint32_t val = 0;
11     MemHotplugState *mem_st = opaque;
12     MemStatus *mdev;
13     Object *o;
14 
15     if (mem_st->selector >= mem_st->dev_count) {
16         trace_mhp_acpi_invalid_slot_selected(mem_st->selector);
17         return 0;
18     }
19 
20     mdev = &mem_st->devs[mem_st->selector];
21     o = OBJECT(mdev->dimm);
22     switch (addr) {
23     case 0x0: /* Lo part of phys address where DIMM is mapped */
24         val = o ? object_property_get_int(o, PC_DIMM_ADDR_PROP, NULL) : 0;
25         trace_mhp_acpi_read_addr_lo(mem_st->selector, val);
26         break;
27     case 0x4: /* Hi part of phys address where DIMM is mapped */
28         val = o ? object_property_get_int(o, PC_DIMM_ADDR_PROP, NULL) >> 32 : 0;
29         trace_mhp_acpi_read_addr_hi(mem_st->selector, val);
30         break;
31     case 0x8: /* Lo part of DIMM size */
32         val = o ? object_property_get_int(o, PC_DIMM_SIZE_PROP, NULL) : 0;
33         trace_mhp_acpi_read_size_lo(mem_st->selector, val);
34         break;
35     case 0xc: /* Hi part of DIMM size */
36         val = o ? object_property_get_int(o, PC_DIMM_SIZE_PROP, NULL) >> 32 : 0;
37         trace_mhp_acpi_read_size_hi(mem_st->selector, val);
38         break;
39     case 0x10: /* node proximity for _PXM method */
40         val = o ? object_property_get_int(o, PC_DIMM_NODE_PROP, NULL) : 0;
41         trace_mhp_acpi_read_pxm(mem_st->selector, val);
42         break;
43     case 0x14: /* pack and return is_* fields */
44         val |= mdev->is_enabled   ? 1 : 0;
45         val |= mdev->is_inserting ? 2 : 0;
46         trace_mhp_acpi_read_flags(mem_st->selector, val);
47         break;
48     default:
49         val = ~0;
50         break;
51     }
52     return val;
53 }
54 
55 static void acpi_memory_hotplug_write(void *opaque, hwaddr addr, uint64_t data,
56                                       unsigned int size)
57 {
58     MemHotplugState *mem_st = opaque;
59     MemStatus *mdev;
60 
61     if (!mem_st->dev_count) {
62         return;
63     }
64 
65     if (addr) {
66         if (mem_st->selector >= mem_st->dev_count) {
67             trace_mhp_acpi_invalid_slot_selected(mem_st->selector);
68             return;
69         }
70     }
71 
72     switch (addr) {
73     case 0x0: /* DIMM slot selector */
74         mem_st->selector = data;
75         trace_mhp_acpi_write_slot(mem_st->selector);
76         break;
77     case 0x4: /* _OST event  */
78         mdev = &mem_st->devs[mem_st->selector];
79         if (data == 1) {
80             /* TODO: handle device insert OST event */
81         } else if (data == 3) {
82             /* TODO: handle device remove OST event */
83         }
84         mdev->ost_event = data;
85         trace_mhp_acpi_write_ost_ev(mem_st->selector, mdev->ost_event);
86         break;
87     case 0x8: /* _OST status */
88         mdev = &mem_st->devs[mem_st->selector];
89         mdev->ost_status = data;
90         trace_mhp_acpi_write_ost_status(mem_st->selector, mdev->ost_status);
91         /* TODO: report async error */
92         /* TODO: implement memory removal on guest signal */
93         break;
94     case 0x14:
95         mdev = &mem_st->devs[mem_st->selector];
96         if (data & 2) { /* clear insert event */
97             mdev->is_inserting  = false;
98             trace_mhp_acpi_clear_insert_evt(mem_st->selector);
99         }
100         break;
101     }
102 
103 }
104 static const MemoryRegionOps acpi_memory_hotplug_ops = {
105     .read = acpi_memory_hotplug_read,
106     .write = acpi_memory_hotplug_write,
107     .endianness = DEVICE_LITTLE_ENDIAN,
108     .valid = {
109         .min_access_size = 1,
110         .max_access_size = 4,
111     },
112 };
113 
114 void acpi_memory_hotplug_init(MemoryRegion *as, Object *owner,
115                               MemHotplugState *state)
116 {
117     MachineState *machine = MACHINE(qdev_get_machine());
118 
119     state->dev_count = machine->ram_slots;
120     if (!state->dev_count) {
121         return;
122     }
123 
124     state->devs = g_malloc0(sizeof(*state->devs) * state->dev_count);
125     memory_region_init_io(&state->io, owner, &acpi_memory_hotplug_ops, state,
126                           "apci-mem-hotplug", ACPI_MEMORY_HOTPLUG_IO_LEN);
127     memory_region_add_subregion(as, ACPI_MEMORY_HOTPLUG_BASE, &state->io);
128 }
129 
130 void acpi_memory_plug_cb(ACPIREGS *ar, qemu_irq irq, MemHotplugState *mem_st,
131                          DeviceState *dev, Error **errp)
132 {
133     MemStatus *mdev;
134     Error *local_err = NULL;
135     int slot = object_property_get_int(OBJECT(dev), "slot", &local_err);
136 
137     if (local_err) {
138         error_propagate(errp, local_err);
139         return;
140     }
141 
142     if (slot >= mem_st->dev_count) {
143         char *dev_path = object_get_canonical_path(OBJECT(dev));
144         error_setg(errp, "acpi_memory_plug_cb: "
145                    "device [%s] returned invalid memory slot[%d]",
146                     dev_path, slot);
147         g_free(dev_path);
148         return;
149     }
150 
151     mdev = &mem_st->devs[slot];
152     mdev->dimm = dev;
153     mdev->is_enabled = true;
154     mdev->is_inserting = true;
155 
156     /* do ACPI magic */
157     ar->gpe.sts[0] |= ACPI_MEMORY_HOTPLUG_STATUS;
158     acpi_update_sci(ar, irq);
159     return;
160 }
161 
162 static const VMStateDescription vmstate_memhp_sts = {
163     .name = "memory hotplug device state",
164     .version_id = 1,
165     .minimum_version_id = 1,
166     .minimum_version_id_old = 1,
167     .fields      = (VMStateField[]) {
168         VMSTATE_BOOL(is_enabled, MemStatus),
169         VMSTATE_BOOL(is_inserting, MemStatus),
170         VMSTATE_UINT32(ost_event, MemStatus),
171         VMSTATE_UINT32(ost_status, MemStatus),
172         VMSTATE_END_OF_LIST()
173     }
174 };
175 
176 const VMStateDescription vmstate_memory_hotplug = {
177     .name = "memory hotplug state",
178     .version_id = 1,
179     .minimum_version_id = 1,
180     .minimum_version_id_old = 1,
181     .fields      = (VMStateField[]) {
182         VMSTATE_UINT32(selector, MemHotplugState),
183         VMSTATE_STRUCT_VARRAY_POINTER_UINT32(devs, MemHotplugState, dev_count,
184                                              vmstate_memhp_sts, MemStatus),
185         VMSTATE_END_OF_LIST()
186     }
187 };
188