1 #include "qemu/osdep.h"
3 #include "hw/acpi/cpu.h"
4 #include "qapi/error.h"
7 #define ACPI_CPU_HOTPLUG_REG_LEN 12
8 #define ACPI_CPU_SELECTOR_OFFSET_WR 0
9 #define ACPI_CPU_FLAGS_OFFSET_RW 4
10 #define ACPI_CPU_CMD_OFFSET_WR 5
11 #define ACPI_CPU_CMD_DATA_OFFSET_RW 8
14 CPHP_GET_NEXT_CPU_WITH_EVENT_CMD
= 0,
18 static uint64_t cpu_hotplug_rd(void *opaque
, hwaddr addr
, unsigned size
)
21 CPUHotplugState
*cpu_st
= opaque
;
24 if (cpu_st
->selector
>= cpu_st
->dev_count
) {
28 cdev
= &cpu_st
->devs
[cpu_st
->selector
];
30 case ACPI_CPU_FLAGS_OFFSET_RW
: /* pack and return is_* fields */
31 val
|= cdev
->cpu
? 1 : 0;
32 val
|= cdev
->is_inserting
? 2 : 0;
33 val
|= cdev
->is_removing
? 4 : 0;
34 trace_cpuhp_acpi_read_flags(cpu_st
->selector
, val
);
36 case ACPI_CPU_CMD_DATA_OFFSET_RW
:
37 switch (cpu_st
->command
) {
38 case CPHP_GET_NEXT_CPU_WITH_EVENT_CMD
:
39 val
= cpu_st
->selector
;
44 trace_cpuhp_acpi_read_cmd_data(cpu_st
->selector
, val
);
52 static void cpu_hotplug_wr(void *opaque
, hwaddr addr
, uint64_t data
,
55 CPUHotplugState
*cpu_st
= opaque
;
58 assert(cpu_st
->dev_count
);
61 if (cpu_st
->selector
>= cpu_st
->dev_count
) {
62 trace_cpuhp_acpi_invalid_idx_selected(cpu_st
->selector
);
68 case ACPI_CPU_SELECTOR_OFFSET_WR
: /* current CPU selector */
69 cpu_st
->selector
= data
;
70 trace_cpuhp_acpi_write_idx(cpu_st
->selector
);
72 case ACPI_CPU_FLAGS_OFFSET_RW
: /* set is_* fields */
73 cdev
= &cpu_st
->devs
[cpu_st
->selector
];
74 if (data
& 2) { /* clear insert event */
75 cdev
->is_inserting
= false;
76 trace_cpuhp_acpi_clear_inserting_evt(cpu_st
->selector
);
77 } else if (data
& 4) { /* clear remove event */
78 cdev
->is_removing
= false;
79 trace_cpuhp_acpi_clear_remove_evt(cpu_st
->selector
);
80 } else if (data
& 8) {
81 DeviceState
*dev
= NULL
;
82 HotplugHandler
*hotplug_ctrl
= NULL
;
85 trace_cpuhp_acpi_ejecting_invalid_cpu(cpu_st
->selector
);
89 trace_cpuhp_acpi_ejecting_cpu(cpu_st
->selector
);
90 dev
= DEVICE(cdev
->cpu
);
91 hotplug_ctrl
= qdev_get_hotplug_handler(dev
);
92 hotplug_handler_unplug(hotplug_ctrl
, dev
, NULL
);
95 case ACPI_CPU_CMD_OFFSET_WR
:
96 trace_cpuhp_acpi_write_cmd(cpu_st
->selector
, data
);
97 if (data
< CPHP_CMD_MAX
) {
98 cpu_st
->command
= data
;
99 if (cpu_st
->command
== CPHP_GET_NEXT_CPU_WITH_EVENT_CMD
) {
100 uint32_t iter
= cpu_st
->selector
;
103 cdev
= &cpu_st
->devs
[iter
];
104 if (cdev
->is_inserting
|| cdev
->is_removing
) {
105 cpu_st
->selector
= iter
;
106 trace_cpuhp_acpi_cpu_has_events(cpu_st
->selector
,
107 cdev
->is_inserting
, cdev
->is_removing
);
110 iter
= iter
+ 1 < cpu_st
->dev_count
? iter
+ 1 : 0;
111 } while (iter
!= cpu_st
->selector
);
120 static const MemoryRegionOps cpu_hotplug_ops
= {
121 .read
= cpu_hotplug_rd
,
122 .write
= cpu_hotplug_wr
,
123 .endianness
= DEVICE_LITTLE_ENDIAN
,
125 .min_access_size
= 1,
126 .max_access_size
= 4,
130 void cpu_hotplug_hw_init(MemoryRegion
*as
, Object
*owner
,
131 CPUHotplugState
*state
, hwaddr base_addr
)
133 MachineState
*machine
= MACHINE(qdev_get_machine());
134 MachineClass
*mc
= MACHINE_GET_CLASS(machine
);
135 CPUArchIdList
*id_list
;
138 assert(mc
->possible_cpu_arch_ids
);
139 id_list
= mc
->possible_cpu_arch_ids(machine
);
140 state
->dev_count
= id_list
->len
;
141 state
->devs
= g_new0(typeof(*state
->devs
), state
->dev_count
);
142 for (i
= 0; i
< id_list
->len
; i
++) {
143 state
->devs
[i
].cpu
= id_list
->cpus
[i
].cpu
;
144 state
->devs
[i
].arch_id
= id_list
->cpus
[i
].arch_id
;
147 memory_region_init_io(&state
->ctrl_reg
, owner
, &cpu_hotplug_ops
, state
,
148 "acpi-mem-hotplug", ACPI_CPU_HOTPLUG_REG_LEN
);
149 memory_region_add_subregion(as
, base_addr
, &state
->ctrl_reg
);
152 static AcpiCpuStatus
*get_cpu_status(CPUHotplugState
*cpu_st
, DeviceState
*dev
)
154 CPUClass
*k
= CPU_GET_CLASS(dev
);
155 uint64_t cpu_arch_id
= k
->get_arch_id(CPU(dev
));
158 for (i
= 0; i
< cpu_st
->dev_count
; i
++) {
159 if (cpu_arch_id
== cpu_st
->devs
[i
].arch_id
) {
160 return &cpu_st
->devs
[i
];
166 void acpi_cpu_plug_cb(HotplugHandler
*hotplug_dev
,
167 CPUHotplugState
*cpu_st
, DeviceState
*dev
, Error
**errp
)
171 cdev
= get_cpu_status(cpu_st
, dev
);
176 cdev
->cpu
= CPU(dev
);
177 if (dev
->hotplugged
) {
178 cdev
->is_inserting
= true;
179 acpi_send_event(DEVICE(hotplug_dev
), ACPI_CPU_HOTPLUG_STATUS
);
183 void acpi_cpu_unplug_request_cb(HotplugHandler
*hotplug_dev
,
184 CPUHotplugState
*cpu_st
,
185 DeviceState
*dev
, Error
**errp
)
189 cdev
= get_cpu_status(cpu_st
, dev
);
194 cdev
->is_removing
= true;
195 acpi_send_event(DEVICE(hotplug_dev
), ACPI_CPU_HOTPLUG_STATUS
);
198 void acpi_cpu_unplug_cb(CPUHotplugState
*cpu_st
,
199 DeviceState
*dev
, Error
**errp
)
203 cdev
= get_cpu_status(cpu_st
, dev
);
211 static const VMStateDescription vmstate_cpuhp_sts
= {
212 .name
= "CPU hotplug device state",
214 .minimum_version_id
= 1,
215 .minimum_version_id_old
= 1,
216 .fields
= (VMStateField
[]) {
217 VMSTATE_BOOL(is_inserting
, AcpiCpuStatus
),
218 VMSTATE_BOOL(is_removing
, AcpiCpuStatus
),
219 VMSTATE_END_OF_LIST()
223 const VMStateDescription vmstate_cpu_hotplug
= {
224 .name
= "CPU hotplug state",
226 .minimum_version_id
= 1,
227 .minimum_version_id_old
= 1,
228 .fields
= (VMStateField
[]) {
229 VMSTATE_UINT32(selector
, CPUHotplugState
),
230 VMSTATE_UINT8(command
, CPUHotplugState
),
231 VMSTATE_STRUCT_VARRAY_POINTER_UINT32(devs
, CPUHotplugState
, dev_count
,
232 vmstate_cpuhp_sts
, AcpiCpuStatus
),
233 VMSTATE_END_OF_LIST()
237 #define CPU_NAME_FMT "C%.03X"
238 #define CPUHP_RES_DEVICE "PRES"
239 #define CPU_LOCK "CPLK"
240 #define CPU_STS_METHOD "CSTA"
241 #define CPU_SCAN_METHOD "CSCN"
242 #define CPU_NOTIFY_METHOD "CTFY"
243 #define CPU_EJECT_METHOD "CEJ0"
245 #define CPU_ENABLED "CPEN"
246 #define CPU_SELECTOR "CSEL"
247 #define CPU_COMMAND "CCMD"
248 #define CPU_DATA "CDAT"
249 #define CPU_INSERT_EVENT "CINS"
250 #define CPU_REMOVE_EVENT "CRMV"
251 #define CPU_EJECT_EVENT "CEJ0"
253 void build_cpus_aml(Aml
*table
, MachineState
*machine
, CPUHotplugFeatures opts
,
255 const char *res_root
,
256 const char *event_handler_method
)
263 Aml
*zero
= aml_int(0);
264 Aml
*one
= aml_int(1);
265 Aml
*sb_scope
= aml_scope("_SB");
266 MachineClass
*mc
= MACHINE_GET_CLASS(machine
);
267 CPUArchIdList
*arch_ids
= mc
->possible_cpu_arch_ids(machine
);
268 char *cphp_res_path
= g_strdup_printf("%s." CPUHP_RES_DEVICE
, res_root
);
269 Object
*obj
= object_resolve_path_type("", TYPE_ACPI_DEVICE_IF
, NULL
);
270 AcpiDeviceIfClass
*adevc
= ACPI_DEVICE_IF_GET_CLASS(obj
);
271 AcpiDeviceIf
*adev
= ACPI_DEVICE_IF(obj
);
273 cpu_ctrl_dev
= aml_device("%s", cphp_res_path
);
277 aml_append(cpu_ctrl_dev
,
278 aml_name_decl("_HID", aml_eisaid("PNP0A06")));
279 aml_append(cpu_ctrl_dev
,
280 aml_name_decl("_UID", aml_string("CPU Hotplug resources")));
281 aml_append(cpu_ctrl_dev
, aml_mutex(CPU_LOCK
, 0));
283 crs
= aml_resource_template();
284 aml_append(crs
, aml_io(AML_DECODE16
, io_base
, io_base
, 1,
285 ACPI_CPU_HOTPLUG_REG_LEN
));
286 aml_append(cpu_ctrl_dev
, aml_name_decl("_CRS", crs
));
288 /* declare CPU hotplug MMIO region with related access fields */
289 aml_append(cpu_ctrl_dev
,
290 aml_operation_region("PRST", AML_SYSTEM_IO
, aml_int(io_base
),
291 ACPI_CPU_HOTPLUG_REG_LEN
));
293 field
= aml_field("PRST", AML_BYTE_ACC
, AML_NOLOCK
,
295 aml_append(field
, aml_reserved_field(ACPI_CPU_FLAGS_OFFSET_RW
* 8));
296 /* 1 if enabled, read only */
297 aml_append(field
, aml_named_field(CPU_ENABLED
, 1));
298 /* (read) 1 if has a insert event. (write) 1 to clear event */
299 aml_append(field
, aml_named_field(CPU_INSERT_EVENT
, 1));
300 /* (read) 1 if has a remove event. (write) 1 to clear event */
301 aml_append(field
, aml_named_field(CPU_REMOVE_EVENT
, 1));
302 /* initiates device eject, write only */
303 aml_append(field
, aml_named_field(CPU_EJECT_EVENT
, 1));
304 aml_append(field
, aml_reserved_field(4));
305 aml_append(field
, aml_named_field(CPU_COMMAND
, 8));
306 aml_append(cpu_ctrl_dev
, field
);
308 field
= aml_field("PRST", AML_DWORD_ACC
, AML_NOLOCK
, AML_PRESERVE
);
309 /* CPU selector, write only */
310 aml_append(field
, aml_named_field(CPU_SELECTOR
, 32));
311 /* flags + cmd + 2byte align */
312 aml_append(field
, aml_reserved_field(4 * 8));
313 aml_append(field
, aml_named_field(CPU_DATA
, 32));
314 aml_append(cpu_ctrl_dev
, field
);
317 aml_append(sb_scope
, cpu_ctrl_dev
);
319 cpus_dev
= aml_device("\\_SB.CPUS");
322 Aml
*ctrl_lock
= aml_name("%s.%s", cphp_res_path
, CPU_LOCK
);
323 Aml
*cpu_selector
= aml_name("%s.%s", cphp_res_path
, CPU_SELECTOR
);
324 Aml
*is_enabled
= aml_name("%s.%s", cphp_res_path
, CPU_ENABLED
);
325 Aml
*cpu_cmd
= aml_name("%s.%s", cphp_res_path
, CPU_COMMAND
);
326 Aml
*cpu_data
= aml_name("%s.%s", cphp_res_path
, CPU_DATA
);
327 Aml
*ins_evt
= aml_name("%s.%s", cphp_res_path
, CPU_INSERT_EVENT
);
328 Aml
*rm_evt
= aml_name("%s.%s", cphp_res_path
, CPU_REMOVE_EVENT
);
329 Aml
*ej_evt
= aml_name("%s.%s", cphp_res_path
, CPU_EJECT_EVENT
);
331 aml_append(cpus_dev
, aml_name_decl("_HID", aml_string("ACPI0010")));
332 aml_append(cpus_dev
, aml_name_decl("_CID", aml_eisaid("PNP0A05")));
334 method
= aml_method(CPU_NOTIFY_METHOD
, 2, AML_NOTSERIALIZED
);
335 for (i
= 0; i
< arch_ids
->len
; i
++) {
336 Aml
*cpu
= aml_name(CPU_NAME_FMT
, i
);
337 Aml
*uid
= aml_arg(0);
338 Aml
*event
= aml_arg(1);
340 ifctx
= aml_if(aml_equal(uid
, aml_int(i
)));
342 aml_append(ifctx
, aml_notify(cpu
, event
));
344 aml_append(method
, ifctx
);
346 aml_append(cpus_dev
, method
);
348 method
= aml_method(CPU_STS_METHOD
, 1, AML_SERIALIZED
);
350 Aml
*idx
= aml_arg(0);
351 Aml
*sta
= aml_local(0);
353 aml_append(method
, aml_acquire(ctrl_lock
, 0xFFFF));
354 aml_append(method
, aml_store(idx
, cpu_selector
));
355 aml_append(method
, aml_store(zero
, sta
));
356 ifctx
= aml_if(aml_equal(is_enabled
, one
));
358 aml_append(ifctx
, aml_store(aml_int(0xF), sta
));
360 aml_append(method
, ifctx
);
361 aml_append(method
, aml_release(ctrl_lock
));
362 aml_append(method
, aml_return(sta
));
364 aml_append(cpus_dev
, method
);
366 method
= aml_method(CPU_EJECT_METHOD
, 1, AML_SERIALIZED
);
368 Aml
*idx
= aml_arg(0);
370 aml_append(method
, aml_acquire(ctrl_lock
, 0xFFFF));
371 aml_append(method
, aml_store(idx
, cpu_selector
));
372 aml_append(method
, aml_store(one
, ej_evt
));
373 aml_append(method
, aml_release(ctrl_lock
));
375 aml_append(cpus_dev
, method
);
377 method
= aml_method(CPU_SCAN_METHOD
, 0, AML_SERIALIZED
);
381 Aml
*has_event
= aml_local(0);
382 Aml
*dev_chk
= aml_int(1);
383 Aml
*eject_req
= aml_int(3);
384 Aml
*next_cpu_cmd
= aml_int(CPHP_GET_NEXT_CPU_WITH_EVENT_CMD
);
386 aml_append(method
, aml_acquire(ctrl_lock
, 0xFFFF));
387 aml_append(method
, aml_store(one
, has_event
));
388 while_ctx
= aml_while(aml_equal(has_event
, one
));
390 /* clear loop exit condition, ins_evt/rm_evt checks
391 * will set it to 1 while next_cpu_cmd returns a CPU
393 aml_append(while_ctx
, aml_store(zero
, has_event
));
394 aml_append(while_ctx
, aml_store(next_cpu_cmd
, cpu_cmd
));
395 ifctx
= aml_if(aml_equal(ins_evt
, one
));
398 aml_call2(CPU_NOTIFY_METHOD
, cpu_data
, dev_chk
));
399 aml_append(ifctx
, aml_store(one
, ins_evt
));
400 aml_append(ifctx
, aml_store(one
, has_event
));
402 aml_append(while_ctx
, ifctx
);
403 else_ctx
= aml_else();
404 ifctx
= aml_if(aml_equal(rm_evt
, one
));
407 aml_call2(CPU_NOTIFY_METHOD
, cpu_data
, eject_req
));
408 aml_append(ifctx
, aml_store(one
, rm_evt
));
409 aml_append(ifctx
, aml_store(one
, has_event
));
411 aml_append(else_ctx
, ifctx
);
412 aml_append(while_ctx
, else_ctx
);
414 aml_append(method
, while_ctx
);
415 aml_append(method
, aml_release(ctrl_lock
));
417 aml_append(cpus_dev
, method
);
419 /* build Processor object for each processor */
420 for (i
= 0; i
< arch_ids
->len
; i
++) {
422 Aml
*uid
= aml_int(i
);
423 GArray
*madt_buf
= g_array_new(0, 1, 1);
424 int arch_id
= arch_ids
->cpus
[i
].arch_id
;
426 if (opts
.apci_1_compatible
&& arch_id
< 255) {
427 dev
= aml_processor(i
, 0, 0, CPU_NAME_FMT
, i
);
429 dev
= aml_device(CPU_NAME_FMT
, i
);
430 aml_append(dev
, aml_name_decl("_HID", aml_string("ACPI0007")));
431 aml_append(dev
, aml_name_decl("_UID", uid
));
434 method
= aml_method("_STA", 0, AML_SERIALIZED
);
435 aml_append(method
, aml_return(aml_call1(CPU_STS_METHOD
, uid
)));
436 aml_append(dev
, method
);
438 /* build _MAT object */
439 assert(adevc
&& adevc
->madt_cpu
);
440 adevc
->madt_cpu(adev
, i
, arch_ids
, madt_buf
);
441 switch (madt_buf
->data
[0]) {
442 case ACPI_APIC_PROCESSOR
: {
443 AcpiMadtProcessorApic
*apic
= (void *)madt_buf
->data
;
444 apic
->flags
= cpu_to_le32(1);
450 aml_append(dev
, aml_name_decl("_MAT",
451 aml_buffer(madt_buf
->len
, (uint8_t *)madt_buf
->data
)));
452 g_array_free(madt_buf
, true);
454 method
= aml_method("_EJ0", 1, AML_NOTSERIALIZED
);
455 aml_append(method
, aml_call1(CPU_EJECT_METHOD
, uid
));
456 aml_append(dev
, method
);
458 aml_append(cpus_dev
, dev
);
461 aml_append(sb_scope
, cpus_dev
);
462 aml_append(table
, sb_scope
);
464 method
= aml_method(event_handler_method
, 0, AML_NOTSERIALIZED
);
465 aml_append(method
, aml_call0("\\_SB.CPUS." CPU_SCAN_METHOD
));
466 aml_append(table
, method
);
468 g_free(cphp_res_path
);