Commit f1e547c7 authored by Evan Lojewski's avatar Evan Lojewski Committed by Andrew Jeffery

mboxd: Add a backend abstraction layer to mboxd.

Introduce a backend abstraction, enabling multiple implementations to be
compiled in at once. This change formally abstracts the two existing
backends, mtd and vpnor.

With the backend abstraction in place, subsequent backends are easier to
implement.

This change is based of Evan's work and he retains authorship credit. I
(AJ) have reworked the patch to pass the vpnor tests, refactored some
parts to enable broader use of const structures and others to clarify
the initialisation sequences.

Due to the existing lack of abstraction the patch has unfortunately
wide-ranging impacts. I've whittled it down as much as I consider
reasonable.

Change-Id: I29984a36dae4ea86ec00b853d2a756f0b9afb3ec
Signed-off-by: default avatarEvan Lojewski <github@meklort.com>
Signed-off-by: Andrew Jeffery's avatarAndrew Jeffery <andrew@aj.id.au>
parent cb93504e
......@@ -16,11 +16,12 @@ mboxd_SOURCES = \
mboxd_LDFLAGS = $(LIBSYSTEMD_LIBS)
mboxd_CFLAGS = $(LIBSYSTEMD_CFLAGS)
# MTD Backing storage
include mtd/Makefile.am.include
if VIRTUAL_PNOR_ENABLED
# VPNOR Backing storage
include vpnor/Makefile.am.include
else
mboxd_SOURCES += flash.c \
lpc_reset.c
endif
mboxctl_SOURCES = mboxctl.c
......
/* SPDX-License-Identifier: Apache-2.0 */
/* Copyright (C) 2018 IBM Corp. */
/* Copyright (C) 2018 Evan Lojewski. */
#ifndef BACKEND_H
#define BACKEND_H
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <mtd/mtd-abi.h>
#define FLASH_DIRTY 0x00
#define FLASH_ERASED 0x01
/* Estimate as to how long (milliseconds) it takes to access a MB from flash */
#define FLASH_ACCESS_MS_PER_MB 8000
struct backend backend_get_mtd(void);
struct backend backend_get_vpnor(void);
enum backend_reset_mode { reset_lpc_flash, reset_lpc_memory };
struct backend_ops;
struct backend {
const struct backend_ops *ops;
/* Backend private data */
void *priv;
/* Flash size from command line (bytes) */
uint32_t flash_size;
/* Erase size (as a shift) */
uint32_t erase_size_shift;
/* Block size (as a shift) */
uint32_t block_size_shift;
};
struct backend_ops {
/*
* init() - Main initialization function for backing device
* @context: The backend context pointer
* @data: Additional backend-implementation-specifc data
* Return: Zero on success, otherwise negative error
*/
int (*init)(struct backend *backend, void *data);
/*
* free() - Main teardown function for backing device
* @context: The backend context pointer
*/
void (*free)(struct backend *backend);
/*
* copy() - Copy data from the flash device into a provided buffer
* @context: The mbox context pointer
* @offset: The flash offset to copy from (bytes)
* @mem: The buffer to copy into (must be of atleast 'size' bytes)
* @size: The number of bytes to copy
* Return: Number of bytes copied on success, otherwise negative error
* code. flash_copy will copy at most 'size' bytes, but it may
* copy less.
*/
int64_t (*copy)(struct backend *backend, uint32_t offset, void *mem,
uint32_t size);
/*
* set_bytemap() - Set the flash erased bytemap
* @context: The mbox context pointer
* @offset: The flash offset to set (bytes)
* @count: Number of bytes to set
* @val: Value to set the bytemap to
*
* The flash bytemap only tracks the erased status at the erase block level so
* this will update the erased state for an (or many) erase blocks
*
* Return: 0 if success otherwise negative error code
*/
int (*set_bytemap)(struct backend *backend, uint32_t offset,
uint32_t count, uint8_t val);
/*
* erase() - Erase the flash
* @context: The backend context pointer
* @offset: The flash offset to erase (bytes)
* @size: The number of bytes to erase
*
* Return: 0 on success otherwise negative error code
*/
int (*erase)(struct backend *backend, uint32_t offset,
uint32_t count);
/*
* write() - Write the flash from a provided buffer
* @context: The backend context pointer
* @offset: The flash offset to write to (bytes)
* @buf: The buffer to write from (must be of atleast size)
* @size: The number of bytes to write
*
* Return: 0 on success otherwise negative error code
*/
int (*write)(struct backend *backend, uint32_t offset, void *buf,
uint32_t count);
/*
* validate() - Validates a requested window
* @context: The backend context pointer
* @offset: The requested flash offset
* @size: The requested region size
* @ro: The requested access type: True for read-only, false
* for read-write
*
* Return: 0 on valid otherwise negative error code
*/
int (*validate)(struct backend *backend,
uint32_t offset, uint32_t size, bool ro);
/*
* reset() - Ready the reserved memory for host startup
* @context: The backend context pointer
* @buf: The LPC reserved memory pointer
* @count The size of the LPC reserved memory region
*
* Return: 0 on success otherwise negative error code
*/
int (*reset)(struct backend *backend, void *buf, uint32_t count);
};
/* Make this better */
static inline int backend_init(struct backend *master, struct backend *with,
void *data)
{
int rc;
assert(master);
/* FIXME: A bit hacky? */
with->flash_size = master->flash_size;
*master = *with;
#ifndef NDEBUG
/* Set some poison values to ensure backends init properly */
master->erase_size_shift = 33;
master->block_size_shift = 34;
#endif
assert(master->ops->init);
rc = master->ops->init(master, data);
if (rc < 0)
return rc;
assert(master->erase_size_shift < 32);
assert(master->block_size_shift < 32);
return 0;
}
static inline void backend_free(struct backend *backend)
{
assert(backend);
if (backend->ops->free)
backend->ops->free(backend);
}
static inline int64_t backend_copy(struct backend *backend,
uint32_t offset, void *mem, uint32_t size)
{
assert(backend);
assert(backend->ops->copy);
return backend->ops->copy(backend, offset, mem, size);
}
static inline int backend_set_bytemap(struct backend *backend,
uint32_t offset, uint32_t count,
uint8_t val)
{
assert(backend);
if (backend->ops->set_bytemap)
return backend->ops->set_bytemap(backend, offset, count, val);
return 0;
}
static inline int backend_erase(struct backend *backend, uint32_t offset,
uint32_t count)
{
assert(backend);
if (backend->ops->erase)
return backend->ops->erase(backend, offset, count);
return 0;
}
static inline int backend_write(struct backend *backend, uint32_t offset,
void *buf, uint32_t count)
{
assert(backend);
assert(backend->ops->write);
return backend->ops->write(backend, offset, buf, count);
}
static inline int backend_validate(struct backend *backend,
uint32_t offset, uint32_t size, bool ro)
{
assert(backend);
if (backend->ops->validate)
return backend->ops->validate(backend, offset, size, ro);
return 0;
}
static inline int backend_reset(struct backend *backend, void *buf,
uint32_t count)
{
assert(backend);
assert(backend->ops->reset);
return backend->ops->reset(backend, buf, count);
}
int backend_probe_mtd(struct backend *master, const char *path);
/* Avoid dependency on vpnor/mboxd_pnor_partition_table.h */
struct vpnor_partition_paths;
int backend_probe_vpnor(struct backend *master,
const struct vpnor_partition_paths *paths);
#endif /* BACKEND_H */
......@@ -6,7 +6,7 @@
#include "common.h"
#include "dbus.h"
#include "mboxd.h"
#include "flash.h"
#include "backend.h"
#include "lpc.h"
#include "transport_mbox.h"
#include "windows.h"
......@@ -62,7 +62,8 @@ int control_kill(struct mbox_context *context)
int control_modified(struct mbox_context *context)
{
/* Flash has been modified - can no longer trust our erased bytemap */
flash_set_bytemap(context, 0, context->flash_size, FLASH_DIRTY);
flash_set_bytemap(context, 0, context->backend.flash_size,
FLASH_DIRTY);
/* Force daemon to reload all windows -> Set BMC event to notify host */
if (windows_reset_all(context)) {
......
/* SPDX-License-Identifier: Apache-2.0 */
/* Copyright (C) 2018 IBM Corp. */
#ifndef FLASH_H
#define FLASH_H
#include <stdbool.h>
#include <stdint.h>
#define FLASH_DIRTY 0x00
#define FLASH_ERASED 0x01
/* Estimate as to how long (milliseconds) it takes to access a MB from flash */
#define FLASH_ACCESS_MS_PER_MB 8000
struct mbox_context;
int flash_dev_init(struct mbox_context *context);
void flash_dev_free(struct mbox_context *context);
int flash_validate(struct mbox_context *context, uint32_t offset,
uint32_t size, bool ro);
int64_t flash_copy(struct mbox_context *context, uint32_t offset, void *mem,
uint32_t size);
int flash_set_bytemap(struct mbox_context *context, uint32_t offset,
uint32_t count, uint8_t val);
int flash_erase(struct mbox_context *context, uint32_t offset, uint32_t count);
int flash_write(struct mbox_context *context, uint32_t offset, void *buf,
uint32_t count);
#endif /* FLASH_H */
......@@ -27,7 +27,7 @@
#include "mboxd.h"
#include "common.h"
#include "lpc.h"
#include "flash.h"
#include "backend.h"
#include <linux/aspeed-lpc-ctrl.h>
#define LPC_CTRL_PATH "/dev/aspeed-lpc-ctrl"
......@@ -108,9 +108,9 @@ int lpc_map_flash(struct mbox_context *context)
* The mask is because the top nibble is the host LPC FW space,
* we want space 0.
*/
.addr = 0x0FFFFFFF & -context->flash_size,
.addr = 0x0FFFFFFF & -context->backend.flash_size,
.offset = 0,
.size = context->flash_size
.size = context->backend.flash_size
};
if (context->state & MAPS_FLASH) {
......@@ -124,7 +124,7 @@ int lpc_map_flash(struct mbox_context *context)
MSG_INFO("Pointing HOST LPC bus at the flash\n");
MSG_INFO("Assuming %dMB of flash: HOST LPC 0x%08x\n",
context->flash_size >> 20, map.addr);
context->backend.flash_size >> 20, map.addr);
if (ioctl(context->fds[LPC_CTRL_FD].fd, ASPEED_LPC_CTRL_IOCTL_MAP, &map)
== -1) {
......@@ -138,7 +138,8 @@ int lpc_map_flash(struct mbox_context *context)
* Since the host now has access to the flash it can change it out from
* under us
*/
return flash_set_bytemap(context, 0, context->flash_size, FLASH_DIRTY);
return flash_set_bytemap(context, 0, context->backend.flash_size,
FLASH_DIRTY);
}
/*
......
......@@ -10,6 +10,5 @@ int lpc_dev_init(struct mbox_context *context);
void lpc_dev_free(struct mbox_context *context);
int lpc_map_flash(struct mbox_context *context);
int lpc_map_memory(struct mbox_context *context);
int lpc_reset(struct mbox_context *context);
#endif /* LPC_H */
// SPDX-License-Identifier: Apache-2.0
// Copyright (C) 2018 IBM Corp.
#define _GNU_SOURCE
#include "lpc.h"
struct mbox_context;
/*
* lpc_reset() - Reset the lpc bus mapping
* @context: The mbox context pointer
*
* Return: 0 on success otherwise negative error code
*/
int lpc_reset(struct mbox_context *context)
{
return lpc_map_flash(context);
}
......@@ -31,7 +31,7 @@
#include "common.h"
#include "dbus.h"
#include "control_dbus.h"
#include "flash.h"
#include "backend.h"
#include "lpc.h"
#include "transport_mbox.h"
#include "transport_dbus.h"
......@@ -246,7 +246,7 @@ static bool parse_cmdline(int argc, char **argv,
case 0:
break;
case 'f':
context->flash_size = strtol(optarg, &endptr, 10);
context->backend.flash_size = strtol(optarg, &endptr, 10);
if (optarg == endptr) {
fprintf(stderr, "Unparseable flash size\n");
return false;
......@@ -255,9 +255,9 @@ static bool parse_cmdline(int argc, char **argv,
case '\0':
break;
case 'M':
context->flash_size <<= 10;
context->backend.flash_size <<= 10;
case 'K':
context->flash_size <<= 10;
context->backend.flash_size <<= 10;
break;
default:
fprintf(stderr, "Unknown units '%c'\n",
......@@ -307,12 +307,15 @@ static bool parse_cmdline(int argc, char **argv,
}
}
if (!context->flash_size) {
if (!context->path) {
context->path = get_dev_mtd();
}
if (!context->backend.flash_size) {
fprintf(stderr, "Must specify a non-zero flash size\n");
return false;
}
MSG_INFO("Flash size: 0x%.8x\n", context->flash_size);
MSG_INFO("Flash size: 0x%.8x\n", context->backend.flash_size);
if (verbosity) {
MSG_INFO("%s logging\n", verbosity == MBOX_LOG_DEBUG ? "Debug" :
......@@ -322,6 +325,24 @@ static bool parse_cmdline(int argc, char **argv,
return true;
}
static int mboxd_backend_init(struct mbox_context *context)
{
int rc;
#ifdef VIRTUAL_PNOR_ENABLED
struct vpnor_partition_paths paths;
vpnor_default_paths(&paths);
rc = backend_probe_vpnor(&context->backend, &paths);
if(rc)
#endif
{
rc = backend_probe_mtd(&context->backend, context->path);
}
return rc;
}
int main(int argc, char **argv)
{
const struct transport_ops *mbox_ops, *dbus_ops;
......@@ -353,6 +374,11 @@ int main(int argc, char **argv)
goto finish;
}
rc = mboxd_backend_init(context);
if (rc) {
goto finish;
}
rc = protocol_init(context);
if (rc) {
goto finish;
......@@ -374,20 +400,11 @@ int main(int argc, char **argv)
goto finish;
}
rc = flash_dev_init(context);
if (rc) {
goto finish;
}
rc = dbus_init(context, &dbus_ops);
if (rc) {
goto finish;
}
#ifdef VIRTUAL_PNOR_ENABLED
init_vpnor(context);
#endif
/* Set the LPC bus mapping */
__protocol_reset(context);
......@@ -421,10 +438,10 @@ finish:
protocol_events_put(context, dbus_ops);
#ifdef VIRTUAL_PNOR_ENABLED
destroy_vpnor(context);
vpnor_destroy(&context->backend);
#endif
dbus_free(context);
flash_dev_free(context);
backend_free(&context->backend);
lpc_dev_free(context);
transport_mbox_free(context);
windows_free(context);
......
......@@ -4,11 +4,13 @@
#ifndef MBOX_H
#define MBOX_H
#include <assert.h>
#include <mtd/mtd-abi.h>
#include <systemd/sd-bus.h>
#include <poll.h>
#include <stdbool.h>
#include "backend.h"
#include "protocol.h"
#include "transport.h"
#include "vpnor/mboxd_pnor_partition_table.h"
......@@ -48,8 +50,7 @@ enum api_version {
#define SIG_FD 2
#define POLL_FDS 3 /* Number of FDs we poll on */
#define LPC_CTRL_FD 3
#define MTD_FD 4
#define TOTAL_FDS 5
#define TOTAL_FDS 4
#define MAPS_FLASH (1 << 0)
#define MAPS_MEM (1 << 1)
......@@ -72,6 +73,10 @@ struct mbox_context {
enum api_version version;
const struct protocol_ops *protocol;
const struct transport_ops *transport;
struct backend backend;
/* Commandline parameters */
const char *path;
/* System State */
enum mbox_state state;
......@@ -96,21 +101,32 @@ struct mbox_context {
uint32_t mem_size;
/* LPC Bus Base Address (bytes) */
uint32_t lpc_base;
/* Flash size from command line (bytes) */
uint32_t flash_size;
/* Bytemap of the erased state of the entire flash */
uint8_t *flash_bmap;
/* Erase size (as a shift) */
uint32_t erase_size_shift;
/* Block size (as a shift) */
uint32_t block_size_shift;
/* Actual Flash Info */
struct mtd_info_user mtd_info;
#ifdef VIRTUAL_PNOR_ENABLED
/* Virtual PNOR partition table */
struct vpnor_partition_table *vpnor;
struct vpnor_partition_paths paths;
#endif
};
/* Temporary flash API compatibility */
static inline int64_t flash_copy(struct mbox_context *context, uint32_t offset,
void *mem, uint32_t size)
{
return backend_copy(&context->backend, offset, mem, size);
}
static inline int flash_set_bytemap(struct mbox_context *context,
uint32_t offset, uint32_t count,
uint8_t val)
{
return backend_set_bytemap(&context->backend, offset, count, val);
}
static inline int flash_erase(struct mbox_context *context, uint32_t offset,
uint32_t count)
{
return backend_erase(&context->backend, offset, count);
}
static inline int flash_write(struct mbox_context *context, uint32_t offset,
void *buf, uint32_t count)
{
return backend_write(&context->backend, offset, buf, count);
}
#endif /* MBOX_H */
mboxd_SOURCES += %reldir%/backend.c
This diff is collapsed.
/* SPDX-License-Identifier: Apache-2.0 */
/* Copyright (C) 2019 IBM Corp. */
#ifndef MTD_BACKEND_H
#define MTD_BACKEND_H
struct mtd_data {
int fd;
uint8_t *flash_bmap;
struct mtd_info_user mtd_info;
};
#endif
......@@ -4,9 +4,10 @@
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include "backend.h"
#include "common.h"
#include "flash.h"
#include "lpc.h"
#include "mboxd.h"
#include "protocol.h"
......@@ -77,11 +78,12 @@ int protocol_events_clear(struct mbox_context *context, uint8_t bmc_event)
return context->transport->clear_events(context, bmc_event, mask);
}
static int protocol_negotiate_version(struct mbox_context *context,
uint8_t requested);
static int protocol_v1_reset(struct mbox_context *context)
{
/* Host requested it -> No BMC Event */
windows_reset_all(context);
return lpc_reset(context);
return __protocol_reset(context);
}
static int protocol_negotiate_version(struct mbox_context *context,
......@@ -109,26 +111,26 @@ static int protocol_v1_get_info(struct mbox_context *context,
io->resp.api_version = rc;
/* Now do all required intialisation for v1 */
context->block_size_shift = BLOCK_SIZE_SHIFT_V1;
context->backend.block_size_shift = BLOCK_SIZE_SHIFT_V1;
MSG_INFO("Block Size: 0x%.8x (shift: %u)\n",
1 << context->block_size_shift, context->block_size_shift);
1 << context->backend.block_size_shift, context->backend.block_size_shift);
/* Knowing blocksize we can allocate the window dirty_bytemap */
windows_alloc_dirty_bytemap(context);
io->resp.v1.read_window_size =
context->windows.default_size >> context->block_size_shift;
context->windows.default_size >> context->backend.block_size_shift;
io->resp.v1.write_window_size =
context->windows.default_size >> context->block_size_shift;
context->windows.default_size >> context->backend.block_size_shift;
return lpc_map_memory(context);
}
static int protocol_v1_get_flash_info(struct mbox_context *context,
struct protocol_get_flash_info *io)
struct protocol_get_flash_info *io)
{
io->resp.v1.flash_size = context->flash_size;
io->resp.v1.erase_size = context->mtd_info.erasesize;
io->resp.v1.flash_size = context->backend.flash_size;
io->resp.v1.erase_size = 1 << context->backend.erase_size_shift;
return 0;
}
......@@ -150,17 +152,20 @@ static inline uint16_t get_lpc_addr_shifted(struct mbox_context *context)
MSG_DBG("LPC address of current window: 0x%.8x\n", lpc_addr);
return lpc_addr >> context->block_size_shift;
return lpc_addr >> context->backend.block_size_shift;
}
static int protocol_v1_create_window(struct mbox_context *context,
struct protocol_create_window *io)
{
uint32_t offset = io->req.offset << context->block_size_shift;
uint32_t size = io->req.size << context->block_size_shift;
struct backend *backend = &context->backend;
uint32_t offset;
uint32_t size;
int rc;
rc = flash_validate(context, offset, size, io->req.ro);
offset = io->req.offset << backend->block_size_shift;
size = io->req.size << backend->block_size_shift;
rc = backend_validate(backend, offset, size, io->req.ro);
if (rc < 0) {
/* Backend does not allow window to be created. */
return rc;
......@@ -227,11 +232,11 @@ static int protocol_v1_mark_dirty(struct mbox_context *context,
/* For V1 offset given relative to flash - we want the window */
off = offset - ((context->current->flash_offset) >>
context->block_size_shift);
context->backend.block_size_shift);
if (off > offset) { /* Underflow - before current window */
MSG_ERR("Tried to mark dirty before start of window\n");
MSG_ERR("requested offset: 0x%x window start: 0x%x\n",
offset << context->block_size_shift,
offset << context->backend.block_size_shift,
context->current->flash_offset);
return -EINVAL;
}
......@@ -241,12 +246,12 @@ static int protocol_v1_mark_dirty(struct mbox_context *context,
* For protocol V1 we can get away with just marking the whole
* block dirty.
*/
size = align_up(size, 1 << context->block_size_shift);
size >>= context->block_size_shift;
size = align_up(size, 1 << context->backend.block_size_shift);
size >>= context->backend.block_size_shift;
MSG_INFO("Dirty window @ 0x%.8x for 0x%.8x\n",
offset << context->block_size_shift,
size << context->block_size_shift);
offset << context->backend.block_size_shift,
size << context->backend.block_size_shift);
return window_set_bytemap(context, context->current, offset, size,
WINDOW_DIRTY);
......@@ -270,7 +275,7 @@ static int generic_flush(struct mbox_context *context)
* (dirty/erased) changes we perform the required action on the backing
* store and update the current streak-type
*/
for (i = 0; i < (context->current->size >> context->block_size_shift);
for (i = 0; i < (context->current->size >> context->backend.block_size_shift);
i++) {
uint8_t cur = context->current->dirty_bmap[i];
if (cur != WINDOW_CLEAN) {
......@@ -312,7 +317,7 @@ static int generic_flush(struct mbox_context *context)
/* Clear the dirty bytemap since we have written back all changes */
return window_set_bytemap(context, context->current, 0,
context->current->size >>
context->block_size_shift,
context->backend.block_size_shift,
WINDOW_CLEAN);
}
......@@ -416,14 +421,14 @@ static int protocol_v2_get_info(struct mbox_context *context,
io->resp.api_version = rc;
/* Now do all required intialisation for v2 */
context->block_size_shift = log_2(context->mtd_info.erasesize);
MSG_INFO("Block Size: 0x%.8x (shift: %u)\n",
1 << context->block_size_shift, context->block_size_shift);
/* Knowing blocksize we can allocate the window dirty_bytemap */
windows_alloc_dirty_bytemap(context);