Page MenuHomeFreeBSD

D34097.id.diff
No OneTemporary

D34097.id.diff

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

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)

Event Timeline