Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openhackware / src / libexec / chrp.c
1 /*
2  * <chrp.c>
3  *
4  * Open Hack'Ware BIOS CHRP boot file loader
5  * 
6  * Copyright (c) 2004-2005 Jocelyn Mayer
7  * 
8  *   This program is free software; you can redistribute it and/or
9  *   modify it under the terms of the GNU General Public License V2
10  *   as published by the Free Software Foundation
11  *
12  *   This program is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *   GNU General Public License for more details.
16  *
17  *   You should have received a copy of the GNU General Public License
18  *   along with this program; if not, write to the Free Software
19  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <ctype.h>
25 #include "bios.h"
26 #include "exec.h"
27 #include "libfs/libfs.h"
28
29 /* Simple XML parser */
30 typedef struct XML_tag_t XML_tag_t;
31 struct XML_tag_t {
32     unsigned char *name;
33     XML_tag_t *up;
34     int dlen;
35     void *data;
36 };
37
38 enum {
39     CHRP_TAG_UNKNOWN = 0,
40     CHRP_TAG_CHRP_BOOT,
41     CHRP_TAG_COMPATIBLE,
42     CHRP_TAG_DESCRIPTION,
43     CHRP_TAG_BOOT_SCRIPT,
44     CHRP_TAG_OS_BADGE_ICONS,
45     CHRP_TAG_ICON,
46     CHRP_TAG_BITMAP,
47     CHRP_TAG_LICENSE,
48 };
49
50 enum {
51     CHRP_SCRIPT_IGNORE = 0,
52     CHRP_SCRIPT_LOAD_BOOT,
53     CHRP_SCRIPT_EMBEDDED,
54 };
55
56 enum {
57     XML_STATE_OUT = 0,
58     XML_STATE_TAG,
59     XML_STATE_DATA,
60 };
61
62 static int XML_get_type (const unsigned char *name)
63 {
64     int ret;
65
66     if (strcmp(name, "CHRP-BOOT") == 0)
67         ret = CHRP_TAG_CHRP_BOOT;
68     else if (strcmp(name, "COMPATIBLE") == 0)
69         ret = CHRP_TAG_COMPATIBLE;
70     else if (strcmp(name, "DESCRIPTION") == 0)
71         ret = CHRP_TAG_DESCRIPTION;
72     else if (strcmp(name, "BOOT-SCRIPT") == 0)
73         ret = CHRP_TAG_BOOT_SCRIPT;
74     else if (strcmp(name, "OS-BADGE-ICONS") == 0)
75         ret = CHRP_TAG_OS_BADGE_ICONS;
76     else if (strcmp(name, "ICON") == 0)
77         ret = CHRP_TAG_ICON;
78     else if (strcmp(name, "BITMAP") == 0)
79         ret = CHRP_TAG_BITMAP;
80     else if (strcmp(name, "LICENSE") == 0)
81         ret = CHRP_TAG_LICENSE;
82     else
83         ret = CHRP_TAG_UNKNOWN;
84
85     return ret;
86 }
87
88 static unsigned char *strfind (const unsigned char *buf, const unsigned char *str)
89 {
90     const unsigned char *pos;
91     int len = strlen(str);
92
93     //    DPRINTF("Look for '%s' in \n'%s'\n", str, buf);
94     for (pos = buf; *pos != '\0'; pos++) {
95         if (memcmp(pos, str, len) == 0)
96             return (unsigned char *)pos;
97     }
98
99     return NULL;
100 }
101
102 int exec_load_chrp (inode_t *file, void **dest, void **entry, void **end,
103                     uint32_t loffset)
104 {
105 #define TMPNAME_LEN 512
106     unsigned char tmpname[TMPNAME_LEN], *tmpp, *buf, *pos, *endc, c;
107     XML_tag_t *tag, *tmp, *first;
108     part_t *part;
109     inode_t *inode;
110     int state;
111     int script_type = CHRP_SCRIPT_IGNORE;
112     uint32_t crc, offset = 0;
113     int ret, rel = 0;
114
115     buf = malloc(16384);
116     /* Check the file head */
117     file_seek(file, loffset);
118     fs_read(file, buf, 11);
119     if (memcmp(buf, "<CHRP-BOOT>", 11) != 0) {
120         ERROR("Not an Apple CHRP boot file !\n");
121         return -2;
122     }
123     /* Re-seek at start of the file and start parsing it */
124     file_seek(file, loffset);
125     pos = buf;
126     tag = NULL;
127     first = NULL;
128     ret = -1;
129     fs_read(file, &c, 1);
130     offset++;
131     for (state = XML_STATE_TAG; state != XML_STATE_OUT;) {
132         /* Get next char */
133         fs_read(file, &c, 1);
134         offset++;
135         if ((state == XML_STATE_TAG && c != '>') ||
136             (state == XML_STATE_DATA && c != '<')) {
137             *pos++ = c;
138             continue;
139         }
140         *pos++ = '\0';
141         switch (state) {
142         case XML_STATE_TAG:
143             if (*buf == '/') {
144                 if (tag == NULL || strcmp(buf + 1, tag->name) != 0) {
145                     ERROR("XML error: open name: '%s' close name: '%s'\n",
146                           buf + 1, tag->name);
147                     goto out;
148                 }
149                 DPRINTF("Close tag: '%s'\n", tag->name);
150                 switch (XML_get_type(tag->name)) {
151                 case CHRP_TAG_CHRP_BOOT:
152                     /* Nothing to do */
153                     break;
154                 case CHRP_TAG_COMPATIBLE:
155                     /* Won't check... */
156                     pos = tag->data;
157                     if (*(char *)tag->data == 0x0d) {
158                         pos++;
159                     }
160                     DPRINTF("Compatible: '%s'\n", pos);
161                     break;
162                 case CHRP_TAG_DESCRIPTION:
163                     pos = tag->data;
164                     if (*(char *)tag->data == 0x0d) {
165                         pos++;
166                     }
167                     DPRINTF("Description: '%s'\n", pos);
168                     break;
169                 case CHRP_TAG_BOOT_SCRIPT:
170                     /* Here is the interresting part... */
171                     crc = crc32(0, tag->data, tag->dlen);
172 #if 0
173                     DPRINTF("Forth script: %08x\n%s\n",
174                             crc, (char *)tag->data);
175 #endif
176                     switch (crc) {
177                     case 0x5464F92C:
178                         /* Mandrake 9.1 CD1 boot script */
179                     case 0x4BC74ECF:
180                         /* Mandrake 10.1 & 10.2 CD1 boot script */
181                     case 0x5B265246:
182                         /* Gentoo 1.2-r1 */
183                         /* Gentoo 2004.1 minimal install CD */
184                         /* Gentoo 1.4 live CDROM */
185                         /* Knopix PPC beta-pre12 */
186                     case 0x75420D8A:
187                         /* Debian woody */
188                         /* Debian 3.0r1 */
189                         script_type = CHRP_SCRIPT_LOAD_BOOT;
190                         goto do_script;
191                     case 0x633e4c9c:
192                         /* Debian Sarge */
193                     case 0xbe3abf60:
194                         /* Debian Sarge, installed on a hard disk drive */
195                         script_type = CHRP_SCRIPT_LOAD_BOOT;
196                         goto do_script;
197                     case 0x07b86bfe:
198                         /* Linux Fedora Core 3 */
199                         script_type = CHRP_SCRIPT_LOAD_BOOT;
200                         goto do_script;
201                     case 0x9ccdf371:
202                         script_type = CHRP_SCRIPT_LOAD_BOOT;
203                         goto do_script;
204                     case 0xEF423926:
205                         /* OpenBSD 3.4 */
206                     case 0x68e4f265:
207                         /* OpenBSD 3.5 */
208                     case 0x3b7ea9e1:
209                         /* OpenBSD 3.6 */
210                         script_type = CHRP_SCRIPT_LOAD_BOOT;
211                         goto do_script;
212                     case 0xB7981DBC:
213                         /* iBook 2 hw test CDROM */
214 #if 1
215                         script_type = CHRP_SCRIPT_LOAD_BOOT;
216                         goto do_script;
217 #endif
218                         
219                     case 0xEA06C1A7:
220                         /* MacOS 9.2 boot script:
221                          * the XCOFF loader is embedded in the file...
222                          */
223                     case 0x53A95958:
224                         /* iBook 2 restore CD (MacOS X 10.2) */
225                         script_type = CHRP_SCRIPT_EMBEDDED;
226                         pos = strfind(tag->data, "elf-offset");
227                         if (pos != NULL) {
228                             /* Go backward until we get the value */
229                             for (--pos; *pos < '0' || *pos > '9'; pos--)
230                                 continue;
231                             for (; *pos >= '0' && *pos <= '9'; pos--)
232                                 continue;
233                             offset = strtol(pos, NULL, 16);
234                             goto do_script;
235                         }
236                         ERROR("Didn't find boot file offset\n");
237                         goto out;
238                     case 0x8d5acb86:
239                         /* Darwin-7.01
240                          * The executable file is embedded after the script
241                          */
242                         script_type = CHRP_SCRIPT_EMBEDDED;
243                         DPRINTF("Boot file embedded at the end of boot script\n");
244                         break;
245                     default:
246                         ERROR("XML error: unknown Forth script: %08x\n%s\n",
247                               crc, (char *)tag->data);
248                         goto out;
249                     }
250                     break;
251
252                 do_script:
253                     switch (script_type) {
254                     case CHRP_SCRIPT_LOAD_BOOT:
255                         pos = strfind(tag->data, "boot");
256                         if (pos != NULL) {
257                             /* Eat everything until file name */
258                             for (pos += 4; *pos != ','; pos++)
259                                 continue;
260                             /* Eat ',' */
261                             for (++pos; isspace(*pos) || *pos == '"'; pos++)
262                                 continue;
263                             /* Find file name end */
264                         redo:
265                             for (endc = pos;
266                                  *endc != ' ' && *endc != '"' &&
267                                  *endc != '\n' && *endc != '\r';
268                                  endc++) {
269                                 if (*endc == '\\')
270                                     *endc = '/';
271                             }
272                             if (memcmp(pos, "ofwboot", 7) == 0) {
273                                 for (pos = endc + 1; *pos == ' '; pos++)
274                                     continue;
275                                 goto redo;
276                             }
277                             *endc = '\0';
278                         }
279                         DPRINTF("Real boot file is: '%s'\n", pos);
280                         part = fs_inode_get_part(file);
281                         /* check if it's a path or just a file */
282                         tmpp = pos;
283                         if ((pos[0] == '/' && pos[1] == '/') ||
284                             pos[0] != '/') {
285                             unsigned char *bootdir;
286                             bootdir = fs_get_boot_dirname(part_fs(part));
287                             if (bootdir == NULL) {
288                                 ERROR("Cannot get boot directory name\n");
289                                 bug();
290                             }
291                             snprintf(tmpname, TMPNAME_LEN,
292                                      "%s/%s", bootdir, pos);
293                             tmpname[TMPNAME_LEN - 1] = '\0';
294                             rel++;
295                             pos = tmpname;
296                             DPRINTF("'%s' => '%s'\n", bootdir, pos);
297                         }
298                     retry:
299                         inode = fs_open(part_fs(part), pos);
300                         if (inode == NULL) {
301                             ERROR("Real boot inode '%s' not found\n", pos);
302                             /* Try in root directory */
303                             if (rel == 1) {
304                                 for (; *tmpp == '/'; tmpp++)
305                                     continue;
306                                 snprintf(tmpname, TMPNAME_LEN, "/%s", tmpp);
307                                 tmpname[TMPNAME_LEN - 1] = '\0';
308                                 rel++;
309                                 goto retry;
310                             }
311                             
312                             bug();
313                         }
314                         ret = _bootfile_load(inode, dest, entry, end, 0, -1);
315                         fs_close(inode);
316                         goto out;
317                     case CHRP_SCRIPT_EMBEDDED:
318                         DPRINTF("Exec offset: %d %08x\n", offset, offset);
319                         ret = 0;
320                         goto out;
321                     case CHRP_SCRIPT_IGNORE:
322                         break;
323                     }
324                     break;
325                 case CHRP_TAG_OS_BADGE_ICONS:
326                 case CHRP_TAG_ICON:
327                     /* Ignore it */
328                     break;
329                 case CHRP_TAG_BITMAP:
330                     /* Ignore it */
331                     break;
332                 case CHRP_TAG_LICENSE:
333                     /* Ignore it */
334                     pos = tag->data;
335                     if (*(char *)tag->data == 0x0d) {
336                         pos++;
337                     }
338                     DPRINTF("License: '%s'\n", pos);
339                     break;
340                 default:
341                     ERROR("XML error: unknown tag: '%s'\n", tag->name);
342                     goto out;
343                 }
344                 tmp = tag->up;
345                 if (tmp == NULL)
346                     state = XML_STATE_OUT;
347                 else
348                     state = XML_STATE_DATA;
349                 free(tag->name);
350                 free(tag->data);
351                 free(tag);
352                 tag = tmp;
353             } else {
354                 tmp = malloc(sizeof(XML_tag_t));
355                 if (tmp == NULL) {
356                     ERROR("Cannot allocate new tag\n");
357                     goto out;
358                 }
359                 tmp->up = tag;
360                 /* Ignore tag attributes */
361                 pos = strchr(buf, ' ');
362                 if (pos != NULL)
363                     *pos = '\0';
364                 tmp->name = strdup(buf);
365                 tag = tmp;
366                 if (first == NULL)
367                     first = tag;
368                 DPRINTF("Open tag '%s'\n", tag->name);
369                 state = XML_STATE_DATA;
370             }
371             break;
372         case XML_STATE_DATA:
373             if (tag->data == NULL) {
374                 tag->dlen = pos - buf;
375                 tag->data = malloc(tag->dlen);
376                 memcpy(tag->data, buf, tag->dlen);
377             }
378             state = XML_STATE_TAG;
379             break;
380         }
381         pos = buf;
382     }
383     ret = 0;
384     fs_read(file, &c, 1);
385     fs_read(file, &c, 1);
386     offset += 2;
387  out:
388 #if 1
389     for (; tag != NULL; tag = tmp) {
390         tmp = tag->up;
391         free(tag->name);
392         free(tag->data);
393         free(tag);
394     }
395 #endif
396     if (ret == 0 && script_type == CHRP_SCRIPT_EMBEDDED) {
397         DPRINTF("Load embedded file from offset %d (%d => %d)\n",
398                 offset, loffset, loffset + offset);
399         ret = _bootfile_load(file, dest, entry, end, loffset + offset, -1);
400     }
401     DPRINTF("Done\n");
402
403     return ret;
404 }