1 /*
2  *   Sun (Sparc32/64) 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_SUN_PARTS
23 
24 #ifdef DEBUG_SUN_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 	int		type;
36 	phandle_t	filesystem_ph;
37 } sunparts_info_t;
38 
39 DECLARE_NODE( sunparts, INSTALL_OPEN, sizeof(sunparts_info_t), "+/packages/sun-parts" );
40 
41 #define SEEK( pos )		({ DPUSH(pos); call_parent(di->seek_xt); POP(); })
42 #define READ( buf, size )	({ PUSH((ucell)buf); PUSH(size); call_parent(di->read_xt); POP(); })
43 
44 /* Layout of SUN partition table */
45 struct sun_disklabel {
46     uint8_t info[128];   /* Informative text string */
47     uint8_t spare0[14];
48     struct sun_info {
49         uint16_t id;
50         uint16_t flags;
51     } infos[8];
52     uint8_t spare[246];  /* Boot information etc. */
53     uint16_t rspeed;     /* Disk rotational speed */
54     uint16_t pcylcount;  /* Physical cylinder count */
55     uint16_t sparecyl;   /* extra sects per cylinder */
56     uint8_t spare2[4];   /* More magic... */
57     uint16_t ilfact;     /* Interleave factor */
58     uint16_t ncyl;       /* Data cylinder count */
59     uint16_t nacyl;      /* Alt. cylinder count */
60     uint16_t ntrks;      /* Tracks per cylinder */
61     uint16_t nsect;      /* Sectors per track */
62     uint8_t spare3[4];   /* Even more magic... */
63     struct sun_partition {
64         uint32_t start_cylinder;
65         uint32_t num_sectors;
66     } partitions[8];
67     uint16_t magic;      /* Magic number */
68     uint16_t csum;       /* Label xor'd checksum */
69 };
70 
71 /* two helper functions */
72 
73 static inline int
has_sun_part_magic(unsigned char * sect)74 has_sun_part_magic(unsigned char *sect)
75 {
76     struct sun_disklabel *p = (struct sun_disklabel *)sect;
77     uint16_t csum, *ush, tmp16;
78 
79     if (__be16_to_cpu(p->magic) != 0xDABE)
80         return 0;
81 
82     csum = 0;
83     for (ush = (uint16_t *)p; ush < (uint16_t *)(p + 1); ush++) {
84         tmp16 = __be16_to_cpu(*ush);
85 	csum ^= tmp16;
86     }
87     return csum == 0;
88 }
89 
90 /* ( open -- flag ) */
91 static void
sunparts_open(sunparts_info_t * di)92 sunparts_open( sunparts_info_t *di )
93 {
94 	char *str = my_args_copy();
95         char *argstr = NULL;
96         char *parstr = NULL;
97 	int parnum = -1;
98 	unsigned char buf[512];
99         struct sun_disklabel *p;
100         unsigned int i, bs;
101         ducell offs, size;
102 	phandle_t ph;
103 
104 	DPRINTF("sunparts_open '%s'\n", str );
105 
106 	/*
107 		Arguments that we accept:
108 		id: [0-7] | [a-h]
109 		[(id)][,][filespec]
110 	*/
111 
112 	if ( str && strlen(str) ) {
113 		/* Detect the arguments */
114 		if ((*str >= '0' && *str <= '9') || (*str >= 'a' && *str < ('a' + 8)) || (*str == ',')) {
115 		    push_str(str);
116 		    PUSH(',');
117 		    fword("left-parse-string");
118 		    parstr = pop_fstr_copy();
119 		    argstr = pop_fstr_copy();
120 		} else {
121 		    argstr = str;
122 		}
123 
124 		/* Convert the id to a partition number */
125 		if (parstr && strlen(parstr)) {
126 		    if (parstr[0] >= 'a' && parstr[0] < ('a' + 8))
127 			parnum = parstr[0] - 'a';
128 		    else
129 			parnum = atol(parstr);
130 		}
131 	}
132 
133 	/* Make sure argstr is not null */
134 	if (argstr == NULL)
135 	    argstr = strdup("");
136 
137 	DPRINTF("parstr: %s  argstr: %s  parnum: %d\n", parstr, argstr, parnum);
138 
139 	di->filesystem_ph = 0;
140 	di->read_xt = find_parent_method("read");
141 	di->seek_xt = find_parent_method("seek");
142 
143 	SEEK( 0 );
144         if (READ(buf, 512) != 512) {
145                 free(str);
146 		RET(0);
147         }
148 
149 	/* Check Magic */
150 	if (!has_sun_part_magic(buf)) {
151 		DPRINTF("Sun partition magic not found.\n");
152                 free(str);
153 		RET(0);
154 	}
155 
156 	bs = 512;
157 	/* get partition data */
158 	p = (struct sun_disklabel *)buf;
159 
160         for (i = 0; i < 8; i++) {
161             DPRINTF("%c: %d + %d, id %x, flags %x\n", 'a' + i,
162                     __be32_to_cpu(p->partitions[i].start_cylinder),
163                     __be32_to_cpu(p->partitions[i].num_sectors),
164                     __be16_to_cpu(p->infos[i].id),
165                     __be16_to_cpu(p->infos[i].flags));
166         }
167 
168         if (parnum < 0)
169             parnum = 0;
170 
171 	DPRINTF("Selected partition %d\n", parnum);
172 
173         offs = (long long)__be32_to_cpu(p->partitions[parnum].start_cylinder) *
174             __be16_to_cpu(p->ntrks) * __be16_to_cpu(p->nsect) * bs;
175 
176         di->offs_hi = offs >> BITS;
177         di->offs_lo = offs & (ucell) -1;
178         size = (long long)__be32_to_cpu(p->partitions[parnum].num_sectors) * bs;
179         if (size == 0) {
180                 DPRINTF("Partition size is 0, exiting\n");
181                 free(str);
182                 RET(0);
183         }
184         di->size_hi = size >> BITS;
185         di->size_lo = size & (ucell) -1;
186         di->type = __be16_to_cpu(p->infos[parnum].id);
187 
188         DPRINTF("Found Sun partition, offs %lld size %lld\n",
189                 (long long)offs, (long long)size);
190 
191 	/* Probe for filesystem at current offset */
192 	DPRINTF("sun-parts: about to probe for fs\n");
193 	DPUSH( offs );
194 	PUSH_ih( my_parent() );
195 	parword("find-filesystem");
196 	DPRINTF("sun-parts: done fs probe\n");
197 
198 	ph = POP_ph();
199 	if( ph ) {
200 		DPRINTF("sun-parts: filesystem found with ph " FMT_ucellx " and args %s\n", ph, argstr);
201 		di->filesystem_ph = ph;
202 
203 		/* If we have been asked to open a particular file, interpose the filesystem package with
204 		   the passed filename as an argument */
205                 if (argstr && strlen(argstr)) {
206 			push_str( argstr );
207 			PUSH_ph( ph );
208 			fword("interpose");
209 		}
210 	} else {
211 		DPRINTF("sun-parts: no filesystem found; bypassing misc-files interpose\n");
212 
213 		/* Solaris Fcode boot blocks assume that the disk-label package will always
214 		   automatically interpose the "ufs-file-system" package if it exists! We
215 		   need to mimic this behaviour in order for the boot to work. */
216 		push_str("ufs-file-system");
217 		feval("find-package");
218 		ph = POP_ph();
219 
220                 if (argstr && strlen(argstr) && ph) {
221 			ph = POP_ph();
222 			push_str(argstr);
223 			PUSH_ph(ph);
224 			fword("interpose");
225 		}
226 	}
227 
228 	free( str );
229         RET( -1 );
230 }
231 
232 /* ( block0 -- flag? ) */
233 static void
sunparts_probe(sunparts_info_t * dummy)234 sunparts_probe( __attribute__((unused))sunparts_info_t *dummy )
235 {
236 	unsigned char *buf = (unsigned char *)POP();
237 
238 	DPRINTF("probing for Sun partitions\n");
239 
240 	RET ( has_sun_part_magic(buf) );
241 }
242 
243 /* ( -- type offset.d size.d ) */
244 static void
sunparts_get_info(sunparts_info_t * di)245 sunparts_get_info( sunparts_info_t *di )
246 {
247 	DPRINTF("Sun get_info\n");
248 	PUSH( di->type );
249 	PUSH( di->offs_lo );
250 	PUSH( di->offs_hi );
251 	PUSH( di->size_lo );
252 	PUSH( di->size_hi );
253 }
254 
255 static void
sunparts_block_size(sunparts_info_t * di)256 sunparts_block_size( __attribute__((unused))sunparts_info_t *di )
257 {
258 	PUSH(512);
259 }
260 
261 static void
sunparts_initialize(sunparts_info_t * di)262 sunparts_initialize( __attribute__((unused))sunparts_info_t *di )
263 {
264 	fword("register-partition-package");
265 }
266 
267 /* ( pos.d -- status ) */
268 static void
sunparts_seek(sunparts_info_t * di)269 sunparts_seek(sunparts_info_t *di )
270 {
271 	long long pos = DPOP();
272 	long long offs, size;;
273 
274 	DPRINTF("sunparts_seek %llx:\n", pos);
275 
276 	/* Seek is invalid if we reach the end of the device */
277 	size = ((ducell)di->size_hi << BITS) | di->size_lo;
278 	if (pos > size)
279 		RET( -1 );
280 
281 	/* Calculate the seek offset for the parent */
282 	offs = ((ducell)di->offs_hi << BITS) | di->offs_lo;
283 	offs += pos;
284 	DPUSH(offs);
285 
286 	DPRINTF("sunparts_seek parent offset %llx:\n", offs);
287 
288 	call_package(di->seek_xt, my_parent());
289 }
290 
291 /* ( buf len -- actlen ) */
292 static void
sunparts_read(sunparts_info_t * di)293 sunparts_read(sunparts_info_t *di )
294 {
295 	DPRINTF("sunparts_read\n");
296 
297 	/* Pass the read back up to the parent */
298 	call_package(di->read_xt, my_parent());
299 }
300 
301 /* ( addr -- size ) */
302 static void
sunparts_load(sunparts_info_t * di)303 sunparts_load( __attribute__((unused))sunparts_info_t *di )
304 {
305 	/* Invoke the loader */
306 	load(my_self());
307 }
308 
309 /* ( pathstr len -- ) */
310 static void
sunparts_dir(sunparts_info_t * di)311 sunparts_dir( sunparts_info_t *di )
312 {
313 	if ( di->filesystem_ph) {
314 		PUSH( my_self() );
315 		push_str("dir");
316 		PUSH( di->filesystem_ph );
317 		fword("find-method");
318 		POP();
319 		fword("execute");
320 	} else {
321 		forth_printf("sun-parts: Unable to determine filesystem\n");
322 		POP();
323 		POP();
324 	}
325 }
326 
327 NODE_METHODS( sunparts ) = {
328 	{ "probe",	sunparts_probe 		},
329 	{ "open",	sunparts_open 		},
330 	{ "get-info",	sunparts_get_info 	},
331 	{ "block-size",	sunparts_block_size 	},
332 	{ "seek",	sunparts_seek 		},
333 	{ "read",	sunparts_read 		},
334 	{ "load",	sunparts_load	 	},
335 	{ "dir",	sunparts_dir 		},
336 	{ NULL,		sunparts_initialize	},
337 };
338 
339 void
sunparts_init(void)340 sunparts_init( void )
341 {
342 	REGISTER_NODE( sunparts );
343 }
344