xref: /openbsd/sys/arch/macppc/stand/ofdev.c (revision 8932bfb7)
1 /*	$OpenBSD: ofdev.c,v 1.18 2011/04/10 09:58:19 miod Exp $	*/
2 /*	$NetBSD: ofdev.c,v 1.1 1997/04/16 20:29:20 thorpej Exp $	*/
3 
4 /*
5  * Copyright (C) 1995, 1996 Wolfgang Solfrank.
6  * Copyright (C) 1995, 1996 TooLs GmbH.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed by TooLs GmbH.
20  * 4. The name of TooLs GmbH may not be used to endorse or promote products
21  *    derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
29  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34 /*
35  * Device I/O routines using Open Firmware
36  */
37 #include <sys/param.h>
38 #include <lib/libkern/libkern.h>
39 #include <sys/disklabel.h>
40 #include <netinet/in.h>
41 
42 #include <lib/libsa/stand.h>
43 #include <lib/libsa/ufs.h>
44 #include <lib/libsa/cd9660.h>
45 #include <lib/libsa/nfs.h>
46 #include <hfs.h>
47 
48 #include <macppc/stand/ofdev.h>
49 
50 extern char bootdev[];
51 
52 /*
53  * this function is passed [device specifier]:[kernel]
54  * however a device specifier may contain a ':'
55  */
56 char namebuf[256];
57 static char *
58 filename(char *str)
59 {
60 	char *cp;
61 	char savec;
62 
63 	cp = strrchr(str, ':');
64 	if (cp == NULL)
65 		return NULL;
66 
67 	savec = *cp;
68 	*cp = 0;
69 	strlcpy(namebuf, cp+1, sizeof namebuf);
70 	return namebuf;
71 }
72 
73 static int
74 strategy(void *devdata, int rw, daddr32_t blk, size_t size, void *buf,
75     size_t *rsize)
76 {
77 	struct of_dev *dev = devdata;
78 	u_quad_t pos;
79 	int n;
80 
81 	if (rw != F_READ)
82 		return EPERM;
83 	if (dev->type != OFDEV_DISK)
84 		panic("strategy");
85 
86 	pos = (u_quad_t)(blk + dev->partoff) * dev->bsize;
87 
88 	for (;;) {
89 		if (OF_seek(dev->handle, pos) < 0)
90 			break;
91 		n = OF_read(dev->handle, buf, size);
92 		if (n == -2)
93 			continue;
94 		if (n < 0)
95 			break;
96 		*rsize = n;
97 		return 0;
98 	}
99 	return EIO;
100 }
101 
102 static int
103 devclose(struct open_file *of)
104 {
105 	struct of_dev *op = of->f_devdata;
106 
107 	if (op->type == OFDEV_NET)
108 		net_close(op);
109 	if (op->dmabuf)
110 		OF_call_method("dma-free", op->handle, 2, 0,
111 		    op->dmabuf, MAXPHYS);
112 
113 	OF_close(op->handle);
114 	op->handle = -1;
115 }
116 
117 struct devsw devsw[1] = {
118 	"OpenFirmware",
119 	strategy,
120 	(int (*)(struct open_file *, ...))nodev,
121 	devclose,
122 	noioctl
123 };
124 int ndevs = sizeof devsw / sizeof devsw[0];
125 
126 static struct fs_ops file_system_ufs = {
127 	ufs_open, ufs_close, ufs_read, ufs_write, ufs_seek, ufs_stat
128 };
129 static struct fs_ops file_system_cd9660 = {
130 	cd9660_open, cd9660_close, cd9660_read, cd9660_write, cd9660_seek,
131 	    cd9660_stat
132 };
133 static struct fs_ops file_system_hfs = {
134 	hfs_open, hfs_close, hfs_read, hfs_write, hfs_seek, hfs_stat
135 };
136 static struct fs_ops file_system_nfs = {
137 	nfs_open, nfs_close, nfs_read, nfs_write, nfs_seek, nfs_stat
138 };
139 
140 struct fs_ops file_system[3];
141 int nfsys;
142 
143 static struct of_dev ofdev = {
144 	-1,
145 };
146 
147 char opened_name[256];
148 
149 static u_long
150 get_long(p)
151 	const void *p;
152 {
153 	const unsigned char *cp = p;
154 
155 	return cp[0] | (cp[1] << 8) | (cp[2] << 16) | (cp[3] << 24);
156 }
157 
158 int
159 read_mac_label(struct of_dev *devp, char *buf, struct disklabel *lp)
160 {
161 	struct part_map_entry *part;
162 	size_t read;
163 	int part_cnt;
164 	int i;
165 	char *s;
166 
167 	if ((strategy(devp, F_READ, 1, DEV_BSIZE, buf, &read) != 0) ||
168 	    (read != DEV_BSIZE))
169 		return ERDLAB;
170 
171 	part = (struct part_map_entry *)buf;
172 
173 	/* if first partition is not valid, assume not HFS/DPME partitioned */
174 	if (part->pmSig != PART_ENTRY_MAGIC)
175 		return ERDLAB;
176 
177 	part_cnt = part->pmMapBlkCnt;
178 
179 	/* first search for "OpenBSD" partition type
180 	 * standard bsd disklabel lives inside at offset 0
181 	 * otherwise, we should fake a bsd partition
182 	 * with first HFS partition starting at 'i'
183 	 * ? will this cause problems with booting bsd.rd from hfs
184 	 */
185 	for (i = 0; i < part_cnt; i++) {
186 		/* read the appropriate block */
187 		if ((strategy(devp, F_READ, 1+i, DEV_BSIZE, buf, &read) != 0)
188 		   || (read != DEV_BSIZE))
189 			return ERDLAB;
190 
191 		part = (struct part_map_entry *)buf;
192 		/* toupper the string, in case caps are different... */
193 		for (s = part->pmPartType; *s; s++)
194 			if ((*s >= 'a') && (*s <= 'z'))
195 				*s = (*s - 'a' + 'A');
196 
197 		if (0 == strcmp(part->pmPartType, PART_TYPE_OPENBSD)) {
198 			/* FOUND OUR PARTITION!!! */
199 			if(strategy(devp, F_READ, part->pmPyPartStart,
200 				DEV_BSIZE, buf, &read) == 0
201 				&& read == DEV_BSIZE)
202 			{
203 				if (!getdisklabel(buf, lp))
204 					return 0;
205 
206 				/* If we have an OpenBSD region
207 				 * but no valid parition table,
208 				 * we cannot load a kernel from
209 				 * it, punt.
210 				 * should not have more than one
211 				 * OpenBSD of DPME type.
212 				 */
213 				return ERDLAB;
214 			}
215 		}
216 	}
217 	return ERDLAB;
218 }
219 
220 /*
221  * Find a valid disklabel.
222  */
223 static int
224 search_label(devp, off, buf, lp, off0)
225 	struct of_dev *devp;
226 	u_long off;
227 	char *buf;
228 	struct disklabel *lp;
229 	u_long off0;
230 {
231 	size_t read;
232 	struct dos_partition *p;
233 	int i;
234 	u_long poff;
235 	static int recursion;
236 
237 	if (strategy(devp, F_READ, off, DEV_BSIZE, buf, &read) ||
238 	    read != DEV_BSIZE)
239 		return ERDLAB;
240 
241 	if (buf[510] != 0x55 || buf[511] != 0xaa)
242 		return ERDLAB;
243 
244 	if (recursion++ <= 1)
245 		off0 += off;
246 	for (p = (struct dos_partition *)(buf + DOSPARTOFF), i = 4;
247 	    --i >= 0; p++) {
248 		if (p->dp_typ == DOSPTYP_OPENBSD) {
249 			poff = get_long(&p->dp_start) + off0;
250 			if (strategy(devp, F_READ, poff + LABELSECTOR,
251 			    DEV_BSIZE, buf, &read) == 0
252 			    && read == DEV_BSIZE) {
253 				if (!getdisklabel(buf, lp)) {
254 					recursion--;
255 					return 0;
256 				}
257 			}
258 			if (strategy(devp, F_READ, off, DEV_BSIZE, buf, &read)
259 			    || read != DEV_BSIZE) {
260 				recursion--;
261 				return ERDLAB;
262 			}
263 		} else if (p->dp_typ == DOSPTYP_EXTEND) {
264 			poff = get_long(&p->dp_start);
265 			if (!search_label(devp, poff, buf, lp, off0)) {
266 				recursion--;
267 				return 0;
268 			}
269 			if (strategy(devp, F_READ, off, DEV_BSIZE, buf, &read)
270 			    || read != DEV_BSIZE) {
271 				recursion--;
272 				return ERDLAB;
273 			}
274 		}
275 	}
276 	recursion--;
277 	return ERDLAB;
278 }
279 
280 int
281 devopen(struct open_file *of, const char *name, char **file)
282 {
283 	char *cp;
284 	char fname[256];
285 	char buf[DEV_BSIZE];
286 	struct disklabel label;
287 	int handle, part;
288 	size_t read;
289 	int error = 0;
290 
291 	if (ofdev.handle != -1)
292 		panic("devopen");
293 	if (of->f_flags != F_READ)
294 		return EPERM;
295 
296 	strlcpy(fname, name, sizeof fname);
297 	cp = filename(fname);
298 	if (cp == NULL)
299 		return ENOENT;
300 	strlcpy(buf, cp, sizeof buf);
301 	strlcpy(opened_name, fname, sizeof opened_name);
302 
303 	strlcat(opened_name, ":", sizeof opened_name);
304 	if (*buf != '/')
305 		strlcat(opened_name, "/", sizeof opened_name);
306 
307 	strlcat(opened_name, buf, sizeof opened_name);
308 	*file = opened_name + strlen(fname) + 1;
309 
310 	if ((handle = OF_finddevice(fname)) == -1)
311 		return ENOENT;
312 	if (OF_getprop(handle, "name", buf, sizeof buf) < 0)
313 		return ENXIO;
314 	if (OF_getprop(handle, "device_type", buf, sizeof buf) < 0)
315 		return ENXIO;
316 	if (!strcmp(buf, "block"))
317 		/*
318 		 * For block devices, indicate raw partition
319 		 * (:0 in OpenFirmware)
320 		 */
321 		strlcat(fname, ":0", sizeof fname);
322 	if ((handle = OF_open(fname)) == -1)
323 		return ENXIO;
324 	bzero(&ofdev, sizeof ofdev);
325 	ofdev.handle = handle;
326 	ofdev.dmabuf = NULL;
327 	OF_call_method("dma-alloc", handle, 1, 1, MAXPHYS, &ofdev.dmabuf);
328 	if (!strcmp(buf, "block")) {
329 		ofdev.type = OFDEV_DISK;
330 		ofdev.bsize = DEV_BSIZE;
331 		/* First try to find a disklabel without MBR partitions */
332 		if (strategy(&ofdev, F_READ,
333 		    LABELSECTOR, DEV_BSIZE, buf, &read) != 0 ||
334 		    read != DEV_BSIZE ||
335 		    getdisklabel(buf, &label)) {
336 			/* Else try MBR partitions */
337 			error = read_mac_label(&ofdev, buf, &label);
338 			if (error == ERDLAB)
339 				error = search_label(&ofdev, 0, buf, &label, 0);
340 
341 			if (error && error != ERDLAB)
342 				goto bad;
343 		}
344 
345 		if (error == ERDLAB) {
346 			/* No label, just use complete disk */
347 			ofdev.partoff = 0;
348 		} else {
349 			part = 0; /* how to pass this parameter */
350 			ofdev.partoff = label.d_partitions[part].p_offset;
351 		}
352 
353 		of->f_dev = devsw;
354 		of->f_devdata = &ofdev;
355 		bcopy(&file_system_ufs, file_system, sizeof file_system[0]);
356 		bcopy(&file_system_cd9660, file_system + 1,
357 		    sizeof file_system[0]);
358 		bcopy(&file_system_hfs, file_system + 2,
359 		    sizeof file_system[0]);
360 		nfsys = 3;
361 		return 0;
362 	}
363 	if (!strcmp(buf, "network")) {
364 		ofdev.type = OFDEV_NET;
365 		of->f_dev = devsw;
366 		of->f_devdata = &ofdev;
367 		bcopy(&file_system_nfs, file_system, sizeof file_system[0]);
368 		nfsys = 1;
369 		if (error = net_open(&ofdev))
370 			goto bad;
371 		return 0;
372 	}
373 	error = EFTYPE;
374 bad:
375 	OF_close(handle);
376 	ofdev.handle = -1;
377 	return error;
378 }
379