summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/boot/ofw/libofw/ofw_disk.c65
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)
{
+
}
OpenPOWER on IntegriCloud