Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 111133
b: refs/heads/master
c: 6aa3524
h: refs/heads/master
i:
  111131: 0f7416c
v: v3
  • Loading branch information
David Daney authored and Ralf Baechle committed Oct 11, 2008
1 parent ea49922 commit c719265
Show file tree
Hide file tree
Showing 7 changed files with 250 additions and 2 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 8192c9ea9ac44213d1266ecb64615519443979b3
refs/heads/master: 6aa3524c182c01b8b8b7c21c4da20c742a9b4d86
6 changes: 6 additions & 0 deletions trunk/arch/mips/include/asm/cpu-info.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#ifndef __ASM_CPU_INFO_H
#define __ASM_CPU_INFO_H

#include <linux/types.h>

#include <asm/cache.h>

/*
Expand Down Expand Up @@ -69,6 +71,10 @@ struct cpuinfo_mips {
int tc_id; /* Thread Context number */
#endif
void *data; /* Additional data */
unsigned int watch_reg_count; /* Number that exist */
unsigned int watch_reg_use_cnt; /* Usable by ptrace */
#define NUM_WATCH_REGS 4
u16 watch_reg_masks[NUM_WATCH_REGS];
} __attribute__((aligned(SMP_CACHE_BYTES)));

extern struct cpuinfo_mips cpu_data[];
Expand Down
20 changes: 20 additions & 0 deletions trunk/arch/mips/include/asm/processor.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,19 @@ struct mips_dsp_state {
{0,} \
}

struct mips3264_watch_reg_state {
/* The width of watchlo is 32 in a 32 bit kernel and 64 in a
64 bit kernel. We use unsigned long as it has the same
property. */
unsigned long watchlo[NUM_WATCH_REGS];
/* Only the mask and IRW bits from watchhi. */
u16 watchhi[NUM_WATCH_REGS];
};

union mips_watch_reg_state {
struct mips3264_watch_reg_state mips3264;
};

typedef struct {
unsigned long seg;
} mm_segment_t;
Expand Down Expand Up @@ -137,6 +150,9 @@ struct thread_struct {
/* Saved state of the DSP ASE, if available. */
struct mips_dsp_state dsp;

/* Saved watch register state, if available. */
union mips_watch_reg_state watch;

/* Other stuff associated with the thread. */
unsigned long cp0_badvaddr; /* Last user fault */
unsigned long cp0_baduaddr; /* Last kernel fault accessing USEG */
Expand Down Expand Up @@ -192,6 +208,10 @@ struct thread_struct {
.dspr = {0, }, \
.dspcontrol = 0, \
}, \
/* \
* saved watch register stuff \
*/ \
.watch = {{{0,},},}, \
/* \
* Other stuff associated with the process \
*/ \
Expand Down
2 changes: 2 additions & 0 deletions trunk/arch/mips/include/asm/thread_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ register struct thread_info *__current_thread_info __asm__("$28");
#define TIF_32BIT_REGS 22 /* also implies 16/32 fprs */
#define TIF_32BIT_ADDR 23 /* 32-bit address space (o32/n32) */
#define TIF_FPUBOUND 24 /* thread bound to FPU-full CPU set */
#define TIF_LOAD_WATCH 25 /* If set, load watch registers */
#define TIF_SYSCALL_TRACE 31 /* syscall trace active */

#define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE)
Expand All @@ -140,6 +141,7 @@ register struct thread_info *__current_thread_info __asm__("$28");
#define _TIF_32BIT_REGS (1<<TIF_32BIT_REGS)
#define _TIF_32BIT_ADDR (1<<TIF_32BIT_ADDR)
#define _TIF_FPUBOUND (1<<TIF_FPUBOUND)
#define _TIF_LOAD_WATCH (1<<TIF_LOAD_WATCH)

/* work to do on interrupt/exception return */
#define _TIF_WORK_MASK (0x0000ffef & ~_TIF_SECCOMP)
Expand Down
32 changes: 32 additions & 0 deletions trunk/arch/mips/include/asm/watch.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2008 David Daney
*/
#ifndef _ASM_WATCH_H
#define _ASM_WATCH_H

#include <linux/bitops.h>

#include <asm/mipsregs.h>

void mips_install_watch_registers(void);
void mips_read_watch_registers(void);
void mips_clear_watch_registers(void);
void mips_probe_watch_registers(struct cpuinfo_mips *c);

#ifdef CONFIG_HARDWARE_WATCHPOINTS
#define __restore_watch() do { \
if (unlikely(test_bit(TIF_LOAD_WATCH, \
&current_thread_info()->flags))) { \
mips_install_watch_registers(); \
} \
} while (0)

#else
#define __restore_watch() do {} while (0)
#endif

#endif /* _ASM_WATCH_H */
2 changes: 1 addition & 1 deletion trunk/arch/mips/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ extra-y := head.o init_task.o vmlinux.lds

obj-y += cpu-probe.o branch.o entry.o genex.o irq.o process.o \
ptrace.o reset.o setup.o signal.o syscall.o \
time.o topology.o traps.o unaligned.o
time.o topology.o traps.o unaligned.o watch.o

obj-$(CONFIG_CEVT_BCM1480) += cevt-bcm1480.o
obj-$(CONFIG_CEVT_R4K) += cevt-r4k.o
Expand Down
188 changes: 188 additions & 0 deletions trunk/arch/mips/kernel/watch.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 2008 David Daney
*/

#include <linux/sched.h>

#include <asm/processor.h>
#include <asm/watch.h>

