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