Pull esi-support into release branch
authorTony Luck <tony.luck@intel.com>
Tue, 26 Sep 2006 16:47:30 +0000 (09:47 -0700)
committerTony Luck <tony.luck@intel.com>
Tue, 26 Sep 2006 16:47:30 +0000 (09:47 -0700)
arch/ia64/Kconfig
arch/ia64/kernel/Makefile
arch/ia64/kernel/esi.c [new file with mode: 0644]
arch/ia64/kernel/esi_stub.S [new file with mode: 0644]
arch/ia64/kernel/ia64_ksyms.c
include/asm-ia64/esi.h [new file with mode: 0644]

index db274da7dba1e4c31280d5678a1a25a40080f643..9ed69e79dd1d57980de0d15c6fdfa49fb38b2131 100644 (file)
@@ -429,6 +429,14 @@ config IA64_PALINFO
 config SGI_SN
        def_bool y if (IA64_SGI_SN2 || IA64_GENERIC)
 
+config IA64_ESI
+       bool "ESI (Extensible SAL Interface) support"
+       help
+         If you say Y here, support is built into the kernel to
+         make ESI calls.  ESI calls are used to support vendor-specific
+         firmware extensions, such as the ability to inject memory-errors
+         for test-purposes.  If you're unsure, say N.
+
 source "drivers/sn/Kconfig"
 
 source "drivers/firmware/Kconfig"
index ad8215a3c586948f3cf92c2e6c80f31a5ff36aa5..31497496eb4bf2d2353b99970f84688ec8959a73 100644 (file)
@@ -32,6 +32,11 @@ obj-$(CONFIG_IA64_UNCACHED_ALLOCATOR)        += uncached.o
 obj-$(CONFIG_AUDIT)            += audit.o
 mca_recovery-y                 += mca_drv.o mca_drv_asm.o
 
+obj-$(CONFIG_IA64_ESI)         += esi.o
+ifneq ($(CONFIG_IA64_ESI),)
+obj-y                          += esi_stub.o   # must be in kernel proper
+endif
+
 # The gate DSO image is built using a special linker script.
 targets += gate.so gate-syms.o
 
