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