DEFINE_XEN_GUEST_HANDLE(CHAR16);
+struct efi_rs_state {
+#ifdef CONFIG_X86
+ /*
+ * The way stacks get set up leads to them always being on an 8-byte
+ * boundary not evenly divisible by 16 (see asm-x86/current.h). The EFI ABI,
+ * just like the CPU one, however requires stacks to be 16-byte aligned
+ * before every function call. Since the compiler assumes this (unless
+ * passing it -mpreferred-stack-boundary=3), it wouldn't generate code to
+ * align the stack to 16 bytes even if putting a 16-byte aligned object
+ * there. Hence we need to force larger than 16-byte alignment, even if we
+ * don't strictly need that.
+ */
+ unsigned long __aligned(32) cr3;
+#endif
+};
+
+struct efi_rs_state efi_rs_enter(void);
+void efi_rs_leave(struct efi_rs_state *);
+
#ifndef COMPAT
/*
const struct efi_pci_rom *__read_mostly efi_pci_roms;
#ifndef CONFIG_ARM /* TODO - disabled until implemented on ARM */
-unsigned long efi_rs_enter(void)
+
+struct efi_rs_state efi_rs_enter(void)
{
static const u16 fcw = FCW_DEFAULT;
static const u32 mxcsr = MXCSR_DEFAULT;
- unsigned long cr3 = read_cr3();
+ struct efi_rs_state state = { .cr3 = 0 };
if ( !efi_l4_pgtable )
- return 0;
+ return state;
+ state.cr3 = read_cr3();
save_fpu_enable();
- asm volatile ( "fldcw %0" :: "m" (fcw) );
+ asm volatile ( "fnclex; fldcw %0" :: "m" (fcw) );
asm volatile ( "ldmxcsr %0" :: "m" (mxcsr) );
spin_lock(&efi_rs_lock);
write_cr3(virt_to_maddr(efi_l4_pgtable));
- return cr3;
+ return state;
}
-void efi_rs_leave(unsigned long cr3)
+void efi_rs_leave(struct efi_rs_state *state)
{
- if ( !cr3 )
+ if ( !state->cr3 )
return;
- write_cr3(cr3);
+ write_cr3(state->cr3);
if ( is_pv_vcpu(current) && !is_idle_vcpu(current) )
{
struct desc_ptr gdt_desc = {
{
EFI_TIME time;
EFI_STATUS status;
- unsigned long cr3 = efi_rs_enter(), flags;
+ struct efi_rs_state state = efi_rs_enter();
+ unsigned long flags;
- if ( !cr3 )
+ if ( !state.cr3 )
return 0;
spin_lock_irqsave(&rtc_lock, flags);
status = efi_rs->GetTime(&time, NULL);
spin_unlock_irqrestore(&rtc_lock, flags);
- efi_rs_leave(cr3);
+ efi_rs_leave(&state);
if ( EFI_ERROR(status) )
return 0;
void efi_halt_system(void)
{
EFI_STATUS status;
- unsigned long cr3 = efi_rs_enter();
+ struct efi_rs_state state = efi_rs_enter();
- if ( !cr3 )
+ if ( !state.cr3 )
return;
status = efi_rs->ResetSystem(EfiResetShutdown, EFI_SUCCESS, 0, NULL);
- efi_rs_leave(cr3);
+ efi_rs_leave(&state);
printk(XENLOG_WARNING "EFI: could not halt system (%#lx)\n", status);
}
void efi_reset_system(bool_t warm)
{
EFI_STATUS status;
- unsigned long cr3 = efi_rs_enter();
+ struct efi_rs_state state = efi_rs_enter();
- if ( !cr3 )
+ if ( !state.cr3 )
return;
status = efi_rs->ResetSystem(warm ? EfiResetWarm : EfiResetCold,
EFI_SUCCESS, 0, NULL);
- efi_rs_leave(cr3);
+ efi_rs_leave(&state);
printk(XENLOG_WARNING "EFI: could not reset system (%#lx)\n", status);
}
break;
case XEN_FW_EFI_RT_VERSION:
{
- unsigned long cr3 = efi_rs_enter();
+ struct efi_rs_state state = efi_rs_enter();
- if ( !cr3 )
+ if ( !state.cr3 )
return -EOPNOTSUPP;
info->version = efi_rs->Hdr.Revision;
- efi_rs_leave(cr3);
+ efi_rs_leave(&state);
break;
}
case XEN_FW_EFI_CONFIG_TABLE:
int efi_runtime_call(struct xenpf_efi_runtime_call *op)
{
- unsigned long cr3, flags;
+ struct efi_rs_state state;
+ unsigned long flags;
EFI_STATUS status = EFI_NOT_STARTED;
int rc = 0;
if ( op->misc )
return -EINVAL;
- cr3 = efi_rs_enter();
- if ( !cr3 )
+ state = efi_rs_enter();
+ if ( !state.cr3 )
return -EOPNOTSUPP;
spin_lock_irqsave(&rtc_lock, flags);
status = efi_rs->GetTime(cast_time(&op->u.get_time.time), &caps);
spin_unlock_irqrestore(&rtc_lock, flags);
- efi_rs_leave(cr3);
+ efi_rs_leave(&state);
if ( !EFI_ERROR(status) )
{
if ( op->misc )
return -EINVAL;
- cr3 = efi_rs_enter();
- if ( !cr3 )
+ state = efi_rs_enter();
+ if ( !state.cr3 )
return -EOPNOTSUPP;
spin_lock_irqsave(&rtc_lock, flags);
status = efi_rs->SetTime(cast_time(&op->u.set_time));
spin_unlock_irqrestore(&rtc_lock, flags);
- efi_rs_leave(cr3);
+ efi_rs_leave(&state);
break;
case XEN_EFI_get_wakeup_time:
if ( op->misc )
return -EINVAL;
- cr3 = efi_rs_enter();
- if ( !cr3 )
+ state = efi_rs_enter();
+ if ( !state.cr3 )
return -EOPNOTSUPP;
spin_lock_irqsave(&rtc_lock, flags);
status = efi_rs->GetWakeupTime(&enabled, &pending,
cast_time(&op->u.get_wakeup_time));
spin_unlock_irqrestore(&rtc_lock, flags);
- efi_rs_leave(cr3);
+ efi_rs_leave(&state);
if ( !EFI_ERROR(status) )
{
XEN_EFI_SET_WAKEUP_TIME_ENABLE_ONLY) )
return -EINVAL;
- cr3 = efi_rs_enter();
- if ( !cr3 )
+ state = efi_rs_enter();
+ if ( !state.cr3 )
return -EOPNOTSUPP;
spin_lock_irqsave(&rtc_lock, flags);
status = efi_rs->SetWakeupTime(!!(op->misc &
NULL :
cast_time(&op->u.set_wakeup_time));
spin_unlock_irqrestore(&rtc_lock, flags);
- efi_rs_leave(cr3);
+ efi_rs_leave(&state);
op->misc = 0;
break;
if ( op->misc )
return -EINVAL;
- cr3 = efi_rs_enter();
- if ( cr3 )
+ state = efi_rs_enter();
+ if ( state.cr3 )
status = efi_rs->GetNextHighMonotonicCount(&op->misc);
else
rc = -EOPNOTSUPP;
- efi_rs_leave(cr3);
+ efi_rs_leave(&state);
break;
case XEN_EFI_get_variable:
else
data = NULL;
- cr3 = efi_rs_enter();
- if ( cr3 )
+ state = efi_rs_enter();
+ if ( state.cr3 )
{
status = efi_rs->GetVariable(
name, cast_guid(&op->u.get_variable.vendor_guid),
&op->misc, &size, data);
- efi_rs_leave(cr3);
+ efi_rs_leave(&state);
if ( !EFI_ERROR(status) &&
copy_to_guest(op->u.get_variable.data, data, size) )
rc = -EFAULT;
else
{
- cr3 = efi_rs_enter();
- if ( cr3 )
+ state = efi_rs_enter();
+ if ( state.cr3 )
status = efi_rs->SetVariable(
name, cast_guid(&op->u.set_variable.vendor_guid),
op->misc, op->u.set_variable.size, data);
else
rc = -EOPNOTSUPP;
- efi_rs_leave(cr3);
+ efi_rs_leave(&state);
}
xfree(data);
return -EFAULT;
}
- cr3 = efi_rs_enter();
- if ( cr3 )
+ state = efi_rs_enter();
+ if ( state.cr3 )
{
status = efi_rs->GetNextVariableName(
&size, name.str,
cast_guid(&op->u.get_next_variable_name.vendor_guid));
- efi_rs_leave(cr3);
+ efi_rs_leave(&state);
/*
* Copy the variable name if necessary. The caller provided size
break;
}
- cr3 = efi_rs_enter();
- if ( !cr3 || (efi_rs->Hdr.Revision >> 16) < 2 )
+ state = efi_rs_enter();
+ if ( !state.cr3 || (efi_rs->Hdr.Revision >> 16) < 2 )
{
- efi_rs_leave(cr3);
+ efi_rs_leave(&state);
return -EOPNOTSUPP;
}
status = efi_rs->QueryVariableInfo(
&op->u.query_variable_info.max_store_size,
&op->u.query_variable_info.remain_store_size,
&op->u.query_variable_info.max_size);
- efi_rs_leave(cr3);
+ efi_rs_leave(&state);
break;
case XEN_EFI_query_capsule_capabilities:
if ( op->misc )
return -EINVAL;
- cr3 = efi_rs_enter();
- if ( !cr3 || (efi_rs->Hdr.Revision >> 16) < 2 )
+ state = efi_rs_enter();
+ if ( !state.cr3 || (efi_rs->Hdr.Revision >> 16) < 2 )
{
- efi_rs_leave(cr3);
+ efi_rs_leave(&state);
return -EOPNOTSUPP;
}
- efi_rs_leave(cr3);
+ efi_rs_leave(&state);
/* XXX fall through for now */
default:
return -ENOSYS;