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