summaryrefslogtreecommitdiff
path: root/util/cbfstool/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'util/cbfstool/util.c')
-rw-r--r--util/cbfstool/util.c282
1 files changed, 282 insertions, 0 deletions
diff --git a/util/cbfstool/util.c b/util/cbfstool/util.c
new file mode 100644
index 0000000000..51905a6d60
--- /dev/null
+++ b/util/cbfstool/util.c
@@ -0,0 +1,282 @@
+/*
+ * cbfstool
+ *
+ * Copyright (C) 2008 Jordan Crouse <jordan@cosmicpenguin.net>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#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;
+}