Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openbios / packages / sun-parts.c
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
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
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
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
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
256 sunparts_block_size( __attribute__((unused))sunparts_info_t *di )
257 {
258         PUSH(512);
259 }
260
261 static void
262 sunparts_initialize( __attribute__((unused))sunparts_info_t *di )
263 {
264         fword("register-partition-package");
265 }
266
267 /* ( pos.d -- status ) */
268 static void
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
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
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
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
340 sunparts_init( void )
341 {
342         REGISTER_NODE( sunparts );
343 }