/* * cbfstool * * Copyright (C) 2008 Jordan Crouse * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA */ #include #include #include #include #include #include #include "cbfstool.h" int get_size(const char *size) { char *next; int val = strtoul(size, &next, 0); /* Support modifiers for the size kK and mM for kbytes and mbytes respectfully */ if (next != NULL) { if (*next == 'k' || *next == 'K') val *= 1024; else if (*next == 'm' || *next == 'M') val *= (1024 * 1024); } return val; } int copy_from_fd(int fd, void *ptr, int size) { unsigned char *p = ptr; while (size > 0) { int ret = read(fd, p, size); if (ret == -1) { ERROR("Error while reading: %m\n"); return -1; } if (ret == 0) { ERROR("Unexpected end of file\n"); return -1; } p += ret; size -= ret; } return 0; } int size_and_open(const char *filename, unsigned int *size) { struct stat s; int fd = open(filename, O_RDONLY); if (fd == -1) { ERROR("Unable to open %s: %m\n", filename); return -1; } if (fstat(fd, &s)) { ERROR("Unable to stat %s: %m\n", filename); close(fd); return -1; } *size = s.st_size; return fd; } int map_rom(struct rom *rom, int size) { rom->ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, rom->fd, 0); if (rom->ptr == MAP_FAILED) { ERROR("Could not map the rom: %m\n"); rom->ptr = NULL; return -1; } return 0; } int open_rom(struct rom *rom, const char *filename) { struct stat s; unsigned long offset; if (stat(filename, &s)) { ERROR("Could not stat %s: %m\n", filename); return -1; } rom->fd = open(filename, O_RDWR); if (rom->fd == -1) { ERROR("Could not open %s: %m\n", filename); rom->fd = 0; return -1; } if (map_rom(rom, s.st_size)) goto err; /* Find the master header */ offset = ROM_READL(rom, s.st_size - 4); rom->header = (struct cbfs_header *) ROM_PTR(rom, s.st_size - (0xFFFFFFFF - offset) - 1); if (ntohl(rom->header->magic) != HEADER_MAGIC) { ERROR("This does not appear to be a valid ROM\n"); goto err; } /* Check that the alignment is correct */ if (ntohl(rom->header->align) == 0) { ERROR("The alignment in the ROM is 0 - probably malformed\n"); goto err; } /* Sanity check that the size matches the file size */ if (ntohl(rom->header->romsize) != s.st_size) { ERROR("The ROM size in the header does not match the file\n"); ERROR("ROM size is %d bytes, file size is %d bytes\n", ntohl(rom->header->romsize), (unsigned int)s.st_size); goto err; } rom->size = ntohl(rom->header->romsize); rom->fssize = rom->size - ntohl(rom->header->bootblocksize); return 0; err: if (rom->ptr != NULL) munmap(rom->ptr, s.st_size); if (rom->fd > 0) close(rom->fd); rom->ptr = NULL; rom->fd = 0; return -1; } int create_rom(struct rom *rom, const unsigned char *filename, int romsize, int bootblocksize, int align) { unsigned char null = '\0'; if (rom->fd != 0) { ERROR("%s already exists - cannot create\n", filename); return -1; } /* Remember the size of the entire ROM */ rom->size = romsize; /* The size of the archive section is everything but the bootblock */ rom->fssize = romsize - bootblocksize; /* Open the file descriptor */ rom->fd = open((char *)filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); if (rom->fd == -1) { ERROR("Could not create %s: %m\n", filename); return -1; } /* Size the new file appropriately */ lseek(rom->fd, romsize - 1, SEEK_SET); write(rom->fd, &null, 1); if (map_rom(rom, romsize)) { close(rom->fd); return -1; } /* Clear the reset vector */ memset(rom->ptr + rom->size - 16, 0, 16); ROM_WRITEL(rom, rom->size - 4, 0xFFFFFFF0 - sizeof(struct cbfs_header)); /* This is a pointer to the header for easy access */ rom->header = (struct cbfs_header *) ROM_PTR(rom, rom->size - 16 - sizeof(struct cbfs_header)); rom->header->magic = htonl(HEADER_MAGIC); rom->header->romsize = htonl(romsize); rom->header->bootblocksize = htonl(bootblocksize); rom->header->align = htonl(align); rom->header->offset = htonl(0); return 0; } int add_bootblock(struct rom *rom, const char *filename) { unsigned int size; //unsigned int offset; int fd = size_and_open(filename, &size); int ret; struct cbfs_header tmp; if (fd == -1) return -1; if (size > ntohl(rom->header->bootblocksize)) { ERROR("The bootblock size is not correct (%d vs %d)\n", size, ntohl(rom->header->bootblocksize)); return -1; } /* Copy the current header into a temporary buffer */ memcpy(&tmp, rom->header, sizeof(struct cbfs_header)); /* Copy the bootblock into place at the end of the file */ ret = copy_from_fd(fd, ROM_PTR(rom, rom->size - ntohl(rom->header->bootblocksize)), size); close(fd); if (ret) { ERROR("Unable to add %s to the bootblock\n", filename); return -1; } /* FIXME: This should point to a location defined by coreboot */ ROM_WRITEL(rom, rom->size - 4, 0xFFFFFFF0 - sizeof(struct cbfs_header)); /* This is a pointer to the header for easy access */ rom->header = (struct cbfs_header *) ROM_PTR(rom, rom->size - 16 - sizeof(struct cbfs_header)); #if 0 /* Figure out the new location for the header */ offset = ROM_READL(rom, rom->size - 4); rom->header = (struct cbfs_header *) ROM_PTR(rom, offset - (0xFFFFFFFF - rom->size)); #endif /* Replace the LAR header */ memcpy(rom->header, &tmp, sizeof(struct cbfs_header)); return 0; } int rom_exists(struct rom *rom) { if (rom->fd <= 0) return 0; return 1; }