xref: /openbmc/linux/arch/ia64/kernel/esi.c (revision 6c33a6f4)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Extensible SAL Interface (ESI) support routines.
4  *
5  * Copyright (C) 2006 Hewlett-Packard Co
6  * 	Alex Williamson <alex.williamson@hp.com>
7  */
8 #include <linux/kernel.h>
9 #include <linux/init.h>
10 #include <linux/module.h>
11 #include <linux/string.h>
12 
13 #include <asm/esi.h>
14 #include <asm/sal.h>
15 
16 MODULE_AUTHOR("Alex Williamson <alex.williamson@hp.com>");
17 MODULE_DESCRIPTION("Extensible SAL Interface (ESI) support");
18 MODULE_LICENSE("GPL");
19 
20 #define MODULE_NAME	"esi"
21 
22 #define ESI_TABLE_GUID					\
23     EFI_GUID(0x43EA58DC, 0xCF28, 0x4b06, 0xB3,		\
24 	     0x91, 0xB7, 0x50, 0x59, 0x34, 0x2B, 0xD4)
25 
26 enum esi_systab_entry_type {
27 	ESI_DESC_ENTRY_POINT = 0
28 };
29 
30 /*
31  * Entry type:	Size:
32  *	0	48
33  */
34 #define ESI_DESC_SIZE(type)	"\060"[(unsigned) (type)]
35 
36 typedef struct ia64_esi_desc_entry_point {
37 	u8 type;
38 	u8 reserved1[15];
39 	u64 esi_proc;
40 	u64 gp;
41 	efi_guid_t guid;
42 } ia64_esi_desc_entry_point_t;
43 
44 struct pdesc {
45 	void *addr;
46 	void *gp;
47 };
48 
49 static struct ia64_sal_systab *esi_systab;
50 
51 static int __init esi_init (void)
52 {
53 	efi_config_table_t *config_tables;
54 	struct ia64_sal_systab *systab;
55 	unsigned long esi = 0;
56 	char *p;
57 	int i;
58 
59 	config_tables = __va(efi.systab->tables);
60 
61 	for (i = 0; i < (int) efi.systab->nr_tables; ++i) {
62 		if (efi_guidcmp(config_tables[i].guid, ESI_TABLE_GUID) == 0) {
63 			esi = config_tables[i].table;
64 			break;
65 		}
66 	}
67 
68 	if (!esi)
69 		return -ENODEV;
70 
71 	systab = __va(esi);
72 
73 	if (strncmp(systab->signature, "ESIT", 4) != 0) {
74 		printk(KERN_ERR "bad signature in ESI system table!");
75 		return -ENODEV;
76 	}
77 
78 	p = (char *) (systab + 1);
79 	for (i = 0; i < systab->entry_count; i++) {
80 		/*
81 		 * The first byte of each entry type contains the type
82 		 * descriptor.
83 		 */
84 		switch (*p) {
85 		      case ESI_DESC_ENTRY_POINT:
86 			break;
87 		      default:
88 			printk(KERN_WARNING "Unknown table type %d found in "
89 			       "ESI table, ignoring rest of table\n", *p);
90 			return -ENODEV;
91 		}
92 
93 		p += ESI_DESC_SIZE(*p);
94 	}
95 
96 	esi_systab = systab;
97 	return 0;
98 }
99 
100 
101 int ia64_esi_call (efi_guid_t guid, struct ia64_sal_retval *isrvp,
102 		   enum esi_proc_type proc_type, u64 func,
103 		   u64 arg1, u64 arg2, u64 arg3, u64 arg4, u64 arg5, u64 arg6,
104 		   u64 arg7)
105 {
106 	struct ia64_fpreg fr[6];
107 	unsigned long flags = 0;
108 	int i;
109 	char *p;
110 
111 	if (!esi_systab)
112 		return -1;
113 
114 	p = (char *) (esi_systab + 1);
115 	for (i = 0; i < esi_systab->entry_count; i++) {
116 		if (*p == ESI_DESC_ENTRY_POINT) {
117 			ia64_esi_desc_entry_point_t *esi = (void *)p;
118 			if (!efi_guidcmp(guid, esi->guid)) {
119 				ia64_sal_handler esi_proc;
120 				struct pdesc pdesc;
121 
122 				pdesc.addr = __va(esi->esi_proc);
123 				pdesc.gp = __va(esi->gp);
124 
125 				esi_proc = (ia64_sal_handler) &pdesc;
126 
127 				ia64_save_scratch_fpregs(fr);
128 				if (proc_type == ESI_PROC_SERIALIZED)
129 					spin_lock_irqsave(&sal_lock, flags);
130 				else if (proc_type == ESI_PROC_MP_SAFE)
131 					local_irq_save(flags);
132 				else
133 					preempt_disable();
134 				*isrvp = (*esi_proc)(func, arg1, arg2, arg3,
135 						     arg4, arg5, arg6, arg7);
136 				if (proc_type == ESI_PROC_SERIALIZED)
137 					spin_unlock_irqrestore(&sal_lock,
138 							       flags);
139 				else if (proc_type == ESI_PROC_MP_SAFE)
140 					local_irq_restore(flags);
141 				else
142 					preempt_enable();
143 				ia64_load_scratch_fpregs(fr);
144 				return 0;
145 			}
146 		}
147 		p += ESI_DESC_SIZE(*p);
148 	}
149 	return -1;
150 }
151 EXPORT_SYMBOL_GPL(ia64_esi_call);
152 
153 int ia64_esi_call_phys (efi_guid_t guid, struct ia64_sal_retval *isrvp,
154 			u64 func, u64 arg1, u64 arg2, u64 arg3, u64 arg4,
155 			u64 arg5, u64 arg6, u64 arg7)
156 {
157 	struct ia64_fpreg fr[6];
158 	unsigned long flags;
159 	u64 esi_params[8];
160 	char *p;
161 	int i;
162 
163 	if (!esi_systab)
164 		return -1;
165 
166 	p = (char *) (esi_systab + 1);
167 	for (i = 0; i < esi_systab->entry_count; i++) {
168 		if (*p == ESI_DESC_ENTRY_POINT) {
169 			ia64_esi_desc_entry_point_t *esi = (void *)p;
170 			if (!efi_guidcmp(guid, esi->guid)) {
171 				ia64_sal_handler esi_proc;
172 				struct pdesc pdesc;
173 
174 				pdesc.addr = (void *)esi->esi_proc;
175 				pdesc.gp = (void *)esi->gp;
176 
177 				esi_proc = (ia64_sal_handler) &pdesc;
178 
179 				esi_params[0] = func;
180 				esi_params[1] = arg1;
181 				esi_params[2] = arg2;
182 				esi_params[3] = arg3;
183 				esi_params[4] = arg4;
184 				esi_params[5] = arg5;
185 				esi_params[6] = arg6;
186 				esi_params[7] = arg7;
187 				ia64_save_scratch_fpregs(fr);
188 				spin_lock_irqsave(&sal_lock, flags);
189 				*isrvp = esi_call_phys(esi_proc, esi_params);
190 				spin_unlock_irqrestore(&sal_lock, flags);
191 				ia64_load_scratch_fpregs(fr);
192 				return 0;
193 			}
194 		}
195 		p += ESI_DESC_SIZE(*p);
196 	}
197 	return -1;
198 }
199 EXPORT_SYMBOL_GPL(ia64_esi_call_phys);
200 
201 static void __exit esi_exit (void)
202 {
203 }
204 
205 module_init(esi_init);
206 module_exit(esi_exit);	/* makes module removable... */
207