mem.c revision b655a209ddb403c1e5002c46e0deac3e5c8a30ed
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Memory special file
*/
#include <sys/bootconf.h>
#include <vm/seg_kmem.h>
#ifdef __sparc
uint64_t *, int *, int *, int *);
extern size_t cpu_get_name_bufsize(void);
extern int cpu_get_mem_sid(char *, char *, int, int *);
#endif /* __sparc */
/*
* Turn a byte length into a pagecount. The DDI btop takes a
* 32-bit size on 32-bit machines, this handles 64-bit sizes for
* large physical-memory 32-bit machines.
*/
static int mm_kmem_io_access;
/*ARGSUSED1*/
static int
{
int i;
struct mem_minor {
char *name;
int privonly;
const char *rdpriv;
const char *wrpriv;
} mm[] = {
};
DDI_FAILURE) {
return (DDI_FAILURE);
}
}
}
"kmem_io_access", 0);
return (DDI_SUCCESS);
}
/*ARGSUSED*/
static int
{
register int error;
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
error = DDI_SUCCESS;
break;
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)0;
error = DDI_SUCCESS;
break;
default:
error = DDI_FAILURE;
}
return (error);
}
/*ARGSUSED1*/
static int
{
case M_NULL:
case M_ZERO:
case M_MEM:
case M_KMEM:
case M_ALLKMEM:
/* standard devices */
break;
default:
/* Unsupported or unknown type */
return (EINVAL);
}
return (0);
}
/*ARGSUSED*/
static int
{
case M_NULL:
case M_ZERO:
case M_MEM:
case M_KMEM:
case M_ALLKMEM:
/*
* A non NULL pollhead pointer should be returned in case
* user polls for 0 events.
*/
return (0);
default:
/* no other devices currently support polling */
return (ENXIO);
}
}
static int
{
/*
* implement zero size to reduce overhead (avoid two failing
* property lookups per stat).
*/
}
static int
{
int error = 0;
if (!pf_is_memory(pfn)) {
if (allowio) {
sizeof (int32_t)) != DDI_SUCCESS)
} else
} else
return (error);
}
#ifdef __sparc
static int
{
int i;
return (i);
}
#else /* __i386, __amd64 */
#define NEED_LOCK_KVADDR(va) 0
#endif /* __sparc */
/*ARGSUSED3*/
static int
{
pfn_t v;
int error = 0;
size_t c;
uio->uio_iovcnt--;
if (uio->uio_iovcnt < 0)
panic("mmrw");
continue;
}
switch (minor) {
case M_MEM:
break;
}
break;
case M_KMEM:
case M_ALLKMEM:
{
int locked = 0;
/*
* If vaddr does not map a valid page, as_pagelock()
* will return failure. Hence we can't check the
* return value and return EFAULT here as we'd like.
* seg_kp and seg_kpm do not properly support
* as_pagelock() for this context so we avoid it
* using the try_lock set check above. Some day when
* the kernel page locking gets redesigned all this
* muck can be cleaned up.
*/
if (try_lock)
if (v == PFN_INVALID) {
if (locked)
break;
}
if (locked)
S_WRITE);
}
break;
case M_ZERO:
no_fault();
break;
}
no_fault();
break;
}
/* else it's a write, fall through to NULL case */
/*FALLTHROUGH*/
case M_NULL:
return (0);
uio->uio_loffset += c;
break;
}
}
}
static int
{
}
static int
{
}
/*
* Private ioctl for libkvm to support kvm_physaddr().
* Given an address space and a VA, compute the PA.
*/
static int
{
proc_t *p;
return (EFAULT);
return (EIO);
} else {
break;
}
}
if (p == NULL)
return (EIO);
if (p == NULL)
return (EIO);
mutex_exit(&p->p_lock);
break;
mutex_enter(&p->p_lock);
}
sprunlock(p);
}
if (pfn == PFN_INVALID)
return (EIO);
return (EFAULT);
return (0);
}
/*
* Given a PA, execute the given page retire command on it.
*/
static int
{
extern int page_retire_test(void);
return (EFAULT);
}
switch (cmd) {
case MEM_PAGE_ISRETIRED:
case MEM_PAGE_UNRETIRE:
return (page_unretire(pa));
case MEM_PAGE_RETIRE:
case MEM_PAGE_RETIRE_MCE:
case MEM_PAGE_RETIRE_UE:
case MEM_PAGE_GETERRORS:
{
sizeof (uint64_t))) {
return (EFAULT);
}
return (rc);
}
case MEM_PAGE_RETIRE_TEST:
return (page_retire_test());
}
return (EINVAL);
}
/*
* Given a mem-scheme FMRI for a page, execute the given page retire
* command on it.
*/
static int
{
int err;
return (err);
return (err);
return (err);
}
switch (cmd) {
case MEM_PAGE_FMRI_ISRETIRED:
case MEM_PAGE_FMRI_RETIRE:
}
return (EINVAL);
}
#ifdef __sparc
/*
* Given a syndrome, syndrome type, and address return the
* associated memory name in the provided data buffer.
*/
static int
{
void *buf;
if ((bufsize = cpu_get_name_bufsize()) == 0)
return (ENOTSUP);
return (err);
/*
* Call into cpu specific code to do the lookup.
*/
return (err);
}
return (ENAMETOOLONG);
}
return (EFAULT);
}
return (0);
}
/*
* Given a syndrome and address return information about the associated memory.
*/
static int
{
int err;
return (EFAULT);
return (err);
return (EFAULT);
return (0);
}
/*
* Given a memory name, return its associated serial id
*/
static int
{
void *buf;
void *name;
if ((bufsize = cpu_get_name_bufsize()) == 0)
return (ENOTSUP);
return (err);
return (err);
}
/*
* Call into cpu specific code to do the lookup.
*/
return (err);
}
return (ENAMETOOLONG);
}
return (EFAULT);
}
return (0);
}
#endif /* __sparc */
/*
* Private ioctls for
* libkvm to support kvm_physaddr().
* FMA support for page_retire() and memory attribute information.
*/
/*ARGSUSED*/
static int
{
return (ENXIO);
return (ENXIO);
switch (cmd) {
case MEM_VTOP:
return (mmioctl_vtop(data));
case MEM_PAGE_RETIRE:
case MEM_PAGE_ISRETIRED:
case MEM_PAGE_UNRETIRE:
case MEM_PAGE_RETIRE_MCE:
case MEM_PAGE_RETIRE_UE:
case MEM_PAGE_GETERRORS:
case MEM_PAGE_RETIRE_TEST:
case MEM_PAGE_FMRI_RETIRE:
case MEM_PAGE_FMRI_ISRETIRED:
#ifdef __sparc
case MEM_NAME:
return (mmioctl_get_mem_name(data));
case MEM_INFO:
return (mmioctl_get_mem_info(data));
case MEM_SID:
return (mmioctl_get_mem_sid(data));
#else
case MEM_NAME:
case MEM_INFO:
case MEM_SID:
return (ENOTSUP);
#endif /* __sparc */
}
return (ENXIO);
}
/*ARGSUSED2*/
static int
{
switch (minor) {
case M_MEM:
return (impl_obmem_pfnum(pf));
}
}
break;
case M_KMEM:
case M_ALLKMEM:
/* no longer supported with KPR */
return (-1);
case M_ZERO:
/*
* mmsegmap() should have already converted
* a mapping request for this device to a mapping
* using seg_vn for anonymous memory.
*/
break;
}
return (-1);
}
/*
* This function is called when a memory device is mmap'ed.
* Set up the mapping to the correct device driver.
*/
static int
{
struct segvn_crargs vn_a;
struct segdev_crargs dev_a;
int error;
off_t i;
/*
* since this is a "clone" object that doesn't yet exist.
*/
return (ENOMEM);
}
} else {
/*
* User specified address -
* Blow away any previous mappings.
*/
}
switch (minor) {
case M_MEM:
return (EINVAL);
}
/*
* Check to ensure that the entire range is
* legal and we are not trying to map in
* more than the device will let us.
*/
return (ENXIO);
}
}
/*
*/
/*
* alias pages that don't have page structs behind them,
* such as kernel stack pages. If someone mmap()s a kernel
* stack page and if we give him a tte with cv, a line from
* that page can get into both pages of the spitfire d$.
* But snoop from another processor will only invalidate
* the first page. This later caused kernel (xc_attention)
* to go into an infinite loop at pil 13 and no interrupts
* could come in. See 1203630.
*
*/
break;
case M_ZERO:
/*
* Passing in a NULL amp gives us the "cloning" effect.
*/
break;
case M_KMEM:
case M_ALLKMEM:
/* No longer supported with KPR. */
break;
case M_NULL:
/*
*/
break;
default:
}
return (error);
}
mmopen, /* open */
nulldev, /* close */
nodev, /* strategy */
nodev, /* print */
nodev, /* dump */
mmread, /* read */
mmwrite, /* write */
mmioctl, /* ioctl */
nodev, /* devmap */
mmmmap, /* mmap */
mmsegmap, /* segmap */
mmchpoll, /* poll */
mmpropop, /* prop_op */
0, /* streamtab */
};
DEVO_REV, /* devo_rev, */
0, /* refcnt */
mm_info, /* get_dev_info */
nulldev, /* identify */
nulldev, /* probe */
mm_attach, /* attach */
nodev, /* detach */
nodev, /* reset */
&mm_cb_ops, /* driver operations */
(struct bus_ops *)0 /* bus operations */
};
};
static struct modlinkage modlinkage = {
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
{
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}
static int
{
if (rw == KSTAT_WRITE)
return (EACCES);
count = 0;
count++;
}
return (0);
}
static int
{
struct memunit {
} *kspmem;
if (rw == KSTAT_WRITE)
return (EACCES);
break;
}
return (0);
}
/*
* Read a mem_name_t from user-space and store it in the mem_name_t
* pointed to by the mem_name argument.
*/
static int
{
if (get_udatamodel() == DATAMODEL_NATIVE) {
return (EFAULT);
}
#ifdef _SYSCALL32
else {
return (EFAULT);
}
#endif /* _SYSCALL32 */
return (0);
}
/*
* Read a mem_page_t from user-space and store it in the mem_page_t
* pointed to by the mpage argument.
*/
static int
{
if (get_udatamodel() == DATAMODEL_NATIVE) {
return (EFAULT);
}
#ifdef _SYSCALL32
else {
return (EFAULT);
}
#endif /* _SYSCALL32 */
return (0);
}
/*
* Expand an FMRI from a mem_page_t.
*/
static int
{
char *buf;
int err;
return (EINVAL);
return (EFAULT);
}
return (err);
}
static int
{
char *scheme;
char *unum;
char **serids;
int err;
/* Verify FMRI scheme name and version number */
return (EINVAL);
}
/*
* There are two ways a physical address can be obtained from a mem
* scheme FMRI. One way is to use the "offset" and "serial"
* members, if they are present, together with the "unum" member to
* calculate a physical address. This is the preferred way since
* it is independent of possible changes to the programming of
* underlying hardware registers that may change the physical address.
* If the "offset" member is not present, then the address is
* retrieved from the "physaddr" member.
*/
#ifdef __sparc
0) {
return (EINVAL);
}
&nserids) != 0) {
return (EINVAL);
} else {
return (err);
}
#else /* __i386, __amd64 */
return (EINVAL);
#endif /* __sparc */
return (0);
}