Changeset View
Changeset View
Standalone View
Standalone View
head/sys/arm/arm/machdep.c
Show First 20 Lines • Show All 89 Lines • ▼ Show 20 Lines | |||||
#include <vm/vm_page.h> | #include <vm/vm_page.h> | ||||
#include <vm/vm_pager.h> | #include <vm/vm_pager.h> | ||||
#include <machine/acle-compat.h> | #include <machine/acle-compat.h> | ||||
#include <machine/armreg.h> | #include <machine/armreg.h> | ||||
#include <machine/atags.h> | #include <machine/atags.h> | ||||
#include <machine/cpu.h> | #include <machine/cpu.h> | ||||
#include <machine/cpuinfo.h> | #include <machine/cpuinfo.h> | ||||
#include <machine/db_machdep.h> | |||||
#include <machine/devmap.h> | #include <machine/devmap.h> | ||||
#include <machine/frame.h> | #include <machine/frame.h> | ||||
#include <machine/intr.h> | #include <machine/intr.h> | ||||
#include <machine/machdep.h> | #include <machine/machdep.h> | ||||
#include <machine/md_var.h> | #include <machine/md_var.h> | ||||
#include <machine/metadata.h> | #include <machine/metadata.h> | ||||
#include <machine/pcb.h> | #include <machine/pcb.h> | ||||
#include <machine/physmem.h> | #include <machine/physmem.h> | ||||
▲ Show 20 Lines • Show All 516 Lines • ▼ Show 20 Lines | ptrace_write_int(struct thread *td, vm_offset_t addr, u_int32_t v) | ||||
uio.uio_offset = (off_t)addr; | uio.uio_offset = (off_t)addr; | ||||
uio.uio_resid = sizeof(u_int32_t); | uio.uio_resid = sizeof(u_int32_t); | ||||
uio.uio_segflg = UIO_SYSSPACE; | uio.uio_segflg = UIO_SYSSPACE; | ||||
uio.uio_rw = UIO_WRITE; | uio.uio_rw = UIO_WRITE; | ||||
uio.uio_td = td; | uio.uio_td = td; | ||||
return proc_rwmem(td->td_proc, &uio); | return proc_rwmem(td->td_proc, &uio); | ||||
} | } | ||||
static u_int | |||||
ptrace_get_usr_reg(void *cookie, int reg) | |||||
{ | |||||
int ret; | |||||
struct thread *td = cookie; | |||||
KASSERT(((reg >= 0) && (reg <= ARM_REG_NUM_PC)), | |||||
("reg is outside range")); | |||||
switch(reg) { | |||||
case ARM_REG_NUM_PC: | |||||
ret = td->td_frame->tf_pc; | |||||
break; | |||||
case ARM_REG_NUM_LR: | |||||
ret = td->td_frame->tf_usr_lr; | |||||
break; | |||||
case ARM_REG_NUM_SP: | |||||
ret = td->td_frame->tf_usr_sp; | |||||
break; | |||||
default: | |||||
ret = *((register_t*)&td->td_frame->tf_r0 + reg); | |||||
break; | |||||
} | |||||
return (ret); | |||||
} | |||||
static u_int | |||||
ptrace_get_usr_int(void* cookie, vm_offset_t offset, u_int* val) | |||||
{ | |||||
struct thread *td = cookie; | |||||
u_int error; | |||||
error = ptrace_read_int(td, offset, val); | |||||
return (error); | |||||
} | |||||
/** | |||||
* This function parses current instruction opcode and decodes | |||||
* any possible jump (change in PC) which might occur after | |||||
* the instruction is executed. | |||||
* | |||||
* @param td Thread structure of analysed task | |||||
* @param cur_instr Currently executed instruction | |||||
* @param alt_next_address Pointer to the variable where | |||||
* the destination address of the | |||||
* jump instruction shall be stored. | |||||
* | |||||
* @return <0> when jump is possible | |||||
* <EINVAL> otherwise | |||||
*/ | |||||
static int | |||||
ptrace_get_alternative_next(struct thread *td, uint32_t cur_instr, | |||||
uint32_t *alt_next_address) | |||||
{ | |||||
int error; | |||||
if (inst_branch(cur_instr) || inst_call(cur_instr) || | |||||
inst_return(cur_instr)) { | |||||
error = arm_predict_branch(td, cur_instr, td->td_frame->tf_pc, | |||||
alt_next_address, ptrace_get_usr_reg, ptrace_get_usr_int); | |||||
return (error); | |||||
} | |||||
return (EINVAL); | |||||
} | |||||
int | int | ||||
ptrace_single_step(struct thread *td) | ptrace_single_step(struct thread *td) | ||||
{ | { | ||||
struct proc *p; | struct proc *p; | ||||
int error; | int error, error_alt; | ||||
uint32_t cur_instr, alt_next = 0; | |||||
/* TODO: This needs to be updated for Thumb-2 */ | /* TODO: This needs to be updated for Thumb-2 */ | ||||
if ((td->td_frame->tf_spsr & PSR_T) != 0) | if ((td->td_frame->tf_spsr & PSR_T) != 0) | ||||
return (EINVAL); | return (EINVAL); | ||||
KASSERT(td->td_md.md_ptrace_instr == 0, | KASSERT(td->td_md.md_ptrace_instr == 0, | ||||
("Didn't clear single step")); | ("Didn't clear single step")); | ||||
KASSERT(td->td_md.md_ptrace_instr_alt == 0, | |||||
("Didn't clear alternative single step")); | |||||
p = td->td_proc; | p = td->td_proc; | ||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | ||||
error = ptrace_read_int(td, td->td_frame->tf_pc + 4, | |||||
&td->td_md.md_ptrace_instr); | error = ptrace_read_int(td, td->td_frame->tf_pc, | ||||
&cur_instr); | |||||
if (error) | if (error) | ||||
goto out; | goto out; | ||||
error = ptrace_write_int(td, td->td_frame->tf_pc + 4, | |||||
error = ptrace_read_int(td, td->td_frame->tf_pc + INSN_SIZE, | |||||
&td->td_md.md_ptrace_instr); | |||||
if (error == 0) { | |||||
error = ptrace_write_int(td, td->td_frame->tf_pc + INSN_SIZE, | |||||
PTRACE_BREAKPOINT); | PTRACE_BREAKPOINT); | ||||
if (error) | if (error) { | ||||
td->td_md.md_ptrace_instr = 0; | td->td_md.md_ptrace_instr = 0; | ||||
td->td_md.md_ptrace_addr = td->td_frame->tf_pc + 4; | } else { | ||||
td->td_md.md_ptrace_addr = td->td_frame->tf_pc + | |||||
INSN_SIZE; | |||||
} | |||||
} | |||||
error_alt = ptrace_get_alternative_next(td, cur_instr, &alt_next); | |||||
if (error_alt == 0) { | |||||
error_alt = ptrace_read_int(td, alt_next, | |||||
&td->td_md.md_ptrace_instr_alt); | |||||
if (error_alt) { | |||||
td->td_md.md_ptrace_instr_alt = 0; | |||||
} else { | |||||
error_alt = ptrace_write_int(td, alt_next, | |||||
PTRACE_BREAKPOINT); | |||||
if (error_alt) | |||||
td->td_md.md_ptrace_instr_alt = 0; | |||||
else | |||||
td->td_md.md_ptrace_addr_alt = alt_next; | |||||
} | |||||
} | |||||
out: | out: | ||||
PROC_LOCK(p); | PROC_LOCK(p); | ||||
return (error); | return ((error != 0) && (error_alt != 0)); | ||||
} | } | ||||
int | int | ||||
ptrace_clear_single_step(struct thread *td) | ptrace_clear_single_step(struct thread *td) | ||||
{ | { | ||||
struct proc *p; | struct proc *p; | ||||
/* TODO: This needs to be updated for Thumb-2 */ | /* TODO: This needs to be updated for Thumb-2 */ | ||||
if ((td->td_frame->tf_spsr & PSR_T) != 0) | if ((td->td_frame->tf_spsr & PSR_T) != 0) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (td->td_md.md_ptrace_instr) { | if (td->td_md.md_ptrace_instr != 0) { | ||||
p = td->td_proc; | p = td->td_proc; | ||||
PROC_UNLOCK(p); | PROC_UNLOCK(p); | ||||
ptrace_write_int(td, td->td_md.md_ptrace_addr, | ptrace_write_int(td, td->td_md.md_ptrace_addr, | ||||
td->td_md.md_ptrace_instr); | td->td_md.md_ptrace_instr); | ||||
PROC_LOCK(p); | PROC_LOCK(p); | ||||
td->td_md.md_ptrace_instr = 0; | td->td_md.md_ptrace_instr = 0; | ||||
} | } | ||||
if (td->td_md.md_ptrace_instr_alt != 0) { | |||||
p = td->td_proc; | |||||
PROC_UNLOCK(p); | |||||
ptrace_write_int(td, td->td_md.md_ptrace_addr_alt, | |||||
td->td_md.md_ptrace_instr_alt); | |||||
PROC_LOCK(p); | |||||
td->td_md.md_ptrace_instr_alt = 0; | |||||
} | |||||
return (0); | return (0); | ||||
} | } | ||||
int | int | ||||
ptrace_set_pc(struct thread *td, unsigned long addr) | ptrace_set_pc(struct thread *td, unsigned long addr) | ||||
{ | { | ||||
td->td_frame->tf_pc = addr; | td->td_frame->tf_pc = addr; | ||||
return (0); | return (0); | ||||
▲ Show 20 Lines • Show All 384 Lines • ▼ Show 20 Lines | init_proc0(vm_offset_t kstack) | ||||
thread0.td_kstack = kstack; | thread0.td_kstack = kstack; | ||||
thread0.td_pcb = (struct pcb *) | thread0.td_pcb = (struct pcb *) | ||||
(thread0.td_kstack + kstack_pages * PAGE_SIZE) - 1; | (thread0.td_kstack + kstack_pages * PAGE_SIZE) - 1; | ||||
thread0.td_pcb->pcb_flags = 0; | thread0.td_pcb->pcb_flags = 0; | ||||
thread0.td_pcb->pcb_vfpcpu = -1; | thread0.td_pcb->pcb_vfpcpu = -1; | ||||
thread0.td_pcb->pcb_vfpstate.fpscr = VFPSCR_DN; | thread0.td_pcb->pcb_vfpstate.fpscr = VFPSCR_DN; | ||||
thread0.td_frame = &proc0_tf; | thread0.td_frame = &proc0_tf; | ||||
pcpup->pc_curpcb = thread0.td_pcb; | pcpup->pc_curpcb = thread0.td_pcb; | ||||
} | |||||
int | |||||
arm_predict_branch(void *cookie, u_int insn, register_t pc, register_t *new_pc, | |||||
u_int (*fetch_reg)(void*, int), u_int (*read_int)(void*, vm_offset_t, u_int*)) | |||||
{ | |||||
u_int addr, nregs, offset = 0; | |||||
int error = 0; | |||||
switch ((insn >> 24) & 0xf) { | |||||
case 0x2: /* add pc, reg1, #value */ | |||||
case 0x0: /* add pc, reg1, reg2, lsl #offset */ | |||||
addr = fetch_reg(cookie, (insn >> 16) & 0xf); | |||||
if (((insn >> 16) & 0xf) == 15) | |||||
addr += 8; | |||||
if (insn & 0x0200000) { | |||||
offset = (insn >> 7) & 0x1e; | |||||
offset = (insn & 0xff) << (32 - offset) | | |||||
(insn & 0xff) >> offset; | |||||
} else { | |||||
offset = fetch_reg(cookie, insn & 0x0f); | |||||
if ((insn & 0x0000ff0) != 0x00000000) { | |||||
if (insn & 0x10) | |||||
nregs = fetch_reg(cookie, | |||||
(insn >> 8) & 0xf); | |||||
else | |||||
nregs = (insn >> 7) & 0x1f; | |||||
switch ((insn >> 5) & 3) { | |||||
case 0: | |||||
/* lsl */ | |||||
offset = offset << nregs; | |||||
break; | |||||
case 1: | |||||
/* lsr */ | |||||
offset = offset >> nregs; | |||||
break; | |||||
default: | |||||
break; /* XXX */ | |||||
} | |||||
} | |||||
*new_pc = addr + offset; | |||||
return (0); | |||||
} | |||||
case 0xa: /* b ... */ | |||||
case 0xb: /* bl ... */ | |||||
addr = ((insn << 2) & 0x03ffffff); | |||||
if (addr & 0x02000000) | |||||
addr |= 0xfc000000; | |||||
*new_pc = (pc + 8 + addr); | |||||
return (0); | |||||
case 0x7: /* ldr pc, [pc, reg, lsl #2] */ | |||||
addr = fetch_reg(cookie, insn & 0xf); | |||||
addr = pc + 8 + (addr << 2); | |||||
error = read_int(cookie, addr, &addr); | |||||
*new_pc = addr; | |||||
return (error); | |||||
case 0x1: /* mov pc, reg */ | |||||
*new_pc = fetch_reg(cookie, insn & 0xf); | |||||
return (0); | |||||
case 0x4: | |||||
case 0x5: /* ldr pc, [reg] */ | |||||
addr = fetch_reg(cookie, (insn >> 16) & 0xf); | |||||
/* ldr pc, [reg, #offset] */ | |||||
if (insn & (1 << 24)) | |||||
offset = insn & 0xfff; | |||||
if (insn & 0x00800000) | |||||
addr += offset; | |||||
else | |||||
addr -= offset; | |||||
error = read_int(cookie, addr, &addr); | |||||
*new_pc = addr; | |||||
return (error); | |||||
case 0x8: /* ldmxx reg, {..., pc} */ | |||||
case 0x9: | |||||
addr = fetch_reg(cookie, (insn >> 16) & 0xf); | |||||
nregs = (insn & 0x5555) + ((insn >> 1) & 0x5555); | |||||
nregs = (nregs & 0x3333) + ((nregs >> 2) & 0x3333); | |||||
nregs = (nregs + (nregs >> 4)) & 0x0f0f; | |||||
nregs = (nregs + (nregs >> 8)) & 0x001f; | |||||
switch ((insn >> 23) & 0x3) { | |||||
case 0x0: /* ldmda */ | |||||
addr = addr - 0; | |||||
break; | |||||
case 0x1: /* ldmia */ | |||||
addr = addr + 0 + ((nregs - 1) << 2); | |||||
break; | |||||
case 0x2: /* ldmdb */ | |||||
addr = addr - 4; | |||||
break; | |||||
case 0x3: /* ldmib */ | |||||
addr = addr + 4 + ((nregs - 1) << 2); | |||||
break; | |||||
} | |||||
error = read_int(cookie, addr, &addr); | |||||
*new_pc = addr; | |||||
return (error); | |||||
default: | |||||
return (EINVAL); | |||||
} | |||||
} | } | ||||
#ifdef ARM_NEW_PMAP | #ifdef ARM_NEW_PMAP | ||||
void | void | ||||
set_stackptrs(int cpu) | set_stackptrs(int cpu) | ||||
{ | { | ||||
set_stackptr(PSR_IRQ32_MODE, | set_stackptr(PSR_IRQ32_MODE, | ||||
▲ Show 20 Lines • Show All 616 Lines • Show Last 20 Lines |