#include <sys/types.h>
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/cred.h>
#include <sys/kmem.h>
#include <sys/lgrp.h>
#include <sys/mman.h>
#include <vm/hat.h>
#include <vm/as.h>
#include <vm/seg.h>
#include <vm/seg_kmem.h>
#include <vm/seg_umap.h>
static boolean_t segumap_verify_safe(caddr_t, size_t);
static int segumap_dup(struct seg *, struct seg *);
static int segumap_unmap(struct seg *, caddr_t, size_t);
static void segumap_free(struct seg *);
static faultcode_t segumap_fault(struct hat *, struct seg *, caddr_t, size_t,
enum fault_type, enum seg_rw);
static faultcode_t segumap_faulta(struct seg *, caddr_t);
static int segumap_setprot(struct seg *, caddr_t, size_t, uint_t);
static int segumap_checkprot(struct seg *, caddr_t, size_t, uint_t);
static int segumap_sync(struct seg *, caddr_t, size_t, int, uint_t);
static size_t segumap_incore(struct seg *, caddr_t, size_t, char *);
static int segumap_lockop(struct seg *, caddr_t, size_t, int, int, ulong_t *,
size_t);
static int segumap_getprot(struct seg *, caddr_t, size_t, uint_t *);
static u_offset_t segumap_getoffset(struct seg *, caddr_t);
static int segumap_gettype(struct seg *, caddr_t);
static int segumap_getvp(struct seg *, caddr_t, struct vnode **);
static int segumap_advise(struct seg *, caddr_t, size_t, uint_t);
static void segumap_dump(struct seg *);
static int segumap_pagelock(struct seg *, caddr_t, size_t, struct page ***,
enum lock_type, enum seg_rw);
static int segumap_setpagesize(struct seg *, caddr_t, size_t, uint_t);
static int segumap_getmemid(struct seg *, caddr_t, memid_t *);
static int segumap_capable(struct seg *, segcapability_t);
static struct seg_ops segumap_ops = {
segumap_dup,
segumap_unmap,
segumap_free,
segumap_fault,
segumap_faulta,
segumap_setprot,
segumap_checkprot,
NULL,
NULL,
segumap_sync,
segumap_incore,
segumap_lockop,
segumap_getprot,
segumap_getoffset,
segumap_gettype,
segumap_getvp,
segumap_advise,
segumap_dump,
segumap_pagelock,
segumap_setpagesize,
segumap_getmemid,
NULL,
segumap_capable,
seg_inherit_notsup
};
int
segumap_create(struct seg **segpp, void *argsp)
{
struct seg *seg = *segpp;
segumap_crargs_t *a = (struct segumap_crargs *)argsp;
segumap_data_t *data;
ASSERT((uintptr_t)a->kaddr > _userlimit);
if ((uintptr_t)a->kaddr <= _userlimit ||
((uintptr_t)a->kaddr + seg->s_size) < (uintptr_t)a->kaddr ||
(a->prot & PROT_USER) == 0 ||
((uintptr_t)a->kaddr & PAGEOFFSET) != 0 ||
!segumap_verify_safe(a->kaddr, seg->s_size)) {
return (EINVAL);
}
data = kmem_zalloc(sizeof (*data), KM_SLEEP);
rw_init(&data->sud_lock, NULL, RW_DEFAULT, NULL);
data->sud_kaddr = a->kaddr;
data->sud_prot = a->prot;
seg->s_ops = &segumap_ops;
seg->s_data = data;
return (0);
}
static boolean_t
segumap_verify_safe(caddr_t kaddr, size_t len)
{
struct seg *seg;
AS_LOCK_ENTER(&kas, RW_READER);
seg = as_segat(&kas, kaddr);
VERIFY(seg != NULL);
VERIFY(seg->s_base + seg->s_size >= kaddr + len);
VERIFY(seg->s_ops == &segkmem_ops);
AS_LOCK_EXIT(&kas);
return (B_TRUE);
}
static int
segumap_dup(struct seg *seg, struct seg *newseg)
{
segumap_data_t *sud = (segumap_data_t *)seg->s_data;
segumap_data_t *newsud;
ASSERT(seg->s_as && AS_WRITE_HELD(seg->s_as));
newsud = kmem_zalloc(sizeof (segumap_data_t), KM_SLEEP);
rw_init(&newsud->sud_lock, NULL, RW_DEFAULT, NULL);
newsud->sud_kaddr = sud->sud_kaddr;
newsud->sud_prot = sud->sud_prot;
newseg->s_ops = seg->s_ops;
newseg->s_data = newsud;
return (0);
}
static int
segumap_unmap(struct seg *seg, caddr_t addr, size_t len)
{
segumap_data_t *sud = (segumap_data_t *)seg->s_data;
ASSERT(seg->s_as && AS_WRITE_HELD(seg->s_as));
if (addr != seg->s_base || len != seg->s_size) {
return (EINVAL);
}
if (sud->sud_softlockcnt != 0) {
return (EAGAIN);
}
hat_unload(seg->s_as->a_hat, addr, len, HAT_UNLOAD_UNMAP);
seg_free(seg);
return (0);
}
static void
segumap_free(struct seg *seg)
{
segumap_data_t *data = (segumap_data_t *)seg->s_data;
ASSERT(data != NULL);
rw_destroy(&data->sud_lock);
VERIFY(data->sud_softlockcnt == 0);
kmem_free(data, sizeof (*data));
seg->s_data = NULL;
}
static faultcode_t
segumap_fault(struct hat *hat, struct seg *seg, caddr_t addr, size_t len,
enum fault_type type, enum seg_rw tw)
{
segumap_data_t *sud = (segumap_data_t *)seg->s_data;
ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as));
if (type == F_PROT) {
return (FC_PROT);
} else if (type == F_SOFTUNLOCK) {
size_t plen = btop(len);
rw_enter(&sud->sud_lock, RW_WRITER);
VERIFY(sud->sud_softlockcnt >= plen);
sud->sud_softlockcnt -= plen;
rw_exit(&sud->sud_lock);
return (0);
}
ASSERT(type == F_INVAL || type == F_SOFTLOCK);
rw_enter(&sud->sud_lock, RW_WRITER);
if (type == F_INVAL ||
(type == F_SOFTLOCK && sud->sud_softlockcnt == 0)) {
for (uintptr_t i = 0; i < seg->s_size; i += PAGESIZE) {
pfn_t pfn;
pfn = hat_getpfnum(kas.a_hat, sud->sud_kaddr + i);
VERIFY(pfn != PFN_INVALID);
hat_devload(seg->s_as->a_hat, seg->s_base + i,
PAGESIZE, pfn, sud->sud_prot, HAT_LOAD);
}
}
if (type == F_SOFTLOCK) {
size_t nval = sud->sud_softlockcnt + btop(len);
if (sud->sud_softlockcnt >= nval) {
rw_exit(&sud->sud_lock);
return (FC_MAKE_ERR(EOVERFLOW));
}
sud->sud_softlockcnt = nval;
}
rw_exit(&sud->sud_lock);
return (0);
}
static faultcode_t
segumap_faulta(struct seg *seg, caddr_t addr)
{
return (0);
}
static int
segumap_setprot(struct seg *seg, caddr_t addr, size_t len, uint_t prot)
{
ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as));
return (EACCES);
}
static int
segumap_checkprot(struct seg *seg, caddr_t addr, size_t len, uint_t prot)
{
segumap_data_t *sud = (segumap_data_t *)seg->s_data;
int error = 0;
ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as));
rw_enter(&sud->sud_lock, RW_READER);
if ((sud->sud_prot & prot) != prot) {
error = EACCES;
}
rw_exit(&sud->sud_lock);
return (error);
}
static int
segumap_sync(struct seg *seg, caddr_t addr, size_t len, int attr, uint_t flags)
{
return (0);
}
static size_t
segumap_incore(struct seg *seg, caddr_t addr, size_t len, char *vec)
{
size_t sz = 0;
ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as));
len = (len + PAGEOFFSET) & PAGEMASK;
while (len > 0) {
*vec = 1;
sz += PAGESIZE;
vec++;
len -= PAGESIZE;
}
return (sz);
}
static int
segumap_lockop(struct seg *seg, caddr_t addr, size_t len, int attr, int op,
ulong_t *lockmap, size_t pos)
{
return (0);
}
static int
segumap_getprot(struct seg *seg, caddr_t addr, size_t len, uint_t *protv)
{
segumap_data_t *sud = (segumap_data_t *)seg->s_data;
size_t pgno;
uint_t prot;
ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as));
rw_enter(&sud->sud_lock, RW_READER);
prot = sud->sud_prot;
rw_exit(&sud->sud_lock);
pgno = seg_page(seg, addr + len) - seg_page(seg, addr) + 1;
while (pgno > 0) {
protv[--pgno] = prot;
}
return (0);
}
static u_offset_t
segumap_getoffset(struct seg *seg, caddr_t addr)
{
return (0);
}
static int
segumap_gettype(struct seg *seg, caddr_t addr)
{
return (MAP_SHARED);
}
static int
segumap_getvp(struct seg *seg, caddr_t addr, struct vnode **vpp)
{
ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as));
*vpp = NULL;
return (0);
}
static int
segumap_advise(struct seg *seg, caddr_t addr, size_t len, uint_t behav)
{
if (behav == MADV_PURGE) {
return (EINVAL);
}
return (0);
}
static void
segumap_dump(struct seg *seg)
{
}
static int
segumap_pagelock(struct seg *seg, caddr_t addr, size_t len, struct page ***ppp,
enum lock_type type, enum seg_rw rw)
{
return (ENOTSUP);
}
static int
segumap_setpagesize(struct seg *seg, caddr_t addr, size_t len, uint_t szc)
{
return (ENOTSUP);
}
static int
segumap_getmemid(struct seg *seg, caddr_t addr, memid_t *memidp)
{
segumap_data_t *sud = (segumap_data_t *)seg->s_data;
memidp->val[0] = (uintptr_t)sud->sud_kaddr;
memidp->val[1] = (uintptr_t)(addr - seg->s_base);
return (0);
}
static int
segumap_capable(struct seg *seg, segcapability_t capability)
{
return (0);
}