| /* |
| * |
| * <bootinfo_load.c> |
| * |
| * bootinfo file loader |
| * |
| * Copyright (C) 2009 Laurent Vivier (Laurent@vivier.eu) |
| * |
| * Original XML parser by Blue Swirl <blauwirbel@gmail.com> |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * version 2 |
| * |
| */ |
| |
| #include "config.h" |
| #include "libopenbios/bindings.h" |
| #include "libopenbios/bootinfo_load.h" |
| #include "libopenbios/ofmem.h" |
| #include "libc/vsprintf.h" |
| |
| //#define DEBUG_BOOTINFO |
| |
| #ifdef DEBUG_BOOTINFO |
| #define DPRINTF(fmt, args...) \ |
| do { printk("%s: " fmt, __func__ , ##args); } while (0) |
| #else |
| #define DPRINTF(fmt, args...) \ |
| do { } while (0) |
| #endif |
| |
| static char * |
| get_device( const char *path ) |
| { |
| int i; |
| static char buf[1024]; |
| |
| for (i = 0; i < sizeof(buf) && path[i] && path[i] != ':'; i++) |
| buf[i] = path[i]; |
| buf[i] = 0; |
| |
| return buf; |
| } |
| |
| static char * |
| get_partition( const char *path ) |
| { |
| static char buf[2]; |
| |
| buf[0] = '\0'; |
| buf[1] = '\0'; |
| |
| while ( *path && *path != ':' ) |
| path++; |
| |
| if (!*path) |
| return buf; |
| path++; |
| |
| if (path[0] == ',' || !strchr(path, ',')) /* if there is not a ',' or no partition id then return */ |
| return buf; |
| |
| /* Must be a partition id */ |
| buf[0] = path[0]; |
| |
| return buf; |
| } |
| |
| static char * |
| get_filename( const char * path , char **dirname) |
| { |
| static char buf[1024]; |
| char *filename; |
| |
| while ( *path && *path != ':' ) |
| path++; |
| |
| if (!*path) { |
| *dirname = NULL; |
| return NULL; |
| } |
| path++; |
| |
| while ( *path && isdigit(*path) ) |
| path++; |
| |
| if (*path == ',') |
| path++; |
| |
| strncpy(buf, path, sizeof(buf)); |
| buf[sizeof(buf) - 1] = 0; |
| |
| filename = strrchr(buf, '\\'); |
| if (filename) { |
| *dirname = buf; |
| (*filename++) = 0; |
| } else { |
| *dirname = NULL; |
| filename = buf; |
| } |
| |
| return filename; |
| } |
| |
| int |
| is_bootinfo(char *bootinfo) |
| { |
| return (strncasecmp(bootinfo, "<chrp-boot", 10) ? 0 : -1); |
| } |
| |
| int |
| bootinfo_load(struct sys_info *info, const char *filename) |
| { |
| // Currently not implemented |
| return LOADER_NOT_SUPPORT; |
| } |
| |
| /* |
| Parse SGML structure like: |
| <chrp-boot> |
| <description>Debian/GNU Linux Installation on IBM CHRP hardware</description> |
| <os-name>Debian/GNU Linux for PowerPC</os-name> |
| <boot-script>boot &device;:\install\yaboot</boot-script> |
| <icon size=64,64 color-space=3,3,2> |
| |
| CHRP system bindings are described at: |
| http://playground.sun.com/1275/bindings/chrp/chrp1_7a.ps |
| */ |
| |
| void |
| bootinfo_init_program(void) |
| { |
| char *base; |
| int proplen; |
| phandle_t chosen; |
| int tag, taglen, script, scriptlen, scriptvalid, entity, chrp; |
| char tagbuf[128], c; |
| char *device, *filename, *directory, *partition; |
| int current, size; |
| char *bootscript; |
| char *tmp; |
| char bootpath[1024]; |
| |
| /* Parse the boot script */ |
| |
| chosen = find_dev("/chosen"); |
| tmp = get_property(chosen, "bootpath", &proplen); |
| memcpy(bootpath, tmp, proplen); |
| bootpath[proplen] = 0; |
| |
| DPRINTF("bootpath %s\n", bootpath); |
| |
| device = get_device(bootpath); |
| partition = get_partition(bootpath); |
| filename = get_filename(bootpath, &directory); |
| |
| feval("load-base"); |
| base = (char*)cell2pointer(POP()); |
| |
| feval("load-size"); |
| size = POP(); |
| |
| /* Some bootinfo scripts contain a binary payload after the |
| NULL-terminated Forth string such as OS 9. Restrict our |
| size to just the Forth section, otherwise we end up trying |
| to allocate memory for the entire binary which might fail. */ |
| size = strnlen(base, size); |
| |
| bootscript = malloc(size); |
| if (bootscript == NULL) { |
| DPRINTF("Can't malloc %d bytes\n", size); |
| return; |
| } |
| |
| if (!is_bootinfo(base)) { |
| DPRINTF("Not a valid bootinfo memory image\n"); |
| free(bootscript); |
| return; |
| } |
| |
| chrp = 0; |
| tag = 0; |
| taglen = 0; |
| script = 0; |
| scriptvalid = 0; |
| scriptlen = 0; |
| entity = 0; |
| current = 0; |
| while (current < size) { |
| |
| c = base[current++]; |
| |
| if (c == '<') { |
| script = 0; |
| tag = 1; |
| taglen = 0; |
| } else if (c == '>') { |
| tag = 0; |
| tagbuf[taglen] = '\0'; |
| if (strncasecmp(tagbuf, "chrp-boot", 9) == 0) { |
| chrp = 1; |
| } else if (chrp == 1) { |
| if (strncasecmp(tagbuf, "boot-script", 11) == 0) { |
| script = 1; |
| scriptlen = 0; |
| } else if (strncasecmp(tagbuf, "/boot-script", 12) == 0) { |
| |
| script = 0; |
| bootscript[scriptlen] = '\0'; |
| |
| DPRINTF("got bootscript %s\n", |
| bootscript); |
| |
| scriptvalid = -1; |
| |
| break; |
| } else if (strncasecmp(tagbuf, "/chrp-boot", 10) == 0) |
| break; |
| } |
| } else if (tag && taglen < sizeof(tagbuf)) { |
| tagbuf[taglen++] = c; |
| } else if (script && c == '&') { |
| entity = 1; |
| taglen = 0; |
| } else if (entity && c ==';') { |
| entity = 0; |
| tagbuf[taglen] = '\0'; |
| if (strncasecmp(tagbuf, "lt", 2) == 0) { |
| bootscript[scriptlen++] = '<'; |
| } else if (strncasecmp(tagbuf, "gt", 2) == 0) { |
| bootscript[scriptlen++] = '>'; |
| } else if (strncasecmp(tagbuf, "device", 6) == 0) { |
| strcpy(bootscript + scriptlen, device); |
| scriptlen += strlen(device); |
| } else if (strncasecmp(tagbuf, "partition", 9) == 0) { |
| strcpy(bootscript + scriptlen, partition); |
| scriptlen += strlen(partition); |
| } else if (strncasecmp(tagbuf, "directory", 9) == 0) { |
| strcpy(bootscript + scriptlen, directory); |
| scriptlen += strlen(directory); |
| } else if (strncasecmp(tagbuf, "filename", 8) == 0) { |
| strcpy(bootscript + scriptlen, filename); |
| scriptlen += strlen(filename); |
| } else if (strncasecmp(tagbuf, "full-path", 9) == 0) { |
| strcpy(bootscript + scriptlen, bootpath); |
| scriptlen += strlen(bootpath); |
| } else { /* unknown, keep it */ |
| bootscript[scriptlen] = '&'; |
| strcpy(bootscript + scriptlen + 1, tagbuf); |
| scriptlen += taglen + 1; |
| bootscript[scriptlen] = ';'; |
| scriptlen++; |
| } |
| } else if (entity && taglen < sizeof(tagbuf)) { |
| tagbuf[taglen++] = c; |
| } else if (script && scriptlen < size) { |
| bootscript[scriptlen++] = c; |
| } |
| } |
| |
| /* If the payload is bootinfo then we execute it immediately */ |
| if (scriptvalid) { |
| DPRINTF("bootscript: %s\n", bootscript); |
| feval(bootscript); |
| } |
| else |
| DPRINTF("Unable to parse bootinfo bootscript\n"); |
| } |