vserver 1.9.3
[linux-2.6.git] / arch / ia64 / kernel / unwind.c
index 6a715d8..69e095a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1999-2003 Hewlett-Packard Co
+ * Copyright (C) 1999-2004 Hewlett-Packard Co
  *     David Mosberger-Tang <davidm@hpl.hp.com>
  * Copyright (C) 2003 Fenghua Yu <fenghua.yu@intel.com>
  *     - Change pt_regs_off() to make it less dependant on pt_regs structure.
@@ -47,7 +47,6 @@
 #include "entry.h"
 #include "unwind_i.h"
 
-#define MIN(a,b)       ((a) < (b) ? (a) : (b))
 #define p5             5
 
 #define UNW_LOG_CACHE_SIZE     7       /* each unw_script is ~256 bytes in size */
@@ -89,6 +88,8 @@ static struct {
        /* list of unwind tables (one per load-module) */
        struct unw_table *tables;
 
+       unsigned long r0;                       /* constant 0 for r0 */
+
        /* table of registers that prologues can save (and order in which they're saved): */
        const unsigned char save_order[8];
 
@@ -239,7 +240,11 @@ static struct {
 #endif
 };
 
-/* Unwind accessors.  */
+static inline int
+read_only (void *addr)
+{
+       return (unsigned long) ((char *) addr - (char *) &unw.r0) < sizeof(unw.r0);
+}
 
 /*
  * Returns offset of rREG in struct pt_regs.
@@ -274,6 +279,8 @@ get_scratch_regs (struct unw_frame_info *info)
        return (struct pt_regs *) info->pt;
 }
 
+/* Unwind accessors.  */
+
 int
 unw_access_gr (struct unw_frame_info *info, int regnum, unsigned long *val, char *nat, int write)
 {
@@ -377,11 +384,16 @@ unw_access_gr (struct unw_frame_info *info, int regnum, unsigned long *val, char
        }
 
        if (write) {
-               *addr = *val;
-               if (*nat)
-                       *nat_addr |= nat_mask;
-               else
-                       *nat_addr &= ~nat_mask;
+               if (read_only(addr)) {
+                       UNW_DPRINT(0, "unwind.%s: ignoring attempt to write read-only location\n",
+                               __FUNCTION__);
+               } else {
+                       *addr = *val;
+                       if (*nat)
+                               *nat_addr |= nat_mask;
+                       else
+                               *nat_addr &= ~nat_mask;
+               }
        } else {
                if ((*nat_addr & nat_mask) == 0) {
                        *val = *addr;
@@ -420,7 +432,11 @@ unw_access_br (struct unw_frame_info *info, int regnum, unsigned long *val, int
                return -1;
        }
        if (write)
-               *addr = *val;
+               if (read_only(addr)) {
+                       UNW_DPRINT(0, "unwind.%s: ignoring attempt to write read-only location\n",
+                               __FUNCTION__);
+               } else
+                       *addr = *val;
        else
                *val = *addr;
        return 0;
@@ -430,7 +446,7 @@ EXPORT_SYMBOL(unw_access_br);
 int
 unw_access_fr (struct unw_frame_info *info, int regnum, struct ia64_fpreg *val, int write)
 {
-       struct ia64_fpreg *addr = 0;
+       struct ia64_fpreg *addr = NULL;
        struct pt_regs *pt;
 
        if ((unsigned) (regnum - 2) >= 126) {
@@ -465,7 +481,11 @@ unw_access_fr (struct unw_frame_info *info, int regnum, struct ia64_fpreg *val,
        }
 
        if (write)
-               *addr = *val;
+               if (read_only(addr)) {
+                       UNW_DPRINT(0, "unwind.%s: ignoring attempt to write read-only location\n",
+                               __FUNCTION__);
+               } else
+                       *addr = *val;
        else
                *val = *addr;
        return 0;
@@ -557,9 +577,13 @@ unw_access_ar (struct unw_frame_info *info, int regnum, unsigned long *val, int
                return -1;
        }
 
-       if (write)
-               *addr = *val;
-       else
+       if (write) {
+               if (read_only(addr)) {
+                       UNW_DPRINT(0, "unwind.%s: ignoring attempt to write read-only location\n",
+                               __FUNCTION__);
+               } else
+                       *addr = *val;
+       } else
                *val = *addr;
        return 0;
 }
@@ -574,9 +598,13 @@ unw_access_pr (struct unw_frame_info *info, unsigned long *val, int write)
        if (!addr)
                addr = &info->sw->pr;
 
-       if (write)
-               *addr = *val;
-       else
+       if (write) {
+               if (read_only(addr)) {
+                       UNW_DPRINT(0, "unwind.%s: ignoring attempt to write read-only location\n",
+                               __FUNCTION__);
+               } else
+                       *addr = *val;
+       } else
                *val = *addr;
        return 0;
 }
@@ -814,7 +842,7 @@ desc_prologue (int body, unw_word rlen, unsigned char mask, unsigned char grsave
                }
                sr->gr_save_loc = grsave;
                sr->any_spills = 0;
-               sr->imask = 0;
+               sr->imask = NULL;
                sr->spill_offset = 0x10;        /* default to psp+16 */
        }
 }
@@ -934,13 +962,13 @@ static inline void
 desc_mem_stack_f (unw_word t, unw_word size, struct unw_state_record *sr)
 {
        set_reg(sr->curr.reg + UNW_REG_PSP, UNW_WHERE_NONE,
-               sr->region_start + MIN((int)t, sr->region_len - 1), 16*size);
+               sr->region_start + min_t(int, t, sr->region_len - 1), 16*size);
 }
 
 static inline void
 desc_mem_stack_v (unw_word t, struct unw_state_record *sr)
 {
-       sr->curr.reg[UNW_REG_PSP].when = sr->region_start + MIN((int)t, sr->region_len - 1);
+       sr->curr.reg[UNW_REG_PSP].when = sr->region_start + min_t(int, t, sr->region_len - 1);
 }
 
 static inline void
@@ -976,7 +1004,7 @@ desc_reg_when (unsigned char regnum, unw_word t, struct unw_state_record *sr)
 
        if (reg->where == UNW_WHERE_NONE)
                reg->where = UNW_WHERE_GR_SAVE;
-       reg->when = sr->region_start + MIN((int)t, sr->region_len - 1);
+       reg->when = sr->region_start + min_t(int, t, sr->region_len - 1);
 }
 
 static inline void
