#include <sys/param.h>
#include <sys/fcntl.h>
#include <sys/malloc.h>
#include <sys/namei.h>
#include <sys/proc.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/vnode.h>
#include <sys/vm.h>
#include <mach/kern_return.h>
extern void kmem_free(vm_map_t, vm_offset_t, vm_size_t);
extern kern_return_t kmem_alloc_wired(vm_map_t, vm_offset_t *, vm_size_t);
static off_t imagesizelimit = (4 * 4096);
static int image_bits = 32;
static char *image_pathname = NULL;
static size_t image_pathlen = 0;
static vm_offset_t image_ptr = NULL;
static off_t image_size = 0;
__private_extern__ void
get_panicimage(vm_offset_t *imageptr, vm_size_t *imagesize, int *imagebits)
{
*imageptr = image_ptr;
*imagesize = image_size;
*imagebits = image_bits;
}
static int
panicimage_from_file(
char *imname,
off_t sizelimit,
vm_offset_t *image,
off_t *filesize,
struct proc *p)
{
int error = 0;
int error1 = 0;
int aresid;
struct nameidata nd;
struct vattr vattr;
struct vnode * vp;
kern_return_t kret;
struct pcred *pcred = p->p_cred;
struct ucred *cred = pcred->pc_ucred;
vm_offset_t iobuf;
NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, imname, p);
error = vn_open(&nd, FREAD, S_IRUSR);
if (error)
return (error);
vp = nd.ni_vp;
if (vp->v_type != VREG) {
error = EFAULT;
goto out;
}
error = VOP_GETATTR(vp, &vattr, cred, p);
if (error)
goto out;
if (vattr.va_size > sizelimit) {
error = EFBIG;
goto out;
}
kret = kmem_alloc_wired(kernel_map, &iobuf,
(vm_size_t)vattr.va_size);
if (kret != KERN_SUCCESS) {
switch (kret) {
default:
error = EINVAL;
break;
case KERN_NO_SPACE:
case KERN_RESOURCE_SHORTAGE:
error = ENOMEM;
break;
case KERN_PROTECTION_FAILURE:
error = EPERM;
break;
}
goto out;
}
error = vn_rdwr(UIO_READ, vp, (caddr_t)iobuf, (int)vattr.va_size,
(off_t)0, UIO_SYSSPACE, IO_NODELOCKED|IO_UNIT,
cred, &aresid, p);
if (error) {
(void)kmem_free(kernel_map, iobuf, (vm_size_t)vattr.va_size);
goto out;
}
*image = iobuf;
*filesize = (off_t)vattr.va_size;
out:
VOP_UNLOCK(vp, 0, p);
error1 = vn_close(vp, FREAD, cred, p);
if (error == 0)
error = error1;
return (error);
}
__private_extern__ int
sysctl_dopanicinfo(name, namelen, oldp, oldlenp, newp, newlen, p)
int *name;
u_int namelen;
void *oldp;
size_t *oldlenp;
void *newp;
size_t newlen;
struct proc *p;
{
int error = 0;
int bitdepth = 32;
char *imname;
if (namelen != 1)
return (ENOTDIR);
switch (name[0]) {
default:
return (EOPNOTSUPP);
case KERN_PANICINFO_MAXSIZE:
if (newp != NULL && (error = suser(p->p_ucred, &p->p_acflag)))
return (error);
error = sysctl_quad(oldp, oldlenp, newp, newlen, &imagesizelimit);
return (error);
case KERN_PANICINFO_IMAGE16:
bitdepth = 16;
case KERN_PANICINFO_IMAGE32:
MALLOC_ZONE(imname, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
if (!newp) {
bcopy(image_pathname, imname, image_pathlen);
imname[image_pathlen] = '\0';
} else
imname[0] = '\0';
error = sysctl_string(oldp, oldlenp, newp, newlen,
imname, MAXPATHLEN);
if (newp && !error) {
char *tmpstr, *oldstr;
off_t filesize = 0;
size_t len;
vm_offset_t image;
vm_offset_t oimage = NULL;
vm_size_t osize = 0;
len = strlen(imname);
oldstr = image_pathname;
error = panicimage_from_file(imname, imagesizelimit,
&image, &filesize, p);
if (error)
goto errout;
if (image_ptr) {
oimage = image_ptr;
osize = image_size;
}
image_ptr = image;
image_bits = bitdepth;
image_size = filesize;
if (oimage)
kmem_free(kernel_map, oimage, osize);
MALLOC(tmpstr, char *, len+1, M_TEMP, M_WAITOK);
bcopy(imname, tmpstr, len);
tmpstr[len] = '\0';
image_pathname = tmpstr;
image_pathlen = len;
FREE(oldstr, M_TEMP);
}
errout:
FREE_ZONE(imname, MAXPATHLEN, M_NAMEI);
return (error);
}
}