| /* |
| * Copyright(c) 2014 Intel Corporation. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of version 2 of the GNU General Public License as |
| * published by the Free Software Foundation. |
| * |
| * 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. |
| */ |
| #include <stdio.h> |
| #include <errno.h> |
| #include <stdlib.h> |
| #include <fcntl.h> |
| #include <limits.h> |
| #include <unistd.h> |
| #include <endian.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <util/list.h> |
| #include <subcmd/parse-options.h> |
| #include <util/size.h> |
| #include <util/util.h> |
| |
| #include <nfit.h> |
| |
| #define DEFAULT_NFIT "local_nfit.bin" |
| static const char *nfit_file = DEFAULT_NFIT; |
| static LIST_HEAD(spas); |
| |
| struct spa { |
| struct list_head list; |
| unsigned long long size, offset; |
| }; |
| |
| static int parse_add_spa(const struct option *option, const char *__arg, int unset) |
| { |
| struct spa *s = calloc(1, sizeof(struct spa)); |
| char *arg = strdup(__arg); |
| char *size, *offset; |
| int rc = -ENOMEM; |
| |
| if (!s || !arg) |
| goto err; |
| rc = -EINVAL; |
| |
| size = arg; |
| offset = strchr(arg, ','); |
| if (!offset) |
| goto err; |
| *offset++ = '\0'; |
| s->size = parse_size64(size); |
| if (s->size == ULLONG_MAX) |
| goto err; |
| s->offset = parse_size64(offset); |
| if (s->offset == ULLONG_MAX) |
| goto err; |
| |
| list_add_tail(&s->list, &spas); |
| free(arg); |
| |
| return 0; |
| |
| err: |
| error("failed to parse --add-spa=%s\n", __arg); |
| free(arg); |
| free(s); |
| return rc; |
| } |
| |
| static unsigned char nfit_checksum(void *buf, size_t size) |
| { |
| unsigned char sum, *data = buf; |
| size_t i; |
| |
| for (sum = 0, i = 0; i < size; i++) |
| sum += data[i]; |
| return 0 - sum; |
| } |
| |
| static void writeq(unsigned long long v, void *a) |
| { |
| unsigned long long *p = a; |
| |
| *p = htole64(v); |
| } |
| |
| static void writel(unsigned long v, void *a) |
| { |
| unsigned long *p = a; |
| |
| *p = htole32(v); |
| } |
| |
| static void writew(unsigned short v, void *a) |
| { |
| unsigned short *p = a; |
| |
| *p = htole16(v); |
| } |
| |
| static void writeb(unsigned char v, void *a) |
| { |
| unsigned char *p = a; |
| |
| *p = v; |
| } |
| |
| static struct nfit *create_nfit(struct list_head *spa_list) |
| { |
| struct nfit_spa *nfit_spa; |
| struct nfit *nfit; |
| struct spa *s; |
| size_t size; |
| char *buf; |
| int i; |
| |
| size = sizeof(struct nfit); |
| list_for_each_entry(s, spa_list, list) |
| size += sizeof(struct nfit_spa); |
| |
| buf = calloc(1, size); |
| if (!buf) |
| return NULL; |
| |
| /* nfit header */ |
| nfit = (struct nfit *) buf; |
| memcpy(nfit->signature, "NFIT", 4); |
| writel(size, &nfit->length); |
| writeb(1, &nfit->revision); |
| memcpy(nfit->oemid, "LOCAL", 6); |
| writew(1, &nfit->oem_tbl_id); |
| writel(1, &nfit->oem_revision); |
| writel(0x80860000, &nfit->creator_id); |
| writel(1, &nfit->creator_revision); |
| |
| nfit_spa = (struct nfit_spa *) (buf + sizeof(*nfit)); |
| i = 1; |
| list_for_each_entry(s, spa_list, list) { |
| writew(NFIT_TABLE_SPA, &nfit_spa->type); |
| writew(sizeof(*nfit_spa), &nfit_spa->length); |
| nfit_spa_uuid_pm(&nfit_spa->type_uuid); |
| writew(i++, &nfit_spa->range_index); |
| writeq(s->offset, &nfit_spa->spa_base); |
| writeq(s->size, &nfit_spa->spa_length); |
| nfit_spa++; |
| } |
| |
| writeb(nfit_checksum(buf, size), &nfit->checksum); |
| |
| return nfit; |
| } |
| |
| static int write_nfit(struct nfit *nfit, const char *file, int force) |
| { |
| int fd; |
| ssize_t rc; |
| mode_t mode = S_IRUSR|S_IRGRP|S_IWUSR|S_IWGRP; |
| |
| fd = open(file, O_RDWR|O_CREAT|O_EXCL, mode); |
| if (fd < 0 && !force && errno == EEXIST) { |
| error("\"%s\" exists, overwrite with --force\n", file); |
| return -EEXIST; |
| } else if (fd < 0 && force && errno == EEXIST) { |
| fd = open(file, O_RDWR|O_CREAT|O_TRUNC, mode); |
| } |
| |
| if (fd < 0) { |
| error("Failed to open \"%s\": %s\n", file, strerror(errno)); |
| return -errno; |
| } |
| |
| rc = write(fd, nfit, le32toh(nfit->length)); |
| close(fd); |
| return rc; |
| } |
| |
| struct ndctl_ctx; |
| int cmd_create_nfit(int argc, const char **argv, void *ctx) |
| { |
| int i, rc = -ENXIO, force = 0; |
| const char * const u[] = { |
| "ndctl create-nfit [<options>]", |
| NULL |
| }; |
| const struct option options[] = { |
| OPT_CALLBACK('a', "add-spa", NULL, "size,offset", |
| "add a system-physical-address range table entry", |
| parse_add_spa), |
| OPT_STRING('o', NULL, &nfit_file, "file", |
| "output to <file> (default: " DEFAULT_NFIT ")"), |
| OPT_INCR('f', "force", &force, "overwrite <file> if it already exists"), |
| OPT_END(), |
| }; |
| struct spa *s, *_s; |
| struct nfit *nfit = NULL; |
| |
| argc = parse_options(argc, argv, options, u, 0); |
| |
| for (i = 0; i < argc; i++) |
| error("unknown parameter \"%s\"\n", argv[i]); |
| if (list_empty(&spas)) |
| error("specify at least one --add-spa= option\n"); |
| |
| if (argc || list_empty(&spas)) |
| usage_with_options(u, options); |
| |
| nfit = create_nfit(&spas); |
| if (!nfit) |
| goto out; |
| |
| rc = write_nfit(nfit, nfit_file, force); |
| if ((unsigned int) rc == le32toh(nfit->length)) { |
| fprintf(stderr, "wrote %d bytes to %s\n", |
| le32toh(nfit->length), nfit_file); |
| rc = 0; |
| } |
| |
| out: |
| free(nfit); |
| list_for_each_entry_safe(s, _s, &spas, list) { |
| list_del(&s->list); |
| free(s); |
| } |
| |
| return rc; |
| } |