龙芯教程
  • Introduction
  • 1 准备工作
  • 2 龙芯启动,向内核迈进
  • 3 完善输出功能
  • 4 例外与中断处理
  • 5 向量化例外与中断处理
Powered by GitBook
On this page
  • 5.1 准备工作
  • 5.2 编写向量化的例外与中断处理程序

5 向量化例外与中断处理

上一章编写了一个简单的例外与中断处理流程,在上一章的例外与中断处理流程中,每触发一个例外或者中断就需要将例外配置寄存器和例外状态寄存器读取一次。读取完成之后,还要计算并判断是属于哪一个例外或者中断。龙芯架构中支持向量化的例外与中断处理,为了避免上述问题,本章将编写一个向量化的例外与中断处理流程。

5.1 准备工作

在开始编写向量化的例外与中断处理流程之前,首先进行一些准备工作:

  • 编写例外与中断上下文结构体,struct pt_regs,其中描述了例外与中断发生后需要保存的上下文;

  • 在访问struct pt_regs结构体时,为访问方便,编写该结构体成员相对于该结构体首地址的偏移宏;

  • 32位与64位的指令会有些不一样,编写一些可移植性更强的宏操作;

  • 为编写例外与中断入口程序时方便,编写保存上下文的宏;

  • 编写一些宏方便在汇编文件中生成函数;

  • 参考项目仓库中的代码,移植include/loongarch.h文件。

编写例外与中断上下文结构体:

/* include/pt_regs.h */
#ifndef _PT_REGS_H
#define _PT_REGS_H

/*
 * This struct defines the way the registers are stored on the stack during
 * a system call/exception. If you add a register here, please also add it to
 * regoffset_table[] in arch/loongarch/kernel/ptrace.c.
 */
struct pt_regs {
	/* hardware irq number */
	unsigned long hwirq;
	/* Main processor registers. */
	unsigned long regs[32];

	/* Original syscall arg0. */
	unsigned long orig_a0;

	/* Special CSR registers. */
	unsigned long csr_era;
	unsigned long csr_badvaddr;
	unsigned long csr_crmd;
	unsigned long csr_prmd;
	unsigned long csr_euen;
	unsigned long csr_ecfg;
	unsigned long csr_estat;
	unsigned long __last[];
} __attribute__((aligned (8)));

#endif /* _PT_REGS_H */

编写struct pt_regs结构体成员相对于该结构体首地址的偏移宏:

/* include/asm-offsets.h */
#ifndef _ASM_OFFSETS_H
#define _ASM_OFFSETS_H

