Page MenuHomeFreeBSD

loongarch: add initial kernel support
Needs ReviewPublic

Authored by zhaoxiaoqiang007_gmail.com on Tue, Sep 22, 2:23 PM.
Tags
None
Referenced Files
F173619577: D59890.diff
Sun, Sep 27, 5:39 AM
Unknown Object (File)
Thu, Sep 24, 6:32 AM
Unknown Object (File)
Thu, Sep 24, 4:48 AM
Unknown Object (File)
Wed, Sep 23, 8:40 AM
Unknown Object (File)
Wed, Sep 23, 8:24 AM
Unknown Object (File)
Tue, Sep 22, 9:59 PM
Unknown Object (File)
Tue, Sep 22, 7:20 PM
Unknown Object (File)
Tue, Sep 22, 7:17 PM

Details

Summary

Add the LoongArch machine code. Including pmap, trap handling,
exception vectors, interrupt controllers, timer, SMP, bus_dma,
ptrace and DDB support, and FDT boot glue, plus small hooks in MI code.
Currently only QEMU is supported.

The port heavily references the existing riscv code.

LoongArch is a new RISC ISA from Loongson Technology, a bit like MIPS
or RISC-V. It defines four privilege levels, PLV0~PLV3: the kernel
runs at PLV0 and applications at PLV3. This port targets the 64-bit
variant (LA64): 32 GPRs, 32 FPRs, optional LSX/LASX vector extensions,
privileged-mode CSRs, a software-managed TLB with ASIDs, and DMWn
direct-mapped address windows used for the kernel direct map.

References:
https://github.com/loongson/LoongArch-Documentation/releases/latest/download/LoongArch-Vol1-v1.00-EN.pdf
https://github.com/loongson/LoongArch-Documentation/releases/latest/download/LoongArch-ELF-ABI-v1.00-EN.pdf
https://loongson.github.io/LoongArch-Documentation/
http://www.loongson.cn/
Signed-off-by: Xiaoqiang Zhao <zhaoxiaoqiang007@gmail.com>

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Skipped
Unit
Tests Skipped
Build Status
Buildable 77168
Build 74051: arc lint + arc unit

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes

I see you have a QEMU kernel. It would be super helpful to have the qemu recipe to give this a test spin.

sys/loongarch/loongarch/uma_machdep.c
39

Is this file getting compiled? vmparam.h defines UMA_USE_DMAP.

sys/loongarch/loongarch/watch.c
4

Missing copyright assignee?

sys/vm/uma_core.c
3655

This is a standalone change, it should go into a separate diff. Same with the memguard.c change.

sys/loongarch/include/loongarchreg.h
278

CSR_DMW0_VSEG has type int, so shifting it by 48 triggers -Wshift-count-overflow. Could we use _ULCAST_ here?

sys/loongarch/include/tlb.h
95

Could the info and the addr be annotated with __unused? This causes buildworld to fail with -Wunused-parameter.

sys/loongarch/include/atomic.h
585

Maybe we need a const here to pass the buildkernel

The number of registers and the comments need to be revised. I have already left a comment at the corresponding code location.

sys/loongarch/include/setjmp.h
40–41

Here is the code in the same style as the RISC-V setjmp.h, but it should be updated with the LoongArch architecture information.

It should be changed to:

#define _JBLEN          40      /* fp, s0-s8, ra, fs0-fs7, magic val, sigmask */
#define _JB_SIGMASK     21

Reference documentation: https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html

The number of registers and the comments need to be revised. I have already left a comment at the corresponding code location.

Thanks for reviewing !
I will take a closer look later.

sys/loongarch/include/loongarchreg.h
278

CSR_DMW0_VSEG has type int, so shifting it by 48 triggers -Wshift-count-overflow. Could we use _ULCAST_ here?

Good catch !

sys/loongarch/include/tlb.h
95

Could the info and the addr be annotated with __unused? This causes buildworld to fail with -Wunused-parameter.

Right !

sys/loongarch/loongarch/uma_machdep.c
39

Is this file getting compiled? vmparam.h defines UMA_USE_DMAP.

oops! Will delete. in next update.

Wrap the assembly fence() in atomic.h, For consistency in code style, use the fence() wrapper newly added in machine/atomic.h. This way, if changes are needed later, only the fence() wrapper has to be modified.

contrib/ofed/include/udma_barrier.h
112

For consistency in code style, use the fence() wrapper newly added in machine/atomic.h. This way, if changes are needed later, only the fence() wrapper has to be modified.

