static inline void handle_syscall_restart(unsigned long save_r0, struct pt_regs *regs, struct sigaction *sa) { /* If we're not from a syscall, bail out */ if (regs->tra < 0) return; /* check for system call restart.. */ switch (regs->regs[0]) { case -ERESTART_RESTARTBLOCK: case -ERESTARTNOHAND: no_system_call_restart: regs->regs[0] = -EINTR; break; case -ERESTARTSYS: if (!(sa->sa_flags & SA_RESTART)) goto no_system_call_restart; /* fallthrough */ case -ERESTARTNOINTR: regs->regs[0] = save_r0; regs->pc -= instruction_size(__raw_readw(regs->pc - 4)); break; } }
/* * Note that 'init' is a special process: it doesn't get signals it doesn't * want to handle. Thus you cannot kill init even with a SIGKILL even by * mistake. * * Note that we go through the signals twice: once to check the signals that * the kernel can handle, and then we build all the user-level signal handling * stack-frames in one go after that. */ static void do_signal(struct pt_regs *regs, unsigned int save_r0) { siginfo_t info; int signr; struct k_sigaction ka; /* * We want the common case to go fast, which * is why we may in certain cases get here from * kernel mode. Just return without doing anything * if so. */ if (!user_mode(regs)) return; signr = get_signal_to_deliver(&info, &ka, regs, NULL); if (signr > 0) { handle_syscall_restart(save_r0, regs, &ka.sa); /* Whee! Actually deliver the signal. */ handle_signal(signr, &ka, &info, regs, save_r0); return; } /* Did we come from a system call? */ if (regs->tra >= 0) { /* Restart the system call - no handlers present */ if (regs->regs[0] == -ERESTARTNOHAND || regs->regs[0] == -ERESTARTSYS || regs->regs[0] == -ERESTARTNOINTR) { regs->regs[0] = save_r0; regs->pc -= instruction_size(__raw_readw(regs->pc - 4)); } else if (regs->regs[0] == -ERESTART_RESTARTBLOCK) { regs->pc -= instruction_size(__raw_readw(regs->pc - 4)); regs->regs[3] = __NR_restart_syscall; } } /* * If there's no signal to deliver, we just put the saved sigmask * back. */ restore_saved_sigmask(); }
static void handle_BUG(struct pt_regs *regs) { enum bug_trap_type tt; tt = report_bug(regs->pc, regs); if (tt == BUG_TRAP_TYPE_WARN) { regs->pc += instruction_size(regs->pc); return; } die("Kernel BUG", regs, TRAPA_BUG_OPCODE & 0xff); }
void assert_index_size(int required_size) const { #ifdef ASSERT int isize = instruction_size() - (is_wide() ? 1 : 0) - 1; if (isize == 2 && cur_bc() == Bytecodes::_iinc) isize = 1; else if (isize <= 2) ; // no change else if (has_giant_index()) isize = 4; else isize = 2; assert(isize = required_size, "wrong index size"); #endif }
/* Convert an instruction into a DATA atom including relocations, if necessary. */ dblock *eval_instruction(instruction *p,section *sec,taddr pc) { /* Auch simpel. Fehlerchecks fehlen. */ taddr size=instruction_size(p,sec,pc); dblock *db=new_dblock(); int c=opt_inst(p,sec,pc); char *d; taddr val; db->size=size; d=db->data=mymalloc(size); *d=c; if(p->qualifiers[0]){ if(c>5) cpu_error(1,p->qualifiers[0]); else if(!strcmp(p->qualifiers[0],"b")) *d|=128; else if(strcmp(p->qualifiers[0],"w")) cpu_error(1,p->qualifiers[0]); } d++; if(c==5){ /* addq */ taddr val; if(!eval_expr(p->op[0]->offset,&val,sec,pc)||val>15) cpu_error(0); *d=((val<<4)|operand_code(p->op[1])); return db; } if(c==7){ expr *tree=p->op[0]->offset; if(!(tree->type==SYM&&tree->c.sym->sec==sec&&tree->c.sym->type==LABSYM&& tree->c.sym->pc-pc>=-128&&tree->c.sym->pc-pc<=127)) cpu_error(0); else *d=tree->c.sym->pc-pc; return db; } *d=((operand_code(p->op[0])<<4)|operand_code(p->op[1])); d++; d=fill_operand(p->op[0],sec,pc,d,&db->relocs,d-db->data); d=fill_operand(p->op[1],sec,pc,d,&db->relocs,d-db->data); return db; }
static void handle_BUG(struct pt_regs *regs) { const struct bug_entry *bug; unsigned long bugaddr = regs->pc; enum bug_trap_type tt; if (!is_valid_bugaddr(bugaddr)) goto invalid; bug = find_bug(bugaddr); /* Switch unwinders when unwind_stack() is called */ if (bug->flags & BUGFLAG_UNWINDER) unwinder_faulted = 1; tt = report_bug(bugaddr, regs); if (tt == BUG_TRAP_TYPE_WARN) { regs->pc += instruction_size(bugaddr); return; } invalid: die("Kernel BUG", regs, TRAPA_BUG_OPCODE & 0xff); }
/* * Note that 'init' is a special process: it doesn't get signals it doesn't * want to handle. Thus you cannot kill init even with a SIGKILL even by * mistake. * * Note that we go through the signals twice: once to check the signals that * the kernel can handle, and then we build all the user-level signal handling * stack-frames in one go after that. */ static void do_signal(struct pt_regs *regs, unsigned int save_r0) { siginfo_t info; int signr; struct k_sigaction ka; sigset_t *oldset; /* * We want the common case to go fast, which * is why we may in certain cases get here from * kernel mode. Just return without doing anything * if so. */ if (!user_mode(regs)) return; if (current_thread_info()->status & TS_RESTORE_SIGMASK) oldset = ¤t->saved_sigmask; else oldset = ¤t->blocked; signr = get_signal_to_deliver(&info, &ka, regs, NULL); if (signr > 0) { handle_syscall_restart(save_r0, regs, &ka.sa); /* Whee! Actually deliver the signal. */ if (handle_signal(signr, &ka, &info, oldset, regs, save_r0) == 0) { /* * A signal was successfully delivered; the saved * sigmask will have been stored in the signal frame, * and will be restored by sigreturn, so we can simply * clear the TS_RESTORE_SIGMASK flag */ current_thread_info()->status &= ~TS_RESTORE_SIGMASK; tracehook_signal_handler(signr, &info, &ka, regs, test_thread_flag(TIF_SINGLESTEP)); } return; } /* Did we come from a system call? */ if (regs->tra >= 0) { /* Restart the system call - no handlers present */ if (regs->regs[0] == -ERESTARTNOHAND || regs->regs[0] == -ERESTARTSYS || regs->regs[0] == -ERESTARTNOINTR) { regs->regs[0] = save_r0; regs->pc -= instruction_size(__raw_readw(regs->pc - 4)); } else if (regs->regs[0] == -ERESTART_RESTARTBLOCK) { regs->pc -= instruction_size(__raw_readw(regs->pc - 4)); regs->regs[3] = __NR_restart_syscall; } } /* * If there's no signal to deliver, we just put the saved sigmask * back. */ if (current_thread_info()->status & TS_RESTORE_SIGMASK) { current_thread_info()->status &= ~TS_RESTORE_SIGMASK; sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); } }
void predict(Options::PredictEnum mode, ReportWriter *writer, const RomAccessor &rom, Trace &trace, const AnnotationResolver &annotations) { if (mode == Options::PRD_NEVER) return; bool limit_to_functions = mode == Options::PRD_FUNCTIONS; Profile profile("Predict", true); struct PredictBranch { const Annotation *annotation; Pointer from_pc; Pointer pc; uint16_t DP, P; uint8_t DB; }; std::vector<PredictBranch> predict_brances; LargeBitfield has_op(256*64*1024); LargeBitfield inside_op(256 * 64 * 1024); for (auto opsit : trace.ops) { const Pointer pc = opsit.first; const Trace::OpVariantLookup &vl = opsit.second; const OpInfo &example = trace.variant(vl, 0); const uint8_t* data = rom.evalPtr(pc); const uint8_t opcode = data[0]; uint32_t op_size = instruction_size(opcode, is_memory_accumulator_wide(example.P), is_index_wide(example.P)); for (uint32_t i = 0; i < op_size; ++i) { has_op.set_bit(bank_add(pc, i)); if (i!=0) inside_op.set_bit(bank_add(pc, i)); } StringBuilder sb; Pointer target_jump, target_no_jump; bool branch_or_jump = decode_static_jump(opcode, rom, pc, &target_jump, &target_no_jump); const Hint *hint = annotations.hint(pc); if (hint && hint->has_hint(Hint::BRANCH_ALWAYS)) { target_no_jump = INVALID_POINTER; } if (hint && hint->has_hint(Hint::BRANCH_NEVER)) { target_jump = INVALID_POINTER; } if (!branch_or_jump) continue; const Annotation *source_annotation = nullptr, *target_annotation = nullptr; annotations.resolve_annotation(pc, &source_annotation); if (target_jump != INVALID_POINTER) { trace.labels.set_bit(target_jump); annotations.resolve_annotation(target_jump, &target_annotation); } if (source_annotation || !limit_to_functions) { PredictBranch p; p.annotation = source_annotation; p.from_pc = pc; p.DB = example.DB; p.DP = example.DP; p.P = example.P; if (target_jump != INVALID_POINTER && (target_annotation == source_annotation || !limit_to_functions)) { p.pc = target_jump; CUSTOM_ASSERT(target_jump != INVALID_POINTER); predict_brances.push_back(p); } if (target_no_jump != INVALID_POINTER && (!limit_to_functions || (target_no_jump >= source_annotation->startOfRange && target_no_jump <= source_annotation->endOfRange))) { // BRA,BRL and the jumps always branches/jumps p.pc = target_no_jump; CUSTOM_ASSERT(target_no_jump != INVALID_POINTER); predict_brances.push_back(p); } } } StringBuilder sb; sb.clear(); if (writer) writer->writeSeperator("Prediction diagnostics"); for (int pbi=0; pbi<(int)predict_brances.size(); ++pbi) { const PredictBranch pb = predict_brances[pbi]; Pointer pc = pb.pc; Pointer r0 = limit_to_functions ? pb.annotation->startOfRange : 0, r1 = limit_to_functions ? pb.annotation->endOfRange : 0xFFFFFF; uint16_t P = pb.P, DP = pb.DP; uint8_t DB = pb.DB; bool P_unknown = false; if (inside_op[pc]) { if (writer) { sb.clear(); sb.format("Predicted jump at %06X jumped inside instruction at %06X. Consider adding a \"hint branch_always/branch_never %06X\" annotation.", pc, pb.from_pc, pb.from_pc); writer->writeComment(sb); } printf("Warning; predicted jump went inside instruction at %06X (from %06X)\n", pc, pb.from_pc); } while (!has_op[pc] && pc >= r0 && pc <= r1) { // Make sure we don't run into a data scope const Annotation *data_scope = nullptr, *function_scope = nullptr; annotations.resolve_annotation(pc, &function_scope, &data_scope); if (data_scope) continue; if (function_scope && function_scope != pb.annotation) continue; uint8_t opcode = rom.evalByte(pc); bool abort_unknown_P = P_unknown; if (P_unknown) { switch(opcode) { case 0x02: // COP const case 0x40: // RTI case 0x6B: // RTL case 0x60: // RTS case 0x3B: // TSC case 0xBA: // TSX case 0x8A: // TXA case 0x9A: // TXS case 0x9B: // TXY case 0x98: // TYA case 0xBB: // TYX case 0xCB: // WAI case 0xEB: // XBA case 0xFB: // XCE case 0xAA: // TAX case 0xA8: // TAY case 0x5B: // TCD case 0x1B: // TCS case 0x7B: // TDC case 0xDB: // STP case 0x38: // SEC case 0xF8: // SED case 0x78: // SEI case 0xE2: // SEP case 0xC2: // REP case 0x18: // CLC case 0xD8: // CLD case 0x58: // CLI case 0xB8: // CLV case 0xCA: // DEX case 0x88: // DEY case 0xE8: // INX case 0xC8: // INY case 0xEA: // NOP case 0x8B: // PHB case 0x0B: // PHD case 0x4B: // PHK case 0x08: // PHP case 0xDA: // PHX case 0x5A: // PHY case 0x68: // PLA case 0xAB: // PLB case 0x2B: // PLD case 0x28: // PLP case 0xFA: // PLX case 0x7A: // PLY abort_unknown_P = false; } } if (abort_unknown_P) { if (writer) { sb.clear(); sb.format("Aborting trace at %06X due to unknown processor status", pc); if (pb.annotation) sb.format("(in %s)", pb.annotation->name.c_str()); writer->writeComment(sb); } break; } uint8_t operand = rom.evalByte(pc + 1); Pointer target_jump, target_no_jump; bool is_jump_or_branch = decode_static_jump(opcode, rom, pc, &target_jump, &target_no_jump); { Trace::OpVariantLookup l; l.count = 1; l.offset = (uint32_t)trace.ops_variants.size(); trace.ops[pc] = l; OpInfo info; info.P = P_unknown ? 0 : P; info.DB = DB; info.DP = DP; info.X = info.Y = 0; info.jump_target = target_jump; info.indirect_base_pointer = INVALID_POINTER; // TODO: We should be able to set this one sometimes trace.ops_variants.push_back(info); // Note that DB and DP here represent a lie :) } const int op_size = instruction_size(rom.evalByte(pc), is_memory_accumulator_wide(P), is_index_wide(P)); for (int i=0; i<op_size; ++i) { // TODO: We should do overlap test for entire range we are "using" now // Also first might not always be best! trace.is_predicted.set_bit(bank_add(pc, i)); has_op.set_bit(bank_add(pc, i)); if (i != 0) inside_op.set_bit(bank_add(pc, i)); } const Hint *hint = annotations.hint(pc); if (hint && hint->has_hint(Hint::BRANCH_NEVER)) { target_jump = INVALID_POINTER; } bool is_jsr = opcode == 0x20||opcode==0x22||opcode==0xFC; if (hint && hint->has_hint(Hint::JUMP_IS_JSR)) { is_jump_or_branch = false; is_jsr = true; } if (is_jump_or_branch) { const Annotation *function = nullptr; if (target_jump != INVALID_POINTER) { annotations.resolve_annotation(target_jump, &function); trace.labels.set_bit(target_jump); } if (limit_to_functions && writer && (!function || function != pb.annotation)) { sb.clear(); sb.format("Branch going out of %s to ", pb.annotation->name.c_str()); if (function) { sb.format("%s [%06X]", function->name.c_str(), target_jump); } else { sb.format("%06X", target_jump); } sb.format(". Not following due to range restriction."); writer->writeComment(sb); } if (target_jump != INVALID_POINTER) { PredictBranch npb = pb; npb.from_pc = pc; npb.pc = target_jump; predict_brances.push_back(npb); } if (hint && hint->has_hint(Hint::BRANCH_ALWAYS)) { // Never continoue after this op since it always diverges control flow continue; } } else if (opcode == 0xE2) { // TODO: // * When we get a REP or SEP parts or P become known again. We could track unknown per the three flags and update. // * Updating DB/DP might be interesting if (operand & 0x10) P |= 0x0010; if (operand & 0x20) P |= 0x0020; } else if (opcode == 0xC2) { if (operand & 0x10) P &= ~0x0010; if (operand & 0x20) P &= ~0x0020; } else if (opcode == 0x28 || opcode == 0xFB) { P_unknown = true; // PLP or XCE // A jump or BRA, stop execution flow (not JSR or non-BRA-branch) } else if (opcode == 0x4C||opcode==0x5C||opcode==0x6C||opcode==0x7C||opcode==0x80) { if (writer && opcode != 0x80) { sb.clear(); sb.format("Not following jump (opcode %02X) at %06X in %s. Only absolute jumps supported.", opcode, pc, pb.annotation->name.c_str()); writer->writeComment(sb); } // TODO: if there is a trace annotation about jmp being jsr we could go on continue; } else if (is_jsr) { if (writer) { sb.clear(); sb.format("Not following jump with subroutine (opcode %02X) at %06X", opcode, pc); if (pb.annotation) sb.format(" in %s.", pb.annotation->name.c_str()); writer->writeComment(sb); } } else if (opcode == 0x40 || opcode == 0x6B || opcode == 0x60) { // some sort of return, stop execution flow continue; } pc += op_size; } } }
static int handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *oldset, struct pt_regs *regs, unsigned int save_r0) { int ret; /* Are we from a system call? */ if (regs->tra >= 0) { /* If so, check system call restarting.. */ switch (regs->regs[0]) { case -ERESTART_RESTARTBLOCK: case -ERESTARTNOHAND: regs->regs[0] = -EINTR; break; case -ERESTARTSYS: if (!(ka->sa.sa_flags & SA_RESTART)) { regs->regs[0] = -EINTR; break; } /* fallthrough */ case -ERESTARTNOINTR: regs->regs[0] = save_r0; regs->pc -= instruction_size( ctrl_inw(regs->pc - 4)); break; } } else { /* gUSA handling */ #ifdef CONFIG_PREEMPT unsigned long flags; local_irq_save(flags); #endif if (regs->regs[15] >= 0xc0000000) { int offset = (int)regs->regs[15]; /* Reset stack pointer: clear critical region mark */ regs->regs[15] = regs->regs[1]; if (regs->pc < regs->regs[0]) /* Go to rewind point #1 */ regs->pc = regs->regs[0] + offset - instruction_size(ctrl_inw(regs->pc-4)); } #ifdef CONFIG_PREEMPT local_irq_restore(flags); #endif } /* Set up the stack frame */ if (ka->sa.sa_flags & SA_SIGINFO) ret = setup_rt_frame(sig, ka, info, oldset, regs); else ret = setup_frame(sig, ka, oldset, regs); if (ka->sa.sa_flags & SA_ONESHOT) ka->sa.sa_handler = SIG_DFL; if (ret == 0) { spin_lock_irq(¤t->sighand->siglock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); if (!(ka->sa.sa_flags & SA_NODEFER)) sigaddset(¤t->blocked,sig); recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); } return ret; }
static void assemble(void) { section *sec; atom *p; char *attr; int bss; final_pass=1; for(sec=first_section; sec; sec=sec->next) { source *lasterrsrc=NULL; int lasterrline=0; sec->pc=sec->org; attr=sec->attr; bss=0; while(*attr) { if(*attr++=='u') { bss=1; break; } } for(p=sec->first; p; p=p->next) { sec->pc=(sec->pc+p->align-1)/p->align*p->align; cur_src=p->src; cur_src->line=p->line; if(p->list&&p->list->atom==p) { p->list->sec=sec; p->list->pc=sec->pc; } if(p->type==INSTRUCTION) { dblock *db; cur_listing=p->list; db=eval_instruction(p->content.inst,sec,sec->pc); if(pic_check) do_pic_check(db->relocs); cur_listing=0; if(DEBUG) { if(db->size!=instruction_size(p->content.inst,sec,sec->pc)) ierror(0); } /*FIXME: sauber freigeben */ myfree(p->content.inst); p->content.db=db; p->type=DATA; } else if(p->type==DATADEF) { dblock *db; cur_listing=p->list; db=eval_data(p->content.defb->op,p->content.defb->bitsize,sec,sec->pc); if(pic_check) do_pic_check(db->relocs); cur_listing=0; /*FIXME: sauber freigeben */ myfree(p->content.defb); p->content.db=db; p->type=DATA; } else if(p->type==RORG) { sblock *sb; taddr space; if(eval_expr(p->content.roffs,&space,sec,sec->pc)) { space=sec->org+space-sec->pc; if (space>=0) { sb=new_sblock(number_expr(space),1,0); p->content.sb=sb; p->type=SPACE; } else general_error(20); /* rorg is lower than current pc */ } else general_error(30); /* expression must be constant */ } else if(p->type==DATA&&bss) { if(lasterrsrc!=p->src||lasterrline!=p->line) { general_error(31); /* initialized data in bss */ lasterrsrc=p->src; lasterrline=p->line; } } #ifdef HAVE_CPU_OPTS else if(p->type==OPTS) cpu_opts(p->content.opts); #endif else if(p->type==PRINTTEXT) printf("%s\n",p->content.ptext); else if (p->type==PRINTEXPR) { taddr val; eval_expr(p->content.pexpr,&val,sec,sec->pc); printf("%ld (0x%lx)\n",(long)val,(unsigned long)val); } sec->pc+=atom_size(p,sec,sec->pc); } } }
int get_constant_u2(bool is_wide = false) const { return bytecode()->get_constant_u2(instruction_size()-2, cur_bc_raw(), is_wide); }
// Sign-extended index byte/short, no widening int get_constant_u1() const { return bytecode()->get_constant_u1(instruction_size()-1, cur_bc_raw()); }