/* * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include #include #include #include #include #include #include #include #include #include /* prototypes not exported by osfmk. */ 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); /* Globals */ static off_t imagesizelimit = (4 * 4096); /* Information about the current panic image */ static int image_bits = 32; /* Bitdepth */ static char *image_pathname = NULL; /* path to it */ static size_t image_pathlen = 0; /* and the length of the pathname */ static vm_offset_t image_ptr = NULL; /* the image itself */ static off_t image_size = 0; /* and the imagesize */ __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; /* Open the file */ 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; } /* get the file size */ error = VOP_GETATTR(vp, &vattr, cred, p); if (error) goto out; /* validate the file size */ if (vattr.va_size > sizelimit) { error = EFBIG; goto out; } /* allocate kernel wired memory */ 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; } /* read the file in the kernel buffer */ 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; } /* * return the image to the caller * freeing this memory is callers responsibility */ *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; /* default is 32 bits */ char *imname; /* all sysctl names at this level are terminal */ if (namelen != 1) return (ENOTDIR); /* overloaded */ 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; /* and fall through */ case KERN_PANICINFO_IMAGE32: /* allocate a buffer for the image pathname */ 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; /* covariable: quiet compiler */ len = strlen(imname); oldstr = image_pathname; error = panicimage_from_file(imname, imagesizelimit, &image, &filesize, p); if (error) goto errout; /* release the old image */ if (image_ptr) { oimage = image_ptr; osize = image_size; } /* remember the new one */ image_ptr = image; image_bits = bitdepth; /* new bith depth */ image_size = filesize; /* new imagesize */ if (oimage) kmem_free(kernel_map, oimage, osize); /* save the new name */ MALLOC(tmpstr, char *, len+1, M_TEMP, M_WAITOK); bcopy(imname, tmpstr, len); tmpstr[len] = '\0'; image_pathname = tmpstr; /* new pathname */ image_pathlen = len; /* new pathname length */ /* free the old name */ FREE(oldstr, M_TEMP); } errout: FREE_ZONE(imname, MAXPATHLEN, M_NAMEI); return (error); } }