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