Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | /* * Copyright (c) 2021 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ #include <sys/sysctl.h> #include <libkern/libkern.h> #include <kern/hvg_hypercall.h> #include <stdbool.h> /* Translate hypercall error code to syscall error code */ static int hv_ret_to_errno(hvg_hcall_return_t ret) { switch (ret) { case HVG_HCALL_ACCESS_DENIED: return EPERM; case HVG_HCALL_INVALID_CODE: case HVG_HCALL_INVALID_PARAMETER: return EINVAL; case HVG_HCALL_IO_FAILED: return EIO; case HVG_HCALL_FEAT_DISABLED: case HVG_HCALL_UNSUPPORTED: return ENOTSUP; default: return ENODEV; } } /* * Trigger a guest kernel core dump (Intel macOS VM only) * Usage: sysctl kern.hvg.trigger_kernel_coredump = 1 * (option selector must be 1, other values reserved). */ static int sysctl_trigger_kernel_coredump(__unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, __unused struct sysctl_req *req) { /* * Static because it retains the filename of the last core dump * (returned when the sysctl is read). */ static hvg_hcall_vmcore_file_t sysctl_vmcore; int error = 0; hvg_hcall_return_t hv_ret; char buf[3]; // 1 digit for dump option + 1 '\0' from userspace sysctl + 1 '\0' if (req->newptr) { // Write request // single digit (1 byte) + 1 terminating byte added by system_cmd sysctl if (req->newlen > 2) { return EINVAL; } error = SYSCTL_IN(req, buf, req->newlen); buf[req->newlen] = '\0'; if (!error) { if (strcmp(buf, "1") != 0) { return EINVAL; } /* Issue hypercall to trigger a dump */ hv_ret = hvg_hcall_trigger_dump(&sysctl_vmcore, HVG_HCALL_DUMP_OPTION_REGULAR); if (hv_ret == HVG_HCALL_SUCCESS) { error = SYSCTL_OUT(req, &sysctl_vmcore, sizeof(sysctl_vmcore)); } else { error = hv_ret_to_errno(hv_ret); } } } else { // Read request error = SYSCTL_OUT(req, &sysctl_vmcore, sizeof(sysctl_vmcore)); } return error; } /* * Get offset from the host's mach_absolute_time. */ static int sysctl_get_mabs_offset(__unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req) { uint64_t offset = 0; const hvg_hcall_return_t ret = hvg_hcall_get_mabs_offset(&offset); if (ret != HVG_HCALL_SUCCESS) { return hv_ret_to_errno(ret); } return SYSCTL_OUT(req, &offset, sizeof(offset)); } /* * Get the host's boot session UUID. */ static int sysctl_get_bootsessionuuid(__unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req) { uuid_string_t uuid = {0}; const hvg_hcall_return_t ret = hvg_hcall_get_bootsessionuuid(uuid); if (ret != HVG_HCALL_SUCCESS) { return hv_ret_to_errno(ret); } return SYSCTL_OUT(req, &uuid, sizeof(uuid)); } static int sysctl_vmm_present(__unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req) { int vmm_present = 0; #if defined(__arm64__) extern int IODTGetDefault(const char *key, void *infoAddr, unsigned int infoSize ); (void) IODTGetDefault("vmm-present", &vmm_present, sizeof(vmm_present)); #elif defined(__x86_64__) extern boolean_t cpuid_vmm_present(void); vmm_present = cpuid_vmm_present(); #endif vmm_present = !!vmm_present; return SYSCTL_OUT(req, &vmm_present, sizeof(vmm_present)); } static SYSCTL_NODE(_kern, OID_AUTO, hvg, CTLFLAG_RW | CTLFLAG_LOCKED | CTLFLAG_ANYBODY, 0, "hypervisor guest"); static SYSCTL_PROC(_kern_hvg, OID_AUTO, trigger_kernel_coredump, CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_NOAUTO, NULL, 0, sysctl_trigger_kernel_coredump, "A", "Request that the hypervisor take a live kernel dump"); static SYSCTL_PROC(_kern_hvg, OID_AUTO, mabs_offset, CTLTYPE_QUAD | CTLFLAG_RD | CTLFLAG_NOAUTO | CTLFLAG_LOCKED, NULL, 0, sysctl_get_mabs_offset, "Q", "host time offset"); static SYSCTL_PROC(_kern_hvg, OID_AUTO, host_bootsessionuuid, CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NOAUTO | CTLFLAG_LOCKED, NULL, 0, sysctl_get_bootsessionuuid, "A", "host boot session UUID"); /* Not dynamic, no need to manually register. */ static SYSCTL_PROC(_kern, OID_AUTO, hv_vmm_present, CTLTYPE_INT | CTLFLAG_ANYBODY | CTLFLAG_KERN | CTLFLAG_LOCKED, NULL, 0, sysctl_vmm_present, "I", "running on a vmm"); __startup_func static void hvg_sysctl_init(void) { struct { hvg_hcall_code_t hcall; struct sysctl_oid *oid; } hvg_sysctl[] = { { .hcall = HVG_HCALL_TRIGGER_DUMP, .oid = &sysctl__kern_hvg_trigger_kernel_coredump, }, { .hcall = HVG_HCALL_GET_MABS_OFFSET, .oid = &sysctl__kern_hvg_mabs_offset, }, { .hcall = HVG_HCALL_GET_BOOTSESSIONUUID, .oid = &sysctl__kern_hvg_host_bootsessionuuid, }, }; #define countof(x) (sizeof(x) / sizeof(x[0])) for (int i = 0; i < countof(hvg_sysctl); i++) { if (hvg_is_hcall_available(hvg_sysctl[i].hcall)) { sysctl_register_oid_early(hvg_sysctl[i].oid); } } } STARTUP(SYSCTL, STARTUP_RANK_MIDDLE, hvg_sysctl_init); |