1 #include "efi.h"
2 #include "runtime.h"
3 #include <xen/cache.h>
4 #include <xen/errno.h>
5 #include <xen/guest_access.h>
6 #include <xen/irq.h>
7 #include <xen/time.h>
8 
9 DEFINE_XEN_GUEST_HANDLE(CHAR16);
10 
11 struct efi_rs_state {
12 #ifdef CONFIG_X86
13  /*
14   * The way stacks get set up leads to them always being on an 8-byte
15   * boundary not evenly divisible by 16 (see asm/current.h). The EFI ABI,
16   * just like the CPU one, however requires stacks to be 16-byte aligned
17   * before every function call. Since the compiler assumes this (unless
18   * passing it -mpreferred-stack-boundary=3), it wouldn't generate code to
19   * align the stack to 16 bytes even if putting a 16-byte aligned object
20   * there. Hence we need to force larger than 16-byte alignment, even if we
21   * don't strictly need that.
22   */
23  unsigned long __aligned(32) cr3;
24     unsigned long msr_s_cet;
25 #endif
26 };
27 
28 struct efi_rs_state efi_rs_enter(void);
29 void efi_rs_leave(struct efi_rs_state *state);
30 
31 #ifndef COMPAT
32 
33 #ifndef CONFIG_ARM
34 # include <asm/i387.h>
35 # include <asm/xstate.h>
36 # include <public/platform.h>
37 #endif
38 
39 unsigned int __read_mostly efi_num_ct;
40 const EFI_CONFIGURATION_TABLE *__read_mostly efi_ct;
41 
42 unsigned int __read_mostly efi_version;
43 unsigned int __read_mostly efi_fw_revision;
44 const CHAR16 *__read_mostly efi_fw_vendor;
45 
46 const EFI_RUNTIME_SERVICES *__read_mostly efi_rs;
47 #ifndef CONFIG_ARM /* TODO - disabled until implemented on ARM */
48 static DEFINE_SPINLOCK(efi_rs_lock);
49 static unsigned int efi_rs_on_cpu = NR_CPUS;
50 #endif
51 
52 UINTN __read_mostly efi_memmap_size;
53 UINTN __read_mostly efi_mdesc_size;
54 void *__read_mostly efi_memmap;
55 
56 UINT64 __read_mostly efi_boot_max_var_store_size;
57 UINT64 __read_mostly efi_boot_remain_var_store_size;
58 UINT64 __read_mostly efi_boot_max_var_size;
59 
60 UINT64 __read_mostly efi_apple_properties_addr;
61 UINTN __read_mostly efi_apple_properties_len;
62 
63 /* Bit field representing available EFI features/properties. */
64 unsigned int efi_flags;
65 
66 struct efi __read_mostly efi = {
67 	.acpi   = EFI_INVALID_TABLE_ADDR,
68 	.acpi20 = EFI_INVALID_TABLE_ADDR,
69 	.mps    = EFI_INVALID_TABLE_ADDR,
70 	.smbios = EFI_INVALID_TABLE_ADDR,
71 	.smbios3 = EFI_INVALID_TABLE_ADDR,
72 };
73 
74 const struct efi_pci_rom *__read_mostly efi_pci_roms;
75 
efi_enabled(unsigned int feature)76 bool efi_enabled(unsigned int feature)
77 {
78     return test_bit(feature, &efi_flags);
79 }
80 
81 #ifndef CONFIG_ARM /* TODO - disabled until implemented on ARM */
82 
efi_rs_enter(void)83 struct efi_rs_state efi_rs_enter(void)
84 {
85     static const u16 fcw = FCW_DEFAULT;
86     static const u32 mxcsr = MXCSR_DEFAULT;
87     struct efi_rs_state state = { .cr3 = 0 };
88 
89     if ( mfn_eq(efi_l4_mfn, INVALID_MFN) )
90         return state;
91 
92     state.cr3 = read_cr3();
93     save_fpu_enable();
94     asm volatile ( "fnclex; fldcw %0" :: "m" (fcw) );
95     asm volatile ( "ldmxcsr %0" :: "m" (mxcsr) );
96 
97     spin_lock(&efi_rs_lock);
98 
99     efi_rs_on_cpu = smp_processor_id();
100 
101     /* prevent fixup_page_fault() from doing anything */
102     irq_enter();
103 
104     if ( is_pv_vcpu(current) && !is_idle_vcpu(current) )
105     {
106         struct desc_ptr gdt_desc = {
107             .limit = LAST_RESERVED_GDT_BYTE,
108             .base  = (unsigned long)(per_cpu(gdt, smp_processor_id()) -
109                                      FIRST_RESERVED_GDT_ENTRY)
110         };
111 
112         lgdt(&gdt_desc);
113     }
114 
115     switch_cr3_cr4(mfn_to_maddr(efi_l4_mfn), read_cr4());
116 
117     /*
118      * At the time of writing (2022), no UEFI firwmare is CET-IBT compatible.
119      * Work is under way to remedy this.
120      *
121      * Stash MSR_S_CET and clobber ENDBR_EN.  This is necessary because
122      * SHSTK_EN isn't configured until very late on the BSP.
123      */
124     if ( cpu_has_xen_ibt )
125     {
126         rdmsrl(MSR_S_CET, state.msr_s_cet);
127         wrmsrl(MSR_S_CET, state.msr_s_cet & ~CET_ENDBR_EN);
128     }
129 
130     return state;
131 }
132 
efi_rs_leave(struct efi_rs_state * state)133 void efi_rs_leave(struct efi_rs_state *state)
134 {
135     struct vcpu *curr = current;
136 
137     if ( !state->cr3 )
138         return;
139 
140     if ( state->msr_s_cet )
141         wrmsrl(MSR_S_CET, state->msr_s_cet);
142 
143     switch_cr3_cr4(state->cr3, read_cr4());
144     if ( is_pv_vcpu(curr) && !is_idle_vcpu(curr) )
145     {
146         struct desc_ptr gdt_desc = {
147             .limit = LAST_RESERVED_GDT_BYTE,
148             .base  = GDT_VIRT_START(curr)
149         };
150 
151         lgdt(&gdt_desc);
152     }
153     irq_exit();
154     efi_rs_on_cpu = NR_CPUS;
155     spin_unlock(&efi_rs_lock);
156     vcpu_restore_fpu_nonlazy(curr, true);
157 }
158 
efi_rs_using_pgtables(void)159 bool efi_rs_using_pgtables(void)
160 {
161     return !mfn_eq(efi_l4_mfn, INVALID_MFN) &&
162            (smp_processor_id() == efi_rs_on_cpu) &&
163            (read_cr3() == mfn_to_maddr(efi_l4_mfn));
164 }
165 
efi_get_time(void)166 unsigned long efi_get_time(void)
167 {
168     EFI_TIME time;
169     EFI_STATUS status;
170     struct efi_rs_state state = efi_rs_enter();
171     unsigned long flags;
172 
173     if ( !state.cr3 )
174         return 0;
175     spin_lock_irqsave(&rtc_lock, flags);
176     status = efi_rs->GetTime(&time, NULL);
177     spin_unlock_irqrestore(&rtc_lock, flags);
178     efi_rs_leave(&state);
179 
180     if ( EFI_ERROR(status) )
181         return 0;
182 
183     return mktime(time.Year, time.Month, time.Day,
184                   time.Hour, time.Minute, time.Second);
185 }
186 
efi_halt_system(void)187 void efi_halt_system(void)
188 {
189     EFI_STATUS status;
190     struct efi_rs_state state = efi_rs_enter();
191 
192     if ( !state.cr3 )
193         return;
194     status = efi_rs->ResetSystem(EfiResetShutdown, EFI_SUCCESS, 0, NULL);
195     efi_rs_leave(&state);
196 
197     printk(XENLOG_WARNING "EFI: could not halt system (%#lx)\n", status);
198 }
199 
efi_reset_system(bool warm)200 void efi_reset_system(bool warm)
201 {
202     EFI_STATUS status;
203     struct efi_rs_state state = efi_rs_enter();
204 
205     if ( !state.cr3 )
206         return;
207     status = efi_rs->ResetSystem(warm ? EfiResetWarm : EfiResetCold,
208                                  EFI_SUCCESS, 0, NULL);
209     efi_rs_leave(&state);
210 
211     printk(XENLOG_WARNING "EFI: could not reset system (%#lx)\n", status);
212 }
213 
214 #endif /* CONFIG_ARM */
215 
wmemchr(const CHAR16 * s,CHAR16 c,UINTN n)216 const CHAR16 *wmemchr(const CHAR16 *s, CHAR16 c, UINTN n)
217 {
218     while ( n && *s != c )
219     {
220         --n;
221         ++s;
222     }
223     return n ? s : NULL;
224 }
225 
226 #endif /* COMPAT */
227 
228 #ifndef CONFIG_ARM /* TODO - disabled until implemented on ARM */
efi_get_info(uint32_t idx,union xenpf_efi_info * info)229 int efi_get_info(uint32_t idx, union xenpf_efi_info *info)
230 {
231     unsigned int i, n;
232 
233     if ( !efi_enabled(EFI_BOOT) )
234         return -ENOSYS;
235 
236     switch ( idx )
237     {
238     case XEN_FW_EFI_VERSION:
239         info->version = efi_version;
240         break;
241     case XEN_FW_EFI_RT_VERSION:
242     {
243         if ( !efi_enabled(EFI_RS) )
244             return -EOPNOTSUPP;
245         info->version = efi_rs->Hdr.Revision;
246         break;
247     }
248     case XEN_FW_EFI_CONFIG_TABLE:
249         info->cfg.addr = __pa(efi_ct);
250         info->cfg.nent = efi_num_ct;
251         break;
252     case XEN_FW_EFI_VENDOR:
253         if ( !efi_fw_vendor )
254             return -EOPNOTSUPP;
255         info->vendor.revision = efi_fw_revision;
256         n = info->vendor.bufsz / sizeof(*efi_fw_vendor);
257         if ( !guest_handle_okay(guest_handle_cast(info->vendor.name,
258                                                   CHAR16), n) )
259             return -EFAULT;
260         for ( i = 0; i < n; ++i )
261         {
262             if ( __copy_to_guest_offset(info->vendor.name, i,
263                                         efi_fw_vendor + i, 1) )
264                 return -EFAULT;
265             if ( !efi_fw_vendor[i] )
266                 break;
267         }
268         break;
269     case XEN_FW_EFI_MEM_INFO:
270         for ( i = 0; i < efi_memmap_size; i += efi_mdesc_size )
271         {
272             EFI_MEMORY_DESCRIPTOR *desc = efi_memmap + i;
273             u64 len = desc->NumberOfPages << EFI_PAGE_SHIFT;
274 
275             if ( info->mem.addr >= desc->PhysicalStart &&
276                  info->mem.addr < desc->PhysicalStart + len )
277             {
278                 info->mem.type = desc->Type;
279                 info->mem.attr = desc->Attribute;
280                 if ( info->mem.addr + info->mem.size < info->mem.addr ||
281                      info->mem.addr + info->mem.size >
282                      desc->PhysicalStart + len )
283                     info->mem.size = desc->PhysicalStart + len -
284                                      info->mem.addr;
285                 return 0;
286             }
287         }
288         return -ESRCH;
289     case XEN_FW_EFI_PCI_ROM: {
290         const struct efi_pci_rom *ent;
291 
292         for ( ent = efi_pci_roms; ent; ent = ent->next )
293             if ( info->pci_rom.segment == ent->segment &&
294                  info->pci_rom.bus == ent->bus &&
295                  info->pci_rom.devfn == ent->devfn &&
296                  info->pci_rom.vendor == ent->vendor &&
297                  info->pci_rom.devid == ent->devid )
298             {
299                 info->pci_rom.address = __pa(ent->data);
300                 info->pci_rom.size = ent->size;
301                 return 0;
302             }
303         return -ESRCH;
304     }
305 
306     case XEN_FW_EFI_APPLE_PROPERTIES:
307         if ( !efi_apple_properties_len )
308             return -ENODATA;
309         info->apple_properties.address = efi_apple_properties_addr;
310         info->apple_properties.size = efi_apple_properties_len;
311         break;
312 
313     default:
314         return -EINVAL;
315     }
316 
317     return 0;
318 }
319 
gwstrlen(XEN_GUEST_HANDLE_PARAM (CHAR16)str)320 static long gwstrlen(XEN_GUEST_HANDLE_PARAM(CHAR16) str)
321 {
322     unsigned long len;
323 
324     for ( len = 0; ; ++len )
325     {
326         CHAR16 c;
327 
328         if ( copy_from_guest_offset(&c, str, len, 1) )
329             return -EFAULT;
330         if ( !c )
331             break;
332     }
333 
334     return len;
335 }
336 
cast_time(struct xenpf_efi_time * time)337 static inline EFI_TIME *cast_time(struct xenpf_efi_time *time)
338 {
339 #define chk_fld(F, f) \
340     BUILD_BUG_ON(sizeof(cast_time(NULL)->F) != sizeof(time->f) || \
341                  offsetof(EFI_TIME, F) != offsetof(struct xenpf_efi_time, f))
342     chk_fld(Year, year);
343     chk_fld(Month, month);
344     chk_fld(Day, day);
345     chk_fld(Hour, hour);
346     chk_fld(Minute, min);
347     chk_fld(Second, sec);
348     chk_fld(Nanosecond, ns);
349     chk_fld(TimeZone, tz);
350     chk_fld(Daylight, daylight);
351 #undef chk_fld
352     return (void *)time;
353 }
354 
cast_guid(struct xenpf_efi_guid * guid)355 static inline EFI_GUID *cast_guid(struct xenpf_efi_guid *guid)
356 {
357 #define chk_fld(n) \
358     BUILD_BUG_ON(sizeof(cast_guid(NULL)->Data##n) != sizeof(guid->data##n) || \
359                  offsetof(EFI_GUID, Data##n) != \
360                  offsetof(struct xenpf_efi_guid, data##n))
361     chk_fld(1);
362     chk_fld(2);
363     chk_fld(3);
364     chk_fld(4);
365 #undef chk_fld
366     return (void *)guid;
367 }
368 
efi_runtime_call(struct xenpf_efi_runtime_call * op)369 int efi_runtime_call(struct xenpf_efi_runtime_call *op)
370 {
371     struct efi_rs_state state;
372     unsigned long flags;
373     EFI_STATUS status = EFI_NOT_STARTED;
374     int rc = 0;
375 
376     if ( !efi_enabled(EFI_BOOT) )
377         return -ENOSYS;
378 
379     if ( !efi_enabled(EFI_RS) )
380         return -EOPNOTSUPP;
381 
382     switch ( op->function )
383     {
384     case XEN_EFI_get_time:
385     {
386         EFI_TIME_CAPABILITIES caps;
387 
388         if ( op->misc )
389             return -EINVAL;
390 
391         state = efi_rs_enter();
392         if ( !state.cr3 )
393             return -EOPNOTSUPP;
394         spin_lock_irqsave(&rtc_lock, flags);
395         status = efi_rs->GetTime(cast_time(&op->u.get_time.time), &caps);
396         spin_unlock_irqrestore(&rtc_lock, flags);
397         efi_rs_leave(&state);
398 
399         if ( !EFI_ERROR(status) )
400         {
401             op->u.get_time.resolution = caps.Resolution;
402             op->u.get_time.accuracy = caps.Accuracy;
403             if ( caps.SetsToZero )
404                 op->misc = XEN_EFI_GET_TIME_SET_CLEARS_NS;
405         }
406     }
407     break;
408 
409     case XEN_EFI_set_time:
410         if ( op->misc )
411             return -EINVAL;
412 
413         state = efi_rs_enter();
414         if ( !state.cr3 )
415             return -EOPNOTSUPP;
416         spin_lock_irqsave(&rtc_lock, flags);
417         status = efi_rs->SetTime(cast_time(&op->u.set_time));
418         spin_unlock_irqrestore(&rtc_lock, flags);
419         efi_rs_leave(&state);
420         break;
421 
422     case XEN_EFI_get_wakeup_time:
423     {
424         BOOLEAN enabled, pending;
425 
426         if ( op->misc )
427             return -EINVAL;
428 
429         state = efi_rs_enter();
430         if ( !state.cr3 )
431             return -EOPNOTSUPP;
432         spin_lock_irqsave(&rtc_lock, flags);
433         status = efi_rs->GetWakeupTime(&enabled, &pending,
434                                        cast_time(&op->u.get_wakeup_time));
435         spin_unlock_irqrestore(&rtc_lock, flags);
436         efi_rs_leave(&state);
437 
438         if ( !EFI_ERROR(status) )
439         {
440             if ( enabled )
441                 op->misc |= XEN_EFI_GET_WAKEUP_TIME_ENABLED;
442             if ( pending )
443                 op->misc |= XEN_EFI_GET_WAKEUP_TIME_PENDING;
444         }
445     }
446     break;
447 
448     case XEN_EFI_set_wakeup_time:
449         if ( op->misc & ~(XEN_EFI_SET_WAKEUP_TIME_ENABLE |
450                           XEN_EFI_SET_WAKEUP_TIME_ENABLE_ONLY) )
451             return -EINVAL;
452 
453         state = efi_rs_enter();
454         if ( !state.cr3 )
455             return -EOPNOTSUPP;
456         spin_lock_irqsave(&rtc_lock, flags);
457         status = efi_rs->SetWakeupTime(!!(op->misc &
458                                           XEN_EFI_SET_WAKEUP_TIME_ENABLE),
459                                        (op->misc &
460                                         XEN_EFI_SET_WAKEUP_TIME_ENABLE_ONLY) ?
461                                        NULL :
462                                        cast_time(&op->u.set_wakeup_time));
463         spin_unlock_irqrestore(&rtc_lock, flags);
464         efi_rs_leave(&state);
465 
466         op->misc = 0;
467         break;
468 
469     case XEN_EFI_get_next_high_monotonic_count:
470         if ( op->misc )
471             return -EINVAL;
472 
473         state = efi_rs_enter();
474         if ( state.cr3 )
475             status = efi_rs->GetNextHighMonotonicCount(&op->misc);
476         else
477             rc = -EOPNOTSUPP;
478         efi_rs_leave(&state);
479         break;
480 
481     case XEN_EFI_get_variable:
482     {
483         CHAR16 *name;
484         long len;
485         unsigned char *data;
486         UINTN size;
487 
488         if ( op->misc )
489             return -EINVAL;
490 
491         len = gwstrlen(guest_handle_cast(op->u.get_variable.name, CHAR16));
492         if ( len < 0 )
493             return len;
494         name = xmalloc_array(CHAR16, ++len);
495         if ( !name )
496            return -ENOMEM;
497         if ( __copy_from_guest(name, op->u.get_variable.name, len) ||
498              wmemchr(name, 0, len) != name + len - 1 )
499         {
500             xfree(name);
501             return -EIO;
502         }
503 
504         size = op->u.get_variable.size;
505         if ( size )
506         {
507             data = xmalloc_bytes(size);
508             if ( !data )
509             {
510                 xfree(name);
511                 return -ENOMEM;
512             }
513         }
514         else
515             data = NULL;
516 
517         state = efi_rs_enter();
518         if ( state.cr3 )
519         {
520             status = efi_rs->GetVariable(
521                 name, cast_guid(&op->u.get_variable.vendor_guid),
522                 &op->misc, &size, data);
523             efi_rs_leave(&state);
524 
525             if ( !EFI_ERROR(status) &&
526                  copy_to_guest(op->u.get_variable.data, data, size) )
527                 rc = -EFAULT;
528             op->u.get_variable.size = size;
529         }
530         else
531             rc = -EOPNOTSUPP;
532 
533         xfree(data);
534         xfree(name);
535     }
536     break;
537 
538     case XEN_EFI_set_variable:
539     {
540         CHAR16 *name;
541         long len;
542         unsigned char *data;
543 
544         len = gwstrlen(guest_handle_cast(op->u.set_variable.name, CHAR16));
545         if ( len < 0 )
546             return len;
547         name = xmalloc_array(CHAR16, ++len);
548         if ( !name )
549            return -ENOMEM;
550         if ( __copy_from_guest(name, op->u.set_variable.name, len) ||
551              wmemchr(name, 0, len) != name + len - 1 )
552         {
553             xfree(name);
554             return -EIO;
555         }
556 
557         data = xmalloc_bytes(op->u.set_variable.size);
558         if ( !data )
559             rc = -ENOMEM;
560         else if ( copy_from_guest(data, op->u.set_variable.data,
561                                   op->u.set_variable.size) )
562             rc = -EFAULT;
563         else
564         {
565             state = efi_rs_enter();
566             if ( state.cr3 )
567                 status = efi_rs->SetVariable(
568                     name, cast_guid(&op->u.set_variable.vendor_guid),
569                     op->misc, op->u.set_variable.size, data);
570             else
571                 rc = -EOPNOTSUPP;
572             efi_rs_leave(&state);
573         }
574 
575         xfree(data);
576         xfree(name);
577     }
578     break;
579 
580     case XEN_EFI_get_next_variable_name:
581     {
582         union {
583             CHAR16 *str;
584             unsigned char *raw;
585         } name;
586         UINTN size;
587 
588         if ( op->misc )
589             return -EINVAL;
590 
591         size = op->u.get_next_variable_name.size;
592         name.raw = xzalloc_bytes(size);
593         if ( !name.raw )
594             return -ENOMEM;
595         if ( copy_from_guest(name.raw, op->u.get_next_variable_name.name,
596                              size) )
597         {
598             xfree(name.raw);
599             return -EFAULT;
600         }
601 
602         state = efi_rs_enter();
603         if ( state.cr3 )
604         {
605             status = efi_rs->GetNextVariableName(
606                 &size, name.str,
607                 cast_guid(&op->u.get_next_variable_name.vendor_guid));
608             efi_rs_leave(&state);
609 
610             /*
611              * Copy the variable name if necessary. The caller provided size
612              * is used because some firmwares update size when they shouldn't.
613              * */
614             if ( !EFI_ERROR(status) &&
615                  __copy_to_guest(op->u.get_next_variable_name.name,
616                                  name.raw, op->u.get_next_variable_name.size) )
617                 rc = -EFAULT;
618             op->u.get_next_variable_name.size = size;
619         }
620         else
621             rc = -EOPNOTSUPP;
622 
623         xfree(name.raw);
624     }
625     break;
626 
627     case XEN_EFI_query_variable_info:
628     {
629         /*
630          * Put OUT variables on the stack to make them 8 byte aligned when
631          * called from the compat handler, as their placement in
632          * compat_pf_efi_runtime_call will make them 4 byte aligned instead
633          * and compilers may validly complain.  This is done regardless of
634          * whether called from the compat handler or not, as it's not worth
635          * the extra logic to differentiate.
636          */
637         uint64_t max_store_size = 0, remain_store_size = 0, max_size = 0;
638 
639         if ( op->misc & ~XEN_EFI_VARINFO_BOOT_SNAPSHOT )
640             return -EINVAL;
641 
642         if ( op->misc & XEN_EFI_VARINFO_BOOT_SNAPSHOT )
643         {
644             if ( (op->u.query_variable_info.attr
645                   & ~EFI_VARIABLE_APPEND_WRITE) !=
646                  (EFI_VARIABLE_NON_VOLATILE |
647                   EFI_VARIABLE_BOOTSERVICE_ACCESS |
648                   EFI_VARIABLE_RUNTIME_ACCESS) )
649                 return -EINVAL;
650 
651             op->u.query_variable_info.max_store_size =
652                 efi_boot_max_var_store_size;
653             op->u.query_variable_info.remain_store_size =
654                 efi_boot_remain_var_store_size;
655             if ( efi_boot_max_var_store_size )
656             {
657                 op->u.query_variable_info.max_size = efi_boot_max_var_size;
658                 status = EFI_SUCCESS;
659             }
660             else
661             {
662                 op->u.query_variable_info.max_size = 0;
663                 status = efi_boot_max_var_size;
664             }
665             break;
666         }
667 
668         if ( !efi_enabled(EFI_RS) || (efi_rs->Hdr.Revision >> 16) < 2 )
669             return -EOPNOTSUPP;
670 
671         state = efi_rs_enter();
672         if ( !state.cr3 )
673             return -EOPNOTSUPP;
674         status = efi_rs->QueryVariableInfo(
675             op->u.query_variable_info.attr, &max_store_size, &remain_store_size,
676             &max_size);
677         efi_rs_leave(&state);
678 
679         op->u.query_variable_info.max_store_size = max_store_size;
680         op->u.query_variable_info.remain_store_size = remain_store_size;
681         op->u.query_variable_info.max_size = max_size;
682 
683         break;
684     }
685 
686     case XEN_EFI_query_capsule_capabilities:
687     case XEN_EFI_update_capsule:
688         if ( op->misc )
689             return -EINVAL;
690 
691         if ( !efi_enabled(EFI_RS) || (efi_rs->Hdr.Revision >> 16) < 2 )
692             return -EOPNOTSUPP;
693         /* XXX fall through for now */
694     default:
695         return -ENOSYS;
696     }
697 
698 #ifndef COMPAT
699     op->status = status;
700 #else
701     op->status = (status & 0x3fffffffU) | ((status >> 32) & 0xc0000000U);
702 #endif
703 
704     return rc;
705 }
706 #endif
707