/*
* Install the watch registers for the current thread. A maximum of
* four registers are installed although the machine may have more.
*/
void mips_install_watch_registers(void)
{
struct mips3264_watch_reg_state *watches =
&current->thread.watch.mips3264;
switch (current_cpu_data.watch_reg_use_cnt) {
default:
BUG();
case 4:
write_c0_watchlo3(watches->watchlo[3]);
/* Write 1 to the I, R, and W bits to clear them, and
1 to G so all ASIDs are trapped. */
write_c0_watchhi3(0x40000007 | watches->watchhi[3]);
case 3:
write_c0_watchlo2(watches->watchlo[2]);
write_c0_watchhi2(0x40000007 | watches->watchhi[2]);
case 2:
write_c0_watchlo1(watches->watchlo[1]);
write_c0_watchhi1(0x40000007 | watches->watchhi[1]);
case 1:
write_c0_watchlo0(watches->watchlo[0]);
write_c0_watchhi0(0x40000007 | watches->watchhi[0]);
}
}

/*
* Read back the watchhi registers so the user space debugger has
* access to the I, R, and W bits. A maximum of four registers are
* read although the machine may have more.
*/
void mips_read_watch_registers(void)
{
struct mips3264_watch_reg_state *watches =
&current->thread.watch.mips3264;
switch (current_cpu_data.watch_reg_use_cnt) {
default:
BUG();
case 4:
watches->watchhi[3] = (read_c0_watchhi3() & 0x0fff);
case 3:
watches->watchhi[2] = (read_c0_watchhi2() & 0x0fff);
case 2:
watches->watchhi[1] = (read_c0_watchhi1() & 0x0fff);
case 1:
watches->watchhi[0] = (read_c0_watchhi0() & 0x0fff);
}
if (current_cpu_data.watch_reg_use_cnt == 1 &&
(watches->watchhi[0] & 7) == 0) {
/* Pathological case of release 1 architecture that
* doesn't set the condition bits. We assume that
* since we got here, the watch condition was met and
* signal that the conditions requested in watchlo
* were met. */
watches->watchhi[0] |= (watches->watchlo[0] & 7);
}
}

/*
* Disable all watch registers. Although only four registers are
* installed, all are cleared to eliminate the possibility of endless
* looping in the watch handler.
*/
void mips_clear_watch_registers(void)
{
switch (current_cpu_data.watch_reg_count) {
default:
BUG();
case 8:
write_c0_watchlo7(0);
case 7:
write_c0_watchlo6(0);
case 6:
write_c0_watchlo5(0);
case 5:
write_c0_watchlo4(0);
case 4:
write_c0_watchlo3(0);
case 3:
write_c0_watchlo2(0);
case 2:
write_c0_watchlo1(0);
case 1:
write_c0_watchlo0(0);
}
}

__cpuinit void mips_probe_watch_registers(struct cpuinfo_mips *c)
{
unsigned int t;

if ((c->options & MIPS_CPU_WATCH) == 0)
return;
/*
* Check which of the I,R and W bits are supported, then
* disable the register.
*/
write_c0_watchlo0(7);
t = read_c0_watchlo0();
write_c0_watchlo0(0);
c->watch_reg_masks[0] = t & 7;

/* Write the mask bits and read them back to determine which
* can be used. */
c->watch_reg_count = 1;
c->watch_reg_use_cnt = 1;
t = read_c0_watchhi0();
write_c0_watchhi0(t | 0xff8);
t = read_c0_watchhi0();
c->watch_reg_masks[0] |= (t & 0xff8);
if ((t & 0x80000000) == 0)
return;

write_c0_watchlo1(7);
t = read_c0_watchlo1();
write_c0_watchlo1(0);
c->watch_reg_masks[1] = t & 7;

c->watch_reg_count = 2;
c->watch_reg_use_cnt = 2;
t = read_c0_watchhi1();
write_c0_watchhi1(t | 0xff8);
t = read_c0_watchhi1();
c->watch_reg_masks[1] |= (t & 0xff8);
if ((t & 0x80000000) == 0)
return;

write_c0_watchlo2(7);
t = read_c0_watchlo2();
write_c0_watchlo2(0);
c->watch_reg_masks[2] = t & 7;

c->watch_reg_count = 3;
c->watch_reg_use_cnt = 3;
t = read_c0_watchhi2();
write_c0_watchhi2(t | 0xff8);
t = read_c0_watchhi2();
c->watch_reg_masks[2] |= (t & 0xff8);
if ((t & 0x80000000) == 0)
return;

write_c0_watchlo3(7);
t = read_c0_watchlo3();
write_c0_watchlo3(0);
c->watch_reg_masks[3] = t & 7;

c->watch_reg_count = 4;
c->watch_reg_use_cnt = 4;
t = read_c0_watchhi3();
write_c0_watchhi3(t | 0xff8);
t = read_c0_watchhi3();
c->watch_reg_masks[3] |= (t & 0xff8);
if ((t & 0x80000000) == 0)
return;

/* We use at most 4, but probe and report up to 8. */
c->watch_reg_count = 5;
t = read_c0_watchhi4();
if ((t & 0x80000000) == 0)
return;

c->watch_reg_count = 6;
t = read_c0_watchhi5();
if ((t & 0x80000000) == 0)
return;

c->watch_reg_count = 7;
t = read_c0_watchhi6();
if ((t & 0x80000000) == 0)
return;

c->watch_reg_count = 8;
}

0 comments on commit c719265

Please sign in to comment.