[Skiboot] [PATCH 2/2] flash: Make size 64 bit safe
Michael Neuling
mikey at neuling.org
Wed Jul 27 12:19:14 AEST 2016
This makes the size of flash 64 bit safe so that we can have flash
devices greater than 4GB. This is especially useful for mambo disks
passed through to Linux.
Fortunately the device tree interface and the linux device driver as
64bit safe so no changes are required there.
Userspace gard and flash tools are also updated to ensure "make check"
still passes.
Signed-off-by: Michael Neuling <mikey at neuling.org>
---
core/flash.c | 9 +++++----
external/gard/gard.c | 11 ++++++++++-
libflash/blocklevel.c | 24 +++++++++++++-----------
libflash/blocklevel.h | 22 +++++++++++-----------
libflash/ecc.c | 15 ++++++++-------
libflash/ecc.h | 12 ++++++------
libflash/file.c | 14 +++++++-------
libflash/libffs.c | 4 +++-
libflash/libflash.c | 14 ++++++++------
libflash/test/test-blocklevel.c | 4 ++--
libflash/test/test-flash.c | 3 ++-
platforms/mambo/mambo.c | 12 ++++++------
12 files changed, 81 insertions(+), 63 deletions(-)
diff --git a/core/flash.c b/core/flash.c
index d3bda65..41bcb13 100644
--- a/core/flash.c
+++ b/core/flash.c
@@ -29,7 +29,7 @@ struct flash {
struct list_node list;
bool busy;
struct blocklevel_device *bl;
- uint32_t size;
+ uint64_t size;
uint32_t block_size;
int id;
};
@@ -192,7 +192,7 @@ static struct dt_node *flash_add_dt_node(struct flash *flash, int id)
flash_node = dt_new_addr(opal_node, "flash", id);
dt_add_property_strings(flash_node, "compatible", "ibm,opal-flash");
dt_add_property_cells(flash_node, "ibm,opal-id", id);
- dt_add_property_cells(flash_node, "reg", 0, flash->size);
+ dt_add_property_u64(flash_node, "reg", flash->size);
dt_add_property_cells(flash_node, "ibm,flash-block-size",
flash->block_size);
@@ -256,7 +256,8 @@ static int num_flashes(void)
int flash_register(struct blocklevel_device *bl, bool is_system_flash)
{
- uint32_t size, block_size;
+ uint64_t size;
+ uint32_t block_size;
struct ffs_handle *ffs;
struct dt_node *node;
struct flash *flash;
@@ -268,7 +269,7 @@ int flash_register(struct blocklevel_device *bl, bool is_system_flash)
return rc;
prlog(PR_INFO, "FLASH: registering flash device %s "
- "(size 0x%x, blocksize 0x%x)\n",
+ "(size 0x%llx, blocksize 0x%x)\n",
name ?: "(unnamed)", size, block_size);
lock(&flash_lock);
diff --git a/external/gard/gard.c b/external/gard/gard.c
index 9da7496..7ec52d8 100644
--- a/external/gard/gard.c
+++ b/external/gard/gard.c
@@ -575,6 +575,7 @@ int main(int argc, char **argv)
const char *action, *progname;
char *filename = NULL;
struct gard_ctx _ctx, *ctx;
+ uint64_t bl_size;
int rc, i = 0;
bool part = 0;
bool ecc = 0;
@@ -643,10 +644,18 @@ int main(int argc, char **argv)
goto out_free;
}
- rc = blocklevel_get_info(ctx->bl, NULL, &(ctx->f_size), NULL);
+ rc = blocklevel_get_info(ctx->bl, NULL, &bl_size, NULL);
if (rc)
goto out;
+ if (bl_size > UINT_MAX) {
+ fprintf(stderr, "MTD device bigger than %i: size:%lu\n",
+ UINT_MAX, bl_size);
+ rc = EXIT_FAILURE;
+ goto out;
+ }
+ ctx->f_size = bl_size;
+
if (!part) {
rc = ffs_init(0, ctx->f_size, ctx->bl, &ctx->ffs, 1);
if (rc)
diff --git a/libflash/blocklevel.c b/libflash/blocklevel.c
index 9591194..9f02ee1 100644
--- a/libflash/blocklevel.c
+++ b/libflash/blocklevel.c
@@ -20,6 +20,7 @@
#include <stdbool.h>
#include <errno.h>
#include <string.h>
+#include <inttypes.h>
#include <libflash/errors.h>
@@ -33,7 +34,7 @@
* 0 - The region is not ECC protected
* -1 - Partially protected
*/
-static int ecc_protected(struct blocklevel_device *bl, uint32_t pos, uint32_t len)
+static int ecc_protected(struct blocklevel_device *bl, uint64_t pos, uint64_t len)
{
int i;
@@ -77,11 +78,11 @@ static int release(struct blocklevel_device *bl)
return rc;
}
-int blocklevel_read(struct blocklevel_device *bl, uint32_t pos, void *buf, uint32_t len)
+int blocklevel_read(struct blocklevel_device *bl, uint64_t pos, void *buf, uint64_t len)
{
int rc;
struct ecc64 *buffer;
- uint32_t ecc_len = ecc_buffer_size(len);
+ uint64_t ecc_len = ecc_buffer_size(len);
if (!bl || !bl->read || !buf) {
errno = EINVAL;
@@ -120,11 +121,11 @@ out:
return rc;
}
-int blocklevel_write(struct blocklevel_device *bl, uint32_t pos, const void *buf, uint32_t len)
+int blocklevel_write(struct blocklevel_device *bl, uint64_t pos, const void *buf, uint64_t len)
{
int rc;
struct ecc64 *buffer;
- uint32_t ecc_len = ecc_buffer_size(len);
+ uint64_t ecc_len = ecc_buffer_size(len);
if (!bl || !bl->write || !buf) {
errno = EINVAL;
@@ -162,7 +163,7 @@ out:
return rc;
}
-int blocklevel_erase(struct blocklevel_device *bl, uint32_t pos, uint32_t len)
+int blocklevel_erase(struct blocklevel_device *bl, uint64_t pos, uint64_t len)
{
int rc;
if (!bl || !bl->erase) {
@@ -172,7 +173,7 @@ int blocklevel_erase(struct blocklevel_device *bl, uint32_t pos, uint32_t len)
/* Programmer may be making a horrible mistake without knowing it */
if (len & bl->erase_mask) {
- fprintf(stderr, "blocklevel_erase: len (0x%08x) is not erase block (0x%08x) aligned\n",
+ fprintf(stderr, "blocklevel_erase: len (0x%"PRIu64") is not erase block (0x%08x) aligned\n",
len, bl->erase_mask + 1);
return FLASH_ERR_ERASE_BOUNDARY;
}
@@ -188,7 +189,7 @@ int blocklevel_erase(struct blocklevel_device *bl, uint32_t pos, uint32_t len)
return rc;
}
-int blocklevel_get_info(struct blocklevel_device *bl, const char **name, uint32_t *total_size,
+int blocklevel_get_info(struct blocklevel_device *bl, const char **name, uint64_t *total_size,
uint32_t *erase_granule)
{
int rc;
@@ -224,9 +225,10 @@ int blocklevel_get_info(struct blocklevel_device *bl, const char **name, uint32_
* returns 0 for b
* returns 1 for c
*/
-static int blocklevel_flashcmp(const void *flash_buf, const void *mem_buf, uint32_t len)
+static int blocklevel_flashcmp(const void *flash_buf, const void *mem_buf, uint64_t len)
{
- int i, same = true;
+ uint64_t i;
+ int same = true;
const uint8_t *f_buf, *m_buf;
f_buf = flash_buf;
@@ -242,7 +244,7 @@ static int blocklevel_flashcmp(const void *flash_buf, const void *mem_buf, uint3
return same ? 0 : 1;
}
-int blocklevel_smart_write(struct blocklevel_device *bl, uint32_t pos, const void *buf, uint32_t len)
+int blocklevel_smart_write(struct blocklevel_device *bl, uint64_t pos, const void *buf, uint64_t len)
{
uint32_t erase_size;
const void *write_buf = buf;
diff --git a/libflash/blocklevel.h b/libflash/blocklevel.h
index 9770e11..a67276c 100644
--- a/libflash/blocklevel.h
+++ b/libflash/blocklevel.h
@@ -20,8 +20,8 @@
#include <stdbool.h>
struct bl_prot_range {
- uint32_t start;
- uint32_t len;
+ uint64_t start;
+ uint64_t len;
};
struct blocklevel_range {
@@ -42,10 +42,10 @@ struct blocklevel_device {
void *priv;
int (*reacquire)(struct blocklevel_device *bl);
int (*release)(struct blocklevel_device *bl);
- int (*read)(struct blocklevel_device *bl, uint32_t pos, void *buf, uint32_t len);
- int (*write)(struct blocklevel_device *bl, uint32_t pos, const void *buf, uint32_t len);
- int (*erase)(struct blocklevel_device *bl, uint32_t pos, uint32_t len);
- int (*get_info)(struct blocklevel_device *bl, const char **name, uint32_t *total_size,
+ int (*read)(struct blocklevel_device *bl, uint64_t pos, void *buf, uint64_t len);
+ int (*write)(struct blocklevel_device *bl, uint64_t pos, const void *buf, uint64_t len);
+ int (*erase)(struct blocklevel_device *bl, uint64_t pos, uint64_t len);
+ int (*get_info)(struct blocklevel_device *bl, const char **name, uint64_t *total_size,
uint32_t *erase_granule);
/*
@@ -58,10 +58,10 @@ struct blocklevel_device {
struct blocklevel_range ecc_prot;
};
-int blocklevel_read(struct blocklevel_device *bl, uint32_t pos, void *buf, uint32_t len);
-int blocklevel_write(struct blocklevel_device *bl, uint32_t pos, const void *buf, uint32_t len);
-int blocklevel_erase(struct blocklevel_device *bl, uint32_t pos, uint32_t len);
-int blocklevel_get_info(struct blocklevel_device *bl, const char **name, uint32_t *total_size,
+int blocklevel_read(struct blocklevel_device *bl, uint64_t pos, void *buf, uint64_t len);
+int blocklevel_write(struct blocklevel_device *bl, uint64_t pos, const void *buf, uint64_t len);
+int blocklevel_erase(struct blocklevel_device *bl, uint64_t pos, uint64_t len);
+int blocklevel_get_info(struct blocklevel_device *bl, const char **name, uint64_t *total_size,
uint32_t *erase_granule);
/*
@@ -72,7 +72,7 @@ int blocklevel_get_info(struct blocklevel_device *bl, const char **name, uint32_
* or slower than the just using blocklevel_erase/write calls.
* directly.
*/
-int blocklevel_smart_write(struct blocklevel_device *bl, uint32_t pos, const void *buf, uint32_t len);
+int blocklevel_smart_write(struct blocklevel_device *bl, uint64_t pos, const void *buf, uint64_t len);
/* Implemented in software at this level */
int blocklevel_ecc_protect(struct blocklevel_device *bl, uint32_t start, uint32_t len);
diff --git a/libflash/ecc.c b/libflash/ecc.c
index 9cd6ad2..0be80b1 100644
--- a/libflash/ecc.c
+++ b/libflash/ecc.c
@@ -17,6 +17,7 @@
/* This is based on the hostboot ecc code */
#include <stdint.h>
+#include <inttypes.h>
#include <ccan/endian/endian.h>
@@ -181,7 +182,7 @@ static inline uint64_t eccflipbit(uint64_t data, uint8_t bit)
* @retval: 0 - success
* @retfal: other - fail
*/
-int memcpy_from_ecc(uint64_t *dst, struct ecc64 *src, uint32_t len)
+int memcpy_from_ecc(uint64_t *dst, struct ecc64 *src, uint64_t len)
{
beint64_t data;
uint8_t ecc;
@@ -190,8 +191,8 @@ int memcpy_from_ecc(uint64_t *dst, struct ecc64 *src, uint32_t len)
if (len & 0x7) {
/* TODO: we could probably handle this */
- FL_ERR("ECC data length must be 8 byte aligned length:%i\n",
- len);
+ FL_ERR("ECC data length must be 8 byte aligned length:%" PRIx64 "\n",
+ len);
return -1;
}
@@ -232,15 +233,15 @@ int memcpy_from_ecc(uint64_t *dst, struct ecc64 *src, uint32_t len)
* @retval: 0 - success
* @retfal: other - fail
*/
-int memcpy_to_ecc(struct ecc64 *dst, const uint64_t *src, uint32_t len)
+int memcpy_to_ecc(struct ecc64 *dst, const uint64_t *src, uint64_t len)
{
struct ecc64 ecc_word;
- uint32_t i;
+ uint64_t i;
if (len & 0x7) {
/* TODO: we could probably handle this */
- FL_ERR("Data to add ECC bytes to must be 8 byte aligned length: %i\n",
- len);
+ FL_ERR("Data to add ECC bytes to must be 8 byte aligned length: %"
+ PRIx64 "\n", len);
return -1;
}
diff --git a/libflash/ecc.h b/libflash/ecc.h
index 1e14724..804b352 100644
--- a/libflash/ecc.h
+++ b/libflash/ecc.h
@@ -27,9 +27,9 @@ struct ecc64 {
uint8_t ecc;
} __attribute__((__packed__));
-extern int memcpy_from_ecc(uint64_t *dst, struct ecc64 *src, uint32_t len);
+extern int memcpy_from_ecc(uint64_t *dst, struct ecc64 *src, uint64_t len);
-extern int memcpy_to_ecc(struct ecc64 *dst, const uint64_t *src, uint32_t len);
+extern int memcpy_to_ecc(struct ecc64 *dst, const uint64_t *src, uint64_t len);
/*
* Calculate the size of a buffer if ECC is added
@@ -43,22 +43,22 @@ extern int memcpy_to_ecc(struct ecc64 *dst, const uint64_t *src, uint32_t len);
#define BYTES_PER_ECC 8
-static inline uint32_t ecc_size(uint32_t len)
+static inline uint64_t ecc_size(uint64_t len)
{
return ALIGN_UP(len, BYTES_PER_ECC) >> 3;
}
-static inline uint32_t ecc_buffer_size(uint32_t len)
+static inline uint64_t ecc_buffer_size(uint64_t len)
{
return ALIGN_UP(len, BYTES_PER_ECC) + ecc_size(len);
}
-static inline int ecc_buffer_size_check(uint32_t len)
+static inline int ecc_buffer_size_check(uint64_t len)
{
return len % (BYTES_PER_ECC + 1);
}
-static inline uint32_t ecc_buffer_size_minus_ecc(uint32_t len)
+static inline uint64_t ecc_buffer_size_minus_ecc(uint64_t len)
{
return len * BYTES_PER_ECC / (BYTES_PER_ECC + 1);
}
diff --git a/libflash/file.c b/libflash/file.c
index 478bc13..946726c 100644
--- a/libflash/file.c
+++ b/libflash/file.c
@@ -59,7 +59,7 @@ static int file_reacquire(struct blocklevel_device *bl)
return 0;
}
-static int file_read(struct blocklevel_device *bl, uint32_t pos, void *buf, uint32_t len)
+static int file_read(struct blocklevel_device *bl, uint64_t pos, void *buf, uint64_t len)
{
struct file_data *file_data = container_of(bl, struct file_data, bl);
int rc, count = 0;
@@ -81,8 +81,8 @@ static int file_read(struct blocklevel_device *bl, uint32_t pos, void *buf, uint
return 0;
}
-static int file_write(struct blocklevel_device *bl, uint32_t dst, const void *src,
- uint32_t len)
+static int file_write(struct blocklevel_device *bl, uint64_t dst, const void *src,
+ uint64_t len)
{
struct file_data *file_data = container_of(bl, struct file_data, bl);
int rc, count = 0;
@@ -111,7 +111,7 @@ static int file_write(struct blocklevel_device *bl, uint32_t dst, const void *sr
* Also, erasing flash leaves all the bits set to 1. This may be expected
* by higher level functions so this function should also emulate that
*/
-static int file_erase(struct blocklevel_device *bl, uint32_t dst, uint32_t len)
+static int file_erase(struct blocklevel_device *bl, uint64_t dst, uint64_t len)
{
unsigned long long int d = ULLONG_MAX;
int i = 0;
@@ -127,7 +127,7 @@ static int file_erase(struct blocklevel_device *bl, uint32_t dst, uint32_t len)
return 0;
}
-static int mtd_erase(struct blocklevel_device *bl, uint32_t dst, uint32_t len)
+static int mtd_erase(struct blocklevel_device *bl, uint64_t dst, uint64_t len)
{
struct file_data *file_data = container_of(bl, struct file_data, bl);
struct erase_info_user erase_info = {
@@ -177,7 +177,7 @@ static int get_info_name(struct file_data *file_data, char **name)
static int mtd_get_info(struct blocklevel_device *bl, const char **name,
- uint32_t *total_size, uint32_t *erase_granule)
+ uint64_t *total_size, uint32_t *erase_granule)
{
struct file_data *file_data = container_of(bl, struct file_data, bl);
struct mtd_info_user mtd_info;
@@ -204,7 +204,7 @@ static int mtd_get_info(struct blocklevel_device *bl, const char **name,
}
static int file_get_info(struct blocklevel_device *bl, const char **name,
- uint32_t *total_size, uint32_t *erase_granule)
+ uint64_t *total_size, uint32_t *erase_granule)
{
struct file_data *file_data = container_of(bl, struct file_data, bl);
struct stat st;
diff --git a/libflash/libffs.c b/libflash/libffs.c
index 8134962..65c3fbe 100644
--- a/libflash/libffs.c
+++ b/libflash/libffs.c
@@ -78,7 +78,7 @@ int ffs_init(uint32_t offset, uint32_t max_size, struct blocklevel_device *bl,
struct ffs_hdr hdr;
struct ffs_hdr blank_hdr;
struct ffs_handle *f;
- uint32_t total_size;
+ uint64_t total_size;
int rc, i;
if (!ffs || !bl)
@@ -90,6 +90,8 @@ int ffs_init(uint32_t offset, uint32_t max_size, struct blocklevel_device *bl,
FL_ERR("FFS: Error %d retrieving flash info\n", rc);
return rc;
}
+ if (total_size > UINT_MAX)
+ return FLASH_ERR_VERIFY_FAILURE;
if ((offset + max_size) < offset)
return FLASH_ERR_PARM_ERROR;
diff --git a/libflash/libflash.c b/libflash/libflash.c
index d3e594a..cf679a5 100644
--- a/libflash/libflash.c
+++ b/libflash/libflash.c
@@ -16,6 +16,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <inttypes.h>
#include "libflash.h"
#include "libflash-priv.h"
@@ -124,7 +125,7 @@ int fl_wren(struct spi_flash_ctrl *ct)
return FLASH_ERR_WREN_TIMEOUT;
}
-static int flash_read(struct blocklevel_device *bl, uint32_t pos, void *buf, uint32_t len)
+static int flash_read(struct blocklevel_device *bl, uint64_t pos, void *buf, uint64_t len)
{
struct flash_chip *c = container_of(bl, struct flash_chip, bl);
struct spi_flash_ctrl *ct = c->ctrl;
@@ -228,7 +229,7 @@ static void fl_get_best_erase(struct flash_chip *c, uint32_t dst, uint32_t size,
*cmd = CMD_BE;
}
-static int flash_erase(struct blocklevel_device *bl, uint32_t dst, uint32_t size)
+static int flash_erase(struct blocklevel_device *bl, uint64_t dst, uint64_t size)
{
struct flash_chip *c = container_of(bl, struct flash_chip, bl);
struct spi_flash_ctrl *ct = c->ctrl;
@@ -244,7 +245,8 @@ static int flash_erase(struct blocklevel_device *bl, uint32_t dst, uint32_t size
if ((dst | size) & c->min_erase_mask)
return FLASH_ERR_ERASE_BOUNDARY;
- FL_DBG("LIBFLASH: Erasing 0x%08x..0%08x...\n", dst, dst + size);
+ FL_DBG("LIBFLASH: Erasing 0x%" PRIx64"..0%" PRIx64 "...\n",
+ dst, dst + size);
/* Use controller erase if supported */
if (ct->erase)
@@ -479,7 +481,7 @@ static enum sm_comp_res flash_smart_comp(struct flash_chip *c,
return is_same ? sm_no_change : sm_need_write;
}
-static int flash_smart_write(struct blocklevel_device *bl, uint32_t dst, const void *src, uint32_t size)
+static int flash_smart_write(struct blocklevel_device *bl, uint64_t dst, const void *src, uint64_t size)
{
struct flash_chip *c = container_of(bl, struct flash_chip, bl);
uint32_t er_size = c->min_erase_mask + 1;
@@ -492,7 +494,7 @@ static int flash_smart_write(struct blocklevel_device *bl, uint32_t dst, const v
return FLASH_ERR_PARM_ERROR;
}
- FL_DBG("LIBFLASH: Smart writing to 0x%08x..0%08x...\n",
+ FL_DBG("LIBFLASH: Smart writing to 0x%" PRIx64 "..0%" PRIx64 "...\n",
dst, dst + size);
/* As long as we have something to write ... */
@@ -792,7 +794,7 @@ static int flash_configure(struct flash_chip *c)
}
static int flash_get_info(struct blocklevel_device *bl, const char **name,
- uint32_t *total_size, uint32_t *erase_granule)
+ uint64_t *total_size, uint32_t *erase_granule)
{
struct flash_chip *c = container_of(bl, struct flash_chip, bl);
if (name)
diff --git a/libflash/test/test-blocklevel.c b/libflash/test/test-blocklevel.c
index ef5d9b5..95669dc 100644
--- a/libflash/test/test-blocklevel.c
+++ b/libflash/test/test-blocklevel.c
@@ -171,8 +171,8 @@ int main(void)
for (i = 0; i < bl->ecc_prot.n_prot - 1; i++) {
if (bl->ecc_prot.prot[i].start + bl->ecc_prot.prot[i].len == bl->ecc_prot.prot[i + 1].start ||
bl->ecc_prot.prot[i + 1].start + bl->ecc_prot.prot[i + 1].len == bl->ecc_prot.prot[i].start) {
- ERR("Problem with protection range merge code, region starting at 0x%08x for 0x%08x appears "
- "to touch region 0x%08x for 0x%08x\n", bl->ecc_prot.prot[i].start, bl->ecc_prot.prot[i].len,
+ ERR("Problem with protection range merge code, region starting at 0x%08lx for 0x%08lx appears "
+ "to touch region 0x%lx for 0x%lx\n", bl->ecc_prot.prot[i].start, bl->ecc_prot.prot[i].len,
bl->ecc_prot.prot[i + 1].start, bl->ecc_prot.prot[i + 1].len);
return 1;
}
diff --git a/libflash/test/test-flash.c b/libflash/test/test-flash.c
index c93aef5..3f77d6f 100644
--- a/libflash/test/test-flash.c
+++ b/libflash/test/test-flash.c
@@ -367,7 +367,8 @@ struct spi_flash_ctrl sim_ctrl = {
int main(void)
{
struct blocklevel_device *bl;
- uint32_t total_size, erase_granule;
+ uint64_t total_size;
+ uint32_t erase_granule;
const char *name;
uint16_t *test;
struct ecc64 *ecc_test;
diff --git a/platforms/mambo/mambo.c b/platforms/mambo/mambo.c
index 3c1d546..066f748 100644
--- a/platforms/mambo/mambo.c
+++ b/platforms/mambo/mambo.c
@@ -117,8 +117,8 @@ struct bogus_disk_info {
int id;
};
-static int bogus_disk_read(struct blocklevel_device *bl, uint32_t pos, void *buf,
- uint32_t len)
+static int bogus_disk_read(struct blocklevel_device *bl, uint64_t pos, void *buf,
+ uint64_t len)
{
struct bogus_disk_info *bdi = bl->priv;
int rc;
@@ -140,8 +140,8 @@ static int bogus_disk_read(struct blocklevel_device *bl, uint32_t pos, void *buf
return rc;
}
-static int bogus_disk_write(struct blocklevel_device *bl, uint32_t pos,
- const void *buf, uint32_t len)
+static int bogus_disk_write(struct blocklevel_device *bl, uint64_t pos,
+ const void *buf, uint64_t len)
{
struct bogus_disk_info *bdi = bl->priv;
@@ -154,13 +154,13 @@ static int bogus_disk_write(struct blocklevel_device *bl, uint32_t pos,
}
static int bogus_disk_erase(struct blocklevel_device *bl __unused,
- uint32_t pos __unused, uint32_t len __unused)
+ uint64_t pos __unused, uint64_t len __unused)
{
return 0; /* NOP */
}
static int bogus_disk_get_info(struct blocklevel_device *bl, const char **name,
- uint32_t *total_size, uint32_t *erase_granule)
+ uint64_t *total_size, uint32_t *erase_granule)
{
struct bogus_disk_info *bdi = bl->priv;
--
2.7.4
More information about the Skiboot
mailing list