Change it to:

#include <sys/types.h>
#include <machine/atomic.h>
#define udma_to_device_barrier() fence()
152

For consistency in code style, use the fence() wrapper newly added in machine/atomic.h. This way, if changes are needed later, only the fence() wrapper has to be modified.

Change it to:

#define udma_from_device_barrier() fence()
224

For consistency in code style, use the fence() wrapper newly added in machine/atomic.h. This way, if changes are needed later, only the fence() wrapper has to be modified.

Change it to:

#define mmio_flush_writes() fence()
sys/loongarch/include/atomic.h
75

Wrap the assembly fence() in atomic.h

#define fence()         c_sync()

Perhaps it would be more appropriate to use the fence() wrapper newly added in machine/atomic.h.

sys/dev/sym/sym_hipd.c
132

Perhaps it would be more appropriate to use the fence() wrapper newly added in machine/atomic.h.

Please clarify whether this is because the pmap has not yet been initialized, or because DMW0 has already been configured during the early console stage. This does not look like something applicable to all LoongArch platforms, and it seems to be bypassing the normal mapping interface.

sys/dev/uart/uart_cpu_loongarch.c
82–85

DMW0 VA is hardcoded directly as the bus_space_handle_t, without calling bus_space_map().
This is a board/firmware-specific magic number, so it is not appropriate to put it in generic kernel code.

Because sys/kern/link_elf_obj.c is a machine-independent file, LoongArch-specific macros cannot be expanded directly in it.

sys/kern/link_elf_obj.c
1004–1005

Because sys/kern/link_elf_obj.c is a machine-independent file, LoongArch-specific macros cannot be expanded directly in it.
Here you are performing arithmetic with VM_MIN_KERNEL_ADDRESS + LA_KMOD_VA_SKIP + LA_KMOD_VA_SIZE directly in MI code, which exposes MD page table layout knowledge at the MI layer.

1009–1015

Because sys/kern/link_elf_obj.c is a machine-independent file, LoongArch-specific macros cannot be expanded directly in it.

You should not delete _bus_dmamap_addsegs() ; please pull the latest main branch code.

sys/kern/subr_busdma_bounce.c
494–515

You should not delete _bus_dmamap_addsegs() and busdma_thread(); please pull the latest main branch code.

sys/kern/link_elf_obj.c
1004–1005

We already do the same for amd64 as you can see, this is really a minor sin. Though, there is something similar happening in kva_import().

It might be that all platforms should define constants for the beginning and end of some reserved KVA space dedicated for kernel modules. We should perhaps go even further and define a submap for that region on platforms that need one.

Please complete the binary format specifier macros PRIb* / PRIB* / SCNb*. Upstream RISC-V/ARM64 _inttypes.h already includes them, and the Loong64 LP64 ABI determines the width selection.

https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html

sys/loongarch/include/_inttypes.h
132

Please complete the binary format specifier macros PRIb* / PRIB* / SCNb*. Upstream RISC-V/ARM64 _inttypes.h already includes them, and the Loong64 LP64 ABI determines the width selection.

https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html

#define PRIb8           "b"     /* uint8_t */
#define PRIb16          "b"     /* uint16_t */
#define PRIb32          "b"     /* uint32_t */
#define PRIb64          "lb"    /* uint64_t */
#define PRIbLEAST8      "b"     /* uint_least8_t */
#define PRIbLEAST16     "b"     /* uint_least16_t */
#define PRIbLEAST32     "b"     /* uint_least32_t */
#define PRIbLEAST64     "lb"    /* uint_least64_t */
#define PRIbFAST8       "b"     /* uint_fast8_t */
#define PRIbFAST16      "b"     /* uint_fast16_t */
#define PRIbFAST32      "b"     /* uint_fast32_t */
#define PRIbFAST64      "lb"    /* uint_fast64_t */
#define PRIbMAX         "jb"    /* uintmax_t */
#define PRIbPTR         "lb"    /* uintptr_t */

#define PRIB8           "B"     /* uint8_t */
#define PRIB16          "B"     /* uint16_t */
#define PRIB32          "B"     /* uint32_t */
#define PRIB64          "lB"    /* uint64_t */
#define PRIBLEAST8      "B"     /* uint_least8_t */
#define PRIBLEAST16     "B"     /* uint_least16_t */
#define PRIBLEAST32     "B"     /* uint_least32_t */
#define PRIBLEAST64     "lB"    /* uint_least64_t */
#define PRIBFAST8       "B"     /* uint_fast8_t */
#define PRIBFAST16      "B"     /* uint_fast16_t */
#define PRIBFAST32      "B"     /* uint_fast32_t */
#define PRIBFAST64      "lB"    /* uint_fast64_t */
#define PRIBMAX         "jB"    /* uintmax_t */
#define PRIBPTR         "lB"    /* uintptr_t */
211

