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