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