add

#define SCNb8           "hhb"   /* uint8_t */
#define SCNb16          "hb"    /* uint16_t */
#define SCNb32          "b"     /* uint32_t */
#define SCNb64          "lb"    /* uint64_t */
#define SCNbLEAST8      "hhb"   /* uint_least8_t */
#define SCNbLEAST16     "hb"    /* uint_least16_t */
#define SCNbLEAST32     "b"     /* uint_least32_t */
#define SCNbLEAST64     "lb"    /* uint_least64_t */
#define SCNbFAST8       "b"     /* uint_fast8_t */
#define SCNbFAST16      "b"     /* uint_fast16_t */
#define SCNbFAST32      "b"     /* uint_fast32_t */
#define SCNbFAST64      "lb"    /* uint_fast64_t */
#define SCNbMAX         "jb"    /* uintmax_t */
#define SCNbPTR         "lb"    /* uintptr_t */

There is no functional issue here, but the placement of the bit-width macros is inconsistent with upstream conventions. They could be moved to the front of the file, consistent with the placement in other architectures, to improve code readability.

sys/loongarch/include/_limits.h
80–82

There is no functional issue here, but the placement of the bit-width macros is inconsistent with upstream conventions. They could be moved to the front of the file, consistent with the placement in other architectures, to improve code readability.

#define	__CHAR_BIT	8		/* number of bits in a char */
#define __SHRT	16		/* number of bits in a short */
#define __INT_BIT	32		/* number of bits in an int */
#define __LLONG_BIT	64		/* number of bits in a long long */

C++ conditional guards should not be added here; adding them here would make the behavior inconsistent with other architectures.

sys/loongarch/include/_stdint.h
34

C++ conditional guards should not be added here; adding them here would make the behavior inconsistent with other architectures.

51

C++ conditional guards should not be added here; adding them here would make the behavior inconsistent with other architectures.

According to the LoongArch ELF ABI register convention, $r21 is marked as Reserved (Non-allocatable) and cannot be used as a PCPU pointer; you can use $tp instead: LoongArch ABI thread pointer register is $tp (r2).

https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html

sys/loongarch/include/asm.h
59–62

According to the LoongArch ELF ABI register convention, $r21 is marked as Reserved (Non-allocatable) and cannot be used as a PCPU pointer; you can use $tp instead: LoongArch ABI thread pointer register is $tp (r2).

Here $t0 is hardcoded as the temporary register for SC and communicated to the compiler via the "t0" clobber, but I think a better approach would be to use the output register res directly for the SC, without needing an extra temporary register.

sys/loongarch/include/atomic.h
252–254

Here $t0 is hardcoded as the temporary register for SC and communicated to the compiler via the "t0" clobber, but I think a better approach would be to use the output register res directly for the SC, without needing an extra temporary register.

Suggested change to:

			"or %1, %5, $zero\n"	/* newval -> res */
			"sc.w   %1, %2\n"		/* Try to store new value */
			"beqz  1, 0b\n"		/* Retry on sc failure */
263

Suggested change to:

			: "memory");
429–431

Here $t0 is hardcoded as the temporary register for SC and communicated to the compiler via the "t0" clobber, but I think a better approach would be to use the output register res directly for the SC, without needing an extra temporary register.

Suggested change to:

			"or %1, %5, $zero"		/* newval -> res */
			"sc.d   %1, %2\n"		/* Try to store new value */
			"beqz   %1, 0b\n"		/* Retry on failure */
440

Suggested change to:

			: "memory");

bus_dma_impl.h uses the old busdma internal API (filter / parent / ref_count) that has already been removed upstream in FreeBSD.

Since both busdma_machdep.c and busdma_bounce.c are implemented against this old contract, these three files must be updated as a set. You can refer to bus_dma_impl.h, busdma_machdep.c, and busdma_bounce.c under sys/riscv/riscv/.

bus_dma.h is just an inline wrapper and does not access the old fields, so it can be kept.

sys/loongarch/include/bus_dma_impl.h
28

