linux/drivers/xen/efi.c
<<
>>
Prefs
   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