@@ -1044,7 +1072,7 @@ desc_label_state (unw_word label, struct unw_state_record *sr)
 static inline int
 desc_is_active (unsigned char qp, unw_word t, struct unw_state_record *sr)
 {
-       if (sr->when_target <= sr->region_start + MIN((int)t, sr->region_len - 1))
+       if (sr->when_target <= sr->region_start + min_t(int, t, sr->region_len - 1))
                return 0;
        if (qp > 0) {
                if ((sr->pr_val & (1UL << qp)) == 0)
@@ -1085,7 +1113,7 @@ desc_spill_reg_p (unsigned char qp, unw_word t, unsigned char abreg, unsigned ch
 
        r = sr->curr.reg + decode_abreg(abreg, 0);
        r->where = where;
-       r->when = sr->region_start + MIN((int)t, sr->region_len - 1);
+       r->when = sr->region_start + min_t(int, t, sr->region_len - 1);
        r->val = (ytreg & 0x7f);
 }
 
@@ -1100,7 +1128,7 @@ desc_spill_psprel_p (unsigned char qp, unw_word t, unsigned char abreg, unw_word
 
        r = sr->curr.reg + decode_abreg(abreg, 1);
        r->where = UNW_WHERE_PSPREL;
-       r->when = sr->region_start + MIN((int)t, sr->region_len - 1);
+       r->when = sr->region_start + min_t(int, t, sr->region_len - 1);
        r->val = 0x10 - 4*pspoff;
 }
 
@@ -1115,7 +1143,7 @@ desc_spill_sprel_p (unsigned char qp, unw_word t, unsigned char abreg, unw_word
 
        r = sr->curr.reg + decode_abreg(abreg, 1);
        r->where = UNW_WHERE_SPREL;
-       r->when = sr->region_start + MIN((int)t, sr->region_len - 1);
+       r->when = sr->region_start + min_t(int, t, sr->region_len - 1);
        r->val = 4*spoff;
 }
 
@@ -1177,7 +1205,7 @@ desc_spill_sprel_p (unsigned char qp, unw_word t, unsigned char abreg, unw_word
 static inline unw_hash_index_t
 hash (unsigned long ip)
 {
-#      define hashmagic        0x9e3779b97f4a7c16      /* based on (sqrt(5)/2-1)*2^64 */
+#      define hashmagic        0x9e3779b97f4a7c16UL    /* based on (sqrt(5)/2-1)*2^64 */
 
        return (ip >> 4)*hashmagic >> (64 - UNW_LOG_HASH_SIZE);
 #undef hashmagic
@@ -1202,7 +1230,7 @@ script_lookup (struct unw_frame_info *info)
        unsigned long ip, pr;
 
        if (UNW_DEBUG_ON(0))
-               return 0;       /* Always regenerate scripts in debug mode */
+               return NULL;    /* Always regenerate scripts in debug mode */
 
        STAT(++unw.stat.cache.lookups);
 
@@ -1216,7 +1244,7 @@ script_lookup (struct unw_frame_info *info)
 
        index = unw.hash[hash(ip)];
        if (index >= UNW_CACHE_SIZE)
-               return 0;
+               return NULL;
 
        script = unw.cache + index;
        while (1) {
@@ -1227,7 +1255,7 @@ script_lookup (struct unw_frame_info *info)
                        return script;
                }
                if (script->coll_chain >= UNW_HASH_SIZE)
-                       return 0;
+                       return NULL;
                script = unw.cache + script->coll_chain;
                STAT(++unw.stat.cache.collision_chain_traversals);
        }
@@ -1277,7 +1305,7 @@ script_new (unsigned long ip)
                if (script->ip) {
                        index = hash(script->ip);
                        tmp = unw.cache + unw.hash[index];
-                       prev = 0;
+                       prev = NULL;
                        while (1) {
                                if (tmp == script) {
                                        if (prev)
@@ -1407,6 +1435,9 @@ compile_reg (struct unw_state_record *sr, int i, struct unw_script *script)
                                need_nat_info = 0;
                        }
                        val = unw.preg_index[UNW_REG_R4 + (rval - 4)];
+               } else if (rval == 0) {
+                       opc = UNW_INSN_MOVE_CONST;
+                       val = 0;
                } else {
                        /* register got spilled to a scratch register */
                        opc = UNW_INSN_MOVE_SCRATCH;
@@ -1479,7 +1510,7 @@ compile_reg (struct unw_state_record *sr, int i, struct unw_script *script)
 static inline const struct unw_table_entry *
 lookup (struct unw_table *table, unsigned long rel_ip)
 {
-       const struct unw_table_entry *e = 0;
+       const struct unw_table_entry *e = NULL;
        unsigned long lo, hi, mid;
 
        /* do a binary search for right entry: */
@@ -1505,8 +1536,8 @@ lookup (struct unw_table *table, unsigned long rel_ip)
 static inline struct unw_script *
 build_script (struct unw_frame_info *info)
 {
-       const struct unw_table_entry *e = 0;
-       struct unw_script *script = 0;
+       const struct unw_table_entry *e = NULL;
+       struct unw_script *script = NULL;
        struct unw_labeled_state *ls, *next;
        unsigned long ip = info->ip;
        struct unw_state_record sr;
@@ -1531,7 +1562,7 @@ build_script (struct unw_frame_info *info)
        if (!script) {
                UNW_DPRINT(0, "unwind.%s: failed to create unwind script\n",  __FUNCTION__);
                STAT(unw.stat.script.build_time += ia64_get_itc() - start);
-               return 0;
+               return NULL;
        }
        unw.cache[info->prev_script].hint = script - unw.cache;
 
@@ -1729,6 +1760,17 @@ run_script (struct unw_script *script, struct unw_frame_info *state)
                        }
                        break;
 
+                     case UNW_INSN_MOVE_CONST:
+                       if (val == 0)
+                               s[dst] = (unsigned long) &unw.r0;
+                       else {
+                               s[dst] = 0;
+                               UNW_DPRINT(0, "unwind.%s: UNW_INSN_MOVE_CONST bad val=%ld\n",
+                                          __FUNCTION__, val);
+                       }
+                       break;
+
+
                      case UNW_INSN_MOVE_STACKED:
                        s[dst] = (unsigned long) ia64_rse_skip_regs((unsigned long *)state->bsp,
                                                                    val);
@@ -1793,7 +1835,7 @@ find_save_locs (struct unw_frame_info *info)
                /* don't let obviously bad addresses pollute the cache */
                /* FIXME: should really be level 0 but it occurs too often. KAO */
                UNW_DPRINT(1, "unwind.%s: rejecting bad ip=0x%lx\n", __FUNCTION__, info->ip);
-               info->rp_loc = 0;
+               info->rp_loc = NULL;
                return -1;
        }
 
@@ -2050,12 +2092,12 @@ unw_add_unwind_table (const char *name, unsigned long segment_base, unsigned lon
        if (end - start <= 0) {
                UNW_DPRINT(0, "unwind.%s: ignoring attempt to insert empty unwind table\n",
                           __FUNCTION__);
-               return 0;
+               return NULL;
        }
 
        table = kmalloc(sizeof(*table), GFP_USER);
        if (!table)
-               return 0;
+               return NULL;
 
        init_unwind_table(table, name, segment_base, gp, table_start, table_end);
 
@@ -2257,7 +2299,7 @@ unw_init (void)
  *     EFAULT  BUF points outside your accessible address space.
  */
 asmlinkage long
-sys_getunwind (void *buf, size_t buf_size)
+sys_getunwind (void __user *buf, size_t buf_size)
 {
        if (buf && buf_size >= unw.gate_table_size)
                if (copy_to_user(buf, unw.gate_table, unw.gate_table_size) != 0)