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