Merge enhancements to the ALTERA Avalon bus generic device attachment

driver to support exposing a GEOM device, which can be used to mount
Avalon-attached ROMs, reserved areas of DRAM, etc, as a filesystem:

commit 9deb1e60eaaaf7a3687e48c58af5efd756f32ec6
Author: Robert N. M. Watson <robert.watson@cl.cam.ac.uk>
Date:   Sat Mar 5 20:33:12 2016 +0000

    Use format strings with make_dev(9) in avgen(4).

commit 0bf2176c23e7425bfa042c08a24f8a25fe6d8885
Author: Robert N. M. Watson <robert.watson@cl.cam.ac.uk>
Date:   Tue Mar 1 10:23:23 2016 +0000

    Implement a new "geomio" configuration argument to altera_avgen(4),
    the generic I/O device we attach to various BERI peripherals.  The new
    option requests that, instead of exposing the underlying device via a
    special device node in /dev, it instead be exposed via geom(4),
    allowing it to be used with filesystems.  The current implementation
    does not allow a device to be exposed both for file/mmap and geom, so
    one of the two models must be selected when configuring it via FDT or
    device.hints.  A typical use of the new option will be:

      sri-cambridge,geomio = "rw";

MFC after:	1 week
Sponsored by:	DARPA, AFRL
This commit is contained in:
Robert Watson 2017-01-28 13:25:06 +00:00
parent 2f1c643658
commit f00e79c847
4 changed files with 240 additions and 37 deletions