bus_dma_impl.h uses the old busdma internal API (filter / parent / ref_count) that has already been removed upstream in FreeBSD.

sys/loongarch/loongarch/busdma_bounce.c
33

Since both busdma_machdep.c and busdma_bounce.c are implemented against this old contract, these three files must be updated as a set. You can refer to bus_dma_impl.h, busdma_machdep.c, and busdma_bounce.c under sys/riscv/riscv/.

sys/loongarch/loongarch/busdma_machdep.c
33

Since both busdma_machdep.c and busdma_bounce.c are implemented against this old contract, these three files must be updated as a set. You can refer to bus_dma_impl.h, busdma_machdep.c, and busdma_bounce.c under sys/riscv/riscv/.

sys/loongarch/include/loongarchreg.h
386–387

According to the fetch-watchpoint-overall-controller and fetch-watchpoint-overall-status sections in the LoongArch-Vol1 manual, FWPC only has the Num field and reserved bits, and only FWPS has the Skip field.

Suggested change to:

#define CSR_FWPS_SKIP_SHIFT 16
#define CSR_FWPS_SKIP       (_ULCAST_(1) << CSR_FWPS_SKIP_SHIFT)

fetch-watchpoint-overall-controller:https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#fetch-watchpoint-overall-controller
fetch-watchpoint-overall-status: https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#fetch-watchpoint-overall-status

sys/loongarch/loongarch/watch.c
205

Apply the same change to:

csr_write64(CSR_FWPS_SKIP, LOONGARCH_CSR_FWPS);
}
zhaoxiaoqiang007_gmail.com added inline comments.
sys/dev/uart/uart_cpu_loongarch.c
82–85

It's a legacy code when I try to initialize UART console in 3A6000 board, on which firmware not correctly provide SPCR/DBG2 in ACPI device probe process. qemu fdt does not have this issue, thus a fallback hardcode. Since current port only targets qemu, I will remove it in next reversion.

sys/kern/link_elf_obj.c
1004–1005

I will check this later

sys/loongarch/include/asm.h
59–62

I'm afraid I disagree, kernel can use reserved register for PCPU pointer, it already be used in Linux kernel.

sys/loongarch/include/atomic.h
585

exactly

sys/loongarch/loongarch/busdma_bounce.c
33

Yes, It seems a rework is needed, thanks

sys/loongarch/loongarch/watch.c
4

will add in next reversion

sys/vm/uma_core.c
3655

okay

sys/loongarch/include/sysarch.h
33

The file header still "RISC-V Timer"; this driver was adapted from the riscv one — please fix the comment.

sys/loongarch/loongarch/loongarch_syscon.c
30–31

This was copied from RISC-V without cleanup; please fix the comment.

sys/loongarch/loongarch/timer.c
36

This was copied from RISC-V without cleanup; please fix the comment.

sys/kern/link_elf_obj.c
1004–1005

We already do the same for amd64 as you can see, this is really a minor sin. Though, there is something similar happening in kva_import().

It might be that all platforms should define constants for the beginning and end of some reserved KVA space dedicated for kernel modules. We should perhaps go even further and define a submap for that region on platforms that need one.

Agreed that per-platform KMOD KVA start/end constants, shared by link_elf_obj.c and kva_import()

sys/loongarch/include/frame.h
131

We use a7 for syscall number. So this should be 7.

sys/loongarch/include/loongarchreg.h
237

The architecture only guarantees at least one SAVE CSR, while this statically reserves SAVE0-SAVE4. Should we document that FreeBSD requires PRCFG1.SAVENum >= 5, and validate that requirement before these registers are first used?

sys/loongarch/loongarch/exception.S
207

This comment seems stale or misplaced?

384

It's better to replace 0x3 with something like CSR_PRMD_PPLV

sys/loongarch/loongarch/locore.S
224

Maybe you mean KS4?

sys/loongarch/loongarch/trap.c
310

Maybe we need to remove this debug printf.

487

should we use tf_badvaddr instead of tf_era here?

492

same here.

sys/loongarch/include/loongarchreg.h
237

Good point.

sys/loongarch/loongarch/exception.S
207

will remove

sys/loongarch/loongarch/locore.S
224

Yes, you are right

sys/loongarch/loongarch/trap.c
310

of course

487

Yes

Fix non-standard C99 anonymous unions/structs in <machine/reg.h> and <machine/ucontext.h>.

