aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/external/common/arch_flash_powerpc.c
diff options
context:
space:
mode:
authorAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
committerAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
commitaf1a266670d040d2f4083ff309d732d648afba2a (patch)
tree2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/skiboot/external/common/arch_flash_powerpc.c
parente02cda008591317b1625707ff8e115a4841aa889 (diff)
Add submodule dependency filesHEADmaster
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/skiboot/external/common/arch_flash_powerpc.c')
-rw-r--r--roms/skiboot/external/common/arch_flash_powerpc.c242
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);
+}