diff options
author | 2023-10-10 14:33:42 +0000 | |
---|---|---|
committer | 2023-10-10 14:33:42 +0000 | |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/skiboot/external/common/arch_flash_powerpc.c | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/skiboot/external/common/arch_flash_powerpc.c')
-rw-r--r-- | roms/skiboot/external/common/arch_flash_powerpc.c | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/roms/skiboot/external/common/arch_flash_powerpc.c b/roms/skiboot/external/common/arch_flash_powerpc.c new file mode 100644 index 000000000..87661296d --- /dev/null +++ b/roms/skiboot/external/common/arch_flash_powerpc.c @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +/* Copyright 2013-2017 IBM Corp. + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <unistd.h> +#include <dirent.h> +#include <stdbool.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <string.h> + +#include <libflash/file.h> + +#include "arch_flash.h" + +#define FDT_FLASH_PATH "/proc/device-tree/chosen/ibm,system-flash" +#define SYSFS_MTD_PATH "/sys/class/mtd" + +static inline void hint_root(void) +{ + fprintf(stderr, "Do you have permission? Are you root?\n"); +} + +static int get_dev_attr(const char *dev, const char *attr_file, uint32_t *attr) +{ + char *dev_path = NULL; + int fd, rc; + + /* + * Needs to be large enough to hold at most uint32_t represented as a + * string in hex with leading 0x + */ + char attr_buf[10]; + + rc = asprintf(&dev_path, "%s/%s/%s", SYSFS_MTD_PATH, dev, attr_file); + if (rc < 0) { + dev_path = NULL; + goto out; + } + + fd = open(dev_path, O_RDONLY); + if (fd == -1) + goto out; + + rc = read(fd, attr_buf, sizeof(attr_buf)); + close(fd); + if (rc == -1) + goto out; + + if (attr) + *attr = strtol(attr_buf, NULL, 0); + + free(dev_path); + return 0; + +out: + free(dev_path); + fprintf(stderr, "Couldn't get MTD attribute '%s' from '%s'\n", dev, attr_file); + return -1; +} + +static int get_dev_mtd(const char *fdt_flash_path, char **mtd_path) +{ + struct dirent **namelist; + char fdt_node_path[PATH_MAX]; + int count, i, rc, fd; + bool done; + + if (!fdt_flash_path) + return -1; + + fd = open(fdt_flash_path, O_RDONLY); + if (fd == -1) { + fprintf(stderr, "Couldn't open '%s' FDT attribute to determine which flash device to use\n", + fdt_flash_path); + fprintf(stderr, "Is your skiboot new enough to expose the flash through the device tree?\n"); + hint_root(); + return -1; + } + + rc = read(fd, fdt_node_path, sizeof(fdt_node_path)); + close(fd); + if (rc == -1) { + fprintf(stderr, "Couldn't read flash FDT node from '%s'\n", fdt_flash_path); + hint_root(); + return -1; + } + + count = scandir(SYSFS_MTD_PATH, &namelist, NULL, alphasort); + if (count == -1) { + fprintf(stderr, "Couldn't scan '%s' for MTD\n", SYSFS_MTD_PATH); + hint_root(); + return -1; + } + + rc = 0; + done = false; + for (i = 0; i < count; i++) { + struct dirent *dirent; + char *dev_path; + char fdt_node_path_tmp[PATH_MAX]; + + dirent = namelist[i]; + + /* + * The call to asprintf must happen last as when it succeeds it + * will allocate dev_path + */ + if (dirent->d_name[0] == '.' || rc || done || + asprintf(&dev_path, "%s/%s/device/of_node", SYSFS_MTD_PATH, dirent->d_name) < 0) { + free(namelist[i]); + continue; + } + + rc = readlink(dev_path, fdt_node_path_tmp, sizeof(fdt_node_path_tmp) - 1); + free(dev_path); + if (rc == -1) { + /* + * This might fail because it could not exist if the system has flash + * devices that present as mtd but don't have corresponding FDT + * nodes, just continue silently. + */ + free(namelist[i]); + /* Should still try the next dir so reset rc */ + rc = 0; + continue; + } + fdt_node_path_tmp[rc] = '\0'; + + if (strstr(fdt_node_path_tmp, fdt_node_path)) { + uint32_t flags, size; + + /* + * size and flags could perhaps have be gotten another way but this + * method is super unlikely to fail so it will do. + */ + + /* Check to see if device is writeable */ + rc = get_dev_attr(dirent->d_name, "flags", &flags); + if (rc) { + free(namelist[i]); + continue; + } + + /* Get the size of the mtd device while we're at it */ + rc = get_dev_attr(dirent->d_name, "size", &size); + if (rc) { + free(namelist[i]); + continue; + } + + rc = asprintf(&dev_path, "/dev/%s", dirent->d_name); + if (rc < 0) { + free(namelist[i]); + continue; + } + rc = 0; + *mtd_path = dev_path; + done = true; + } + free(namelist[i]); + } + free(namelist); + + if (!done) { + fprintf(stderr, "Couldn't find '%s' corresponding MTD\n", fdt_flash_path); + fprintf(stderr, "Is your kernel new enough to expose MTD?\n"); + } + + /* explicit negative value so as to not return a libflash code */ + return done ? rc : -1; +} + +static struct blocklevel_device *arch_init_blocklevel(const char *file, bool keep_alive) +{ + int rc; + struct blocklevel_device *new_bl = NULL; + char *real_file = NULL; + + if (!file) { + rc = get_dev_mtd(FDT_FLASH_PATH, &real_file); + if (rc) + return NULL; + } + + rc = file_init_path(file ? file : real_file, NULL, keep_alive, &new_bl); + if (rc) + new_bl = NULL; + free(real_file); + return new_bl; +} + +/* Skiboot will worry about this for us */ +int __attribute__((const)) arch_flash_set_wrprotect(struct blocklevel_device *bl, int set) +{ + (void)bl; + (void)set; + + return 0; +} + +enum flash_access __attribute__((const)) arch_flash_access(struct blocklevel_device *bl, + enum flash_access access) +{ + (void)bl; + + if (access != PNOR_MTD) + return ACCESS_INVAL; + + return PNOR_MTD; +} + +int arch_flash_init(struct blocklevel_device **r_bl, const char *file, bool keep_alive) +{ + struct blocklevel_device *new_bl; + + /* + * In theory here we should check that something crazy wasn't + * passed to arch_flash_access() and refuse to init. + * However, arch_flash_access won't accept anything except + * PNOR_MTD, if they want something different then they should + * have checked with arch_flash_access() + */ + new_bl = arch_init_blocklevel(file, keep_alive); + if (!new_bl) + return -1; + + *r_bl = new_bl; + return 0; +} + +void arch_flash_close(struct blocklevel_device *bl, const char *file) +{ + (void)file; + + file_exit_close(bl); +} |