#include <sys/types.h>
#include <sys/errno.h>
#include <sys/mman.h>
#include <sys/cred.h>
#include <sys/model.h>
#include <sys/vnode.h>
#include <sys/systm.h>
#include <sys/kmem.h>
#include <sys/file.h>
#include <sys/vfs.h>
#include <sys/sysmacros.h>
#include <sys/mmapobj.h>
#define MOBJ_STACK_SEGS 6
static void
mmapobj_copy_64to32(mmapobj_result_t *source, mmapobj_result32_t *dest, int num)
{
int i;
for (i = 0; i < num; i++) {
dest[i].mr_addr = (caddr32_t)(uintptr_t)source[i].mr_addr;
dest[i].mr_msize = (size32_t)source[i].mr_msize;
dest[i].mr_fsize = (size32_t)source[i].mr_fsize;
dest[i].mr_offset = (size32_t)source[i].mr_offset;
dest[i].mr_prot = source[i].mr_prot;
dest[i].mr_flags = source[i].mr_flags;
}
}
int
mmapobjsys(int fd, uint_t flags, mmapobj_result_t *storage,
uint_t *elements, void *arg)
{
uint_t num_mapped;
uint_t num_in;
int error;
int old_error;
size_t padding = 0;
mmapobj_result_t stack_mr[MOBJ_STACK_SEGS];
mmapobj_result_t *mrp = stack_mr;
struct file *fp;
struct vnode *vp;
model_t model;
int convert_64to32 = 0;
uint_t alloc_num = 0;
if ((flags & ~MMOBJ_ALL_FLAGS) != 0) {
return (set_errno(EINVAL));
}
if (((flags & MMOBJ_PADDING) == 0) && arg != NULL) {
return (set_errno(EINVAL));
}
fp = getf(fd);
if (fp == NULL) {
return (set_errno(EBADF));
}
vp = fp->f_vnode;
if ((fp->f_flag & FREAD) == 0) {
error = EACCES;
goto out;
}
error = copyin(elements, &num_mapped, sizeof (uint_t));
if (error) {
error = EFAULT;
goto out;
}
num_in = num_mapped;
model = get_udatamodel();
if (model != DATAMODEL_NATIVE) {
ASSERT(model == DATAMODEL_ILP32);
convert_64to32 = 1;
}
if (flags & MMOBJ_PADDING) {
if (convert_64to32) {
size32_t padding32;
error = copyin(arg, &padding32, sizeof (padding32));
padding = padding32;
} else {
error = copyin(arg, &padding, sizeof (padding));
}
if (error) {
error = EFAULT;
goto out;
}
if (padding) {
padding = P2ROUNDUP(padding, PAGESIZE);
if (padding == 0) {
error = ENOMEM;
goto out;
}
}
if (padding == 0) {
flags = flags & (~MMOBJ_PADDING);
}
}
if (num_mapped > MOBJ_STACK_SEGS) {
num_mapped = MOBJ_STACK_SEGS;
}
retry:
error = mmapobj(vp, flags, mrp, &num_mapped, padding, fp->f_cred);
if (error == E2BIG && alloc_num == 0) {
if (num_mapped > MOBJ_STACK_SEGS && num_mapped <= num_in) {
mrp = kmem_alloc(sizeof (mmapobj_result_t) * num_mapped,
KM_SLEEP);
alloc_num = num_mapped;
goto retry;
}
}
old_error = error;
if (error == 0 || error == E2BIG) {
error = copyout(&num_mapped, elements, sizeof (uint_t));
if (error) {
error = EFAULT;
if (old_error == 0) {
mmapobj_unmap(mrp, num_mapped, num_mapped, 0);
}
} else if (num_in < num_mapped) {
ASSERT(old_error == E2BIG);
error = E2BIG;
} else {
if (convert_64to32) {
mmapobj_result32_t *mrp32;
mrp32 = kmem_alloc(num_mapped * sizeof (*mrp32),
KM_SLEEP);
mmapobj_copy_64to32(mrp, mrp32, num_mapped);
error = copyout(mrp32, (void *)storage,
num_mapped * sizeof (mmapobj_result32_t));
kmem_free(mrp32, num_mapped * sizeof (*mrp32));
} else {
error = copyout(mrp, (void *)storage,
num_mapped * sizeof (mmapobj_result_t));
}
if (error) {
error = EFAULT;
mmapobj_unmap(mrp, num_mapped, num_mapped, 0);
}
}
}
if (alloc_num != 0) {
ASSERT(mrp != stack_mr);
ASSERT(num_mapped > MOBJ_STACK_SEGS);
kmem_free(mrp,
alloc_num * sizeof (mmapobj_result_t));
}
out:
releasef(fd);
if (error) {
return (set_errno(error));
} else {
return (0);
}
}