commit a40b81b1c64504004d67f265b9284c7ca9f5f099
parent 19176444e19f44995419389945871864a8f3ed43
Author: Roberto Vargas <roberto.vargas@arm.com>
Date: Thu, 16 May 2019 10:05:22 +0100
[dev] Add devblk.c
This driver is intended for block based devices (for example
sd cards) and it implements a very simplified version of a buffer
cache. The blocks actually written when the buffer is taken for
other block or when fsync() or sync() are called.
Change-Id: I3cbab2eb348cd2401e951adaa876ba581a5fcc86
Diffstat:
3 files changed, 314 insertions(+), 0 deletions(-)
diff --git a/drivers/blk.h b/drivers/blk.h
@@ -0,0 +1,13 @@
+typedef struct blk Blk;
+typedef struct blkphy Blkphy;
+
+struct blk {
+ mutex_t m;
+ Blkphy *phy;
+};
+
+struct blkphy {
+ void (*init)(Blk *dev, Attr *attr);
+ int (*bread)(Blk *dev, long blkno, void *buf);
+ int (*bwrite)(Blk *dev, long blkno, void *buf);
+};
diff --git a/drivers/devblk.c b/drivers/devblk.c
@@ -0,0 +1,300 @@
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <rcode/rcode.h>
+#include <rcode/io.h>
+
+#include "dev.h"
+#include "blk.h"
+
+#define NR_BUFFERS 1
+#define NR_BLKS 2
+#define BLKSIZ 512
+
+typedef struct buffer Buffer;
+
+enum Odevqid {
+ Qblockfs,
+ Qraw,
+ Qctl,
+};
+
+enum bstatus {
+ BBUSY = 1 << 0,
+ BDIRTY = 1 << 1,
+ BVALID = 1 << 2,
+ BERROR = 1 << 3,
+};
+
+struct buffer {
+ int devno;
+ long block;
+ int flags;
+ void *ptr;
+ mutex_t mutex;
+};
+
+static char buffers[NR_BUFFERS][BLKSIZ];
+static Buffer bcache[NR_BUFFERS];
+
+static const Dirtab dirtab[] = {
+ {"raw", Qraw, 0, O_READ | O_WRITE},
+ {"ctl", Qctl, 0, O_READ | O_WRITE}
+};
+
+static Blk *blks[NR_BLKS];
+static int nblks;
+
+static int
+blkwalk(Chan *c, const char *name)
+{
+ return devwalk(c, name, dirtab, NELEM(dirtab), devgen);
+}
+
+static int
+blkstat(Chan *c, char *file, unsigned char *buf, int n)
+{
+ return devstat(c, file, buf, n, dirtab, NELEM(dirtab), devgen);
+}
+
+static Buffer *
+getblk(int devno, long blkno)
+{
+ Buffer *bp = &bcache[blkno % NR_BUFFERS];
+ Blk *dev;
+
+repeat:
+ lock(&bp->mutex);
+ if (bp->flags & BBUSY) {
+ unlock(&bp->mutex);
+ /* TODO: sleep(); */
+ goto repeat;
+ }
+
+ if ((bp->flags & BVALID) == 0 ||
+ bp->devno != devno || bp->block != blkno) {
+ if (bp->flags & BDIRTY) {
+ dev = blks[bp->devno];
+ if (dev->phy->bwrite(dev, bp->block, bp->ptr) < 0) {
+ errno = EIO;
+ bp->flags |= BERROR;
+ goto return_buffer;
+ }
+ }
+
+ bp->devno = devno;
+ bp->block = blkno;
+ bp->flags = 0;
+ dev = blks[devno];
+
+ switch (dev->phy->bread(dev, blkno, bp->ptr)) {
+ case -1:
+ bp->flags |= BERROR;
+ break;
+ case 1:
+ bp->flags |= BVALID;
+ break;
+ }
+ }
+
+return_buffer:
+ bp->flags |= BBUSY;
+ unlock(&bp->mutex);
+ return bp;
+}
+
+static int
+brelse(Buffer *bp)
+{
+ int r = 0;
+ Blk *dev;
+
+ if ((bp->flags & BBUSY) == 0)
+ panic("brelse");
+
+ lock(&bp->mutex);
+ if ((bp->flags & BDIRTY) != 0) {
+ dev = blks[bp->devno];
+ if (dev->phy->bwrite(dev, bp->block, bp->ptr) < 0) {
+ errno = EIO;
+ bp->flags |= BERROR;
+ r = -1;
+ }
+ }
+ bp->flags &= ~(BBUSY|BDIRTY);
+ unlock(&bp->mutex);
+
+ return r;
+}
+
+static int
+rawread(Chan *c, void *buf, int nbytes)
+{
+ int n, cnt;
+ Buffer *bp;
+ long off, blk;
+ char *ptr = buf;
+
+ for (cnt = 0; cnt < nbytes; cnt += n) {
+ blk = c->offset / BLKSIZ;
+ off = c->offset % BLKSIZ;
+
+ bp = getblk(c->dev, blk);
+ switch (bp->flags & (BVALID | BERROR)) {
+ case BVALID|BERROR:
+ case BERROR:
+ cnt = -1;
+ case 0:
+ brelse(bp);
+ return cnt;
+ case BVALID:
+ n = BLKSIZ - off;
+ if (n > nbytes)
+ n = nbytes;
+ memcpy(ptr, bp->ptr + off, n);
+ ptr += n;
+ c->offset += n;
+ bp->flags |= BDIRTY;
+ brelse(bp);
+ break;
+ }
+ }
+
+ return cnt;
+}
+
+static int
+blkread(Chan *c, void *buf, int n)
+{
+ switch (c->qid & ~CHDIR) {
+ case Qblockfs:
+ return dirread(c, buf, n, dirtab, NELEM(dirtab), devgen);
+ case Qraw:
+ return rawread(c, buf, n);
+ case Qctl:
+ /* TODO */
+ default:
+ panic("blkread");
+ }
+}
+
+static int
+rawwrite(Chan *c, void *buf, int nbytes)
+{
+ int n, cnt;
+ Buffer *bp;
+ long off, blk;
+ char *ptr = buf;
+
+ for (cnt = 0; cnt < nbytes; cnt += n) {
+ blk = c->offset / BLKSIZ;
+ off = c->offset % BLKSIZ;
+
+ bp = getblk(c->dev, blk);
+ switch (bp->flags & (BVALID | BERROR)) {
+ case 0:
+ errno = EFBIG;
+ case BVALID|BERROR:
+ case BERROR:
+ brelse(bp);
+ return -1;
+ case BVALID:
+ n = BLKSIZ - off;
+ if (n > nbytes)
+ n = nbytes;
+ memcpy(bp->ptr + off, ptr, n);
+ ptr += n;
+ c->offset += n;
+ bp->flags |= BDIRTY;
+ brelse(bp);
+ break;
+ }
+ }
+ return cnt;
+}
+
+static int
+blkwrite(Chan *c, void *buf, int n)
+{
+ switch (c->qid & ~CHDIR) {
+ case Qraw:
+ return rawwrite(c, buf, n);
+ case Qctl:
+ /* TODO */
+ default:
+ panic("blkwrite");
+ }
+}
+
+static int
+blksync(Chan *c, int what)
+{
+ Blk *dev;
+ Buffer *bp;
+ int r = 0;
+
+ for (bp = bcache; bp < &bcache[NR_BUFFERS]; ++bp) {
+ lock(&bp->mutex);
+
+ if ((bp->flags & BDIRTY) != 0 &&
+ (what == SYNCALL || bp->devno == c->dev)) {
+ dev = blks[bp->devno];
+ if (dev->phy->bwrite(dev, bp->block, bp->ptr) < 0) {
+ r = -1;
+ errno = EIO;
+ bp->flags |= BERROR;
+ } else {
+ bp->flags &= BDIRTY;
+ }
+ }
+
+ unlock(&bp->mutex);
+ }
+
+ return r;
+}
+
+void
+blklink(Blkphy *phy, Attr *attr)
+{
+ size_t siz;
+ Blk *dev;
+ static int first = 1;
+
+ if (nblks == NR_BLKS)
+ panic("blklink");
+
+ if (first) {
+ int i;
+ Buffer *bp;
+
+ for (i = 0; i < NR_BUFFERS; i++) {
+ bp = &bcache[i];
+ bp->ptr = buffers[i];
+ bp->flags = 0;
+ }
+ first = 0;
+ }
+
+ siz = sizeof(Blk);
+ dev = memset(alloc(siz), 0, siz);
+ dev->phy = phy;
+ blks[nblks++] = dev;
+
+ (*phy->init)(dev, attr);
+}
+
+const Dev blkdevtab = {
+ .id = 'b',
+ .stat = blkstat,
+ .clone = devclone,
+ .attach = devattach,
+ .walk = blkwalk,
+ .read = blkread,
+ .write = blkwrite,
+ .mount = deverrmount,
+ .seek = devseek,
+ .sync = blksync,
+};
diff --git a/target/hosted/rcode b/target/hosted/rcode
@@ -4,6 +4,7 @@ dev
dummyuart base=0x1c0c0000,clk=24000000,cfg=b115200 l8 #t0
dummyuart base=0x1c0c0100,clk=24000000 #t1
cons
+ blk
ar
blob
bios 0x100,0x400