1 /* 2 * EFI support for Xen. 3 * 4 * Copyright (C) 1999 VA Linux Systems 5 * Copyright (C) 1999 Walt Drummond <drummond@valinux.com> 6 * Copyright (C) 1999-2002 Hewlett-Packard Co. 7 * David Mosberger-Tang <davidm@hpl.hp.com> 8 * Stephane Eranian <eranian@hpl.hp.com> 9 * Copyright (C) 2005-2008 Intel Co. 10 * Fenghua Yu <fenghua.yu@intel.com> 11 * Bibo Mao <bibo.mao@intel.com> 12 * Chandramouli Narayanan <mouli@linux.intel.com> 13 * Huang Ying <ying.huang@intel.com> 14 * Copyright (C) 2011 Novell Co. 15 * Jan Beulich <JBeulich@suse.com> 16 * Copyright (C) 2011-2012 Oracle Co. 17 * Liang Tang <liang.tang@oracle.com> 18 * Copyright (c) 2014 Oracle Co., Daniel Kiper 19 */ 20 21 #include <linux/bug.h> 22 #include <linux/efi.h> 23 #include <linux/init.h> 24 #include <linux/string.h> 25 26 #include <xen/interface/xen.h> 27 #include <xen/interface/platform.h> 28 #include <xen/xen.h> 29 30 #include <asm/xen/hypercall.h> 31 32 #define INIT_EFI_OP(name) \ 33 {.cmd = XENPF_efi_runtime_call, \ 34 .u.efi_runtime_call.function = XEN_EFI_##name, \ 35 .u.efi_runtime_call.misc = 0} 36 37 #define efi_data(op) (op.u.efi_runtime_call) 38 39 static efi_status_t xen_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) 40 { 41 struct xen_platform_op op = INIT_EFI_OP(get_time); 42 43 if (HYPERVISOR_dom0_op(&op) < 0) 44 return EFI_UNSUPPORTED; 45 46 if (tm) { 47 BUILD_BUG_ON(sizeof(*tm) != sizeof(efi_data(op).u.get_time.time)); 48 memcpy(tm, &efi_data(op).u.get_time.time, sizeof(*tm)); 49 } 50 51 if (tc) { 52 tc->resolution = efi_data(op).u.get_time.resolution; 53 tc->accuracy = efi_data(op).u.get_time.accuracy; 54 tc->sets_to_zero = !!(efi_data(op).misc & 55 XEN_EFI_GET_TIME_SET_CLEARS_NS); 56 } 57 58 return efi_data(op).status; 59 } 60 61 static efi_status_t xen_efi_set_time(efi_time_t *tm) 62 { 63 struct xen_platform_op op = INIT_EFI_OP(set_time); 64 65 BUILD_BUG_ON(sizeof(*tm) != sizeof(efi_data(op).u.set_time)); 66 memcpy(&efi_data(op).u.set_time, tm, sizeof(*tm)); 67 68 if (HYPERVISOR_dom0_op(&op) < 0) 69 return EFI_UNSUPPORTED; 70 71 return efi_data(op).status; 72 } 73 74 static efi_status_t xen_efi_get_wakeup_time(efi_bool_t *enabled, 75 efi_bool_t *pending, 76 efi_time_t *tm) 77 { 78 struct xen_platform_op op = INIT_EFI_OP(get_wakeup_time); 79 80 if (HYPERVISOR_dom0_op(&op) < 0) 81 return EFI_UNSUPPORTED; 82 83 if (tm) { 84 BUILD_BUG_ON(sizeof(*tm) != sizeof(efi_data(op).u.get_wakeup_time)); 85 memcpy(tm, &efi_data(op).u.get_wakeup_time, sizeof(*tm)); 86 } 87 88 if (enabled) 89 *enabled = !!(efi_data(op).misc & XEN_EFI_GET_WAKEUP_TIME_ENABLED); 90 91 if (pending) 92 *pending = !!(efi_data(op).misc & XEN_EFI_GET_WAKEUP_TIME_PENDING); 93 94 return efi_data(op).status; 95 } 96 97 static efi_status_t xen_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm) 98 { 99 struct xen_platform_op op = INIT_EFI_OP(set_wakeup_time); 100 101 BUILD_BUG_ON(sizeof(*tm) != sizeof(efi_data(op).u.set_wakeup_time)); 102 if (enabled) 103 efi_data(op).misc = XEN_EFI_SET_WAKEUP_TIME_ENABLE; 104 if (tm) 105 memcpy(&efi_data(op).u.set_wakeup_time, tm, sizeof(*tm)); 106 else 107 efi_data(op).misc |= XEN_EFI_SET_WAKEUP_TIME_ENABLE_ONLY; 108 109 if (HYPERVISOR_dom0_op(&op) < 0) 110 return EFI_UNSUPPORTED; 111 112 return efi_data(op).status; 113 } 114 115 static efi_status_t xen_efi_get_variable(efi_char16_t *name, 116 efi_guid_t *vendor, 117 u32 *attr, 118 unsigned long *data_size, 119 void *data) 120 { 121 struct xen_platform_op op = INIT_EFI_OP(get_variable); 122 123 set_xen_guest_handle(efi_data(op).u.get_variable.name, name); 124 BUILD_BUG_ON(sizeof(*vendor) != 125 sizeof(efi_data(op).u.get_variable.vendor_guid)); 126 memcpy(&efi_data(op).u.get_variable.vendor_guid, vendor, sizeof(*vendor)); 127 efi_data(op).u.get_variable.size = *data_size; 128 set_xen_guest_handle(efi_data(op).u.get_variable.data, data); 129 130 if (HYPERVISOR_dom0_op(&op) < 0) 131 return EFI_UNSUPPORTED; 132 133 *data_size = efi_data(op).u.get_variable.size; 134 if (attr) 135 *attr = efi_data(op).misc; 136 137 return efi_data(op).status; 138 } 139 140 static efi_status_t xen_efi_get_next_variable(unsigned long *name_size, 141 efi_char16_t *name, 142 efi_guid_t *vendor) 143 { 144 struct xen_platform_op op = INIT_EFI_OP(get_next_variable_name); 145 146 efi_data(op).u.get_next_variable_name.size = *name_size; 147 set_xen_guest_handle(efi_data(op).u.get_next_variable_name.name, name); 148 BUILD_BUG_ON(sizeof(*vendor) != 149 sizeof(efi_data(op).u.get_next_variable_name.vendor_guid)); 150 memcpy(&efi_data(op).u.get_next_variable_name.vendor_guid, vendor, 151 sizeof(*vendor)); 152 153 if (HYPERVISOR_dom0_op(&op) < 0) 154 return EFI_UNSUPPORTED; 155 156 *name_size = efi_data(op).u.get_next_variable_name.size; 157 memcpy(vendor, &efi_data(op).u.get_next_variable_name.vendor_guid, 158 sizeof(*vendor)); 159 160 return efi_data(op).status; 161 } 162 163 static efi_status_t xen_efi_set_variable(efi_char16_t *name, 164 efi_guid_t *vendor, 165 u32 attr, 166 unsigned long data_size, 167 void *data) 168 { 169 struct xen_platform_op op = INIT_EFI_OP(set_variable); 170 171 set_xen_guest_handle(efi_data(op).u.set_variable.name, name); 172 efi_data(op).misc = attr; 173 BUILD_BUG_ON(sizeof(*vendor) != 174 sizeof(efi_data(op).u.set_variable.vendor_guid)); 175 memcpy(&efi_data(op).u.set_variable.vendor_guid, vendor, sizeof(*vendor)); 176 efi_data(op).u.set_variable.size = data_size; 177 set_xen_guest_handle(efi_data(op).u.set_variable.data, data); 178 179 if (HYPERVISOR_dom0_op(&op) < 0) 180 return EFI_UNSUPPORTED; 181 182 return efi_data(op).status; 183 } 184 185 static efi_status_t xen_efi_query_variable_info(u32 attr, 186 u64 *storage_space, 187 u64 *remaining_space, 188 u64 *max_variable_size) 189 { 190 struct xen_platform_op op = INIT_EFI_OP(query_variable_info); 191 192 if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) 193 return EFI_UNSUPPORTED; 194 195 efi_data(op).u.query_variable_info.attr = attr; 196 197 if (HYPERVISOR_dom0_op(&op) < 0) 198 return EFI_UNSUPPORTED; 199 200 *storage_space = efi_data(op).u.query_variable_info.max_store_size; 201 *remaining_space = efi_data(op).u.query_variable_info.remain_store_size; 202 *max_variable_size = efi_data(op).u.query_variable_info.max_size; 203 204 return efi_data(op).status; 205 } 206 207 static efi_status_t xen_efi_get_next_high_mono_count(u32 *count) 208 { 209 struct xen_platform_op op = INIT_EFI_OP(get_next_high_monotonic_count); 210 211 if (HYPERVISOR_dom0_op(&op) < 0) 212 return EFI_UNSUPPORTED; 213 214 *count = efi_data(op).misc; 215 216 return efi_data(op).status; 217 } 218 219 static efi_status_t xen_efi_update_capsule(efi_capsule_header_t **capsules, 220 unsigned long count, 221 unsigned long sg_list) 222 { 223 struct xen_platform_op op = INIT_EFI_OP(update_capsule); 224 225 if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) 226 return EFI_UNSUPPORTED; 227 228 set_xen_guest_handle(efi_data(op).u.update_capsule.capsule_header_array, 229 capsules); 230 efi_data(op).u.update_capsule.capsule_count = count; 231 efi_data(op).u.update_capsule.sg_list = sg_list; 232 233 if (HYPERVISOR_dom0_op(&op) < 0) 234 return EFI_UNSUPPORTED; 235 236 return efi_data(op).status; 237 } 238 239 static efi_status_t xen_efi_query_capsule_caps(efi_capsule_header_t **capsules, 240 unsigned long count, 241 u64 *max_size, 242 int *reset_type) 243 { 244 struct xen_platform_op op = INIT_EFI_OP(query_capsule_capabilities); 245 246 if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) 247 return EFI_UNSUPPORTED; 248 249 set_xen_guest_handle(efi_data(op).u.query_capsule_capabilities.capsule_header_array, 250 capsules); 251 efi_data(op).u.query_capsule_capabilities.capsule_count = count; 252 253 if (HYPERVISOR_dom0_op(&op) < 0) 254 return EFI_UNSUPPORTED; 255 256 *max_size = efi_data(op).u.query_capsule_capabilities.max_capsule_size; 257 *reset_type = efi_data(op).u.query_capsule_capabilities.reset_type; 258 259 return efi_data(op).status; 260 } 261 262 static efi_char16_t vendor[100] __initdata; 263 264 static efi_system_table_t efi_systab_xen __initdata = { 265 .hdr = { 266 .signature = EFI_SYSTEM_TABLE_SIGNATURE, 267 .revision = 0, /* Initialized later. */ 268 .headersize = 0, /* Ignored by Linux Kernel. */ 269 .crc32 = 0, /* Ignored by Linux Kernel. */ 270 .reserved = 0 271 }, 272 .fw_vendor = EFI_INVALID_TABLE_ADDR, /* Initialized later. */ 273 .fw_revision = 0, /* Initialized later. */ 274 .con_in_handle = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */ 275 .con_in = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */ 276 .con_out_handle = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */ 277 .con_out = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */ 278 .stderr_handle = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */ 279 .stderr = EFI_INVALID_TABLE_ADDR, /* Not used under Xen. */ 280 .runtime = (efi_runtime_services_t *)EFI_INVALID_TABLE_ADDR, 281 /* Not used under Xen. */ 282 .boottime = (efi_boot_services_t *)EFI_INVALID_TABLE_ADDR, 283 /* Not used under Xen. */ 284 .nr_tables = 0, /* Initialized later. */ 285 .tables = EFI_INVALID_TABLE_ADDR /* Initialized later. */ 286 }; 287 288 static const struct efi efi_xen __initconst = { 289 .systab = NULL, /* Initialized later. */ 290 .runtime_version = 0, /* Initialized later. */ 291 .mps = EFI_INVALID_TABLE_ADDR, 292 .acpi = EFI_INVALID_TABLE_ADDR, 293 .acpi20 = EFI_INVALID_TABLE_ADDR, 294 .smbios = EFI_INVALID_TABLE_ADDR, 295 .sal_systab = EFI_INVALID_TABLE_ADDR, 296 .boot_info = EFI_INVALID_TABLE_ADDR, 297 .hcdp = EFI_INVALID_TABLE_ADDR, 298 .uga = EFI_INVALID_TABLE_ADDR, 299 .uv_systab = EFI_INVALID_TABLE_ADDR, 300 .fw_vendor = EFI_INVALID_TABLE_ADDR, 301 .runtime = EFI_INVALID_TABLE_ADDR, 302 .config_table = EFI_INVALID_TABLE_ADDR, 303 .get_time = xen_efi_get_time, 304 .set_time = xen_efi_set_time, 305 .get_wakeup_time = xen_efi_get_wakeup_time, 306 .set_wakeup_time = xen_efi_set_wakeup_time, 307 .get_variable = xen_efi_get_variable, 308 .get_next_variable = xen_efi_get_next_variable, 309 .set_variable = xen_efi_set_variable, 310 .query_variable_info = xen_efi_query_variable_info, 311 .update_capsule = xen_efi_update_capsule, 312 .query_capsule_caps = xen_efi_query_capsule_caps, 313 .get_next_high_mono_count = xen_efi_get_next_high_mono_count, 314 .reset_system = NULL, /* Functionality provided by Xen. */ 315 .set_virtual_address_map = NULL, /* Not used under Xen. */ 316 .memmap = NULL, /* Not used under Xen. */ 317 .flags = 0 /* Initialized later. */ 318 }; 319 320 efi_system_table_t __init *xen_efi_probe(void) 321 { 322 struct xen_platform_op op = { 323 .cmd = XENPF_firmware_info, 324 .u.firmware_info = { 325 .type = XEN_FW_EFI_INFO, 326 .index = XEN_FW_EFI_CONFIG_TABLE 327 } 328 }; 329 union xenpf_efi_info *info = &op.u.firmware_info.u.efi_info; 330 331 if (!xen_initial_domain() || HYPERVISOR_dom0_op(&op) < 0) 332 return NULL; 333 334 /* Here we know that Xen runs on EFI platform. */ 335 336 efi = efi_xen; 337 338 efi_systab_xen.tables = info->cfg.addr; 339 efi_systab_xen.nr_tables = info->cfg.nent; 340 341 op.cmd = XENPF_firmware_info; 342 op.u.firmware_info.type = XEN_FW_EFI_INFO; 343 op.u.firmware_info.index = XEN_FW_EFI_VENDOR; 344 info->vendor.bufsz = sizeof(vendor); 345 set_xen_guest_handle(info->vendor.name, vendor); 346 347 if (HYPERVISOR_dom0_op(&op) == 0) { 348 efi_systab_xen.fw_vendor = __pa_symbol(vendor); 349 efi_systab_xen.fw_revision = info->vendor.revision; 350 } else 351 efi_systab_xen.fw_vendor = __pa_symbol(L"UNKNOWN"); 352 353 op.cmd = XENPF_firmware_info; 354 op.u.firmware_info.type = XEN_FW_EFI_INFO; 355 op.u.firmware_info.index = XEN_FW_EFI_VERSION; 356 357 if (HYPERVISOR_dom0_op(&op) == 0) 358 efi_systab_xen.hdr.revision = info->version; 359 360 op.cmd = XENPF_firmware_info; 361 op.u.firmware_info.type = XEN_FW_EFI_INFO; 362 op.u.firmware_info.index = XEN_FW_EFI_RT_VERSION; 363 364 if (HYPERVISOR_dom0_op(&op) == 0) 365 efi.runtime_version = info->version; 366 367 return &efi_systab_xen; 368 } 369