Merge branch 'misc' into devel
Conflicts: arch/arm/Kconfig
This commit is contained in:
@@ -583,6 +583,11 @@ void __init pci_common_init(struct hw_pci *hw)
|
||||
* Assign resources.
|
||||
*/
|
||||
pci_bus_assign_resources(bus);
|
||||
|
||||
/*
|
||||
* Enable bridges
|
||||
*/
|
||||
pci_enable_bridges(bus);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@@ -25,83 +25,6 @@
|
||||
* machine ID for example).
|
||||
*/
|
||||
__HEAD
|
||||
__error_a:
|
||||
#ifdef CONFIG_DEBUG_LL
|
||||
mov r4, r1 @ preserve machine ID
|
||||
adr r0, str_a1
|
||||
bl printascii
|
||||
mov r0, r4
|
||||
bl printhex8
|
||||
adr r0, str_a2
|
||||
bl printascii
|
||||
adr r3, __lookup_machine_type_data
|
||||
ldmia r3, {r4, r5, r6} @ get machine desc list
|
||||
sub r4, r3, r4 @ get offset between virt&phys
|
||||
add r5, r5, r4 @ convert virt addresses to
|
||||
add r6, r6, r4 @ physical address space
|
||||
1: ldr r0, [r5, #MACHINFO_TYPE] @ get machine type
|
||||
bl printhex8
|
||||
mov r0, #'\t'
|
||||
bl printch
|
||||
ldr r0, [r5, #MACHINFO_NAME] @ get machine name
|
||||
add r0, r0, r4
|
||||
bl printascii
|
||||
mov r0, #'\n'
|
||||
bl printch
|
||||
add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc
|
||||
cmp r5, r6
|
||||
blo 1b
|
||||
adr r0, str_a3
|
||||
bl printascii
|
||||
b __error
|
||||
ENDPROC(__error_a)
|
||||
|
||||
str_a1: .asciz "\nError: unrecognized/unsupported machine ID (r1 = 0x"
|
||||
str_a2: .asciz ").\n\nAvailable machine support:\n\nID (hex)\tNAME\n"
|
||||
str_a3: .asciz "\nPlease check your kernel config and/or bootloader.\n"
|
||||
.align
|
||||
#else
|
||||
b __error
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Lookup machine architecture in the linker-build list of architectures.
|
||||
* Note that we can't use the absolute addresses for the __arch_info
|
||||
* lists since we aren't running with the MMU on (and therefore, we are
|
||||
* not in the correct address space). We have to calculate the offset.
|
||||
*
|
||||
* r1 = machine architecture number
|
||||
* Returns:
|
||||
* r3, r4, r6 corrupted
|
||||
* r5 = mach_info pointer in physical address space
|
||||
*/
|
||||
__lookup_machine_type:
|
||||
adr r3, __lookup_machine_type_data
|
||||
ldmia r3, {r4, r5, r6}
|
||||
sub r3, r3, r4 @ get offset between virt&phys
|
||||
add r5, r5, r3 @ convert virt addresses to
|
||||
add r6, r6, r3 @ physical address space
|
||||
1: ldr r3, [r5, #MACHINFO_TYPE] @ get machine type
|
||||
teq r3, r1 @ matches loader number?
|
||||
beq 2f @ found
|
||||
add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc
|
||||
cmp r5, r6
|
||||
blo 1b
|
||||
mov r5, #0 @ unknown machine
|
||||
2: mov pc, lr
|
||||
ENDPROC(__lookup_machine_type)
|
||||
|
||||
/*
|
||||
* Look in arch/arm/kernel/arch.[ch] for information about the
|
||||
* __arch_info structures.
|
||||
*/
|
||||
.align 2
|
||||
.type __lookup_machine_type_data, %object
|
||||
__lookup_machine_type_data:
|
||||
.long .
|
||||
.long __arch_info_begin
|
||||
.long __arch_info_end
|
||||
.size __lookup_machine_type_data, . - __lookup_machine_type_data
|
||||
|
||||
/* Determine validity of the r2 atags pointer. The heuristic requires
|
||||
* that the pointer be aligned, in the first 16k of physical RAM and
|
||||
@@ -109,8 +32,6 @@ __lookup_machine_type_data:
|
||||
* of this function may be more lenient with the physical address and
|
||||
* may also be able to move the ATAGS block if necessary.
|
||||
*
|
||||
* r8 = machinfo
|
||||
*
|
||||
* Returns:
|
||||
* r2 either valid atags pointer, or zero
|
||||
* r5, r6 corrupted
|
||||
@@ -184,17 +105,6 @@ __mmap_switched_data:
|
||||
.long init_thread_union + THREAD_START_SP @ sp
|
||||
.size __mmap_switched_data, . - __mmap_switched_data
|
||||
|
||||
/*
|
||||
* This provides a C-API version of __lookup_machine_type
|
||||
*/
|
||||
ENTRY(lookup_machine_type)
|
||||
stmfd sp!, {r4 - r6, lr}
|
||||
mov r1, r0
|
||||
bl __lookup_machine_type
|
||||
mov r0, r5
|
||||
ldmfd sp!, {r4 - r6, pc}
|
||||
ENDPROC(lookup_machine_type)
|
||||
|
||||
/*
|
||||
* This provides a C-API version of __lookup_processor_type
|
||||
*/
|
||||
|
@@ -44,9 +44,6 @@ ENTRY(stext)
|
||||
bl __lookup_processor_type @ r5=procinfo r9=cpuid
|
||||
movs r10, r5 @ invalid processor (r5=0)?
|
||||
beq __error_p @ yes, error 'p'
|
||||
bl __lookup_machine_type @ r5=machinfo
|
||||
movs r8, r5 @ invalid machine (r5=0)?
|
||||
beq __error_a @ yes, error 'a'
|
||||
|
||||
adr lr, BSYM(__after_proc_init) @ return (PIC) address
|
||||
ARM( add pc, r10, #PROCINFO_INITFUNC )
|
||||
|
@@ -87,14 +87,10 @@ ENTRY(stext)
|
||||
movs r10, r5 @ invalid processor (r5=0)?
|
||||
THUMB( it eq ) @ force fixup-able long branch encoding
|
||||
beq __error_p @ yes, error 'p'
|
||||
bl __lookup_machine_type @ r5=machinfo
|
||||
movs r8, r5 @ invalid machine (r5=0)?
|
||||
THUMB( it eq ) @ force fixup-able long branch encoding
|
||||
beq __error_a @ yes, error 'a'
|
||||
|
||||
/*
|
||||
* r1 = machine no, r2 = atags,
|
||||
* r8 = machinfo, r9 = cpuid, r10 = procinfo
|
||||
* r9 = cpuid, r10 = procinfo
|
||||
*/
|
||||
bl __vet_atags
|
||||
#ifdef CONFIG_SMP_ON_UP
|
||||
@@ -105,7 +101,7 @@ ENTRY(stext)
|
||||
/*
|
||||
* The following calls CPU specific code in a position independent
|
||||
* manner. See arch/arm/mm/proc-*.S for details. r10 = base of
|
||||
* xxx_proc_info structure selected by __lookup_machine_type
|
||||
* xxx_proc_info structure selected by __lookup_processor_type
|
||||
* above. On return, the CPU will be ready for the MMU to be
|
||||
* turned on, and r0 will hold the CPU control register value.
|
||||
*/
|
||||
@@ -124,7 +120,6 @@ ENDPROC(stext)
|
||||
* amount which are required to get the kernel running, which
|
||||
* generally means mapping in the kernel code.
|
||||
*
|
||||
* r8 = machinfo
|
||||
* r9 = cpuid
|
||||
* r10 = procinfo
|
||||
*
|
||||
|
@@ -179,14 +179,21 @@ int __init arch_probe_nr_irqs(void)
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
|
||||
static void route_irq(struct irq_desc *desc, unsigned int irq, unsigned int cpu)
|
||||
static bool migrate_one_irq(struct irq_data *d)
|
||||
{
|
||||
pr_debug("IRQ%u: moving from cpu%u to cpu%u\n", irq, desc->irq_data.node, cpu);
|
||||
unsigned int cpu = cpumask_any_and(d->affinity, cpu_online_mask);
|
||||
bool ret = false;
|
||||
|
||||
raw_spin_lock_irq(&desc->lock);
|
||||
desc->irq_data.chip->irq_set_affinity(&desc->irq_data,
|
||||
cpumask_of(cpu), false);
|
||||
raw_spin_unlock_irq(&desc->lock);
|
||||
if (cpu >= nr_cpu_ids) {
|
||||
cpu = cpumask_any(cpu_online_mask);
|
||||
ret = true;
|
||||
}
|
||||
|
||||
pr_debug("IRQ%u: moving from cpu%u to cpu%u\n", d->irq, d->node, cpu);
|
||||
|
||||
d->chip->irq_set_affinity(d, cpumask_of(cpu), true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -198,25 +205,30 @@ void migrate_irqs(void)
|
||||
{
|
||||
unsigned int i, cpu = smp_processor_id();
|
||||
struct irq_desc *desc;
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
for_each_irq_desc(i, desc) {
|
||||
struct irq_data *d = &desc->irq_data;
|
||||
bool affinity_broken = false;
|
||||
|
||||
if (d->node == cpu) {
|
||||
unsigned int newcpu = cpumask_any_and(d->affinity,
|
||||
cpu_online_mask);
|
||||
if (newcpu >= nr_cpu_ids) {
|
||||
if (printk_ratelimit())
|
||||
printk(KERN_INFO "IRQ%u no longer affine to CPU%u\n",
|
||||
i, cpu);
|
||||
raw_spin_lock(&desc->lock);
|
||||
do {
|
||||
if (desc->action == NULL)
|
||||
break;
|
||||
|
||||
cpumask_setall(d->affinity);
|
||||
newcpu = cpumask_any_and(d->affinity,
|
||||
cpu_online_mask);
|
||||
}
|
||||
if (d->node != cpu)
|
||||
break;
|
||||
|
||||
route_irq(desc, i, newcpu);
|
||||
}
|
||||
affinity_broken = migrate_one_irq(d);
|
||||
} while (0);
|
||||
raw_spin_unlock(&desc->lock);
|
||||
|
||||
if (affinity_broken && printk_ratelimit())
|
||||
pr_warning("IRQ%u no longer affine to CPU%u\n", i, cpu);
|
||||
}
|
||||
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
#endif /* CONFIG_HOTPLUG_CPU */
|
||||
|
@@ -76,6 +76,7 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex,
|
||||
for (i = 0; i < relsec->sh_size / sizeof(Elf32_Rel); i++, rel++) {
|
||||
unsigned long loc;
|
||||
Elf32_Sym *sym;
|
||||
const char *symname;
|
||||
s32 offset;
|
||||
#ifdef CONFIG_THUMB2_KERNEL
|
||||
u32 upper, lower, sign, j1, j2;
|
||||
@@ -83,18 +84,18 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex,
|
||||
|
||||
offset = ELF32_R_SYM(rel->r_info);
|
||||
if (offset < 0 || offset > (symsec->sh_size / sizeof(Elf32_Sym))) {
|
||||
printk(KERN_ERR "%s: bad relocation, section %d reloc %d\n",
|
||||
pr_err("%s: section %u reloc %u: bad relocation sym offset\n",
|
||||
module->name, relindex, i);
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
sym = ((Elf32_Sym *)symsec->sh_addr) + offset;
|
||||
symname = strtab + sym->st_name;
|
||||
|
||||
if (rel->r_offset < 0 || rel->r_offset > dstsec->sh_size - sizeof(u32)) {
|
||||
printk(KERN_ERR "%s: out of bounds relocation, "
|
||||
"section %d reloc %d offset %d size %d\n",
|
||||
module->name, relindex, i, rel->r_offset,
|
||||
dstsec->sh_size);
|
||||
pr_err("%s: section %u reloc %u sym '%s': out of bounds relocation, offset %d size %u\n",
|
||||
module->name, relindex, i, symname,
|
||||
rel->r_offset, dstsec->sh_size);
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
@@ -120,10 +121,10 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex,
|
||||
if (offset & 3 ||
|
||||
offset <= (s32)0xfe000000 ||
|
||||
offset >= (s32)0x02000000) {
|
||||
printk(KERN_ERR
|
||||
"%s: relocation out of range, section "
|
||||
"%d reloc %d sym '%s'\n", module->name,
|
||||
relindex, i, strtab + sym->st_name);
|
||||
pr_err("%s: section %u reloc %u sym '%s': relocation %u out of range (%#lx -> %#x)\n",
|
||||
module->name, relindex, i, symname,
|
||||
ELF32_R_TYPE(rel->r_info), loc,
|
||||
sym->st_value);
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
@@ -196,10 +197,10 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex,
|
||||
if (!(offset & 1) ||
|
||||
offset <= (s32)0xff000000 ||
|
||||
offset >= (s32)0x01000000) {
|
||||
printk(KERN_ERR
|
||||
"%s: relocation out of range, section "
|
||||
"%d reloc %d sym '%s'\n", module->name,
|
||||
relindex, i, strtab + sym->st_name);
|
||||
pr_err("%s: section %u reloc %u sym '%s': relocation %u out of range (%#lx -> %#x)\n",
|
||||
module->name, relindex, i, symname,
|
||||
ELF32_R_TYPE(rel->r_info), loc,
|
||||
sym->st_value);
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
|
@@ -26,8 +26,6 @@
|
||||
#include <asm/system.h>
|
||||
#include <asm/traps.h>
|
||||
|
||||
#include "ptrace.h"
|
||||
|
||||
#define REG_PC 15
|
||||
#define REG_PSR 16
|
||||
/*
|
||||
@@ -184,389 +182,12 @@ put_user_reg(struct task_struct *task, int offset, long data)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
read_u32(struct task_struct *task, unsigned long addr, u32 *res)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = access_process_vm(task, addr, res, sizeof(*res), 0);
|
||||
|
||||
return ret == sizeof(*res) ? 0 : -EIO;
|
||||
}
|
||||
|
||||
static inline int
|
||||
read_instr(struct task_struct *task, unsigned long addr, u32 *res)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (addr & 1) {
|
||||
u16 val;
|
||||
ret = access_process_vm(task, addr & ~1, &val, sizeof(val), 0);
|
||||
ret = ret == sizeof(val) ? 0 : -EIO;
|
||||
*res = val;
|
||||
} else {
|
||||
u32 val;
|
||||
ret = access_process_vm(task, addr & ~3, &val, sizeof(val), 0);
|
||||
ret = ret == sizeof(val) ? 0 : -EIO;
|
||||
*res = val;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get value of register `rn' (in the instruction)
|
||||
*/
|
||||
static unsigned long
|
||||
ptrace_getrn(struct task_struct *child, unsigned long insn)
|
||||
{
|
||||
unsigned int reg = (insn >> 16) & 15;
|
||||
unsigned long val;
|
||||
|
||||
val = get_user_reg(child, reg);
|
||||
if (reg == 15)
|
||||
val += 8;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get value of operand 2 (in an ALU instruction)
|
||||
*/
|
||||
static unsigned long
|
||||
ptrace_getaluop2(struct task_struct *child, unsigned long insn)
|
||||
{
|
||||
unsigned long val;
|
||||
int shift;
|
||||
int type;
|
||||
|
||||
if (insn & 1 << 25) {
|
||||
val = insn & 255;
|
||||
shift = (insn >> 8) & 15;
|
||||
type = 3;
|
||||
} else {
|
||||
val = get_user_reg (child, insn & 15);
|
||||
|
||||
if (insn & (1 << 4))
|
||||
shift = (int)get_user_reg (child, (insn >> 8) & 15);
|
||||
else
|
||||
shift = (insn >> 7) & 31;
|
||||
|
||||
type = (insn >> 5) & 3;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 0: val <<= shift; break;
|
||||
case 1: val >>= shift; break;
|
||||
case 2:
|
||||
val = (((signed long)val) >> shift);
|
||||
break;
|
||||
case 3:
|
||||
val = (val >> shift) | (val << (32 - shift));
|
||||
break;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get value of operand 2 (in a LDR instruction)
|
||||
*/
|
||||
static unsigned long
|
||||
ptrace_getldrop2(struct task_struct *child, unsigned long insn)
|
||||
{
|
||||
unsigned long val;
|
||||
int shift;
|
||||
int type;
|
||||
|
||||
val = get_user_reg(child, insn & 15);
|
||||
shift = (insn >> 7) & 31;
|
||||
type = (insn >> 5) & 3;
|
||||
|
||||
switch (type) {
|
||||
case 0: val <<= shift; break;
|
||||
case 1: val >>= shift; break;
|
||||
case 2:
|
||||
val = (((signed long)val) >> shift);
|
||||
break;
|
||||
case 3:
|
||||
val = (val >> shift) | (val << (32 - shift));
|
||||
break;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
#define OP_MASK 0x01e00000
|
||||
#define OP_AND 0x00000000
|
||||
#define OP_EOR 0x00200000
|
||||
#define OP_SUB 0x00400000
|
||||
#define OP_RSB 0x00600000
|
||||
#define OP_ADD 0x00800000
|
||||
#define OP_ADC 0x00a00000
|
||||
#define OP_SBC 0x00c00000
|
||||
#define OP_RSC 0x00e00000
|
||||
#define OP_ORR 0x01800000
|
||||
#define OP_MOV 0x01a00000
|
||||
#define OP_BIC 0x01c00000
|
||||
#define OP_MVN 0x01e00000
|
||||
|
||||
static unsigned long
|
||||
get_branch_address(struct task_struct *child, unsigned long pc, unsigned long insn)
|
||||
{
|
||||
u32 alt = 0;
|
||||
|
||||
switch (insn & 0x0e000000) {
|
||||
case 0x00000000:
|
||||
case 0x02000000: {
|
||||
/*
|
||||
* data processing
|
||||
*/
|
||||
long aluop1, aluop2, ccbit;
|
||||
|
||||
if ((insn & 0x0fffffd0) == 0x012fff10) {
|
||||
/*
|
||||
* bx or blx
|
||||
*/
|
||||
alt = get_user_reg(child, insn & 15);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if ((insn & 0xf000) != 0xf000)
|
||||
break;
|
||||
|
||||
aluop1 = ptrace_getrn(child, insn);
|
||||
aluop2 = ptrace_getaluop2(child, insn);
|
||||
ccbit = get_user_reg(child, REG_PSR) & PSR_C_BIT ? 1 : 0;
|
||||
|
||||
switch (insn & OP_MASK) {
|
||||
case OP_AND: alt = aluop1 & aluop2; break;
|
||||
case OP_EOR: alt = aluop1 ^ aluop2; break;
|
||||
case OP_SUB: alt = aluop1 - aluop2; break;
|
||||
case OP_RSB: alt = aluop2 - aluop1; break;
|
||||
case OP_ADD: alt = aluop1 + aluop2; break;
|
||||
case OP_ADC: alt = aluop1 + aluop2 + ccbit; break;
|
||||
case OP_SBC: alt = aluop1 - aluop2 + ccbit; break;
|
||||
case OP_RSC: alt = aluop2 - aluop1 + ccbit; break;
|
||||
case OP_ORR: alt = aluop1 | aluop2; break;
|
||||
case OP_MOV: alt = aluop2; break;
|
||||
case OP_BIC: alt = aluop1 & ~aluop2; break;
|
||||
case OP_MVN: alt = ~aluop2; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x04000000:
|
||||
case 0x06000000:
|
||||
/*
|
||||
* ldr
|
||||
*/
|
||||
if ((insn & 0x0010f000) == 0x0010f000) {
|
||||
unsigned long base;
|
||||
|
||||
base = ptrace_getrn(child, insn);
|
||||
if (insn & 1 << 24) {
|
||||
long aluop2;
|
||||
|
||||
if (insn & 0x02000000)
|
||||
aluop2 = ptrace_getldrop2(child, insn);
|
||||
else
|
||||
aluop2 = insn & 0xfff;
|
||||
|
||||
if (insn & 1 << 23)
|
||||
base += aluop2;
|
||||
else
|
||||
base -= aluop2;
|
||||
}
|
||||
read_u32(child, base, &alt);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x08000000:
|
||||
/*
|
||||
* ldm
|
||||
*/
|
||||
if ((insn & 0x00108000) == 0x00108000) {
|
||||
unsigned long base;
|
||||
unsigned int nr_regs;
|
||||
|
||||
if (insn & (1 << 23)) {
|
||||
nr_regs = hweight16(insn & 65535) << 2;
|
||||
|
||||
if (!(insn & (1 << 24)))
|
||||
nr_regs -= 4;
|
||||
} else {
|
||||
if (insn & (1 << 24))
|
||||
nr_regs = -4;
|
||||
else
|
||||
nr_regs = 0;
|
||||
}
|
||||
|
||||
base = ptrace_getrn(child, insn);
|
||||
|
||||
read_u32(child, base + nr_regs, &alt);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x0a000000: {
|
||||
/*
|
||||
* bl or b
|
||||
*/
|
||||
signed long displ;
|
||||
/* It's a branch/branch link: instead of trying to
|
||||
* figure out whether the branch will be taken or not,
|
||||
* we'll put a breakpoint at both locations. This is
|
||||
* simpler, more reliable, and probably not a whole lot
|
||||
* slower than the alternative approach of emulating the
|
||||
* branch.
|
||||
*/
|
||||
displ = (insn & 0x00ffffff) << 8;
|
||||
displ = (displ >> 6) + 8;
|
||||
if (displ != 0 && displ != 4)
|
||||
alt = pc + displ;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return alt;
|
||||
}
|
||||
|
||||
static int
|
||||
swap_insn(struct task_struct *task, unsigned long addr,
|
||||
void *old_insn, void *new_insn, int size)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = access_process_vm(task, addr, old_insn, size, 0);
|
||||
if (ret == size)
|
||||
ret = access_process_vm(task, addr, new_insn, size, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
add_breakpoint(struct task_struct *task, struct debug_info *dbg, unsigned long addr)
|
||||
{
|
||||
int nr = dbg->nsaved;
|
||||
|
||||
if (nr < 2) {
|
||||
u32 new_insn = BREAKINST_ARM;
|
||||
int res;
|
||||
|
||||
res = swap_insn(task, addr, &dbg->bp[nr].insn, &new_insn, 4);
|
||||
|
||||
if (res == 4) {
|
||||
dbg->bp[nr].address = addr;
|
||||
dbg->nsaved += 1;
|
||||
}
|
||||
} else
|
||||
printk(KERN_ERR "ptrace: too many breakpoints\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear one breakpoint in the user program. We copy what the hardware
|
||||
* does and use bit 0 of the address to indicate whether this is a Thumb
|
||||
* breakpoint or an ARM breakpoint.
|
||||
*/
|
||||
static void clear_breakpoint(struct task_struct *task, struct debug_entry *bp)
|
||||
{
|
||||
unsigned long addr = bp->address;
|
||||
union debug_insn old_insn;
|
||||
int ret;
|
||||
|
||||
if (addr & 1) {
|
||||
ret = swap_insn(task, addr & ~1, &old_insn.thumb,
|
||||
&bp->insn.thumb, 2);
|
||||
|
||||
if (ret != 2 || old_insn.thumb != BREAKINST_THUMB)
|
||||
printk(KERN_ERR "%s:%d: corrupted Thumb breakpoint at "
|
||||
"0x%08lx (0x%04x)\n", task->comm,
|
||||
task_pid_nr(task), addr, old_insn.thumb);
|
||||
} else {
|
||||
ret = swap_insn(task, addr & ~3, &old_insn.arm,
|
||||
&bp->insn.arm, 4);
|
||||
|
||||
if (ret != 4 || old_insn.arm != BREAKINST_ARM)
|
||||
printk(KERN_ERR "%s:%d: corrupted ARM breakpoint at "
|
||||
"0x%08lx (0x%08x)\n", task->comm,
|
||||
task_pid_nr(task), addr, old_insn.arm);
|
||||
}
|
||||
}
|
||||
|
||||
void ptrace_set_bpt(struct task_struct *child)
|
||||
{
|
||||
struct pt_regs *regs;
|
||||
unsigned long pc;
|
||||
u32 insn;
|
||||
int res;
|
||||
|
||||
regs = task_pt_regs(child);
|
||||
pc = instruction_pointer(regs);
|
||||
|
||||
if (thumb_mode(regs)) {
|
||||
printk(KERN_WARNING "ptrace: can't handle thumb mode\n");
|
||||
return;
|
||||
}
|
||||
|
||||
res = read_instr(child, pc, &insn);
|
||||
if (!res) {
|
||||
struct debug_info *dbg = &child->thread.debug;
|
||||
unsigned long alt;
|
||||
|
||||
dbg->nsaved = 0;
|
||||
|
||||
alt = get_branch_address(child, pc, insn);
|
||||
if (alt)
|
||||
add_breakpoint(child, dbg, alt);
|
||||
|
||||
/*
|
||||
* Note that we ignore the result of setting the above
|
||||
* breakpoint since it may fail. When it does, this is
|
||||
* not so much an error, but a forewarning that we may
|
||||
* be receiving a prefetch abort shortly.
|
||||
*
|
||||
* If we don't set this breakpoint here, then we can
|
||||
* lose control of the thread during single stepping.
|
||||
*/
|
||||
if (!alt || predicate(insn) != PREDICATE_ALWAYS)
|
||||
add_breakpoint(child, dbg, pc + 4);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure no single-step breakpoint is pending. Returns non-zero
|
||||
* value if child was being single-stepped.
|
||||
*/
|
||||
void ptrace_cancel_bpt(struct task_struct *child)
|
||||
{
|
||||
int i, nsaved = child->thread.debug.nsaved;
|
||||
|
||||
child->thread.debug.nsaved = 0;
|
||||
|
||||
if (nsaved > 2) {
|
||||
printk("ptrace_cancel_bpt: bogus nsaved: %d!\n", nsaved);
|
||||
nsaved = 2;
|
||||
}
|
||||
|
||||
for (i = 0; i < nsaved; i++)
|
||||
clear_breakpoint(child, &child->thread.debug.bp[i]);
|
||||
}
|
||||
|
||||
void user_disable_single_step(struct task_struct *task)
|
||||
{
|
||||
task->ptrace &= ~PT_SINGLESTEP;
|
||||
ptrace_cancel_bpt(task);
|
||||
}
|
||||
|
||||
void user_enable_single_step(struct task_struct *task)
|
||||
{
|
||||
task->ptrace |= PT_SINGLESTEP;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by kernel/ptrace.c when detaching..
|
||||
*/
|
||||
void ptrace_disable(struct task_struct *child)
|
||||
{
|
||||
user_disable_single_step(child);
|
||||
/* Nothing to do. */
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -576,8 +197,6 @@ void ptrace_break(struct task_struct *tsk, struct pt_regs *regs)
|
||||
{
|
||||
siginfo_t info;
|
||||
|
||||
ptrace_cancel_bpt(tsk);
|
||||
|
||||
info.si_signo = SIGTRAP;
|
||||
info.si_errno = 0;
|
||||
info.si_code = TRAP_BRKPT;
|
||||
|
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/ptrace.h
|
||||
*
|
||||
* Copyright (C) 2000-2003 Russell King
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/ptrace.h>
|
||||
|
||||
extern void ptrace_cancel_bpt(struct task_struct *);
|
||||
extern void ptrace_set_bpt(struct task_struct *);
|
||||
extern void ptrace_break(struct task_struct *, struct pt_regs *);
|
||||
|
||||
/*
|
||||
* Send SIGTRAP if we're single-stepping
|
||||
*/
|
||||
static inline void single_step_trap(struct task_struct *task)
|
||||
{
|
||||
if (task->ptrace & PT_SINGLESTEP) {
|
||||
ptrace_cancel_bpt(task);
|
||||
send_sig(SIGTRAP, task, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void single_step_clear(struct task_struct *task)
|
||||
{
|
||||
if (task->ptrace & PT_SINGLESTEP)
|
||||
ptrace_cancel_bpt(task);
|
||||
}
|
||||
|
||||
static inline void single_step_set(struct task_struct *task)
|
||||
{
|
||||
if (task->ptrace & PT_SINGLESTEP)
|
||||
ptrace_set_bpt(task);
|
||||
}
|
@@ -9,6 +9,7 @@
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/ftrace.h>
|
||||
|
||||
#if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND)
|
||||
#include <linux/sched.h>
|
||||
|
@@ -308,7 +308,44 @@ static void __init cacheid_init(void)
|
||||
* already provide the required functionality.
|
||||
*/
|
||||
extern struct proc_info_list *lookup_processor_type(unsigned int);
|
||||
extern struct machine_desc *lookup_machine_type(unsigned int);
|
||||
|
||||
static void __init early_print(const char *str, ...)
|
||||
{
|
||||
extern void printascii(const char *);
|
||||
char buf[256];
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, str);
|
||||
vsnprintf(buf, sizeof(buf), str, ap);
|
||||
va_end(ap);
|
||||
|
||||
#ifdef CONFIG_DEBUG_LL
|
||||
printascii(buf);
|
||||
#endif
|
||||
printk("%s", buf);
|
||||
}
|
||||
|
||||
static struct machine_desc * __init lookup_machine_type(unsigned int type)
|
||||
{
|
||||
extern struct machine_desc __arch_info_begin[], __arch_info_end[];
|
||||
struct machine_desc *p;
|
||||
|
||||
for (p = __arch_info_begin; p < __arch_info_end; p++)
|
||||
if (type == p->nr)
|
||||
return p;
|
||||
|
||||
early_print("\n"
|
||||
"Error: unrecognized/unsupported machine ID (r1 = 0x%08x).\n\n"
|
||||
"Available machine support:\n\nID (hex)\tNAME\n", type);
|
||||
|
||||
for (p = __arch_info_begin; p < __arch_info_end; p++)
|
||||
early_print("%08x\t%s\n", p->nr, p->name);
|
||||
|
||||
early_print("\nPlease check your kernel config and/or bootloader.\n");
|
||||
|
||||
while (true)
|
||||
/* can't use cpu_relax() here as it may require MMU setup */;
|
||||
}
|
||||
|
||||
static void __init feat_v6_fixup(void)
|
||||
{
|
||||
|
@@ -20,7 +20,6 @@
|
||||
#include <asm/unistd.h>
|
||||
#include <asm/vfp.h>
|
||||
|
||||
#include "ptrace.h"
|
||||
#include "signal.h"
|
||||
|
||||
#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
|
||||
@@ -348,8 +347,6 @@ asmlinkage int sys_sigreturn(struct pt_regs *regs)
|
||||
if (restore_sigframe(regs, frame))
|
||||
goto badframe;
|
||||
|
||||
single_step_trap(current);
|
||||
|
||||
return regs->ARM_r0;
|
||||
|
||||
badframe:
|
||||
@@ -383,8 +380,6 @@ asmlinkage int sys_rt_sigreturn(struct pt_regs *regs)
|
||||
if (do_sigaltstack(&frame->sig.uc.uc_stack, NULL, regs->ARM_sp) == -EFAULT)
|
||||
goto badframe;
|
||||
|
||||
single_step_trap(current);
|
||||
|
||||
return regs->ARM_r0;
|
||||
|
||||
badframe:
|
||||
@@ -706,8 +701,6 @@ static void do_signal(struct pt_regs *regs, int syscall)
|
||||
if (try_to_freeze())
|
||||
goto no_signal;
|
||||
|
||||
single_step_clear(current);
|
||||
|
||||
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
|
||||
if (signr > 0) {
|
||||
sigset_t *oldset;
|
||||
@@ -726,7 +719,6 @@ static void do_signal(struct pt_regs *regs, int syscall)
|
||||
if (test_thread_flag(TIF_RESTORE_SIGMASK))
|
||||
clear_thread_flag(TIF_RESTORE_SIGMASK);
|
||||
}
|
||||
single_step_set(current);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -772,7 +764,6 @@ static void do_signal(struct pt_regs *regs, int syscall)
|
||||
sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL);
|
||||
}
|
||||
}
|
||||
single_step_set(current);
|
||||
}
|
||||
|
||||
asmlinkage void
|
||||
|
@@ -23,6 +23,7 @@
|
||||
#include <linux/kexec.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include <asm/atomic.h>
|
||||
#include <asm/cacheflush.h>
|
||||
@@ -32,7 +33,6 @@
|
||||
#include <asm/unwind.h>
|
||||
#include <asm/tls.h>
|
||||
|
||||
#include "ptrace.h"
|
||||
#include "signal.h"
|
||||
|
||||
static const char *handler[]= { "prefetch abort", "data abort", "address exception", "interrupt" };
|
||||
@@ -256,7 +256,7 @@ static int __die(const char *str, int err, struct thread_info *thread, struct pt
|
||||
return ret;
|
||||
}
|
||||
|
||||
DEFINE_SPINLOCK(die_lock);
|
||||
static DEFINE_SPINLOCK(die_lock);
|
||||
|
||||
/*
|
||||
* This function is protected against re-entrancy.
|
||||
|
Reference in New Issue
Block a user