summaryrefslogtreecommitdiffstats
path: root/qemu/roms/openbios/packages/mac-parts.c
blob: 16c87caf8c5be44bd2aafbf3a3d0b286d799e8e4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
/*
 *   Creation Date: <2003/12/04 17:07:05 samuel>
 *   Time-stamp: <2004/01/07 19:36:09 samuel>
 *
 *	<mac-parts.c>
 *
 *	macintosh partition support
 *
 *   Copyright (C) 2003, 2004 Samuel Rydh (samuel@ibrium.se)
 *
 *   This program is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU General Public License
 *   version 2
 *
 */

#include "config.h"
#include "libopenbios/bindings.h"
#include "libopenbios/load.h"
#include "mac-parts.h"
#include "libc/byteorder.h"
#include "libc/vsprintf.h"
#include "packages.h"

//#define CONFIG_DEBUG_MAC_PARTS

#ifdef CONFIG_DEBUG_MAC_PARTS
#define DPRINTF(fmt, args...) \
do { printk("MAC-PARTS: " fmt , ##args); } while (0)
#else
#define DPRINTF(fmt, args...) do {} while(0)
#endif

typedef struct {
	xt_t		seek_xt, read_xt;
	ucell	        offs_hi, offs_lo;
        ucell	        size_hi, size_lo;
	ucell		bootcode_addr, bootcode_entry;
	unsigned int	blocksize;
	phandle_t	filesystem_ph;
} macparts_info_t;

DECLARE_NODE( macparts, INSTALL_OPEN, sizeof(macparts_info_t), "+/packages/mac-parts" );

#define SEEK( pos )		({ DPUSH(pos); call_parent(di->seek_xt); POP(); })
#define READ( buf, size )	({ PUSH(pointer2cell(buf)); PUSH(size); call_parent(di->read_xt); POP(); })

/* ( open -- flag ) */
static void
macparts_open( macparts_info_t *di )
{
	char *str = my_args_copy();
	char *parstr = NULL, *argstr = NULL;
	char *tmpstr;
	int bs, parnum=-1, apple_parnum=-1;
	int parlist[2], parlist_size = 0;
	desc_map_t dmap;
	part_entry_t par;
	int ret = 0, i = 0, j = 0;
	int want_bootcode = 0;
	phandle_t ph;
	ducell offs = 0, size = -1;

	DPRINTF("macparts_open '%s'\n", str );

	/* 
		Arguments that we accept:
		id: [0-7]
		[(id)][,][filespec]
	*/
	
	if ( str && strlen(str) ) {
		/* Detect the arguments */
		if ((*str >= '0' && *str <= '9') || (*str == ',')) {
		    push_str(str);
		    PUSH(',');
		    fword("left-parse-string");
		    parstr = pop_fstr_copy();
		    argstr = pop_fstr_copy();
		} else {
		    argstr = str;
		}

		/* Make sure argstr is not null */
		if (argstr == NULL)
		    argstr = strdup("");	
		
		/* Convert the id to a partition number */
		if (parstr && strlen(parstr))
		    parnum = atol(parstr);

		/* Detect if we are looking for the bootcode */
		if (strcmp(argstr, "%BOOT") == 0) {
		    want_bootcode = 1;
		}
	}

	DPRINTF("parstr: %s  argstr: %s  parnum: %d\n", parstr, argstr, parnum);

	DPRINTF("want_bootcode %d\n", want_bootcode);
	DPRINTF("macparts_open %d\n", parnum);

	di->filesystem_ph = 0;
	di->read_xt = find_parent_method("read");
	di->seek_xt = find_parent_method("seek");

	SEEK( 0 );
	if( READ(&dmap, sizeof(dmap)) != sizeof(dmap) )
		goto out;

	/* partition maps might support multiple block sizes; in this case,
	 * pmPyPartStart is typically given in terms of 512 byte blocks.
	 */
	bs = __be16_to_cpu(dmap.sbBlockSize);
	if( bs != 512 ) {
		SEEK( 512 );
		READ( &par, sizeof(par) );
		if( __be16_to_cpu(par.pmSig) == DESC_PART_SIGNATURE )
			bs = 512;
	}
	SEEK( bs );
	if( READ(&par, sizeof(par)) != sizeof(par) )
		goto out;
        if (__be16_to_cpu(par.pmSig) != DESC_PART_SIGNATURE)
		goto out;

	/*
	 * Implement partition selection as per the PowerPC Microprocessor CHRP bindings
	 */

	if (argstr == NULL || parnum == 0) {
		/* According to the spec, partition 0 as well as no arguments means the whole disk */
		offs = (long long)0;
		size = (long long)__be32_to_cpu(dmap.sbBlkCount) * bs;

		di->blocksize = (unsigned int)bs;

		di->offs_hi = offs >> BITS;
		di->offs_lo = offs & (ucell) -1;
	
		di->size_hi = size >> BITS;
		di->size_lo = size & (ucell) -1;

		ret = -1;
		goto out;

	} else if (parnum == -1) {

		DPRINTF("mac-parts: counted %d partitions\n", __be32_to_cpu(par.pmMapBlkCnt));

		/* No partition was explicitly requested so let's find a suitable partition... */
		for (i = 1; i <= __be32_to_cpu(par.pmMapBlkCnt); i++) {
			SEEK( bs * i );
			READ( &par, sizeof(par) );
			if ( __be16_to_cpu(par.pmSig) != DESC_PART_SIGNATURE ||
                            !__be32_to_cpu(par.pmPartBlkCnt) )
				continue;

			DPRINTF("found partition %d type: %s with status %x\n", i, par.pmPartType, __be32_to_cpu(par.pmPartStatus));

			/* If we have a valid, allocated and readable partition... */
			if( (__be32_to_cpu(par.pmPartStatus) & kPartitionAUXIsValid) &&
			(__be32_to_cpu(par.pmPartStatus) & kPartitionAUXIsAllocated) &&
			(__be32_to_cpu(par.pmPartStatus) & kPartitionAUXIsReadable) ) {

				/* Unfortunately Apple's OF implementation doesn't follow the OF PowerPC CHRP bindings
				 * and instead will brute-force boot the first valid partition it finds with a
				 * type of either "Apple_Boot", "Apple_HFS" or "DOS_FAT_". Here we store the id
				 * of the first partition that matches these criteria to use as a fallback later
				 * if required. */
				
				if (apple_parnum == -1 &&
				    (strcmp(par.pmPartType, "Apple_Boot") == 0 || 
				    strcmp(par.pmPartType, "Apple_Bootstrap") == 0 || 
				    strcmp(par.pmPartType, "Apple_HFS") == 0 ||
				    strcmp(par.pmPartType, "DOS_FAT_") == 0)) {
					apple_parnum = i;
					
					DPRINTF("Located Apple OF fallback partition %d\n", apple_parnum);
				}
				
				/* If the partition is also bootable and the pmProcessor field matches "PowerPC" (insensitive
				 * match), then according to the CHRP bindings this is our chosen partition */
				for (j = 0; j < strlen(par.pmProcessor); j++) {
				    par.pmProcessor[j] = tolower(par.pmProcessor[j]);
				}				
				
				if ((__be32_to_cpu(par.pmPartStatus) & kPartitionAUXIsBootValid) &&
				    strcmp(par.pmProcessor, "powerpc") == 0) {
				    parnum = i;
				
				    DPRINTF("Located CHRP-compliant boot partition %d\n", parnum);
				}
			}
		}
		
		/* If we found a valid CHRP partition, add it to the list */
		if (parnum > 0) {
		    parlist[parlist_size++] = parnum;
		}

		/* If we found an Apple OF fallback partition, add it to the list */
		if (apple_parnum > 0 && apple_parnum != parnum) {
		    parlist[parlist_size++] = apple_parnum;
		}
		
	} else {
		/* Another partition was explicitly requested */
		parlist[parlist_size++] = parnum;
		
		DPRINTF("Partition %d explicitly requested\n", parnum);
	}

	/* Attempt to use our CHRP partition, optionally followed by our Apple OF fallback partition */
	for (j = 0; j < parlist_size; j++) {
	
	    /* Make sure our partition is valid */
	    parnum = parlist[j];
	    
	    DPRINTF("Selected partition %d\n", parnum);
	    
	    SEEK( bs * parnum );
	    READ( &par, sizeof(par) );	

	    if(! ((__be32_to_cpu(par.pmPartStatus) & kPartitionAUXIsValid) &&
			(__be32_to_cpu(par.pmPartStatus) & kPartitionAUXIsAllocated) &&
			(__be32_to_cpu(par.pmPartStatus) & kPartitionAUXIsReadable)) ) {
		DPRINTF("Partition %d is not valid, allocated and readable\n", parnum);
		goto out;
	    }
	    
	    ret = -1;

	    offs = (long long)__be32_to_cpu(par.pmPyPartStart) * bs;
	    size = (long long)__be32_to_cpu(par.pmPartBlkCnt) * bs;	
	    
	    if (want_bootcode) {
		    
		/* If size == 0 then fail because we requested bootcode but it doesn't exist */
		size = (long long)__be32_to_cpu(par.pmBootSize);
		if (!size) {
		    ret = 0;
		    goto out;
		}

		/* Adjust seek position so 0 = start of bootcode */
		offs += (long long)__be32_to_cpu(par.pmLgBootStart) * bs;

		di->bootcode_addr = __be32_to_cpu(par.pmBootLoad);
		di->bootcode_entry = __be32_to_cpu(par.pmBootEntry);
	    }
	    
	    di->blocksize = (unsigned int)bs;	
	    
	    di->offs_hi = offs >> BITS;
	    di->offs_lo = offs & (ucell) -1;

	    di->size_hi = size >> BITS;
	    di->size_lo = size & (ucell) -1;

	    /* If we're trying to execute bootcode then we're all done */
	    if (want_bootcode) {
	        goto out;
	    }

	    /* We have a valid partition - so probe for a filesystem at the current offset */
	    DPRINTF("mac-parts: about to probe for fs\n");
	    DPUSH( offs );
	    PUSH_ih( my_parent() );
	    parword("find-filesystem");
	    DPRINTF("mac-parts: done fs probe\n");

	    ph = POP_ph();
	    if( ph ) {
		    DPRINTF("mac-parts: filesystem found on partition %d with ph " FMT_ucellx " and args %s\n", parnum, ph, argstr);
		    di->filesystem_ph = ph;
		    
		    /* In case no partition was specified, set a special selected-partition-args property
		       giving the device parameters that we can use to generate bootpath */
		    tmpstr = malloc(strlen(argstr) + 2 + 1);
		    if (strlen(argstr)) {
			sprintf(tmpstr, "%d,%s", parnum, argstr);
		    } else {
			sprintf(tmpstr, "%d", parnum);
		    }

		    push_str(tmpstr);
		    feval("strdup encode-string \" selected-partition-args\" property");

		    free(tmpstr);
		
		    /* If we have been asked to open a particular file, interpose the filesystem package with 
		    the passed filename as an argument */
		    if (strlen(argstr)) {
			    push_str( argstr );
			    PUSH_ph( ph );
			    fword("interpose");
		    }
		    
		    goto out;
	    } else {
		    DPRINTF("mac-parts: no filesystem found on partition %d; bypassing misc-files interpose\n", parnum);
		    
		    /* Here we have a valid partition; however if we tried to pass in a file argument for a
		       partition that doesn't contain a filesystem, then we must fail */
		    if (strlen(argstr)) {
			ret = 0;
		    }
	    }
	}
	    
	free( str );

out:
	PUSH( ret );
}

/* ( block0 -- flag? ) */
static void
macparts_probe( macparts_info_t *dummy )
{
	desc_map_t *dmap = (desc_map_t*)cell2pointer(POP());

	DPRINTF("macparts_probe %x ?= %x\n", dmap->sbSig, DESC_MAP_SIGNATURE);
	if( __be16_to_cpu(dmap->sbSig) != DESC_MAP_SIGNATURE )
		RET(0);
	RET(-1);
}

/* ( -- type offset.d size.d ) */
static void
macparts_get_info( macparts_info_t *di )
{
	DPRINTF("macparts_get_info");

	PUSH( -1 );		/* no type */
	PUSH( di->offs_lo );
	PUSH( di->offs_hi );
	PUSH( di->size_lo );
	PUSH( di->size_hi );
}

/* ( -- size entry addr ) */
static void
macparts_get_bootcode_info( macparts_info_t *di )
{
	DPRINTF("macparts_get_bootcode_info");

	PUSH( di->size_lo );
	PUSH( di->bootcode_entry );
	PUSH( di->bootcode_addr );
}

static void
macparts_block_size( macparts_info_t *di )
{
	DPRINTF("macparts_block_size = %x\n", di->blocksize);
	PUSH(di->blocksize);
}

static void
macparts_initialize( macparts_info_t *di )
{
	fword("register-partition-package");
}

/* ( pos.d -- status ) */
static void
macparts_seek(macparts_info_t *di )
{
	long long pos = DPOP();
	long long offs, size;

	DPRINTF("macparts_seek %llx:\n", pos);

	/* Seek is invalid if we reach the end of the device */
	size = ((ducell)di->size_hi << BITS) | di->size_lo;
	if (pos > size)
		RET( -1 );

	/* Calculate the seek offset for the parent */
	offs = ((ducell)di->offs_hi << BITS) | di->offs_lo;
	offs += pos;
	DPUSH(offs);

	DPRINTF("macparts_seek parent offset %llx:\n", offs);

	call_package(di->seek_xt, my_parent());
}

/* ( buf len -- actlen ) */
static void
macparts_read(macparts_info_t *di )
{
	DPRINTF("macparts_read\n");

	/* Pass the read back up to the parent */
	call_package(di->read_xt, my_parent());
}

/* ( addr -- size ) */
static void
macparts_load( __attribute__((unused))macparts_info_t *di )
{
	/* Invoke the loader */
	load(my_self());
}

/* ( pathstr len -- ) */
static void
macparts_dir( macparts_info_t *di )
{
	/* On PPC Mac, the first partition chosen according to the CHRP boot
	specification (i.e. marked as bootable) may not necessarily contain 
	a valid FS */
	if ( di->filesystem_ph ) {
		PUSH( my_self() );
		push_str("dir");
		PUSH( di->filesystem_ph );
		fword("find-method");
		POP();
		fword("execute");
	} else {
		forth_printf("mac-parts: Unable to determine filesystem\n");
		POP();
		POP();
	}
}

NODE_METHODS( macparts ) = {
	{ "probe",		macparts_probe	 		},
	{ "open",		macparts_open 			},
	{ "seek",		macparts_seek 			},
	{ "read",		macparts_read 			},
	{ "load",		macparts_load 			},
	{ "dir",		macparts_dir 			},
	{ "get-info",		macparts_get_info 		},
	{ "get-bootcode-info",	macparts_get_bootcode_info	},
	{ "block-size",		macparts_block_size 		},
	{ NULL,			macparts_initialize		},
};

void
macparts_init( void )
{
	REGISTER_NODE( macparts );
}