Anonymous unions/structs are a C11 / GNU extension and are not valid in
ISO C99. FreeBSD public ABI headers, especially <machine/reg.h> used by
PT_GETREGS /_PRSTATUS, should be compilable with strict C compilers.
Other architectures (riscv, arm64, x86) keep their reg.h free of
anonymous unions.

This change flattens struct reg and struct gpregs into named members
only, and updates the memcpy sites in exec_machdep.c that previously
used the regs[32] / gp_regs[32] array view. The binary layout of the
32 GPRs is unchanged, so the ABI is preserved; only the source-level
access path changes.

sys/loongarch/include/reg.h
41–54

Fix non-standard C99 anonymous unions/structs in <machine/reg.h> and <machine/ucontext.h>.

Suggested change to:

	/* general purpose registers */
	__uint64_t	r0;
	__uint64_t	ra;
	__uint64_t	tp;
	__uint64_t	sp;
	__uint64_t	a[8];
	__uint64_t	t[9];
	__uint64_t      r21;
	__uint64_t	fp;
	__uint64_t	s[9];
sys/loongarch/include/ucontext.h
39–52

Fix non-standard C99 anonymous unions/structs in <machine/reg.h> and <machine/ucontext.h>.

Suggested change to:

	/* general purpose registers */
	__register r0;
	__register_t ra;
	__register_t tp;
	__register_t sp;
	__register_t gp_a[8];
	__register_t gp_t[9];
	__register_t r21;
	__register_t fp;
	__register_t gp_s[9];
sys/loongarch/loongarch/exec_machdep.c
98

Sync the same change:

memcpy(regs, frame->tf_regs, sizeof(frame->tf_regs));
119

Sync the same change:

memcpy(frame->tf_regs, regs, sizeof(frame->tf_regs));
266–267

Sync the same change:

memcpy(&mcp->mc_gpregs, tf->tf_regs, sizeof(tf->tf_regs));
294

Sync the same change:

memcpy(tf->tf_regs, &mcp->mc_gpregs, sizeof(tf->tf_regs));

Remove the privileged CSRs, so struct reg becomes smaller and the ptrace ABI is cleaner.

FreeBSD LoongArch port should follow FreeBSD own conventions rather than Linux:
Linux kernel fills 5 CSRs (crmd, prmd, euen, misc, ecfg, estat) into the reserved[10] slots of struct user_pt_regs; that is Linux's own ptrace ABI choice. But FreeBSD's struct reg convention exposes only the GPRs and minimal user-mode state (orig_a0, PC, bad), and privileged CSRs should remain in the kernel's struct trapframe.

struct reg, as the user-space ABI for PT_GETREGS / NT_PRSTATUS, should contain only the general-purpose registers, PC (csr_era), the original syscall argument (orig_a0), and the faulting virtual address (csr_badv). The privileged CSRs (crmd/prmd/euen/misc/ecfg/estat) are kernel-mode state and should remain in struct trapframe, rather than being mixed into struct reg.

LoongArch-Vol1 1.4.2. References to Control and Status Registers: https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#references-to-control-and-status-registers

LoongArch-Vol1 2.1.3. Running Privilege Levels: https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#running-privilege-levels

sys/loongarch/include/reg.h
56–73

Remove the privileged CSRs, so struct reg becomes smaller and the ptrace ABI is cleaner.

Suggested change to:

 	/*
	 * User-visible state exposed via PT_GETREGS / NT_PRSTATUS+	 * orig_a0 preserves the original syscall arg0 for syscall tracing.
	 * csr_era is the exception return address (PC).
	 * csr_badv is the faulting virtual address.
	 * Privileged CSRs are intentionally omitted from ABI.
 	 */
	__uint64_t	orig_a0;	/* original syscall arg0 */
	__uint64_t	csr_era;	/* PC (exception return addr) */
	__uint64_t	csr_badv;	/* bad virtual address */

LoongArch-Vol1 1.4.2. References to Control and Status Registers: https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#references-to-control-and-status-registers

LoongArch-Vol1 2.1.3. Running Privilege Levels: https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#running-privilege-levels

sys/loongarch/include/ucontext.h
54–62

Sync the same change:

	/*
	 * User-visible state saved/restored for signals.
	 * Privileged CSRs are kept in the kernel trapframe and restored by
	 * ERTN; they are not part of the user signal.
	 */
	__register_t	gp_era;		/* PC (exception return address) */
	__register_t	gp_badvaddr;	/* faulting virtual address */
	__register_t	gp_orig_a0	/* original syscall arg0 */