/* LoongArch pt_regs offsets. */
#define PT_HWIRQ 0
#define PT_R0 8 /* offsetof(struct pt_regs, regs[0]) */
#define PT_R1 16 /* offsetof(struct pt_regs, regs[1]) */
#define PT_R2 24 /* offsetof(struct pt_regs, regs[2]) */
#define PT_R3 32 /* offsetof(struct pt_regs, regs[3]) */
#define PT_R4 40 /* offsetof(struct pt_regs, regs[4]) */
#define PT_R5 48 /* offsetof(struct pt_regs, regs[5]) */
#define PT_R6 56 /* offsetof(struct pt_regs, regs[6]) */
#define PT_R7 64 /* offsetof(struct pt_regs, regs[7]) */
#define PT_R8 72 /* offsetof(struct pt_regs, regs[8]) */
#define PT_R9 80 /* offsetof(struct pt_regs, regs[9]) */
#define PT_R10 88 /* offsetof(struct pt_regs, regs[10]) */
#define PT_R11 96 /* offsetof(struct pt_regs, regs[11]) */
#define PT_R12 104 /* offsetof(struct pt_regs, regs[12]) */
#define PT_R13 112 /* offsetof(struct pt_regs, regs[13]) */
#define PT_R14 120 /* offsetof(struct pt_regs, regs[14]) */
#define PT_R15 128 /* offsetof(struct pt_regs, regs[15]) */
#define PT_R16 136 /* offsetof(struct pt_regs, regs[16]) */
#define PT_R17 144 /* offsetof(struct pt_regs, regs[17]) */
#define PT_R18 152 /* offsetof(struct pt_regs, regs[18]) */
#define PT_R19 160 /* offsetof(struct pt_regs, regs[19]) */
#define PT_R20 168 /* offsetof(struct pt_regs, regs[20]) */
#define PT_R21 176 /* offsetof(struct pt_regs, regs[21]) */
#define PT_R22 184 /* offsetof(struct pt_regs, regs[22]) */
#define PT_R23 192 /* offsetof(struct pt_regs, regs[23]) */
#define PT_R24 200 /* offsetof(struct pt_regs, regs[24]) */
#define PT_R25 208 /* offsetof(struct pt_regs, regs[25]) */
#define PT_R26 216 /* offsetof(struct pt_regs, regs[26]) */
#define PT_R27 224 /* offsetof(struct pt_regs, regs[27]) */
#define PT_R28 232 /* offsetof(struct pt_regs, regs[28]) */
#define PT_R29 240 /* offsetof(struct pt_regs, regs[29]) */
#define PT_R30 248 /* offsetof(struct pt_regs, regs[30]) */
#define PT_R31 256 /* offsetof(struct pt_regs, regs[31]) */
#define PT_CRMD 288 /* offsetof(struct pt_regs, csr_crmd) */
#define PT_PRMD 296 /* offsetof(struct pt_regs, csr_prmd) */
#define PT_EUEN 304 /* offsetof(struct pt_regs, csr_euen) */
#define PT_ECFG 312 /* offsetof(struct pt_regs, csr_ecfg) */
#define PT_ESTAT 320 /* offsetof(struct pt_regs, csr_estat) */
#define PT_ERA 272 /* offsetof(struct pt_regs, csr_era) */
#define PT_BVADDR 280 /* offsetof(struct pt_regs, csr_badvaddr) */
#define PT_ORIG_A0 264 /* offsetof(struct pt_regs, orig_a0) */
#define PT_SIZE 328 /* sizeof(struct pt_regs) */

#endif /* _ASM_OFFSETS_H */

编写一些可移植性更强的宏操作:

/* include/asm.h */
#ifndef _ASM_H
#define _ASM_H

/*
 * How to add/sub/load/store/shift C int variables.
 */
#if (__SIZEOF_INT__ == 4)
#define INT_ADD		add.w
#define INT_ADDI	addi.w
#define INT_SUB		sub.w
#define INT_L		ld.w
#define INT_S		st.w
#define INT_SLL		slli.w
#define INT_SLLV	sll.w
#define INT_SRL		srli.w
#define INT_SRLV	srl.w
#define INT_SRA		srai.w
#define INT_SRAV	sra.w
#endif

#if (__SIZEOF_INT__ == 8)
#define INT_ADD		add.d
#define INT_ADDI	addi.d
#define INT_SUB		sub.d
#define INT_L		ld.d
#define INT_S		st.d
#define INT_SLL		slli.d
#define INT_SLLV	sll.d
#define INT_SRL		srli.d
#define INT_SRLV	srl.d
#define INT_SRA		srai.d
#define INT_SRAV	sra.d
#endif

/*
 * How to add/sub/load/store/shift C long variables.
 */
#if (__SIZEOF_LONG__ == 4)
#define LONG_ADD	add.w
#define LONG_ADDI	addi.w
#define LONG_SUB	sub.w
#define LONG_L		ld.w
#define LONG_S		st.w
#define LONG_SLL	slli.w
#define LONG_SLLV	sll.w
#define LONG_SRL	srli.w
#define LONG_SRLV	srl.w
#define LONG_SRA	srai.w
#define LONG_SRAV	sra.w

#ifdef __ASSEMBLY__
#define LONG		.word
#endif
#define LONGSIZE	4
#define LONGMASK	3
#define LONGLOG		2
#endif

#if (__SIZEOF_LONG__ == 8)
#define LONG_ADD	add.d
#define LONG_ADDI	addi.d
#define LONG_SUB	sub.d
#define LONG_L		ld.d
#define LONG_S		st.d
#define LONG_SLL	slli.d
#define LONG_SLLV	sll.d
#define LONG_SRL	srli.d
#define LONG_SRLV	srl.d
#define LONG_SRA	srai.d
#define LONG_SRAV	sra.d

