4 * Copyright (C) 2004 Stefan Reinauer
6 * This code is based (and copied in many places) from
7 * mac partition support by Samuel Rydh (samuel@ibrium.se)
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
16 #include "libopenbios/bindings.h"
17 #include "libopenbios/load.h"
18 #include "libc/byteorder.h"
19 #include "libc/vsprintf.h"
22 //#define DEBUG_PC_PARTS
25 #define DPRINTF(fmt, args...) \
26 do { printk(fmt , ##args); } while (0)
28 #define DPRINTF(fmt, args...)
32 xt_t seek_xt, read_xt;
33 ucell offs_hi, offs_lo;
34 ucell size_hi, size_lo;
35 phandle_t filesystem_ph;
38 DECLARE_NODE( pcparts, INSTALL_OPEN, sizeof(pcparts_info_t), "+/packages/pc-parts" );
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(); })
43 /* three helper functions */
45 static inline int has_pc_valid_partition(unsigned char *sect)
47 /* Make sure the partition table contains at least one valid entry */
48 return (sect[0x1c2] != 0 || sect[0x1d2] != 0 || sect[0x1e2] != 0);
51 static inline int has_pc_part_magic(unsigned char *sect)
53 return sect[0x1fe]==0x55 && sect[0x1ff]==0xAA;
56 static inline int is_pc_extended_part(unsigned char type)
58 return type==5 || type==0xf || type==0x85;
61 /* ( open -- flag ) */
63 pcparts_open( pcparts_info_t *di )
65 char *str = my_args_copy();
66 char *argstr = strdup("");
67 char *parstr = strdup("");
73 /* Layout of PC partition table */
81 unsigned char e_sector;
83 u32 start_sect; /* unaligned little endian */
84 u32 nr_sects; /* ditto */
87 unsigned char buf[512];
89 DPRINTF("pcparts_open '%s'\n", str );
92 Arguments that we accept:
98 /* Detect the arguments */
99 if ((*str >= '0' && *str <= '7') || (*str == ',')) {
102 fword("left-parse-string");
103 parstr = pop_fstr_copy();
104 argstr = pop_fstr_copy();
109 /* Convert the id to a partition number */
110 if (parstr && strlen(parstr))
111 parnum = atol(parstr);
114 /* Make sure argstr is not null */
118 DPRINTF("parstr: %s argstr: %s parnum: %d\n", parstr, argstr, parnum);
124 di->filesystem_ph = 0;
125 di->read_xt = find_parent_method("read");
126 di->seek_xt = find_parent_method("seek");
129 if( READ(buf, 512) != 512 )
133 if (!has_pc_part_magic(buf)) {
134 DPRINTF("pc partition magic not found.\n");
138 /* Actual partition data */
139 partition = (struct pc_partition *) (buf + 0x1be);
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));
148 /* primary partition */
150 memcpy(p, partition, sizeof(struct pc_partition));
152 if (p->type == 0 || is_pc_extended_part(p->type)) {
153 DPRINTF("partition %d does not exist\n", parnum+1 );
157 offs = (long long)(__le32_to_cpu(p->start_sect)) * bs;
158 di->offs_hi = offs >> BITS;
159 di->offs_lo = offs & (ucell) -1;
161 size = (long long)(__le32_to_cpu(p->nr_sects)) * bs;
162 di->size_hi = size >> BITS;
163 di->size_lo = size & (ucell) -1;
165 DPRINTF("Primary partition at sector %x\n", __le32_to_cpu(p->start_sect));
169 /* Extended partition */
171 unsigned long ext_start, cur_table;
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))
181 DPRINTF("Extended partition not found\n");
185 DPRINTF("Extended partition at %d\n", i+1);
187 /* Visit each logical partition labels */
188 ext_start = __le32_to_cpu(p[i].start_sect);
189 cur_table = ext_start;
192 while (cur_part <= parnum) {
193 DPRINTF("cur_part=%d at %lx\n", cur_part, cur_table);
195 SEEK( cur_table * bs );
196 if( READ(buf, sizeof(512)) != sizeof(512) )
199 if (!has_pc_part_magic(buf)) {
200 DPRINTF("Extended partition has no magic\n");
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));
208 /* First entry is the logical partition */
209 if (cur_part == parnum) {
211 DPRINTF("Partition %d is empty\n", parnum+1);
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;
219 size = (long long)__le32_to_cpu(p->nr_sects) * bs;
220 di->size_hi = size >> BITS;
221 di->size_lo = size & (ucell) -1;
227 /* Second entry is link to next partition */
228 if (!is_pc_extended_part(p[1].type)) {
229 DPRINTF("no link\n");
233 cur_table = ext_start + __le32_to_cpu(p[1].start_sect);
238 DPRINTF("Logical partition %d does not exist\n", parnum+1);
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");
249 PUSH_ih( my_parent() );
250 parword("find-filesystem");
251 DPRINTF("pc-parts: done fs probe\n");
255 DPRINTF("pc-parts: filesystem found with ph " FMT_ucellx " and args %s\n", ph, argstr);
256 di->filesystem_ph = ph;
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)) {
266 DPRINTF("pc-parts: no filesystem found; bypassing misc-files interpose\n");
272 DPRINTF("pc-parts: unable to locate partition\n");
279 /* ( block0 -- flag? ) */
281 pcparts_probe( pcparts_info_t *dummy )
283 unsigned char *buf = (unsigned char *)cell2pointer(POP());
285 DPRINTF("probing for PC partitions\n");
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
290 RET ( has_pc_part_magic(buf) && has_pc_valid_partition(buf) );
293 /* ( -- type offset.d size.d ) */
295 pcparts_get_info( pcparts_info_t *di )
297 DPRINTF("PC get_info\n");
298 PUSH( -1 ); /* no type */
306 pcparts_block_size( __attribute__((unused))pcparts_info_t *di )
312 pcparts_initialize( pcparts_info_t *di )
314 fword("register-partition-package");
317 /* ( pos.d -- status ) */
319 pcparts_seek(pcparts_info_t *di )
321 long long pos = DPOP();
322 long long offs, size;
324 DPRINTF("pcparts_seek %llx:\n", pos);
326 /* Seek is invalid if we reach the end of the device */
327 size = ((ducell)di->size_hi << BITS) | di->size_lo;
331 /* Calculate the seek offset for the parent */
332 offs = ((ducell)di->offs_hi << BITS) | di->offs_lo;
336 DPRINTF("pcparts_seek parent offset %llx:\n", offs);
338 call_package(di->seek_xt, my_parent());
341 /* ( buf len -- actlen ) */
343 pcparts_read(pcparts_info_t *di )
345 DPRINTF("pcparts_read\n");
347 /* Pass the read back up to the parent */
348 call_package(di->read_xt, my_parent());
351 /* ( addr -- size ) */
353 pcparts_load( __attribute__((unused))pcparts_info_t *di )
355 /* Invoke the loader */
359 /* ( pathstr len -- ) */
361 pcparts_dir( pcparts_info_t *di )
363 if ( di->filesystem_ph ) {
366 PUSH( di->filesystem_ph );
367 fword("find-method");
371 forth_printf("pc-parts: Unable to determine filesystem\n");
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 },
392 REGISTER_NODE( pcparts );