xref: /openbmc/qemu/hw/acpi/vmclock.c (revision 0e3327b690b76b7c3966b028110ee053cc16a385)
1*3634039bSDavid Woodhouse /*
2*3634039bSDavid Woodhouse  * Virtual Machine Clock Device
3*3634039bSDavid Woodhouse  *
4*3634039bSDavid Woodhouse  * Copyright © 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5*3634039bSDavid Woodhouse  *
6*3634039bSDavid Woodhouse  * Authors: David Woodhouse <dwmw2@infradead.org>
7*3634039bSDavid Woodhouse  *
8*3634039bSDavid Woodhouse  * This work is licensed under the terms of the GNU GPL, version 2 or later.
9*3634039bSDavid Woodhouse  * See the COPYING file in the top-level directory.
10*3634039bSDavid Woodhouse  */
11*3634039bSDavid Woodhouse 
12*3634039bSDavid Woodhouse #include "qemu/osdep.h"
13*3634039bSDavid Woodhouse #include "qapi/error.h"
14*3634039bSDavid Woodhouse #include "qemu/module.h"
15*3634039bSDavid Woodhouse #include "hw/i386/e820_memory_layout.h"
16*3634039bSDavid Woodhouse #include "hw/acpi/acpi.h"
17*3634039bSDavid Woodhouse #include "hw/acpi/aml-build.h"
18*3634039bSDavid Woodhouse #include "hw/acpi/vmclock.h"
19*3634039bSDavid Woodhouse #include "hw/nvram/fw_cfg.h"
20*3634039bSDavid Woodhouse #include "hw/qdev-properties.h"
21*3634039bSDavid Woodhouse #include "hw/qdev-properties-system.h"
22*3634039bSDavid Woodhouse #include "migration/vmstate.h"
23*3634039bSDavid Woodhouse #include "system/reset.h"
24*3634039bSDavid Woodhouse 
25*3634039bSDavid Woodhouse #include "standard-headers/linux/vmclock-abi.h"
26*3634039bSDavid Woodhouse 
vmclock_build_acpi(VmclockState * vms,GArray * table_data,BIOSLinker * linker,const char * oem_id)27*3634039bSDavid Woodhouse void vmclock_build_acpi(VmclockState *vms, GArray *table_data,
28*3634039bSDavid Woodhouse                         BIOSLinker *linker, const char *oem_id)
29*3634039bSDavid Woodhouse {
30*3634039bSDavid Woodhouse     Aml *ssdt, *dev, *scope, *crs;
31*3634039bSDavid Woodhouse     AcpiTable table = { .sig = "SSDT", .rev = 1,
32*3634039bSDavid Woodhouse                         .oem_id = oem_id, .oem_table_id = "VMCLOCK" };
33*3634039bSDavid Woodhouse 
34*3634039bSDavid Woodhouse     /* Put VMCLOCK into a separate SSDT table */
35*3634039bSDavid Woodhouse     acpi_table_begin(&table, table_data);
36*3634039bSDavid Woodhouse     ssdt = init_aml_allocator();
37*3634039bSDavid Woodhouse 
38*3634039bSDavid Woodhouse     scope = aml_scope("\\_SB");
39*3634039bSDavid Woodhouse     dev = aml_device("VCLK");
40*3634039bSDavid Woodhouse     aml_append(dev, aml_name_decl("_HID", aml_string("AMZNC10C")));
41*3634039bSDavid Woodhouse     aml_append(dev, aml_name_decl("_CID", aml_string("VMCLOCK")));
42*3634039bSDavid Woodhouse     aml_append(dev, aml_name_decl("_DDN", aml_string("VMCLOCK")));
43*3634039bSDavid Woodhouse 
44*3634039bSDavid Woodhouse     /* Simple status method */
45*3634039bSDavid Woodhouse     aml_append(dev, aml_name_decl("_STA", aml_int(0xf)));
46*3634039bSDavid Woodhouse 
47*3634039bSDavid Woodhouse     crs = aml_resource_template();
48*3634039bSDavid Woodhouse     aml_append(crs, aml_qword_memory(AML_POS_DECODE,
49*3634039bSDavid Woodhouse                                      AML_MIN_FIXED, AML_MAX_FIXED,
50*3634039bSDavid Woodhouse                                      AML_CACHEABLE, AML_READ_ONLY,
51*3634039bSDavid Woodhouse                                      0xffffffffffffffffULL,
52*3634039bSDavid Woodhouse                                      vms->physaddr,
53*3634039bSDavid Woodhouse                                      vms->physaddr + VMCLOCK_SIZE - 1,
54*3634039bSDavid Woodhouse                                      0, VMCLOCK_SIZE));
55*3634039bSDavid Woodhouse     aml_append(dev, aml_name_decl("_CRS", crs));
56*3634039bSDavid Woodhouse     aml_append(scope, dev);
57*3634039bSDavid Woodhouse     aml_append(ssdt, scope);
58*3634039bSDavid Woodhouse 
59*3634039bSDavid Woodhouse     g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len);
60*3634039bSDavid Woodhouse     acpi_table_end(linker, &table);
61*3634039bSDavid Woodhouse     free_aml_allocator();
62*3634039bSDavid Woodhouse }
63*3634039bSDavid Woodhouse 
vmclock_update_guest(VmclockState * vms)64*3634039bSDavid Woodhouse static void vmclock_update_guest(VmclockState *vms)
65*3634039bSDavid Woodhouse {
66*3634039bSDavid Woodhouse     uint64_t disruption_marker;
67*3634039bSDavid Woodhouse     uint32_t seq_count;
68*3634039bSDavid Woodhouse 
69*3634039bSDavid Woodhouse     if (!vms->clk) {
70*3634039bSDavid Woodhouse         return;
71*3634039bSDavid Woodhouse     }
72*3634039bSDavid Woodhouse 
73*3634039bSDavid Woodhouse     seq_count = le32_to_cpu(vms->clk->seq_count) | 1;
74*3634039bSDavid Woodhouse     vms->clk->seq_count = cpu_to_le32(seq_count);
75*3634039bSDavid Woodhouse     /* These barriers pair with read barriers in the guest */
76*3634039bSDavid Woodhouse     smp_wmb();
77*3634039bSDavid Woodhouse 
78*3634039bSDavid Woodhouse     disruption_marker = le64_to_cpu(vms->clk->disruption_marker);
79*3634039bSDavid Woodhouse     disruption_marker++;
80*3634039bSDavid Woodhouse     vms->clk->disruption_marker = cpu_to_le64(disruption_marker);
81*3634039bSDavid Woodhouse 
82*3634039bSDavid Woodhouse     /* These barriers pair with read barriers in the guest */
83*3634039bSDavid Woodhouse     smp_wmb();
84*3634039bSDavid Woodhouse     vms->clk->seq_count = cpu_to_le32(seq_count + 1);
85*3634039bSDavid Woodhouse }
86*3634039bSDavid Woodhouse 
87*3634039bSDavid Woodhouse /*
88*3634039bSDavid Woodhouse  * After restoring an image, we need to update the guest memory to notify
89*3634039bSDavid Woodhouse  * it of clock disruption.
90*3634039bSDavid Woodhouse  */
vmclock_post_load(void * opaque,int version_id)91*3634039bSDavid Woodhouse static int vmclock_post_load(void *opaque, int version_id)
92*3634039bSDavid Woodhouse {
93*3634039bSDavid Woodhouse     VmclockState *vms = opaque;
94*3634039bSDavid Woodhouse 
95*3634039bSDavid Woodhouse     vmclock_update_guest(vms);
96*3634039bSDavid Woodhouse     return 0;
97*3634039bSDavid Woodhouse }
98*3634039bSDavid Woodhouse 
99*3634039bSDavid Woodhouse static const VMStateDescription vmstate_vmclock = {
100*3634039bSDavid Woodhouse     .name = "vmclock",
101*3634039bSDavid Woodhouse     .version_id = 1,
102*3634039bSDavid Woodhouse     .minimum_version_id = 1,
103*3634039bSDavid Woodhouse     .post_load = vmclock_post_load,
104*3634039bSDavid Woodhouse     .fields = (const VMStateField[]) {
105*3634039bSDavid Woodhouse         VMSTATE_UINT64(physaddr, VmclockState),
106*3634039bSDavid Woodhouse         VMSTATE_END_OF_LIST()
107*3634039bSDavid Woodhouse     },
108*3634039bSDavid Woodhouse };
109*3634039bSDavid Woodhouse 
vmclock_handle_reset(void * opaque)110*3634039bSDavid Woodhouse static void vmclock_handle_reset(void *opaque)
111*3634039bSDavid Woodhouse {
112*3634039bSDavid Woodhouse     VmclockState *vms = VMCLOCK(opaque);
113*3634039bSDavid Woodhouse 
114*3634039bSDavid Woodhouse     if (!memory_region_is_mapped(&vms->clk_page)) {
115*3634039bSDavid Woodhouse         memory_region_add_subregion_overlap(get_system_memory(),
116*3634039bSDavid Woodhouse                                             vms->physaddr,
117*3634039bSDavid Woodhouse                                             &vms->clk_page, 0);
118*3634039bSDavid Woodhouse     }
119*3634039bSDavid Woodhouse }
120*3634039bSDavid Woodhouse 
vmclock_realize(DeviceState * dev,Error ** errp)121*3634039bSDavid Woodhouse static void vmclock_realize(DeviceState *dev, Error **errp)
122*3634039bSDavid Woodhouse {
123*3634039bSDavid Woodhouse     VmclockState *vms = VMCLOCK(dev);
124*3634039bSDavid Woodhouse 
125*3634039bSDavid Woodhouse     /*
126*3634039bSDavid Woodhouse      * Given that this function is executing, there is at least one VMCLOCK
127*3634039bSDavid Woodhouse      * device. Check if there are several.
128*3634039bSDavid Woodhouse      */
129*3634039bSDavid Woodhouse     if (!find_vmclock_dev()) {
130*3634039bSDavid Woodhouse         error_setg(errp, "at most one %s device is permitted", TYPE_VMCLOCK);
131*3634039bSDavid Woodhouse         return;
132*3634039bSDavid Woodhouse     }
133*3634039bSDavid Woodhouse 
134*3634039bSDavid Woodhouse     vms->physaddr = VMCLOCK_ADDR;
135*3634039bSDavid Woodhouse 
136*3634039bSDavid Woodhouse     e820_add_entry(vms->physaddr, VMCLOCK_SIZE, E820_RESERVED);
137*3634039bSDavid Woodhouse 
138*3634039bSDavid Woodhouse     memory_region_init_ram(&vms->clk_page, OBJECT(dev), "vmclock_page",
139*3634039bSDavid Woodhouse                            VMCLOCK_SIZE, &error_abort);
140*3634039bSDavid Woodhouse     memory_region_set_enabled(&vms->clk_page, true);
141*3634039bSDavid Woodhouse     vms->clk = memory_region_get_ram_ptr(&vms->clk_page);
142*3634039bSDavid Woodhouse     memset(vms->clk, 0, VMCLOCK_SIZE);
143*3634039bSDavid Woodhouse 
144*3634039bSDavid Woodhouse     vms->clk->magic = cpu_to_le32(VMCLOCK_MAGIC);
145*3634039bSDavid Woodhouse     vms->clk->size = cpu_to_le16(VMCLOCK_SIZE);
146*3634039bSDavid Woodhouse     vms->clk->version = cpu_to_le16(1);
147*3634039bSDavid Woodhouse 
148*3634039bSDavid Woodhouse     /* These are all zero and thus default, but be explicit */
149*3634039bSDavid Woodhouse     vms->clk->clock_status = VMCLOCK_STATUS_UNKNOWN;
150*3634039bSDavid Woodhouse     vms->clk->counter_id = VMCLOCK_COUNTER_INVALID;
151*3634039bSDavid Woodhouse 
152*3634039bSDavid Woodhouse     qemu_register_reset(vmclock_handle_reset, vms);
153*3634039bSDavid Woodhouse 
154*3634039bSDavid Woodhouse     vmclock_update_guest(vms);
155*3634039bSDavid Woodhouse }
156*3634039bSDavid Woodhouse 
vmclock_device_class_init(ObjectClass * klass,void * data)157*3634039bSDavid Woodhouse static void vmclock_device_class_init(ObjectClass *klass, void *data)
158*3634039bSDavid Woodhouse {
159*3634039bSDavid Woodhouse     DeviceClass *dc = DEVICE_CLASS(klass);
160*3634039bSDavid Woodhouse 
161*3634039bSDavid Woodhouse     dc->vmsd = &vmstate_vmclock;
162*3634039bSDavid Woodhouse     dc->realize = vmclock_realize;
163*3634039bSDavid Woodhouse     dc->hotpluggable = false;
164*3634039bSDavid Woodhouse     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
165*3634039bSDavid Woodhouse }
166*3634039bSDavid Woodhouse 
167*3634039bSDavid Woodhouse static const TypeInfo vmclock_device_info = {
168*3634039bSDavid Woodhouse     .name          = TYPE_VMCLOCK,
169*3634039bSDavid Woodhouse     .parent        = TYPE_DEVICE,
170*3634039bSDavid Woodhouse     .instance_size = sizeof(VmclockState),
171*3634039bSDavid Woodhouse     .class_init    = vmclock_device_class_init,
172*3634039bSDavid Woodhouse };
173*3634039bSDavid Woodhouse 
vmclock_register_types(void)174*3634039bSDavid Woodhouse static void vmclock_register_types(void)
175*3634039bSDavid Woodhouse {
176*3634039bSDavid Woodhouse     type_register_static(&vmclock_device_info);
177*3634039bSDavid Woodhouse }
178*3634039bSDavid Woodhouse 
179*3634039bSDavid Woodhouse type_init(vmclock_register_types)
180