#ifdef __ASSEMBLY__
#define LONG		.dword
#endif
#define LONGSIZE	8
#define LONGMASK	7
#define LONGLOG		3
#endif

/*
 * How to add/sub/load/store/shift pointers.
 */
#if (__SIZEOF_POINTER__ == 4)
#define PTR_ADD		add.w
#define PTR_ADDI	addi.w
#define PTR_SUB		sub.w
#define PTR_L		ld.w
#define PTR_S		st.w
#define PTR_LI		li.w
#define PTR_SLL		slli.w
#define PTR_SLLV	sll.w
#define PTR_SRL		srli.w
#define PTR_SRLV	srl.w
#define PTR_SRA		srai.w
#define PTR_SRAV	sra.w

#define PTR_SCALESHIFT	2

#ifdef __ASSEMBLY__
#define PTR		.word
#endif
#define PTRSIZE		4
#define PTRLOG		2
#endif

#if (__SIZEOF_POINTER__ == 8)
#define PTR_ADD		add.d
#define PTR_ADDI	addi.d
#define PTR_SUB		sub.d
#define PTR_L		ld.d
#define PTR_S		st.d
#define PTR_LI		li.d
#define PTR_SLL		slli.d
#define PTR_SLLV	sll.d
#define PTR_SRL		srli.d
#define PTR_SRLV	srl.d
#define PTR_SRA		srai.d
#define PTR_SRAV	sra.d

#define PTR_SCALESHIFT	3

#ifdef __ASSEMBLY__
#define PTR		.dword
#endif
#define PTRSIZE		8
#define PTRLOG		3
#endif

#endif /* _ASM_H */

编写保存上下文的宏:

/* include/stackframe.h */
#ifndef _STACKFRAME_H
#define _STACKFRAME_H

#include <loongarch.h>
#include <regdef.h>
#include <asm.h>
#include <asm-offsets.h>

/* Make the addition of cfi info a little easier. */
	.macro cfi_rel_offset reg offset=0 docfi=0
	.if \docfi
	.cfi_rel_offset \reg, \offset
	.endif
	.endm

	.macro cfi_st reg offset=0 docfi=0
	cfi_rel_offset \reg, \offset, \docfi
	LONG_S	\reg, sp, \offset
	.endm

	.macro cfi_restore reg offset=0 docfi=0
	.if \docfi
	.cfi_restore \reg
	.endif
	.endm

	.macro cfi_ld reg offset=0 docfi=0
	LONG_L	\reg, sp, \offset
	cfi_restore \reg \offset \docfi
	.endm

	.macro BACKUP_T0T1
	csrwr	t0, EXCEPTION_KS0
	csrwr	t1, EXCEPTION_KS1
	.endm

	.macro RELOAD_T0T1
	csrrd   t0, EXCEPTION_KS0
	csrrd   t1, EXCEPTION_KS1
	.endm

	.macro	SAVE_TEMP docfi=0
	RELOAD_T0T1
	cfi_st	t0, PT_R12, \docfi
	cfi_st	t1, PT_R13, \docfi
	cfi_st	t2, PT_R14, \docfi
	cfi_st	t3, PT_R15, \docfi
	cfi_st	t4, PT_R16, \docfi
	cfi_st	t5, PT_R17, \docfi
	cfi_st	t6, PT_R18, \docfi
	cfi_st	t7, PT_R19, \docfi
	cfi_st	t8, PT_R20, \docfi
	.endm

	.macro	SAVE_STATIC docfi=0
	cfi_st	s0, PT_R23, \docfi
	cfi_st	s1, PT_R24, \docfi
	cfi_st	s2, PT_R25, \docfi
	cfi_st	s3, PT_R26, \docfi
	cfi_st	s4, PT_R27, \docfi
	cfi_st	s5, PT_R28, \docfi
	cfi_st	s6, PT_R29, \docfi
	cfi_st	s7, PT_R30, \docfi
	cfi_st	s8, PT_R31, \docfi
	.endm

	.macro	SAVE_SOME docfi=0
	csrrd	t1, LOONGARCH_CSR_PRMD
	andi	t1, t1, 0x3	/* extract pplv bit */
	move	t0, sp
	beqz	t1, 8f
	/* Called from user mode, new stack. */
	/* get_saved_sp docfi=\docfi */