diff --git a/arch/ia64/kernel/esi.c b/arch/ia64/kernel/esi.c
new file mode 100644 (file)
index 0000000..ebf4e98
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Extensible SAL Interface (ESI) support routines.
+ *
+ * Copyright (C) 2006 Hewlett-Packard Co
+ *     Alex Williamson <alex.williamson@hp.com>
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#include <asm/esi.h>
+#include <asm/sal.h>
+
+MODULE_AUTHOR("Alex Williamson <alex.williamson@hp.com>");
+MODULE_DESCRIPTION("Extensible SAL Interface (ESI) support");
+MODULE_LICENSE("GPL");
+
+#define MODULE_NAME    "esi"
+
+#define ESI_TABLE_GUID                                 \
+    EFI_GUID(0x43EA58DC, 0xCF28, 0x4b06, 0xB3,         \
+            0x91, 0xB7, 0x50, 0x59, 0x34, 0x2B, 0xD4)
+
+enum esi_systab_entry_type {
+       ESI_DESC_ENTRY_POINT = 0
+};
+
+/*
+ * Entry type: Size:
+ *     0       48
+ */
+#define ESI_DESC_SIZE(type)    "\060"[(unsigned) (type)]
+
+typedef struct ia64_esi_desc_entry_point {
+       u8 type;
+       u8 reserved1[15];
+       u64 esi_proc;
+       u64 gp;
+       efi_guid_t guid;
+} ia64_esi_desc_entry_point_t;
+
+struct pdesc {
+       void *addr;
+       void *gp;
+};
+
+static struct ia64_sal_systab *esi_systab;
+
+static int __init esi_init (void)
+{
+       efi_config_table_t *config_tables;
+       struct ia64_sal_systab *systab;
+       unsigned long esi = 0;
+       char *p;
+       int i;
+
+       config_tables = __va(efi.systab->tables);
+
+       for (i = 0; i < (int) efi.systab->nr_tables; ++i) {
+               if (efi_guidcmp(config_tables[i].guid, ESI_TABLE_GUID) == 0) {
+                       esi = config_tables[i].table;
+                       break;
+               }
+       }
+
+       if (!esi)
+               return -ENODEV;;
+
+       systab = __va(esi);
+
+       if (strncmp(systab->signature, "ESIT", 4) != 0) {
+               printk(KERN_ERR "bad signature in ESI system table!");
+               return -ENODEV;
+       }
+
+       p = (char *) (systab + 1);
+       for (i = 0; i < systab->entry_count; i++) {
+               /*
+                * The first byte of each entry type contains the type
+                * descriptor.
+                */
+               switch (*p) {
+                     case ESI_DESC_ENTRY_POINT:
+                       break;
+                     default:
+                       printk(KERN_WARNING "Unkown table type %d found in "
+                              "ESI table, ignoring rest of table\n", *p);
+                       return -ENODEV;
+               }
+
+               p += ESI_DESC_SIZE(*p);
+       }
+
+       esi_systab = systab;
+       return 0;
+}
+
+
+int ia64_esi_call (efi_guid_t guid, struct ia64_sal_retval *isrvp,
+                  enum esi_proc_type proc_type, u64 func,
+                  u64 arg1, u64 arg2, u64 arg3, u64 arg4, u64 arg5, u64 arg6,
+                  u64 arg7)
+{
+       struct ia64_fpreg fr[6];
+       unsigned long flags = 0;
+       int i;
+       char *p;
+
+       if (!esi_systab)
+               return -1;
+
+       p = (char *) (esi_systab + 1);
+       for (i = 0; i < esi_systab->entry_count; i++) {
+               if (*p == ESI_DESC_ENTRY_POINT) {
+                       ia64_esi_desc_entry_point_t *esi = (void *)p;
+                       if (!efi_guidcmp(guid, esi->guid)) {
+                               ia64_sal_handler esi_proc;
+                               struct pdesc pdesc;
+
+                               pdesc.addr = __va(esi->esi_proc);
+                               pdesc.gp = __va(esi->gp);
+
+                               esi_proc = (ia64_sal_handler) &pdesc;
+
+                               ia64_save_scratch_fpregs(fr);
+                               if (proc_type == ESI_PROC_SERIALIZED)
+                                       spin_lock_irqsave(&sal_lock, flags);
+                               else if (proc_type == ESI_PROC_MP_SAFE)
+                                       local_irq_save(flags);
+                               else
+                                       preempt_disable();
+                               *isrvp = (*esi_proc)(func, arg1, arg2, arg3,
+                                                    arg4, arg5, arg6, arg7);
+                               if (proc_type == ESI_PROC_SERIALIZED)
+                                       spin_unlock_irqrestore(&sal_lock,
+                                                              flags);
+                               else if (proc_type == ESI_PROC_MP_SAFE)
+                                       local_irq_restore(flags);
+                               else
+                                       preempt_enable();
+                               ia64_load_scratch_fpregs(fr);
+                               return 0;
+                       }
+               }
+               p += ESI_DESC_SIZE(*p);
+       }
+       return -1;
+}
+EXPORT_SYMBOL_GPL(ia64_esi_call);
+
+int ia64_esi_call_phys (efi_guid_t guid, struct ia64_sal_retval *isrvp,
+                       u64 func, u64 arg1, u64 arg2, u64 arg3, u64 arg4,
+                       u64 arg5, u64 arg6, u64 arg7)
+{
+       struct ia64_fpreg fr[6];
+       unsigned long flags;
+       u64 esi_params[8];
+       char *p;
+       int i;
+
+       if (!esi_systab)
+               return -1;
+
+       p = (char *) (esi_systab + 1);
+       for (i = 0; i < esi_systab->entry_count; i++) {
+               if (*p == ESI_DESC_ENTRY_POINT) {
+                       ia64_esi_desc_entry_point_t *esi = (void *)p;
+                       if (!efi_guidcmp(guid, esi->guid)) {
+                               ia64_sal_handler esi_proc;
+                               struct pdesc pdesc;
+
+                               pdesc.addr = (void *)esi->esi_proc;
+                               pdesc.gp = (void *)esi->gp;
+
+                               esi_proc = (ia64_sal_handler) &pdesc;
+
+                               esi_params[0] = func;
+                               esi_params[1] = arg1;
+                               esi_params[2] = arg2;
+                               esi_params[3] = arg3;
+                               esi_params[4] = arg4;
+                               esi_params[5] = arg5;
+                               esi_params[6] = arg6;
+                               esi_params[7] = arg7;
+                               ia64_save_scratch_fpregs(fr);
+                               spin_lock_irqsave(&sal_lock, flags);
+                               *isrvp = esi_call_phys(esi_proc, esi_params);
+                               spin_unlock_irqrestore(&sal_lock, flags);
+                               ia64_load_scratch_fpregs(fr);
+                               return 0;
+                       }
+               }
+               p += ESI_DESC_SIZE(*p);
+       }
+       return -1;
+}
+EXPORT_SYMBOL_GPL(ia64_esi_call_phys);
+
+static void __exit esi_exit (void)
+{
+}
+
+module_init(esi_init);
+module_exit(esi_exit); /* makes module removable... */
diff --git a/arch/ia64/kernel/esi_stub.S b/arch/ia64/kernel/esi_stub.S
new file mode 100644 (file)
index 0000000..6b3d6c1
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * ESI call stub.
+ *
+ * Copyright (C) 2005 Hewlett-Packard Co
+ *     Alex Williamson <alex.williamson@hp.com>
+ *
+ * Based on EFI call stub by David Mosberger.  The stub is virtually
+ * identical to the one for EFI phys-mode calls, except that ESI
+ * calls may have up to 8 arguments, so they get passed to this routine
+ * through memory.
+ *
+ * This stub allows us to make ESI calls in physical mode with interrupts
+ * turned off.  ESI calls may not support calling from virtual mode.
+ *
+ * Google for "Extensible SAL specification" for a document describing the
+ * ESI standard.
+ */
+
+/*
+ * PSR settings as per SAL spec (Chapter 8 in the "IA-64 System
+ * Abstraction Layer Specification", revision 2.6e).  Note that
+ * psr.dfl and psr.dfh MUST be cleared, despite what this manual says.
+ * Otherwise, SAL dies whenever it's trying to do an IA-32 BIOS call
+ * (the br.ia instruction fails unless psr.dfl and psr.dfh are
+ * cleared).  Fortunately, SAL promises not to touch the floating
+ * point regs, so at least we don't have to save f2-f127.
+ */
+#define PSR_BITS_TO_CLEAR                                              \
+       (IA64_PSR_I | IA64_PSR_IT | IA64_PSR_DT | IA64_PSR_RT |         \
+        IA64_PSR_DD | IA64_PSR_SS | IA64_PSR_RI | IA64_PSR_ED |        \
+        IA64_PSR_DFL | IA64_PSR_DFH)
+
+#define PSR_BITS_TO_SET                                                        \
+       (IA64_PSR_BN)
+
+#include <asm/processor.h>
+#include <asm/asmmacro.h>
+
+/*
+ * Inputs:
+ *     in0 = address of function descriptor of ESI routine to call
+ *     in1 = address of array of ESI parameters
+ *
+ * Outputs:
+ *     r8 = result returned by called function
+ */
+GLOBAL_ENTRY(esi_call_phys)
+       .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(2)
+       alloc loc1=ar.pfs,2,7,8,0
+       ld8 r2=[in0],8                  // load ESI function's entry point
+       mov loc0=rp
+       .body
+       ;;
+       ld8 out0=[in1],8                // ESI params loaded from array
+       ;;                              // passing all as inputs doesn't work
+       ld8 out1=[in1],8
+       ;;
+       ld8 out2=[in1],8
+       ;;
+       ld8 out3=[in1],8
+       ;;
+       ld8 out4=[in1],8
+       ;;
+       ld8 out5=[in1],8
+       ;;
+       ld8 out6=[in1],8
+       ;;
+       ld8 out7=[in1]
+       mov loc2=gp                     // save global pointer
+       mov loc4=ar.rsc                 // save RSE configuration
+       mov ar.rsc=0                    // put RSE in enforced lazy, LE mode
+       ;;
+       ld8 gp=[in0]                    // load ESI function's global pointer
+       movl r16=PSR_BITS_TO_CLEAR
+       mov loc3=psr                    // save processor status word
+       movl r17=PSR_BITS_TO_SET
+       ;;
+       or loc3=loc3,r17
+       mov b6=r2
+       ;;
+       andcm r16=loc3,r16      // get psr with IT, DT, and RT bits cleared
+       br.call.sptk.many rp=ia64_switch_mode_phys
+.ret0: mov loc5=r19                    // old ar.bsp
+       mov loc6=r20                    // old sp
+       br.call.sptk.many rp=b6         // call the ESI function
+.ret1: mov ar.rsc=0                    // put RSE in enforced lazy, LE mode
+       mov r16=loc3                    // save virtual mode psr
+       mov r19=loc5                    // save virtual mode bspstore
+       mov r20=loc6                    // save virtual mode sp
+       br.call.sptk.many rp=ia64_switch_mode_virt // return to virtual mode
+.ret2: mov ar.rsc=loc4                 // restore RSE configuration
+       mov ar.pfs=loc1
+       mov rp=loc0
+       mov gp=loc2
+       br.ret.sptk.many rp
+END(esi_call_phys)
index 3ead20fb6f4b2f45d7ec6f6bee19f82a3d4364af..879c1817bd1c8589c1ff7c85a659e777b67abc50 100644 (file)
@@ -105,5 +105,9 @@ EXPORT_SYMBOL(ia64_spinlock_contention);
 # endif
 #endif
 
