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