8:
	PTR_ADDI sp, sp, -PT_SIZE
	.if \docfi
	.cfi_def_cfa sp, 0
	.endif
	cfi_st	t0, PT_R3, \docfi
	cfi_rel_offset  sp, PT_R3, \docfi
	LONG_S	zero, sp, PT_R0
	csrrd	t0, LOONGARCH_CSR_PRMD
	LONG_S	t0, sp, PT_PRMD
	csrrd	t0, LOONGARCH_CSR_CRMD
	LONG_S	t0, sp, PT_CRMD
	csrrd	t0, LOONGARCH_CSR_EUEN
	LONG_S  t0, sp, PT_EUEN
	csrrd	t0, LOONGARCH_CSR_ECFG
	LONG_S	t0, sp, PT_ECFG
	csrrd	t0, LOONGARCH_CSR_ESTAT
	PTR_S	t0, sp, PT_ESTAT
	cfi_st	ra, PT_R1, \docfi
	cfi_st	a0, PT_R4, \docfi
	cfi_st	a1, PT_R5, \docfi
	cfi_st	a2, PT_R6, \docfi
	cfi_st	a3, PT_R7, \docfi
	cfi_st	a4, PT_R8, \docfi
	cfi_st	a5, PT_R9, \docfi
	cfi_st	a6, PT_R10, \docfi
	cfi_st	a7, PT_R11, \docfi
	csrrd	ra, LOONGARCH_CSR_ERA
	LONG_S	ra, sp, PT_ERA
	.if \docfi
	.cfi_rel_offset ra, PT_ERA
	.endif
	cfi_st	tp, PT_R2, \docfi
	cfi_st	fp, PT_R22, \docfi

	/* Set thread_info if we're coming from user mode */
	csrrd	t0, LOONGARCH_CSR_PRMD
	andi	t0, t0, 0x3	/* extract pplv bit */
	beqz	t0, 9f

	/* li.d	tp, ~_THREAD_MASK */
	/* and	tp, tp, sp */
	/* cfi_st  u0, PT_R21, \docfi */
	/* csrrd	u0, PERCPU_BASE_KS */
9:
	.endm

	.macro	SAVE_ALL docfi=0
	SAVE_SOME \docfi
	SAVE_TEMP \docfi
	SAVE_STATIC \docfi
	.endm

	.macro	RESTORE_TEMP docfi=0
	cfi_ld	t0, PT_R12, \docfi
	cfi_ld	t1, PT_R13, \docfi
	cfi_ld	t2, PT_R14, \docfi
	cfi_ld	t3, PT_R15, \docfi
	cfi_ld	t4, PT_R16, \docfi
	cfi_ld	t5, PT_R17, \docfi
	cfi_ld	t6, PT_R18, \docfi
	cfi_ld	t7, PT_R19, \docfi
	cfi_ld	t8, PT_R20, \docfi
	.endm

	.macro	RESTORE_STATIC docfi=0
	cfi_ld	s0, PT_R23, \docfi
	cfi_ld	s1, PT_R24, \docfi
	cfi_ld	s2, PT_R25, \docfi
	cfi_ld	s3, PT_R26, \docfi
	cfi_ld	s4, PT_R27, \docfi
	cfi_ld	s5, PT_R28, \docfi
	cfi_ld	s6, PT_R29, \docfi
	cfi_ld	s7, PT_R30, \docfi
	cfi_ld	s8, PT_R31, \docfi
	.endm

	.macro	RESTORE_SOME docfi=0
	LONG_L	a0, sp, PT_PRMD
	andi    a0, a0, 0x3	/* extract pplv bit */
	beqz    a0, 8f
	/* cfi_ld  u0, PT_R21, \docfi */
