xref: /openbsd-src/sys/arch/macppc/stand/ofdev.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: ofdev.c,v 1.22 2015/03/23 20:18:52 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 char opened_name[256];
53 
54 /*
55  * this function is passed [device specifier]:[kernel]
56  * however a device specifier may contain a ':'
57  */
58 static int
59 parsename(char *str, char **file)
60 {
61 	char *cp;
62 	int aliases;
63 	size_t len;
64 
65 	cp = strrchr(str, ':');
66 	if (cp == NULL)
67 		return 1;
68 
69 	*cp++ = 0;
70 
71 	if ((aliases = OF_finddevice("/aliases")) == -1 ||
72 	    OF_getprop(aliases, str, opened_name, sizeof opened_name) < 0)
73 		strlcpy(opened_name, str, sizeof opened_name);
74 
75 	len = strlcat(opened_name, ":", sizeof opened_name);
76 	if (*cp != '/')
77 		strlcat(opened_name, "/", sizeof opened_name);
78 
79 	if (strlcat(opened_name, cp, sizeof opened_name) >= sizeof opened_name)
80 		return 1;
81 
82 	*file = opened_name + len + 1;
83 
84 	return 0;
85 }
86 
87 static int
88 strategy(void *devdata, int rw, daddr32_t blk, size_t size, void *buf,
89     size_t *rsize)
90 {
91 	struct of_dev *dev = devdata;
92 	u_quad_t pos;
93 	int n;
94 
95 	if (rw != F_READ)
96 		return EPERM;
97 	if (dev->type != OFDEV_DISK)
98 		panic("strategy");
99 
100 	pos = (u_quad_t)(blk + dev->partoff) * dev->bsize;
101 
102 	for (;;) {
103 		if (OF_seek(dev->handle, pos) < 0)
104 			break;
105 		n = OF_read(dev->handle, buf, size);
106 		if (n == -2)
107 			continue;
108 		if (n < 0)
109 			break;
110 		*rsize = n;
111 		return 0;
112 	}
113 	return EIO;
114 }
115 
116 static int
117 devclose(struct open_file *of)
118 {
119 	struct of_dev *op = of->f_devdata;
120 
121 	if (op->type == OFDEV_NET)
122 		net_close(op);
123 	if (op->dmabuf)
124 		OF_call_method("dma-free", op->handle, 2, 0,
125 		    op->dmabuf, MAXBSIZE);
126 
127 	OF_close(op->handle);
128 	free(op, sizeof *op);
129 }
130 
131 struct devsw devsw[1] = {
132 	"OpenFirmware",
133 	strategy,
134 	(int (*)(struct open_file *, ...))nodev,
135 	devclose,
136 	noioctl
137 };
138 int ndevs = sizeof devsw / sizeof devsw[0];
139 
140 static struct fs_ops file_system_ufs = {
141 	ufs_open, ufs_close, ufs_read, ufs_write, ufs_seek, ufs_stat,
142 	ufs_readdir
143 };
144 static struct fs_ops file_system_cd9660 = {
145 	cd9660_open, cd9660_close, cd9660_read, cd9660_write, cd9660_seek,
146 	cd9660_stat, cd9660_readdir
147 };
148 static struct fs_ops file_system_hfs = {
149 	hfs_open, hfs_close, hfs_read, hfs_write, hfs_seek, hfs_stat,
150 	hfs_readdir
151 };
152 static struct fs_ops file_system_nfs = {
153 	nfs_open, nfs_close, nfs_read, nfs_write, nfs_seek, nfs_stat,
154 	nfs_readdir
155 };
156 
157 struct fs_ops file_system[3];
158 int nfsys;
159 
160 static u_long
161 get_long(p)
162 	const void *p;
163 {
164 	const unsigned char *cp = p;
165 
166 	return cp[0] | (cp[1] << 8) | (cp[2] << 16) | (cp[3] << 24);
167 }
168 
169 int
170 read_mac_label(struct of_dev *devp, char *buf, struct disklabel *lp)
171 {
172 	struct part_map_entry *part;
173 	size_t read;
174 	int part_cnt;
175 	int i;
176 	char *s;
177 
178 	if ((strategy(devp, F_READ, 1, DEV_BSIZE, buf, &read) != 0) ||
179 	    (read != DEV_BSIZE))
180 		return ERDLAB;
181 
182 	part = (struct part_map_entry *)buf;
183 
184 	/* if first partition is not valid, assume not HFS/DPME partitioned */
185 	if (part->pmSig != PART_ENTRY_MAGIC)
186 		return ERDLAB;
187 
188 	part_cnt = part->pmMapBlkCnt;
189 
190 	/* first search for "OpenBSD" partition type
191 	 * standard bsd disklabel lives inside at offset 0
192 	 * otherwise, we should fake a bsd partition
193 	 * with first HFS partition starting at 'i'
194 	 * ? will this cause problems with booting bsd.rd from hfs
195 	 */
196 	for (i = 0; i < part_cnt; i++) {
197 		/* read the appropriate block */
198 		if ((strategy(devp, F_READ, 1+i, DEV_BSIZE, buf, &read) != 0)
199 		   || (read != DEV_BSIZE))
200 			return ERDLAB;
201 
202 		part = (struct part_map_entry *)buf;
203 		/* toupper the string, in case caps are different... */
204 		for (s = part->pmPartType; *s; s++)
205 			if ((*s >= 'a') && (*s <= 'z'))
206 				*s = (*s - 'a' + 'A');
207 
208 		if (0 == strcmp(part->pmPartType, PART_TYPE_OPENBSD)) {
209 			/* FOUND OUR PARTITION!!! */
210 			if(strategy(devp, F_READ, part->pmPyPartStart,
211 				DEV_BSIZE, buf, &read) == 0
212 				&& read == DEV_BSIZE)
213 			{
214 				if (!getdisklabel(buf, lp))
215 					return 0;
216 
217 				/* If we have an OpenBSD region
218 				 * but no valid parition table,
219 				 * we cannot load a kernel from
220 				 * it, punt.
221 				 * should not have more than one
222 				 * OpenBSD of DPME type.
223 				 */
224 				return ERDLAB;
225 			}
226 		}
227 	}
228 	return ERDLAB;
229 }
230 
231 /*
232  * Find a valid disklabel.
233  */
234 static int
235 search_label(devp, off, buf, lp, off0)
236 	struct of_dev *devp;
237 	u_long off;
238 	char *buf;
239 	struct disklabel *lp;
240 	u_long off0;
241 {
242 	size_t read;
243 	struct dos_partition *p;
244 	int i;
245 	u_long poff;
246 	static int recursion;
247 
248 	if (strategy(devp, F_READ, off, DEV_BSIZE, buf, &read) ||
249 	    read != DEV_BSIZE)
250 		return ERDLAB;
251 
252 	if (buf[510] != 0x55 || buf[511] != 0xaa)
253 		return ERDLAB;
254 
255 	if (recursion++ <= 1)
256 		off0 += off;
257 	for (p = (struct dos_partition *)(buf + DOSPARTOFF), i = 4;
258 	    --i >= 0; p++) {
259 		if (p->dp_typ == DOSPTYP_OPENBSD) {
260 			poff = get_long(&p->dp_start) + off0;
261 			if (strategy(devp, F_READ, poff + LABELSECTOR,
262 			    DEV_BSIZE, buf, &read) == 0
263 			    && read == DEV_BSIZE) {
264 				if (!getdisklabel(buf, lp)) {
265 					recursion--;
266 					return 0;
267 				}
268 			}
269 			if (strategy(devp, F_READ, off, DEV_BSIZE, buf, &read)
270 			    || read != DEV_BSIZE) {
271 				recursion--;
272 				return ERDLAB;
273 			}
274 		} else if (p->dp_typ == DOSPTYP_EXTEND) {
275 			poff = get_long(&p->dp_start);
276 			if (!search_label(devp, poff, buf, lp, off0)) {
277 				recursion--;
278 				return 0;
279 			}
280 			if (strategy(devp, F_READ, off, DEV_BSIZE, buf, &read)
281 			    || read != DEV_BSIZE) {
282 				recursion--;
283 				return ERDLAB;
284 			}
285 		}
286 	}
287 	recursion--;
288 	return ERDLAB;
289 }
290 
291 int
292 devopen(struct open_file *of, const char *name, char **file)
293 {
294 	struct of_dev *ofdev;
295 	char fname[256];
296 	char buf[DEV_BSIZE];
297 	struct disklabel label;
298 	int handle, part;
299 	size_t read;
300 	int error = 0;
301 
302 	if (of->f_flags != F_READ)
303 		return EPERM;
304 
305 	strlcpy(fname, name, sizeof fname);
306 	if (parsename(fname, file))
307 		return ENOENT;
308 
309 	if ((handle = OF_finddevice(fname)) == -1)
310 		return ENOENT;
311 	if (OF_getprop(handle, "name", buf, sizeof buf) < 0)
312 		return ENXIO;
313 	if (OF_getprop(handle, "device_type", buf, sizeof buf) < 0)
314 		return ENXIO;
315 	if (!strcmp(buf, "block"))
316 		/*
317 		 * For block devices, indicate raw partition
318 		 * (:0 in OpenFirmware)
319 		 */
320 		strlcat(fname, ":0", sizeof fname);
321 	if ((ofdev = alloc(sizeof *ofdev)) == NULL)
322 		return ENOMEM;
323 	bzero(ofdev, sizeof *ofdev);
324 
325 	if ((handle = OF_open(fname)) == -1) {
326 		free(ofdev, sizeof *ofdev);
327 		return ENXIO;
328 	}
329 
330 	ofdev->handle = handle;
331 	ofdev->dmabuf = NULL;
332 	OF_call_method("dma-alloc", handle, 1, 1, MAXBSIZE, &ofdev->dmabuf);
333 	if (!strcmp(buf, "block")) {
334 		ofdev->type = OFDEV_DISK;
335 		ofdev->bsize = DEV_BSIZE;
336 		/* First try to find a disklabel without MBR partitions */
337 		if (strategy(ofdev, F_READ,
338 		    LABELSECTOR, DEV_BSIZE, buf, &read) != 0 ||
339 		    read != DEV_BSIZE ||
340 		    getdisklabel(buf, &label)) {
341 			/* Else try MBR partitions */
342 			error = read_mac_label(ofdev, buf, &label);
343 			if (error == ERDLAB)
344 				error = search_label(ofdev, 0, buf, &label, 0);
345 
346 			if (error && error != ERDLAB)
347 				goto bad;
348 		}
349 
350 		if (error == ERDLAB) {
351 			/* No label, just use complete disk */
352 			ofdev->partoff = 0;
353 		} else {
354 			part = 0; /* how to pass this parameter */
355 			ofdev->partoff = label.d_partitions[part].p_offset;
356 		}
357 
358 		of->f_dev = devsw;
359 		of->f_devdata = ofdev;
360 		bcopy(&file_system_ufs, file_system, sizeof file_system[0]);
361 		bcopy(&file_system_cd9660, file_system + 1,
362 		    sizeof file_system[0]);
363 		bcopy(&file_system_hfs, file_system + 2,
364 		    sizeof file_system[0]);
365 		nfsys = 3;
366 		return 0;
367 	}
368 	if (!strcmp(buf, "network")) {
369 		ofdev->type = OFDEV_NET;
370 		of->f_dev = devsw;
371 		of->f_devdata = ofdev;
372 		bcopy(&file_system_nfs, file_system, sizeof file_system[0]);
373 		nfsys = 1;
374 		if (error = net_open(ofdev))
375 			goto bad;
376 		return 0;
377 	}
378 	error = EFTYPE;
379 bad:
380 	OF_close(handle);
381 	free(ofdev, sizeof *ofdev);
382 	return error;
383 }
384