1 /* $Id: p1275.c,v 1.22 2001/10/18 09:40:00 davem Exp $
2  * p1275.c: Sun IEEE 1275 PROM low level interface routines
3  *
4  * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
5  */
6 
7 #include <linux/kernel.h>
8 #include <linux/init.h>
9 #include <linux/sched.h>
10 #include <linux/smp.h>
11 #include <linux/string.h>
12 #include <linux/spinlock.h>
13 
14 #include <asm/openprom.h>
15 #include <asm/oplib.h>
16 #include <asm/system.h>
17 #include <asm/spitfire.h>
18 #include <asm/pstate.h>
19 
20 struct {
21 	long prom_callback;			/* 0x00 */
22 	void (*prom_cif_handler)(long *);	/* 0x08 */
23 	unsigned long prom_cif_stack;		/* 0x10 */
24 	unsigned long prom_args [23];		/* 0x18 */
25 	char prom_buffer [3000];
26 } p1275buf;
27 
28 extern void prom_world(int);
29 
prom_cif_interface(void)30 void prom_cif_interface (void)
31 {
32 	__asm__ __volatile__ (
33 "	mov	%0, %%o0\n"
34 "	ldx	[%%o0 + 0x010], %%o1	! prom_cif_stack\n"
35 "	save	%%o1, -0x190, %%sp\n"
36 "	ldx	[%%i0 + 0x008], %%l2	! prom_cif_handler\n"
37 "	rdpr	%%pstate, %%l4\n"
38 "	wrpr	%%g0, 0x15, %%pstate	! save alternate globals\n"
39 "	stx	%%g1, [%%sp + 2047 + 0x0b0]\n"
40 "	stx	%%g2, [%%sp + 2047 + 0x0b8]\n"
41 "	stx	%%g3, [%%sp + 2047 + 0x0c0]\n"
42 "	stx	%%g4, [%%sp + 2047 + 0x0c8]\n"
43 "	stx	%%g5, [%%sp + 2047 + 0x0d0]\n"
44 "	stx	%%g6, [%%sp + 2047 + 0x0d8]\n"
45 "	stx	%%g7, [%%sp + 2047 + 0x0e0]\n"
46 "	wrpr	%%g0, 0x814, %%pstate	! save interrupt globals\n"
47 "	stx	%%g1, [%%sp + 2047 + 0x0e8]\n"
48 "	stx	%%g2, [%%sp + 2047 + 0x0f0]\n"
49 "	stx	%%g3, [%%sp + 2047 + 0x0f8]\n"
50 "	stx	%%g4, [%%sp + 2047 + 0x100]\n"
51 "	stx	%%g5, [%%sp + 2047 + 0x108]\n"
52 "	stx	%%g6, [%%sp + 2047 + 0x110]\n"
53 "	stx	%%g7, [%%sp + 2047 + 0x118]\n"
54 "	wrpr	%%g0, 0x14, %%pstate	! save normal globals\n"
55 "	stx	%%g1, [%%sp + 2047 + 0x120]\n"
56 "	stx	%%g2, [%%sp + 2047 + 0x128]\n"
57 "	stx	%%g3, [%%sp + 2047 + 0x130]\n"
58 "	stx	%%g4, [%%sp + 2047 + 0x138]\n"
59 "	stx	%%g5, [%%sp + 2047 + 0x140]\n"
60 "	stx	%%g6, [%%sp + 2047 + 0x148]\n"
61 "	stx	%%g7, [%%sp + 2047 + 0x150]\n"
62 "	wrpr	%%g0, 0x414, %%pstate	! save mmu globals\n"
63 "	stx	%%g1, [%%sp + 2047 + 0x158]\n"
64 "	stx	%%g2, [%%sp + 2047 + 0x160]\n"
65 "	stx	%%g3, [%%sp + 2047 + 0x168]\n"
66 "	stx	%%g4, [%%sp + 2047 + 0x170]\n"
67 "	stx	%%g5, [%%sp + 2047 + 0x178]\n"
68 "	stx	%%g6, [%%sp + 2047 + 0x180]\n"
69 "	stx	%%g7, [%%sp + 2047 + 0x188]\n"
70 "	mov	%%g1, %%l0		! also save to locals, so we can handle\n"
71 "	mov	%%g2, %%l1		! tlb faults later on, when accessing\n"
72 "	mov	%%g3, %%l3		! the stack.\n"
73 "	mov	%%g7, %%l5\n"
74 "	wrpr	%%l4, %1, %%pstate	! turn off interrupts\n"
75 "	call	%%l2\n"
76 "	 add	%%i0, 0x018, %%o0	! prom_args\n"
77 "	wrpr	%%g0, 0x414, %%pstate	! restore mmu globals\n"
78 "	mov	%%l0, %%g1\n"
79 "	mov	%%l1, %%g2\n"
80 "	mov	%%l3, %%g3\n"
81 "	mov	%%l5, %%g7\n"
82 "	wrpr	%%g0, 0x14, %%pstate	! restore normal globals\n"
83 "	ldx	[%%sp + 2047 + 0x120], %%g1\n"
84 "	ldx	[%%sp + 2047 + 0x128], %%g2\n"
85 "	ldx	[%%sp + 2047 + 0x130], %%g3\n"
86 "	ldx	[%%sp + 2047 + 0x138], %%g4\n"
87 "	ldx	[%%sp + 2047 + 0x140], %%g5\n"
88 "	ldx	[%%sp + 2047 + 0x148], %%g6\n"
89 "	ldx	[%%sp + 2047 + 0x150], %%g7\n"
90 "	wrpr	%%g0, 0x814, %%pstate	! restore interrupt globals\n"
91 "	ldx	[%%sp + 2047 + 0x0e8], %%g1\n"
92 "	ldx	[%%sp + 2047 + 0x0f0], %%g2\n"
93 "	ldx	[%%sp + 2047 + 0x0f8], %%g3\n"
94 "	ldx	[%%sp + 2047 + 0x100], %%g4\n"
95 "	ldx	[%%sp + 2047 + 0x108], %%g5\n"
96 "	ldx	[%%sp + 2047 + 0x110], %%g6\n"
97 "	ldx	[%%sp + 2047 + 0x118], %%g7\n"
98 "	wrpr	%%g0, 0x15, %%pstate	! restore alternate globals\n"
99 "	ldx	[%%sp + 2047 + 0x0b0], %%g1\n"
100 "	ldx	[%%sp + 2047 + 0x0b8], %%g2\n"
101 "	ldx	[%%sp + 2047 + 0x0c0], %%g3\n"
102 "	ldx	[%%sp + 2047 + 0x0c8], %%g4\n"
103 "	ldx	[%%sp + 2047 + 0x0d0], %%g5\n"
104 "	ldx	[%%sp + 2047 + 0x0d8], %%g6\n"
105 "	ldx	[%%sp + 2047 + 0x0e0], %%g7\n"
106 "	wrpr	%%l4, 0, %%pstate	! restore original pstate\n"
107 "	ret\n"
108 "	 restore\n"
109 "	" : : "r" (&p1275buf), "i" (PSTATE_IE));
110 }
111 
prom_cif_callback(void)112 void prom_cif_callback(void)
113 {
114 	__asm__ __volatile__ (
115 "	mov	%0, %%o1\n"
116 "	save	%%sp, -0x270, %%sp\n"
117 "	rdpr	%%pstate, %%l4\n"
118 "	wrpr	%%g0, 0x15, %%pstate	! save PROM alternate globals\n"
119 "	stx	%%g1, [%%sp + 2047 + 0x0b0]\n"
120 "	stx	%%g2, [%%sp + 2047 + 0x0b8]\n"
121 "	stx	%%g3, [%%sp + 2047 + 0x0c0]\n"
122 "	stx	%%g4, [%%sp + 2047 + 0x0c8]\n"
123 "	stx	%%g5, [%%sp + 2047 + 0x0d0]\n"
124 "	stx	%%g6, [%%sp + 2047 + 0x0d8]\n"
125 "	stx	%%g7, [%%sp + 2047 + 0x0e0]\n"
126 "					! restore Linux alternate globals\n"
127 "	ldx	[%%sp + 2047 + 0x190], %%g1\n"
128 "	ldx	[%%sp + 2047 + 0x198], %%g2\n"
129 "	ldx	[%%sp + 2047 + 0x1a0], %%g3\n"
130 "	ldx	[%%sp + 2047 + 0x1a8], %%g4\n"
131 "	ldx	[%%sp + 2047 + 0x1b0], %%g5\n"
132 "	ldx	[%%sp + 2047 + 0x1b8], %%g6\n"
133 "	ldx	[%%sp + 2047 + 0x1c0], %%g7\n"
134 "	wrpr	%%g0, 0x814, %%pstate	! save PROM interrupt globals\n"
135 "	stx	%%g1, [%%sp + 2047 + 0x0e8]\n"
136 "	stx	%%g2, [%%sp + 2047 + 0x0f0]\n"
137 "	stx	%%g3, [%%sp + 2047 + 0x0f8]\n"
138 "	stx	%%g4, [%%sp + 2047 + 0x100]\n"
139 "	stx	%%g5, [%%sp + 2047 + 0x108]\n"
140 "	stx	%%g6, [%%sp + 2047 + 0x110]\n"
141 "	stx	%%g7, [%%sp + 2047 + 0x118]\n"
142 "					! restore Linux interrupt globals\n"
143 "	ldx	[%%sp + 2047 + 0x1c8], %%g1\n"
144 "	ldx	[%%sp + 2047 + 0x1d0], %%g2\n"
145 "	ldx	[%%sp + 2047 + 0x1d8], %%g3\n"
146 "	ldx	[%%sp + 2047 + 0x1e0], %%g4\n"
147 "	ldx	[%%sp + 2047 + 0x1e8], %%g5\n"
148 "	ldx	[%%sp + 2047 + 0x1f0], %%g6\n"
149 "	ldx	[%%sp + 2047 + 0x1f8], %%g7\n"
150 "	wrpr	%%g0, 0x14, %%pstate	! save PROM normal globals\n"
151 "	stx	%%g1, [%%sp + 2047 + 0x120]\n"
152 "	stx	%%g2, [%%sp + 2047 + 0x128]\n"
153 "	stx	%%g3, [%%sp + 2047 + 0x130]\n"
154 "	stx	%%g4, [%%sp + 2047 + 0x138]\n"
155 "	stx	%%g5, [%%sp + 2047 + 0x140]\n"
156 "	stx	%%g6, [%%sp + 2047 + 0x148]\n"
157 "	stx	%%g7, [%%sp + 2047 + 0x150]\n"
158 "					! restore Linux normal globals\n"
159 "	ldx	[%%sp + 2047 + 0x200], %%g1\n"
160 "	ldx	[%%sp + 2047 + 0x208], %%g2\n"
161 "	ldx	[%%sp + 2047 + 0x210], %%g3\n"
162 "	ldx	[%%sp + 2047 + 0x218], %%g4\n"
163 "	ldx	[%%sp + 2047 + 0x220], %%g5\n"
164 "	ldx	[%%sp + 2047 + 0x228], %%g6\n"
165 "	ldx	[%%sp + 2047 + 0x230], %%g7\n"
166 "	wrpr	%%g0, 0x414, %%pstate	! save PROM mmu globals\n"
167 "	stx	%%g1, [%%sp + 2047 + 0x158]\n"
168 "	stx	%%g2, [%%sp + 2047 + 0x160]\n"
169 "	stx	%%g3, [%%sp + 2047 + 0x168]\n"
170 "	stx	%%g4, [%%sp + 2047 + 0x170]\n"
171 "	stx	%%g5, [%%sp + 2047 + 0x178]\n"
172 "	stx	%%g6, [%%sp + 2047 + 0x180]\n"
173 "	stx	%%g7, [%%sp + 2047 + 0x188]\n"
174 "					! restore Linux mmu globals\n"
175 "	ldx	[%%sp + 2047 + 0x238], %%o0\n"
176 "	ldx	[%%sp + 2047 + 0x240], %%o1\n"
177 "	ldx	[%%sp + 2047 + 0x248], %%l2\n"
178 "	ldx	[%%sp + 2047 + 0x250], %%l3\n"
179 "	ldx	[%%sp + 2047 + 0x258], %%l5\n"
180 "	ldx	[%%sp + 2047 + 0x260], %%l6\n"
181 "	ldx	[%%sp + 2047 + 0x268], %%l7\n"
182 "					! switch to Linux tba\n"
183 "	sethi	%%hi(sparc64_ttable_tl0), %%l1\n"
184 "	rdpr	%%tba, %%l0		! save PROM tba\n"
185 "	mov	%%o0, %%g1\n"
186 "	mov	%%o1, %%g2\n"
187 "	mov	%%l2, %%g3\n"
188 "	mov	%%l3, %%g4\n"
189 "	mov	%%l5, %%g5\n"
190 "	mov	%%l6, %%g6\n"
191 "	mov	%%l7, %%g7\n"
192 "	wrpr	%%l1, %%tba		! install Linux tba\n"
193 "	wrpr	%%l4, 0, %%pstate	! restore PSTATE\n"
194 "	call	prom_world\n"
195 "	 mov	%%g0, %%o0\n"
196 "	ldx	[%%i1 + 0x000], %%l2\n"
197 "	call	%%l2\n"
198 "	 mov	%%i0, %%o0\n"
199 "	mov	%%o0, %%l1\n"
200 "	call	prom_world\n"
201 "	 or	%%g0, 1, %%o0\n"
202 "	wrpr	%%g0, 0x14, %%pstate	! interrupts off\n"
203 "					! restore PROM mmu globals\n"
204 "	ldx	[%%sp + 2047 + 0x158], %%o0\n"
205 "	ldx	[%%sp + 2047 + 0x160], %%o1\n"
206 "	ldx	[%%sp + 2047 + 0x168], %%l2\n"
207 "	ldx	[%%sp + 2047 + 0x170], %%l3\n"
208 "	ldx	[%%sp + 2047 + 0x178], %%l5\n"
209 "	ldx	[%%sp + 2047 + 0x180], %%l6\n"
210 "	ldx	[%%sp + 2047 + 0x188], %%l7\n"
211 "	wrpr	%%g0, 0x414, %%pstate	! restore PROM mmu globals\n"
212 "	mov	%%o0, %%g1\n"
213 "	mov	%%o1, %%g2\n"
214 "	mov	%%l2, %%g3\n"
215 "	mov	%%l3, %%g4\n"
216 "	mov	%%l5, %%g5\n"
217 "	mov	%%l6, %%g6\n"
218 "	mov	%%l7, %%g7\n"
219 "	wrpr	%%l0, %%tba		! restore PROM tba\n"
220 "	wrpr	%%g0, 0x14, %%pstate	! restore PROM normal globals\n"
221 "	ldx	[%%sp + 2047 + 0x120], %%g1\n"
222 "	ldx	[%%sp + 2047 + 0x128], %%g2\n"
223 "	ldx	[%%sp + 2047 + 0x130], %%g3\n"
224 "	ldx	[%%sp + 2047 + 0x138], %%g4\n"
225 "	ldx	[%%sp + 2047 + 0x140], %%g5\n"
226 "	ldx	[%%sp + 2047 + 0x148], %%g6\n"
227 "	ldx	[%%sp + 2047 + 0x150], %%g7\n"
228 "	wrpr	%%g0, 0x814, %%pstate	! restore PROM interrupt globals\n"
229 "	ldx	[%%sp + 2047 + 0x0e8], %%g1\n"
230 "	ldx	[%%sp + 2047 + 0x0f0], %%g2\n"
231 "	ldx	[%%sp + 2047 + 0x0f8], %%g3\n"
232 "	ldx	[%%sp + 2047 + 0x100], %%g4\n"
233 "	ldx	[%%sp + 2047 + 0x108], %%g5\n"
234 "	ldx	[%%sp + 2047 + 0x110], %%g6\n"
235 "	ldx	[%%sp + 2047 + 0x118], %%g7\n"
236 "	wrpr	%%g0, 0x15, %%pstate	! restore PROM alternate globals\n"
237 "	ldx	[%%sp + 2047 + 0x0b0], %%g1\n"
238 "	ldx	[%%sp + 2047 + 0x0b8], %%g2\n"
239 "	ldx	[%%sp + 2047 + 0x0c0], %%g3\n"
240 "	ldx	[%%sp + 2047 + 0x0c8], %%g4\n"
241 "	ldx	[%%sp + 2047 + 0x0d0], %%g5\n"
242 "	ldx	[%%sp + 2047 + 0x0d8], %%g6\n"
243 "	ldx	[%%sp + 2047 + 0x0e0], %%g7\n"
244 "	wrpr	%%l4, 0, %%pstate\n"
245 "	ret\n"
246 "	 restore %%l1, 0, %%o0\n"
247 "	" : : "r" (&p1275buf), "i" (PSTATE_PRIV));
248 }
249 
250 /*
251  * This provides SMP safety on the p1275buf. prom_callback() drops this lock
252  * to allow recursuve acquisition.
253  */
254 spinlock_t prom_entry_lock = SPIN_LOCK_UNLOCKED;
255 
p1275_cmd(char * service,long fmt,...)256 long p1275_cmd (char *service, long fmt, ...)
257 {
258 	char *p, *q;
259 	unsigned long flags;
260 	int nargs, nrets, i;
261 	va_list list;
262 	long attrs, x;
263 	long ctx = 0;
264 
265 	p = p1275buf.prom_buffer;
266 	ctx = spitfire_get_primary_context ();
267 	if (ctx) {
268 		flushw_user ();
269 		spitfire_set_primary_context (0);
270 	}
271 
272 	spin_lock_irqsave(&prom_entry_lock, flags);
273 
274 	p1275buf.prom_args[0] = (unsigned long)p;		/* service */
275 	strcpy (p, service);
276 	p = (char *)(((long)(strchr (p, 0) + 8)) & ~7);
277 	p1275buf.prom_args[1] = nargs = (fmt & 0x0f);		/* nargs */
278 	p1275buf.prom_args[2] = nrets = ((fmt & 0xf0) >> 4); 	/* nrets */
279 	attrs = fmt >> 8;
280 	va_start(list, fmt);
281 	for (i = 0; i < nargs; i++, attrs >>= 3) {
282 		switch (attrs & 0x7) {
283 		case P1275_ARG_NUMBER:
284 			p1275buf.prom_args[i + 3] =
285 						(unsigned)va_arg(list, long);
286 			break;
287 		case P1275_ARG_IN_64B:
288 			p1275buf.prom_args[i + 3] =
289 				va_arg(list, unsigned long);
290 			break;
291 		case P1275_ARG_IN_STRING:
292 			strcpy (p, va_arg(list, char *));
293 			p1275buf.prom_args[i + 3] = (unsigned long)p;
294 			p = (char *)(((long)(strchr (p, 0) + 8)) & ~7);
295 			break;
296 		case P1275_ARG_OUT_BUF:
297 			(void) va_arg(list, char *);
298 			p1275buf.prom_args[i + 3] = (unsigned long)p;
299 			x = va_arg(list, long);
300 			i++; attrs >>= 3;
301 			p = (char *)(((long)(p + (int)x + 7)) & ~7);
302 			p1275buf.prom_args[i + 3] = x;
303 			break;
304 		case P1275_ARG_IN_BUF:
305 			q = va_arg(list, char *);
306 			p1275buf.prom_args[i + 3] = (unsigned long)p;
307 			x = va_arg(list, long);
308 			i++; attrs >>= 3;
309 			memcpy (p, q, (int)x);
310 			p = (char *)(((long)(p + (int)x + 7)) & ~7);
311 			p1275buf.prom_args[i + 3] = x;
312 			break;
313 		case P1275_ARG_OUT_32B:
314 			(void) va_arg(list, char *);
315 			p1275buf.prom_args[i + 3] = (unsigned long)p;
316 			p += 32;
317 			break;
318 		case P1275_ARG_IN_FUNCTION:
319 			p1275buf.prom_args[i + 3] =
320 					(unsigned long)prom_cif_callback;
321 			p1275buf.prom_callback = va_arg(list, long);
322 			break;
323 		}
324 	}
325 	va_end(list);
326 
327 	prom_world(1);
328 	prom_cif_interface();
329 	prom_world(0);
330 
331 	attrs = fmt >> 8;
332 	va_start(list, fmt);
333 	for (i = 0; i < nargs; i++, attrs >>= 3) {
334 		switch (attrs & 0x7) {
335 		case P1275_ARG_NUMBER:
336 			(void) va_arg(list, long);
337 			break;
338 		case P1275_ARG_IN_STRING:
339 			(void) va_arg(list, char *);
340 			break;
341 		case P1275_ARG_IN_FUNCTION:
342 			(void) va_arg(list, long);
343 			break;
344 		case P1275_ARG_IN_BUF:
345 			(void) va_arg(list, char *);
346 			(void) va_arg(list, long);
347 			i++; attrs >>= 3;
348 			break;
349 		case P1275_ARG_OUT_BUF:
350 			p = va_arg(list, char *);
351 			x = va_arg(list, long);
352 			memcpy (p, (char *)(p1275buf.prom_args[i + 3]), (int)x);
353 			i++; attrs >>= 3;
354 			break;
355 		case P1275_ARG_OUT_32B:
356 			p = va_arg(list, char *);
357 			memcpy (p, (char *)(p1275buf.prom_args[i + 3]), 32);
358 			break;
359 		}
360 	}
361 	va_end(list);
362 	x = p1275buf.prom_args [nargs + 3];
363 
364 	spin_unlock_irqrestore(&prom_entry_lock, flags);
365 
366 	if (ctx)
367 		spitfire_set_primary_context (ctx);
368 
369 	return x;
370 }
371 
prom_cif_init(void * cif_handler,void * cif_stack)372 void prom_cif_init(void *cif_handler, void *cif_stack)
373 {
374 	p1275buf.prom_cif_handler = (void (*)(long *))cif_handler;
375 	p1275buf.prom_cif_stack = (unsigned long)cif_stack;
376 }
377