8:
	LONG_L	a0, sp, PT_ERA
	csrwr	a0, LOONGARCH_CSR_ERA
	LONG_L	a0, sp, PT_PRMD
	csrwr	a0, LOONGARCH_CSR_PRMD
	cfi_ld	ra, PT_R1, \docfi
	cfi_ld	a0, PT_R4, \docfi
	cfi_ld	a1, PT_R5, \docfi
	cfi_ld	a2, PT_R6, \docfi
	cfi_ld	a3, PT_R7, \docfi
	cfi_ld	a4, PT_R8, \docfi
	cfi_ld	a5, PT_R9, \docfi
	cfi_ld	a6, PT_R10, \docfi
	cfi_ld	a7, PT_R11, \docfi
	cfi_ld	tp, PT_R2, \docfi
	cfi_ld	fp, PT_R22, \docfi
	.endm

	.macro	RESTORE_SP_AND_RET docfi=0
	cfi_ld	sp, PT_R3, \docfi
	ertn
	.endm

	.macro	RESTORE_ALL_AND_RET docfi=0
	RESTORE_STATIC \docfi
	RESTORE_TEMP \docfi
	RESTORE_SOME \docfi
	RESTORE_SP_AND_RET \docfi
	.endm

#endif /* _STACKFRAME_H */

编写一些宏方便在汇编文件中生成函数:

/* include/linkage.h */
#ifndef _LINKAGE_H
#define _LINKAGE_H

#define __ALIGN		.align 2
#define __ALIGN_STR	".align 2"

#ifndef __ALIGN
#define __ALIGN		.align 4,0x90
#define __ALIGN_STR	".align 4,0x90"
#endif

#define ALIGN __ALIGN

#define STT_NOTYPE  0
#define STT_OBJECT  1
#define STT_FUNC    2
#define STT_SECTION 3
#define STT_FILE    4
#define STT_COMMON  5
#define STT_TLS     6

/* Some toolchains use other characters (e.g. '`') to mark new line in macro */
#ifndef ASM_NL
#define ASM_NL		 ;
#endif

/* SYM_ENTRY -- use only if you have to for non-paired symbols */
#ifndef SYM_ENTRY
#define SYM_ENTRY(name, linkage, align...)		\
	linkage(name) ASM_NL				\
	align ASM_NL					\
	name:
#endif

/* SYM_T_FUNC -- type used by assembler to mark functions */
#ifndef SYM_T_FUNC
#define SYM_T_FUNC				STT_FUNC
#endif

/* SYM_L_* -- linkage of symbols */
#define SYM_L_GLOBAL(name)			.globl name
#define SYM_L_WEAK(name)			.weak name
#define SYM_L_LOCAL(name)			/* nothing */

/* SYM_A_* -- align the symbol? */
#define SYM_A_ALIGN				ALIGN
#define SYM_A_NONE				/* nothing */

/* SYM_START -- use only if you have to */
#ifndef SYM_START
#define SYM_START(name, linkage, align...)		\
	SYM_ENTRY(name, linkage, align)
#endif

/* SYM_END -- use only if you have to */
#ifndef SYM_END
#define SYM_END(name, sym_type)				\
	.type name sym_type ASM_NL			\
	.size name, .-name
#endif

#define SYM_FUNC_START(name)				\
	SYM_START(name, SYM_L_GLOBAL, SYM_A_ALIGN)	\
	.cfi_startproc;

#define SYM_FUNC_START_NOALIGN(name)			\
	SYM_START(name, SYM_L_GLOBAL, SYM_A_NONE)	\
	.cfi_startproc;

#define SYM_FUNC_START_LOCAL(name)			\
	SYM_START(name, SYM_L_LOCAL, SYM_A_ALIGN)	\
	.cfi_startproc;

#define SYM_FUNC_START_LOCAL_NOALIGN(name)		\
	SYM_START(name, SYM_L_LOCAL, SYM_A_NONE)	\
	.cfi_startproc;

#define SYM_FUNC_START_WEAK(name)			\
	SYM_START(name, SYM_L_WEAK, SYM_A_ALIGN)	\
	.cfi_startproc;

#define SYM_FUNC_START_WEAK_NOALIGN(name)		\
	SYM_START(name, SYM_L_WEAK, SYM_A_NONE)		\
	.cfi_startproc;

#define SYM_FUNC_END(name)				\
	.cfi_endproc;					\
	SYM_END(name, SYM_T_FUNC)

