Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F142018008
D34097.id.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
72 KB
Referenced Files
None
Subscribers
None
D34097.id.diff
View Options
diff --git a/include/door.h b/include/door.h
new file mode 100644
--- /dev/null
+++ b/include/door.h
@@ -0,0 +1,32 @@
+#ifndef _DOOR_H_
+#define _DOOR_H_
+
+#include <sys/types.h>
+#include <sys/door.h>
+
+typedef u_int uint_t;
+
+typedef void *(server_procedure)(
+ void *udata, char *argp, size_t arg_size, door_desc_t *dp, u_int n_desc);
+
+int door_create(server_procedure *procedure, void *udata, u_int attributes);
+int door_call(int fd, door_arg_t *args);
+int door_return(
+ char *data_ptr, size_t data_size, door_desc_t *desc, size_t num_desc);
+int door_attach(int fd, const char *path);
+int door_detach(const char *path);
+int door_revoke(int fd);
+int door_info(int fd, struct door_info *info);
+int door_getparam(int fd, int param, size_t *out);
+int door_setparam(int fd, int param, size_t val);
+int door_bind(int did);
+int door_unbind(void);
+
+typedef void door_server_func_t(door_info_t *);
+door_server_func_t *door_server_create(door_server_func_t *);
+
+/* Solaris compatibility */
+#define fattach(fd, path) door_attach((fd), (path))
+#define fdetach(path) door_detach((path))
+
+#endif /* _DOOR_H_ */
diff --git a/sys/amd64/amd64/door_machdep.c b/sys/amd64/amd64/door_machdep.c
new file mode 100644
--- /dev/null
+++ b/sys/amd64/amd64/door_machdep.c
@@ -0,0 +1,17 @@
+#include <sys/door_extern.h>
+#include <sys/proc.h>
+#include <sys/param.h>
+
+#include <machine/cpu.h>
+#include <machine/md_var.h>
+
+
+void door_set_args(struct thread *td, void *cookiep, char *argp, size_t arg_size, struct door_desc *dp, u_int n_desc){
+ td->td_retval[0] = (register_t)cookiep;
+ td->td_frame->tf_rsi = (register_t)argp;
+ /* The second arg should go in rdx, but it gets overwritten by td->retval[1] in cpu_set_syscall_retval */
+ td->td_retval[1] = arg_size;
+ td->td_frame->tf_rcx = (register_t)dp;
+ td->td_frame->tf_r8 = (register_t)n_desc;
+}
+
diff --git a/sys/conf/files b/sys/conf/files
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -3604,6 +3604,7 @@
fs/tmpfs/tmpfs_fifoops.c optional tmpfs
fs/tmpfs/tmpfs_vfsops.c optional tmpfs
fs/tmpfs/tmpfs_subr.c optional tmpfs
+fs/doorfs/doorfs_vnops.c standard
gdb/gdb_cons.c optional gdb
gdb/gdb_main.c optional gdb
gdb/gdb_packet.c optional gdb
@@ -3770,6 +3771,7 @@
kern/kern_descrip.c standard
kern/kern_dtrace.c optional kdtrace_hooks
kern/kern_dump.c standard
+kern/kern_door.c standard
kern/kern_environment.c standard
kern/kern_et.c standard
kern/kern_event.c standard
diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64
--- a/sys/conf/files.amd64
+++ b/sys/conf/files.amd64
@@ -81,6 +81,7 @@
compile-with "${NORMAL_S} -g0" \
no-ctfconvert
amd64/amd64/machdep.c standard
+amd64/amd64/door_machdep.c standard
amd64/amd64/mem.c optional mem
amd64/amd64/minidump_machdep.c standard
amd64/amd64/mp_machdep.c optional smp
diff --git a/sys/fs/cd9660/cd9660_vnops.c b/sys/fs/cd9660/cd9660_vnops.c
--- a/sys/fs/cd9660/cd9660_vnops.c
+++ b/sys/fs/cd9660/cd9660_vnops.c
@@ -107,6 +107,7 @@
return (EROFS);
case VCHR:
case VBLK:
+ case VDOOR:
case VSOCK:
case VFIFO:
case VNON:
diff --git a/sys/fs/doorfs/doorfs_vnops.c b/sys/fs/doorfs/doorfs_vnops.c
new file mode 100644
--- /dev/null
+++ b/sys/fs/doorfs/doorfs_vnops.c
@@ -0,0 +1,191 @@
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/door.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/proc.h>
+#include <sys/syslog.h>
+#include <sys/unistd.h>
+#include <sys/vnode.h>
+
+MALLOC_DECLARE(M_DOOR);
+
+static int
+door_open(struct vop_open_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ KASSERT(vp->v_type == VDOOR, ("door_open invoked on non-door vnode"));
+
+ struct door *d = vp->v_door_vnode_info->v_door;
+ struct vop_vector *prev_vnops =
+ ap->a_vp->v_door_vnode_info->v_prev_vnops;
+
+ KASSERT(
+ vp != d->door_vnode, ("door_open invoked on non-attached vnode"));
+
+ DOOR_LOCK(d);
+ d->refcount++;
+ DOOR_UNLOCK(d);
+
+ return prev_vnops->vop_open(ap);
+}
+
+static int
+door_close(struct vop_close_args *ap)
+{
+
+ struct vnode *vp = ap->a_vp;
+ KASSERT(vp->v_type == VDOOR, ("door_close invoked on non-door vnode"));
+
+ struct door *d = vp->v_door_vnode_info->v_door;
+
+ DOOR_LOCK(d);
+
+ d->refcount--;
+
+ if ((d->attr & (DOOR_UNREF | DOOR_UNREF_MULTI)) && d->refcount == 1) {
+ d->unrefcount++;
+
+ if ((d->attr & DOOR_UNREF_ACTIVE) == 0) {
+ /*
+ * If an unref thread is not bound yet
+ * (can occur if client only opens and closes door file
+ * shortly after door_create) wait for a while and then
+ * (hopefully) proceed.
+ */
+ if (d->unref_td == NULL) {
+ int error = cv_timedwait(
+ &d->unref_cv, &d->door_lock, hz / 2);
+ if (error == EWOULDBLOCK) {
+ // TODO: log error rather than
+ // panicking
+ printf("door_close: no unref thread attached");
+ return 0;
+ }
+ }
+
+ DOOR_UNLOCK(d);
+ /* Deliver unref */
+ wakeup_one(d->unref_td);
+ }
+
+ } else if (d->refcount == 0) {
+
+ /*
+ * This close could have come from another process which opened
+ * an attached node or from the owning process.
+ */
+ if (d->proc) {
+ PROC_LOCK(d->proc);
+ LIST_REMOVE(d, entries);
+
+ /* Wake all calling threads waiting for a server */
+ cv_broadcast(&d->proc->door_td_pool.pool_cv);
+ PROC_UNLOCK(d->proc);
+
+ d->proc = NULL;
+ }
+
+ if (d->attr & DOOR_PRIVATE) {
+ /* Wake all calling threads waiting for a server */
+ cv_broadcast(&d->priv_pool.pool_cv);
+ }
+
+ /* "Detach" door from vnode if the last reference was held by
+ * another process */
+ if (d->door_vnode != vp) {
+ vp->v_type = VREG;
+ vp->v_op = vp->v_door_vnode_info->v_prev_vnops;
+ }
+
+ struct door_vnode_info *tmp = vp->v_door_vnode_info;
+ vp->v_door_vnode_info = NULL;
+
+ wakeup(d);
+ while (d->active_invocations > 0) {
+ cv_wait(&d->close_cv, &d->door_lock);
+ }
+
+ DOOR_UNLOCK(d);
+
+
+ cv_destroy(&d->unref_cv);
+ cv_destroy(&d->close_cv);
+
+ free(tmp, M_TEMP);
+ free(d, M_DOOR);
+ } else {
+ DOOR_UNLOCK(d);
+ }
+
+ return 0;
+}
+
+/* A VDOOR type vnode can only be accessed when attached */
+static int
+door_access(struct vop_access_args *ap)
+{
+ KASSERT(ap->a_vp->v_type == VDOOR,
+ ("door_access invoked on non-door vnode"));
+ /* Forward access to underlying fs */
+ struct vop_vector *prev_vnops =
+ ap->a_vp->v_door_vnode_info->v_prev_vnops;
+ KASSERT(prev_vnops, ("door_access invoked on detached door vnode"));
+
+ return prev_vnops->vop_access(ap);
+}
+
+static int
+door_accessx(struct vop_accessx_args *ap)
+{
+ KASSERT(ap->a_vp->v_type == VDOOR,
+ ("door_accessx invoked on non-door vnode"));
+ /* Forward access to underlying fs */
+ struct vop_vector *prev_vnops =
+ ap->a_vp->v_door_vnode_info->v_prev_vnops;
+
+ return prev_vnops->vop_accessx(ap);
+}
+
+static int
+door_reclaim(struct vop_reclaim_args *ap)
+{
+
+ struct vnode *vp = ap->a_vp;
+ KASSERT(
+ vp->v_type == VDOOR, ("door_reclaim invoked on non-door vnode"));
+
+ return 0;
+}
+
+struct vop_vector door_vnops = {
+ .vop_default = &default_vnodeops,
+
+ .vop_access = door_access,
+ .vop_accessx = door_accessx,
+ .vop_close = door_close,
+ .vop_reclaim = door_reclaim,
+ .vop_open = door_open,
+
+ .vop_create = VOP_EBADF,
+ .vop_getattr = VOP_EBADF,
+ .vop_advlock = VOP_EBADF,
+ .vop_ioctl = VOP_EBADF,
+ .vop_kqfilter = VOP_EBADF,
+ .vop_link = VOP_EBADF,
+ .vop_mkdir = VOP_EBADF,
+ .vop_mknod = VOP_EBADF,
+ .vop_pathconf = VOP_EBADF,
+ .vop_print = VOP_EBADF,
+ .vop_read = VOP_EBADF,
+ .vop_readdir = VOP_EBADF,
+ .vop_readlink = VOP_EBADF,
+ .vop_reallocblks = VOP_EBADF,
+ .vop_rename = VOP_EBADF,
+ .vop_rmdir = VOP_EBADF,
+ .vop_remove = VOP_EBADF,
+ .vop_setattr = VOP_EBADF,
+ .vop_symlink = VOP_EBADF,
+ .vop_write = VOP_EBADF,
+};
+VFS_VOP_VECTOR_REGISTER(door_vnops);
diff --git a/sys/fs/tmpfs/tmpfs_subr.c b/sys/fs/tmpfs/tmpfs_subr.c
--- a/sys/fs/tmpfs/tmpfs_subr.c
+++ b/sys/fs/tmpfs/tmpfs_subr.c
@@ -647,6 +647,7 @@
case VSOCK:
case VLNK:
case VREG:
+ case VDOOR:
break;
case VNON:
case VBAD:
diff --git a/sys/fs/udf/udf_vnops.c b/sys/fs/udf/udf_vnops.c
--- a/sys/fs/udf/udf_vnops.c
+++ b/sys/fs/udf/udf_vnops.c
@@ -363,6 +363,7 @@
case VNON:
case VBAD:
case VMARKER:
+ case VDOOR:
return (0);
}
}
diff --git a/sys/kern/init_sysent.c b/sys/kern/init_sysent.c
--- a/sys/kern/init_sysent.c
+++ b/sys/kern/init_sysent.c
@@ -644,4 +644,5 @@
{ .sy_narg = AS(fspacectl_args), .sy_call = (sy_call_t *)sys_fspacectl, .sy_auevent = AUE_FSPACECTL, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 580 = fspacectl */
{ .sy_narg = 0, .sy_call = (sy_call_t *)sys_sched_getcpu, .sy_auevent = AUE_NULL, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 581 = sched_getcpu */
{ .sy_narg = AS(swapoff_args), .sy_call = (sy_call_t *)sys_swapoff, .sy_auevent = AUE_SWAPOFF, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 582 = swapoff */
+ { .sy_narg = AS(door_args), .sy_call = (sy_call_t *)sys_door, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 583 = door */
};
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -4760,6 +4760,8 @@
return ("eventfd");
case DTYPE_LINUXTFD:
return ("ltimer");
+ case DTYPE_DOOR:
+ return ("door");
default:
return ("unkn");
}
diff --git a/sys/kern/kern_door.c b/sys/kern/kern_door.c
new file mode 100644
--- /dev/null
+++ b/sys/kern/kern_door.c
@@ -0,0 +1,2034 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/buf.h>
+#include <sys/door.h>
+#include <sys/door_extern.h>
+#include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/filedesc.h>
+#include <sys/limits.h>
+#include <sys/mman.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/rwlock.h>
+#include <sys/sched.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/sysent.h>
+#include <sys/sysproto.h>
+#include <sys/vnode.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_map.h>
+#include <vm/vm_object.h>
+#include <vm/vm_page.h>
+#include <vm/vm_param.h>
+
+MALLOC_DECLARE(M_DOOR);
+
+MALLOC_DEFINE(M_DOOR, "door", "Solaris Doors IPC kernel descriptor");
+MALLOC_DEFINE(M_DOOR_INFO, "door_info", "Solaris Doors IPC thread info");
+
+#define DOOR_DATA_COPY_THRESHOLD (PAGE_SIZE * 4)
+#define DOOR_RESULT_MAX_SIZE (PAGE_SIZE * 4)
+#define DOOR_MAX_DATA_SIZE (INT_MAX)
+
+FEATURE(doors, "Solaris Doors IPC support");
+
+static u_int __cur_id = 0;
+
+static int
+fd_to_door(struct thread *td, int fd, struct door **d)
+{
+ struct file *fp;
+
+ int error = fget(td, fd, &cap_read_rights, &fp);
+ if (error) {
+ return error;
+ }
+
+ if (fp->f_type == DTYPE_VNODE) {
+
+ struct vnode *vp = fp->f_vnode;
+ if (vp && vp->v_type == VDOOR) {
+ KASSERT(
+ vp->v_door_vnode_info->v_door != NULL, ("Door not set"));
+ *d = vp->v_door_vnode_info->v_door;
+ }
+ } else if (fp->f_type == DTYPE_DOOR) { /* The server process passed its
+ * internal door descriptor */
+ *d = (struct door *)fp->f_data;
+ } else {
+ fdrop(fp, td);
+ return (EBADF);
+ }
+
+ fdrop(fp, td);
+ return 0;
+}
+
+static struct door_td_info *
+td_get_door_info(struct thread *td)
+{
+
+ if (!td->td_door) {
+ struct door_td_info *info = malloc(sizeof(struct door_td_info),
+ M_DOOR_INFO, M_NOWAIT | M_ZERO);
+ if (!info) {
+ return NULL;
+ }
+
+ td->td_door = info;
+
+ cv_init(&td->td_door->server.hold_cv, "door_server_cv");
+ cv_init(&td->td_door->client.hold_cv, "door_client_cv");
+ mtx_init(&td->td_door->lock, "door_info_lock", MTX_DEF, 0);
+ }
+
+ return td->td_door;
+}
+
+/*
+ * Door thread pool routines.
+ */
+
+static int
+door_pool_depleted(struct thread *td)
+{
+ int empty;
+ struct door *d = td->td_door->server.cur_door;
+
+ if (d && (d->attr & DOOR_PRIVATE)) {
+ DOOR_LOCK(d);
+ empty = SLIST_EMPTY(&d->priv_pool.td_pool);
+ DOOR_UNLOCK(d);
+ } else {
+ PROC_LOCK(td->td_proc);
+ empty = SLIST_EMPTY(&td->td_proc->door_td_pool.td_pool);
+ PROC_UNLOCK(td->td_proc);
+ }
+
+ return empty;
+}
+
+/*
+ * Tries to fetch a server thread from the corresponding pool. Blocks until a
+ * thread is available.
+ */
+static struct thread *
+door_fetch_server_thread(struct door *d)
+{
+ struct thread *ret = NULL;
+ int error;
+
+fetch:
+ if (d->attr & DOOR_PRIVATE) {
+ DOOR_LOCK_ASSERT(d, MA_OWNED);
+
+ ret = SLIST_FIRST(&d->priv_pool.td_pool);
+ if (ret) {
+ SLIST_REMOVE_HEAD(&d->priv_pool.td_pool, td_door_pool);
+ } else {
+ error = cv_wait_sig(
+ &d->priv_pool.pool_cv, &d->door_lock);
+ if (error == 0 && !DOOR_IS_REVOKED(d)) {
+ goto fetch;
+ } else {
+ return NULL;
+ }
+ }
+ } else {
+ /* Dropping door lock to avoid LOR */
+ DOOR_UNLOCK(d);
+ PROC_LOCK(d->proc);
+
+ ret = SLIST_FIRST(&d->proc->door_td_pool.td_pool);
+ if (ret) {
+ SLIST_REMOVE_HEAD(
+ &d->proc->door_td_pool.td_pool, td_door_pool);
+ } else {
+ error = cv_wait_sig(
+ &d->proc->door_td_pool.pool_cv, &d->proc->p_mtx);
+ PROC_UNLOCK(d->proc);
+ DOOR_LOCK(d);
+ if (error == 0 && !DOOR_IS_REVOKED(d)) {
+ goto fetch;
+ } else {
+ return NULL;
+ }
+ }
+
+ PROC_UNLOCK(d->proc);
+ DOOR_LOCK(d);
+ }
+ ret->td_door->server.queued = 0;
+ return ret;
+}
+
+/*
+ * Remove target thread from a corresponding thread pool.
+ */
+static void
+door_detach_server_thread(struct thread *td)
+{
+ struct door *d = td->td_door->server.cur_door;
+
+ if (d && (d->attr & DOOR_PRIVATE)) {
+ DOOR_LOCK(d);
+ if(!SLIST_EMPTY(&d->priv_pool.td_pool)){
+ SLIST_REMOVE(&d->priv_pool.td_pool, td, thread, td_door_pool);
+ }
+ DOOR_UNLOCK(d);
+ } else if (td->td_proc) {
+ PROC_LOCK(td->td_proc);
+ if(!SLIST_EMPTY(&td->td_proc->door_td_pool.td_pool)){
+ SLIST_REMOVE(&td->td_proc->door_td_pool.td_pool, td, thread,
+ td_door_pool);
+ }
+ PROC_UNLOCK(td->td_proc);
+ }
+ td->td_door->server.queued = 0;
+}
+
+/*
+ * Add a new server thread to the relevant thread pool.
+ * Notifies caller threads waiting on a door thread pool cv.
+ */
+static void
+door_add_server_thread(struct thread *td)
+{
+ struct door *d = td->td_door->server.cur_door;
+
+ if (d && (d->attr & DOOR_PRIVATE)) {
+ DOOR_LOCK(d);
+ SLIST_INSERT_HEAD(&d->priv_pool.td_pool, td, td_door_pool);
+
+ /* Wake one calling thread waiting for a server */
+ cv_signal(&d->priv_pool.pool_cv);
+ DOOR_UNLOCK(d);
+ } else {
+ PROC_LOCK(td->td_proc);
+ SLIST_INSERT_HEAD(
+ &td->td_proc->door_td_pool.td_pool, td, td_door_pool);
+ cv_signal(&td->td_proc->door_td_pool.pool_cv);
+ PROC_UNLOCK(td->td_proc);
+ }
+
+ td->td_door->server.queued = 1;
+}
+
+extern struct vop_vector door_vnops;
+/*
+ * kern_door_create:
+ *
+ * Door creation routine. Upon successful initialization,
+ * the created door structure is linked to a file structure and
+ * inserted into the current process door pool.
+ * Returns a file descriptor representing the door.
+ *
+ */
+static int
+kern_door_create(struct thread *td, void *procedure, void *udata, long attr)
+{
+ struct file *fp;
+ struct filedescent *fde;
+ struct filedesc *fdp;
+
+ int ret_fd;
+ int error;
+
+ if ((attr & ~DOOR_CREATE_MASK) ||
+ ((attr & (DOOR_UNREF | DOOR_UNREF_MULTI)) ==
+ (DOOR_UNREF | DOOR_UNREF_MULTI)))
+ return (EINVAL);
+
+ struct door *new_door = (struct door *)malloc(
+ sizeof(struct door), M_DOOR, M_ZERO | M_WAITOK);
+ if (new_door == NULL) {
+ return -(ENOMEM);
+ }
+
+ error = falloc_caps(td, &fp, &ret_fd, FREAD, NULL);
+ if (error) {
+ free(new_door, M_DOOR);
+ return -(error);
+ }
+ finit(fp, FNONBLOCK, DTYPE_DOOR, new_door, &vnops);
+
+ fdp = td->td_proc->p_fd;
+
+ FILEDESC_XLOCK(fdp);
+ fde = fdeget_locked(fdp, ret_fd);
+ if (fde != NULL) {
+ fde->fde_flags = (fde->fde_flags & ~UF_EXCLOSE) | (UF_EXCLOSE);
+ }
+ FILEDESC_XUNLOCK(fdp);
+
+ fp->f_data = (void *)new_door;
+
+ new_door->procedure = procedure;
+ new_door->udata = udata;
+ new_door->attr = attr;
+ new_door->proc = td->td_proc;
+ new_door->id = __cur_id++;
+ new_door->desc_max = (attr & DOOR_REFUSE_DESC) ? 0 : INT_MAX;
+ new_door->data_max = DOOR_MAX_DATA_SIZE;
+
+ new_door->refcount = 1;
+ fp->f_count = 1;
+
+ SLIST_INIT(&new_door->priv_pool.td_pool);
+ cv_init(&new_door->priv_pool.pool_cv, "door_pool_cv");
+
+ cv_init(&new_door->unref_cv, "door_unref_cv");
+ cv_init(&new_door->close_cv, "door_close_cv");
+
+ getnewvnode("doorfs", NULL, &door_vnops, &new_door->door_vnode);
+
+ new_door->attached_vnode_info = (struct door_vnode_info *)malloc(
+ sizeof(struct door_vnode_info), M_TEMP, M_WAITOK);
+
+ struct door_vnode_info *v_info = (struct door_vnode_info *)malloc(
+ sizeof(struct door_vnode_info), M_TEMP, M_WAITOK);
+
+ v_info->v_door = new_door;
+ v_info->v_prev_vnops = NULL;
+
+ new_door->door_vnode->v_type = VDOOR;
+ new_door->door_vnode->v_door_vnode_info = v_info;
+ fp->f_vnode = new_door->door_vnode;
+
+ mtx_init(&new_door->door_lock, "door_lock", MTX_DEF, 0);
+
+ PROC_LOCK(td->td_proc);
+ LIST_INSERT_HEAD(&td->td_proc->p_doors, new_door, entries);
+ PROC_UNLOCK(td->td_proc);
+
+ return ret_fd;
+}
+
+/*
+ * Utility routines for common door-related vmspace operations.
+ */
+
+/*
+ * Attempt to fetch 'count' pages from map 'm'.
+ * Resulting pages are wired and zeroed if a page had to be allocated.
+ */
+static int
+door_pages_lookup(vm_map_t m, vm_offset_t start, vm_page_t *pgs, int count)
+{
+ vm_object_t obj;
+ vm_pindex_t pindex;
+ vm_map_entry_t entry;
+ vm_prot_t prot;
+ boolean_t wired;
+
+ int error = vm_map_lookup(
+ &m, start, VM_PROT_RW, &entry, &obj, &pindex, &prot, &wired);
+ if (error != KERN_SUCCESS) {
+ return (vm_mmap_to_errno(error));
+ }
+ vm_map_lookup_done(m, entry);
+
+ VM_OBJECT_WLOCK(obj);
+
+ int grabbed = vm_page_grab_pages(
+ obj, pindex, VM_ALLOC_ZERO | VM_ALLOC_WIRED, pgs, count);
+ if (grabbed < count) {
+ VM_OBJECT_WUNLOCK(obj);
+ return (EFAULT);
+ }
+
+ VM_OBJECT_WUNLOCK(obj);
+
+ return 0;
+}
+
+static inline void
+door_unbusy_pages(vm_page_t *pgs, int count)
+{
+
+ for (int i = 0; i < count; i++) {
+ vm_page_xunbusy(pgs[i]);
+ }
+
+ return;
+}
+
+/*
+ * Copy 'src_size' bytes from 'src_page' + 'src_offset' from
+ * the source process to 'dst_page' + 'src_offset' in the target process's address space.
+ * Used for shared mapping argument transfers.
+ */
+static int
+door_copy_unaligned_page(vm_map_t src_map, vm_offset_t src_page,
+ vm_offset_t src_offset, size_t src_size, vm_map_t dst_map,
+ vm_offset_t dst_page)
+{
+ vm_map_entry_t _entry;
+ vm_prot_t _prot;
+ boolean_t _wired;
+
+ vm_object_t src_obj;
+ vm_pindex_t src_pindex;
+ vm_object_t dst_obj;
+ vm_pindex_t dst_pindex;
+
+ vm_page_t pgs[2] = { 0 };
+
+ int error = vm_map_lookup(&dst_map, dst_page, VM_PROT_READ, &_entry,
+ &dst_obj, &dst_pindex, &_prot, &_wired);
+ if (error != KERN_SUCCESS) {
+ return (vm_mmap_to_errno(error));
+ }
+ vm_map_lookup_done(dst_map, _entry);
+
+ VM_OBJECT_WLOCK(dst_obj);
+ vm_page_grab_valid(&pgs[0], dst_obj, dst_pindex, 0);
+
+ VM_OBJECT_WUNLOCK(dst_obj);
+
+ pmap_zero_page(pgs[0]);
+
+ error = vm_map_lookup(&src_map, src_page, VM_PROT_RW, &_entry, &src_obj,
+ &src_pindex, &_prot, &_wired);
+ if (error) {
+ vm_page_xunbusy(pgs[0]);
+ return (vm_mmap_to_errno(error));
+ }
+ vm_map_lookup_done(src_map, _entry);
+
+ VM_OBJECT_WLOCK(src_obj);
+ /*
+ * We have not specified VM_ALLOC_NOCREAT because a client might
+ * leave some argument pages zeroed
+ */
+ vm_page_grab_valid(&pgs[1], src_obj, src_pindex, 0);
+ if (!pgs[1]) {
+ VM_OBJECT_WUNLOCK(src_obj);
+ vm_page_xunbusy(pgs[0]);
+ return (EFAULT);
+ }
+
+ VM_OBJECT_WUNLOCK(src_obj);
+
+ door_unbusy_pages(pgs, 2);
+
+ vm_offset_t kaddr = kva_alloc(2 * PAGE_SIZE);
+ if (!kaddr) {
+ return ENOMEM;
+ }
+ pmap_qenter(kaddr, &pgs[0], 2);
+ bcopy((void *)(kaddr + PAGE_SIZE + src_offset),
+ (void *)(kaddr + src_offset), src_size);
+ pmap_qremove(kaddr, 2);
+
+ kva_free(kaddr, 2 * PAGE_SIZE);
+
+ return 0;
+}
+
+/*
+ * Create a shared mapping of a source buffer in the target process.
+ * If the buffer is unaligned the contents of the buffer stored on
+ * unaligned pages (the first and/or the last page) are copied to the target process.
+ * to prevent information leaks.
+ */
+static __noinline int
+door_vm_map_shared(vm_map_t src_map, vm_map_t dst_map, void *src_buf,
+ size_t src_buf_size, vm_prot_t prot, vm_offset_t *dst_addr)
+{
+
+ vm_object_t src_obj;
+ vm_pindex_t src_pindex;
+ vm_map_entry_t entry;
+ vm_prot_t _prot;
+ boolean_t _wired;
+
+ vm_offset_t src_end_addr = (vm_offset_t)src_buf + src_buf_size;
+ vm_offset_t dst_start_addr = 0;
+ vm_offset_t dst_argp = 0;
+
+ vm_offset_t buf_offset = ((vm_offset_t)src_buf & PAGE_MASK);
+ vm_offset_t buf_end_offset = (src_end_addr & PAGE_MASK);
+
+ size_t total_mapping_size = round_page(src_buf_size) +
+ ((buf_offset != 0) * PAGE_SIZE);
+
+ int error = vm_map_lookup(&src_map, trunc_page((vm_offset_t)src_buf),
+ prot, &entry, &src_obj, &src_pindex, &_prot, &_wired);
+ if (error) {
+ return (vm_mmap_to_errno(error));
+ }
+ vm_map_lookup_done(src_map, entry);
+
+ vm_map_lock(dst_map);
+ dst_start_addr = vm_map_findspace(
+ dst_map, trunc_page(src_buf), total_mapping_size);
+ if (dst_start_addr == (vm_map_max(dst_map) - src_buf_size + 1)) {
+ return (ENOMEM);
+ }
+ vm_map_unlock(dst_map);
+
+ dst_argp = dst_start_addr + buf_offset;
+
+ /*
+ * Explicitly copy unaligned buffer contents to prevent address space
+ * leaks
+ */
+
+ /* Check if src_buffer is aligned */
+ if (buf_offset) {
+ vm_map_lock(dst_map);
+
+ error = vm_map_insert(dst_map, NULL, 0, dst_start_addr,
+ dst_start_addr + PAGE_SIZE, prot, prot, 0);
+ if (error) {
+ vm_map_unlock(dst_map);
+
+ return (vm_mmap_to_errno(error));
+ }
+ vm_map_unlock(dst_map);
+
+ error = door_copy_unaligned_page(src_map, trunc_page(src_buf),
+ buf_offset, (size_t)(PAGE_SIZE - buf_offset), dst_map,
+ dst_start_addr);
+ if (error) {
+ vm_map_remove(dst_map, dst_start_addr,
+ dst_start_addr + PAGE_SIZE);
+ return error;
+ }
+
+ dst_start_addr += PAGE_SIZE;
+ total_mapping_size -= PAGE_SIZE;
+ src_buf_size -= (PAGE_SIZE - buf_offset);
+ src_pindex++;
+ }
+
+ /* Check whether the buffer spans across the whole last page */
+ if (buf_end_offset) {
+ vm_offset_t dst_last_page_addr = trunc_page(
+ dst_start_addr + src_buf_size);
+
+ vm_map_lock(dst_map);
+
+ error = vm_map_insert(dst_map, NULL, 0, dst_last_page_addr,
+ dst_last_page_addr + PAGE_SIZE, prot, prot, 0);
+ if (error) {
+ vm_map_unlock(dst_map);
+ return (vm_mmap_to_errno(error));
+ }
+ vm_map_unlock(dst_map);
+
+ error = door_copy_unaligned_page(src_map,
+ trunc_page(src_end_addr), 0, buf_end_offset, dst_map,
+ dst_last_page_addr);
+ if (error) {
+ vm_map_remove(dst_map, dst_last_page_addr,
+ dst_last_page_addr + PAGE_SIZE);
+ return error;
+ }
+
+ total_mapping_size -= PAGE_SIZE;
+ }
+
+ vm_object_reference(src_obj);
+
+ error = vm_map_find(dst_map, src_obj, IDX_TO_OFF(src_pindex),
+ &dst_start_addr, total_mapping_size, 0, VMFS_NO_SPACE,
+ VM_PROT_READ, VM_PROT_READ, 0);
+ if (error != KERN_SUCCESS) {
+ return (vm_mmap_to_errno(error));
+ }
+
+ *dst_addr = dst_argp;
+ error = 0;
+
+ return (error);
+}
+
+/*
+ * Attempts to close previously opened file descriptors.
+ * This is used when a file descriptors are passed to server threads
+ * as arguments and when dealing with file descriptor arguments marked
+ * with DOOR_RELEASE.
+ */
+static int
+door_fd_close(struct thread *td, door_desc_t *descs, size_t n_desc)
+{
+ for (u_int i = 0; i < n_desc; i++) {
+ struct file *fp;
+ int fd = descs[i].d_data.d_desc.d_descriptor;
+
+ int error = fget(td, fd, &cap_read_rights, &fp);
+ if (error) {
+ return error;
+ }
+
+ fdclose(td, fp, fd);
+ }
+
+ return 0;
+}
+
+/*
+ * Attempts to map an address range from a process to kernelspace.
+ */
+static int
+door_mapin(vm_map_t src_map, vm_offset_t src, size_t size, vm_page_t **pgs,
+ int *n_pgs, vm_offset_t *kaddr)
+{
+
+ vm_offset_t kern_addr;
+
+ int src_pgs_count = (round_page(size) +
+ (((size_t)src & PAGE_MASK) ? PAGE_SIZE : 0)) /
+ PAGE_SIZE;
+ size_t mapping_size = src_pgs_count * PAGE_SIZE;
+
+ vm_page_t *src_pgs = (vm_page_t *)malloc(
+ sizeof(vm_page_t *) * src_pgs_count, M_TEMP, M_NOWAIT);
+ if (!src_pgs) {
+ return ENOMEM;
+ }
+
+ kern_addr = kva_alloc(mapping_size);
+ if (!kern_addr) {
+ free(src_pgs, M_TEMP);
+ return (ENOMEM);
+ }
+
+ int error = door_pages_lookup(
+ src_map, trunc_page(src), src_pgs, src_pgs_count);
+ if (error) {
+ kva_free(kern_addr, mapping_size);
+ free(src_pgs, M_TEMP);
+ return error;
+ }
+
+ pmap_qenter(kern_addr, src_pgs, src_pgs_count);
+
+ *pgs = src_pgs;
+ *n_pgs = src_pgs_count;
+ *kaddr = kern_addr;
+
+ return 0;
+}
+
+/*
+ * Attempts to unmap a previously mapped process address range
+ * from the kernel map.
+ */
+static void
+door_mapout(vm_offset_t kaddr, vm_page_t *pgs, size_t n_pgs)
+{
+ pmap_qremove(kaddr, n_pgs);
+ kva_free(kaddr, n_pgs * PAGE_SIZE);
+ door_unbusy_pages(pgs, n_pgs);
+
+ free(pgs, M_TEMP);
+}
+
+/*
+ * Copies target pages from a source process.
+ */
+static int
+door_copy(vm_offset_t src, vm_map_t dst_map, vm_offset_t dst, size_t size)
+{
+ vm_offset_t kaddr;
+ vm_page_t *pgs;
+ int n_pages;
+
+ int error = door_mapin(dst_map, dst, size, &pgs, &n_pages, &kaddr);
+ if (error) {
+ return error;
+ }
+
+ error = copyin((void *)src, (void *)(kaddr + (dst & PAGE_MASK)), size);
+ if (error) {
+ door_mapout(kaddr, pgs, n_pages);
+ return (error);
+ }
+
+ door_mapout(kaddr, pgs, n_pages);
+
+ return 0;
+}
+
+/*
+ * Open files specified by source process descriptors in the
+ * current process and store new fds.
+ * Used for transferring file descriptor arguments and results.
+ */
+static int
+door_desc_translate(struct door *d, struct thread *src_td,
+ struct thread *dst_td, door_desc_t *udesc_p, size_t n_desc, vm_offset_t dst,
+ door_desc_t *translated_descs)
+{
+ vm_offset_t dst_kern_addr;
+ vm_page_t *pgs = NULL;
+ int n_pgs;
+
+ int error = door_mapin(&dst_td->td_proc->p_vmspace->vm_map, dst,
+ n_desc * sizeof(door_desc_t), &pgs, &n_pgs, &dst_kern_addr);
+ if (error) {
+ return error;
+ }
+
+ struct filedesc *fdp = src_td->td_proc->p_fd;
+ struct filedescent *fde;
+
+ door_desc_t *dst_descs = (door_desc_t *)dst_kern_addr;
+ u_int n_dst_descs = 0;
+ door_desc_t *udescs = (door_desc_t *)malloc(
+ sizeof(door_desc_t) * n_desc, M_TEMP, M_NOWAIT);
+ if (!udescs) {
+ door_mapout(dst_kern_addr, pgs, n_pgs);
+ return (ENOMEM);
+ }
+
+ error = copyin(
+ (void *)udesc_p, (void *)(udescs), n_desc * sizeof(door_desc_t));
+ if (error) {
+ error = (EINVAL);
+ goto err;
+ }
+
+ for (u_int i = 0; i < n_desc; i++) {
+
+ struct file *fp;
+
+ door_desc_t *src_desc = &udescs[i];
+ door_desc_t *dst_desc = &dst_descs[i];
+
+ int dst_fd;
+ int dst_fd_flags = O_CLOEXEC;
+ int src_fd = src_desc->d_data.d_desc.d_descriptor;
+
+ if (src_desc->d_attributes & DOOR_DESCRIPTOR) {
+
+ error = fget(src_td, src_fd, &cap_read_rights, &fp);
+ if (error) {
+ goto err;
+ }
+
+ /*
+ * Close source fd in src_td if DOOR_RELEASE was
+ * passed
+ */
+ if (src_desc->d_attributes & DOOR_RELEASE) {
+ fdclose(src_td, fp, src_fd);
+ }
+
+ FILEDESC_XLOCK(fdp);
+ fde = fdeget_locked(fdp, src_fd);
+ if (fde != NULL) {
+ dst_fd_flags |=
+ fde->fde_flags; /* Share descriptor
+ * flags */
+ }
+ FILEDESC_XUNLOCK(fdp);
+
+ error = finstall(
+ dst_td, fp, &dst_fd, dst_fd_flags, NULL);
+ if (error) {
+ goto err;
+ }
+
+ dst_desc->d_attributes = DOOR_DESCRIPTOR |
+ (d->attr & DOOR_ATTR_MASK);
+ dst_desc->d_data.d_desc.d_descriptor = dst_fd;
+ dst_desc->d_data.d_desc.d_id = d->id;
+
+ /* Store translated descriptor info in upcall info */
+ if (translated_descs) {
+ translated_descs[i] = *dst_desc;
+ }
+ n_dst_descs++;
+ }
+ }
+
+ door_mapout(dst_kern_addr, pgs, n_pgs);
+ free(udescs, M_TEMP);
+
+ return 0;
+
+err:
+ if (n_dst_descs) {
+ error = door_fd_close(dst_td, dst_descs, n_dst_descs);
+ if (error) {
+ door_mapout(dst_kern_addr, pgs, n_pgs);
+ free(udescs, M_TEMP);
+ return error;
+ }
+ }
+ door_mapout(dst_kern_addr, pgs, n_pgs);
+ free(udescs, M_TEMP);
+ return error;
+}
+
+/*
+ * Transfers arguments from a calling thread to the server thread.
+ *
+ * Arguments are either copied when smaller than DOOR_DATA_THRESHOLD or
+ * transferred by creating a shared mapping for the server process.
+ * This routine checks target door parameters (e.g. max/min arg size)
+ * and returns an error if a parameter check is failed.
+ */
+static int
+door_transfer_args(struct thread *server_td, struct thread *client_td,
+ door_arg_t *args, struct door *d)
+{
+
+ char *dst_argp = NULL;
+ size_t dst_data_size = 0;
+ door_desc_t *dst_dp = NULL;
+ u_int dst_n_desc = 0;
+ int error;
+
+ vm_map_t st_map;
+ vm_map_t client_map;
+
+ size_t total_arg_size = args->data_ptr ? args->data_size : 0;
+ total_arg_size += (args->desc_ptr ?
+ (args->desc_num * sizeof(door_desc_t)) :
+ 0);
+
+ struct door_upcall_info *dst_upcall =
+ &server_td->td_door->client.upcall;
+
+ dst_upcall->translated_desc_ptr = NULL;
+ dst_upcall->translated_desc_num = 0;
+
+ if (!total_arg_size) {
+ goto upcall_fill;
+ }
+
+ DOOR_LOCK(d);
+ if (total_arg_size > d->data_max || total_arg_size < d->data_min) {
+ DOOR_UNLOCK(d);
+ return (ENOBUFS);
+ }
+
+ if (args->desc_num > d->desc_max) {
+ DOOR_UNLOCK(d);
+ return (ENFILE);
+ }
+
+ DOOR_UNLOCK(d);
+
+ st_map = &server_td->td_proc->p_vmspace->vm_map;
+ client_map = &client_td->td_proc->p_vmspace->vm_map;
+
+ if (total_arg_size <= DOOR_DATA_COPY_THRESHOLD) {
+ vm_offset_t dst_arg_addr = 0;
+
+ error = vm_mmap(st_map, &dst_arg_addr, total_arg_size,
+ VM_PROT_RW, VM_PROT_RW, MAP_PRIVATE, OBJT_DEFAULT, NULL, 0);
+ if (error) {
+ error = (vm_mmap_to_errno(error));
+ goto err;
+ }
+
+ if (args->data_ptr) {
+ error = door_copy((vm_offset_t)args->data_ptr, st_map,
+ dst_arg_addr, args->data_size);
+ if (error) {
+ vm_map_remove(st_map, dst_arg_addr,
+ dst_arg_addr + total_arg_size);
+ goto err;
+ }
+
+ dst_argp = (char *)dst_arg_addr;
+ dst_data_size = args->data_size;
+ }
+
+ /* Translate and copy descriptors */
+ if (args->desc_ptr) {
+ vm_offset_t u_dst_descs_addr = dst_arg_addr +
+ dst_data_size;
+
+ door_desc_t *translated_descs = (door_desc_t *)malloc(
+ args->desc_num * sizeof(door_desc_t), M_TEMP,
+ M_NOWAIT);
+ if (!translated_descs) {
+ return (ENOMEM);
+ }
+
+ error = door_desc_translate(d, client_td, server_td,
+ args->desc_ptr, args->desc_num, u_dst_descs_addr,
+ translated_descs);
+ if (error) {
+ vm_map_remove(st_map, dst_arg_addr,
+ dst_arg_addr + total_arg_size);
+ free(translated_descs, M_TEMP);
+ return error;
+ }
+
+ dst_dp = (door_desc_t *)u_dst_descs_addr;
+ dst_n_desc = args->desc_num;
+
+ dst_upcall->translated_desc_ptr = translated_descs;
+ dst_upcall->translated_desc_num = dst_n_desc;
+ }
+ } else {
+ vm_offset_t st_region_start;
+
+ error = door_vm_map_shared(client_map, st_map, args->data_ptr,
+ args->data_size, VM_PROT_RW, &st_region_start);
+ if (error) {
+ goto err;
+ }
+
+ dst_argp = (char *)st_region_start;
+ dst_data_size = args->data_size;
+ }
+
+upcall_fill:
+ dst_upcall->args.data_ptr = dst_argp;
+ dst_upcall->args.data_size = dst_data_size;
+ dst_upcall->args.desc_ptr = dst_dp;
+ dst_upcall->args.desc_num = dst_n_desc;
+
+ server_td->td_door->client.upcall.active = 1;
+
+ return 0;
+
+err:
+ return error;
+}
+
+
+/*
+ * kern_door_call:
+ *
+ * Invoke a door procedure attached to door 'd'.
+ *
+ * Tries to fetch a server thread from a door thread pool,
+ * transfer arguments to the server address space and wake the
+ * previously selected server thread.
+ * Blocks until a server thread is available.
+ */
+static int __noinline
+kern_door_call(struct thread *td, struct door *d, door_arg_t *uargs)
+{
+ struct thread *server_thread = door_fetch_server_thread(d);
+ struct door_td_info *d_td_info;
+ struct door_client_info *td_client;
+ struct door_server_info *td_server;
+ int error;
+
+ if (server_thread == NULL) {
+ DOOR_UNLOCK(d);
+ return (EAGAIN);
+ }
+
+ DOOR_UNLOCK(d);
+
+ d_td_info = server_thread->td_door;
+ KASSERT(d_td_info, ("Thread has no door info attached"));
+
+ DOOR_INFO_LOCK(d_td_info);
+
+ td_client = &d_td_info->client;
+ td_client->td_caller = td;
+
+ /* Attach door to server thread */
+ td_server = &d_td_info->server;
+ td_server->cur_door = d;
+
+ if (uargs) {
+ door_arg_t *args = &td_client->uargs;
+
+ error = copyin((void *)uargs, (void *)args, sizeof(door_arg_t));
+ if (error) {
+ DOOR_INFO_UNLOCK(d_td_info);
+ goto err;
+ }
+
+ if ((d->attr & DOOR_REFUSE_DESC) && (args->desc_ptr)) {
+ error = EOPNOTSUPP;
+ DOOR_INFO_UNLOCK(d_td_info);
+ goto err;
+ }
+
+ if (args->rbuf && (args->rsize >= DOOR_RESULT_MAX_SIZE)) {
+ error = E2BIG;
+ DOOR_INFO_UNLOCK(d_td_info);
+ goto err;
+ }
+
+ DOOR_TD_HOLD(td_server);
+ DOOR_INFO_UNLOCK(d_td_info);
+
+ error = door_transfer_args(server_thread, td, args, d);
+ if (error) {
+ goto err;
+ }
+
+ DOOR_INFO_LOCK(d_td_info);
+ td_client->uargs_addr = (void *)uargs;
+ DOOR_TD_RELEASE(td_server);
+ }
+
+ DOOR_LOCK(d);
+ d->active_invocations++;
+ DOOR_UNLOCK(d);
+
+ wakeup_one((void *)server_thread);
+
+sleep:
+ error = mtx_sleep((void *)td, &d_td_info->lock, PCATCH, "drcall", 0);
+
+ /* Check if we have been interrupted */
+ if (error) {
+ td_client->td_caller = NULL;
+ DOOR_INFO_UNLOCK(d_td_info);
+
+ DOOR_LOCK(d);
+ d->active_invocations--;
+ if (d->active_invocations <= 0) {
+ cv_broadcast(&d->close_cv);
+ }
+ DOOR_UNLOCK(d);
+
+ return (error);
+ }
+
+ if (td_client->error != 0) {
+ td_client->td_caller = NULL;
+
+ if (td_client->error == DOOR_EXIT) {
+ DOOR_INFO_UNLOCK(d_td_info);
+ /* Server thread is exiting, free door info */
+ cv_destroy(&d_td_info->server.hold_cv);
+ cv_destroy(&d_td_info->client.hold_cv);
+
+ free(d_td_info, M_DOOR_INFO);
+
+ DOOR_LOCK(d);
+ d->active_invocations--;
+ if (d->active_invocations <= 0) {
+ cv_broadcast(&d->close_cv);
+ }
+ DOOR_UNLOCK(d);
+
+ return (EINTR);
+ } else if (td_client->error == DOOR_WAIT) {
+ td_client->error = 0;
+ goto sleep;
+ }
+ }
+
+ while (DOOR_TD_HELD(td_client)) {
+ cv_wait(&td_client->hold_cv, &d->door_lock);
+ }
+ /* Write any changes to client args */
+ if (td_client->uargs_changed) {
+ error = copyout(&td_client->uargs, td_client->uargs_addr,
+ sizeof(door_arg_t));
+ if (error) {
+ return error;
+ }
+ }
+
+ if (td_client->uargs_addr) {
+ td_client->uargs_addr = 0;
+ }
+
+ DOOR_INFO_UNLOCK(d_td_info);
+
+ DOOR_LOCK(d);
+ d->active_invocations--;
+ if (d->active_invocations <= 0) {
+ cv_broadcast(&d->close_cv);
+ }
+
+ DOOR_UNLOCK(d);
+
+ return 0;
+
+err:
+ td_client->td_caller = NULL;
+ return error;
+}
+
+/*
+ * Transfer results from a server thread to the calling thread.
+ */
+static int
+door_transfer_results(char *rbuf, size_t rbuf_size, door_desc_t *descs,
+ size_t n_desc, struct thread *server_td, struct thread *client_td,
+ struct door *d)
+{
+ int error;
+
+ vm_offset_t rbuf_mapped_addr = 0;
+ vm_offset_t desc_mapped_addr = 0;
+
+ size_t rbuf_mapped_size = 0;
+ size_t desc_mapped_size = 0;
+
+ struct door_client_info *client = &server_td->td_door->client;
+ vm_map_t client_map = &client_td->td_proc->p_vmspace->vm_map;
+
+ if (rbuf) {
+ char *client_rbuf = client->uargs.rbuf;
+ size_t client_rbuf_size = client->uargs.rsize;
+
+ if (client_rbuf) {
+ if (client_rbuf_size < rbuf_size) {
+ return (E2BIG);
+ }
+ } else {
+ /* Allocate a result region for the client */
+ /* This needs to be munmaped later */
+ rbuf_mapped_size = round_page(rbuf_size);
+
+ error = vm_mmap(client_map, &rbuf_mapped_addr,
+ rbuf_mapped_size, VM_PROT_RW, VM_PROT_RW,
+ MAP_PRIVATE, OBJT_DEFAULT, NULL, 0);
+ if (error) {
+ return (vm_mmap_to_errno(error));
+ }
+
+ client->uargs.rbuf = client_rbuf = (char *)
+ rbuf_mapped_addr;
+ client->uargs.rsize = client_rbuf_size = rbuf_size;
+
+ client->uargs_changed = 1;
+ }
+
+ error = door_copy((vm_offset_t)rbuf, client_map,
+ (vm_offset_t)client_rbuf, rbuf_size);
+ if (error) {
+ goto err;
+ }
+ }
+ if (descs) {
+ desc_mapped_size = round_page(n_desc * sizeof(door_desc_t));
+
+ error = vm_mmap(client_map, &desc_mapped_addr, desc_mapped_size,
+ VM_PROT_RW, VM_PROT_RW, MAP_PRIVATE, OBJT_DEFAULT, NULL, 0);
+ if (error) {
+ error = (vm_mmap_to_errno(error));
+ goto err;
+ }
+
+ error = door_desc_translate(d, server_td, client_td, descs,
+ n_desc, desc_mapped_addr, NULL);
+ if (error) {
+ goto err;
+ }
+
+ client->uargs.desc_ptr = (door_desc_t *)desc_mapped_addr;
+ client->uargs.desc_num = n_desc;
+
+ client->uargs_changed = 1;
+ }
+
+ return 0;
+
+err:
+ if (rbuf_mapped_addr) {
+ vm_map_remove(client_map, rbuf_mapped_addr,
+ rbuf_mapped_addr + rbuf_mapped_size);
+ }
+ if (desc_mapped_addr) {
+ vm_map_remove(client_map, desc_mapped_addr,
+ desc_mapped_addr + desc_mapped_size);
+ }
+
+ return error;
+}
+
+/*
+ * Remove a previously created shared mapping.
+ */
+static int
+door_vm_unmap(vm_map_t dst_map, struct door_client_info *client)
+{
+
+ struct door_upcall_args *args = &client->upcall.args;
+
+ if (args->data_ptr) {
+ int error = vm_map_remove(dst_map, trunc_page(args->data_ptr),
+ trunc_page(args->data_ptr + args->data_size));
+ if (error) {
+ return error;
+ }
+ }
+
+ if (args->desc_ptr) {
+ int error = vm_map_remove(dst_map, trunc_page(args->desc_ptr),
+ trunc_page(args->desc_ptr +
+ (args->desc_num * sizeof(door_desc_t))));
+ if (error) {
+ return error;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Setup door procedure execution by modifying target thread frame.
+ * Called when a server thread is activated, right before returning
+ * from kern_door_return.
+ * Procedure arguments are set via the machine-dependent
+ * 'door_set_args' routine.
+ */
+static int
+door_set_upcall(struct thread *td, struct door_td_info *d_info,
+ struct door_upcall_info *upcall)
+{
+
+ struct door_upcall_args *args = &upcall->args;
+ struct door *d = d_info->server.cur_door;
+
+ cpu_set_upcall(
+ td, (void (*)(void *))d->procedure, NULL, &d_info->server.stack);
+ door_set_args(td, d->udata, args->data_ptr, args->data_size,
+ args->desc_ptr, args->desc_num);
+
+ return 0;
+}
+
+/*
+ * Houses a single unref thread.
+ */
+static int
+door_unref(struct thread *td, struct door *d)
+{
+ DOOR_LOCK_ASSERT(d, MA_OWNED);
+
+ if ((d->attr & (DOOR_UNREF | DOOR_UNREF_MULTI))) {
+
+ if (d->unrefcount == 0) {
+
+ int error = mtx_sleep(
+ (void *)td, &d->door_lock, PCATCH, "drunrf", 0);
+ if (error) { /* Check for interrupts or exit() */
+ d->unref_td = NULL;
+ DOOR_UNLOCK(d);
+
+ struct door_td_info *info = td->td_door;
+
+ thread_lock(td);
+ td->td_door = NULL;
+ thread_unlock(td);
+
+ free(info, M_DOOR_INFO);
+ return (EINTR);
+ }
+ }
+
+ cpu_set_upcall(td, (void (*)(void *))d->procedure, NULL,
+ &td->td_door->server.stack);
+ door_set_args(td, d->udata, DOOR_UNREF_DATA, 0, 0, 0);
+
+ if (d->attr & DOOR_UNREF) {
+ d->attr &= ~DOOR_UNREF;
+ }
+
+ d->attr |= DOOR_UNREF_ACTIVE;
+ d->unrefcount--;
+ } else {
+ d->unref_td = NULL;
+ }
+
+ DOOR_UNLOCK(d);
+
+ return (0);
+}
+
+/*
+ * kern_door_return:
+ *
+ * Register a door server thread or return door procedure invocation results to the caller.
+ *
+ * A newly created door server thread registers itself by calling door_return where it is
+ * placed on the appropriate door thread pool and blocks until a caller fetches it from the pool.
+ *
+ * Upon returning from a successful door procedure invocation each door procedure must call door_return.
+ * This routine then copies any results to the client, places the thread on the appropriate thread pool and blocks once again
+ *
+ */
+static int __noinline
+kern_door_return(struct thread *td, char *result_ptr, size_t result_size,
+ door_desc_t *dp, long num_desc, void *stack_base, size_t stack_size)
+{
+ struct thread *server_thread = td;
+ struct proc *server_proc = td->td_proc;
+ int error = 0;
+
+ struct door_td_info *d_info = td_get_door_info(server_thread);
+ if (!d_info) {
+ return (ENOMEM);
+ }
+
+ DOOR_INFO_LOCK(d_info);
+ struct door_client_info *client = &d_info->client;
+ struct door_server_info *server = &d_info->server;
+
+ /*
+ * Check if a newly created server thread called door_return or
+ * door_bind
+ */
+ if (!server->cur_door ||
+ (server->private_pool && client->td_caller == NULL)) {
+ DOOR_INFO_UNLOCK(d_info);
+ /* Check stack mapping */
+ vm_map_lock(&server_proc->p_vmspace->vm_map);
+ // TODO: check protection
+ // if (!vm_map_check_protection(&server_proc->p_vmspace->vm_map,
+ //(vm_offset_t) stack_base, (vm_offset_t) stack_base +
+ // stack_size, VM_PROT_RW)) {
+ if (!vm_map_range_valid(&server_proc->p_vmspace->vm_map,
+ (vm_offset_t)stack_base,
+ (vm_offset_t)stack_base + stack_size)) {
+ server_thread->td_door = NULL;
+ vm_map_unlock(&server_proc->p_vmspace->vm_map);
+
+ free(d_info, M_DOOR_INFO);
+ return (EFAULT);
+ }
+ vm_map_unlock(&server_proc->p_vmspace->vm_map);
+
+ /* Assume that the thread stack info never changes */
+ server->stack.ss_sp = stack_base;
+ server->stack.ss_size = stack_size;
+
+ goto release;
+ }
+
+ struct door *d = d_info->server.cur_door;
+
+ DOOR_LOCK(d);
+ if (__predict_false(td == d->unref_td)) {
+
+ d->attr &= ~DOOR_UNREF_ACTIVE;
+ if (!(d->attr & (DOOR_UNREF | DOOR_UNREF_MULTI))) {
+ d->unref_td = NULL;
+ d->unrefcount--;
+
+ DOOR_UNLOCK(d);
+ DOOR_INFO_UNLOCK(d_info);
+
+ td->td_door = NULL;
+ free(d_info, M_DOOR_INFO);
+
+ return 0;
+ }
+ DOOR_INFO_UNLOCK(d_info);
+
+ return door_unref(td, d);
+ }
+ DOOR_UNLOCK(d);
+
+ /* Check if caller is still active */
+ if (client->upcall.active && client->td_caller == NULL) {
+ DOOR_INFO_UNLOCK(d_info);
+ if (d->attr & DOOR_NO_CANCEL) {
+ goto release;
+ } else {
+ goto out;
+ }
+ }
+
+ if (result_ptr || dp) {
+ DOOR_TD_HOLD(client);
+ DOOR_INFO_UNLOCK(d_info);
+
+ error = door_transfer_results(result_ptr, result_size, dp,
+ num_desc, td, client->td_caller, d);
+ if (error) {
+ DOOR_INFO_LOCK(d_info);
+ client->error = error;
+ DOOR_TD_RELEASE(client);
+ DOOR_INFO_UNLOCK(d_info);
+
+ goto out;
+ }
+
+ DOOR_INFO_LOCK(d_info);
+ client->error = 0;
+ DOOR_TD_RELEASE(client);
+ }
+
+ if (!server->private_pool) {
+ server->cur_door = NULL;
+ }
+
+ struct thread *td_caller = client->td_caller;
+
+ client->upcall.active = 0;
+ client->td_caller = NULL;
+ client->error = 0;
+ DOOR_INFO_UNLOCK(d_info);
+
+ wakeup_one((void *)td_caller);
+
+release:
+ /* Unmap transferred arguments */
+ door_vm_unmap(&server_proc->p_vmspace->vm_map, client);
+
+ /* Free any opened descriptors */
+ if (client->upcall.translated_desc_ptr) {
+ door_fd_close(td, client->upcall.translated_desc_ptr,
+ client->upcall.translated_desc_num);
+
+ free(client->upcall.translated_desc_ptr, M_TEMP);
+ client->upcall.translated_desc_ptr = NULL;
+ client->upcall.translated_desc_num = 0;
+ }
+
+ door_add_server_thread(server_thread);
+
+sleep:
+ DOOR_INFO_LOCK(d_info);
+ error = mtx_sleep(
+ (void *)server_thread, &d_info->lock, PCATCH, "drret", 0);
+
+ client = &d_info->client;
+
+ /* Handle wakeup */
+ if (error) {
+ if (client->td_caller) {
+ client->error = error;
+ DOOR_INFO_UNLOCK(d_info);
+ wakeup_one((void *)client->td_caller);
+ } else {
+ DOOR_INFO_UNLOCK(d_info);
+ }
+ goto out;
+ } else if (client->td_caller) {
+ /* Check if client is still copying args */
+ door_set_upcall(server_thread, d_info, &client->upcall);
+ client->error = DOOR_WAIT;
+ DOOR_INFO_UNLOCK(d_info);
+
+ } else {
+ goto sleep;
+ }
+
+ return 0;
+out:
+ door_slam(td);
+ return error;
+}
+
+/*
+* Attaches a door to a regular file. This is achieved by latching onto an existing file's vnode,
+* changing its type to VDOOR and its vnops to door_vnops which forwards certain ops to the original vnops vector.
+*/
+static int
+kern_door_attach(
+ struct thread *td, long fd, const char *path)
+{
+ struct nameidata nd;
+ struct vnode *door_vp;
+ struct file *door_file;
+ struct door *door;
+
+ int error = fget(td, fd, &cap_read_rights, &door_file);
+ if (error) {
+ return (error);
+ }
+ // dont increase reference count
+ fdrop(door_file, td);
+
+ if (door_file->f_type != DTYPE_DOOR) {
+ return (EINVAL);
+ }
+ door = door_file->f_data;
+
+ if (DOOR_IS_REVOKED(door)) {
+ return (ECANCELED);
+ }
+
+ struct door_vnode_info *v_info = door->attached_vnode_info;
+
+ NDPREINIT(&nd);
+ // restart:
+ NDINIT(&nd, LOOKUP,
+ FOLLOW | AUDITVNODE1, UIO_USERSPACE, path);
+ if ((error = namei(&nd)) != 0)
+ goto err;
+ if (nd.ni_vp != NULL) {
+ NDFREE(&nd, NDF_ONLY_PNBUF);
+
+ door_vp = nd.ni_vp;
+ VI_LOCK(door_vp);
+
+ /* Check if an existing door is already attached */
+ if (door_vp->v_type == VDOOR) {
+ VI_UNLOCK(door_vp);
+ error = (EEXIST);
+ goto err;
+ }
+
+ /* Only attach to "regular" files */
+ if (door_vp->v_type != VREG) {
+ VI_UNLOCK(door_vp);
+ error = (EINVAL);
+ goto err;
+ }
+
+ door_vp->v_type = VDOOR;
+
+ v_info->v_door = door;
+ v_info->v_prev_vnops = door_vp->v_op;
+
+ door_vp->v_door_vnode_info = v_info;
+ door_vp->v_op = &door_vnops;
+
+ VI_UNLOCK(door_vp);
+
+
+ DOOR_LOCK(door);
+ door->attached_vnode = door_vp;
+ DOOR_UNLOCK(door);
+
+ return (0);
+ } else {
+ error = (ENOENT);
+ }
+
+err:
+ free(v_info, M_TEMP);
+ return (error);
+}
+
+/*
+* Detaches a door from a previously attached file.
+*/
+static int
+kern_door_detach(struct thread *td, const char *path)
+{
+ struct nameidata nd;
+ struct vnode *door_vp;
+ struct door *door;
+ struct door_vnode_info *v_info;
+
+ int error;
+
+ NDPREINIT(&nd);
+ // restart:
+ NDINIT(&nd, LOOKUP,
+ FOLLOW | AUDITVNODE1, UIO_USERSPACE, path);
+ if ((error = namei(&nd)) != 0)
+ return (error);
+ if (nd.ni_vp != NULL) {
+ NDFREE(&nd, NDF_ONLY_PNBUF);
+
+ door_vp = nd.ni_vp;
+ VI_LOCK(door_vp);
+
+ /* Check vnode type */
+ if (door_vp->v_type != VDOOR) {
+ VI_UNLOCK(door_vp);
+ return (EINVAL);
+ }
+
+ v_info = door_vp->v_door_vnode_info;
+ door = v_info->v_door;
+
+ /* Restore previous vnode info */
+ door_vp->v_op = v_info->v_prev_vnops;
+ door_vp->v_type = VREG;
+
+ VI_UNLOCK(door_vp);
+
+
+ DOOR_LOCK(door);
+ door->attached_vnode = NULL;
+ DOOR_UNLOCK(door);
+
+ return (0);
+ } else {
+ error = (ENOENT);
+ }
+
+ return (error);
+}
+
+/*
+ * Cleans up all thread door-related resources. Called from
+ * thread_exit and kern_door_return when exiting due to an error.
+ */
+void
+door_slam(struct thread *td)
+{
+ struct door_td_info *d_info = td->td_door;
+ struct door *d = d_info->server.cur_door;
+
+ /* Check if there is an active client */
+ DOOR_INFO_LOCK(d_info);
+
+ if (door_pool_depleted(td) && ((td->td_proc->p_flag & P_WEXIT) == 0)) {
+ td->td_retval[0] = DOOR_POOL_DEPLETED;
+ }
+
+ if (d_info->client.td_caller) {
+ d_info->client.error = DOOR_EXIT;
+
+ d = d_info->server.cur_door;
+ DOOR_LOCK(d);
+ d->active_invocations--;
+
+ if (d->active_invocations <= 0) {
+ cv_broadcast(&d->close_cv);
+ }
+ DOOR_UNLOCK(d);
+
+ if (d_info->client.upcall.translated_desc_ptr) {
+ door_fd_close(td,
+ d_info->client.upcall.translated_desc_ptr,
+ d_info->client.upcall.translated_desc_num);
+ DOOR_INFO_UNLOCK(d_info);
+ free(d_info->client.upcall.translated_desc_ptr, M_TEMP);
+
+ } else {
+ DOOR_INFO_UNLOCK(d_info);
+ }
+ wakeup_one(d_info->client.td_caller);
+
+ } else {
+ /* We are responsible for deallocation */
+ DOOR_INFO_UNLOCK(d_info);
+
+ /*
+ * Remove thread from respective pool (check for
+ * unref thread)
+ */
+ if (d_info->server.queued) {
+ door_detach_server_thread(td);
+ }
+
+ cv_destroy(&d_info->server.hold_cv);
+ cv_destroy(&d_info->client.hold_cv);
+
+ free(d_info, M_DOOR_INFO);
+ }
+
+ thread_lock(td);
+ td->td_door = NULL;
+ thread_unlock(td);
+
+ return;
+}
+
+/*
+ * Removes all doors owned by process p. Called from exit1.
+ */
+void
+door_exit(struct proc *p)
+{
+ struct door *d;
+
+ LIST_FOREACH (d, &p->p_doors, entries) {
+ KASSERT(
+ d->proc == p, ("Called door_exit with non-owning process"));
+
+ DOOR_LOCK(d);
+ d->attr |= DOOR_REVOKED;
+ d->proc = NULL;
+ /* Reset refcount to 1 so
+ * that the final close frees
+ * all door structures */
+ d->refcount = 1;
+
+ /* Detach door from an attached file, if any */
+ if (d->attached_vnode != NULL) {
+ struct vnode *vp = d->attached_vnode;
+ VI_LOCK(vp);
+ vp->v_type = VREG;
+ vp->v_op = vp->v_door_vnode_info->v_prev_vnops;
+ vp->v_door_vnode_info = NULL;
+
+ d->attached_vnode = NULL;
+ VI_UNLOCK(vp);
+ }
+
+ /* Wake all calling threads waiting for a server */
+ if (d->attr & DOOR_PRIVATE) {
+ cv_broadcast(&d->priv_pool.pool_cv);
+ }
+ cv_broadcast(&p->door_td_pool.pool_cv);
+
+ DOOR_UNLOCK(d);
+ }
+
+ return;
+}
+
+/*
+ * Registers a single unref thread.
+ */
+
+static int
+kern_door_unref(
+ struct thread *td, struct door *d, void *stack_base, size_t stack_size)
+{
+
+ struct door_td_info *td_info;
+
+ DOOR_LOCK(d);
+
+ if (d->unref_td) {
+ DOOR_UNLOCK(d);
+
+ return (EALREADY);
+ }
+
+ if ((d->attr & (DOOR_UNREF | DOOR_UNREF_MULTI)) == 0) {
+ DOOR_UNLOCK(d);
+ return (EINVAL);
+ }
+
+ DOOR_UNLOCK(d);
+
+ td_info = td_get_door_info(td);
+ if (!td_info) {
+ return (ENOMEM);
+ }
+
+ DOOR_LOCK(d);
+
+ d->unref_td = td;
+ td_info->server.stack.ss_sp = stack_base;
+ td_info->server.stack.ss_size = stack_size;
+
+ td_info->server.cur_door = d;
+
+ cv_broadcast(&d->unref_cv);
+
+ return door_unref(td, d);
+}
+
+/*
+* Prohibits further invocation on target door.
+* Does not affect active invocations.
+*/
+static int
+kern_door_revoke(struct thread *td, struct door *d)
+{
+ if (td->td_proc != d->proc) {
+ return (EPERM);
+ }
+
+ if ((d->attr & DOOR_REVOKED) == 0) {
+ d->attr |= DOOR_REVOKED;
+ }
+
+ return 0;
+}
+
+/*
+* Provides door info by filling out a user-provided door_info_t structure.
+*/
+static int
+kern_door_info(struct thread *td, struct door *d, void *u_door_info)
+{
+
+ struct door_info d_info;
+
+ d_info.di_target = d->proc->p_pid;
+ d_info.di_proc = d->procedure;
+ d_info.di_data = d->udata;
+ d_info.di_attributes = (d->attr & DOOR_ATTR_MASK) |
+ (d->refcount ? 0 : DOOR_IS_UNREF) |
+ ((td->td_proc == d->proc) ? DOOR_LOCAL : 0);
+ d_info.di_uniquifier = d->id;
+
+ int error = copyout(
+ (void *)&d_info, u_door_info, sizeof(struct door_info));
+ if (error) {
+ return error;
+ }
+
+ return 0;
+}
+
+/*
+* Sets various parameters for a door.
+*/
+static int
+kern_door_setparam(struct thread *td, struct door *d, long param, size_t val)
+{
+ if (td->td_proc != d->proc) {
+ return (EPERM);
+ }
+
+ int error = 0;
+
+ DOOR_LOCK(d);
+ switch (param) {
+ case DOOR_PARAM_DESC_MAX: {
+ if (d->attr & DOOR_REFUSE_DESC) {
+ error = (EOPNOTSUPP);
+ break;
+ }
+
+ if (val > DOOR_MAX_DATA_SIZE) {
+ error = (ERANGE);
+ break;
+ }
+
+ d->desc_max = val;
+ break;
+ }
+ case DOOR_PARAM_DATA_MAX: {
+ if (val > DOOR_MAX_DATA_SIZE) {
+ error = (ERANGE);
+ break;
+ }
+
+ d->data_max = val;
+ break;
+ }
+ case DOOR_PARAM_DATA_MIN: {
+ if (val > DOOR_MAX_DATA_SIZE) {
+ error = (ERANGE);
+ break;
+ }
+
+ if (d->data_max < val) {
+ error = (EINVAL);
+ break;
+ }
+
+ d->data_min = val;
+ break;
+ }
+ default:
+ error = (EINVAL);
+ }
+ DOOR_UNLOCK(d);
+
+ return (error);
+}
+
+/*
+* Fetches desired parameter from door.
+*/
+static int
+kern_door_getparam(struct door *d, int param, size_t *u_param_val)
+{
+ size_t retval;
+
+ DOOR_LOCK(d);
+ switch (param) {
+ case DOOR_PARAM_DESC_MAX:
+ retval = d->desc_max;
+ break;
+ case DOOR_PARAM_DATA_MAX:
+ retval = d->data_max;
+ break;
+ case DOOR_PARAM_DATA_MIN:
+ retval = d->data_min;
+ break;
+ default:
+ DOOR_UNLOCK(d);
+ return (EINVAL);
+ }
+
+ DOOR_UNLOCK(d);
+ int error = copyout((void *)&retval, u_param_val, sizeof(size_t));
+ if (error) {
+ return error;
+ }
+
+ return (0);
+}
+
+/*
+* Binds invoking thread to a private door thread pool.
+*/
+static int
+kern_door_bind(
+ struct thread *td, struct door *d)
+{
+
+ DOOR_LOCK(d);
+
+ if (d->proc != td->td_proc) {
+ DOOR_UNLOCK(d);
+ return (EPERM);
+ }
+
+ if (!(d->attr & DOOR_PRIVATE)) {
+ DOOR_UNLOCK(d);
+ return (EINVAL);
+ }
+ DOOR_UNLOCK(d);
+
+
+ if (td->td_door == NULL) {
+ td_get_door_info(td);
+ }
+
+ td->td_door->server.cur_door = d;
+ td->td_door->server.private_pool = 1;
+
+ return 0;
+}
+
+/*
+* Releases invoking thread from private door thread pool.
+*/
+static int
+kern_door_unbind(struct thread *td)
+{
+ struct door *d = td->td_door->server.cur_door;
+
+ if (d == NULL) {
+ return (EBADF);
+ }
+
+ DOOR_LOCK(d);
+
+ if (!(d->attr & DOOR_PRIVATE)) {
+ DOOR_UNLOCK(d);
+ return (EINVAL);
+ }
+
+ td->td_door->server.cur_door = NULL;
+
+ DOOR_UNLOCK(d);
+ return (0);
+}
+
+/*
+* System call wrapper for all door-related system calls.
+*/
+int
+sys_door(struct thread *td, struct door_args *args)
+{
+ switch (args->subcode) {
+ case DOOR_CREATE: {
+ int door_fd;
+
+ /* Check procedure */
+ if (args->arg1 == NULL) {
+ td->td_retval[0] = -1;
+ return (EINVAL);
+ }
+
+ door_fd = kern_door_create(
+ td, args->arg1, (char *)args->arg2, (long)args->arg3);
+ if (door_fd < 0) {
+ td->td_retval[0] = -1;
+ return -(door_fd);
+ }
+
+ td->td_retval[0] = door_fd;
+ return (0);
+ }
+ case DOOR_CALL: {
+ struct door *door;
+ int error = fd_to_door(td, (long)args->arg1, &door);
+ if (error) {
+ td->td_retval[0] = -1;
+ return error;
+ }
+
+ DOOR_LOCK(door);
+ if (DOOR_IS_REVOKED(door)) {
+ DOOR_UNLOCK(door);
+ return (ECANCELED);
+ }
+
+ return kern_door_call(td, door, (door_arg_t *)args->arg2);
+ }
+ case DOOR_RETURN:
+ return kern_door_return(td, (char *)args->arg1,
+ (size_t)args->arg2, (door_desc_t *)args->arg3,
+ (long)args->arg4, args->arg5, (size_t)args->arg6);
+ case DOOR_ATTACH: {
+ return kern_door_attach(
+ td, (long)args->arg1, (const char *)args->arg2);
+ }
+
+ case DOOR_DETACH: {
+ return kern_door_detach(td, (const char *)args->arg1);
+ }
+
+ case DOOR_UNREFSYS: {
+ struct door *door;
+ int error = fd_to_door(td, (long)args->arg1, &door);
+ if (error) {
+ td->td_retval[0] = -1;
+ return error;
+ }
+
+ return kern_door_unref(
+ td, door, args->arg2, (size_t)args->arg3);
+ }
+ case DOOR_REVOKE: {
+ struct door *door;
+ int error = fd_to_door(td, (long)args->arg1, &door);
+ if (error) {
+ td->td_retval[0] = -1;
+ return error;
+ }
+
+ return kern_door_revoke(td, door);
+ }
+
+ case DOOR_INFO: {
+ struct door *door;
+ int error = fd_to_door(td, (long)args->arg1, &door);
+ if (error) {
+ td->td_retval[0] = -1;
+ return error;
+ }
+
+ return kern_door_info(td, door, args->arg2);
+ }
+
+ case DOOR_SETPARAM: {
+ struct door *door;
+ int error = fd_to_door(td, (long)args->arg1, &door);
+ if (error) {
+ td->td_retval[0] = -1;
+ return error;
+ }
+
+ return kern_door_setparam(
+ td, door, (long)args->arg2, (size_t)args->arg3);
+ }
+ case DOOR_GETPARAM: {
+ struct door *door;
+ int error = fd_to_door(td, (long)args->arg1, &door);
+ if (error) {
+ td->td_retval[0] = -1;
+ return error;
+ }
+
+ return kern_door_getparam(
+ door, (long)args->arg2, (size_t *)args->arg3);
+ }
+ case DOOR_BIND: {
+ struct door *door;
+ int error = fd_to_door(td, (long)args->arg1, &door);
+ if (error) {
+ td->td_retval[0] = -1;
+ return error;
+ }
+
+ return kern_door_bind(td, door);
+ }
+ case DOOR_UNBIND: {
+ return kern_door_unbind(td);
+ }
+
+ default:
+ return (EINVAL);
+ }
+}
diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c
--- a/sys/kern/kern_exit.c
+++ b/sys/kern/kern_exit.c
@@ -213,6 +213,8 @@
__unreachable();
}
+void door_exit(struct proc *p);
+
/*
* Exit: deallocate address space and other resources, change proc state to
* zombie, and unlink proc from allproc and parent's lists. Save exit status
@@ -242,6 +244,8 @@
panic("Going nowhere without my init!");
}
+
+
/*
* Deref SU mp, since the thread does not return to userspace.
*/
@@ -251,6 +255,9 @@
* MUST abort all other threads before proceeding past here.
*/
PROC_LOCK(p);
+ if(!LIST_EMPTY(&p->p_doors)){
+ door_exit(p);
+ }
/*
* First check if some other thread or external request got
* here before us. If so, act appropriately: exit or suspend.
@@ -317,6 +324,7 @@
msleep(&p->p_lock, &p->p_mtx, PWAIT, "exithold", 0);
PROC_UNLOCK(p);
+
/* Drain the limit callout while we don't have the proc locked */
callout_drain(&p->p_limco);
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c
--- a/sys/kern/kern_fork.c
+++ b/sys/kern/kern_fork.c
@@ -626,6 +626,11 @@
LIST_INIT(&p2->p_children);
LIST_INIT(&p2->p_orphans);
+ /* Initialize door-related resources */
+ LIST_INIT(&p2->p_doors);
+ SLIST_INIT(&p2->door_td_pool.td_pool);
+ cv_init(&p2->door_td_pool.pool_cv, "door_pool_cv");
+
callout_init_mtx(&p2->p_itcallout, &p2->p_mtx, 0);
TAILQ_INIT(&p2->p_kqtim_stop);
diff --git a/sys/kern/kern_thr.c b/sys/kern/kern_thr.c
--- a/sys/kern/kern_thr.c
+++ b/sys/kern/kern_thr.c
@@ -166,6 +166,7 @@
/* Set up our machine context. */
stack.ss_sp = param->stack_base;
stack.ss_size = param->stack_size;
+
/* Set upcall address to user thread entry function. */
cpu_set_upcall(td, param->start_func, param->arg, &stack);
/* Setup user TLS address and TLS pointer register. */
@@ -305,11 +306,11 @@
return (0);
}
+
int
sys_thr_exit(struct thread *td, struct thr_exit_args *uap)
/* long *state */
{
-
umtx_thread_exit(td);
/* Signal userland that it can free the stack. */
@@ -321,6 +322,10 @@
return (kern_thr_exit(td));
}
+void door_slam(struct thread *td);
+
+
+
int
kern_thr_exit(struct thread *td)
{
@@ -328,6 +333,7 @@
p = td->td_proc;
+
/*
* If all of the threads in a process call this routine to
* exit (e.g. all threads call pthread_exit()), exactly one
@@ -353,6 +359,12 @@
return (0);
}
+ if(td->td_door){
+ PROC_UNLOCK(p);
+ door_slam(td);
+ PROC_LOCK(p);
+ }
+
if (p->p_sysent->sv_ontdexit != NULL)
p->p_sysent->sv_ontdexit(td);
@@ -378,6 +390,7 @@
AUDIT_SYSCALL_EXIT(0, td);
#endif
+
PROC_SLOCK(p);
thread_stopped(p);
thread_exit();
diff --git a/sys/kern/syscalls.c b/sys/kern/syscalls.c
--- a/sys/kern/syscalls.c
+++ b/sys/kern/syscalls.c
@@ -589,4 +589,5 @@
"fspacectl", /* 580 = fspacectl */
"sched_getcpu", /* 581 = sched_getcpu */
"swapoff", /* 582 = swapoff */
+ "door", /* 583 = door */
};
diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master
--- a/sys/kern/syscalls.master
+++ b/sys/kern/syscalls.master
@@ -3307,6 +3307,20 @@
);
}
+583 AUE_NULL STD {
+ int door(
+ int subcode,
+ void *arg1,
+ void *arg2,
+ void *arg3,
+ void *arg4,
+ void *arg5,
+ void *arg6,
+ );
+ }
+
+
+
; Please copy any additions and changes to the following compatability tables:
; sys/compat/freebsd32/syscalls.master
; vim: syntax=off
diff --git a/sys/kern/systrace_args.c b/sys/kern/systrace_args.c
--- a/sys/kern/systrace_args.c
+++ b/sys/kern/systrace_args.c
@@ -3417,6 +3417,19 @@
*n_args = 2;
break;
}
+ /* door */
+ case 583: {
+ struct door_args *p = params;
+ iarg[a++] = p->subcode; /* int */
+ uarg[a++] = (intptr_t)p->arg1; /* void * */
+ uarg[a++] = (intptr_t)p->arg2; /* void * */
+ uarg[a++] = (intptr_t)p->arg3; /* void * */
+ uarg[a++] = (intptr_t)p->arg4; /* void * */
+ uarg[a++] = (intptr_t)p->arg5; /* void * */
+ uarg[a++] = (intptr_t)p->arg6; /* void * */
+ *n_args = 7;
+ break;
+ }
default:
*n_args = 0;
break;
@@ -9134,6 +9147,34 @@
break;
};
break;
+ /* door */
+ case 583:
+ switch (ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "userland void *";
+ break;
+ case 2:
+ p = "userland void *";
+ break;
+ case 3:
+ p = "userland void *";
+ break;
+ case 4:
+ p = "userland void *";
+ break;
+ case 5:
+ p = "userland void *";
+ break;
+ case 6:
+ p = "userland void *";
+ break;
+ default:
+ break;
+ };
+ break;
default:
break;
};
@@ -11087,6 +11128,11 @@
if (ndx == 0 || ndx == 1)
p = "int";
break;
+ /* door */
+ case 583:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
default:
break;
};
diff --git a/sys/kern/vfs_default.c b/sys/kern/vfs_default.c
--- a/sys/kern/vfs_default.c
+++ b/sys/kern/vfs_default.c
@@ -1718,6 +1718,9 @@
case VFIFO:
mode |= S_IFIFO;
break;
+ case VDOOR:
+ mode |= S_IFDOOR;
+ break;
default:
error = EBADF;
goto out;
diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c
--- a/sys/kern/vfs_subr.c
+++ b/sys/kern/vfs_subr.c
@@ -140,11 +140,11 @@
*/
enum vtype iftovt_tab[16] = {
VNON, VFIFO, VCHR, VNON, VDIR, VNON, VBLK, VNON,
- VREG, VNON, VLNK, VNON, VSOCK, VNON, VNON, VNON
+ VREG, VNON, VLNK, VNON, VSOCK, VDOOR, VNON, VNON
};
-int vttoif_tab[10] = {
+int vttoif_tab[11] = {
0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK,
- S_IFSOCK, S_IFIFO, S_IFMT, S_IFMT
+ S_IFSOCK, S_IFIFO, S_IFMT, S_IFMT, S_IFDOOR
};
/*
@@ -4065,7 +4065,7 @@
*/
static const char * const typename[] =
{"VNON", "VREG", "VDIR", "VBLK", "VCHR", "VLNK", "VSOCK", "VFIFO", "VBAD",
- "VMARKER"};
+ "VMARKER", "VDOOR"};
_Static_assert((VHOLD_ALL_FLAGS & ~VHOLD_NO_SMR) == 0,
"new hold count flag not added to vn_printf");
diff --git a/sys/sys/door.h b/sys/sys/door.h
new file mode 100644
--- /dev/null
+++ b/sys/sys/door.h
@@ -0,0 +1,206 @@
+#ifndef _DOOR_
+#define _DOOR_
+
+#include <sys/param.h>
+
+#define DOOR_UNREF 0x01 /* Deliver an unref notification with door */
+#define DOOR_PRIVATE 0x02 /* Use a private pool of server threads */
+#define DOOR_UNREF_MULTI 0x10 /* Deliver unref notification more than once */
+#define DOOR_REFUSE_DESC 0x40 /* Do not accept descriptors from callers */
+#define DOOR_NO_CANCEL 0x80 /* No server thread cancel on client abort */
+#define DOOR_NO_DEPLETION_CB \
+ 0x100 /* No thread create callbacks on depletion \
+ */
+
+/* Attributes (additional) returned with door_info and door_desc_t data */
+#define DOOR_LOCAL 0x04 /* Descriptor is local to current process */
+#define DOOR_REVOKED 0x08 /* Door has been revoked */
+#define DOOR_IS_UNREF 0x20 /* Door is currently unreferenced */
+#define DOOR_PRIVCREATE 0x200 /* Door has a private thread creation func */
+#define DOOR_DEPLETION_CB 0x400 /* Set only during depletion callbacks */
+#define DOOR_UNREF_ACTIVE 0x800 /* Ongoing UNREF invocation */
+
+#define DOOR_UNREF_DATA ((void *)1)
+#define DOOR_POOL_DEPLETED \
+ (-2) /* Internal return value used by kern_door_return and libdoor */
+
+#define DOOR_CREATE_MASK \
+ (DOOR_UNREF | DOOR_PRIVATE | DOOR_UNREF_MULTI | DOOR_REFUSE_DESC | \
+ DOOR_NO_CANCEL)
+#define DOOR_ATTR_MASK \
+ (DOOR_CREATE_MASK | DOOR_LOCAL | DOOR_REVOKED | DOOR_IS_UNREF)
+
+#define DOOR_DESCRIPTOR 0x10000 /* A file descriptor is being passed */
+#define DOOR_RELEASE 0x40000 /* Passed references are also released */
+
+/* door parameters */
+#define DOOR_PARAM_DESC_MAX 1 /* max number of request descriptors */
+#define DOOR_PARAM_DATA_MAX 2 /* max bytes of request data */
+#define DOOR_PARAM_DATA_MIN 3 /* min bytes of request data */
+
+typedef u_int door_attr_t;
+typedef u_int door_id_t;
+typedef void *door_ptr_t;
+
+typedef struct door_desc {
+ door_attr_t d_attributes; /* Tag for union */
+ union {
+ struct {
+ int d_descriptor; /* Descriptor */
+ door_id_t d_id; /* Unique door id */
+ } d_desc;
+ } d_data;
+} door_desc_t;
+
+typedef struct door_arg {
+ char *data_ptr; /* Argument/result data */
+ size_t data_size; /* Argument/result data size */
+ door_desc_t *desc_ptr; /* Argument/result descriptors */
+ u_int desc_num; /* Argument/result num discriptors */
+ char *rbuf; /* Result area */
+ size_t rsize; /* Result size */
+} door_arg_t;
+
+typedef struct door_info {
+ pid_t di_target; /* door server pid */
+ door_ptr_t di_proc; /* server function */
+ door_ptr_t di_data; /* data cookie for invocation */
+ door_attr_t di_attributes; /* door attributes */
+ door_id_t di_uniquifier; /* unique id among all doors */
+} door_info_t;
+
+/* Syscall subcodes */
+#define DOOR_CREATE 0
+#define DOOR_REVOKE 1
+#define DOOR_INFO 2
+#define DOOR_CALL 3
+#define DOOR_BIND 6
+#define DOOR_UNBIND 7
+#define DOOR_UNREFSYS 8
+#define DOOR_UCRED 9
+#define DOOR_RETURN 10
+#define DOOR_GETPARAM 11
+#define DOOR_SETPARAM 12
+#define DOOR_ATTACH 13
+#define DOOR_DETACH 14
+
+#ifdef _KERNEL
+#include <sys/types.h>
+#include <sys/door_common.h>
+#include <sys/kernel.h>
+#include <sys/signal.h>
+
+#define DOOR_WAIT -1
+#define DOOR_EXIT -2
+
+void door_slam(struct thread *td);
+void door_exit(struct proc *p);
+
+struct door;
+
+struct door_upcall_args {
+ char *data_ptr; /* Argument/result data */
+ size_t data_size; /* Argument/result data size */
+ door_desc_t *desc_ptr; /* Argument/result descriptors */
+ u_int desc_num; /* Argument/result num discriptors */
+};
+
+struct door_upcall_info {
+ int active;
+
+ struct door_upcall_args args; /* Arguments passed to door_return */
+
+ door_desc_t *translated_desc_ptr; /* Argument/result descriptors */
+ u_int translated_desc_num; /* Argument/result num discriptors */
+};
+
+struct door_client_info {
+ struct thread *td_caller;
+
+ door_arg_t uargs; /* Arguments passed via door_call */
+ void *uargs_addr; /* Pointer to userspace door_arg_t */
+ boolean_t uargs_changed; /* Signals that a result and/or descriptor buffer
+ have been allocated for the client */
+
+ boolean_t hold;
+ struct cv hold_cv;
+
+ int error;
+
+ struct door_upcall_info upcall;
+};
+
+struct door_server_info {
+ struct door *cur_door;
+ stack_t stack;
+ boolean_t private_pool;
+ boolean_t queued;
+ boolean_t hold;
+ struct cv hold_cv;
+};
+
+struct door_td_info {
+ struct mtx lock;
+ struct door_server_info server;
+ struct door_client_info client;
+};
+
+struct vnops;
+
+struct door_vnode_info {
+ struct door *v_door;
+ struct vop_vector *v_prev_vnops;
+};
+
+struct door {
+
+ struct mtx door_lock;
+ u_int attr;
+ /* Currently, a single door can only be bound to a single file */
+ struct vnode *door_vnode;
+ struct vnode *attached_vnode;
+ struct door_vnode_info *attached_vnode_info;
+
+ size_t desc_max;
+ size_t data_min;
+ size_t data_max;
+
+ int refcount;
+ int unrefcount;
+
+ int active_invocations;
+ struct cv close_cv;
+
+ struct proc *proc;
+ struct thread *unref_td;
+ struct cv unref_cv;
+
+ door_pool_t priv_pool;
+
+ LIST_ENTRY(door) entries;
+
+ void (*procedure)(void);
+ void *udata;
+
+ door_id_t id;
+};
+
+#define DOOR_TD_HELD(info) (((info)->hold) == 1)
+#define DOOR_TD_HOLD(info) (((info)->hold) = 1)
+#define DOOR_TD_RELEASE(info) \
+ do { \
+ ((info)->hold) = 0; \
+ cv_signal(&(info)->hold_cv); \
+ } while (0)
+
+#define DOOR_INFO_LOCK(i) mtx_lock(&(i)->lock)
+#define DOOR_INFO_UNLOCK(i) mtx_unlock(&(i)->lock)
+
+#define DOOR_LOCK(d) mtx_lock(&(d)->door_lock)
+#define DOOR_UNLOCK(d) mtx_unlock(&(d)->door_lock)
+#define DOOR_LOCK_ASSERT(d, val) mtx_assert(&(d)->door_lock, (val));
+#define DOOR_IS_REVOKED(d) ((d)->attr & DOOR_REVOKED)
+
+
+#endif /* _KERNEL */
+#endif /* _DOOR_ */
diff --git a/sys/sys/door_common.h b/sys/sys/door_common.h
new file mode 100644
--- /dev/null
+++ b/sys/sys/door_common.h
@@ -0,0 +1,14 @@
+/* Used by door.h and proc.h */
+#ifndef _DOOR_COMMON_
+#define _DOOR_COMMON_
+
+
+#include <sys/condvar.h>
+#include <sys/queue.h>
+
+typedef struct {
+ SLIST_HEAD(, thread) td_pool;
+ struct cv pool_cv;
+} door_pool_t;
+
+#endif /* _DOOR_COMMON_ */
diff --git a/sys/sys/door_extern.h b/sys/sys/door_extern.h
new file mode 100644
--- /dev/null
+++ b/sys/sys/door_extern.h
@@ -0,0 +1,17 @@
+#ifndef _DOOR_EXTERN_
+#define _DOOR_EXTERN_
+
+#ifdef _KERNEL
+
+#include <sys/types.h>
+#include <sys/signal.h>
+
+struct thread;
+struct door_desc;
+
+/* NB: udata (the first arg) is set by cpu_set_upcall */
+void door_set_args(struct thread *td, void *cookiep, char *argp,
+ size_t arg_size, struct door_desc *dp, u_int n_desc);
+
+#endif /* _KERNEL */
+#endif /* _DOOR_EXTERN_ */
diff --git a/sys/sys/file.h b/sys/sys/file.h
--- a/sys/sys/file.h
+++ b/sys/sys/file.h
@@ -71,6 +71,8 @@
#define DTYPE_PROCDESC 12 /* process descriptor */
#define DTYPE_EVENTFD 13 /* eventfd */
#define DTYPE_LINUXTFD 14 /* emulation timerfd type */
+#define DTYPE_DOOR 15 /* Solaris Door endpoint */
+
#ifdef _KERNEL
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -54,6 +54,7 @@
#include <sys/lock_profile.h>
#include <sys/_mutex.h>
#include <sys/osd.h>
+#include <sys/door_common.h>
#include <sys/priority.h>
#include <sys/rtprio.h> /* XXX. */
#include <sys/runq.h>
@@ -200,6 +201,7 @@
struct vm_map;
struct vm_map_entry;
struct epoch_tracker;
+struct door_td_info;
struct syscall_args {
u_int code;
@@ -341,6 +343,7 @@
void *td_sigblock_ptr; /* (k) uptr for fast sigblock. */
uint32_t td_sigblock_val; /* (k) fast sigblock value read at
td_sigblock_ptr on kern entry */
+
#define td_endcopy td_pcb
/*
@@ -389,6 +392,8 @@
#ifdef EPOCH_TRACE
SLIST_HEAD(, epoch_tracker) td_epochs;
#endif
+ struct door_td_info *td_door;
+ SLIST_ENTRY(thread) td_door_pool;
};
struct thread0_storage {
@@ -744,6 +749,9 @@
LIST_HEAD(, proc) p_orphans; /* (e) Pointer to list of orphans. */
TAILQ_HEAD(, kq_timer_cb_data) p_kqtim_stop; /* (c) */
+
+ LIST_HEAD(, door) p_doors; /* List of doors. */
+ door_pool_t door_td_pool; /* Door thread pool. */
};
#define p_session p_pgrp->pg_session
diff --git a/sys/sys/stat.h b/sys/sys/stat.h
--- a/sys/sys/stat.h
+++ b/sys/sys/stat.h
@@ -275,7 +275,9 @@
#define S_IFREG 0100000 /* regular */
#define S_IFLNK 0120000 /* symbolic link */
#define S_IFSOCK 0140000 /* socket */
+#define S_IFDOOR 0150000 /* door */
#define S_ISVTX 0001000 /* save swapped text even after use */
+
#endif
#if __BSD_VISIBLE
#define S_IFWHT 0160000 /* whiteout */
diff --git a/sys/sys/syscall.h b/sys/sys/syscall.h
--- a/sys/sys/syscall.h
+++ b/sys/sys/syscall.h
@@ -520,4 +520,5 @@
#define SYS_fspacectl 580
#define SYS_sched_getcpu 581
#define SYS_swapoff 582
-#define SYS_MAXSYSCALL 583
+#define SYS_door 583
+#define SYS_MAXSYSCALL 584
diff --git a/sys/sys/syscall.mk b/sys/sys/syscall.mk
--- a/sys/sys/syscall.mk
+++ b/sys/sys/syscall.mk
@@ -425,4 +425,5 @@
aio_readv.o \
fspacectl.o \
sched_getcpu.o \
- swapoff.o
+ swapoff.o \
+ door.o
diff --git a/sys/sys/sysproto.h b/sys/sys/sysproto.h
--- a/sys/sys/sysproto.h
+++ b/sys/sys/sysproto.h
@@ -1855,6 +1855,15 @@
char name_l_[PADL_(const char *)]; const char * name; char name_r_[PADR_(const char *)];
char flags_l_[PADL_(u_int)]; u_int flags; char flags_r_[PADR_(u_int)];
};
+struct door_args {
+ char subcode_l_[PADL_(int)]; int subcode; char subcode_r_[PADR_(int)];
+ char arg1_l_[PADL_(void *)]; void * arg1; char arg1_r_[PADR_(void *)];
+ char arg2_l_[PADL_(void *)]; void * arg2; char arg2_r_[PADR_(void *)];
+ char arg3_l_[PADL_(void *)]; void * arg3; char arg3_r_[PADR_(void *)];
+ char arg4_l_[PADL_(void *)]; void * arg4; char arg4_r_[PADR_(void *)];
+ char arg5_l_[PADL_(void *)]; void * arg5; char arg5_r_[PADR_(void *)];
+ char arg6_l_[PADL_(void *)]; void * arg6; char arg6_r_[PADR_(void *)];
+};
int sys_exit(struct thread *, struct exit_args *);
int sys_fork(struct thread *, struct fork_args *);
int sys_read(struct thread *, struct read_args *);
@@ -2250,6 +2259,7 @@
int sys_fspacectl(struct thread *, struct fspacectl_args *);
int sys_sched_getcpu(struct thread *, struct sched_getcpu_args *);
int sys_swapoff(struct thread *, struct swapoff_args *);
+int sys_door(struct thread *, struct door_args *);
#ifdef COMPAT_43
@@ -3219,6 +3229,7 @@
#define SYS_AUE_fspacectl AUE_FSPACECTL
#define SYS_AUE_sched_getcpu AUE_NULL
#define SYS_AUE_swapoff AUE_SWAPOFF
+#define SYS_AUE_door AUE_NULL
#undef PAD_
#undef PADL_
diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h
--- a/sys/sys/vnode.h
+++ b/sys/sys/vnode.h
@@ -57,7 +57,7 @@
* Vnode types. VNON means no type.
*/
enum vtype { VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO, VBAD,
- VMARKER };
+ VMARKER, VDOOR};
enum vgetstate { VGET_NONE, VGET_HOLDCNT, VGET_USECOUNT };
/*
@@ -67,6 +67,7 @@
struct namecache;
struct cache_fpl;
+struct door_vnode_info;
struct vpollinfo {
struct mtx vpi_lock; /* lock to protect below */
@@ -127,6 +128,7 @@
struct unpcb *v_unpcb; /* v unix domain net (VSOCK) */
struct cdev *v_rdev; /* v device (VCHR, VBLK) */
struct fifoinfo *v_fifoinfo; /* v fifo (VFIFO) */
+ struct door_vnode_info *v_door_vnode_info; /* v ptr to attached door (VDOOR) */
};
/*
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Jan 15, 11:57 PM (37 m, 17 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27656098
Default Alt Text
D34097.id.diff (72 KB)
Attached To
Mode
D34097: Solaris doors IPC implementation
Attached
Detach File
Event Timeline
Log In to Comment