[Skiboot] [PATCH 5/5] libflash/test: Add tests for mbox-flash
Cyril Bur
cyril.bur at au1.ibm.com
Fri May 12 23:55:58 AEST 2017
On Tue, 2017-05-02 at 14:48 +1000, Suraj Jitindar Singh wrote:
> On Mon, 2017-04-24 at 19:14 +1000, Cyril Bur wrote:
> > A first basic set of tests for mbox-flash. These tests do their
> > testing
> > by stubbing out or otherwise replacing functions not in
> > libflash/mbox-flash.c. The stubbed out version of the function can
> > then
> > be used to emulate a BMC mbox daemon talking to back to the code in
> > mbox-flash and it can ensure that there is some adherance to the
> > protocol and that from a blocklevel api point of view the world
> > appears
> > sane.
> >
> > This makes these tests simple to run and they have been integrated
> > into `make
> > check`. The down side is that these tests rely on duplicated feature
> > incomplete BMC daemon behaviour. Therefore these tests are a strong
> > indicator of broken behavaiour but a very unreliable indicator of
> > correctness.
> >
> > Full integration tests with a 'read' BMC daemon are probably beyond
> > the
> > scope of this repository.
>
> Comments below
>
> Suraj
>
> >
> > Signed-off-by: Cyril Bur <cyril.bur at au1.ibm.com>
> > ---
> > .gitignore | 1 +
> > include/skiboot.h | 2 +
> > libflash/mbox-flash.c | 2 +
> > libflash/test/Makefile.check | 12 +-
> > libflash/test/mbox-server.c | 480
> > +++++++++++++++++++++++++++++++++++++++++++
> > libflash/test/mbox-server.h | 10 +
> > libflash/test/stubs.c | 78 ++++++-
> > libflash/test/stubs.h | 27 +++
> > libflash/test/test-mbox.c | 308 +++++++++++++++++++++++++++
> > 9 files changed, 913 insertions(+), 7 deletions(-)
> > create mode 100644 libflash/test/mbox-server.c
> > create mode 100644 libflash/test/mbox-server.h
> > create mode 100644 libflash/test/stubs.h
> > create mode 100644 libflash/test/test-mbox.c
> >
> > diff --git a/.gitignore b/.gitignore
> > index 17e71f9c..0de1bb0a 100644
> > --- a/.gitignore
> > +++ b/.gitignore
> > @@ -73,6 +73,7 @@ libc/test/run-time-gcov
> > libflash/test/test-blocklevel
> > libflash/test/test-flash
> > libflash/test/test-ecc
> > +libflash/test/test-mbox
> > libflash/test/test-flash-gcov
> > libstb/create-container
> > libstb/test/run-stb-container
> > diff --git a/include/skiboot.h b/include/skiboot.h
> > index 2b1f8a57..8289c4ca 100644
> > --- a/include/skiboot.h
> > +++ b/include/skiboot.h
> > @@ -137,6 +137,7 @@ extern unsigned int pcie_max_link_speed;
> > /* Convert a 4-bit number to a hex char */
> > extern char __attrconst tohex(uint8_t nibble);
> >
> > +#ifndef __TEST__
> > /* Bit position of the most significant 1-bit (LSB=0, MSB=63) */
> > static inline int ilog2(unsigned long val)
> > {
> > @@ -151,6 +152,7 @@ static inline bool is_pow2(unsigned long val)
> > {
> > return val == (1ul << ilog2(val));
> > }
> > +#endif
> >
> > #define lo32(x) ((x) & 0xffffffff)
> > #define hi32(x) (((x) >> 32) & 0xffffffff)
> > diff --git a/libflash/mbox-flash.c b/libflash/mbox-flash.c
> > index 5d29215c..79358f31 100644
> > --- a/libflash/mbox-flash.c
> > +++ b/libflash/mbox-flash.c
> > @@ -34,8 +34,10 @@
> > #include <ccan/container_of/container_of.h>
> >
> > #ifndef __SKIBOOT__
> > +#ifndef __TEST__
> > #error "This libflash backend must be compiled with skiboot"
> > #endif
> > +#endif
> >
> > #define MBOX_DEFAULT_TIMEOUT 30
> >
> > diff --git a/libflash/test/Makefile.check
> > b/libflash/test/Makefile.check
> > index 1f92b9d2..29283fd8 100644
> > --- a/libflash/test/Makefile.check
> > +++ b/libflash/test/Makefile.check
> > @@ -1,5 +1,5 @@
> > # -*-Makefile-*-
> > -LIBFLASH_TEST := libflash/test/test-flash libflash/test/test-ecc
> > libflash/test/test-blocklevel
> > +LIBFLASH_TEST := libflash/test/test-flash libflash/test/test-ecc
> > libflash/test/test-blocklevel libflash/test/test-mbox
> >
> > LCOV_EXCLUDE += $(LIBFLASH_TEST:%=%.c)
> >
> > @@ -16,13 +16,13 @@ $(LIBFLASH_TEST:%=%-gcov-run) : %-run: %
> > $(LIBFLASH_TEST:%=%-check) : %-check: %
> > $(call QTEST, RUN-TEST ,$(VALGRIND) $<, $<)
> >
> > -libflash/test/stubs.o: libflash/test/stubs.c
> > - $(call Q, HOSTCC ,$(HOSTCC) $(HOSTCFLAGS) -g -c -o $@ $<,
> > $<)
> > -
> > -$(LIBFLASH_TEST) : libflash/test/stubs.o libflash/libflash.c
> > libflash/ecc.c libflash/blocklevel.c
> > +LIBFLASH_TEST_EXTRA := libflash/test/stubs.o libflash/test/mbox-
> > server.o
> > +$(LIBFLASH_TEST_EXTRA) : %.o : %.c
> > + $(call Q, HOSTCC ,$(HOSTCC) $(HOSTCFLAGS) -D__TEST__ -Wno-
> > error=suggest-attribute=const -g -c -o $@ $<, $<)
> >
> > +$(LIBFLASH_TEST) : libflash/libflash.c libflash/ecc.c
> > libflash/blocklevel.c $(LIBFLASH_TEST_EXTRA)
> > $(LIBFLASH_TEST) : % : %.c
> > - $(call Q, HOSTCC ,$(HOSTCC) $(HOSTCFLAGS) -O0 -g -I include
> > -I . -o $@ $< libflash/test/stubs.o, $<)
> > + $(call Q, HOSTCC ,$(HOSTCC) $(HOSTCFLAGS) -D__TEST__ -Wno-
> > error=suggest-attribute=const -O0 -g -I include -I . -o $@ $<
> > $(LIBFLASH_TEST_EXTRA), $<)
> >
> > $(LIBFLASH_TEST:%=%-gcov): %-gcov : %.c %
> > $(call Q, HOSTCC ,$(HOSTCC) $(HOSTCFLAGS) $(HOSTGCOVCFLAGS)
> > -I include -I . -o $@ $< libflash/test/stubs.o, $<)
> > diff --git a/libflash/test/mbox-server.c b/libflash/test/mbox-
> > server.c
> > new file mode 100644
> > index 00000000..47585370
> > --- /dev/null
> > +++ b/libflash/test/mbox-server.c
> > @@ -0,0 +1,480 @@
> > +/* Copyright 2017 IBM Corp.
> > + *
> > + * Licensed under the Apache License, Version 2.0 (the "License");
> > + * you may not use this file except in compliance with the License.
> > + * You may obtain a copy of the License at
> > + *
> > + * http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing,
> > software
> > + * distributed under the License is distributed on an "AS IS" BASIS,
> > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> > + * implied.
> > + * See the License for the specific language governing permissions
> > and
> > + * limitations under the License.
> > + */
> > +
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <stdint.h>
> > +#include <string.h>
> > +#include <stdarg.h>
> > +
> > +#include <sys/mman.h> /* for mprotect() */
> > +
> > +#define pr_fmt(fmt) "MBOX-SERVER: " fmt
> > +#include "skiboot.h"
> > +#include "opal-api.h"
> > +
> > +#include "mbox-server.h"
> > +#include "stubs.h"
> > +
> > +#define ERASE_GRANULE 0x100
> > +
> > +
> > +#define LPC_BLOCKS 256
> > +#define LPC_SIZE (LPC_BLOCKS * (1 << server_state.block_shift))
> > +
> > +#define __unused __attribute__((unused))
> > +
> > +enum win_type {
> > + WIN_CLOSED,
> > + WIN_READ,
> > + WIN_WRITE
> > +};
> > +
> > +typedef void (*mbox_data_cb)(struct bmc_mbox_msg *msg, void *priv);
> > +typedef void (*mbox_attn_cb)(uint8_t reg, void *priv);
> > +
> > +struct {
> > + mbox_data_cb fn;
> > + void *cb_data;
> > + mbox_attn_cb attn;
> > + void *cb_attn;
> > +} mbox_data;
> > +
> > +static struct {
> > + int api;
> > + bool reset;
> > +
> > + void *lpc_base;
> > + size_t lpc_size;
> > +
> > + uint8_t attn_reg;
> > +
> > + uint32_t block_shift;
> > + uint32_t erase_granule;
> > +
> > + uint16_t def_read_win; /* default window size in blocks */
> > + uint16_t def_write_win;
> > +
> > + uint16_t max_read_win; /* max window size in blocks */
> > + uint16_t max_write_win;
> > +
> > + enum win_type win_type;
> > + uint32_t win_base;
> > + uint32_t win_size;
> > + bool win_dirty;
> > +} server_state;
> > +
> > +
> > +static bool check_window(uint32_t pos, uint32_t size)
> > +{
> > + /* If size is zero then all is well */
> > + if (size == 0)
> > + return true;
> > +
> > + if (server_state.api == 1) {
> > + /*
> > + * Can actually be stricter in v1 because pos is
> > relative to
> > + * flash not window
> > + */
> > + if (pos < server_state.win_base ||
> > + pos + size > server_state.win_base +
> > server_state.win_size) {
> > + fprintf(stderr, "pos: 0x%08x size: 0x%08x
> > aren't in active window\n",
> > + pos, size);
> > + fprintf(stderr, "window pos: 0x%08x window
> > size: 0x%08x\n",
> > + server_state.win_base,
> > server_state.win_size);
> > + return false;
> > + }
> > + } else {
> > + if (pos + size > server_state.win_base +
> > server_state.win_size)
>
> Given pos is a window index this should be pos + size > win_size?
>
Ah damn window indexes, also yes, also I think theres actually bigger
problems - I'm using it wrt 'lpc addresses' and in do_dirty() which I
think they're only the same by chance and by the fact that these tests
use zero a lot. In theory they could be quite different.
> > + return false;
> > + }
> > + return true;
> > +}
> > +
> > +/* skiboot test stubs */
> > +int64_t lpc_read(enum OpalLPCAddressType __unused addr_type,
> > uint32_t addr,
> > + uint32_t *data, uint32_t sz);
> > +int64_t lpc_read(enum OpalLPCAddressType __unused addr_type,
> > uint32_t addr,
> > + uint32_t *data, uint32_t sz)
> > +{
> > + /* Let it read from a write window... Spec says it ok! */
> > + if (!check_window(addr, sz) || server_state.win_type ==
> > WIN_CLOSED)
> > + return 1;
> > + memcpy(data, server_state.lpc_base + addr, sz);
> > + return 0;
> > +}
> > +
> > +int64_t lpc_write(enum OpalLPCAddressType __unused addr_type,
> > uint32_t addr,
> > + uint32_t data, uint32_t sz);
> > +int64_t lpc_write(enum OpalLPCAddressType __unused addr_type,
> > uint32_t addr,
> > + uint32_t data, uint32_t sz)
> > +{
> > + if (!check_window(addr, sz) || server_state.win_type !=
> > WIN_WRITE)
> > + return 1;
> > + memcpy(server_state.lpc_base + addr, &data, sz);
> > + return 0;
> > +}
> > +
> > +int bmc_mbox_register_attn(mbox_attn_cb handler, void *drv_data)
> > +{
> > + mbox_data.attn = handler;
> > + mbox_data.cb_attn = drv_data;
> > +
> > + return 0;
> > +}
> > +
> > +uint8_t bmc_mbox_get_attn_reg(void)
> > +{
> > + return server_state.attn_reg;
> > +}
> > +
> > +int bmc_mbox_register_callback(mbox_data_cb handler, void *drv_data)
> > +{
> > + mbox_data.fn = handler;
> > + mbox_data.cb_data = drv_data;
> > +
> > + return 0;
> > +}
> > +
> > +static int close_window(bool check)
> > +{
> > + /*
> > + * This isn't strictly prohibited and some daemons let you
> > close
> > + * windows even if none are open.
> > + * I've made the test fail because closing with no windows
> > open is
> > + * a sign that something 'interesting' has happened.
> > + * You should investigate why
> > + *
> > + * If check is false it is because we just want to do the
> > logic
> > + * because open window has been called - you can open a
> > window
> > + * over a closed window obviously
> > + */
> > + if (check && server_state.win_type == WIN_CLOSED)
> > + return MBOX_R_PARAM_ERROR;
> > +
> > + server_state.win_type = WIN_CLOSED;
> > + mprotect(server_state.lpc_base, LPC_SIZE, PROT_NONE);
> > +
> > + return MBOX_R_SUCCESS;
> > +}
> > +
> > +static int do_dirty(uint32_t pos, uint32_t size)
> > +{
> > + pos <<= server_state.block_shift;
> > + if (server_state.api > 1)
> > + size <<= server_state.block_shift;
> > + if (!check_window(pos, size)) {
> > + prlog(PR_ERR, "Trying to dirty not in open window
> > range\n");
> > + return MBOX_R_PARAM_ERROR;
> > + }
> > + if (server_state.win_type != WIN_WRITE) {
> > + prlog(PR_ERR, "Trying to dirty not write window\n");
> > + return MBOX_R_PARAM_ERROR;
> > + }
> > +
> > + /* Thats about all actually */
> > + return MBOX_R_SUCCESS;
> > +}
> > +
> > +static int open_window(struct bmc_mbox_msg *msg, bool write, u32
> > offset, u32 size)
> > +{
> > + int max_size = server_state.max_read_win <<
> > server_state.block_shift;
> > + //int win_size = server_state.def_read_win;
> > + enum win_type type = WIN_READ;
> > + int prot = PROT_READ;
> > +
> > + assert(server_state.win_type == WIN_CLOSED);
> > +
> > + /* Shift params up */
> > + offset <<= server_state.block_shift;
> > + size <<= server_state.block_shift;
> > +
> > + if (!size || server_state.api == 1)
> > + size = server_state.def_read_win <<
> > server_state.block_shift;
> > +
> > + if (write) {
> > + max_size = server_state.max_write_win <<
> > server_state.block_shift;
> > + //win_size = server_state.def_write_win;
> > + prot |= PROT_WRITE;
> > + type = WIN_WRITE;
> > + /* Use the default size if zero size is set */
> > + if (!size || server_state.api == 1)
> > + size = server_state.def_write_win <<
> > server_state.block_shift;
> > + }
> > +
> > +
> > + prlog(PR_INFO, "Opening range %#.8x, %#.8x for %s\n",
> > + offset, offset + size - 1, write ? "writing"
> > : "reading");
> > +
> > + /* XXX: Document this behaviour */
> > + if ((size + offset) > LPC_SIZE) {
> > + prlog(PR_INFO, "tried to open beyond end of
> > flash\n");
> > + return MBOX_R_PARAM_ERROR;
> > + }
> > +
> > + /* XXX: should we do this before or after checking for
> > errors?
> > + * Doing it afterwards ensures consistency between
> > + * implementations
> > + */
> > + if (server_state.api == 2)
> > + size = MIN(size, max_size);
> > +
> > + mprotect(server_state.lpc_base + offset, size, prot);
> > + server_state.win_type = type;
> > + server_state.win_base = offset;
> > + server_state.win_size = size;
> > +
> > + memset(msg->args, 0, sizeof(msg->args));
> > + bmc_put_u16(msg, 0, offset >> server_state.block_shift);
> > + if (server_state.api == 1) {
> > + /*
> > + * Put nonsense in here because v1 mbox-flash
> > shouldn't know about it.
> > + * If v1 mbox-flash does read this, 0xffff should
> > trigger a big mistake.
> > + */
> > + bmc_put_u16(msg, 2, 0xffff >>
> > server_state.block_shift);
> > + bmc_put_u16(msg, 4, 0xffff >>
> > server_state.block_shift);
> > + } else {
> > + bmc_put_u16(msg, 2, size >>
> > server_state.block_shift);
> > + bmc_put_u16(msg, 4, offset >>
> > server_state.block_shift);
> > + }
> > + return MBOX_R_SUCCESS;
> > +}
> > +
> > +int bmc_mbox_enqueue(struct bmc_mbox_msg *msg)
> > +{
> > + /*
> > + * FIXME: should we be using the same storage for message
> > + * and response?
> > + */
> > + int rc = MBOX_R_SUCCESS;
> > + uint32_t start, size;
> > +
> > + if (server_state.reset && msg->command !=
> > MBOX_C_GET_MBOX_INFO &&
> > + msg->command !=
> > MBOX_C_BMC_EVENT_ACK) {
> > + /*
> > + * Real daemons should return an error, but for
> > testing we'll
> > + * be a bit more strict
> > + */
> > + prlog(PR_EMERG, "Server was in reset state - illegal
> > command %d\n",
> > + msg->command);
> > + exit(1);
> > + }
> > +
> > + switch (msg->command) {
> > + case MBOX_C_RESET_STATE:
> > + prlog(PR_INFO, "RESET_STATE\n");
> > + rc = open_window(msg, false, 0, LPC_BLOCKS);
> > + memset(msg->args, 0, sizeof(msg->args));
> > + break;
> > +
> > + case MBOX_C_GET_MBOX_INFO:
> > + prlog(PR_INFO, "GET_MBOX_INFO version = %d,
> > block_shift = %d\n",
> > + server_state.api,
> > server_state.block_shift);
> > + msg->args[0] = server_state.api;
> > + if (server_state.api == 1) {
> > + prlog(PR_INFO, "\tread_size =
> > 0x%08x, write_size = 0x%08x\n",
> > + server_state.def_rea
> > d_win, server_state.def_write_win);
> > + bmc_put_u16(msg, 1,
> > server_state.def_read_win);
> > + bmc_put_u16(msg, 3,
> > server_state.def_write_win);
> > + msg->args[5] = 0xff; /* If v1 reads
> > this, 0xff will force the mistake */
> > + } else {
> > + msg->args[5] =
> > server_state.block_shift;
> > + }
> > + server_state.reset = false;
> > + break;
> > +
> > + case MBOX_C_GET_FLASH_INFO:
> > + prlog(PR_INFO, "GET_FLASH_INFO: size:
> > 0x%08x, erase: 0x%08x\n",
> > + LPC_SIZE,
> > server_state.erase_granule);
> > + if (server_state.api == 1) {
> > + bmc_put_u32(msg, 0, LPC_SIZE);
> > + bmc_put_u32(msg, 4,
> > server_state.erase_granule);
> > + } else {
> > + bmc_put_u16(msg, 0, LPC_SIZE >>
> > server_state.block_shift);
> > + bmc_put_u16(msg, 2,
> > server_state.erase_granule >> server_state.block_shift);
> > + }
> > + break;
> > +
> > + case MBOX_C_CREATE_READ_WINDOW:
> > + start = bmc_get_u16(msg, 0);
> > + size = bmc_get_u16(msg, 2);
> > + prlog(PR_INFO, "CREATE_READ_WINDOW: pos:
> > 0x%08x, len: 0x%08x\n", start, size);
> > + rc = close_window(false);
> > + if (rc != MBOX_R_SUCCESS)
> > + break;
> > + rc = open_window(msg, false, start, size);
> > + break;
> > +
> > + case MBOX_C_CLOSE_WINDOW:
> > + rc = close_window(true);
> > + break;
> > +
> > + case MBOX_C_CREATE_WRITE_WINDOW:
> > + start = bmc_get_u16(msg, 0);
> > + size = bmc_get_u16(msg, 2);
> > + prlog(PR_INFO, "CREATE_WRITE_WINDOW: pos:
> > 0x%08x, len: 0x%08x\n", start, size);
> > + rc = close_window(false);
> > + if (rc != MBOX_R_SUCCESS)
> > + break;
> > + rc = open_window(msg, true, start, size);
> > + break;
> > +
> > + /* TODO: make these do something */
> > + case MBOX_C_WRITE_FLUSH:
> > + prlog(PR_INFO, "WRITE_FLUSH\n");
> > + if (server_state.api > 1 &&
> > !server_state.win_dirty) {
> > + prlog(PR_EMERG, "Version >1 called
> > FLUSH without a previous DIRTY\n");
>
> I think this is technically allowed, but if you're just trying to catch
> weird behaviour then makes sense to leave it in :)
>
Yes, theres nothing in the spec that says it can't be done so no actual
daemon should have this check, however, if there wasn't a previous
dirty I have some questions as to why flush was called at all (on >
version 1).
> > + exit (1);
> > + }
> > + server_state.win_dirty = false;
> > + if (server_state.api > 1)
> > + break;
> > +
> > + /* This is only done on V1 */
> > + start = bmc_get_u16(msg, 0);
> > + if (server_state.api == 1)
> > + size = bmc_get_u32(msg, 2);
> > + else
> > + size = bmc_get_u16(msg, 2);
> > + prlog(PR_INFO, "\tpos: 0x%08x len:
> > 0x%08x\n", start, size);
> > + rc = do_dirty(start, size);
> > + break;
> > + case MBOX_C_MARK_WRITE_DIRTY:
> > + start = bmc_get_u16(msg, 0);
> > + if (server_state.api == 1)
> > + size = bmc_get_u32(msg, 2);
> > + else
> > + size = bmc_get_u16(msg, 2);
> > + prlog(PR_INFO, "MARK_WRITE_DIRTY: pos:
> > 0x%08x, len: %08x\n", start, size);
> > + server_state.win_dirty = true;
> > + rc = do_dirty(start, size);
> > + break;
> > + case MBOX_C_BMC_EVENT_ACK:
> > + /*
> > + * Clear any BMC notifier flags. Don't clear
> > the server
> > + * reset state here, it is a permitted
> > command but only
> > + * GET_INFO should clear it.
> > + *
> > + * Make sure that msg->args[0] is only
> > acking bits we told
> > + * it about, in sever_state.attn_reg. The
> > caveat is that
> > + * it could NOT ack some bits...
> > + */
> > + prlog(PR_INFO, "BMC_EVENT_ACK 0x%02x\n",
> > msg->args[0]);
> > + if ((msg->args[0] | server_state.attn_reg)
> > != server_state.attn_reg) {
> > + prlog(PR_EMERG, "Tried to ack bits
> > we didn't say!\n");
> > + exit(1);
> > + }
> > + msg->bmc &= ~msg->args[0];
> > + server_state.attn_reg &= ~msg->args[0];
> > + break;
> > + case MBOX_C_MARK_WRITE_ERASED:
> > + start = bmc_get_u16(msg, 0) <<
> > server_state.block_shift;
> > + size = bmc_get_u16(msg, 2) <<
> > server_state.block_shift;
> > + /* If we've negotiated v1 this should never
> > be called */
> > + if (server_state.api == 1) {
> > + prlog(PR_EMERG, "Version 1 protocol
> > called a V2 only command\n");
> > + exit(1);
> > + }
> > + /*
> > + * This will likely result in flush (but not
> > + * dirty) being called. This is the point.
> > + */
> > + server_state.win_dirty = true;
> > + /* This should really be done when they call
> > flush */
> > + memset(server_state.lpc_base +
> > server_state.win_base + start, 0xff, size);
> > + break;
> > + default:
> > + prlog(PR_EMERG, "Got unknown command code
> > from mbox: %d\n", msg->command);
> > + }
> > +
> > + prerror("command response = %d\n", rc);
> > + msg->response = rc;
> > +
> > + /* now that we've handled the message, holla-back */
> > + mbox_data.fn(msg, mbox_data.cb_data);
> > +
> > + return 0;
> > +}
> > +
> > +int mbox_server_memcmp(int off, const void *buf, size_t len)
> > +{
> > + return memcmp(server_state.lpc_base + off, buf, len);
> > +}
> > +
> > +void mbox_server_memset(int c)
> > +{
> > + memset(server_state.lpc_base, c, server_state.lpc_size);
> > +}
> > +
> > +uint32_t mbox_server_total_size(void)
> > +{
> > + /* Not actually but for this server we don't differenciate
>
> differentiate...
>
Thanks.
> > */
> > + return server_state.lpc_size;
> > +}
> > +
> > +uint32_t mbox_server_erase_granule(void)
> > +{
> > + return server_state.erase_granule;
> > +}
> > +
> > +int mbox_server_version(void)
> > +{
> > + return server_state.api;
> > +}
> > +
> > +int mbox_server_reset(unsigned int version)
> > +{
> > + if (version > 2)
> > + return 1;
> > +
> > + server_state.api = version;
> > + server_state.erase_granule = 0x1000; /* Thats the minmum
> > size for !v1 */
> > + server_state.attn_reg = MBOX_ATTN_BMC_REBOOT |
> > MBOX_ATTN_BMC_DAEMON_READY;
> > + server_state.win_type = WIN_CLOSED;
> > + server_state.reset = true;
> > + mbox_data.attn(MBOX_ATTN_BMC_REBOOT, mbox_data.cb_attn);
> > +
> > + return 0;
> > +}
> > +
> > +int mbox_server_init(void)
> > +{
> > + server_state.api = 1;
> > + server_state.reset = true;
> > +
> > + /* We're always ready! */
> > + server_state.attn_reg = MBOX_ATTN_BMC_DAEMON_READY;
> > +
> > + /* setup server */
> > + server_state.block_shift = 12; /* TODO add tests to change
> > the block_shift */
> > + server_state.erase_granule = ERASE_GRANULE;
> > + server_state.lpc_size = LPC_SIZE;
> > + server_state.lpc_base = malloc(LPC_SIZE);
> > +
> > + server_state.def_read_win = 1; /* These are in units of
> > block shift "= 1 is 4K" */
> > + server_state.def_write_win = 1; /* These are in units of
> > block shift "= 1 is 4K" */
> > +
> > + server_state.max_read_win = LPC_BLOCKS;
> > + server_state.max_write_win = LPC_BLOCKS;
> > + server_state.win_type = WIN_CLOSED;
> > +
> > + return 0;
> > +}
> > +
> > +void mbox_server_destroy(void)
> > +{
> > + free(server_state.lpc_base);
> > +}
> > diff --git a/libflash/test/mbox-server.h b/libflash/test/mbox-
> > server.h
> > new file mode 100644
> > index 00000000..fa17d85e
> > --- /dev/null
> > +++ b/libflash/test/mbox-server.h
> > @@ -0,0 +1,10 @@
> > +#include <stdint.h>
> > +
> > +uint32_t mbox_server_total_size(void);
> > +uint32_t mbox_server_erase_granule(void);
> > +int mbox_server_version(void);
> > +void mbox_server_memset(int c);
> > +int mbox_server_memcmp(int off, const void *buf, size_t len);
> > +int mbox_server_reset(unsigned int version);
> > +int mbox_server_init(void);
> > +void mbox_server_destroy(void);
> > diff --git a/libflash/test/stubs.c b/libflash/test/stubs.c
> > index aabf018d..d3cde856 100644
> > --- a/libflash/test/stubs.c
> > +++ b/libflash/test/stubs.c
> > @@ -1,4 +1,4 @@
> > -/* Copyright 2013-2014 IBM Corp.
> > +/* Copyright 2013-2017 IBM Corp.
> > *
> > * Licensed under the Apache License, Version 2.0 (the "License");
> > * you may not use this file except in compliance with the License.
> > @@ -14,3 +14,79 @@
> > * limitations under the License.
> > */
> > /* Stubs for libflash test */
> > +#include <stdbool.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <stdint.h>
> > +#include <string.h>
> > +#include <stdarg.h>
> > +#include <sys/unistd.h> /* for usleep */
> > +
> > +#include "../../include/lpc-mbox.h"
> > +#include "stubs.h"
> > +
> > +#define __unused __attribute__((unused))
> > +
> > +void check_timers(bool __unused unused)
> > +{
> > + return;
> > +}
> > +
> > +void time_wait_ms(unsigned long ms)
> > +{
> > + usleep(ms * 1000);
> > +}
> > +
> > +/* skiboot stubs */
> > +unsigned long mftb(void)
> > +{
> > + return 42;
> > +}
> > +unsigned long tb_hz = 512000000ul;
> > +
> > +void _prlog(int __unused log_level, const char* fmt, ...)
> > +{
> > + va_list ap;
> > +
> > + va_start(ap, fmt);
> > + vprintf(fmt, ap);
> > + va_end(ap);
> > +}
> > +
> > +/* accessor junk */
> > +
> > +void bmc_put_u16(struct bmc_mbox_msg *msg, int offset, uint16_t
> > data)
> > +{
> > + msg->args[offset + 0] = data & 0xff;
> > + msg->args[offset + 1] = data >> 8;
> > +}
> > +
> > +void bmc_put_u32(struct bmc_mbox_msg *msg, int offset, uint32_t
> > data)
> > +{
> > + msg->args[offset + 0] = (data) & 0xff;
> > + msg->args[offset + 1] = (data >> 8) & 0xff;
> > + msg->args[offset + 2] = (data >> 16) & 0xff;
> > + msg->args[offset + 3] = (data >> 24) & 0xff;
> > +}
> > +
> > +u32 bmc_get_u32(struct bmc_mbox_msg *msg, int offset)
> > +{
> > + u32 data = 0;
> > +
> > + data |= msg->args[offset + 0];
> > + data |= msg->args[offset + 1] << 8;
> > + data |= msg->args[offset + 2] << 16;
> > + data |= msg->args[offset + 3] << 24;
> > +
> > + return data;
> > +}
> > +
> > +u16 bmc_get_u16(struct bmc_mbox_msg *msg, int offset)
> > +{
> > + u16 data = 0;
> > +
> > + data |= msg->args[offset + 0];
> > + data |= msg->args[offset + 1] << 8;
> > +
> > + return data;
> > +}
> > diff --git a/libflash/test/stubs.h b/libflash/test/stubs.h
> > new file mode 100644
> > index 00000000..bc6d3f38
> > --- /dev/null
> > +++ b/libflash/test/stubs.h
> > @@ -0,0 +1,27 @@
> > +/* Copyright 2013-2017 IBM Corp.
> > + *
> > + * Licensed under the Apache License, Version 2.0 (the "License");
> > + * you may not use this file except in compliance with the License.
> > + * You may obtain a copy of the License at
> > + *
> > + * http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing,
> > software
> > + * distributed under the License is distributed on an "AS IS" BASIS,
> > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> > + * implied.
> > + * See the License for the specific language governing permissions
> > and
> > + * limitations under the License.
> > + */
> > +#include <stdint.h>
> > +
> > +#include "../../include/lpc-mbox.h"
> > +
> > +void check_timers(bool unused);
> > +void time_wait_ms(unsigned long ms);
> > +unsigned long mftb(void);
> > +void _prlog(int log_level, const char* fmt, ...);
> > +void bmc_put_u16(struct bmc_mbox_msg *msg, int offset, uint16_t
> > data);
> > +void bmc_put_u32(struct bmc_mbox_msg *msg, int offset, uint32_t
> > data);
> > +u16 bmc_get_u16(struct bmc_mbox_msg *msg, int offset);
> > +u32 bmc_get_u32(struct bmc_mbox_msg *msg, int offset);
> > diff --git a/libflash/test/test-mbox.c b/libflash/test/test-mbox.c
> > new file mode 100644
> > index 00000000..45f37672
> > --- /dev/null
> > +++ b/libflash/test/test-mbox.c
> > @@ -0,0 +1,308 @@
> > +/* Copyright 2017 IBM Corp.
> > + *
> > + * Licensed under the Apache License, Version 2.0 (the "License");
> > + * you may not use this file except in compliance with the License.
> > + * You may obtain a copy of the License at
> > + *
> > + * http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing,
> > software
> > + * distributed under the License is distributed on an "AS IS" BASIS,
> > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> > + * implied.
> > + * See the License for the specific language governing permissions
> > and
> > + * limitations under the License.
> > + */
> > +
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <stdint.h>
> > +#include <string.h>
> > +#include <stdarg.h>
> > +
> > +#include <libflash/libflash.h>
> > +#include <libflash/libflash-priv.h>
> > +
> > +#include "stubs.h"
> > +#include "mbox-server.h"
> > +
> > +#define zalloc(n) calloc(1, n)
> > +#define __unused __attribute__((unused))
> > +
> > +#undef pr_fmt
> > +
> > +#include "../libflash.c"
> > +#include "../mbox-flash.c"
> > +#include "../ecc.c"
> > +#include "../blocklevel.c"
> > +
> > +#undef pr_fmt
> > +#define pr_fmt(fmt) "MBOX-PROXY: " fmt
> > +
> > +/* client interface */
> > +
> > +#include "../../include/lpc-mbox.h"
> > +
> > +#define ERR(...) FL_DBG(__VA_ARGS__)
> > +
> > +static int run_flash_test(struct blocklevel_device *bl)
> > +{
> > + struct mbox_flash_data *mbox_flash;
> > + char hello[] = "Hello World";
> > + uint32_t erase_granule;
> > + uint64_t total_size;
> > + const char *name;
> > + uint16_t *test;
> > + char *tmp;
> > + int i, rc;
> > +
> > + mbox_flash = container_of(bl, struct mbox_flash_data, bl);
> > +
> > + /*
> > + * Do something first so that if it has been reset it does
> > that
> > + * before we check versions
> > + */
> > + rc = blocklevel_get_info(bl, &name, &total_size,
> > &erase_granule);
> > + if (rc) {
> > + ERR("blocklevel_get_info() failed with err %d\n",
> > rc);
> > + return 1;
> > + }
> > + if (total_size != mbox_server_total_size()) {
> > + ERR("Total flash size is incorrect: 0x%08lx v
> > 0x%08x\n",
> > + total_size,
> > mbox_server_total_size());
> > + return 1;
> > + }
> > + if (erase_granule != mbox_server_erase_granule()) {
> > + ERR("Erase granule is incorrect\n");
> > + return 1;
> > + }
> > +
> > +
> > + /* Sanity check that mbox_flash has inited correctly */
> > + if (mbox_flash->version != mbox_server_version()) {
> > + ERR("MBOX Flash didn't agree with the server
> > version\n");
> > + return 1;
> > + }
> > + if (mbox_flash->version == 1 && mbox_flash->shift != 12) {
> > + ERR("MBOX Flash version 1 isn't using a 4K
> > shift\n");
> > + return 1;
> > + }
> > +
> > + mbox_server_memset(0xff);
> > +
> > + test = malloc(0x10000 * 2);
> > +
> > + /* Make up a test pattern */
> > + for (i = 0; i < 0x10000; i++)
> > + test[i] = i;
> > +
> > + /* Write 64k of stuff at 0 and at 128k */
> > + printf("Writing test patterns...\n");
> > + rc = blocklevel_write(bl, 0, test, 0x10000);
> > + if (rc) {
> > + ERR("blocklevel_write(0, 0x10000) failed with err
> > %d\n", rc);
> > + return 1;
> > + }
> > + rc = blocklevel_write(bl, 0x20000, test, 0x10000);
> > + if (rc) {
> > + ERR("blocklevel_write(0x20000, 0x10000) failed with
> > err %d\n", rc);
> > + return 1;
> > + }
> > +
> > + if (mbox_server_memcmp(0, test, 0xfffc)) {
> > + ERR("Test pattern mismatch !\n");
> > + return 1;
> > + }
> > +
> > + /* Write "Hello world" straddling the 64k boundary */
> > + printf("Writing test string...\n");
> > + rc = blocklevel_write(bl, 0xfffc, hello, sizeof(hello));
> > + if (rc) {
> > + ERR("blocklevel_write(0xfffc, %s, %lu) failed with
> > err %d\n",
> > + hello, sizeof(hello), rc);
> > + return 1;
> > + }
> > +
> > + /* Check result */
> > + if (mbox_server_memcmp(0xfffc, hello, sizeof(hello))) {
> > + ERR("Test string mismatch!\n");
> > + return 1;
> > + }
> > +
> > + /* Erase granule is 0x100, this shouldn't succeed */
> > + rc = blocklevel_erase(bl, 0, 0x50);
> > + if (!rc) {
> > + ERR("blocklevel_erase(0, 0x50) didn't fail!\n");
> > + return 1;
> > + }
> > +
> > + /* Check it didn't silently erase */
> > + if (mbox_server_memcmp(0, test, 0xfffc)) {
> > + ERR("Test pattern mismatch !\n");
> > + return 1;
> > + }
> > +
> > + /*
> > + * For v1 protocol this should NOT call MARK_WRITE_ERASED!
> > + * The server MARK_WRITE_ERASED will call exit(1) if it gets
> > a
> > + * MARK_WRITE_ERASED and version == 1
> > + */
> > + rc = blocklevel_erase(bl, 0, 0x1000);
> > + if (rc) {
> > + ERR("blocklevel_erase(0, 0x1000) failed with err
> > %d\n", rc);
> > + return 1;
> > + }
> > +
> > + /*
> > + * Version 1 doesn't specify that the buffer actually
> > becomes 0xff
> > + * It is up to the daemon to do what it wants really - there
> > are
> > + * implementations that do nothing but writes to the same
> > region
> > + * work fine
> > + */
> > +
> > + /* This check is important for v2 */
> > + /* Check stuff got erased */
> > + tmp = malloc(0x2000);
> > + if (mbox_server_version() > 1) {
> > + if (!tmp) {
> > + ERR("malloc(0x1000) failed\n");
> > + return 1;
> > + }
> > + memset(tmp, 0xff, 0x1000);
> > + if (mbox_server_memcmp(0, tmp, 0x1000)) {
> > + ERR("Buffer not erased\n");
> > + rc = 1;
> > + goto out;
> > + }
> > + }
> > +
> > + /* Read beyond the end of flash */
> > + rc = blocklevel_read(bl, total_size, tmp, 0x1000);
> > + if (!rc) {
> > + ERR("blocklevel_read(total_size, 0x1000) (read
> > beyond the end) succeeded\n");
> > + goto out;
> > + }
> > +
> > + /* Test some simple write/read cases, avoid first page */
> > + rc = blocklevel_write(bl, 0x2000, test, 0x800);
> > + if (rc) {
> > + ERR("blocklevel_write(0x2000, 0x800) failed with err
> > %d\n", rc);
> > + goto out;
> > + }
> > + rc = blocklevel_write(bl, 0x2800, test, 0x800);
> > + if (rc) {
> > + ERR("blocklevel_write(0x2800, 0x800) failed with err
> > %d\n", rc);
> > + goto out;
> > + }
> > +
> > + rc = mbox_server_memcmp(0x2000, test, 0x800);
> > + if (rc) {
> > + ERR("%s:%d mbox_server_memcmp miscompare\n",
> > __FILE__, __LINE__);
> > + goto out;
> > + }
> > + rc = mbox_server_memcmp(0x2800, test, 0x800);
> > + if (rc) {
> > + ERR("%s:%d mbox_server_memcmp miscompare\n",
> > __FILE__, __LINE__);
> > + goto out;
> > + }
> > +
> > + /* Great so the writes made it, can we read them back? Do it
> > in
> > + * four small reads */
> > + for (i = 0; i < 4; i++) {
> > + rc = blocklevel_read(bl, 0x2000 + (i * 0x400), tmp +
> > (i * 0x400), 0x400);
> > + if (rc) {
> > + ERR("blocklevel_read(0x%08x, 0x400) failed
> > with err %d\n",
> > + 0x2000 + (i * 0x400), rc);
> > + goto out;
> > + }
> > + }
> > + rc = memcmp(test, tmp, 0x800);
> > + if (rc) {
> > + ERR("%s:%d read back miscompare\n", __FILE__,
> > __LINE__);
> > + goto out;
> > + }
> > + rc = memcmp(test, tmp + 0x800, 0x800);
> > + if (rc) {
> > + ERR("%s:%d read back miscompare\n", __FILE__,
> > __LINE__);
> > + goto out;
> > + }
> > +
> > + /*
> > + * Make sure we didn't corrupt other stuff, also make sure
> > one
> > + * blocklevel call will understand how to read from two
> > windows
> > + */
> > + for (i = 3; i < 10; i = i + 2) {
> > + rc = blocklevel_read(bl, i * 0x1000, tmp, 0x2000);
> > + if (rc) {
> > + ERR("blocklevel_read(0x%08x, 0x1000 failed
> > with err: %d\n", i * 0x1000, rc);
> > + goto out;
> > + }
> > + rc = memcmp(((char *)test) + (i * 0x1000), tmp,
> > 0x2000);
> > + if (rc) {
> > + ERR("%s:%d read back miscompare (pos:
> > 0x%08x)\n", __FILE__, __LINE__, i * 0x1000);
> > + goto out;
> > + }
> > + }
> > +
> > + srand(1);
> > + /*
> > + * Try to jump around the place doing a tonne of small
> > reads.
> > + * Worth doing the same with writes TODO
> > + */
> > + for (i = 0; i < 1000; i++) {
> > + int r = rand();
> > + /* Avoid reading too far, just skip it */
> > + if ((r % 0x10000) + (r % 0x2000) > 0x10000)
> > + continue;
> > +
> > + rc = blocklevel_read(bl, 0x20000 + (r % 0x10000),
> > tmp, r % 0x2000);
> > + if (rc) {
> > + ERR("blocklevel_read(0x%08x, 0x%08x) failed
> > with err %d\n", 0x20000 + (r % 0x100000), r % 0x2000, rc);
> > + goto out;
> > + }
> > + rc = memcmp(((char *)test) + (r % 0x10000), tmp, r %
> > 0x2000);
> > + if (rc) {
> > + ERR("%s:%d read back miscompare (pos:
> > 0x%08x)\n", __FILE__, __LINE__, 0x20000 + (r % 0x10000));
> > + goto out;
> > + }
> > + }
> > +out:
> > + free(tmp);
> > + return rc;
> > +}
> > +
> > +int main(void)
> > +{
> > + struct blocklevel_device *bl;
> > + int rc;
> > +
> > + libflash_debug = true;
> > +
> > + mbox_server_init();
> > +
> > + printf("Doing mbox-flash V1 tests\n");
> > +
> > + /* run test */
> > + mbox_flash_init(&bl);
> > + rc = run_flash_test(bl);
> > + if (rc)
> > + goto out;
> > + /*
> > + * Trick mbox-flash into thinking there was a reboot so we
> > can
> > + * switch to v2
> > + */
> > +
> > + printf("Doing mbox-flash V2 tests\n");
> > +
> > + mbox_server_reset(2);
> > +
> > + /* Do all the tests again */
> > + rc = run_flash_test(bl);
> > +
> > +out:
> > + mbox_flash_exit(bl);
> > +
> > + mbox_server_destroy();
> > +
> > + return rc;
> > +}
>
>
More information about the Skiboot
mailing list