Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / packages / mac-parts.c
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
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                         /* If we have a valid, allocated and readable partition... */
162                         if( (__be32_to_cpu(par.pmPartStatus) & kPartitionAUXIsValid) &&
163                         (__be32_to_cpu(par.pmPartStatus) & kPartitionAUXIsAllocated) &&
164                         (__be32_to_cpu(par.pmPartStatus) & kPartitionAUXIsReadable) ) {
165
166                                 /* Unfortunately Apple's OF implementation doesn't follow the OF PowerPC CHRP bindings
167                                  * and instead will brute-force boot the first valid partition it finds with a
168                                  * type of either "Apple_Boot", "Apple_HFS" or "DOS_FAT_". Here we store the id
169                                  * of the first partition that matches these criteria to use as a fallback later
170                                  * if required. */
171                                 
172                                 if (apple_parnum == -1 &&
173                                     (strcmp(par.pmPartType, "Apple_Boot") == 0 || 
174                                     strcmp(par.pmPartType, "Apple_Bootstrap") == 0 || 
175                                     strcmp(par.pmPartType, "Apple_HFS") == 0 ||
176                                     strcmp(par.pmPartType, "DOS_FAT_") == 0)) {
177                                         apple_parnum = i;
178                                         
179                                         DPRINTF("Located Apple OF fallback partition %d\n", apple_parnum);
180                                 }
181                                 
182                                 /* If the partition is also bootable and the pmProcessor field matches "PowerPC" (insensitive
183                                  * match), then according to the CHRP bindings this is our chosen partition */
184                                 for (j = 0; j < strlen(par.pmProcessor); j++) {
185                                     par.pmProcessor[j] = tolower(par.pmProcessor[j]);
186                                 }                               
187                                 
188                                 if ((__be32_to_cpu(par.pmPartStatus) & kPartitionAUXIsBootValid) &&
189                                     strcmp(par.pmProcessor, "powerpc") == 0) {
190                                     parnum = i;
191                                 
192                                     DPRINTF("Located CHRP-compliant boot partition %d\n", parnum);
193                                 }
194                         }
195                 }
196                 
197                 /* If we found a valid CHRP partition, add it to the list */
198                 if (parnum > 0) {
199                     parlist[parlist_size++] = parnum;
200                 }
201
202                 /* If we found an Apple OF fallback partition, add it to the list */
203                 if (apple_parnum > 0 && apple_parnum != parnum) {
204                     parlist[parlist_size++] = apple_parnum;
205                 }
206                 
207         } else {
208                 /* Another partition was explicitly requested */
209                 parlist[parlist_size++] = parnum;
210                 
211                 DPRINTF("Partition %d explicitly requested\n", parnum);
212         }
213
214         /* Attempt to use our CHRP partition, optionally followed by our Apple OF fallback partition */
215         for (j = 0; j < parlist_size; j++) {
216         
217             /* Make sure our partition is valid */
218             parnum = parlist[j];
219             
220             DPRINTF("Selected partition %d\n", parnum);
221             
222             SEEK( bs * parnum );
223             READ( &par, sizeof(par) );  
224
225             if(! ((__be32_to_cpu(par.pmPartStatus) & kPartitionAUXIsValid) &&
226                         (__be32_to_cpu(par.pmPartStatus) & kPartitionAUXIsAllocated) &&
227                         (__be32_to_cpu(par.pmPartStatus) & kPartitionAUXIsReadable)) ) {
228                 DPRINTF("Partition %d is not valid, allocated and readable\n", parnum);
229                 goto out;
230             }
231             
232             ret = -1;
233
234             offs = (long long)__be32_to_cpu(par.pmPyPartStart) * bs;
235             size = (long long)__be32_to_cpu(par.pmPartBlkCnt) * bs;     
236             
237             if (want_bootcode) {
238                     
239                 /* If size == 0 then fail because we requested bootcode but it doesn't exist */
240                 size = (long long)__be32_to_cpu(par.pmBootSize);
241                 if (!size) {
242                     ret = 0;
243                     goto out;
244                 }
245
246                 /* Adjust seek position so 0 = start of bootcode */
247                 offs += (long long)__be32_to_cpu(par.pmLgBootStart) * bs;
248
249                 di->bootcode_addr = __be32_to_cpu(par.pmBootLoad);
250                 di->bootcode_entry = __be32_to_cpu(par.pmBootEntry);
251             }
252             
253             di->blocksize = (unsigned int)bs;   
254             
255             di->offs_hi = offs >> BITS;
256             di->offs_lo = offs & (ucell) -1;
257
258             di->size_hi = size >> BITS;
259             di->size_lo = size & (ucell) -1;
260
261             /* If we're trying to execute bootcode then we're all done */
262             if (want_bootcode) {
263                 goto out;
264             }
265
266             /* We have a valid partition - so probe for a filesystem at the current offset */
267             DPRINTF("mac-parts: about to probe for fs\n");
268             DPUSH( offs );
269             PUSH_ih( my_parent() );
270             parword("find-filesystem");
271             DPRINTF("mac-parts: done fs probe\n");
272
273             ph = POP_ph();
274             if( ph ) {
275                     DPRINTF("mac-parts: filesystem found on partition %d with ph " FMT_ucellx " and args %s\n", parnum, ph, argstr);
276                     di->filesystem_ph = ph;
277                     
278                     /* In case no partition was specified, set a special selected-partition-args property
279                        giving the device parameters that we can use to generate bootpath */
280                     tmpstr = malloc(strlen(argstr) + 2 + 1);
281                     if (strlen(argstr)) {
282                         sprintf(tmpstr, "%d,%s", parnum, argstr);
283                     } else {
284                         sprintf(tmpstr, "%d", parnum);
285                     }
286
287                     push_str(tmpstr);
288                     feval("strdup encode-string \" selected-partition-args\" property");
289
290                     free(tmpstr);
291                 
292                     /* If we have been asked to open a particular file, interpose the filesystem package with 
293                     the passed filename as an argument */
294                     if (strlen(argstr)) {
295                             push_str( argstr );
296                             PUSH_ph( ph );
297                             fword("interpose");
298                     }
299                     
300                     goto out;
301             } else {
302                     DPRINTF("mac-parts: no filesystem found on partition %d; bypassing misc-files interpose\n", parnum);
303                     
304                     /* Here we have a valid partition; however if we tried to pass in a file argument for a
305                        partition that doesn't contain a filesystem, then we must fail */
306                     if (strlen(argstr)) {
307                         ret = 0;
308                     }
309             }
310         }
311             
312         free( str );
313
314 out:
315         PUSH( ret );
316 }
317
318 /* ( block0 -- flag? ) */
319 static void
320 macparts_probe( macparts_info_t *dummy )
321 {
322         desc_map_t *dmap = (desc_map_t*)cell2pointer(POP());
323
324         DPRINTF("macparts_probe %x ?= %x\n", dmap->sbSig, DESC_MAP_SIGNATURE);
325         if( __be16_to_cpu(dmap->sbSig) != DESC_MAP_SIGNATURE )
326                 RET(0);
327         RET(-1);
328 }
329
330 /* ( -- type offset.d size.d ) */
331 static void
332 macparts_get_info( macparts_info_t *di )
333 {
334         DPRINTF("macparts_get_info");
335
336         PUSH( -1 );             /* no type */
337         PUSH( di->offs_lo );
338         PUSH( di->offs_hi );
339         PUSH( di->size_lo );
340         PUSH( di->size_hi );
341 }
342
343 /* ( -- size entry addr ) */
344 static void
345 macparts_get_bootcode_info( macparts_info_t *di )
346 {
347         DPRINTF("macparts_get_bootcode_info");
348
349         PUSH( di->size_lo );
350         PUSH( di->bootcode_entry );
351         PUSH( di->bootcode_addr );
352 }
353
354 static void
355 macparts_block_size( macparts_info_t *di )
356 {
357         DPRINTF("macparts_block_size = %x\n", di->blocksize);
358         PUSH(di->blocksize);
359 }
360
361 static void
362 macparts_initialize( macparts_info_t *di )
363 {
364         fword("register-partition-package");
365 }
366
367 /* ( pos.d -- status ) */
368 static void
369 macparts_seek(macparts_info_t *di )
370 {
371         long long pos = DPOP();
372         long long offs, size;
373
374         DPRINTF("macparts_seek %llx:\n", pos);
375
376         /* Seek is invalid if we reach the end of the device */
377         size = ((ducell)di->size_hi << BITS) | di->size_lo;
378         if (pos > size)
379                 RET( -1 );
380
381         /* Calculate the seek offset for the parent */
382         offs = ((ducell)di->offs_hi << BITS) | di->offs_lo;
383         offs += pos;
384         DPUSH(offs);
385
386         DPRINTF("macparts_seek parent offset %llx:\n", offs);
387
388         call_package(di->seek_xt, my_parent());
389 }
390
391 /* ( buf len -- actlen ) */
392 static void
393 macparts_read(macparts_info_t *di )
394 {
395         DPRINTF("macparts_read\n");
396
397         /* Pass the read back up to the parent */
398         call_package(di->read_xt, my_parent());
399 }
400
401 /* ( addr -- size ) */
402 static void
403 macparts_load( __attribute__((unused))macparts_info_t *di )
404 {
405         /* Invoke the loader */
406         load(my_self());
407 }
408
409 /* ( pathstr len -- ) */
410 static void
411 macparts_dir( macparts_info_t *di )
412 {
413         /* On PPC Mac, the first partition chosen according to the CHRP boot
414         specification (i.e. marked as bootable) may not necessarily contain 
415         a valid FS */
416         if ( di->filesystem_ph ) {
417                 PUSH( my_self() );
418                 push_str("dir");
419                 PUSH( di->filesystem_ph );
420                 fword("find-method");
421                 POP();
422                 fword("execute");
423         } else {
424                 forth_printf("mac-parts: Unable to determine filesystem\n");
425                 POP();
426                 POP();
427         }
428 }
429
430 NODE_METHODS( macparts ) = {
431         { "probe",              macparts_probe                  },
432         { "open",               macparts_open                   },
433         { "seek",               macparts_seek                   },
434         { "read",               macparts_read                   },
435         { "load",               macparts_load                   },
436         { "dir",                macparts_dir                    },
437         { "get-info",           macparts_get_info               },
438         { "get-bootcode-info",  macparts_get_bootcode_info      },
439         { "block-size",         macparts_block_size             },
440         { NULL,                 macparts_initialize             },
441 };
442
443 void
444 macparts_init( void )
445 {
446         REGISTER_NODE( macparts );
447 }