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