Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / packages / pc-parts.c
1 /*
2  *   pc partition support
3  *
4  *   Copyright (C) 2004 Stefan Reinauer
5  *
6  *   This code is based (and copied in many places) from
7  *   mac partition support by Samuel Rydh (samuel@ibrium.se)
8  *
9  *   This program is free software; you can redistribute it and/or
10  *   modify it under the terms of the GNU General Public License
11  *   version 2
12  *
13  */
14
15 #include "config.h"
16 #include "libopenbios/bindings.h"
17 #include "libopenbios/load.h"
18 #include "libc/byteorder.h"
19 #include "libc/vsprintf.h"
20 #include "packages.h"
21
22 //#define DEBUG_PC_PARTS
23
24 #ifdef DEBUG_PC_PARTS
25 #define DPRINTF(fmt, args...)                   \
26     do { printk(fmt , ##args); } while (0)
27 #else
28 #define DPRINTF(fmt, args...)
29 #endif
30
31 typedef struct {
32         xt_t            seek_xt, read_xt;
33         ucell           offs_hi, offs_lo;
34         ucell           size_hi, size_lo;
35         phandle_t       filesystem_ph;
36 } pcparts_info_t;
37
38 DECLARE_NODE( pcparts, INSTALL_OPEN, sizeof(pcparts_info_t), "+/packages/pc-parts" );
39
40 #define SEEK( pos )             ({ DPUSH(pos); call_parent(di->seek_xt); POP(); })
41 #define READ( buf, size )       ({ PUSH(pointer2cell(buf)); PUSH(size); call_parent(di->read_xt); POP(); })
42
43 /* three helper functions */
44
45 static inline int has_pc_valid_partition(unsigned char *sect)
46 {
47         /* Make sure the partition table contains at least one valid entry */
48         return (sect[0x1c2] != 0 || sect[0x1d2] != 0 || sect[0x1e2] != 0);
49 }
50
51 static inline int has_pc_part_magic(unsigned char *sect)
52 {
53         return sect[0x1fe]==0x55 && sect[0x1ff]==0xAA;
54 }
55
56 static inline int is_pc_extended_part(unsigned char type)
57 {
58         return type==5 || type==0xf || type==0x85;
59 }
60
61 /* ( open -- flag ) */
62 static void
63 pcparts_open( pcparts_info_t *di )
64 {
65         char *str = my_args_copy();
66         char *argstr = strdup("");
67         char *parstr = strdup("");
68         int bs, parnum=-1;
69         int found = 0;
70         phandle_t ph;
71         ducell offs, size;
72
73         /* Layout of PC partition table */
74         struct pc_partition {
75                 unsigned char boot;
76                 unsigned char head;
77                 unsigned char sector;
78                 unsigned char cyl;
79                 unsigned char type;
80                 unsigned char e_head;
81                 unsigned char e_sector;
82                 unsigned char e_cyl;
83                 u32 start_sect; /* unaligned little endian */
84                 u32 nr_sects; /* ditto */
85         } *p, *partition;
86
87         unsigned char buf[512];
88
89         DPRINTF("pcparts_open '%s'\n", str );
90
91         /* 
92                 Arguments that we accept:
93                 id: [0-7]
94                 [(id)][,][filespec]
95         */
96
97         if ( strlen(str) ) {
98                 /* Detect the arguments */
99                 if ((*str >= '0' && *str <= '7') || (*str == ',')) {
100                     push_str(str);
101                     PUSH(',');
102                     fword("left-parse-string");
103                     parstr = pop_fstr_copy();
104                     argstr = pop_fstr_copy();
105                 } else {
106                     argstr = str;
107                 }
108                         
109                 /* Convert the id to a partition number */
110                 if (parstr && strlen(parstr))
111                     parnum = atol(parstr);
112         }
113
114         /* Make sure argstr is not null */
115         if (argstr == NULL)
116             argstr = strdup("");
117         
118         DPRINTF("parstr: %s  argstr: %s  parnum: %d\n", parstr, argstr, parnum);
119         free(parstr);
120
121         if( parnum < 0 )
122                 parnum = 0;
123
124         di->filesystem_ph = 0;
125         di->read_xt = find_parent_method("read");
126         di->seek_xt = find_parent_method("seek");
127
128         SEEK( 0 );
129         if( READ(buf, 512) != 512 )
130                 RET(0);
131
132         /* Check Magic */
133         if (!has_pc_part_magic(buf)) {
134                 DPRINTF("pc partition magic not found.\n");
135                 RET(0);
136         }
137
138         /* Actual partition data */
139         partition = (struct pc_partition *) (buf + 0x1be);
140
141         /* Make sure we use a copy accessible from an aligned pointer (some archs
142            e.g. SPARC will crash otherwise) */
143         p = malloc(sizeof(struct pc_partition));
144
145         bs = 512;
146
147         if (parnum < 4) {
148                 /* primary partition */
149                 partition += parnum;
150                 memcpy(p, partition, sizeof(struct pc_partition));
151
152                 if (p->type == 0 || is_pc_extended_part(p->type)) {
153                         DPRINTF("partition %d does not exist\n", parnum+1 );
154                         RET( 0 );
155                 }
156
157                 offs = (long long)(__le32_to_cpu(p->start_sect)) * bs;
158                 di->offs_hi = offs >> BITS;
159                 di->offs_lo = offs & (ucell) -1;
160
161                 size = (long long)(__le32_to_cpu(p->nr_sects)) * bs;
162                 di->size_hi = size >> BITS;
163                 di->size_lo = size & (ucell) -1;
164
165                 DPRINTF("Primary partition at sector %x\n", __le32_to_cpu(p->start_sect));
166
167                 found = 1;
168         } else {
169                 /* Extended partition */
170                 int i, cur_part;
171                 unsigned long ext_start, cur_table;
172
173                 /* Search for the extended partition
174                  * which contains logical partitions */
175                 for (i = 0; i < 4; i++) {
176                         if (is_pc_extended_part(p[i].type))
177                                 break;
178                 }
179
180                 if (i >= 4) {
181                         DPRINTF("Extended partition not found\n");
182                         RET( 0 );
183                 }
184
185                 DPRINTF("Extended partition at %d\n", i+1);
186
187                 /* Visit each logical partition labels */
188                 ext_start = __le32_to_cpu(p[i].start_sect);
189                 cur_table = ext_start;
190                 cur_part = 4;
191
192                 while (cur_part <= parnum) {
193                         DPRINTF("cur_part=%d at %lx\n", cur_part, cur_table);
194
195                         SEEK( cur_table * bs );
196                         if( READ(buf, sizeof(512)) != sizeof(512) )
197                                 RET( 0 );
198
199                         if (!has_pc_part_magic(buf)) {
200                                 DPRINTF("Extended partition has no magic\n");
201                                 break;
202                         }
203
204                         /* Read the extended partition, making sure we are aligned again */
205                         partition = (struct pc_partition *) (buf + 0x1be);
206                         memcpy(p, partition, sizeof(struct pc_partition));
207
208                         /* First entry is the logical partition */
209                         if (cur_part == parnum) {
210                                 if (p->type == 0) {
211                                         DPRINTF("Partition %d is empty\n", parnum+1);
212                                         RET( 0 );
213                                 }
214
215                                 offs = (long long)(cur_table+__le32_to_cpu(p->start_sect)) * bs;
216                                 di->offs_hi = offs >> BITS;
217                                 di->offs_lo = offs & (ucell) -1;
218
219                                 size = (long long)__le32_to_cpu(p->nr_sects) * bs;
220                                 di->size_hi = size >> BITS;
221                                 di->size_lo = size & (ucell) -1;
222
223                                 found = 1;
224                                 break;
225                         }
226
227                         /* Second entry is link to next partition */
228                         if (!is_pc_extended_part(p[1].type)) {
229                                 DPRINTF("no link\n");
230                                 break;
231                         }
232
233                         cur_table = ext_start + __le32_to_cpu(p[1].start_sect);
234                         cur_part++;
235                 }
236
237                 if (!found) {
238                         DPRINTF("Logical partition %d does not exist\n", parnum+1);
239                         RET( 0 );
240                 }
241         }
242         
243         free(p);
244
245         if (found) {
246                 /* We have a valid partition - so probe for a filesystem at the current offset */
247                 DPRINTF("pc-parts: about to probe for fs\n");
248                 DPUSH( offs );
249                 PUSH_ih( my_parent() );
250                 parword("find-filesystem");
251                 DPRINTF("pc-parts: done fs probe\n");
252         
253                 ph = POP_ph();
254                 if( ph ) {
255                         DPRINTF("pc-parts: filesystem found with ph " FMT_ucellx " and args %s\n", ph, argstr);
256                         di->filesystem_ph = ph;
257
258                         /* If we have been asked to open a particular file, interpose the filesystem package with 
259                         the passed filename as an argument */
260                         if (strlen(argstr)) {
261                                 push_str( argstr );
262                                 PUSH_ph( ph );
263                                 fword("interpose");
264                         }
265                 } else {
266                         DPRINTF("pc-parts: no filesystem found; bypassing misc-files interpose\n");
267                 }
268         
269                 free( str );
270                 RET( -1 );
271         } else {
272                 DPRINTF("pc-parts: unable to locate partition\n");
273
274                 free( str );
275                 RET( 0 );
276         }
277 }
278
279 /* ( block0 -- flag? ) */
280 static void
281 pcparts_probe( pcparts_info_t *dummy )
282 {
283         unsigned char *buf = (unsigned char *)cell2pointer(POP());
284
285         DPRINTF("probing for PC partitions\n");
286
287         /* We also check that at least one valid partition exists; this is because
288         some CDs seem broken in that they have a partition table but it is empty
289         e.g. MorphOS. */
290         RET ( has_pc_part_magic(buf) && has_pc_valid_partition(buf) );
291 }
292
293 /* ( -- type offset.d size.d ) */
294 static void
295 pcparts_get_info( pcparts_info_t *di )
296 {
297         DPRINTF("PC get_info\n");
298         PUSH( -1 );             /* no type */
299         PUSH( di->offs_lo );
300         PUSH( di->offs_hi );
301         PUSH( di->size_lo );
302         PUSH( di->size_hi );
303 }
304
305 static void
306 pcparts_block_size( __attribute__((unused))pcparts_info_t *di )
307 {
308         PUSH(512);
309 }
310
311 static void
312 pcparts_initialize( pcparts_info_t *di )
313 {
314         fword("register-partition-package");
315 }
316
317 /* ( pos.d -- status ) */
318 static void
319 pcparts_seek(pcparts_info_t *di )
320 {
321         long long pos = DPOP();
322         long long offs, size;
323
324         DPRINTF("pcparts_seek %llx:\n", pos);
325
326         /* Seek is invalid if we reach the end of the device */
327         size = ((ducell)di->size_hi << BITS) | di->size_lo;
328         if (pos > size)
329                 RET( -1 );
330
331         /* Calculate the seek offset for the parent */
332         offs = ((ducell)di->offs_hi << BITS) | di->offs_lo;
333         offs += pos;
334         DPUSH(offs);
335
336         DPRINTF("pcparts_seek parent offset %llx:\n", offs);
337
338         call_package(di->seek_xt, my_parent());
339 }
340
341 /* ( buf len -- actlen ) */
342 static void
343 pcparts_read(pcparts_info_t *di )
344 {
345         DPRINTF("pcparts_read\n");
346
347         /* Pass the read back up to the parent */
348         call_package(di->read_xt, my_parent());
349 }
350
351 /* ( addr -- size ) */
352 static void
353 pcparts_load( __attribute__((unused))pcparts_info_t *di )
354 {
355         /* Invoke the loader */
356         load(my_self());
357 }
358
359 /* ( pathstr len -- ) */
360 static void
361 pcparts_dir( pcparts_info_t *di )
362 {
363         if ( di->filesystem_ph ) {
364                 PUSH( my_self() );
365                 push_str("dir");
366                 PUSH( di->filesystem_ph );
367                 fword("find-method");
368                 POP();
369                 fword("execute");
370         } else {
371                 forth_printf("pc-parts: Unable to determine filesystem\n");
372                 POP();
373                 POP();
374         }
375 }
376
377 NODE_METHODS( pcparts ) = {
378         { "probe",      pcparts_probe           },
379         { "open",       pcparts_open            },
380         { "seek",       pcparts_seek            },
381         { "read",       pcparts_read            },
382         { "load",       pcparts_load            },
383         { "dir",        pcparts_dir             },
384         { "get-info",   pcparts_get_info        },
385         { "block-size", pcparts_block_size      },
386         { NULL,         pcparts_initialize      },
387 };
388
389 void
390 pcparts_init( void )
391 {
392         REGISTER_NODE( pcparts );
393 }