xref: /openbmc/qemu/hw/mem/pc-dimm.c (revision 988717b46b6424907618cb845ace9d69062703af)
1  /*
2   * Dimm device for Memory Hotplug
3   *
4   * Copyright ProfitBricks GmbH 2012
5   * Copyright (C) 2014 Red Hat Inc
6   *
7   * This library is free software; you can redistribute it and/or
8   * modify it under the terms of the GNU Lesser General Public
9   * License as published by the Free Software Foundation; either
10   * version 2 of the License, or (at your option) any later version.
11   *
12   * This library is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15   * Lesser General Public License for more details.
16   *
17   * You should have received a copy of the GNU Lesser General Public
18   * License along with this library; if not, see <http://www.gnu.org/licenses/>
19   */
20  
21  #include "qemu/osdep.h"
22  #include "hw/boards.h"
23  #include "hw/mem/pc-dimm.h"
24  #include "hw/qdev-properties.h"
25  #include "migration/vmstate.h"
26  #include "hw/mem/nvdimm.h"
27  #include "hw/mem/memory-device.h"
28  #include "qapi/error.h"
29  #include "qapi/visitor.h"
30  #include "qemu/module.h"
31  #include "sysemu/hostmem.h"
32  #include "sysemu/numa.h"
33  #include "trace.h"
34  
35  static int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp);
36  
37  void pc_dimm_pre_plug(PCDIMMDevice *dimm, MachineState *machine,
38                        const uint64_t *legacy_align, Error **errp)
39  {
40      Error *local_err = NULL;
41      int slot;
42  
43      slot = object_property_get_int(OBJECT(dimm), PC_DIMM_SLOT_PROP,
44                                     &error_abort);
45      if ((slot < 0 || slot >= machine->ram_slots) &&
46           slot != PC_DIMM_UNASSIGNED_SLOT) {
47          error_setg(&local_err, "invalid slot number, valid range is [0-%"
48                     PRIu64 "]", machine->ram_slots - 1);
49          goto out;
50      }
51  
52      slot = pc_dimm_get_free_slot(slot == PC_DIMM_UNASSIGNED_SLOT ? NULL : &slot,
53                                   machine->ram_slots, &local_err);
54      if (local_err) {
55          goto out;
56      }
57      object_property_set_int(OBJECT(dimm), slot, PC_DIMM_SLOT_PROP,
58                              &error_abort);
59      trace_mhp_pc_dimm_assigned_slot(slot);
60  
61      memory_device_pre_plug(MEMORY_DEVICE(dimm), machine, legacy_align,
62                             &local_err);
63  out:
64      error_propagate(errp, local_err);
65  }
66  
67  void pc_dimm_plug(PCDIMMDevice *dimm, MachineState *machine, Error **errp)
68  {
69      PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
70      MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm,
71                                                                &error_abort);
72  
73      memory_device_plug(MEMORY_DEVICE(dimm), machine);
74      vmstate_register_ram(vmstate_mr, DEVICE(dimm));
75  }
76  
77  void pc_dimm_unplug(PCDIMMDevice *dimm, MachineState *machine)
78  {
79      PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
80      MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm,
81                                                                &error_abort);
82  
83      memory_device_unplug(MEMORY_DEVICE(dimm), machine);
84      vmstate_unregister_ram(vmstate_mr, DEVICE(dimm));
85  }
86  
87  static int pc_dimm_slot2bitmap(Object *obj, void *opaque)
88  {
89      unsigned long *bitmap = opaque;
90  
91      if (object_dynamic_cast(obj, TYPE_PC_DIMM)) {
92          DeviceState *dev = DEVICE(obj);
93          if (dev->realized) { /* count only realized DIMMs */
94              PCDIMMDevice *d = PC_DIMM(obj);
95              set_bit(d->slot, bitmap);
96          }
97      }
98  
99      object_child_foreach(obj, pc_dimm_slot2bitmap, opaque);
100      return 0;
101  }
102  
103  static int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp)
104  {
105      unsigned long *bitmap;
106      int slot = 0;
107  
108      if (max_slots <= 0) {
109          error_setg(errp, "no slots where allocated, please specify "
110                     "the 'slots' option");
111          return slot;
112      }
113  
114      bitmap = bitmap_new(max_slots);
115      object_child_foreach(qdev_get_machine(), pc_dimm_slot2bitmap, bitmap);
116  
117      /* check if requested slot is not occupied */
118      if (hint) {
119          if (*hint >= max_slots) {
120              error_setg(errp, "invalid slot# %d, should be less than %d",
121                         *hint, max_slots);
122          } else if (!test_bit(*hint, bitmap)) {
123              slot = *hint;
124          } else {
125              error_setg(errp, "slot %d is busy", *hint);
126          }
127          goto out;
128      }
129  
130      /* search for free slot */
131      slot = find_first_zero_bit(bitmap, max_slots);
132      if (slot == max_slots) {
133          error_setg(errp, "no free slots available");
134      }
135  out:
136      g_free(bitmap);
137      return slot;
138  }
139  
140  static Property pc_dimm_properties[] = {
141      DEFINE_PROP_UINT64(PC_DIMM_ADDR_PROP, PCDIMMDevice, addr, 0),
142      DEFINE_PROP_UINT32(PC_DIMM_NODE_PROP, PCDIMMDevice, node, 0),
143      DEFINE_PROP_INT32(PC_DIMM_SLOT_PROP, PCDIMMDevice, slot,
144                        PC_DIMM_UNASSIGNED_SLOT),
145      DEFINE_PROP_LINK(PC_DIMM_MEMDEV_PROP, PCDIMMDevice, hostmem,
146                       TYPE_MEMORY_BACKEND, HostMemoryBackend *),
147      DEFINE_PROP_END_OF_LIST(),
148  };
149  
150  static void pc_dimm_get_size(Object *obj, Visitor *v, const char *name,
151                               void *opaque, Error **errp)
152  {
153      Error *local_err = NULL;
154      uint64_t value;
155  
156      value = memory_device_get_region_size(MEMORY_DEVICE(obj), &local_err);
157      if (local_err) {
158          error_propagate(errp, local_err);
159          return;
160      }
161  
162      visit_type_uint64(v, name, &value, errp);
163  }
164  
165  static void pc_dimm_init(Object *obj)
166  {
167      object_property_add(obj, PC_DIMM_SIZE_PROP, "uint64", pc_dimm_get_size,
168                          NULL, NULL, NULL, &error_abort);
169  }
170  
171  static void pc_dimm_realize(DeviceState *dev, Error **errp)
172  {
173      PCDIMMDevice *dimm = PC_DIMM(dev);
174      PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
175      MachineState *ms = MACHINE(qdev_get_machine());
176      int nb_numa_nodes = ms->numa_state->num_nodes;
177  
178      if (!dimm->hostmem) {
179          error_setg(errp, "'" PC_DIMM_MEMDEV_PROP "' property is not set");
180          return;
181      } else if (host_memory_backend_is_mapped(dimm->hostmem)) {
182          char *path = object_get_canonical_path_component(OBJECT(dimm->hostmem));
183          error_setg(errp, "can't use already busy memdev: %s", path);
184          g_free(path);
185          return;
186      }
187      if (((nb_numa_nodes > 0) && (dimm->node >= nb_numa_nodes)) ||
188          (!nb_numa_nodes && dimm->node)) {
189          error_setg(errp, "'DIMM property " PC_DIMM_NODE_PROP " has value %"
190                     PRIu32 "' which exceeds the number of numa nodes: %d",
191                     dimm->node, nb_numa_nodes ? nb_numa_nodes : 1);
192          return;
193      }
194  
195      if (ddc->realize) {
196          ddc->realize(dimm, errp);
197      }
198  
199      host_memory_backend_set_mapped(dimm->hostmem, true);
200  }
201  
202  static void pc_dimm_unrealize(DeviceState *dev, Error **errp)
203  {
204      PCDIMMDevice *dimm = PC_DIMM(dev);
205  
206      host_memory_backend_set_mapped(dimm->hostmem, false);
207  }
208  
209  static MemoryRegion *pc_dimm_get_memory_region(PCDIMMDevice *dimm, Error **errp)
210  {
211      if (!dimm->hostmem) {
212          error_setg(errp, "'" PC_DIMM_MEMDEV_PROP "' property must be set");
213          return NULL;
214      }
215  
216      return host_memory_backend_get_memory(dimm->hostmem);
217  }
218  
219  static uint64_t pc_dimm_md_get_addr(const MemoryDeviceState *md)
220  {
221      return object_property_get_uint(OBJECT(md), PC_DIMM_ADDR_PROP, &error_abort);
222  }
223  
224  static void pc_dimm_md_set_addr(MemoryDeviceState *md, uint64_t addr,
225                                  Error **errp)
226  {
227      object_property_set_uint(OBJECT(md), addr, PC_DIMM_ADDR_PROP, errp);
228  }
229  
230  static MemoryRegion *pc_dimm_md_get_memory_region(MemoryDeviceState *md,
231                                                    Error **errp)
232  {
233      return pc_dimm_get_memory_region(PC_DIMM(md), errp);
234  }
235  
236  static void pc_dimm_md_fill_device_info(const MemoryDeviceState *md,
237                                          MemoryDeviceInfo *info)
238  {
239      PCDIMMDeviceInfo *di = g_new0(PCDIMMDeviceInfo, 1);
240      const DeviceClass *dc = DEVICE_GET_CLASS(md);
241      const PCDIMMDevice *dimm = PC_DIMM(md);
242      const DeviceState *dev = DEVICE(md);
243  
244      if (dev->id) {
245          di->has_id = true;
246          di->id = g_strdup(dev->id);
247      }
248      di->hotplugged = dev->hotplugged;
249      di->hotpluggable = dc->hotpluggable;
250      di->addr = dimm->addr;
251      di->slot = dimm->slot;
252      di->node = dimm->node;
253      di->size = object_property_get_uint(OBJECT(dimm), PC_DIMM_SIZE_PROP,
254                                          NULL);
255      di->memdev = object_get_canonical_path(OBJECT(dimm->hostmem));
256  
257      if (object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)) {
258          info->u.nvdimm.data = di;
259          info->type = MEMORY_DEVICE_INFO_KIND_NVDIMM;
260      } else {
261          info->u.dimm.data = di;
262          info->type = MEMORY_DEVICE_INFO_KIND_DIMM;
263      }
264  }
265  
266  static void pc_dimm_class_init(ObjectClass *oc, void *data)
267  {
268      DeviceClass *dc = DEVICE_CLASS(oc);
269      PCDIMMDeviceClass *ddc = PC_DIMM_CLASS(oc);
270      MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(oc);
271  
272      dc->realize = pc_dimm_realize;
273      dc->unrealize = pc_dimm_unrealize;
274      device_class_set_props(dc, pc_dimm_properties);
275      dc->desc = "DIMM memory module";
276  
277      ddc->get_vmstate_memory_region = pc_dimm_get_memory_region;
278  
279      mdc->get_addr = pc_dimm_md_get_addr;
280      mdc->set_addr = pc_dimm_md_set_addr;
281      /* for a dimm plugged_size == region_size */
282      mdc->get_plugged_size = memory_device_get_region_size;
283      mdc->get_memory_region = pc_dimm_md_get_memory_region;
284      mdc->fill_device_info = pc_dimm_md_fill_device_info;
285  }
286  
287  static TypeInfo pc_dimm_info = {
288      .name          = TYPE_PC_DIMM,
289      .parent        = TYPE_DEVICE,
290      .instance_size = sizeof(PCDIMMDevice),
291      .instance_init = pc_dimm_init,
292      .class_init    = pc_dimm_class_init,
293      .class_size    = sizeof(PCDIMMDeviceClass),
294      .interfaces = (InterfaceInfo[]) {
295          { TYPE_MEMORY_DEVICE },
296          { }
297      },
298  };
299  
300  static void pc_dimm_register_types(void)
301  {
302      type_register_static(&pc_dimm_info);
303  }
304  
305  type_init(pc_dimm_register_types)
306