Logo Search packages:      
Sourcecode: lcrash version File versions  Download package

kl_mem.c

/*
 * $Id: kl_mem.c,v 1.2 2005/02/23 01:09:12 tjm Exp $
 *
 * This file is part of libklib.
 * A library which provides access to Linux system kernel dumps.
 *
 * Created by Silicon Graphics, Inc.
 * Contributions by IBM, NEC, and others
 *
 * Copyright (C) 1999 - 2005 Silicon Graphics, Inc. All rights reserved.
 * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
 * Copyright 2000 Junichi Nomura, NEC Solutions <j-nomura@ce.jp.nec.com>
 *
 * This code is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version. See the file COPYING for more
 * information.
 */

#include <klib.h>


/* declarations of static functions
 */
static k_error_t _kl_seekmem(kl_dumpinfo_t*, kaddr_t);


/* function definitions
 */

/*
 * _kl_seekmem()
 * 
 * Seek through physical system memory or vmcore image to the offset
 * of a particular physical address.
 */
static k_error_t
_kl_seekmem(kl_dumpinfo_t *dip, kaddr_t paddr)
{
      off_t off;

      kl_reset_error();
      if ((off = lseek(dip->core_fd, paddr, SEEK_SET)) == -1) {
            KL_ERROR = KLE_INVALID_LSEEK;
      }
      return(KL_ERROR);
}


/*
 * kl_readmem()
 *
 * Read a 'size' block from physical address 'addr' in the system 
 * memory image.
 */
k_error_t
kl_readmem(kaddr_t addr, unsigned size, void *buffer)
{
      int sz, bytes;
      kl_dumpinfo_t *dip;
      void *ptr;

      kl_reset_error();
      if ((dip = KLP->dump)) {
            if (CORE_IS_KMEM) {
                        /* For live system reads, we have to make sure
                         * that the memory we are trying to access is
                         * valid (there are some systems where trying
                         * to read non-existant memory brings the system
                         * down).
                         */
                        if (kl_valid_physmem(addr, size)) {
                                (void)_kl_seekmem(dip, addr);
                        }
                  if (!KL_ERROR) {
                        bytes = 0;
                        ptr = buffer;
                        while (bytes < size) {
                              sz = read(dip->core_fd, 
                                    ptr, (size - bytes));
                              if (sz == -1) {
                                    KL_ERROR = KLE_INVALID_READ;
                                    break;
                              }
                              bytes += sz;
                              ptr += sz;
                        }
                  } 
            } else if(dip->core_type == s390_core) {
                        if(!(_kl_seekmem(dip, addr +
                               KL_DUMP_HEADER_SZ_S390SA))){
                                if (read(dip->core_fd, buffer, size) != size) {
                                        KL_ERROR = KLE_INVALID_READ;
                                }
                        }
                } else {
                  if (kl_cmpreadmem(dip->core_fd, addr, buffer,
                        size, 0) <= 0) {
                        if (!KL_ERROR) {
                              KL_ERROR = KLE_INVALID_READ;
                        }
                  }
            }
      } else {
            KL_ERROR = KLE_BAD_KERNINFO;
      }
      return(KL_ERROR);
}


/*
 * kl_virtop()
 *
 *   Translate a virtual address into a physical address. 
 */
int
kl_virtop(kaddr_t vaddr, void *m, kaddr_t *paddr)
{
      int mm_alloced = 0;
      void *mmp = m;

      *paddr = (kaddr_t) NULL;

      kl_reset_error();

      if (!mmp && KL_KADDR_IS_PHYSICAL(vaddr)) {
            *paddr = (vaddr - KL_PAGE_OFFSET);
      } else if (mmp || KL_INIT_MM) {
            /* Treat address as logical and map to a physical one */
            if (!mmp) {
                  if((mmp = kl_alloc_block(MM_STRUCT_SZ, K_TEMP))) {
                        kl_readmem(KL_INIT_MM, MM_STRUCT_SZ, mmp);
                        if (KL_ERROR) {
                              kl_free_block(mmp);
                              mmp = NULL;
                        } else {
                              mm_alloced++;
                        }
                  }
            }
            if (mmp) {
                  *paddr = KL_MMAP_VIRTOP(vaddr, mmp);
                  if(KL_ERROR){
                        KL_ERROR = KLE_INVALID_MAPPING;
                  }
            }
      } else {
            /* Treat as a physical address but make sure
             * the address does not exceed maximum physical
             * memory.
             */
            if(vaddr > KL_PAGE_OFFSET){
                  vaddr -= KL_PAGE_OFFSET;
            }
            if ((vaddr >> KL_PAGE_SHIFT) < NUM_PHYSPAGES) {
                  *paddr = vaddr;
            } else {
                  KL_ERROR = KLE_INVALID_PADDR;
            }
      }

      if (mm_alloced) {
            kl_free_block(mmp);
      }     

      if(KL_ERROR){
            *paddr = (kaddr_t) NULL;
            return(1);
      } else {
            return(0);
      }
}