#endif /* _LINKAGE_H */

移植include/loongarch.h文件:根据项目仓库的main分支,复制即可。该文件中存在一些配置宏,配置通过编译选项设置,所以修改Makefile文件,如下所示:

/* Makefile */
CFLAGS = -O1 -g -DCONFIG_LOONGARCH64 -DCONFIG_VA_BITS_40 -DCONFIG_PAGE_SIZE_4KB -DCONFIG_PGTABLE_LEVELS=3 -Wall -march=loongarch64 -mabi=lp64s -ffreestanding -fno-common -nostdlib -fno-stack-protector -fno-pie -no-pie -c -I ./include/ -I ./lib/ -I ./lib/kernel/ -I kernel/
AFLAGS = -g -DCONFIG_LOONGARCH64 -DCONFIG_VA_BITS_40 -DCONFIG_PAGE_SIZE_4KB -DCONFIG_PGTABLE_LEVELS=3 -nostdinc -D__ASSEMBLY__ -fno-PIE -march=loongarch64 -mabi=lp64s -G0 -pipe -msoft-float -mexplicit-relocs -ffreestanding -mno-check-zero-division -c -I ./include/

5.2 编写向量化的例外与中断处理程序

编写生成中断处理入口程序的宏,do_vint函数后续实现:

/* loongarch/genex.S */
#include <stackframe.h>
#include <linkage.h>

	.macro	BUILD_VI_HANDLER num
	.align	5
SYM_FUNC_START(handle_vint_\num)
	BACKUP_T0T1
	SAVE_ALL
	addi.d	t0, zero, \num
	cfi_st	t0, PT_HWIRQ, 0
	move	a0, sp
	move	a1, sp
	la.abs	t0, do_vint
	jirl	ra, t0, 0
	RESTORE_ALL_AND_RET
SYM_FUNC_END(handle_vint_\num)
	.endm

生成中断处理入口程序,并生成中断处理入口程序向量数组:

/* loongarch/genex.S */
	BUILD_VI_HANDLER 0
	BUILD_VI_HANDLER 1
	BUILD_VI_HANDLER 2
	BUILD_VI_HANDLER 3
	BUILD_VI_HANDLER 4
	BUILD_VI_HANDLER 5
	BUILD_VI_HANDLER 6
	BUILD_VI_HANDLER 7
	BUILD_VI_HANDLER 8
	BUILD_VI_HANDLER 9
	BUILD_VI_HANDLER 10
	BUILD_VI_HANDLER 11
	BUILD_VI_HANDLER 12
	BUILD_VI_HANDLER 13

.section .data, "aw"
	.align	3
        .globl  vector_table
vector_table:
	PTR	handle_vint_0
	PTR	handle_vint_1
	PTR	handle_vint_2
	PTR	handle_vint_3
	PTR	handle_vint_4
	PTR	handle_vint_5
	PTR	handle_vint_6
	PTR	handle_vint_7
	PTR	handle_vint_8
	PTR	handle_vint_9
	PTR	handle_vint_10
	PTR	handle_vint_11
	PTR	handle_vint_12
	PTR	handle_vint_13

然后将该文件写入Makefile,该文件是汇编文件,编译选项使用AFLAGS。

# Makefile
$(BUILD_DIR)/genex.o: loongarch/genex.S
	$(CC) $(AFLAGS) $< -o $@

为了能够将本项目的代码方便移植到其他项目,当体系结构相关文件放在 os-elephant-dev/loongarch/ 目录下,与龙芯架构相关的头文件都在 include/ 目录下。将setup.h文件移到 os-elephant-dev/loongarch/ 目录下,并在setup_arch函数中写上例外与中断初始化的接口。

移动后修改Makefile中编译setup.o时的依赖。

/* include/setup.h */
#ifndef _SETUP_H
#define _SETUP_H

#define VECSIZE	0x200

extern void set_handler(unsigned long offset, void *addr, unsigned long len);

extern void per_cpu_trap_init(int cpu);
extern void setup_arch(void);
extern void trap_init(void);

#endif /* _SETUP_H */
/* loongarch/setup.c */
#include <setup.h>
#include <bootinfo.h>

