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 
has_pc_valid_partition(unsigned char * sect)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 
has_pc_part_magic(unsigned char * sect)51 static inline int has_pc_part_magic(unsigned char *sect)
52 {
53 	return sect[0x1fe]==0x55 && sect[0x1ff]==0xAA;
54 }
55 
is_pc_extended_part(unsigned char type)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
pcparts_open(pcparts_info_t * di)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 		/* If PReP boot partition, exit immediately with no filesystem probe */
168 		if (p->type == 0x41) {
169 			RET(-1);
170 		}
171 
172 		found = 1;
173 	} else {
174 		/* Extended partition */
175 		int i, cur_part;
176 		unsigned long ext_start, cur_table;
177 
178 		/* Search for the extended partition
179 		 * which contains logical partitions */
180 		for (i = 0; i < 4; i++) {
181 			if (is_pc_extended_part(p[i].type))
182 				break;
183 		}
184 
185 		if (i >= 4) {
186 			DPRINTF("Extended partition not found\n");
187 			RET( 0 );
188 		}
189 
190 		DPRINTF("Extended partition at %d\n", i+1);
191 
192 		/* Visit each logical partition labels */
193 		ext_start = __le32_to_cpu(p[i].start_sect);
194 		cur_table = ext_start;
195 		cur_part = 4;
196 
197 		while (cur_part <= parnum) {
198 			DPRINTF("cur_part=%d at %lx\n", cur_part, cur_table);
199 
200 			SEEK( cur_table * bs );
201 			if( READ(buf, sizeof(512)) != sizeof(512) )
202 				RET( 0 );
203 
204 			if (!has_pc_part_magic(buf)) {
205 				DPRINTF("Extended partition has no magic\n");
206 				break;
207 			}
208 
209 			/* Read the extended partition, making sure we are aligned again */
210 			partition = (struct pc_partition *) (buf + 0x1be);
211 			memcpy(p, partition, sizeof(struct pc_partition));
212 
213 			/* First entry is the logical partition */
214 			if (cur_part == parnum) {
215 				if (p->type == 0) {
216 					DPRINTF("Partition %d is empty\n", parnum+1);
217 					RET( 0 );
218 				}
219 
220 				offs = (long long)(cur_table+__le32_to_cpu(p->start_sect)) * bs;
221 				di->offs_hi = offs >> BITS;
222 				di->offs_lo = offs & (ucell) -1;
223 
224 				size = (long long)__le32_to_cpu(p->nr_sects) * bs;
225 				di->size_hi = size >> BITS;
226 				di->size_lo = size & (ucell) -1;
227 
228 				/* If PReP boot partition, exit immediately with no filesystem probe */
229 				if (p->type == 0x41) {
230 					RET(-1);
231 				}
232 
233 				found = 1;
234 				break;
235 			}
236 
237 			/* Second entry is link to next partition */
238 			if (!is_pc_extended_part(p[1].type)) {
239 				DPRINTF("no link\n");
240 				break;
241 			}
242 
243 			cur_table = ext_start + __le32_to_cpu(p[1].start_sect);
244 			cur_part++;
245 		}
246 
247 		if (!found) {
248 			DPRINTF("Logical partition %d does not exist\n", parnum+1);
249 			RET( 0 );
250 		}
251 	}
252 
253 	free(p);
254 
255 	if (found) {
256 		/* We have a valid partition - so probe for a filesystem at the current offset */
257 		DPRINTF("pc-parts: about to probe for fs\n");
258 		DPUSH( offs );
259 		PUSH_ih( my_parent() );
260 		parword("find-filesystem");
261 		DPRINTF("pc-parts: done fs probe\n");
262 
263 		ph = POP_ph();
264 		if( ph ) {
265 			DPRINTF("pc-parts: filesystem found with ph " FMT_ucellx " and args %s\n", ph, argstr);
266 			di->filesystem_ph = ph;
267 
268 			/* If we have been asked to open a particular file, interpose the filesystem package with
269 			the passed filename as an argument */
270 			if (strlen(argstr)) {
271 				push_str( argstr );
272 				PUSH_ph( ph );
273 				fword("interpose");
274 			}
275 		} else {
276 			DPRINTF("pc-parts: no filesystem found; bypassing misc-files interpose\n");
277 		}
278 
279 		free( str );
280 		RET( -1 );
281 	} else {
282 		DPRINTF("pc-parts: unable to locate partition\n");
283 
284 		free( str );
285 		RET( 0 );
286 	}
287 }
288 
289 /* ( block0 -- flag? ) */
290 static void
pcparts_probe(pcparts_info_t * dummy)291 pcparts_probe( pcparts_info_t *dummy )
292 {
293 	unsigned char *buf = (unsigned char *)cell2pointer(POP());
294 
295 	DPRINTF("probing for PC partitions\n");
296 
297 	/* We also check that at least one valid partition exists; this is because
298 	some CDs seem broken in that they have a partition table but it is empty
299 	e.g. MorphOS. */
300 	RET ( has_pc_part_magic(buf) && has_pc_valid_partition(buf) );
301 }
302 
303 /* ( -- type offset.d size.d ) */
304 static void
pcparts_get_info(pcparts_info_t * di)305 pcparts_get_info( pcparts_info_t *di )
306 {
307 	DPRINTF("PC get_info\n");
308 	PUSH( -1 );		/* no type */
309 	PUSH( di->offs_lo );
310 	PUSH( di->offs_hi );
311 	PUSH( di->size_lo );
312 	PUSH( di->size_hi );
313 }
314 
315 static void
pcparts_block_size(pcparts_info_t * di)316 pcparts_block_size( __attribute__((unused))pcparts_info_t *di )
317 {
318 	PUSH(512);
319 }
320 
321 static void
pcparts_initialize(pcparts_info_t * di)322 pcparts_initialize( pcparts_info_t *di )
323 {
324 	fword("register-partition-package");
325 }
326 
327 /* ( pos.d -- status ) */
328 static void
pcparts_seek(pcparts_info_t * di)329 pcparts_seek(pcparts_info_t *di )
330 {
331 	long long pos = DPOP();
332 	long long offs, size;
333 
334 	DPRINTF("pcparts_seek %llx:\n", pos);
335 
336 	/* Seek is invalid if we reach the end of the device */
337 	size = ((ducell)di->size_hi << BITS) | di->size_lo;
338 	if (pos > size)
339 		RET( -1 );
340 
341 	/* Calculate the seek offset for the parent */
342 	offs = ((ducell)di->offs_hi << BITS) | di->offs_lo;
343 	offs += pos;
344 	DPUSH(offs);
345 
346 	DPRINTF("pcparts_seek parent offset %llx:\n", offs);
347 
348 	call_package(di->seek_xt, my_parent());
349 }
350 
351 /* ( buf len -- actlen ) */
352 static void
pcparts_read(pcparts_info_t * di)353 pcparts_read(pcparts_info_t *di )
354 {
355 	DPRINTF("pcparts_read\n");
356 
357 	/* Pass the read back up to the parent */
358 	call_package(di->read_xt, my_parent());
359 }
360 
361 /* ( addr -- size ) */
362 static void
pcparts_load(pcparts_info_t * di)363 pcparts_load( __attribute__((unused))pcparts_info_t *di )
364 {
365 	/* Invoke the loader */
366 	load(my_self());
367 }
368 
369 /* ( pathstr len -- ) */
370 static void
pcparts_dir(pcparts_info_t * di)371 pcparts_dir( pcparts_info_t *di )
372 {
373 	if ( di->filesystem_ph ) {
374 		PUSH( my_self() );
375 		push_str("dir");
376 		PUSH( di->filesystem_ph );
377 		fword("find-method");
378 		POP();
379 		fword("execute");
380 	} else {
381 		forth_printf("pc-parts: Unable to determine filesystem\n");
382 		POP();
383 		POP();
384 	}
385 }
386 
387 NODE_METHODS( pcparts ) = {
388 	{ "probe",	pcparts_probe 		},
389 	{ "open",	pcparts_open 		},
390 	{ "seek",	pcparts_seek 		},
391 	{ "read",	pcparts_read 		},
392 	{ "load",	pcparts_load 		},
393 	{ "dir",	pcparts_dir 		},
394 	{ "get-info",	pcparts_get_info 	},
395 	{ "block-size",	pcparts_block_size 	},
396 	{ NULL,		pcparts_initialize	},
397 };
398 
399 void
pcparts_init(void)400 pcparts_init( void )
401 {
402 	REGISTER_NODE( pcparts );
403 }
404