diff options
| -rw-r--r-- | arch/loongarch/Kconfig | 5 | ||||
| -rw-r--r-- | arch/loongarch/Kconfig.debug | 1 | ||||
| -rw-r--r-- | arch/loongarch/include/asm/hw_breakpoint.h | 4 | ||||
| -rw-r--r-- | arch/loongarch/kernel/hw_breakpoint.c | 96 | ||||
| -rw-r--r-- | arch/loongarch/kernel/ptrace.c | 47 | ||||
| -rw-r--r-- | arch/loongarch/kvm/exit.c | 2 | 
6 files changed, 91 insertions, 64 deletions
diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index e38139c576ee..ddc042895d01 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -143,7 +143,7 @@ config LOONGARCH  	select HAVE_LIVEPATCH  	select HAVE_MOD_ARCH_SPECIFIC  	select HAVE_NMI -	select HAVE_OBJTOOL if AS_HAS_EXPLICIT_RELOCS +	select HAVE_OBJTOOL if AS_HAS_EXPLICIT_RELOCS && AS_HAS_THIN_ADD_SUB && !CC_IS_CLANG  	select HAVE_PCI  	select HAVE_PERF_EVENTS  	select HAVE_PERF_REGS @@ -261,6 +261,9 @@ config AS_HAS_EXPLICIT_RELOCS  config AS_HAS_FCSR_CLASS  	def_bool $(as-instr,movfcsr2gr \$t0$(comma)\$fcsr0) +config AS_HAS_THIN_ADD_SUB +	def_bool $(cc-option,-Wa$(comma)-mthin-add-sub) +  config AS_HAS_LSX_EXTENSION  	def_bool $(as-instr,vld \$vr0$(comma)\$a0$(comma)0) diff --git a/arch/loongarch/Kconfig.debug b/arch/loongarch/Kconfig.debug index 98d60630c3d4..8b2ce5b5d43e 100644 --- a/arch/loongarch/Kconfig.debug +++ b/arch/loongarch/Kconfig.debug @@ -28,6 +28,7 @@ config UNWINDER_PROLOGUE  config UNWINDER_ORC  	bool "ORC unwinder" +	depends on HAVE_OBJTOOL  	select OBJTOOL  	help  	  This option enables the ORC (Oops Rewind Capability) unwinder for diff --git a/arch/loongarch/include/asm/hw_breakpoint.h b/arch/loongarch/include/asm/hw_breakpoint.h index 21447fb1efc7..d78330916bd1 100644 --- a/arch/loongarch/include/asm/hw_breakpoint.h +++ b/arch/loongarch/include/asm/hw_breakpoint.h @@ -75,6 +75,8 @@ do {								\  #define CSR_MWPC_NUM		0x3f  #define CTRL_PLV_ENABLE		0x1e +#define CTRL_PLV0_ENABLE	0x02 +#define CTRL_PLV3_ENABLE	0x10  #define MWPnCFG3_LoadEn		8  #define MWPnCFG3_StoreEn	9 @@ -101,7 +103,7 @@ struct perf_event;  struct perf_event_attr;  extern int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl, -				  int *gen_len, int *gen_type, int *offset); +				  int *gen_len, int *gen_type);  extern int arch_check_bp_in_kernelspace(struct arch_hw_breakpoint *hw);  extern int hw_breakpoint_arch_parse(struct perf_event *bp,  				    const struct perf_event_attr *attr, diff --git a/arch/loongarch/kernel/hw_breakpoint.c b/arch/loongarch/kernel/hw_breakpoint.c index fc55c4de2a11..621ad7634df7 100644 --- a/arch/loongarch/kernel/hw_breakpoint.c +++ b/arch/loongarch/kernel/hw_breakpoint.c @@ -174,11 +174,21 @@ void flush_ptrace_hw_breakpoint(struct task_struct *tsk)  static int hw_breakpoint_control(struct perf_event *bp,  				 enum hw_breakpoint_ops ops)  { -	u32 ctrl; +	u32 ctrl, privilege;  	int i, max_slots, enable; +	struct pt_regs *regs;  	struct perf_event **slots;  	struct arch_hw_breakpoint *info = counter_arch_bp(bp); +	if (arch_check_bp_in_kernelspace(info)) +		privilege = CTRL_PLV0_ENABLE; +	else +		privilege = CTRL_PLV3_ENABLE; + +	/*  Whether bp belongs to a task. */ +	if (bp->hw.target) +		regs = task_pt_regs(bp->hw.target); +  	if (info->ctrl.type == LOONGARCH_BREAKPOINT_EXECUTE) {  		/* Breakpoint */  		slots = this_cpu_ptr(bp_on_reg); @@ -197,31 +207,38 @@ static int hw_breakpoint_control(struct perf_event *bp,  	switch (ops) {  	case HW_BREAKPOINT_INSTALL:  		/* Set the FWPnCFG/MWPnCFG 1~4 register. */ -		write_wb_reg(CSR_CFG_ADDR, i, 0, info->address); -		write_wb_reg(CSR_CFG_ADDR, i, 1, info->address); -		write_wb_reg(CSR_CFG_MASK, i, 0, info->mask); -		write_wb_reg(CSR_CFG_MASK, i, 1, info->mask); -		write_wb_reg(CSR_CFG_ASID, i, 0, 0); -		write_wb_reg(CSR_CFG_ASID, i, 1, 0);  		if (info->ctrl.type == LOONGARCH_BREAKPOINT_EXECUTE) { -			write_wb_reg(CSR_CFG_CTRL, i, 0, CTRL_PLV_ENABLE); +			write_wb_reg(CSR_CFG_ADDR, i, 0, info->address); +			write_wb_reg(CSR_CFG_MASK, i, 0, info->mask); +			write_wb_reg(CSR_CFG_ASID, i, 0, 0); +			write_wb_reg(CSR_CFG_CTRL, i, 0, privilege);  		} else { +			write_wb_reg(CSR_CFG_ADDR, i, 1, info->address); +			write_wb_reg(CSR_CFG_MASK, i, 1, info->mask); +			write_wb_reg(CSR_CFG_ASID, i, 1, 0);  			ctrl = encode_ctrl_reg(info->ctrl); -			write_wb_reg(CSR_CFG_CTRL, i, 1, ctrl | CTRL_PLV_ENABLE); +			write_wb_reg(CSR_CFG_CTRL, i, 1, ctrl | privilege);  		}  		enable = csr_read64(LOONGARCH_CSR_CRMD);  		csr_write64(CSR_CRMD_WE | enable, LOONGARCH_CSR_CRMD); +		if (bp->hw.target) +			regs->csr_prmd |= CSR_PRMD_PWE;  		break;  	case HW_BREAKPOINT_UNINSTALL:  		/* Reset the FWPnCFG/MWPnCFG 1~4 register. */ -		write_wb_reg(CSR_CFG_ADDR, i, 0, 0); -		write_wb_reg(CSR_CFG_ADDR, i, 1, 0); -		write_wb_reg(CSR_CFG_MASK, i, 0, 0); -		write_wb_reg(CSR_CFG_MASK, i, 1, 0); -		write_wb_reg(CSR_CFG_CTRL, i, 0, 0); -		write_wb_reg(CSR_CFG_CTRL, i, 1, 0); -		write_wb_reg(CSR_CFG_ASID, i, 0, 0); -		write_wb_reg(CSR_CFG_ASID, i, 1, 0); +		if (info->ctrl.type == LOONGARCH_BREAKPOINT_EXECUTE) { +			write_wb_reg(CSR_CFG_ADDR, i, 0, 0); +			write_wb_reg(CSR_CFG_MASK, i, 0, 0); +			write_wb_reg(CSR_CFG_CTRL, i, 0, 0); +			write_wb_reg(CSR_CFG_ASID, i, 0, 0); +		} else { +			write_wb_reg(CSR_CFG_ADDR, i, 1, 0); +			write_wb_reg(CSR_CFG_MASK, i, 1, 0); +			write_wb_reg(CSR_CFG_CTRL, i, 1, 0); +			write_wb_reg(CSR_CFG_ASID, i, 1, 0); +		} +		if (bp->hw.target) +			regs->csr_prmd &= ~CSR_PRMD_PWE;  		break;  	} @@ -283,7 +300,7 @@ int arch_check_bp_in_kernelspace(struct arch_hw_breakpoint *hw)   * to generic breakpoint descriptions.   */  int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl, -			   int *gen_len, int *gen_type, int *offset) +			   int *gen_len, int *gen_type)  {  	/* Type */  	switch (ctrl.type) { @@ -303,11 +320,6 @@ int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl,  		return -EINVAL;  	} -	if (!ctrl.len) -		return -EINVAL; - -	*offset = __ffs(ctrl.len); -  	/* Len */  	switch (ctrl.len) {  	case LOONGARCH_BREAKPOINT_LEN_1: @@ -386,21 +398,17 @@ int hw_breakpoint_arch_parse(struct perf_event *bp,  			     struct arch_hw_breakpoint *hw)  {  	int ret; -	u64 alignment_mask, offset; +	u64 alignment_mask;  	/* Build the arch_hw_breakpoint. */  	ret = arch_build_bp_info(bp, attr, hw);  	if (ret)  		return ret; -	if (hw->ctrl.type != LOONGARCH_BREAKPOINT_EXECUTE) -		alignment_mask = 0x7; -	else +	if (hw->ctrl.type == LOONGARCH_BREAKPOINT_EXECUTE) {  		alignment_mask = 0x3; -	offset = hw->address & alignment_mask; - -	hw->address &= ~alignment_mask; -	hw->ctrl.len <<= offset; +		hw->address &= ~alignment_mask; +	}  	return 0;  } @@ -471,12 +479,15 @@ void breakpoint_handler(struct pt_regs *regs)  	slots = this_cpu_ptr(bp_on_reg);  	for (i = 0; i < boot_cpu_data.watch_ireg_count; ++i) { -		bp = slots[i]; -		if (bp == NULL) -			continue; -		perf_bp_event(bp, regs); +		if ((csr_read32(LOONGARCH_CSR_FWPS) & (0x1 << i))) { +			bp = slots[i]; +			if (bp == NULL) +				continue; +			perf_bp_event(bp, regs); +			csr_write32(0x1 << i, LOONGARCH_CSR_FWPS); +			update_bp_registers(regs, 0, 0); +		}  	} -	update_bp_registers(regs, 0, 0);  }  NOKPROBE_SYMBOL(breakpoint_handler); @@ -488,12 +499,15 @@ void watchpoint_handler(struct pt_regs *regs)  	slots = this_cpu_ptr(wp_on_reg);  	for (i = 0; i < boot_cpu_data.watch_dreg_count; ++i) { -		wp = slots[i]; -		if (wp == NULL) -			continue; -		perf_bp_event(wp, regs); +		if ((csr_read32(LOONGARCH_CSR_MWPS) & (0x1 << i))) { +			wp = slots[i]; +			if (wp == NULL) +				continue; +			perf_bp_event(wp, regs); +			csr_write32(0x1 << i, LOONGARCH_CSR_MWPS); +			update_bp_registers(regs, 0, 1); +		}  	} -	update_bp_registers(regs, 0, 1);  }  NOKPROBE_SYMBOL(watchpoint_handler); diff --git a/arch/loongarch/kernel/ptrace.c b/arch/loongarch/kernel/ptrace.c index c114c5ef1332..200109de1971 100644 --- a/arch/loongarch/kernel/ptrace.c +++ b/arch/loongarch/kernel/ptrace.c @@ -494,28 +494,14 @@ static int ptrace_hbp_fill_attr_ctrl(unsigned int note_type,  				     struct arch_hw_breakpoint_ctrl ctrl,  				     struct perf_event_attr *attr)  { -	int err, len, type, offset; +	int err, len, type; -	err = arch_bp_generic_fields(ctrl, &len, &type, &offset); +	err = arch_bp_generic_fields(ctrl, &len, &type);  	if (err)  		return err; -	switch (note_type) { -	case NT_LOONGARCH_HW_BREAK: -		if ((type & HW_BREAKPOINT_X) != type) -			return -EINVAL; -		break; -	case NT_LOONGARCH_HW_WATCH: -		if ((type & HW_BREAKPOINT_RW) != type) -			return -EINVAL; -		break; -	default: -		return -EINVAL; -	} -  	attr->bp_len	= len;  	attr->bp_type	= type; -	attr->bp_addr	+= offset;  	return 0;  } @@ -609,10 +595,27 @@ static int ptrace_hbp_set_ctrl(unsigned int note_type,  		return PTR_ERR(bp);  	attr = bp->attr; -	decode_ctrl_reg(uctrl, &ctrl); -	err = ptrace_hbp_fill_attr_ctrl(note_type, ctrl, &attr); -	if (err) -		return err; + +	switch (note_type) { +	case NT_LOONGARCH_HW_BREAK: +		ctrl.type = LOONGARCH_BREAKPOINT_EXECUTE; +		ctrl.len = LOONGARCH_BREAKPOINT_LEN_4; +		break; +	case NT_LOONGARCH_HW_WATCH: +		decode_ctrl_reg(uctrl, &ctrl); +		break; +	default: +		return -EINVAL; +	} + +	if (uctrl & CTRL_PLV_ENABLE) { +		err = ptrace_hbp_fill_attr_ctrl(note_type, ctrl, &attr); +		if (err) +			return err; +		attr.disabled = 0; +	} else { +		attr.disabled = 1; +	}  	return modify_user_hw_breakpoint(bp, &attr);  } @@ -643,6 +646,10 @@ static int ptrace_hbp_set_addr(unsigned int note_type,  	struct perf_event *bp;  	struct perf_event_attr attr; +	/* Kernel-space address cannot be monitored by user-space */ +	if ((unsigned long)addr >= XKPRANGE) +		return -EINVAL; +  	bp = ptrace_hbp_get_initialised_bp(note_type, tsk, idx);  	if (IS_ERR(bp))  		return PTR_ERR(bp); diff --git a/arch/loongarch/kvm/exit.c b/arch/loongarch/kvm/exit.c index c86e099af5ca..a68573e091c0 100644 --- a/arch/loongarch/kvm/exit.c +++ b/arch/loongarch/kvm/exit.c @@ -761,7 +761,7 @@ static void kvm_handle_service(struct kvm_vcpu *vcpu)  	default:  		ret = KVM_HCALL_INVALID_CODE;  		break; -	}; +	}  	kvm_write_reg(vcpu, LOONGARCH_GPR_A0, ret);  }  | 
