diff options
-rw-r--r-- | sys/boot/ofw/libofw/ofw_disk.c | 65 |
1 files changed, 58 insertions, 7 deletions
diff --git a/sys/boot/ofw/libofw/ofw_disk.c b/sys/boot/ofw/libofw/ofw_disk.c index dfeafe0..e859c8f 100644 --- a/sys/boot/ofw/libofw/ofw_disk.c +++ b/sys/boot/ofw/libofw/ofw_disk.c @@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$"); */ #include <sys/param.h> +#include <sys/queue.h> #include <netinet/in.h> @@ -60,9 +61,18 @@ struct devsw ofwdisk = { ofwd_print }; +struct opened_dev { + ihandle_t handle; + u_int count; + SLIST_ENTRY(opened_dev) link; +}; + +SLIST_HEAD(, opened_dev) opened_devs = SLIST_HEAD_INITIALIZER(opened_dev); + static int ofwd_init(void) { + return 0; } @@ -73,7 +83,6 @@ ofwd_strategy(void *devdata, int flag, daddr_t dblk, size_t size, char *buf, struct ofw_devdesc *dp = (struct ofw_devdesc *)devdata; daddr_t pos; int n; - int i, j; pos = dblk * 512; do { @@ -90,18 +99,47 @@ ofwd_strategy(void *devdata, int flag, daddr_t dblk, size_t size, char *buf, static int ofwd_open(struct open_file *f, ...) { + char path[256]; struct ofw_devdesc *dp; - phandle_t handle; + struct opened_dev *odp; va_list vl; va_start(vl, f); dp = va_arg(vl, struct ofw_devdesc *); va_end(vl); - if ((handle = OF_open(dp->d_path)) == -1) { + /* + * We're not guaranteed to be able to open a device more than once + * simultaneously and there is no OFW standard method to determine + * whether a device is already opened. Opening a device more than + * once happens to work with most OFW block device drivers but + * triggers a trap with at least the driver for the on-board SCSI + * controller in Sun Ultra 1. Upper layers and MI code expect to + * be able to open a device more than once however. As a workaround + * keep track of the opened devices and reuse the instance handle + * when asked to open an already opened device. + */ + SLIST_FOREACH(odp, &opened_devs, link) { + if (OF_instance_to_path(odp->handle, path, sizeof(path)) == -1) + continue; + if (strcmp(path, dp->d_path) == 0) { + odp->count++; + dp->d_handle = odp->handle; + return 0; + } + } + odp = malloc(sizeof(struct opened_dev)); + if (odp == NULL) { + printf("ofwd_open: malloc failed\n"); + return ENOMEM; + } + if ((odp->handle = OF_open(dp->d_path)) == -1) { printf("ofwd_open: Could not open %s\n", dp->d_path); - return 1; + free(odp); + return ENOENT; } - dp->d_handle = handle; + odp->count = 1; + SLIST_INSERT_HEAD(&opened_devs, odp, link); + dp->d_handle = odp->handle; return 0; } @@ -109,8 +147,20 @@ static int ofwd_close(struct open_file *f) { struct ofw_devdesc *dev = f->f_devdata; - - OF_close(dev->d_handle); + struct opened_dev *odp; + + SLIST_FOREACH(odp, &opened_devs, link) { + if (odp->handle == dev->d_handle) { + odp->count--; + if (odp->count == 0) { + SLIST_REMOVE(&opened_devs, odp, opened_dev, + link); + OF_close(odp->handle); + free(odp); + } + break; + } + } return 0; } @@ -124,4 +174,5 @@ ofwd_ioctl(struct open_file *f, u_long cmd, void *data) static void ofwd_print(int verbose) { + } |