/*
 * kl_get_block()
 * 
 *   Read a size block from virtual address addr in the system memory image.
 */
k_error_t
kl_get_block(kaddr_t addr, unsigned size, void *bp, void *mmap)
{
      kaddr_t paddr;
      size_t s;
      kaddr_t vaddr=addr;

      if (!bp) {
            KL_ERROR = KLE_NULL_BUFF;
      } else if (!size) {
            KL_ERROR = KLE_ZERO_SIZE;
#ifdef DUMP_ARCH_IA64
      } else if(ARCH_IS_IA64(KL_ARCH)) {
            while (size > 0){
                  kaddr_t tmp = vaddr;
                  int got_block = 0;

                  s = ((vaddr & KL_PAGE_MASK) | (~KL_PAGE_MASK)) - 
                        vaddr + 1;
                  s = (size > s) ? s : size;
                  vaddr = KL_FIX_VADDR(vaddr, s);
                  if (KL_KADDR_IS_HIGHMEM(vaddr) && 
                        KLP && CORE_IS_KMEM) {
                              kl_readkmem(vaddr, s, bp);
                              if(KL_ERROR) {
                                    /*
                                     * SGI ia64 kernel doesn't map 
                                     * ia64_boot_param in 
                                     * /dev/kmem or /proc/kcore.
                                     *
                                     * Try again with physical memory.
                                     */
                                    kl_reset_error();
                              } else {
                                    got_block = 1;
                              }
                  } 
                  if (!got_block) {
                        if ( KL_VIRTOP(vaddr, mmap, &paddr) ) {
                              return(KL_ERROR);
                        }
                        kl_readmem(paddr, s, bp);
                  }
                  vaddr = tmp;
                  size=size - s;
                  vaddr=vaddr + s;
                  bp=bp + s;
            }
#endif
      } else {
            while (size > 0){
                  s=((vaddr & KL_PAGE_MASK) | (~KL_PAGE_MASK)) - 
                        vaddr + 1;
                  s= (size > s) ? s : size;
                  vaddr = KL_FIX_VADDR(vaddr, s);
                  if(KL_VIRTOP(vaddr, mmap, &paddr) ) {
                        return(KL_ERROR);
                  }
                  if (KLP && (CORE_IS_KMEM) &&
                        (paddr >= (KL_HIGH_MEMORY - KL_PAGE_OFFSET))) {
                        /*
                         * At present, /dev/mem supports only 
                         * virtual memory mapping into low mem.
                         * Hence, /dev/kmem will be used for highmem.
                         */
                        if (kl_readkmem(vaddr, s, bp)) {
                              return(KL_ERROR);
                        }
                  } else {
                        if(kl_readmem(paddr, s, bp)){
                              return(KL_ERROR);
                        }
                  }
                  size=size - s;
                  vaddr=vaddr + s;
                  bp=bp + s;
            }
            return(0);
      }
      return(KL_ERROR);
}


/*
 * kl_kaddr_to_ptr() 
 *
 *   Return the pointer stored at kernel address 'k'
 */
kaddr_t
kl_kaddr_to_ptr(kaddr_t k)
{
      kaddr_t k1;

      k1 = KL_VREAD_PTR(k);
      if (KL_ERROR) {
            return((kaddr_t)0);
      } else {
            return(k1);
      }
}


