]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/kern/kern_panicinfo.c
xnu-344.tar.gz
[apple/xnu.git] / bsd / kern / kern_panicinfo.c
diff --git a/bsd/kern/kern_panicinfo.c b/bsd/kern/kern_panicinfo.c
new file mode 100644 (file)
index 0000000..62f79da
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * 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 <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>
+
+/* 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;
+                       vm_size_t osize;
+
+                       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);
+       }
+}