unsigned long fw_arg0, fw_arg1, fw_arg2;
unsigned long kernelsp;

void setup_arch(void)
{
	/**
	 * 例外与中断的初始化
	 */
	per_cpu_trap_init(0);
}
# Makefile
$(BUILD_DIR)/setup.o: loongarch/setup.c
	$(CC) $(CFLAGS) $< -o $@

per_cpu_trap_init()函数实现如下所示:

/* loongarch/trap.c */
#include <setup.h>
#include <loongarch.h>
#include <pt_regs.h>
#include <cacheflush.h>
#include <stdio-kernel.h>
#include <string.h>

#define SZ_64K		0x00010000

/**
 * 普通例外与中断处理程序入口
 */
unsigned long eentry;
/**
 * tlb重填例外处理程序入口
 */
unsigned long tlbrentry;

long exception_handlers[VECSIZE * 128 / sizeof(long)] __attribute__((aligned(SZ_64K)));

/**
 * configure_exception_vector - 配置例外与中断处理程序入口
 */
static void configure_exception_vector(void)
{
	eentry = (unsigned long)exception_handlers;
	tlbrentry = (unsigned long)exception_handlers + 80 * VECSIZE;

	/**
	 * 设置普通例外与中断处理程序入口
	 */
	csr_write64(eentry, LOONGARCH_CSR_EENTRY);
	/**
	 * 设置机器错误例外处理程序入口
	 */
	csr_write64(eentry, LOONGARCH_CSR_MERRENTRY);
	/**
	 * 设置tlb重填例外处理程序入口
	 */
	csr_write64(tlbrentry, LOONGARCH_CSR_TLBRENTRY);
}

void per_cpu_trap_init(int cpu)
{
	/**
	 * 设置例外与中断处理程序的间距
	 */
	setup_vint_size(VECSIZE);
	/**
	 * 配置例外与中断处理程序入口
	 */
	configure_exception_vector();
}

loongarch/trap.c文件是一个新增文件,添加到Makefile中。在包含头文件时还包含了cacheflush.h文件,该文件的实现如下所示:

/* include/cacheflush.h */
#ifndef _CACHEFLUSH_H
#define _CACHEFLUSH_H

void local_flush_icache_range(unsigned long start, unsigned long end);

#endif /* _CACHEFLUSH_H */
/* loongarch/cache.c */
#include <cacheflush.h>

/* Cache operations. */
void local_flush_icache_range(unsigned long start, unsigned long end)
{
	asm volatile ("\tibar 0\n"::);
}

编写例外与中断处理入口程序C语言部分,注意这里修改了do_irq()函数的接口,后面会调整一些体系结构无关代码:

/* loongarch/trap.c */
extern void do_irq(struct pt_regs *regs, uint64_t virq);

/**
 * hwirq_to_virq - 硬件中断号转换为虚拟中断号
 * @hwirq: 硬件中断号
 */
static unsigned long hwirq_to_virq(unsigned long hwirq)
{
	return EXCCODE_INT_START + hwirq;
}

/**
 * do_vint - 中断处理入口程序(C语言部分)
 * @regs: 指向中断栈内容
 * @sp: 中断栈指针
 * regs == sp
 */
void do_vint(struct pt_regs *regs, unsigned long sp)
{
	unsigned long hwirq = *(unsigned long *)regs;
	unsigned long virq;

	virq = hwirq_to_virq(hwirq);
	do_irq(regs, virq);
}

编写例外与中断处理程序初始化:

/* loongarch/trap.c */
extern void *vector_table[];

/**
 * set_handler - 设置例外处理程序句柄
 *
 * @offset: 相对于例外处理程序入口地址的偏移量
 * @addr: 例外处理程序地址
 * @size: 例外处理程序大小
 */
void set_handler(unsigned long offset, void *addr, unsigned long size)
{
	memcpy((void *)eentry + offset, addr, size);
	local_flush_icache_range(eentry + offset, eentry + offset + size);
}

/**
 * trap_init - 例外与中断处理初始化
 */
