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