+#if defined(CONFIG_IA64_ESI) || defined(CONFIG_IA64_ESI_MODULE)
+extern void esi_call_phys (void);
+EXPORT_SYMBOL_GPL(esi_call_phys);
+#endif
 extern char ia64_ivt[];
 EXPORT_SYMBOL(ia64_ivt);
diff --git a/include/asm-ia64/esi.h b/include/asm-ia64/esi.h
new file mode 100644 (file)
index 0000000..84aac0e
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * ESI service calls.
+ *
+ * Copyright (c) Copyright 2005-2006 Hewlett-Packard Development Company, L.P.
+ *     Alex Williamson <alex.williamson@hp.com>
+ */
+#ifndef esi_h
+#define esi_h
+
+#include <linux/efi.h>
+
+#define ESI_QUERY                      0x00000001
+#define ESI_OPEN_HANDLE                        0x02000000
+#define ESI_CLOSE_HANDLE               0x02000001
+
+enum esi_proc_type {
+       ESI_PROC_SERIALIZED,    /* calls need to be serialized */
+       ESI_PROC_MP_SAFE,       /* MP-safe, but not reentrant */
+       ESI_PROC_REENTRANT      /* MP-safe and reentrant */
+};
+
+extern int ia64_esi_init (void);
+extern struct ia64_sal_retval esi_call_phys (void *, u64 *);
+extern int ia64_esi_call(efi_guid_t, struct ia64_sal_retval *,
+                        enum esi_proc_type,
+                        u64, u64, u64, u64, u64, u64, u64, u64);
+extern int ia64_esi_call_phys(efi_guid_t, struct ia64_sal_retval *, u64, u64,
+                              u64, u64, u64, u64, u64, u64);
+
+#endif /* esi_h */