rtas.c revision 259258
11638Srgrimes/*-
21638Srgrimes * Copyright (c) 2011 Nathan Whitehorn
31638Srgrimes * All rights reserved.
41638Srgrimes *
51638Srgrimes * Redistribution and use in source and binary forms, with or without
61638Srgrimes * modification, are permitted provided that the following conditions
71638Srgrimes * are met:
81638Srgrimes * 1. Redistributions of source code must retain the above copyright
91638Srgrimes *    notice, this list of conditions and the following disclaimer.
101638Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111638Srgrimes *    notice, this list of conditions and the following disclaimer in the
12263142Seadler *    documentation and/or other materials provided with the distribution.
131638Srgrimes *
141638Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
151638Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
161638Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
171638Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
181638Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
191638Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
201638Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
211638Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
221638Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
231638Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
241638Srgrimes * SUCH DAMAGE.
251638Srgrimes */
261638Srgrimes
271638Srgrimes#include <sys/cdefs.h>
281638Srgrimes__FBSDID("$FreeBSD: stable/10/sys/powerpc/ofw/rtas.c 259258 2013-12-12 13:00:07Z andreast $");
291638Srgrimes
301638Srgrimes#include <sys/param.h>
311638Srgrimes#include <sys/kernel.h>
321638Srgrimes#include <sys/lock.h>
331638Srgrimes#include <sys/mutex.h>
341638Srgrimes#include <sys/systm.h>
351638Srgrimes#include <sys/proc.h>
361638Srgrimes
371638Srgrimes#include <vm/vm.h>
381638Srgrimes#include <vm/vm_page.h>
391638Srgrimes#include <vm/pmap.h>
401638Srgrimes
411638Srgrimes#include <machine/bus.h>
421638Srgrimes#include <machine/md_var.h>
431638Srgrimes#include <machine/pcb.h>
441638Srgrimes#include <machine/pmap.h>
451638Srgrimes#include <machine/rtas.h>
461638Srgrimes#include <machine/stdarg.h>
471638Srgrimes
481638Srgrimes#include <dev/ofw/openfirm.h>
491638Srgrimes
501638Srgrimesstatic MALLOC_DEFINE(M_RTAS, "rtas", "Run Time Abstraction Service");
511638Srgrimes
521638Srgrimesstatic vm_offset_t	rtas_bounce_phys;
53static caddr_t		rtas_bounce_virt;
54static off_t		rtas_bounce_offset;
55static size_t		rtas_bounce_size;
56static uintptr_t	rtas_private_data;
57static struct mtx	rtas_mtx;
58static phandle_t	rtas;
59
60/* From ofwcall.S */
61int rtascall(vm_offset_t callbuffer, uintptr_t rtas_privdat);
62extern uintptr_t	rtas_entry;
63extern register_t	rtasmsr;
64
65int setfault(faultbuf);             /* defined in locore.S */
66
67/*
68 * After the VM is up, allocate RTAS memory and instantiate it
69 */
70
71static void rtas_setup(void *);
72
73SYSINIT(rtas_setup, SI_SUB_KMEM, SI_ORDER_ANY, rtas_setup, NULL);
74
75static void
76rtas_setup(void *junk)
77{
78	ihandle_t rtasi;
79	cell_t rtas_size = 0, rtas_ptr;
80	char path[31];
81	int result;
82
83	rtas = OF_finddevice("/rtas");
84	if (rtas == -1) {
85		rtas = 0;
86		return;
87	}
88	OF_package_to_path(rtas, path, sizeof(path));
89	rtasi = OF_open(path);
90	if (rtasi == 0) {
91		rtas = 0;
92		printf("Error initializing RTAS: could not open node\n");
93		return;
94	}
95
96	mtx_init(&rtas_mtx, "RTAS", NULL, MTX_SPIN);
97
98	/* RTAS must be called with everything turned off in MSR */
99	rtasmsr = mfmsr();
100	rtasmsr &= ~(PSL_IR | PSL_DR | PSL_EE | PSL_SE);
101	#ifdef __powerpc64__
102	rtasmsr &= ~PSL_SF;
103	#endif
104
105	/*
106	 * Allocate rtas_size + one page of contiguous, wired physical memory
107	 * that can fit into a 32-bit address space and accessed from real mode.
108	 * This is used both to bounce arguments and for RTAS private data.
109	 *
110	 * It must be 4KB-aligned and not cross a 256 MB boundary.
111	 */
112
113	OF_getprop(rtas, "rtas-size", &rtas_size, sizeof(rtas_size));
114	rtas_size = round_page(rtas_size);
115	rtas_bounce_virt = contigmalloc(rtas_size + PAGE_SIZE, M_RTAS, 0, 0,
116	    ulmin(platform_real_maxaddr(), BUS_SPACE_MAXADDR_32BIT),
117	    4096, 256*1024*1024);
118
119	rtas_private_data = vtophys(rtas_bounce_virt);
120	rtas_bounce_virt += rtas_size;	/* Actual bounce area */
121	rtas_bounce_phys = vtophys(rtas_bounce_virt);
122	rtas_bounce_size = PAGE_SIZE;
123
124	/*
125	 * Instantiate RTAS. We always use the 32-bit version.
126	 */
127
128	result = OF_call_method("instantiate-rtas", rtasi, 1, 1,
129	    (cell_t)rtas_private_data, &rtas_ptr);
130	OF_close(rtasi);
131
132	if (result != 0) {
133		rtas = 0;
134		rtas_ptr = 0;
135		printf("Error initializing RTAS (%d)\n", result);
136		return;
137	}
138
139	rtas_entry = (uintptr_t)(rtas_ptr);
140}
141
142static cell_t
143rtas_real_map(const void *buf, size_t len)
144{
145	cell_t phys;
146
147	mtx_assert(&rtas_mtx, MA_OWNED);
148
149	/*
150	 * Make sure the bounce page offset satisfies any reasonable
151	 * alignment constraint.
152	 */
153	rtas_bounce_offset += sizeof(register_t) -
154	    (rtas_bounce_offset % sizeof(register_t));
155
156	if (rtas_bounce_offset + len > rtas_bounce_size) {
157		panic("Oversize RTAS call!");
158		return 0;
159	}
160
161	if (buf != NULL)
162		memcpy(rtas_bounce_virt + rtas_bounce_offset, buf, len);
163	else
164		return (0);
165
166	phys = rtas_bounce_phys + rtas_bounce_offset;
167	rtas_bounce_offset += len;
168
169	return (phys);
170}
171
172static void
173rtas_real_unmap(cell_t physaddr, void *buf, size_t len)
174{
175	mtx_assert(&rtas_mtx, MA_OWNED);
176
177	if (physaddr == 0)
178		return;
179
180	memcpy(buf, rtas_bounce_virt + (physaddr - rtas_bounce_phys), len);
181}
182
183/* Check if we have RTAS */
184int
185rtas_exists(void)
186{
187	return (rtas != 0);
188}
189
190/* Call an RTAS method by token */
191int
192rtas_call_method(cell_t token, int nargs, int nreturns, ...)
193{
194	vm_offset_t argsptr;
195	faultbuf env, *oldfaultbuf;
196	va_list ap;
197	struct {
198		cell_t token;
199		cell_t nargs;
200		cell_t nreturns;
201		cell_t args_n_results[12];
202	} args;
203	int n, result;
204
205	if (!rtas_exists() || nargs + nreturns > 12)
206		return (-1);
207
208	args.token = token;
209	va_start(ap, nreturns);
210
211	mtx_lock_spin(&rtas_mtx);
212	rtas_bounce_offset = 0;
213
214	args.nargs = nargs;
215	args.nreturns = nreturns;
216
217	for (n = 0; n < nargs; n++)
218		args.args_n_results[n] = va_arg(ap, cell_t);
219
220	argsptr = rtas_real_map(&args, sizeof(args));
221
222	/* Get rid of any stale machine checks that have been waiting.  */
223	__asm __volatile ("sync; isync");
224	oldfaultbuf = curthread->td_pcb->pcb_onfault;
225        if (!setfault(env)) {
226		__asm __volatile ("sync");
227		result = rtascall(argsptr, rtas_private_data);
228		__asm __volatile ("sync; isync");
229	} else {
230		result = RTAS_HW_ERROR;
231	}
232	curthread->td_pcb->pcb_onfault = oldfaultbuf;
233	__asm __volatile ("sync");
234
235	rtas_real_unmap(argsptr, &args, sizeof(args));
236	mtx_unlock_spin(&rtas_mtx);
237
238	if (result < 0)
239		return (result);
240
241	for (n = nargs; n < nargs + nreturns; n++)
242		*va_arg(ap, cell_t *) = args.args_n_results[n];
243	return (result);
244}
245
246/* Look up an RTAS token */
247cell_t
248rtas_token_lookup(const char *method)
249{
250	cell_t token;
251
252	if (!rtas_exists())
253		return (-1);
254
255	if (OF_getprop(rtas, method, &token, sizeof(token)) == -1)
256		return (-1);
257
258	return (token);
259}
260
261
262