]>
Commit | Line | Data |
---|---|---|
3ef77aca IM |
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" | |
c06b2ffb | 5 | #include "hw/qdev-core.h" |
dfe292ff | 6 | #include "trace.h" |
5f41fbba | 7 | #include "qapi-event.h" |
3ef77aca | 8 | |
43f50410 IM |
9 | static ACPIOSTInfo *acpi_memory_device_status(int slot, MemStatus *mdev) |
10 | { | |
11 | ACPIOSTInfo *info = g_new0(ACPIOSTInfo, 1); | |
12 | ||
13 | info->slot_type = ACPI_SLOT_TYPE_DIMM; | |
14 | info->slot = g_strdup_printf("%d", slot); | |
15 | info->source = mdev->ost_event; | |
16 | info->status = mdev->ost_status; | |
17 | if (mdev->dimm) { | |
18 | DeviceState *dev = DEVICE(mdev->dimm); | |
19 | if (dev->id) { | |
20 | info->device = g_strdup(dev->id); | |
21 | info->has_device = true; | |
22 | } | |
23 | } | |
24 | return info; | |
25 | } | |
26 | ||
27 | void acpi_memory_ospm_status(MemHotplugState *mem_st, ACPIOSTInfoList ***list) | |
28 | { | |
29 | int i; | |
30 | ||
31 | for (i = 0; i < mem_st->dev_count; i++) { | |
32 | ACPIOSTInfoList *elem = g_new0(ACPIOSTInfoList, 1); | |
33 | elem->value = acpi_memory_device_status(i, &mem_st->devs[i]); | |
34 | elem->next = NULL; | |
35 | **list = elem; | |
36 | *list = &elem->next; | |
37 | } | |
38 | } | |
39 | ||
3ef77aca IM |
40 | static uint64_t acpi_memory_hotplug_read(void *opaque, hwaddr addr, |
41 | unsigned int size) | |
42 | { | |
43 | uint32_t val = 0; | |
44 | MemHotplugState *mem_st = opaque; | |
45 | MemStatus *mdev; | |
46 | Object *o; | |
47 | ||
48 | if (mem_st->selector >= mem_st->dev_count) { | |
dfe292ff | 49 | trace_mhp_acpi_invalid_slot_selected(mem_st->selector); |
3ef77aca IM |
50 | return 0; |
51 | } | |
52 | ||
53 | mdev = &mem_st->devs[mem_st->selector]; | |
54 | o = OBJECT(mdev->dimm); | |
55 | switch (addr) { | |
56 | case 0x0: /* Lo part of phys address where DIMM is mapped */ | |
57 | val = o ? object_property_get_int(o, PC_DIMM_ADDR_PROP, NULL) : 0; | |
dfe292ff | 58 | trace_mhp_acpi_read_addr_lo(mem_st->selector, val); |
3ef77aca IM |
59 | break; |
60 | case 0x4: /* Hi part of phys address where DIMM is mapped */ | |
61 | val = o ? object_property_get_int(o, PC_DIMM_ADDR_PROP, NULL) >> 32 : 0; | |
dfe292ff | 62 | trace_mhp_acpi_read_addr_hi(mem_st->selector, val); |
3ef77aca IM |
63 | break; |
64 | case 0x8: /* Lo part of DIMM size */ | |
65 | val = o ? object_property_get_int(o, PC_DIMM_SIZE_PROP, NULL) : 0; | |
dfe292ff | 66 | trace_mhp_acpi_read_size_lo(mem_st->selector, val); |
3ef77aca IM |
67 | break; |
68 | case 0xc: /* Hi part of DIMM size */ | |
69 | val = o ? object_property_get_int(o, PC_DIMM_SIZE_PROP, NULL) >> 32 : 0; | |
dfe292ff | 70 | trace_mhp_acpi_read_size_hi(mem_st->selector, val); |
3ef77aca IM |
71 | break; |
72 | case 0x10: /* node proximity for _PXM method */ | |
73 | val = o ? object_property_get_int(o, PC_DIMM_NODE_PROP, NULL) : 0; | |
dfe292ff | 74 | trace_mhp_acpi_read_pxm(mem_st->selector, val); |
3ef77aca IM |
75 | break; |
76 | case 0x14: /* pack and return is_* fields */ | |
77 | val |= mdev->is_enabled ? 1 : 0; | |
78 | val |= mdev->is_inserting ? 2 : 0; | |
64fec58e | 79 | val |= mdev->is_removing ? 4 : 0; |
dfe292ff | 80 | trace_mhp_acpi_read_flags(mem_st->selector, val); |
3ef77aca IM |
81 | break; |
82 | default: | |
83 | val = ~0; | |
84 | break; | |
85 | } | |
86 | return val; | |
87 | } | |
88 | ||
89 | static void acpi_memory_hotplug_write(void *opaque, hwaddr addr, uint64_t data, | |
90 | unsigned int size) | |
91 | { | |
92 | MemHotplugState *mem_st = opaque; | |
93 | MemStatus *mdev; | |
5f41fbba | 94 | ACPIOSTInfo *info; |
c06b2ffb ZG |
95 | DeviceState *dev = NULL; |
96 | HotplugHandler *hotplug_ctrl = NULL; | |
bc09e061 | 97 | Error *local_err = NULL; |
3ef77aca IM |
98 | |
99 | if (!mem_st->dev_count) { | |
100 | return; | |
101 | } | |
102 | ||
103 | if (addr) { | |
104 | if (mem_st->selector >= mem_st->dev_count) { | |
dfe292ff | 105 | trace_mhp_acpi_invalid_slot_selected(mem_st->selector); |
3ef77aca IM |
106 | return; |
107 | } | |
108 | } | |
109 | ||
110 | switch (addr) { | |
111 | case 0x0: /* DIMM slot selector */ | |
112 | mem_st->selector = data; | |
dfe292ff | 113 | trace_mhp_acpi_write_slot(mem_st->selector); |
3ef77aca IM |
114 | break; |
115 | case 0x4: /* _OST event */ | |
116 | mdev = &mem_st->devs[mem_st->selector]; | |
117 | if (data == 1) { | |
118 | /* TODO: handle device insert OST event */ | |
119 | } else if (data == 3) { | |
120 | /* TODO: handle device remove OST event */ | |
121 | } | |
122 | mdev->ost_event = data; | |
dfe292ff | 123 | trace_mhp_acpi_write_ost_ev(mem_st->selector, mdev->ost_event); |
3ef77aca IM |
124 | break; |
125 | case 0x8: /* _OST status */ | |
126 | mdev = &mem_st->devs[mem_st->selector]; | |
127 | mdev->ost_status = data; | |
dfe292ff | 128 | trace_mhp_acpi_write_ost_status(mem_st->selector, mdev->ost_status); |
3ef77aca | 129 | /* TODO: implement memory removal on guest signal */ |
5f41fbba IM |
130 | |
131 | info = acpi_memory_device_status(mem_st->selector, mdev); | |
132 | qapi_event_send_acpi_device_ost(info, &error_abort); | |
133 | qapi_free_ACPIOSTInfo(info); | |
3ef77aca | 134 | break; |
c06b2ffb | 135 | case 0x14: /* set is_* fields */ |
3ef77aca IM |
136 | mdev = &mem_st->devs[mem_st->selector]; |
137 | if (data & 2) { /* clear insert event */ | |
138 | mdev->is_inserting = false; | |
dfe292ff | 139 | trace_mhp_acpi_clear_insert_evt(mem_st->selector); |
c06b2ffb ZG |
140 | } else if (data & 4) { |
141 | mdev->is_removing = false; | |
142 | trace_mhp_acpi_clear_remove_evt(mem_st->selector); | |
143 | } else if (data & 8) { | |
144 | if (!mdev->is_enabled) { | |
145 | trace_mhp_acpi_ejecting_invalid_slot(mem_st->selector); | |
146 | break; | |
147 | } | |
148 | ||
149 | dev = DEVICE(mdev->dimm); | |
150 | hotplug_ctrl = qdev_get_hotplug_handler(dev); | |
151 | /* call pc-dimm unplug cb */ | |
bc09e061 ZG |
152 | hotplug_handler_unplug(hotplug_ctrl, dev, &local_err); |
153 | if (local_err) { | |
154 | trace_mhp_acpi_pc_dimm_delete_failed(mem_st->selector); | |
155 | qapi_event_send_mem_unplug_error(dev->id, | |
156 | error_get_pretty(local_err), | |
157 | &error_abort); | |
903a41d3 | 158 | error_free(local_err); |
bc09e061 ZG |
159 | break; |
160 | } | |
c06b2ffb | 161 | trace_mhp_acpi_pc_dimm_deleted(mem_st->selector); |
3ef77aca IM |
162 | } |
163 | break; | |
c06b2ffb ZG |
164 | default: |
165 | break; | |
3ef77aca IM |
166 | } |
167 | ||
168 | } | |
169 | static const MemoryRegionOps acpi_memory_hotplug_ops = { | |
170 | .read = acpi_memory_hotplug_read, | |
171 | .write = acpi_memory_hotplug_write, | |
172 | .endianness = DEVICE_LITTLE_ENDIAN, | |
173 | .valid = { | |
174 | .min_access_size = 1, | |
175 | .max_access_size = 4, | |
176 | }, | |
177 | }; | |
178 | ||
179 | void acpi_memory_hotplug_init(MemoryRegion *as, Object *owner, | |
180 | MemHotplugState *state) | |
181 | { | |
182 | MachineState *machine = MACHINE(qdev_get_machine()); | |
183 | ||
184 | state->dev_count = machine->ram_slots; | |
185 | if (!state->dev_count) { | |
186 | return; | |
187 | } | |
188 | ||
189 | state->devs = g_malloc0(sizeof(*state->devs) * state->dev_count); | |
190 | memory_region_init_io(&state->io, owner, &acpi_memory_hotplug_ops, state, | |
22dc50d7 | 191 | "acpi-mem-hotplug", ACPI_MEMORY_HOTPLUG_IO_LEN); |
3ef77aca IM |
192 | memory_region_add_subregion(as, ACPI_MEMORY_HOTPLUG_BASE, &state->io); |
193 | } | |
194 | ||
4aae99b6 TC |
195 | /** |
196 | * acpi_memory_slot_status: | |
197 | * @mem_st: memory hotplug state | |
198 | * @dev: device | |
199 | * @errp: set in case of an error | |
200 | * | |
201 | * Obtain a single memory slot status. | |
202 | * | |
203 | * This function will be called by memory unplug request cb and unplug cb. | |
204 | */ | |
205 | static MemStatus * | |
206 | acpi_memory_slot_status(MemHotplugState *mem_st, | |
207 | DeviceState *dev, Error **errp) | |
3ef77aca | 208 | { |
3ef77aca | 209 | Error *local_err = NULL; |
1d515701 TC |
210 | int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, |
211 | &local_err); | |
3ef77aca IM |
212 | |
213 | if (local_err) { | |
214 | error_propagate(errp, local_err); | |
4aae99b6 | 215 | return NULL; |
3ef77aca IM |
216 | } |
217 | ||
218 | if (slot >= mem_st->dev_count) { | |
219 | char *dev_path = object_get_canonical_path(OBJECT(dev)); | |
4aae99b6 | 220 | error_setg(errp, "acpi_memory_slot_status: " |
3ef77aca IM |
221 | "device [%s] returned invalid memory slot[%d]", |
222 | dev_path, slot); | |
223 | g_free(dev_path); | |
4aae99b6 TC |
224 | return NULL; |
225 | } | |
226 | ||
227 | return &mem_st->devs[slot]; | |
228 | } | |
229 | ||
230 | void acpi_memory_plug_cb(ACPIREGS *ar, qemu_irq irq, MemHotplugState *mem_st, | |
231 | DeviceState *dev, Error **errp) | |
232 | { | |
233 | MemStatus *mdev; | |
5c42eef2 XG |
234 | DeviceClass *dc = DEVICE_GET_CLASS(dev); |
235 | ||
236 | if (!dc->hotpluggable) { | |
237 | return; | |
238 | } | |
4aae99b6 TC |
239 | |
240 | mdev = acpi_memory_slot_status(mem_st, dev, errp); | |
241 | if (!mdev) { | |
3ef77aca IM |
242 | return; |
243 | } | |
244 | ||
3ef77aca IM |
245 | mdev->dimm = dev; |
246 | mdev->is_enabled = true; | |
4828b10b IM |
247 | if (dev->hotplugged) { |
248 | mdev->is_inserting = true; | |
3ef77aca | 249 | |
4828b10b IM |
250 | /* do ACPI magic */ |
251 | acpi_send_gpe_event(ar, irq, ACPI_MEMORY_HOTPLUG_STATUS); | |
252 | } | |
3ef77aca | 253 | } |
f816a62d | 254 | |
64fec58e TC |
255 | void acpi_memory_unplug_request_cb(ACPIREGS *ar, qemu_irq irq, |
256 | MemHotplugState *mem_st, | |
257 | DeviceState *dev, Error **errp) | |
258 | { | |
259 | MemStatus *mdev; | |
260 | ||
261 | mdev = acpi_memory_slot_status(mem_st, dev, errp); | |
262 | if (!mdev) { | |
263 | return; | |
264 | } | |
265 | ||
266 | mdev->is_removing = true; | |
267 | ||
268 | /* Do ACPI magic */ | |
ca9b46bc | 269 | acpi_send_gpe_event(ar, irq, ACPI_MEMORY_HOTPLUG_STATUS); |
64fec58e TC |
270 | } |
271 | ||
f7d3e29d TC |
272 | void acpi_memory_unplug_cb(MemHotplugState *mem_st, |
273 | DeviceState *dev, Error **errp) | |
274 | { | |
275 | MemStatus *mdev; | |
276 | ||
277 | mdev = acpi_memory_slot_status(mem_st, dev, errp); | |
278 | if (!mdev) { | |
279 | return; | |
280 | } | |
281 | ||
282 | mdev->is_enabled = false; | |
283 | mdev->dimm = NULL; | |
284 | } | |
285 | ||
f816a62d IM |
286 | static const VMStateDescription vmstate_memhp_sts = { |
287 | .name = "memory hotplug device state", | |
288 | .version_id = 1, | |
289 | .minimum_version_id = 1, | |
290 | .minimum_version_id_old = 1, | |
291 | .fields = (VMStateField[]) { | |
292 | VMSTATE_BOOL(is_enabled, MemStatus), | |
293 | VMSTATE_BOOL(is_inserting, MemStatus), | |
294 | VMSTATE_UINT32(ost_event, MemStatus), | |
295 | VMSTATE_UINT32(ost_status, MemStatus), | |
296 | VMSTATE_END_OF_LIST() | |
297 | } | |
298 | }; | |
299 | ||
300 | const VMStateDescription vmstate_memory_hotplug = { | |
301 | .name = "memory hotplug state", | |
302 | .version_id = 1, | |
303 | .minimum_version_id = 1, | |
304 | .minimum_version_id_old = 1, | |
305 | .fields = (VMStateField[]) { | |
306 | VMSTATE_UINT32(selector, MemHotplugState), | |
307 | VMSTATE_STRUCT_VARRAY_POINTER_UINT32(devs, MemHotplugState, dev_count, | |
308 | vmstate_memhp_sts, MemStatus), | |
309 | VMSTATE_END_OF_LIST() | |
310 | } | |
311 | }; |