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 41 static 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 63 static 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 76 static 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 99 static 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 117 static 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 142 static 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 165 static 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 187 static 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 209 static 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 221 static 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 241 static 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 264 static efi_char16_t vendor[100] __initdata; 265 266 static 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 290 static 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 .sal_systab = EFI_INVALID_TABLE_ADDR, 298 .boot_info = EFI_INVALID_TABLE_ADDR, 299 .hcdp = EFI_INVALID_TABLE_ADDR, 300 .uga = EFI_INVALID_TABLE_ADDR, 301 .uv_systab = EFI_INVALID_TABLE_ADDR, 302 .fw_vendor = EFI_INVALID_TABLE_ADDR, 303 .runtime = EFI_INVALID_TABLE_ADDR, 304 .config_table = EFI_INVALID_TABLE_ADDR, 305 .get_time = xen_efi_get_time, 306 .set_time = xen_efi_set_time, 307 .get_wakeup_time = xen_efi_get_wakeup_time, 308 .set_wakeup_time = xen_efi_set_wakeup_time, 309 .get_variable = xen_efi_get_variable, 310 .get_next_variable = xen_efi_get_next_variable, 311 .set_variable = xen_efi_set_variable, 312 .query_variable_info = xen_efi_query_variable_info, 313 .update_capsule = xen_efi_update_capsule, 314 .query_capsule_caps = xen_efi_query_capsule_caps, 315 .get_next_high_mono_count = xen_efi_get_next_high_mono_count, 316 .reset_system = NULL, /* Functionality provided by Xen. */ 317 .set_virtual_address_map = NULL, /* Not used under Xen. */ 318 .memmap = NULL, /* Not used under Xen. */ 319 .flags = 0 /* Initialized later. */ 320 }; 321 322 efi_system_table_t __init *xen_efi_probe(void) 323 { 324 struct xen_platform_op op = { 325 .cmd = XENPF_firmware_info, 326 .u.firmware_info = { 327 .type = XEN_FW_EFI_INFO, 328 .index = XEN_FW_EFI_CONFIG_TABLE 329 } 330 }; 331 union xenpf_efi_info *info = &op.u.firmware_info.u.efi_info; 332 333 if (!xen_initial_domain() || HYPERVISOR_dom0_op(&op) < 0) 334 return NULL; 335 336 /* Here we know that Xen runs on EFI platform. */ 337 338 efi = efi_xen; 339 340 efi_systab_xen.tables = info->cfg.addr; 341 efi_systab_xen.nr_tables = info->cfg.nent; 342 343 op.cmd = XENPF_firmware_info; 344 op.u.firmware_info.type = XEN_FW_EFI_INFO; 345 op.u.firmware_info.index = XEN_FW_EFI_VENDOR; 346 info->vendor.bufsz = sizeof(vendor); 347 set_xen_guest_handle(info->vendor.name, vendor); 348 349 if (HYPERVISOR_dom0_op(&op) == 0) { 350 efi_systab_xen.fw_vendor = __pa_symbol(vendor); 351 efi_systab_xen.fw_revision = info->vendor.revision; 352 } else 353 efi_systab_xen.fw_vendor = __pa_symbol(L"UNKNOWN"); 354 355 op.cmd = XENPF_firmware_info; 356 op.u.firmware_info.type = XEN_FW_EFI_INFO; 357 op.u.firmware_info.index = XEN_FW_EFI_VERSION; 358 359 if (HYPERVISOR_dom0_op(&op) == 0) 360 efi_systab_xen.hdr.revision = info->version; 361 362 op.cmd = XENPF_firmware_info; 363 op.u.firmware_info.type = XEN_FW_EFI_INFO; 364 op.u.firmware_info.index = XEN_FW_EFI_RT_VERSION; 365 366 if (HYPERVISOR_dom0_op(&op) == 0) 367 efi.runtime_version = info->version; 368 369 return &efi_systab_xen; 370 } 371