View file

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2012-2013 Robert N. M. Watson
* Copyright (c) 2012-2013, 2016 Robert N. M. Watson
* All rights reserved.
*
* This software was developed by SRI International and the University of
@ -32,6 +32,7 @@
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/bio.h>
#include <sys/bus.h>
#include <sys/condvar.h>
#include <sys/conf.h>
@ -45,6 +46,8 @@ __FBSDID("$FreeBSD$");
#include <sys/systm.h>
#include <sys/uio.h>
#include <geom/geom_disk.h>
#include <machine/bus.h>
#include <machine/resource.h>
@ -65,14 +68,19 @@ static d_mmap_t altera_avgen_mmap;
static d_read_t altera_avgen_read;
static d_write_t altera_avgen_write;
#define ALTERA_AVGEN_DEVNAME "altera_avgen"
#define ALTERA_AVGEN_DEVNAME_FMT (ALTERA_AVGEN_DEVNAME "%d")
static struct cdevsw avg_cdevsw = {
.d_version = D_VERSION,
.d_mmap = altera_avgen_mmap,
.d_read = altera_avgen_read,
.d_write = altera_avgen_write,
.d_name = "altera_avgen",
.d_name = ALTERA_AVGEN_DEVNAME,
};
#define ALTERA_AVGEN_SECTORSIZE 512 /* Not configurable at this time. */
static int
altera_avgen_read(struct cdev *dev, struct uio *uio, int flag)
{
@ -227,11 +235,103 @@ altera_avgen_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr,
return (0);
}
/*
* NB: We serialise block reads and writes in case the OS is generating
* concurrent I/O against the same block, in which case we want one I/O (or
* another) to win. This is not sufficient to provide atomicity for the
* sector in the presence of a fail stop -- however, we're just writing this
* to non-persistent DRAM .. right?
*/
static void
altera_avgen_disk_strategy(struct bio *bp)
{
struct altera_avgen_softc *sc;
void *data;
long bcount;
daddr_t pblkno;
sc = bp->bio_disk->d_drv1;
data = bp->bio_data;
bcount = bp->bio_bcount;
pblkno = bp->bio_pblkno;
/*
* Serialize block reads / writes.
*/
mtx_lock(&sc->avg_disk_mtx);
switch (bp->bio_cmd) {
case BIO_READ:
if (!(sc->avg_flags & ALTERA_AVALON_FLAG_GEOM_READ)) {
biofinish(bp, NULL, EIO);
break;
}
switch (sc->avg_width) {
case 1:
bus_read_region_1(sc->avg_res,
bp->bio_pblkno * ALTERA_AVGEN_SECTORSIZE,
(uint8_t *)data, bcount);
break;
case 2:
bus_read_region_2(sc->avg_res,
bp->bio_pblkno * ALTERA_AVGEN_SECTORSIZE,
(uint16_t *)data, bcount / 2);
break;
case 4:
bus_read_region_4(sc->avg_res,
bp->bio_pblkno * ALTERA_AVGEN_SECTORSIZE,
(uint32_t *)data, bcount / 4);
break;
default:
panic("%s: unexpected width %u", __func__,
sc->avg_width);
}
break;
case BIO_WRITE:
if (!(sc->avg_flags & ALTERA_AVALON_FLAG_GEOM_WRITE)) {
biofinish(bp, NULL, EROFS);
break;
}
switch (sc->avg_width) {
case 1:
bus_write_region_1(sc->avg_res,
bp->bio_pblkno * ALTERA_AVGEN_SECTORSIZE,
(uint8_t *)data, bcount);
break;
case 2:
bus_write_region_2(sc->avg_res,
bp->bio_pblkno * ALTERA_AVGEN_SECTORSIZE,
(uint16_t *)data, bcount / 2);
break;
case 4:
bus_write_region_4(sc->avg_res,
bp->bio_pblkno * ALTERA_AVGEN_SECTORSIZE,
(uint32_t *)data, bcount / 4);
break;
default:
panic("%s: unexpected width %u", __func__,
sc->avg_width);
}
break;
default:
panic("%s: unsupported I/O operation %d", __func__,
bp->bio_cmd);
}
mtx_unlock(&sc->avg_disk_mtx);
biofinish(bp, NULL, 0);
}
static int
altera_avgen_process_options(struct altera_avgen_softc *sc,
const char *str_fileio, const char *str_mmapio, const char *str_devname,
int devunit)
const char *str_fileio, const char *str_geomio, const char *str_mmapio,
const char *str_devname, int devunit)
{
const char *cp;
device_t dev = sc->avg_dev;
@ -239,12 +339,30 @@ altera_avgen_process_options(struct altera_avgen_softc *sc,
/*
* Check for valid combinations of options.
*/
if (str_fileio == NULL && str_mmapio == NULL) {
if (str_fileio == NULL && str_geomio == NULL && str_mmapio == NULL) {
device_printf(dev,
"at least one of %s or %s must be specified\n",
ALTERA_AVALON_STR_FILEIO, ALTERA_AVALON_STR_MMAPIO);
"at least one of %s, %s, or %s must be specified\n",
ALTERA_AVALON_STR_FILEIO, ALTERA_AVALON_STR_GEOMIO,
ALTERA_AVALON_STR_MMAPIO);
return (ENXIO);
}
/*
* Validity check: a device can either be a GEOM device (in which case
* we use GEOM to register the device node), or a special device --
* but not both as that causes a collision in /dev.
*/
if (str_geomio != NULL && (str_fileio != NULL || str_mmapio != NULL)) {
device_printf(dev,
"at most one of %s and (%s or %s) may be specified\n",
ALTERA_AVALON_STR_GEOMIO, ALTERA_AVALON_STR_FILEIO,
ALTERA_AVALON_STR_MMAPIO);
return (ENXIO);
}
/*
* Ensure that a unit is specified if a name is also specified.
*/
if (str_devname == NULL && devunit != -1) {
device_printf(dev, "%s requires %s be specified\n",
ALTERA_AVALON_STR_DEVUNIT, ALTERA_AVALON_STR_DEVNAME);
@ -288,6 +406,25 @@ altera_avgen_process_options(struct altera_avgen_softc *sc,
}
}
}
if (str_geomio != NULL) {
for (cp = str_geomio; *cp != '\0'; cp++){
switch (*cp) {
case ALTERA_AVALON_CHAR_READ:
sc->avg_flags |= ALTERA_AVALON_FLAG_GEOM_READ;
break;
case ALTERA_AVALON_CHAR_WRITE:
sc->avg_flags |= ALTERA_AVALON_FLAG_GEOM_WRITE;
break;
default:
device_printf(dev,
"invalid %s character %c\n",
ALTERA_AVALON_STR_GEOMIO, *cp);
return (ENXIO);
}
}
}
if (str_mmapio != NULL) {
for (cp = str_mmapio; *cp != '\0'; cp++) {
switch (*cp) {
@ -317,13 +454,14 @@ altera_avgen_process_options(struct altera_avgen_softc *sc,
int
altera_avgen_attach(struct altera_avgen_softc *sc, const char *str_fileio,
const char *str_mmapio, const char *str_devname, int devunit)
const char *str_geomio, const char *str_mmapio, const char *str_devname,
int devunit)
{
device_t dev = sc->avg_dev;
int error;
error = altera_avgen_process_options(sc, str_fileio, str_mmapio,
str_devname, devunit);
error = altera_avgen_process_options(sc, str_fileio, str_geomio,
str_mmapio, str_devname, devunit);
if (error)
return (error);
@ -339,23 +477,59 @@ altera_avgen_attach(struct altera_avgen_softc *sc, const char *str_fileio,
}
}
/* Device node allocation. */
if (str_devname == NULL) {
str_devname = "altera_avgen%d";
/*
* If a GEOM permission is requested, then create the device via GEOM.
* Otherwise, create a special device. We checked during options
* processing that both weren't requested a once.
*/
if (str_devname != NULL) {
sc->avg_name = strdup(str_devname, M_TEMP);
devunit = sc->avg_unit;
} else
sc->avg_name = strdup(ALTERA_AVGEN_DEVNAME, M_TEMP);
if (sc->avg_flags & (ALTERA_AVALON_FLAG_GEOM_READ |
ALTERA_AVALON_FLAG_GEOM_WRITE)) {
mtx_init(&sc->avg_disk_mtx, "altera_avgen_disk", NULL,
MTX_DEF);
sc->avg_disk = disk_alloc();
sc->avg_disk->d_drv1 = sc;
sc->avg_disk->d_strategy = altera_avgen_disk_strategy;
if (devunit == -1)
devunit = 0;
sc->avg_disk->d_name = sc->avg_name;
sc->avg_disk->d_unit = devunit;
/*
* NB: As avg_res is a multiple of PAGE_SIZE, it is also a
* multiple of ALTERA_AVGEN_SECTORSIZE.
*/
sc->avg_disk->d_sectorsize = ALTERA_AVGEN_SECTORSIZE;
sc->avg_disk->d_mediasize = rman_get_size(sc->avg_res);
sc->avg_disk->d_maxsize = ALTERA_AVGEN_SECTORSIZE;
disk_create(sc->avg_disk, DISK_VERSION);
} else {
/* Device node allocation. */
if (str_devname == NULL) {
str_devname = ALTERA_AVGEN_DEVNAME_FMT;
devunit = sc->avg_unit;
}
if (devunit != -1)
sc->avg_cdev = make_dev(&avg_cdevsw, sc->avg_unit,
UID_ROOT, GID_WHEEL, S_IRUSR | S_IWUSR, "%s%d",
str_devname, devunit);
else
sc->avg_cdev = make_dev(&avg_cdevsw, sc->avg_unit,
UID_ROOT, GID_WHEEL, S_IRUSR | S_IWUSR,
"%s", str_devname);
if (sc->avg_cdev == NULL) {
device_printf(sc->avg_dev, "%s: make_dev failed\n",
__func__);
return (ENXIO);
}
/* XXXRW: Slight race between make_dev(9) and here. */
sc->avg_cdev->si_drv1 = sc;
}
if (devunit != -1)
sc->avg_cdev = make_dev(&avg_cdevsw, sc->avg_unit, UID_ROOT,
GID_WHEEL, S_IRUSR | S_IWUSR, str_devname, devunit);
else
sc->avg_cdev = make_dev(&avg_cdevsw, sc->avg_unit, UID_ROOT,
GID_WHEEL, S_IRUSR | S_IWUSR, str_devname);
if (sc->avg_cdev == NULL) {
device_printf(sc->avg_dev, "%s: make_dev failed\n", __func__);
return (ENXIO);
}
/* XXXRW: Slight race between make_dev(9) and here. */
sc->avg_cdev->si_drv1 = sc;
return (0);
}
@ -363,5 +537,15 @@ void
altera_avgen_detach(struct altera_avgen_softc *sc)
{
destroy_dev(sc->avg_cdev);
KASSERT((sc->avg_disk != NULL) || (sc->avg_cdev != NULL),
("%s: neither GEOM nor special device", __func__));
if (sc->avg_disk != NULL) {
disk_gone(sc->avg_disk);
disk_destroy(sc->avg_disk);
free(sc->avg_name, M_TEMP);
mtx_destroy(&sc->avg_disk_mtx);
} else {
destroy_dev(sc->avg_cdev);
}
}

View file

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2012 Robert N. M. Watson
* Copyright (c) 2012, 2016 Robert N. M. Watson
* All rights reserved.
*
* This software was developed by SRI International and the University of
@ -39,6 +39,7 @@ struct altera_avgen_softc {
*/
device_t avg_dev;
int avg_unit;
char *avg_name;
/*
* The device node and memory-mapped I/O region.
@ -52,6 +53,13 @@ struct altera_avgen_softc {
*/
u_int avg_flags;
u_int avg_width;
u_int avg_sectorsize;
/*
* disk(9) state, if required for this device.
*/
struct disk *avg_disk;
struct mtx avg_disk_mtx;
};
/*
@ -63,6 +71,8 @@ struct altera_avgen_softc {
#define ALTERA_AVALON_FLAG_MMAP_READ 0x04
#define ALTERA_AVALON_FLAG_MMAP_WRITE 0x08
#define ALTERA_AVALON_FLAG_MMAP_EXEC 0x10
#define ALTERA_AVALON_FLAG_GEOM_READ 0x20
#define ALTERA_AVALON_FLAG_GEOM_WRITE 0x40
#define ALTERA_AVALON_CHAR_READ 'r'
#define ALTERA_AVALON_CHAR_WRITE 'w'
@ -70,6 +80,7 @@ struct altera_avgen_softc {
#define ALTERA_AVALON_STR_WIDTH "width"
#define ALTERA_AVALON_STR_FILEIO "fileio"
#define ALTERA_AVALON_STR_GEOMIO "geomio"
#define ALTERA_AVALON_STR_MMAPIO "mmapio"
#define ALTERA_AVALON_STR_DEVNAME "devname"
#define ALTERA_AVALON_STR_DEVUNIT "devunit"
@ -78,8 +89,8 @@ struct altera_avgen_softc {
* Driver setup routines from the bus attachment/teardown.
*/
int altera_avgen_attach(struct altera_avgen_softc *sc,
const char *str_fileio, const char *str_mmapio,
const char *str_devname, int devunit);
const char *str_fileio, const char *str_geomio,
const char *str_mmapio, const char *str_devname, int devunit);
void altera_avgen_detach(struct altera_avgen_softc *sc);
extern devclass_t altera_avgen_devclass;

View file

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2012-2013 Robert N. M. Watson
* Copyright (c) 2012-2013, 2016 Robert N. M. Watson
* All rights reserved.
*
* This software was developed by SRI International and the University of
@ -75,7 +75,7 @@ static int
altera_avgen_fdt_attach(device_t dev)
{
struct altera_avgen_softc *sc;
char *str_fileio, *str_mmapio;
char *str_fileio, *str_geomio, *str_mmapio;
char *str_devname;
phandle_t node;
pcell_t cell;
@ -90,6 +90,7 @@ altera_avgen_fdt_attach(device_t dev)
* expose the device via /dev.
*/
str_fileio = NULL;
str_geomio = NULL;
str_mmapio = NULL;
str_devname = NULL;
devunit = -1;
@ -99,6 +100,8 @@ altera_avgen_fdt_attach(device_t dev)
sc->avg_width = cell;
(void)OF_getprop_alloc(node, "sri-cambridge,fileio", sizeof(char),
(void **)&str_fileio);
(void)OF_getprop_alloc(node, "sri-cambridge,geomio", sizeof(char),
(void **)&str_geomio);
(void)OF_getprop_alloc(node, "sri-cambridge,mmapio", sizeof(char),
(void **)&str_mmapio);
(void)OF_getprop_alloc(node, "sri-cambridge,devname", sizeof(char),
@ -114,13 +117,15 @@ altera_avgen_fdt_attach(device_t dev)
device_printf(dev, "couldn't map memory\n");
return (ENXIO);
}
error = altera_avgen_attach(sc, str_fileio, str_mmapio, str_devname,
devunit);
error = altera_avgen_attach(sc, str_fileio, str_geomio, str_mmapio,
str_devname, devunit);
if (error != 0)
bus_release_resource(dev, SYS_RES_MEMORY, sc->avg_rid,
sc->avg_res);
if (str_fileio != NULL)
OF_prop_free(str_fileio);
if (str_geomio != NULL)
OF_prop_free(str_geomio);
if (str_mmapio != NULL)
OF_prop_free(str_mmapio);
if (str_devname != NULL)

View file

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2012-2013 Robert N. M. Watson
* Copyright (c) 2012-2013, 2016 Robert N. M. Watson
* All rights reserved.
*
* This software was developed by SRI International and the University of
@ -64,7 +64,7 @@ static int
altera_avgen_nexus_attach(device_t dev)
{
struct altera_avgen_softc *sc;
const char *str_fileio, *str_mmapio;
const char *str_fileio, *str_geomio, *str_mmapio;
const char *str_devname;
int devunit, error;
@ -77,6 +77,7 @@ altera_avgen_nexus_attach(device_t dev)
* on the device, and whether it is cached.
*/
str_fileio = NULL;
str_geomio = NULL;
str_mmapio = NULL;
str_devname = NULL;
devunit = -1;
@ -89,6 +90,8 @@ altera_avgen_nexus_attach(device_t dev)
}
(void)resource_string_value(device_get_name(dev),
device_get_unit(dev), ALTERA_AVALON_STR_FILEIO, &str_fileio);
(void)resource_string_value(device_get_name(dev),
device_get_unit(dev), ALTERA_AVALON_STR_GEOMIO, &str_geomio);
(void)resource_string_value(device_get_name(dev),
device_get_unit(dev), ALTERA_AVALON_STR_MMAPIO, &str_mmapio);
(void)resource_string_value(device_get_name(dev),
@ -104,8 +107,8 @@ altera_avgen_nexus_attach(device_t dev)
device_printf(dev, "couldn't map memory\n");
return (ENXIO);
}
error = altera_avgen_attach(sc, str_fileio, str_mmapio, str_devname,
devunit);
error = altera_avgen_attach(sc, str_fileio, str_geomio, str_mmapio,
str_devname, devunit);
if (error != 0)
bus_release_resource(dev, SYS_RES_MEMORY, sc->avg_rid,
sc->avg_res);