rtas.c revision 222613
1/*-
2 * Copyright (c) 2011 Nathan Whitehorn
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/powerpc/ofw/rtas.c 222613 2011-06-02 14:12:37Z nwhitehorn $");
29
30#include <sys/param.h>
31#include <sys/kernel.h>
32#include <sys/lock.h>
33#include <sys/mutex.h>
34#include <sys/systm.h>
35
36#include <vm/vm.h>
37#include <vm/vm_page.h>
38#include <vm/pmap.h>
39
40#include <machine/bus.h>
41#include <machine/md_var.h>
42#include <machine/pmap.h>
43#include <machine/rtas.h>
44#include <machine/stdarg.h>
45
46#include <dev/ofw/openfirm.h>
47
48MALLOC_DEFINE(M_RTAS, "rtas", "Run Time Abstraction Service");
49
50static vm_offset_t	rtas_bounce_phys;
51static caddr_t		rtas_bounce_virt;
52static off_t		rtas_bounce_offset;
53static size_t		rtas_bounce_size;
54static uintptr_t	rtas_private_data;
55static struct mtx	rtas_mtx;
56static phandle_t	rtas;
57
58/* From ofwcall.S */
59int rtascall(vm_offset_t callbuffer, uintptr_t rtas_privdat);
60extern uintptr_t	rtas_entry;
61extern register_t	rtasmsr;
62
63/*
64 * After the VM is up, allocate RTAS memory and instantiate it
65 */
66
67static void rtas_setup(void *);
68
69SYSINIT(rtas_setup, SI_SUB_KMEM, SI_ORDER_ANY, rtas_setup, NULL);
70
71static void
72rtas_setup(void *junk)
73{
74	ihandle_t rtasi;
75	cell_t rtas_size = 0, rtas_ptr;
76	char path[31];
77	int result;
78
79	rtas = OF_finddevice("/rtas");
80	if (rtas == -1) {
81		rtas = 0;
82		return;
83	}
84	OF_package_to_path(rtas, path, sizeof(path));
85	rtasi = OF_open(path);
86	if (rtasi == 0) {
87		rtas = 0;
88		printf("Error initializing RTAS: could not open node\n");
89		return;
90	}
91
92	mtx_init(&rtas_mtx, "RTAS", MTX_DEF, 0);
93
94	/* RTAS must be called with everything turned off in MSR */
95	rtasmsr = mfmsr();
96	rtasmsr &= ~(PSL_IR | PSL_DR | PSL_EE | PSL_SE);
97	#ifdef __powerpc64__
98	rtasmsr &= ~PSL_SF;
99	#endif
100
101	/*
102	 * Allocate rtas_size + one page of contiguous, wired physical memory
103	 * that can fit into a 32-bit address space and accessed from real mode.
104	 * This is used both to bounce arguments and for RTAS private data.
105	 *
106	 * It must be 4KB-aligned and not cross a 256 MB boundary.
107	 */
108
109	OF_getprop(rtas, "rtas-size", &rtas_size, sizeof(rtas_size));
110	rtas_size = round_page(rtas_size);
111	rtas_bounce_virt = contigmalloc(rtas_size + PAGE_SIZE, M_RTAS, 0, 0,
112	    ulmin(platform_real_maxaddr(), BUS_SPACE_MAXADDR_32BIT),
113	    4096, 256*1024*1024);
114
115	rtas_private_data = vtophys(rtas_bounce_virt);
116	rtas_bounce_virt += rtas_size;	/* Actual bounce area */
117	rtas_bounce_phys = vtophys(rtas_bounce_virt);
118	rtas_bounce_size = PAGE_SIZE;
119
120	/*
121	 * Instantiate RTAS. We always use the 32-bit version.
122	 */
123
124	result = OF_call_method("instantiate-rtas", rtasi, 1, 1,
125	    (cell_t)rtas_private_data, &rtas_ptr);
126	OF_close(rtasi);
127
128	if (result != 0) {
129		rtas = 0;
130		rtas_ptr = 0;
131		printf("Error initializing RTAS (%d)\n", result);
132		return;
133	}
134
135	rtas_entry = (uintptr_t)(rtas_ptr);
136}
137
138static cell_t
139rtas_real_map(const void *buf, size_t len)
140{
141	cell_t phys;
142
143	mtx_assert(&rtas_mtx, MA_OWNED);
144
145	/*
146	 * Make sure the bounce page offset satisfies any reasonable
147	 * alignment constraint.
148	 */
149	rtas_bounce_offset += sizeof(register_t) -
150	    (rtas_bounce_offset % sizeof(register_t));
151
152	if (rtas_bounce_offset + len > rtas_bounce_size) {
153		panic("Oversize RTAS call!");
154		return 0;
155	}
156
157	if (buf != NULL)
158		memcpy(rtas_bounce_virt + rtas_bounce_offset, buf, len);
159	else
160		return (0);
161
162	phys = rtas_bounce_phys + rtas_bounce_offset;
163	rtas_bounce_offset += len;
164
165	return (phys);
166}
167
168static void
169rtas_real_unmap(cell_t physaddr, void *buf, size_t len)
170{
171	mtx_assert(&rtas_mtx, MA_OWNED);
172
173	if (physaddr == 0)
174		return;
175
176	memcpy(buf, rtas_bounce_virt + (physaddr - rtas_bounce_phys), len);
177}
178
179/* Check if we have RTAS */
180int
181rtas_exists(void)
182{
183	return (rtas != 0);
184}
185
186/* Call an RTAS method by token */
187int
188rtas_call_method(cell_t token, int nargs, int nreturns, ...)
189{
190	vm_offset_t argsptr;
191	va_list ap;
192	struct {
193		cell_t token;
194		cell_t nargs;
195		cell_t nreturns;
196		cell_t args_n_results[12];
197	} args;
198	int n, result;
199
200	if (!rtas_exists() || nargs + nreturns > 12)
201		return (-1);
202
203	args.token = token;
204	va_start(ap, nreturns);
205
206	mtx_lock(&rtas_mtx);
207	rtas_bounce_offset = 0;
208
209	args.nargs = nargs;
210	args.nreturns = nreturns;
211
212	for (n = 0; n < nargs; n++)
213		args.args_n_results[n] = va_arg(ap, cell_t);
214
215	argsptr = rtas_real_map(&args, sizeof(args));
216	result = rtascall(argsptr, rtas_private_data);
217	rtas_real_unmap(argsptr, &args, sizeof(args));
218	mtx_unlock(&rtas_mtx);
219
220	if (result < 0)
221		return (result);
222
223	for (n = nargs; n < nargs + nreturns; n++)
224		*va_arg(ap, cell_t *) = args.args_n_results[n];
225	return (result);
226}
227
228/* Look up an RTAS token */
229cell_t
230rtas_token_lookup(const char *method)
231{
232	cell_t token;
233
234	if (!rtas_exists())
235		return (-1);
236
237	if (OF_getprop(rtas, method, &token, sizeof(token)) == -1)
238		return (-1);
239
240	return (token);
241}
242
243
244