1
0
Fork 0
forked from len0rd/rockbox
foxbox/lib/x1000-installer/test_lib/fakenand.c
Aidan MacDonald 06423cab58 x1000-installer: Initial commit of new framework
This is a new flash installer framework for the X1000 targets.

A bunch of this code is *UNTESTED* but there is an external test
harness which allows the library to be built and tested on a PC.

Once tests are written and the bugs are ironed out this framework
will replace the existing installer code.

New features:

- Update tarballs are MD5-checksummed to guarantee integrity.
- The flash map is no longer fixed -- updates are self describing and
  carry a map file which specifies the areas to update.
- Can take full or partial backups with checksums computed on the fly.
- Supports an additional verification mode which reads back data after
  writing to ensure the flash contents were not silently corrupted.

Change-Id: I29a89190c7ff566019f6a844ad0571f01fb7192f
2021-11-27 15:28:19 -05:00

270 lines
7 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2021 Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "nand-x1000.h"
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
const char* nand_backing_file = "fakeNAND.bin";
const char* nand_meta_file = "fakeNAND_meta.bin";
struct nand_trace* nand_trace = NULL;
size_t nand_trace_capacity = 0;
size_t nand_trace_length = 0;
static struct nand_trace* nand_trace_cur = NULL;
static int injected_err = 0;
#define METAF_PROGRAMMED 1
static const nand_chip fake_chip = {
/* ATO25D1GA */
.log2_ppb = 6, /* 64 pages */
.page_size = 2048,
.oob_size = 64,
.nr_blocks = 1024,
};
void nand_trace_reset(size_t size)
{
nand_trace = realloc(nand_trace, size);
nand_trace_capacity = size;
nand_trace_length = 0;
nand_trace_cur = nand_trace;
}
void nand_inject_error(int rc)
{
injected_err = rc;
}
nand_drv* nand_init(void)
{
static bool inited = false;
static uint8_t scratch_buf[NAND_DRV_SCRATCHSIZE];
static uint8_t page_buf[NAND_DRV_MAXPAGESIZE];
static nand_drv d;
if(!inited) {
d.scratch_buf = scratch_buf;
d.page_buf = page_buf;
d.chip = &fake_chip;
inited = true;
}
return &d;
}
static void lock_assert(bool cond, const char* msg)
{
if(!cond) {
fprintf(stderr, "%s\n", msg);
fflush(stderr);
abort();
}
}
void nand_lock(nand_drv* drv)
{
drv->lock_count++;
}
void nand_unlock(nand_drv* drv)
{
lock_assert(drv->lock_count > 0, "nand_unlock() called when not locked");
drv->lock_count--;
}
#define CHECK_INJECTED_ERROR \
do { int __err = injected_err; injected_err = 0; if(__err) return __err; } while(0)
int nand_open(nand_drv* drv)
{
lock_assert(drv->lock_count > 0, "nand_open(): lock not held");
CHECK_INJECTED_ERROR;
if(drv->refcount > 0) {
drv->refcount++;
return NAND_SUCCESS;
}
/* leaks an fd on error but this is only testing... */
drv->fd = open(nand_backing_file, O_RDWR|O_CREAT, 0644);
drv->metafd = open(nand_meta_file, O_RDWR|O_CREAT, 0644);
if(drv->fd < 0 || drv->metafd < 0)
goto err;
drv->ppb = 1 << drv->chip->log2_ppb;
drv->fpage_size = drv->chip->page_size + drv->chip->oob_size;
/* make backing file the correct size */
if(ftruncate(drv->fd, drv->chip->page_size * drv->ppb * drv->chip->nr_blocks) < 0)
goto err;
if(ftruncate(drv->metafd, drv->chip->nr_blocks * drv->ppb) < 0)
goto err;
drv->refcount++;
return NAND_SUCCESS;
err:
if(drv->fd >= 0)
close(drv->fd);
if(drv->metafd >= 0)
close(drv->metafd);
return NAND_ERR_OTHER;
}
void nand_close(nand_drv* drv)
{
lock_assert(drv->lock_count > 0, "nand_close(): lock not held");
if(--drv->refcount > 0)
return;
close(drv->fd);
close(drv->metafd);
drv->fd = -1;
drv->metafd = -1;
}
static int read_meta(nand_drv* drv, nand_page_t page)
{
/* probably won't fail */
if(lseek(drv->metafd, page, SEEK_SET) < 0)
return NAND_ERR_OTHER;
if(read(drv->metafd, drv->scratch_buf, 1) != 1)
return NAND_ERR_OTHER;
return drv->scratch_buf[0];
}
static int write_meta(nand_drv* drv, nand_page_t page, int val)
{
drv->scratch_buf[0] = val;
if(lseek(drv->metafd, page, SEEK_SET) < 0)
return NAND_ERR_OTHER;
if(write(drv->metafd, drv->scratch_buf, 1) != 1)
return NAND_ERR_OTHER;
return NAND_SUCCESS;
}
static int upd_meta(nand_drv* drv, nand_page_t page, uint8_t clr, uint8_t set)
{
int meta = read_meta(drv, page);
if(meta < 0)
return meta;
meta &= ~clr;
meta |= set;
return write_meta(drv, page, meta);
}
static int page_program(nand_drv* drv, nand_page_t page, const void* buffer,
uint8_t clr, uint8_t set)
{
if(lseek(drv->fd, page * drv->chip->page_size, SEEK_SET) < 0)
return NAND_ERR_OTHER;
if(write(drv->fd, buffer, drv->chip->page_size) != (ssize_t)drv->chip->page_size)
return NAND_ERR_PROGRAM_FAIL;
return upd_meta(drv, page, clr, set);
}
static void trace(enum nand_trace_type ty, enum nand_trace_exception ex, nand_page_t addr)
{
if(nand_trace_length < nand_trace_capacity) {
nand_trace_cur->type = ty;
nand_trace_cur->exception = ex;
nand_trace_cur->addr = addr;
nand_trace_cur++;
nand_trace_length++;
}
}
int nand_block_erase(nand_drv* drv, nand_block_t block)
{
lock_assert(drv->lock_count > 0, "nand_block_erase(): lock not held");
CHECK_INJECTED_ERROR;
trace(NTT_ERASE, NTE_NONE, block);
memset(drv->page_buf, 0xff, drv->fpage_size);
for(unsigned i = 0; i < drv->ppb; ++i) {
int rc = page_program(drv, block + i, drv->page_buf, METAF_PROGRAMMED, 0);
if(rc < 0)
return NAND_ERR_ERASE_FAIL;
}
return NAND_SUCCESS;
}
int nand_page_program(nand_drv* drv, nand_page_t page, const void* buffer)
{
lock_assert(drv->lock_count > 0, "nand_page_program(): lock not held");
CHECK_INJECTED_ERROR;
int meta = read_meta(drv, page);
if(meta < 0)
return meta;
enum nand_trace_exception exception = NTE_NONE;
if(meta & METAF_PROGRAMMED)
exception = NTE_DOUBLE_PROGRAMMED;
trace(NTT_PROGRAM, exception, page);
return page_program(drv, page, buffer, 0, METAF_PROGRAMMED);
}
int nand_page_read(nand_drv* drv, nand_page_t page, void* buffer)
{
lock_assert(drv->lock_count > 0, "nand_page_read(): lock not held");
CHECK_INJECTED_ERROR;
enum nand_trace_exception exception = NTE_NONE;
int meta = read_meta(drv, page);
if(meta < 0)
return meta;
if(meta & METAF_PROGRAMMED) {
if(lseek(drv->fd, page * drv->chip->page_size, SEEK_SET) < 0)
return NAND_ERR_OTHER;
if(read(drv->fd, buffer, drv->chip->page_size) != (ssize_t)drv->chip->page_size)
return NAND_ERR_OTHER;
} else {
memset(buffer, 0xff, drv->chip->page_size);
exception = NTE_CLEARED;
}
trace(NTT_READ, exception, page);
memset(buffer + drv->chip->page_size, 0xff, drv->chip->oob_size);
return NAND_SUCCESS;
}