aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/external/common
diff options
context:
space:
mode:
Diffstat (limited to 'roms/skiboot/external/common')
-rw-r--r--roms/skiboot/external/common/.gitignore3
-rw-r--r--roms/skiboot/external/common/arch_flash.h38
-rw-r--r--roms/skiboot/external/common/arch_flash_arm.c374
-rw-r--r--roms/skiboot/external/common/arch_flash_arm_io.h86
-rw-r--r--roms/skiboot/external/common/arch_flash_common.c50
-rw-r--r--roms/skiboot/external/common/arch_flash_powerpc.c242
-rw-r--r--roms/skiboot/external/common/arch_flash_powerpc_io.h0
-rw-r--r--roms/skiboot/external/common/arch_flash_unknown.c42
-rw-r--r--roms/skiboot/external/common/arch_flash_unknown_io.h0
-rwxr-xr-xroms/skiboot/external/common/get_arch.sh11
-rw-r--r--roms/skiboot/external/common/rules.mk76
11 files changed, 922 insertions, 0 deletions
diff --git a/roms/skiboot/external/common/.gitignore b/roms/skiboot/external/common/.gitignore
new file mode 100644
index 000000000..3535d9768
--- /dev/null
+++ b/roms/skiboot/external/common/.gitignore
@@ -0,0 +1,3 @@
+ast-sf-ctrl.c
+ast.h
+io.h \ No newline at end of file
diff --git a/roms/skiboot/external/common/arch_flash.h b/roms/skiboot/external/common/arch_flash.h
new file mode 100644
index 000000000..0dff8b704
--- /dev/null
+++ b/roms/skiboot/external/common/arch_flash.h
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2015 IBM Corp.
+ */
+
+#ifndef __EXTERNAL_ARCH_FLASH_H
+#define __EXTERNAL_ARCH_FLASH_H
+
+#include <getopt.h>
+#include <libflash/blocklevel.h>
+
+enum flash_access {
+ PNOR_DIRECT,
+ PNOR_MTD,
+ BMC_DIRECT,
+ BMC_MTD,
+ ACCESS_INVAL
+};
+
+int arch_flash_init(struct blocklevel_device **bl, const char *file,
+ bool keep_alive);
+
+void arch_flash_close(struct blocklevel_device *bl, const char *file);
+
+/* Low level functions that an architecture may support */
+
+/*
+ * If called BEFORE init, then this dictates how the flash will be
+ * accessed.
+ * If called AFTER init, then this returns how the flash is being accessed.
+ */
+enum flash_access arch_flash_access(struct blocklevel_device *bl,
+ enum flash_access access);
+
+int arch_flash_erase_chip(struct blocklevel_device *bl);
+int arch_flash_4b_mode(struct blocklevel_device *bl, int set_4b);
+int arch_flash_set_wrprotect(struct blocklevel_device *bl, int set);
+
+#endif /* __EXTERNAL_ARCH_FLASH_H */
diff --git a/roms/skiboot/external/common/arch_flash_arm.c b/roms/skiboot/external/common/arch_flash_arm.c
new file mode 100644
index 000000000..cfe7b96aa
--- /dev/null
+++ b/roms/skiboot/external/common/arch_flash_arm.c
@@ -0,0 +1,374 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2015-2016 IBM Corp.
+ */
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <assert.h>
+
+#include <ccan/container_of/container_of.h>
+
+#include <libflash/libflash.h>
+#include <libflash/file.h>
+#include "ast.h"
+#include "arch_flash.h"
+#include "arch_flash_arm_io.h"
+
+struct flash_chip;
+
+static struct arch_arm_data {
+ int fd;
+ void *ahb_reg_map;
+ void *gpio_ctrl;
+ size_t ahb_flash_base;
+ size_t ahb_flash_size;
+ void *ahb_flash_map;
+ enum flash_access access;
+ struct flash_chip *flash_chip;
+ struct blocklevel_device *init_bl;
+} arch_data;
+
+uint32_t ast_ahb_readl(uint32_t offset)
+{
+ assert(((offset ^ AHB_REGS_BASE) & ~(AHB_REGS_SIZE - 1)) == 0);
+
+ return readl(arch_data.ahb_reg_map + (offset - AHB_REGS_BASE));
+}
+
+void ast_ahb_writel(uint32_t val, uint32_t offset)
+{
+ assert(((offset ^ AHB_REGS_BASE) & ~(AHB_REGS_SIZE - 1)) == 0);
+
+ writel(val, arch_data.ahb_reg_map + (offset - AHB_REGS_BASE));
+}
+
+int ast_copy_to_ahb(uint32_t reg, const void *src, uint32_t len)
+{
+ if (reg < arch_data.ahb_flash_base ||
+ (reg + len) > (arch_data.ahb_flash_base + arch_data.ahb_flash_size))
+ return -1;
+ reg -= arch_data.ahb_flash_base;
+
+ if (((reg | (unsigned long)src | len) & 3) == 0) {
+ while(len > 3) {
+ uint32_t val = *(uint32_t *)src;
+ writel(val, arch_data.ahb_flash_map + reg);
+ src += 4;
+ reg += 4;
+ len -= 4;
+ }
+ }
+
+ while(len--) {
+ uint8_t val = *(uint8_t *)src;
+ writeb(val, arch_data.ahb_flash_map + reg++);
+ src += 1;
+ }
+ return 0;
+}
+
+/*
+ * GPIO stuff to be replaced by higher level accessors for
+ * controlling the flash write lock via sysfs
+ */
+
+static inline uint32_t gpio_ctl_readl(uint32_t offset)
+{
+ return readl(arch_data.gpio_ctrl + offset);
+}
+
+static inline void gpio_ctl_writel(uint32_t val, uint32_t offset)
+{
+ writel(val, arch_data.gpio_ctrl + offset);
+}
+
+static bool set_wrprotect(bool protect)
+{
+ uint32_t reg;
+ bool was_protected;
+
+ reg = gpio_ctl_readl(0x20);
+ was_protected = !!(reg & 0x00004000);
+ if (protect)
+ reg |= 0x00004000; /* GPIOF[6] value */
+ else
+ reg &= ~0x00004000; /* GPIOF[6] value */
+ gpio_ctl_writel(reg, 0x20);
+ reg = gpio_ctl_readl(0x24);
+ reg |= 0x00004000; /* GPIOF[6] direction */
+ gpio_ctl_writel(reg, 0x24);
+
+ return was_protected;
+}
+
+int ast_copy_from_ahb(void *dst, uint32_t reg, uint32_t len)
+{
+ if (reg < arch_data.ahb_flash_base ||
+ (reg + len) > (arch_data.ahb_flash_base + arch_data.ahb_flash_size))
+ return -1;
+ reg -= arch_data.ahb_flash_base;
+
+ if (((reg | (unsigned long)dst | len) & 3) == 0) {
+ while(len > 3) {
+ *(uint32_t *)dst = readl(arch_data.ahb_flash_map + reg);
+ dst += 4;
+ reg += 4;
+ len -= 4;
+ }
+ }
+
+ while(len--) {
+ *(uint8_t *)dst = readb(arch_data.ahb_flash_map + reg++);
+ dst += 1;
+ }
+ return 0;
+}
+
+static void close_devs(void)
+{
+ /*
+ * Old code doesn't do this, not sure why not
+ *
+ * munmap(arch_data.ahb_flash_map, arch_data.ahb_flash_size);
+ * munmap(arch_data.gpio_ctrl, GPIO_CTRL_SIZE);
+ * munmap(arch_data.ahb_reg_map, AHB_REGS_SIZE);
+ * close(arch_data.fd);
+ */
+}
+
+static int open_devs(enum flash_access access)
+{
+ if (access != BMC_DIRECT && access != PNOR_DIRECT)
+ return -1;
+
+ arch_data.fd = open("/dev/mem", O_RDWR | O_SYNC);
+ if (arch_data.fd < 0) {
+ perror("can't open /dev/mem");
+ return -1;
+ }
+
+ arch_data.ahb_reg_map = mmap(0, AHB_REGS_SIZE, PROT_READ | PROT_WRITE,
+ MAP_SHARED, arch_data.fd, AHB_REGS_BASE);
+ if (arch_data.ahb_reg_map == MAP_FAILED) {
+ perror("can't map AHB registers /dev/mem");
+ return -1;
+ }
+ arch_data.gpio_ctrl = mmap(0, GPIO_CTRL_SIZE, PROT_READ | PROT_WRITE,
+ MAP_SHARED, arch_data.fd, GPIO_CTRL_BASE);
+ if (arch_data.gpio_ctrl == MAP_FAILED) {
+ perror("can't map GPIO control via /dev/mem");
+ return -1;
+ }
+ arch_data.ahb_flash_base = access == BMC_DIRECT ? BMC_FLASH_BASE : PNOR_FLASH_BASE;
+ arch_data.ahb_flash_size = access == BMC_DIRECT ? BMC_FLASH_SIZE : PNOR_FLASH_SIZE;
+ arch_data.ahb_flash_map = mmap(0, arch_data.ahb_flash_size, PROT_READ |
+ PROT_WRITE, MAP_SHARED, arch_data.fd, arch_data.ahb_flash_base);
+ if (arch_data.ahb_flash_map == MAP_FAILED) {
+ perror("can't map flash via /dev/mem");
+ return -1;
+ }
+ return 0;
+}
+
+static struct blocklevel_device *flash_setup(enum flash_access access)
+{
+ int rc;
+ struct blocklevel_device *bl;
+ struct spi_flash_ctrl *fl;
+
+ if (access != BMC_DIRECT && access != PNOR_DIRECT)
+ return NULL;
+
+ /* Open and map devices */
+ rc = open_devs(access);
+ if (rc)
+ return NULL;
+
+ /* Create the AST flash controller */
+ rc = ast_sf_open(access == BMC_DIRECT ? AST_SF_TYPE_BMC : AST_SF_TYPE_PNOR, &fl);
+ if (rc) {
+ fprintf(stderr, "Failed to open controller\n");
+ return NULL;
+ }
+
+ /* Open flash chip */
+ rc = flash_init(fl, &bl, &arch_data.flash_chip);
+ if (rc) {
+ fprintf(stderr, "Failed to open flash chip\n");
+ return NULL;
+ }
+
+ return bl;
+}
+
+static bool is_bmc_part(const char *str) {
+ /*
+ * On AMI firmmware "fullpart" is what they called the BMC partition
+ * On OpenBMC "bmc" is what they called the BMC partition
+ */
+ return strstr(str, "fullpart") || strstr(str, "bmc");
+}
+
+static bool is_pnor_part(const char *str) {
+ /*
+ * On AMI firmware "PNOR" is what they called the full PNOR
+ * On OpenBMC "pnor" is what they called the full PNOR
+ */
+ return strcasestr(str, "pnor");
+}
+
+static char *get_dev_mtd(enum flash_access access)
+{
+ FILE *f;
+ char *ret = NULL, *pos = NULL;
+ char line[255];
+
+ if (access != BMC_MTD && access != PNOR_MTD)
+ return NULL;
+
+ f = fopen("/proc/mtd", "r");
+ if (!f)
+ return NULL;
+
+ while (!pos && fgets(line, sizeof(line), f) != NULL) {
+ /* Going to have issues if we didn't get the full line */
+ if (line[strlen(line) - 1] != '\n')
+ break;
+
+ if (access == BMC_MTD && is_bmc_part(line)) {
+ pos = strchr(line, ':');
+ if (!pos)
+ break;
+
+ } else if (access == PNOR_MTD && is_pnor_part(line)) {
+ pos = strchr(line, ':');
+ if (!pos)
+ break;
+ }
+ }
+ if (pos) {
+ *pos = '\0';
+ if (asprintf(&ret, "/dev/%s", line) == -1)
+ ret = NULL;
+ }
+
+ fclose(f);
+ return ret;
+}
+
+enum flash_access arch_flash_access(struct blocklevel_device *bl,
+ enum flash_access access)
+{
+ if (access == ACCESS_INVAL)
+ return ACCESS_INVAL;
+
+ if (!arch_data.init_bl) {
+ arch_data.access = access;
+ return access;
+ }
+
+ /* Called with a BL not inited here, bail */
+ if (arch_data.init_bl != bl)
+ return ACCESS_INVAL;
+
+ return arch_data.flash_chip ? arch_data.access : ACCESS_INVAL;
+}
+
+int arch_flash_erase_chip(struct blocklevel_device *bl)
+{
+ /* Called with a BL not inited here, bail */
+ if (!arch_data.init_bl || arch_data.init_bl != bl)
+ return -1;
+
+ if (!arch_data.flash_chip) {
+ /* Just assume its a regular erase */
+ int rc;
+ uint64_t total_size;
+
+ rc = blocklevel_get_info(bl, NULL, &total_size, NULL);
+ if (rc)
+ return rc;
+
+ return blocklevel_erase(bl, 0, total_size);
+ }
+
+ return flash_erase_chip(arch_data.flash_chip);
+}
+
+int arch_flash_4b_mode(struct blocklevel_device *bl, int set_4b)
+{
+ /* Called with a BL not inited here, bail */
+ if (!arch_data.init_bl || arch_data.init_bl != bl)
+ return -1;
+
+ if (!arch_data.flash_chip)
+ return -1;
+
+ return flash_force_4b_mode(arch_data.flash_chip, set_4b);
+}
+
+int arch_flash_set_wrprotect(struct blocklevel_device *bl, int set)
+{
+ /* Called with a BL not inited here, bail */
+ if (!arch_data.init_bl || arch_data.init_bl != bl)
+ return -1;
+
+ if (arch_data.access == PNOR_MTD || arch_data.access == BMC_MTD)
+ return 0; /* Kernel looks after this for us */
+
+ if (!arch_data.flash_chip)
+ return -1;
+
+ return set_wrprotect(set);
+}
+
+int arch_flash_init(struct blocklevel_device **r_bl, const char *file, bool keep_alive)
+{
+ struct blocklevel_device *new_bl;
+ int rc = 0;
+
+ /* Check we haven't already inited */
+ if (arch_data.init_bl)
+ return -1;
+
+ if (file) {
+ rc = file_init_path(file, NULL, keep_alive, &new_bl);
+ } else if (arch_data.access == BMC_MTD || arch_data.access == PNOR_MTD) {
+ char *mtd_dev;
+
+ mtd_dev = get_dev_mtd(arch_data.access);
+ if (!mtd_dev) {
+ return -1;
+ }
+ rc = file_init_path(mtd_dev, NULL, keep_alive, &new_bl);
+ free(mtd_dev);
+ } else {
+ new_bl = flash_setup(arch_data.access);
+ if (!new_bl)
+ rc = -1;
+ }
+ if (rc)
+ return rc;
+
+ arch_data.init_bl = new_bl;
+ *r_bl = new_bl;
+ return 0;
+}
+
+void arch_flash_close(struct blocklevel_device *bl, const char *file)
+{
+ if (file || arch_data.access == BMC_MTD || arch_data.access == PNOR_MTD) {
+ file_exit_close(bl);
+ } else {
+ flash_exit_close(bl, &ast_sf_close);
+ close_devs();
+ }
+}
diff --git a/roms/skiboot/external/common/arch_flash_arm_io.h b/roms/skiboot/external/common/arch_flash_arm_io.h
new file mode 100644
index 000000000..85dbaf482
--- /dev/null
+++ b/roms/skiboot/external/common/arch_flash_arm_io.h
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Copyright 2015 IBM Corp.
+ */
+#ifndef __IO_H
+#define __IO_H
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <libflash/libflash.h>
+
+/* AST AHB register base */
+#define AHB_REGS_BASE 0x1E600000
+#define AHB_REGS_SIZE 0x00200000
+
+/* AST GPIO control regs */
+#define GPIO_CTRL_BASE 0x1E780000
+#define GPIO_CTRL_SIZE 0x1000
+
+/* AST AHB mapping of PNOR */
+#define PNOR_FLASH_BASE 0x30000000
+#define PNOR_FLASH_SIZE 0x04000000
+
+/* AST AHB mapping of BMC flash */
+#define BMC_FLASH_BASE 0x20000000
+#define BMC_FLASH_SIZE 0x04000000
+
+/* Address of flash mapping on LPC FW space */
+#define LPC_FLASH_BASE 0x0e000000
+#define LPC_CTRL_BASE 0x1e789000
+
+static inline uint8_t readb(void *addr)
+{
+ asm volatile("" : : : "memory");
+ return *(volatile uint8_t *)addr;
+}
+
+static inline uint16_t readw(void *addr)
+{
+ asm volatile("" : : : "memory");
+ return *(volatile uint16_t *)addr;
+}
+
+static inline uint32_t readl(void *addr)
+{
+ asm volatile("" : : : "memory");
+ return *(volatile uint32_t *)addr;
+}
+
+static inline void writeb(uint8_t val, void *addr)
+{
+ asm volatile("" : : : "memory");
+ *(volatile uint8_t *)addr = val;
+}
+
+static inline void writew(uint16_t val, void *addr)
+{
+ asm volatile("" : : : "memory");
+ *(volatile uint16_t *)addr = val;
+}
+
+static inline void writel(uint32_t val, void *addr)
+{
+ asm volatile("" : : : "memory");
+ *(volatile uint32_t *)addr = val;
+}
+
+/*
+ * AHB register and flash access
+ */
+
+extern uint32_t ast_ahb_readl(uint32_t offset);
+extern void ast_ahb_writel(uint32_t val, uint32_t offset);
+extern int ast_copy_to_ahb(uint32_t reg, const void *src, uint32_t len);
+extern int ast_copy_from_ahb(void *dst, uint32_t reg, uint32_t len);
+
+static inline void check_platform(bool *has_sfc, bool *has_ast)
+{
+ *has_sfc = false;
+ *has_ast = true;
+}
+
+#endif /* __IO_H */
+
diff --git a/roms/skiboot/external/common/arch_flash_common.c b/roms/skiboot/external/common/arch_flash_common.c
new file mode 100644
index 000000000..c7a6e95ca
--- /dev/null
+++ b/roms/skiboot/external/common/arch_flash_common.c
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/*
+ * Default implementations
+ *
+ * Copyright 2015-2017 IBM Corp.
+ */
+
+#include <stdlib.h>
+#include <libflash/blocklevel.h>
+#include "arch_flash.h"
+
+/*
+ * This just assumes that an erase from zero to total size is
+ * 'correct'.
+ * An erase from zero to total size is the correct approach for
+ * powerpc and x86. ARM has it own function which also includes a call
+ * to the flash driver.
+ */
+int __attribute__((weak)) arch_flash_erase_chip(struct blocklevel_device *bl)
+{
+ int rc;
+ uint64_t total_size;
+
+ rc = blocklevel_get_info(bl, NULL, &total_size, NULL);
+ if (rc)
+ return rc;
+
+ return blocklevel_erase(bl, 0, total_size);
+}
+
+int __attribute__((weak,const)) arch_flash_4b_mode(struct blocklevel_device *bl, int set_4b)
+{
+ (void)bl;
+ (void)set_4b;
+ return -1;
+}
+
+enum flash_access __attribute__((weak,const)) arch_flash_access(struct blocklevel_device *bl, enum flash_access access)
+{
+ (void)bl;
+ (void)access;
+ return ACCESS_INVAL;
+}
+
+int __attribute__((weak,const)) arch_flash_set_wrprotect(struct blocklevel_device *bl, int set)
+{
+ (void)bl;
+ (void)set;
+ return -1;
+}
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);
+}
diff --git a/roms/skiboot/external/common/arch_flash_powerpc_io.h b/roms/skiboot/external/common/arch_flash_powerpc_io.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/common/arch_flash_powerpc_io.h
diff --git a/roms/skiboot/external/common/arch_flash_unknown.c b/roms/skiboot/external/common/arch_flash_unknown.c
new file mode 100644
index 000000000..00a355600
--- /dev/null
+++ b/roms/skiboot/external/common/arch_flash_unknown.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: Apache-2.0
+/* Copyright 2013-2017 IBM Corp.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <libflash/file.h>
+
+#include "arch_flash.h"
+
+int arch_flash_init(struct blocklevel_device **r_bl, const char *file, bool keep_alive)
+{
+ int rc;
+ struct blocklevel_device *new_bl;
+
+ /* Must have passed through a file to operate on */
+ if (!file) {
+ fprintf(stderr, "Cannot operate without a file\n");
+ return -1;
+ }
+
+ rc = file_init_path(file, NULL, keep_alive, &new_bl);
+ if (rc)
+ 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);
+}
diff --git a/roms/skiboot/external/common/arch_flash_unknown_io.h b/roms/skiboot/external/common/arch_flash_unknown_io.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/roms/skiboot/external/common/arch_flash_unknown_io.h
diff --git a/roms/skiboot/external/common/get_arch.sh b/roms/skiboot/external/common/get_arch.sh
new file mode 100755
index 000000000..682710fcb
--- /dev/null
+++ b/roms/skiboot/external/common/get_arch.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+echo "#if defined(__powerpc__)
+echo -n ARCH_POWERPC
+#elif defined(__arm__)
+echo -n ARCH_ARM
+#else
+echo -n ARCH_UNKNOWN
+#endif" | $1cpp | /bin/sh
+
diff --git a/roms/skiboot/external/common/rules.mk b/roms/skiboot/external/common/rules.mk
new file mode 100644
index 000000000..7190813b8
--- /dev/null
+++ b/roms/skiboot/external/common/rules.mk
@@ -0,0 +1,76 @@
+# SPDX-License-Identifier: Apache-2.0
+
+CC ?= $(CROSS_COMPILE)gcc
+LD ?= $(CROSS_COMPILE)ld
+ARCH := $(shell $(GET_ARCH) "$(CROSS_COMPILE)")
+
+ifeq ($(ARCH),ARCH_ARM)
+arch := arm
+ARCH_FILES := arch_flash_common.c arch_flash_arm.c ast-sf-ctrl.c
+else
+ifeq ($(ARCH),ARCH_POWERPC)
+arch := powerpc
+ARCH_FILES := arch_flash_common.c arch_flash_powerpc.c
+else
+arch := unknown
+ARCH_FILES := arch_flash_common.c arch_flash_unknown.c
+endif
+endif
+
+# Use make V=1 for a verbose build.
+ifndef V
+ Q_CC= @echo ' CC ' $@;
+ Q_LINK= @echo ' LINK ' $@;
+ Q_LN= @echo ' LN ' $@;
+ Q_MKDIR=@echo ' MKDIR ' $@;
+endif
+
+
+.PHONY: links
+links: libflash ccan common
+
+libflash:
+ $(Q_LN)ln -sf ../../libflash ./libflash
+
+ccan:
+ $(Q_LN)ln -sf ../../ccan ./ccan
+
+common:
+ $(Q_LN)ln -sf ../common ./common
+
+make_version.sh:
+ $(Q_LN)ln -sf ../../make_version.sh
+
+ARCH_SRC := $(addprefix common/,$(ARCH_FILES))
+ARCH_OBJS := $(addprefix common-,$(ARCH_FILES:.c=.o))
+
+# Arch links are like this so we can have dependencies work (so that we don't
+# run the rule when the links exist), pretty build output (knowing the target
+# name) and a list of the files so we can clean them up.
+ARCH_LINKS ?= common/ast-sf-ctrl.c common/ast.h common/io.h common/lpc.h
+
+arch_links: $(ARCH_LINKS)
+common/ast.h : ../../include/ast.h | common
+ $(Q_LN)ln -sf ../../include/ast.h common/ast.h
+
+common/io.h : ../common/arch_flash_$(arch)_io.h | common
+ $(Q_LN)ln -sf arch_flash_$(arch)_io.h common/io.h
+
+common/ast-sf-ctrl.c : ../../hw/ast-bmc/ast-sf-ctrl.c | common
+ $(Q_LN)ln -sf ../../hw/ast-bmc/ast-sf-ctrl.c common/ast-sf-ctrl.c
+
+common/lpc.h: ../../include/lpc.h | common
+ $(Q_LN)ln -sf ../../include/lpc.h common/lpc.h
+
+.PHONY: arch_clean
+arch_clean:
+ rm -rf $(ARCH_OBJS) $(ARCH_LINKS)
+
+$(ARCH_SRC): | common
+
+$(ARCH_OBJS): common-%.o: common/%.c $(ARCH_LINKS)
+ $(Q_CC)$(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
+
+common-arch_flash.o: $(ARCH_OBJS)
+ $(Q_LD)$(LD) -r $(ARCH_OBJS) -o $@
+