void trap_init(void)
{
	unsigned long i;
	void *vector_start;
	unsigned long tcfg = 0x01000000UL | (1U << 0) | (1U << 1);
	unsigned long ecfg;

	/**
	 * 清空中断状态
	 */
	clear_csr_ecfg(ECFG0_IM);
	clear_csr_estat(ESTATF_IP);

	/**
	 * 初始化中断处理入口程序
	 */
	for (i = EXCCODE_INT_START; i < EXCCODE_INT_END; i++) {
		vector_start = vector_table[i - EXCCODE_INT_START];
		set_handler(i * VECSIZE, vector_start, VECSIZE);
	}

	local_flush_icache_range(eentry, eentry + 0x400);

	write_csr_tcfg(tcfg);
	ecfg = read_csr_ecfg();
	change_csr_ecfg(CSR_ECFG_IM, ecfg | 0x1 << 11);
}

接下来,调整体系结构无关代码,现在例外与中断处理函数的接口变了,所以修改一下函数指针声明。为了不影响x86架构的代码,使用条件编译避免修改x86架构的指针声明。并且将体系结构无关代码的初始化函数换一个函数名,即irq_init()函数:

/* kernel/interrupt.h */
#ifndef CONFIG_LOONGARCH64

typedef void* intr_handler;
void idt_init(void);

#else

#include <pt_regs.h>

typedef void (*intr_handler)(struct pt_regs *regs);
void irq_init(void);

#endif

修改体系结构无关代码中,中断处理程序初始化函数:

/* kernel/irq.c */
void irq_init(void)
{
	exception_init();
	register_handler(EXCCODE_TIMER, timer_interrupt);

	printk("irq_init done\n");
}

修改do_irq()函数:

/* kernel/irq.c */
void do_irq(struct pt_regs *regs, uint64_t virq)
{
	// printk("virq = %d ", virq);
	intr_table[virq](regs);
}

修改通用中断处理程序:

/* kernel/irq.c */
static void general_intr_handler(struct pt_regs *regs)
{
	unsigned long hwirq = *(unsigned long *)regs;
	unsigned long virq = EXCCODE_INT_START + hwirq;
	printk("!!!!!!!      exception message begin  !!!!!!!!\n");
	printk("intr_table[%d]: %s happened", intr_name[virq]);
	printk("\n!!!!!!!      exception message end    !!!!!!!!\n");
	while(1);
}

修改临时的时钟中断处理程序:

/* kernel/irq.c */
void timer_interrupt(struct pt_regs *regs)
{
	printk("timer interrupt\n");
	/* ack */
	write_csr_ticlr(read_csr_ticlr() | (0x1 << 0));
}

删除trap_handler()函数、trap_entry.S文件。删除trap_entry.S文件之后,在Makfile中也删除$(BUILD_DIR)/trap_entry.o编译目标。最后在init_all()函数中进行测试:

/* kernel/init.c */
#ifdef CONFIG_LOONGARCH64
#include <setup.h>
#include <ns16550a.h>
#endif

#ifdef CONFIG_LOONGARCH64
extern void irq_init(void);
#endif

/*负责初始化所有模块 */
void init_all()
{
	char str[] = "os-loongson";
	int a = 1, b = 16;
#ifdef CONFIG_LOONGARCH64
	serial_ns16550a_init(9600);
	put_str("hello os-loongson\n");
#endif
	put_str("init_all\n");
	printk("hello %s-%c%d.%d\n", str, 'v', 0, a);
	printk("init_all: 0x%x\n", b);
#ifndef CONFIG_LOONGARCH64
	idt_init();	     // 初始化中断
#else
	setup_arch();
	trap_init();
	irq_init();
	intr_enable();
#endif
	while(1);
	// mem_init();	     // 初始化内存管理系统
	// thread_init();    // 初始化线程相关结构
	// timer_init();     // 初始化PIT
	// console_init();   // 控制台初始化最好放在开中断之前
	// keyboard_init();  // 键盘初始化
	// tss_init();       // tss初始化
	// syscall_init();   // 初始化系统调用
	// intr_enable();    // 后面的ide_init需要打开中断
	// ide_init();	     // 初始化硬盘
	// filesys_init();   // 初始化文件系统
}
Previous4 例外与中断处理

Last updated 1 year ago