/*
 * kl_uint() -- Return an unsigned integer value stored in a structure
 *
 *    Pointer 'p' points to a buffer that contains a kernel structure
 *    of type 's.' If the size of member 'm' is less than eight bytes
 *    then right shift the value the appropriate number of bytes so that
 *    it lines up properly in an uint64_t space. Return the resulting
 *    value.
 */
uint64_t
kl_uint(void *p, char *s, char *m, unsigned int offset)
{
      int nbytes;
      uint64_t v;
      void *source;

      if (!(nbytes = kl_member_size(s, m))) {
            KL_ERROR = KLE_BAD_FIELD;
            return((uint64_t)0);
      }
      source = (K_PTR(p, s, m) + offset);
      switch(nbytes) {
            case 1:
                  v = KL_GET_UINT8(source);
                  break;
            case 2:
                  v = KL_GET_UINT16(source);
                  break;
            case 4:
                  v = KL_GET_UINT32(source);
                  break;
            case 8:
                  v = KL_GET_UINT64(source);
                  break;
            default:
                  KL_ERROR = KLE_BAD_FIELD;
                  return((uint64_t)0);
      }
      return(v);
}


/*
 * kl_int() -- Return a signed integer value stored in a structure
 *
 *    Pointer 'p' points to a buffer that contains a kernel structure
 *    of type 's.' If the size of member 'm' is less than eight bytes
 *    then right shift the value the appropriate number of bytes so that
 *    it lines up properly in an uint64_t space. Return the resulting
 *    value.
 */
int64_t
kl_int(void *p, char *s, char *m, unsigned offset)
{
      int nbytes;
      int64_t v;
      void *source;

      if (!(nbytes = kl_member_size(s, m))) {
            KL_ERROR = KLE_BAD_FIELD;
            return((int64_t)0);
      }
      source = (void *)(K_ADDR(p, s, m) + offset);
      switch(nbytes) {
            case 1:
                  v = KL_GET_INT8(source);
                  break;
            case 2:
                  v = KL_GET_INT16(source);
                  break;
            case 4:
                  v = KL_GET_INT32(source);
                  break;
            case 8:
                  v = KL_GET_INT64(source);
                  break;
            default:
                  KL_ERROR = KLE_BAD_FIELD;
                  return((int64_t)0);
      }
      return(v);
}


/*
 * kl_kaddr() -- Return a kernel virtual address stored in a structure
 *
 *   Pointer 'p' points to a buffer that contains a kernel structure
 *   of type 's.' Get the kernel address located in member 'm.' 
 */
kaddr_t
kl_kaddr(void *p, char *s, char *m)
{
      kaddr_t k;

      k = KL_GET_PTR(K_PTR(p,s,m));
      return(k);
}


/*
 * kl_is_valid_kaddr();
 */
int
kl_is_valid_kaddr(kaddr_t addr, void *mmap, int flags)
{
      kaddr_t dummy;
      kl_reset_error();

      KL_VIRTOP(addr, mmap, &dummy);
      if (KL_ERROR) {
            return(0);
      }
      if ((flags & WORD_ALIGN_FLAG) && (addr % KL_NBPW)) {
            KL_ERROR = KLE_INVALID_VADDR_ALIGN;
            return(0);
      }
      return(1);
}

/*
 * Name: kl_readmem()
 * Func: Read a 'size' block from virtual address 'addr' in the system 
 *       memory image.
 */
k_error_t
kl_readkmem(kaddr_t addr, unsigned size, void *buffer)
{
      kl_dumpinfo_t *dip;
      int fd;
      ssize_t readcnt;

      kl_reset_error();

      dip = KLP->dump;
      if (dip && CORE_IS_KMEM) {
            if ((fd = open("/dev/kmem", O_RDONLY)) < 0)
                  return KLE_INVALID_READ; 

            if (lseek(fd, addr, SEEK_SET) == -1) {
                  close(fd);
                  return KLE_INVALID_READ;
            }

            readcnt = read(fd, buffer, size);
            if (readcnt != size)
                  KL_ERROR = KLE_INVALID_READ;

            close(fd);
      } else {
            KL_ERROR = KLE_INVALID_READ;
      }
      return(KL_ERROR);
}

Generated by  Doxygen 1.6.0   Back to index