1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26#include <sys/promif.h>
27#include <sys/promimpl.h>
28
29/*
30 *  Returns 0 on error. Otherwise returns a handle.
31 */
32int
33prom_open(char *path)
34{
35	cell_t ci[5];
36	promif_owrap_t *ow;
37#ifdef PROM_32BIT_ADDRS
38	char *opath = NULL;
39	size_t len;
40
41	if ((uintptr_t)path > (uint32_t)-1) {
42		opath = path;
43		len = prom_strlen(opath) + 1; /* include terminating NUL */
44		path = promplat_alloc(len);
45		if (path == NULL)
46			return (0);
47		(void) prom_strcpy(path, opath);
48	}
49#endif
50
51	ow = promif_preout();
52	promif_preprom();
53	ci[0] = p1275_ptr2cell("open");		/* Service name */
54	ci[1] = (cell_t)1;			/* #argument cells */
55	ci[2] = (cell_t)1;			/* #result cells */
56	ci[3] = p1275_ptr2cell(path);		/* Arg1: Pathname */
57	ci[4] = (cell_t)0;			/* Res1: Prime result */
58
59	(void) p1275_cif_handler(&ci);
60
61	promif_postprom();
62	promif_postout(ow);
63
64#ifdef PROM_32BIT_ADDRS
65	if (opath != NULL)
66		promplat_free(path, len);
67#endif
68
69	return (p1275_cell2int(ci[4]));		/* Res1: ihandle */
70}
71
72
73int
74prom_seek(int fd, unsigned long long offset)
75{
76	cell_t ci[7];
77
78	ci[0] = p1275_ptr2cell("seek");		/* Service name */
79	ci[1] = (cell_t)3;			/* #argument cells */
80	ci[2] = (cell_t)1;			/* #result cells */
81	ci[3] = p1275_uint2cell((uint_t)fd);	/* Arg1: ihandle */
82	ci[4] = p1275_ull2cell_high(offset);	/* Arg2: pos.hi */
83	ci[5] = p1275_ull2cell_low(offset);	/* Arg3: pos.lo */
84	ci[6] = (cell_t)-1;			/* Res1: Prime result */
85
86	promif_preprom();
87	(void) p1275_cif_handler(&ci);
88	promif_postprom();
89
90	return (p1275_cell2int(ci[6]));		/* Res1: actual */
91}
92
93/*ARGSUSED3*/
94ssize_t
95prom_read(ihandle_t fd, caddr_t buf, size_t len, uint_t startblk, char devtype)
96{
97	cell_t ci[7];
98	promif_owrap_t *ow;
99#ifdef PROM_32BIT_ADDRS
100	caddr_t obuf = NULL;
101
102	if ((uintptr_t)buf > (uint32_t)-1) {
103		obuf = buf;
104		buf = promplat_alloc(len);
105		if (buf == NULL)
106			return (-1);
107	}
108#endif
109
110	ow = promif_preout();
111	promif_preprom();
112
113	ci[0] = p1275_ptr2cell("read");		/* Service name */
114	ci[1] = (cell_t)3;			/* #argument cells */
115	ci[2] = (cell_t)1;			/* #result cells */
116	ci[3] = p1275_size2cell((uint_t)fd);	/* Arg1: ihandle */
117	ci[4] = p1275_ptr2cell(buf);		/* Arg2: buffer address */
118	ci[5] = p1275_uint2cell(len);		/* Arg3: buffer length */
119	ci[6] = (cell_t)-1;			/* Res1: Prime result */
120
121	(void) p1275_cif_handler(&ci);
122
123	promif_postprom();
124	promif_postout(ow);
125
126#ifdef PROM_32BIT_ADDRS
127	if (obuf != NULL) {
128		promplat_bcopy(buf, obuf, len);
129		promplat_free(buf, len);
130	}
131#endif
132
133	return (p1275_cell2size(ci[6]));	/* Res1: actual length */
134}
135
136/*
137 * prom_write is the only prom_*() function we have to intercept
138 * because all the other prom_*() io interfaces eventually call
139 * into prom_write().
140 */
141/*ARGSUSED3*/
142ssize_t
143prom_write(ihandle_t fd, caddr_t buf, size_t len, uint_t startblk, char devtype)
144{
145	cell_t ci[7];
146	promif_owrap_t *ow;
147	ssize_t rlen;
148
149#ifdef PROM_32BIT_ADDRS
150	caddr_t obuf = NULL;
151	static char smallbuf[256];
152
153	ASSERT(buf);
154#endif
155
156	/*
157	 * If the callback address is set, attempt to redirect
158	 * console output back into kernel terminal emulator.
159	 */
160	if (promif_redirect != NULL && fd == prom_stdout_ihandle()) {
161		ow = promif_preout();
162		rlen = promif_redirect(promif_redirect_arg, (uchar_t *)buf,
163		    len);
164		promif_postout(ow);
165		return (rlen);
166	}
167
168#ifdef PROM_32BIT_ADDRS
169	if ((uintptr_t)buf > (uint32_t)-1) {
170		/*
171		 * This is a hack for kernel message output.
172		 * By avoiding calls to promplat_alloc (and
173		 * using smallbuf instead) when memory is low
174		 * we can print shortish kernel messages without
175		 * deadlocking. smallbuf should be at least as
176		 * large as the automatic buffer in
177		 * prom_printf.c:_doprint()'s stack frame.
178		 * promplat_alloc() can block on a mutex and so
179		 * is called here before calling promif_preprom().
180		 */
181		if (len > sizeof (smallbuf)) {
182			obuf = buf;
183			buf = promplat_alloc(len);
184			if (buf == NULL) {
185				return (-1);
186			}
187			promplat_bcopy(obuf, buf, len);
188		}
189	}
190#endif
191
192	/*
193	 * Normally we'd call promif_preprom() just before
194	 * calling into the prom (to enforce single-threaded
195	 * access) but here we need to call it before accessing
196	 * smallbuf, since smallbuf is statically allocated and
197	 * hence can only be accessed by one thread at a time.
198	 */
199	ow = promif_preout();
200	promif_preprom();
201
202#ifdef PROM_32BIT_ADDRS
203	if ((uintptr_t)buf > (uint32_t)-1) {
204		/*
205		 * If buf is small enough, use smallbuf
206		 * instead of promplat_alloc() (see above)
207		 * smallbuf is static, so single thread
208		 * access to it by using it only after
209		 * promif_preprom()
210		 */
211		if (len <= sizeof (smallbuf)) {
212			promplat_bcopy(buf, smallbuf, len);
213			buf = smallbuf;
214		}
215	}
216#endif
217
218	ci[0] = p1275_ptr2cell("write");	/* Service name */
219	ci[1] = (cell_t)3;			/* #argument cells */
220	ci[2] = (cell_t)1;			/* #result cells */
221	ci[3] = p1275_uint2cell((uint_t)fd);	/* Arg1: ihandle */
222	ci[4] = p1275_ptr2cell(buf);		/* Arg2: buffer addr */
223	ci[5] = p1275_size2cell(len);		/* Arg3: buffer len */
224	ci[6] = (cell_t)-1;			/* Res1: Prime result */
225
226	(void) p1275_cif_handler(&ci);
227	rlen = p1275_cell2size(ci[6]);		/* Res1: actual len */
228
229	promif_postprom();
230	promif_postout(ow);
231
232#ifdef PROM_32BIT_ADDRS
233	if (obuf != NULL)
234		promplat_free(buf, len);
235#endif
236
237	return (rlen);
238}
239
240int
241prom_close(int fd)
242{
243	cell_t ci[4];
244	promif_owrap_t *ow;
245
246	ci[0] = p1275_ptr2cell("close");	/* Service name */
247	ci[1] = (cell_t)1;			/* #argument cells */
248	ci[2] = (cell_t)0;			/* #result cells */
249	ci[3] = p1275_uint2cell((uint_t)fd);	/* Arg1: ihandle */
250
251	ow = promif_preout();
252	promif_preprom();
253	(void) p1275_cif_handler(&ci);
254	promif_postprom();
255	promif_postout(ow);
256
257	return (0);
258}
259