1 /*
2  *   Creation Date: <2003/12/04 17:07:05 samuel>
3  *   Time-stamp: <2004/01/07 19:36:09 samuel>
4  *
5  *	<mac-parts.c>
6  *
7  *	macintosh partition support
8  *
9  *   Copyright (C) 2003, 2004 Samuel Rydh (samuel@ibrium.se)
10  *
11  *   This program is free software; you can redistribute it and/or
12  *   modify it under the terms of the GNU General Public License
13  *   version 2
14  *
15  */
16 
17 #include "config.h"
18 #include "libopenbios/bindings.h"
19 #include "libopenbios/load.h"
20 #include "mac-parts.h"
21 #include "libc/byteorder.h"
22 #include "libc/vsprintf.h"
23 #include "packages.h"
24 
25 //#define CONFIG_DEBUG_MAC_PARTS
26 
27 #ifdef CONFIG_DEBUG_MAC_PARTS
28 #define DPRINTF(fmt, args...) \
29 do { printk("MAC-PARTS: " fmt , ##args); } while (0)
30 #else
31 #define DPRINTF(fmt, args...) do {} while(0)
32 #endif
33 
34 typedef struct {
35 	xt_t		seek_xt, read_xt;
36 	ucell	        offs_hi, offs_lo;
37         ucell	        size_hi, size_lo;
38 	ucell		bootcode_addr, bootcode_entry;
39 	unsigned int	blocksize;
40 	phandle_t	filesystem_ph;
41 } macparts_info_t;
42 
43 DECLARE_NODE( macparts, INSTALL_OPEN, sizeof(macparts_info_t), "+/packages/mac-parts" );
44 
45 #define SEEK( pos )		({ DPUSH(pos); call_parent(di->seek_xt); POP(); })
46 #define READ( buf, size )	({ PUSH(pointer2cell(buf)); PUSH(size); call_parent(di->read_xt); POP(); })
47 
48 /* ( open -- flag ) */
49 static void
macparts_open(macparts_info_t * di)50 macparts_open( macparts_info_t *di )
51 {
52 	char *str = my_args_copy();
53 	char *parstr = NULL, *argstr = NULL;
54 	char *tmpstr;
55 	int bs, parnum=-1, apple_parnum=-1;
56 	int parlist[2], parlist_size = 0;
57 	desc_map_t dmap;
58 	part_entry_t par;
59 	int ret = 0, i = 0, j = 0;
60 	int want_bootcode = 0;
61 	phandle_t ph;
62 	ducell offs = 0, size = -1;
63 
64 	DPRINTF("macparts_open '%s'\n", str );
65 
66 	/*
67 		Arguments that we accept:
68 		id: [0-7]
69 		[(id)][,][filespec]
70 	*/
71 
72 	if ( str && strlen(str) ) {
73 		/* Detect the arguments */
74 		if ((*str >= '0' && *str <= '9') || (*str == ',')) {
75 		    push_str(str);
76 		    PUSH(',');
77 		    fword("left-parse-string");
78 		    parstr = pop_fstr_copy();
79 		    argstr = pop_fstr_copy();
80 		} else {
81 		    argstr = str;
82 		}
83 
84 		/* Make sure argstr is not null */
85 		if (argstr == NULL)
86 		    argstr = strdup("");
87 
88 		/* Convert the id to a partition number */
89 		if (parstr && strlen(parstr))
90 		    parnum = atol(parstr);
91 
92 		/* Detect if we are looking for the bootcode */
93 		if (strcmp(argstr, "%BOOT") == 0) {
94 		    want_bootcode = 1;
95 		}
96 	}
97 
98 	DPRINTF("parstr: %s  argstr: %s  parnum: %d\n", parstr, argstr, parnum);
99 
100 	DPRINTF("want_bootcode %d\n", want_bootcode);
101 	DPRINTF("macparts_open %d\n", parnum);
102 
103 	di->filesystem_ph = 0;
104 	di->read_xt = find_parent_method("read");
105 	di->seek_xt = find_parent_method("seek");
106 
107 	SEEK( 0 );
108 	if( READ(&dmap, sizeof(dmap)) != sizeof(dmap) )
109 		goto out;
110 
111 	/* partition maps might support multiple block sizes; in this case,
112 	 * pmPyPartStart is typically given in terms of 512 byte blocks.
113 	 */
114 	bs = __be16_to_cpu(dmap.sbBlockSize);
115 	if( bs != 512 ) {
116 		SEEK( 512 );
117 		READ( &par, sizeof(par) );
118 		if( __be16_to_cpu(par.pmSig) == DESC_PART_SIGNATURE )
119 			bs = 512;
120 	}
121 	SEEK( bs );
122 	if( READ(&par, sizeof(par)) != sizeof(par) )
123 		goto out;
124         if (__be16_to_cpu(par.pmSig) != DESC_PART_SIGNATURE)
125 		goto out;
126 
127 	/*
128 	 * Implement partition selection as per the PowerPC Microprocessor CHRP bindings
129 	 */
130 
131 	if (argstr == NULL || parnum == 0) {
132 		/* According to the spec, partition 0 as well as no arguments means the whole disk */
133 		offs = (long long)0;
134 		size = (long long)__be32_to_cpu(dmap.sbBlkCount) * bs;
135 
136 		di->blocksize = (unsigned int)bs;
137 
138 		di->offs_hi = offs >> BITS;
139 		di->offs_lo = offs & (ucell) -1;
140 
141 		di->size_hi = size >> BITS;
142 		di->size_lo = size & (ucell) -1;
143 
144 		ret = -1;
145 		goto out;
146 
147 	} else if (parnum == -1) {
148 
149 		DPRINTF("mac-parts: counted %d partitions\n", __be32_to_cpu(par.pmMapBlkCnt));
150 
151 		/* No partition was explicitly requested so let's find a suitable partition... */
152 		for (i = 1; i <= __be32_to_cpu(par.pmMapBlkCnt); i++) {
153 			SEEK( bs * i );
154 			READ( &par, sizeof(par) );
155 			if ( __be16_to_cpu(par.pmSig) != DESC_PART_SIGNATURE ||
156                             !__be32_to_cpu(par.pmPartBlkCnt) )
157 				continue;
158 
159 			DPRINTF("found partition %d type: %s with status %x\n", i, par.pmPartType, __be32_to_cpu(par.pmPartStatus));
160 
161 			/* Unfortunately Apple's OF implementation doesn't follow the OF PowerPC CHRP bindings
162 			* and instead will brute-force boot the first valid partition it finds with a
163 			* type of either "Apple_Boot", "Apple_HFS" or "DOS_FAT_". Here we store the id
164 			* of the first partition that matches these criteria to use as a fallback later
165 			* if required. */
166 			if (apple_parnum == -1 &&
167 				(strcmp(par.pmPartType, "Apple_Boot") == 0 ||
168 				strcmp(par.pmPartType, "Apple_Bootstrap") == 0 ||
169 				strcmp(par.pmPartType, "Apple_HFS") == 0 ||
170 				strcmp(par.pmPartType, "DOS_FAT_") == 0)) {
171 				apple_parnum = i;
172 
173 				DPRINTF("Located Apple OF fallback partition %d\n", apple_parnum);
174 			}
175 
176 			/* If we have a valid, allocated and readable partition... */
177 			if( (__be32_to_cpu(par.pmPartStatus) & kPartitionAUXIsValid) &&
178 				(__be32_to_cpu(par.pmPartStatus) & kPartitionAUXIsAllocated) &&
179 				(__be32_to_cpu(par.pmPartStatus) & kPartitionAUXIsReadable) ) {
180 
181 				/* If the partition is also bootable and the pmProcessor field matches "PowerPC" (insensitive
182 				 * match), then according to the CHRP bindings this is our chosen partition */
183 				for (j = 0; j < strlen(par.pmProcessor); j++) {
184 				    par.pmProcessor[j] = tolower(par.pmProcessor[j]);
185 				}
186 
187 				if ((__be32_to_cpu(par.pmPartStatus) & kPartitionAUXIsBootValid) &&
188 				    strcmp(par.pmProcessor, "powerpc") == 0) {
189 				    parnum = i;
190 
191 				    DPRINTF("Located CHRP-compliant boot partition %d\n", parnum);
192 				}
193 			}
194 		}
195 
196 		/* If we found a valid CHRP partition, add it to the list */
197 		if (parnum > 0) {
198 		    parlist[parlist_size++] = parnum;
199 		}
200 
201 		/* If we found an Apple OF fallback partition, add it to the list */
202 		if (apple_parnum > 0 && apple_parnum != parnum) {
203 		    parlist[parlist_size++] = apple_parnum;
204 		}
205 
206 	} else {
207 		/* Another partition was explicitly requested */
208 		parlist[parlist_size++] = parnum;
209 
210 		DPRINTF("Partition %d explicitly requested\n", parnum);
211 	}
212 
213 	/* Attempt to use our CHRP partition, optionally followed by our Apple OF fallback partition */
214 	for (j = 0; j < parlist_size; j++) {
215 
216 	    /* Make sure our partition is valid */
217 	    parnum = parlist[j];
218 
219 	    DPRINTF("Selected partition %d\n", parnum);
220 
221 	    SEEK( bs * parnum );
222 	    READ( &par, sizeof(par) );
223 
224 	    if(! ((__be32_to_cpu(par.pmPartStatus) & kPartitionAUXIsValid) &&
225 			(__be32_to_cpu(par.pmPartStatus) & kPartitionAUXIsAllocated) &&
226 			(__be32_to_cpu(par.pmPartStatus) & kPartitionAUXIsReadable)) ) {
227 		DPRINTF("WARNING: Partition %d is not valid, allocated and readable\n", parnum);
228 	    }
229 
230 	    ret = -1;
231 
232 	    offs = (long long)__be32_to_cpu(par.pmPyPartStart) * bs;
233 	    size = (long long)__be32_to_cpu(par.pmPartBlkCnt) * bs;
234 
235 	    if (want_bootcode) {
236 		/* If size == 0 then fail because we requested bootcode but it doesn't exist */
237 		size = (long long)__be32_to_cpu(par.pmBootSize);
238 		if (!size) {
239 		    ret = 0;
240 		    goto out;
241 		}
242 
243 		/* Adjust seek position so 0 = start of bootcode */
244 		offs += (long long)__be32_to_cpu(par.pmLgBootStart) * bs;
245 
246 		di->bootcode_addr = __be32_to_cpu(par.pmBootLoad);
247 		di->bootcode_entry = __be32_to_cpu(par.pmBootEntry);
248 	    }
249 
250 	    di->blocksize = (unsigned int)bs;
251 
252 	    di->offs_hi = offs >> BITS;
253 	    di->offs_lo = offs & (ucell) -1;
254 
255 	    di->size_hi = size >> BITS;
256 	    di->size_lo = size & (ucell) -1;
257 
258 	    /* If we're trying to execute bootcode then we're all done */
259 	    if (want_bootcode) {
260 	        goto out;
261 	    }
262 
263 	    /* We have a valid partition - so probe for a filesystem at the current offset */
264 	    DPRINTF("mac-parts: about to probe for fs\n");
265 	    DPUSH( offs );
266 	    PUSH_ih( my_parent() );
267 	    parword("find-filesystem");
268 	    DPRINTF("mac-parts: done fs probe\n");
269 
270 	    ph = POP_ph();
271 	    if( ph ) {
272 		    DPRINTF("mac-parts: filesystem found on partition %d with ph " FMT_ucellx " and args %s\n", parnum, ph, argstr);
273 		    di->filesystem_ph = ph;
274 
275 		    /* In case no partition was specified, set a special selected-partition-args property
276 		       giving the device parameters that we can use to generate bootpath */
277 		    tmpstr = malloc(strlen(argstr) + 2 + 1);
278 		    if (strlen(argstr)) {
279 			sprintf(tmpstr, "%d,%s", parnum, argstr);
280 		    } else {
281 			sprintf(tmpstr, "%d", parnum);
282 		    }
283 
284 		    push_str(tmpstr);
285 		    feval("strdup encode-string \" selected-partition-args\" property");
286 
287 		    free(tmpstr);
288 
289 		    /* If we have been asked to open a particular file, interpose the filesystem package with
290 		    the passed filename as an argument */
291 		    if (strlen(argstr)) {
292 			    push_str( argstr );
293 			    PUSH_ph( ph );
294 			    fword("interpose");
295 		    }
296 
297 		    goto out;
298 	    } else {
299 		    DPRINTF("mac-parts: no filesystem found on partition %d; bypassing misc-files interpose\n", parnum);
300 
301 		    /* Here we have a valid partition; however if we tried to pass in a file argument for a
302 		       partition that doesn't contain a filesystem, then we must fail */
303 		    if (strlen(argstr)) {
304 			ret = 0;
305 		    }
306 	    }
307 	}
308 
309 	free( str );
310 
311 out:
312 	PUSH( ret );
313 }
314 
315 /* ( block0 -- flag? ) */
316 static void
macparts_probe(macparts_info_t * dummy)317 macparts_probe( macparts_info_t *dummy )
318 {
319 	desc_map_t *dmap = (desc_map_t*)cell2pointer(POP());
320 
321 	DPRINTF("macparts_probe %x ?= %x\n", dmap->sbSig, DESC_MAP_SIGNATURE);
322 	if( __be16_to_cpu(dmap->sbSig) != DESC_MAP_SIGNATURE )
323 		RET(0);
324 	RET(-1);
325 }
326 
327 /* ( -- type offset.d size.d ) */
328 static void
macparts_get_info(macparts_info_t * di)329 macparts_get_info( macparts_info_t *di )
330 {
331 	DPRINTF("macparts_get_info");
332 
333 	PUSH( -1 );		/* no type */
334 	PUSH( di->offs_lo );
335 	PUSH( di->offs_hi );
336 	PUSH( di->size_lo );
337 	PUSH( di->size_hi );
338 }
339 
340 /* ( -- size entry addr ) */
341 static void
macparts_get_bootcode_info(macparts_info_t * di)342 macparts_get_bootcode_info( macparts_info_t *di )
343 {
344 	DPRINTF("macparts_get_bootcode_info");
345 
346 	PUSH( di->size_lo );
347 	PUSH( di->bootcode_entry );
348 	PUSH( di->bootcode_addr );
349 }
350 
351 static void
macparts_block_size(macparts_info_t * di)352 macparts_block_size( macparts_info_t *di )
353 {
354 	DPRINTF("macparts_block_size = %x\n", di->blocksize);
355 	PUSH(di->blocksize);
356 }
357 
358 static void
macparts_initialize(macparts_info_t * di)359 macparts_initialize( macparts_info_t *di )
360 {
361 	fword("register-partition-package");
362 }
363 
364 /* ( pos.d -- status ) */
365 static void
macparts_seek(macparts_info_t * di)366 macparts_seek(macparts_info_t *di )
367 {
368 	long long pos = DPOP();
369 	long long offs, size;
370 
371 	DPRINTF("macparts_seek %llx:\n", pos);
372 
373 	/* Seek is invalid if we reach the end of the device */
374 	size = ((ducell)di->size_hi << BITS) | di->size_lo;
375 	if (pos > size)
376 		RET( -1 );
377 
378 	/* Calculate the seek offset for the parent */
379 	offs = ((ducell)di->offs_hi << BITS) | di->offs_lo;
380 	offs += pos;
381 	DPUSH(offs);
382 
383 	DPRINTF("macparts_seek parent offset %llx:\n", offs);
384 
385 	call_package(di->seek_xt, my_parent());
386 }
387 
388 /* ( buf len -- actlen ) */
389 static void
macparts_read(macparts_info_t * di)390 macparts_read(macparts_info_t *di )
391 {
392 	DPRINTF("macparts_read\n");
393 
394 	/* Pass the read back up to the parent */
395 	call_package(di->read_xt, my_parent());
396 }
397 
398 /* ( addr -- size ) */
399 static void
macparts_load(macparts_info_t * di)400 macparts_load( __attribute__((unused))macparts_info_t *di )
401 {
402 	/* Invoke the loader */
403 	load(my_self());
404 }
405 
406 /* ( pathstr len -- ) */
407 static void
macparts_dir(macparts_info_t * di)408 macparts_dir( macparts_info_t *di )
409 {
410 	/* On PPC Mac, the first partition chosen according to the CHRP boot
411 	specification (i.e. marked as bootable) may not necessarily contain
412 	a valid FS */
413 	if ( di->filesystem_ph ) {
414 		PUSH( my_self() );
415 		push_str("dir");
416 		PUSH( di->filesystem_ph );
417 		fword("find-method");
418 		POP();
419 		fword("execute");
420 	} else {
421 		forth_printf("mac-parts: Unable to determine filesystem\n");
422 		POP();
423 		POP();
424 	}
425 }
426 
427 NODE_METHODS( macparts ) = {
428 	{ "probe",		macparts_probe	 		},
429 	{ "open",		macparts_open 			},
430 	{ "seek",		macparts_seek 			},
431 	{ "read",		macparts_read 			},
432 	{ "load",		macparts_load 			},
433 	{ "dir",		macparts_dir 			},
434 	{ "get-info",		macparts_get_info 		},
435 	{ "get-bootcode-info",	macparts_get_bootcode_info	},
436 	{ "block-size",		macparts_block_size 		},
437 	{ NULL,			macparts_initialize		},
438 };
439 
440 void
macparts_init(void)441 macparts_init( void )
442 {
443 	REGISTER_NODE( macparts );
444 }
445