?? tfs.c
字號(hào):
/* tfs.c:
* Tiny File System
* TFS supports the ability to store/access files in flash. The TFS
* functions provide a command at the monitor's user interface (the
* "tfs" command) as well as a library of functions that are available to
* the monitor/application code on this target (TFS api).
*
* The code that supports TFS in the MicroMonitor package spans across
* several files. This is done so that various pieces of TFS can optionally
* be compiled in or out (using INCLUDE_XXX macros in config.h) of the
* monitor package...
*
* tfs.c:
* Core TFS code that cannot be optionally omitted without eliminating
* the TFS facility from the monitor.
*
* tfsapi.c:
* This file contains the code that supports the application's ability
* to use the TFS api. Since some of the api is used by the monitor
* itself, not all of the api-specific code is there, some of it is
* in tfs.c.
*
* tfsclean.c:
* TFS can be configured to have a robust power-hit-safe cleanup
* mechanism which requires a significant amount of flash, or a simple,
* non-power-hit-safe mechanism. Both implementations are in tfsclean.c.
*
* tfscli.c:
* If you don't need the "tfs" command in your command line interface,
* then the code in this file can be omitted.
*
* tfsloader.c:
* TFS can support COFF, ELF or A.OUT binary file formats. The code
* to load each of these formats from flash to RAM is here.
*
* tfslog.c:
* If there is a need to log flash interaction to a file, then this
* file contains code to support that.
*
*
* NOTES:
* * Dealing with multiple task access:
* Since the monitor is inherently a single threaded program
* potentially being used in a multi-tasking environment, the monitor's
* access functions (API) must be provided with a lock/unlock
* wrapper that will guarantee sequential access to all of the monitor
* facilities. Refer to monlib.c to see this implementation. This
* provides the protection needed by TFS to keep multiple "mon_"
* functions from being executed by different tasks.
* Note that originally this was supported with tfsctrl(TFS_MUTEX ) and
* it only protected the tfs API functions. This turned out to be
* insufficient because it did not prevent other tasks from calling
* other non-tfs functions in the monitor while tfs access (and
* potentially, flash update) was in progress. This meant that a flash
* update could be in progress and some other task could call mon_getenv()
* (for example). This could screw up the flash update because
* mon_getenv() might be fetched out of the same flash device that
* the TFS operation is being performed on.
*
* * Dealing with cache coherency:
* I believe the only concern here is that Icache must be invalidated
* and Dcache must be flushed whenever TFS does a memory copy that may
* ultimately be executable code. This is handled at the end of the
* tfsmemcpy function by calling flushDcache() and invalidateIcache().
* It is the application's responsibility to give the monitor the
* appropriate functions (see assigncachefuncs()) if necessary.
*
* * Configuring a device to run as TFS memory:
* Assuming you are using power-safe cleanup...
* TFS expects that on any given device used for storage of files, the
* device is broken up into some number of sectors with the last sector
* being the largest and used as the spare sector for defragmentation.
* All other sector sizes must be smaller than the SPARE sector and the
* sector just prior to the spare is used for defragmentation state
* overhead. This sector should be large enough to allow the overhead
* space to grow down from the top without filling the sector. For most
* flash devices, these two sectors (spare and overhead) are usually the
* same size and are large. For FlashRam, the device should be configured
* so that these two sectors are large. The spare sector will never be
* allowed to contain any file information (because it is 100% dedicated to
* the defragmentation process) and the sector next to this can have files
* in it, but the overhead space is also in this sector.
*
* * Testing TFS:
* There are three files dedicated to testing the file system. Two of them
* (tfstestscript & tfstestscript1) are scripts that are put into the
* file system and run. The third file (tfstest.c) is a piece of code
* that can be built into a small application that runs out of TFS to test
* all of the API functionality.
* - tfstestscript:
* This script is used to simply bang on normal defragmentation. It
* builds files with sizes and names based on the content of memory
* starting at $APPRAMBASE. Changing the content of memory starting at
* $APPRAMBASE will change the characteristics of this test so it is
* somewhat random. It is not 100% generic, but can be used as a
* base for testing TFS on various systems.
* - tfstestscript1:
* This script is used to bang on the power-safe defragmentation of
* TFS. It simulates power hits that might occur during defragmentation.
* This script assumes that the monitor has been built with the
* DEFRAG_TEST_ENABLED flag set.
* - tfstest.c:
* This code can be built into a small application that will thoroughly
* exercise the TFS API. This file can also be used as a reference for
* some examples of TFS api usage.
*
* General notice:
* This code is part of a boot-monitor package developed as a generic base
* platform for embedded system designs. As such, it is likely to be
* distributed to various projects beyond the control of the original
* author. Please notify the author of any enhancements made or bugs found
* so that all may benefit from the changes. In addition, notification back
* to the author will allow the new user to pick up changes that may have
* been made by other users after this version of the code was distributed.
*
* Author: Ed Sutter
* email: esutter@lucent.com (home: lesutter@worldnet.att.net)
* phone: 908-582-2351 (home: 908-889-5161)
*/
#include "config.h"
#include "cpu.h"
#include "stddefs.h"
#include "genlib.h"
#include "tfs.h"
#include "tfsdev.h"
#include "flash.h"
#if INCLUDE_TFS
char *(*tfsGetAtime)(long,char *,int);
long (*tfsGetLtime)(void);
void (*tfsDocommand)(char *,int);
TDEV tfsDeviceTbl[TFSDEVTOT];
TFILE **tfsAlist;
int ScriptExitFlag;
struct tfsdat tfsSlots[TFS_MAXOPEN];
long tfsTrace;
static long tfsFmodCount;
static int tfsAlistSize, tfsOldDelFlagCheckActive;
/* tfsflgtbl & tfserrtbl:
* Tables that establish an easy lookup mechanism to convert from
* bitfield to string or character.
* Note that TFS_ULVL0 is commented out. I leave it in here as a place
* holder (comment), but it actually is not needed becasue ulvl_0 is the
* default if no other ulvl is specified.
*/
struct tfsflg tfsflgtbl[] = {
{ TFS_BRUN, 'b', "run_at_boot", TFS_BRUN },
{ TFS_QRYBRUN, 'B', "qry_run_at_boot", TFS_QRYBRUN },
{ TFS_EXEC, 'e', "executable", TFS_EXEC },
{ TFS_EBIN, 'E', TFS_EBIN_NAME, TFS_EBIN },
{ TFS_IPMOD, 'i', "inplace_modifiable", TFS_IPMOD },
{ TFS_UNREAD, 'u', "ulvl_unreadable", TFS_UNREAD },
/* { TFS_ULVL0, '0', "ulvl_0", TFS_ULVLMSK }, */
{ TFS_ULVL1, '1', "ulvl_1", TFS_ULVLMSK },
{ TFS_ULVL2, '2', "ulvl_2", TFS_ULVLMSK },
{ TFS_ULVL3, '3', "ulvl_3", TFS_ULVLMSK },
{ TFS_CPRS, 'c', "compressed", TFS_CPRS },
{ 0, 0, 0, 0 }
};
static struct tfserr tfserrtbl[] = {
{ TFS_OKAY, "no error" },
{ TFSERR_NOFILE, "file not found" },
{ TFSERR_NOSLOT, "max fps opened" },
{ TFSERR_EOF, "end of file" },
{ TFSERR_BADARG, "bad argument" },
{ TFSERR_NOTEXEC, "not executable" },
{ TFSERR_BADCRC, "bad crc" },
{ TFSERR_FILEEXISTS, "file already exists" },
{ TFSERR_FLASHFAILURE, "flash operation failed" },
{ TFSERR_WRITEMAX, "max write count exceeded" },
{ TFSERR_RDONLY, "file is read-only" },
{ TFSERR_BADFD, "invalid descriptor" },
{ TFSERR_BADHDR, "bad binary executable header" },
{ TFSERR_CORRUPT, "corrupt file" },
{ TFSERR_MEMFAIL, "memory failure" },
{ TFSERR_NOTIPMOD, "file is not in-place-modifiable" },
{ TFSERR_FLASHFULL, "out of flash space" },
{ TFSERR_USERDENIED, "user level access denied" },
{ TFSERR_NAMETOOBIG, "name or info field too big" },
{ TFSERR_FILEINUSE, "file in use" },
{ TFSERR_SCRIPTINSUB, "can't put script in subroutine" },
{ TFSERR_NOTAVAILABLE, "tfs facility not available" },
{ TFSERR_BADFLAG, "bad flag" },
{ 0,0 }
};
/* dummyAtime() & dummyLtime():
* These two functions are loaded into the function pointers as defaults
* for the time-retrieval stuff used in TFS.
*/
static char *
dummyAtime(long tval,char *buf,int buflen)
{
/* strcpy(buf,"Fri Sep 13 00:00:00 1986"); */
*buf = 0;
return(buf);
}
static long
dummyLtime(void)
{
return(TIME_UNDEFINED);
}
/* getdfsdev():
* Input is a file pointer; based on that pointer return the appropriate
* device header pointer. If error, just return 0.
* A "device" in TFS is some block of some type of memory that is assumed
* to be contiguous space that can be configured as a block of sectors (to
* look like flash). For most systems, there is only one (the flash);
* other systems may have battery-backed RAM, etc...
* Note that this is not fully implemented.
*/
static TDEV *
gettfsdev(TFILE *fp)
{
TDEV *tdp;
for(tdp=tfsDeviceTbl;tdp->start != TFSEOT;tdp++) {
if ((fp >= (TFILE *)tdp->start) &&
(fp < (TFILE *)tdp->end))
return(tdp);
}
return(0);
}
TDEV *
gettfsdev_fromprefix(char * prefix, int verbose)
{
TDEV *tdp;
for(tdp=tfsDeviceTbl;tdp->start != TFSEOT;tdp++) {
if (!strcmp(prefix,tdp->prefix))
return(tdp);
}
if (verbose)
printf("Bad device prefix: %s\n",prefix);
return(0);
}
/* tfsflasherase(), tfsflasheraseall() & tfsflashwrite():
* Wrappers for corresponding flash operations. The wrappers are used
* to provide one place for the incrmentation of tfsFmodCount.
*/
int
tfsflasheraseall(TDEV *tdp)
{
int snum, last;
if (tfsTrace > 2)
printf("tfsflasheraseall(%s)\n",tdp->prefix);
tfsFmodCount++;
/* Erase the sectors within the device that are used for file store... */
if (addrtosector((char *)tdp->start,&snum,0,0) < 0)
return(TFSERR_MEMFAIL);
last = snum + tdp->sectorcount;
while(snum < last) {
if (AppFlashErase(snum++) == -1)
return(TFSERR_MEMFAIL);
}
/* Erase the spare... */
if (addrtosector((char *)tdp->spare,&snum,0,0) < 0)
return(TFSERR_MEMFAIL);
if (AppFlashErase(snum) == -1)
return(TFSERR_MEMFAIL);
return(TFS_OKAY);
}
int
tfsflasherase(int snum)
{
if (tfsTrace > 2)
printf("tfsflasherase(%d)\n",snum);
tfsFmodCount++;
return(AppFlashErase(snum));
}
int
tfsflashwrite(ulong *dest,ulong *src,long bytecnt)
{
if (tfsTrace > 2)
printf("tfsflashwrite(0x%lx,0x%lx,%ld)\n",
(ulong)dest,(ulong)src,bytecnt);
if (bytecnt < 0)
return(-1);
tfsFmodCount++;
return(AppFlashWrite(dest,src,bytecnt));
}
/* tfserrmsg():
* Return the error message string that corresponds to the incoming
* tfs error number.
*/
char *
tfserrmsg(int errno)
{
struct tfserr *tep;
tep = tfserrtbl;
while(tep->msg) {
if (errno == tep->err)
return(tep->msg);
tep++;
}
return("unknown tfs errno");
}
/* tfsmakeStale():
* Modify the state of a file to be stale.
* Do this by clearing the TFS_NOTSTALE flag in the tfs header.
* This function is used by tfsadd() when in the process of
* updating a file that already exists in the flash.
* See comments above tfsadd() for more details on the TFS_NOTSTALE flag.
*/
static int
tfsmakeStale(TFILE *tfp)
{
ulong flags;
flags = TFS_FLAGS(tfp) & ~TFS_NSTALE;
if (tfsflashwrite(&tfp->flags,&flags,sizeof(long)) < 0)
return(TFSERR_FLASHFAILURE);
return(TFS_OKAY);
}
/* tfsflagsbtoa():
* Convert binary flags to ascii and return the string.
*/
char *
tfsflagsbtoa(long flags,char *fstr)
{
int i;
struct tfsflg *tfp;
if ((!flags) || (!fstr))
return((char *)0);
i = 0;
tfp = tfsflgtbl;
*fstr = 0;
while(tfp->sdesc) {
if ((flags & tfp->mask) == tfp->flag)
fstr[i++] = tfp->sdesc;
tfp++;
}
fstr[i] = 0;
return(fstr);
}
/* tfsflagsatob():
* Convert ascii flags to binary and return the long.
*/
static int
tfsflagsatob(char *fstr, long *flag)
{
struct tfsflg *tfp;
*flag = 0;
if (!fstr)
return(TFSERR_BADFLAG);
while(*fstr) {
tfp = tfsflgtbl;
while(tfp->sdesc) {
if (*fstr == tfp->sdesc) {
*flag |= tfp->flag;
break;
}
tfp++;
}
if (!tfp->flag)
return(TFSERR_BADFLAG);
fstr++;
}
return(TFS_OKAY);
}
/* validtfshdr():
* Return 1 if the header pointed to by the incoming header pointer is valid.
* Else return 0. The header crc is calculated based on the hdrcrc
* and next members of the structure being zero.
* Note that if the file is deleted, then just ignore the crc and return 1.
*/
int
validtfshdr(TFILE *hdr)
{
TFILE hdrcpy;
ulong hdrcrc;
/* A few quick checks... */
if (!hdr || hdr->hdrsize == ERASED16)
return(0);
/* Run a crc32 test...
* The header crc was originally calculated (in tfsadd()) with the
* header crc and next pointer nulled out; so a copy must be made and
* these two fields cleared. Also, note that the TFS_NSTALE and
* TFS_ACTIVE flags are forced to be set in the copy. This is done
* because it is possible that either of these bits may have been
* cleared due to other TFS interaction; hence, they need to be set
* prior to crc validateion.
* Note also that earlier versions of TFS deleted a file by clearing
* the entire flags field. This made it impossible to do a header crc
* check on a deleted file; deletion has been changed to simply clear
* the TFS_ACTIVE bit in the flags, so now a deleted file's header can
* can be crc tested by simply forcing the TFS_ACTIVE bit high as was
* mentioned above.
*/
hdrcpy = *hdr;
hdrcrc = hdr->hdrcrc;
hdrcpy.next = 0;
hdrcpy.hdrcrc = 0;
hdrcpy.flags |= (TFS_NSTALE | TFS_ACTIVE);
if (crc32((uchar *)&hdrcpy,TFSHDRSIZ) == hdrcrc)
return(1);
else {
/* Support transition to new deletion flag method... */
if ((hdr->flags == 0) && tfsOldDelFlagCheckActive)
return(1);
printf("Bad TFS hdr crc @ 0x%lx\n",(ulong)hdr);
return(0);
}
}
/* nextfp():
* Used as a common means of retrieving the next file header pointer. It
* does some sanity checks based on the fact that all pointers must fall
* within the TFSSTART<->TFSEND memory range and since each file is placed
* just after the previous one in linear memory space, fp->next should
* always be greater than fp.
*/
TFILE *
nextfp(TFILE *fp, TDEV *tdp)
{
if (!tdp)
tdp = gettfsdev(fp);
/* Make some basic in-range checks... */
if ((!tdp) || (fp < (TFILE *)tdp->start) || (fp > (TFILE *)tdp->end) ||
(fp->next < (TFILE *)tdp->start) || (fp->next > (TFILE *)tdp->end) ||
(fp->next <= fp)) {
printf("Bad TFS hdr ptr @ 0x%lx\n",(ulong)fp);
return(0);
}
return(fp->next);
}
/* tfsflasherased():
* Jump to the point in flash after the last file in TFS, then verify
* that all remaining flash that is dedicated to TFS is erased (0xff).
* If erased, return 1; else return 0.
*/
int
tfsflasherased(TDEV *tdp, int verbose)
{
ulong *lp;
TFILE *tfp;
tfp = (TFILE *)tdp->start;
while(validtfshdr(tfp))
tfp = nextfp(tfp,tdp);
lp = (ulong *)tfp;
while (lp < (ulong *)tdp->end) {
if (*lp != ERASED32) {
if (verbose)
printf("End of TFS on %s not erased at 0x%lx\n",
tdp->prefix,(ulong)lp);
return(0);
}
#ifdef WATCHDOG_MACRO
WATCHDOG_MACRO();
#endif
lp++;
}
return(1);
}
/* tfsmemuse():
* Step through one (or all) TFS devices and tally up various memory usage
* totals. See definition of tfsmem structure for more details.
* If incoming tdpin pointer is NULL, then tally up for all TFS devices;
* otherwise, tally up for only the one device pointed to by tdpin.
*/
int
tfsmemuse(TDEV *tdpin, TINFO *tinfo, int verbose)
{
int devtot;
TFILE *tfp;
TDEV *tdp;
/* Start by clearing incoming structure... */
tinfo->pso = 0;
tinfo->sos = 0;
tinfo->memtot = 0;
tinfo->liveftot = 0;
tinfo->deadftot = 0;
tinfo->livedata = 0;
tinfo->deaddata = 0;
tinfo->liveovrhd = 0;
tinfo->deadovrhd = 0;
if (verbose) {
printf("TFS Memory Usage...\n ");
printf(" name start end spare spsize scnt type\n");
}
devtot = 0;
for (tdp=tfsDeviceTbl;tdp->start != TFSEOT;tdp++) {
if (!tdpin || (tdpin == tdp)) {
devtot++;
tfp = (TFILE *)tdp->start;
if (verbose) {
printf("%10s: 0x%08lx|0x%08lx|0x%08lx|0x%06lx|%4ld|0x%lx\n",
tdp->prefix,(ulong)(tdp->start),(ulong)(tdp->end),
(ulong)(tdp->spare),tdp->sparesize,
tdp->sectorcount,(ulong)(tdp->devinfo));
}
tinfo->memtot += ((tdp->end - tdp->start) + 1) + tdp->sparesize;
tinfo->pso += (tdp->sectorcount * 4) + 16;
tinfo->sos += tdp->sparesize;
while(validtfshdr(tfp)) {
if (TFS_FILEEXISTS(tfp)) {
tinfo->liveftot++;
tinfo->livedata += TFS_SIZE(tfp);
tinfo->liveovrhd += (TFSHDRSIZ + sizeof(struct defraghdr));
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -