diff --git a/.github/workflows/actions/setup-musl/action.yml b/.github/workflows/actions/setup-musl/action.yml index 67062e057a..9fef2b71c9 100644 --- a/.github/workflows/actions/setup-musl/action.yml +++ b/.github/workflows/actions/setup-musl/action.yml @@ -16,22 +16,45 @@ runs: path: ${{ inputs.arch }}-linux-musl-cross key: ${{ inputs.arch }}-linux-musl-cross - name: Download musl toolchain - if: steps.cache-musl.outputs.cache-hit != 'true' + if: ${{ steps.cache-musl.outputs.cache-hit != 'true' && inputs.arch != 'loongarch64' }} shell: bash run: | MUSL_PATH=${{ inputs.arch }}-linux-musl-cross wget https://musl.cc/${MUSL_PATH}.tgz tar -xf ${MUSL_PATH}.tgz + - name: Download musl toolchain + if: ${{ steps.cache-musl.outputs.cache-hit != 'true' && inputs.arch == 'loongarch64' }} + shell: bash + run: | + TOOLCHAIN_REPO=https://github.com/LoongsonLab/oscomp-toolchains-for-oskernel + wget ${TOOLCHAIN_REPO}/releases/download/gcc-13.2.0-loongarch64/gcc-13.2.0-loongarch64-linux-gnu.tgz + wget ${TOOLCHAIN_REPO}/raw/refs/heads/main/musl-loongarch64-1.2.2.tgz + tar -xf gcc-13.2.0-loongarch64-linux-gnu.tgz + tar -xf musl-loongarch64-1.2.2.tgz - uses: actions/cache/save@v4 - if: steps.cache-musl.outputs.cache-hit != 'true' + if: ${{ steps.cache-musl.outputs.cache-hit != 'true' && inputs.arch != 'loongarch64' }} with: path: ${{ inputs.arch }}-linux-musl-cross key: ${{ inputs.arch }}-linux-musl-cross + - uses: actions/cache/save@v4 + if: ${{ steps.cache-musl.outputs.cache-hit != 'true' && inputs.arch == 'loongarch64' }} + with: + path: | + gcc-13.2.0-loongarch64-linux-gnu + musl-loongarch64-1.2.2 + key: ${{ inputs.arch }}-linux-musl-cross - name: Add to PATH environment variable + if: inputs.arch != 'loongarch64' shell: bash run: | echo "$PWD/${{ inputs.arch }}-linux-musl-cross/bin" >> $GITHUB_PATH + - name: Add to PATH environment variable + if: inputs.arch == 'loongarch64' + shell: bash + run: | + echo "$PWD/gcc-13.2.0-loongarch64-linux-gnu/bin" >> $GITHUB_PATH + echo "$PWD/musl-loongarch64-1.2.2/bin" >> $GITHUB_PATH - name: Verify installation shell: bash run: | diff --git a/.github/workflows/actions/setup-qemu/action.yml b/.github/workflows/actions/setup-qemu/action.yml index 819fb08797..5c6c860777 100644 --- a/.github/workflows/actions/setup-qemu/action.yml +++ b/.github/workflows/actions/setup-qemu/action.yml @@ -25,7 +25,7 @@ runs: sudo apt-get update && sudo apt-get install -y ninja-build libslirp-dev libglib2.0-dev wget https://download.qemu.org/$QEMU_PATH.tar.xz && tar -xJf $QEMU_PATH.tar.xz cd $QEMU_PATH \ - && ./configure --prefix=$PREFIX --target-list=x86_64-softmmu,riscv64-softmmu,aarch64-softmmu --enable-slirp \ + && ./configure --prefix=$PREFIX --target-list=x86_64-softmmu,riscv64-softmmu,aarch64-softmmu,loongarch64-softmmu --enable-slirp \ && make -j > /dev/null 2>&1 \ && make install - uses: actions/cache/save@v4 @@ -44,3 +44,4 @@ runs: qemu-system-x86_64 --version qemu-system-aarch64 --version qemu-system-riscv64 --version + qemu-system-loongarch64 --version diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d9c082a925..425ba5e665 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - arch: [x86_64, riscv64, aarch64] + arch: [x86_64, riscv64, aarch64, loongarch64] rust-toolchain: [nightly, nightly-2024-12-25] env: RUSTUP_TOOLCHAIN: ${{ matrix.rust-toolchain }} @@ -18,7 +18,7 @@ jobs: with: toolchain: ${{ matrix.rust-toolchain }} components: rust-src, clippy, rustfmt - targets: x86_64-unknown-none, riscv64gc-unknown-none-elf, aarch64-unknown-none, aarch64-unknown-none-softfloat + targets: x86_64-unknown-none, riscv64gc-unknown-none-elf, aarch64-unknown-none, aarch64-unknown-none-softfloat, loongarch64-unknown-none - uses: Swatinem/rust-cache@v2 - name: Check rust version run: rustc --version --verbose @@ -40,7 +40,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - arch: [x86_64, riscv64, aarch64] + arch: [x86_64, riscv64, aarch64, loongarch64] rust-toolchain: [nightly, nightly-2024-12-25] env: RUSTUP_TOOLCHAIN: ${{ matrix.rust-toolchain }} @@ -50,7 +50,7 @@ jobs: with: toolchain: ${{ matrix.rust-toolchain }} components: rust-src, llvm-tools - targets: x86_64-unknown-none, riscv64gc-unknown-none-elf, aarch64-unknown-none, aarch64-unknown-none-softfloat + targets: x86_64-unknown-none, riscv64gc-unknown-none-elf, aarch64-unknown-none, aarch64-unknown-none-softfloat, loongarch64-unknown-none - uses: Swatinem/rust-cache@v2 - run: cargo install cargo-binutils - name: Build helloworld @@ -92,7 +92,7 @@ jobs: with: toolchain: ${{ matrix.rust-toolchain }} components: rust-src, llvm-tools - targets: x86_64-unknown-none, riscv64gc-unknown-none-elf, aarch64-unknown-none, aarch64-unknown-none-softfloat + targets: x86_64-unknown-none, riscv64gc-unknown-none-elf, aarch64-unknown-none, aarch64-unknown-none-softfloat, loongarch64-unknown-none - uses: Swatinem/rust-cache@v2 - run: cargo install cargo-binutils - name: Build helloworld for x86_64-pc-oslab diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e00874afd6..5ab0eed038 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,7 +3,7 @@ name: Test CI on: [push, pull_request] env: - qemu-version: 8.2.0 + qemu-version: 9.2.1 rust-toolchain: nightly-2024-12-25 arceos-apps: '855106c' @@ -26,7 +26,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - arch: [x86_64, riscv64, aarch64] + arch: [x86_64, riscv64, aarch64, loongarch64] steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable diff --git a/Cargo.lock b/Cargo.lock index 0a142db4f7..5ffba8495b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -467,7 +467,9 @@ dependencies = [ "lazyinit", "linkme", "log", + "loongArch64", "memory_addr", + "ns16550a", "page_table_entry", "page_table_multiarch", "percpu", @@ -1218,6 +1220,16 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +[[package]] +name = "loongArch64" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd48200d465466664e4e899b204b77b5447d60b1ababdad3a2c49ae85417b552" +dependencies = [ + "bit_field", + "bitflags 1.3.2", +] + [[package]] name = "managed" version = "0.8.0" @@ -1261,6 +1273,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "ns16550a" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "667fbdfccff4775918606516ff6ca51b898ac0842c4bc41c960a55a9832545a6" + [[package]] name = "num-traits" version = "0.2.19" @@ -1279,8 +1297,7 @@ checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "page_table_entry" version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "937b855b31ff3fa1274a5a4dfc1e57f124d26321adfa80ba03cdcc65921e8718" +source = "git+https://github.com/yfblock/page_table_multiarch.git?rev=c8a13b0#c8a13b0fe4af4d3b2ef2b57cc5fff3f6495424b9" dependencies = [ "aarch64-cpu 10.0.0", "bitflags 2.8.0", @@ -1291,8 +1308,7 @@ dependencies = [ [[package]] name = "page_table_multiarch" version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12a26475e53be3cb87fcf9decd20e871bb78d7c4ba12fb03de79683d21ff3dad" +source = "git+https://github.com/yfblock/page_table_multiarch.git?rev=c8a13b0#c8a13b0fe4af4d3b2ef2b57cc5fff3f6495424b9" dependencies = [ "log", "memory_addr", diff --git a/Cargo.toml b/Cargo.toml index dfc0a37bda..46cd173ab5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,5 +65,9 @@ axsync = { path = "modules/axsync" } axtask = { path = "modules/axtask" } axdma = { path = "modules/axdma" } +[patch.crates-io] +page_table_multiarch = { git = "https://github.com/yfblock/page_table_multiarch.git", rev = "c8a13b0"} +page_table_entry = { git = "https://github.com/yfblock/page_table_multiarch.git", rev = "c8a13b0" } + [profile.release] lto = true diff --git a/Dockerfile b/Dockerfile index ebd911ace7..b38630762e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,12 @@ FROM rust:slim +RUN echo /etc/apt/sources.list << deb http://apt.llvm.org/bookworm/ llvm-toolchain-bookworm main +RUN wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc + RUN apt-get update \ - && apt-get install -y --no-install-recommends libclang-dev qemu-system wget make \ + && apt-get install -y --no-install-recommends libclang-19-dev wget make python3 \ + xz-utils python3-venv ninja-build bzip2 meson \ + pkg-config libglib2.0-dev git libslirp-dev \ && rm -rf /var/lib/apt/lists/* RUN cargo install cargo-binutils axconfig-gen @@ -13,9 +18,24 @@ RUN rustc --version RUN wget https://musl.cc/aarch64-linux-musl-cross.tgz \ && wget https://musl.cc/riscv64-linux-musl-cross.tgz \ && wget https://musl.cc/x86_64-linux-musl-cross.tgz \ + && wget https://github.com/LoongsonLab/oscomp-toolchains-for-oskernel/releases/download/gcc-13.2.0-loongarch64/gcc-13.2.0-loongarch64-linux-gnu.tgz \ + && wget https://github.com/LoongsonLab/oscomp-toolchains-for-oskernel/raw/refs/heads/main/musl-loongarch64-1.2.2.tgz \ && tar zxf aarch64-linux-musl-cross.tgz \ && tar zxf riscv64-linux-musl-cross.tgz \ && tar zxf x86_64-linux-musl-cross.tgz \ + && tar zxf gcc-13.2.0-loongarch64-linux-gnu.tgz \ + && tar zxf musl-loongarch64-1.2.2.tgz && cd musl-loongarch64-1.2.2 && ./setup && cd .. \ && rm -f *.tgz +RUN wget https://download.qemu.org/qemu-9.2.1.tar.xz \ + && tar xf qemu-9.2.1.tar.xz \ + && cd qemu-9.2.1 \ + && ./configure --prefix=/qemu-bin-9.2.1 \ + --target-list=loongarch64-softmmu,riscv64-softmmu,aarch64-softmmu,x86_64-softmmu \ + --enable-gcov --enable-debug --enable-slirp \ + && make -j$(nproc) \ + && make install +RUN rm -rf qemu-9.2.1 qemu-9.2.1.tar.xz + ENV PATH="/x86_64-linux-musl-cross/bin:/aarch64-linux-musl-cross/bin:/riscv64-linux-musl-cross/bin:$PATH" +ENV PATH="/gcc-13.2.0-loongarch64-linux-gnu/bin:/musl-loongarch64-1.2.2/bin:/qemu-bin-9.2.1/bin:$PATH" diff --git a/Makefile b/Makefile index f71378e3d8..1553705941 100644 --- a/Makefile +++ b/Makefile @@ -93,8 +93,10 @@ else ifeq ($(ARCH), aarch64) endif else ifeq ($(ARCH), riscv64) TARGET := riscv64gc-unknown-none-elf +else ifeq ($(ARCH), loongarch64) + TARGET := loongarch64-unknown-none else - $(error "ARCH" must be one of "x86_64", "riscv64", or "aarch64") + $(error "ARCH" must be one of "x86_64", "riscv64", "aarch64" or "loongarch64") endif export AX_ARCH=$(ARCH) diff --git a/README.md b/README.md index 208c9504b9..dca19f633a 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ ArceOS was inspired a lot by [Unikraft](https://github.com/unikraft/unikraft). ## Features & TODOs -* [x] Architecture: x86_64, riscv64, aarch64 -* [x] Platform: QEMU pc-q35 (x86_64), virt (riscv64/aarch64) +* [x] Architecture: x86_64, riscv64, aarch64, loongarch64 +* [x] Platform: QEMU pc-q35 (x86_64), virt (riscv64/aarch64/loongarch64) * [x] Multi-thread * [x] FIFO/RR/CFS scheduler * [x] VirtIO net/blk/gpu drivers @@ -78,26 +78,47 @@ Download & install [musl](https://musl.cc) toolchains: wget https://musl.cc/aarch64-linux-musl-cross.tgz wget https://musl.cc/riscv64-linux-musl-cross.tgz wget https://musl.cc/x86_64-linux-musl-cross.tgz +wget https://github.com/LoongsonLab/oscomp-toolchains-for-oskernel/releases/download/gcc-13.2.0-loongarch64/gcc-13.2.0-loongarch64-linux-gnu.tgz +wget https://github.com/LoongsonLab/oscomp-toolchains-for-oskernel/raw/refs/heads/main/musl-loongarch64-1.2.2.tgz # install tar zxf aarch64-linux-musl-cross.tgz tar zxf riscv64-linux-musl-cross.tgz tar zxf x86_64-linux-musl-cross.tgz +tar zxf gcc-13.2.0-loongarch64-linux-gnu.tgz +tar zxf musl-loongarch64-1.2.2.tgz && cd musl-loongarch64-1.2.2 && ./setup && cd .. # exec below command in bash OR add below info in ~/.bashrc -export PATH=`pwd`/x86_64-linux-musl-cross/bin:`pwd`/aarch64-linux-musl-cross/bin:`pwd`/riscv64-linux-musl-cross/bin:$PATH +export PATH=`pwd`/x86_64-linux-musl-cross/bin:`pwd`/aarch64-linux-musl-cross/bin:`pwd`/riscv64-linux-musl-cross/bin:`pwd`/gcc-13.2.0-loongarch64-linux-gnu/bin:`pwd`/musl-loongarch64-1.2.2/bin:$PATH ``` Other systems and arch please refer to [Qemu Download](https://www.qemu.org/download/#linux) #### 2. Build & Run +##### quick run + ```bash -# build app in arceos directory -make A=path/to/app ARCH= LOG= +# cd arceos directory +cd arceos +# build&run app in arceos directory +# clean everything +make clean +# Example Pattern: make ARCH= LOG= A=path/to/app FEATURES=... run +# riscv64 example +make ARCH=riscv64 LOG=debug A=examples/helloworld run +# x86_64 example +make clean +make ARCH=x86_64 LOG=debug A=examples/helloworld run +# aarch64 example +make clean +make ARCH=aarch64 LOG=debug A=examples/helloworld run +# loongarch64 example +make clean +make ARCH=loongarch64 LOG=debug A=examples/helloworld run ``` Where `path/to/app` is the relative path to the application. Examples applications can be found in the [examples](examples/) directory or the [arceos-apps](https://github.com/arceos-org/arceos-apps) repository. -`` should be one of `riscv64`, `aarch64`, `x86_64`. +`` should be one of `riscv64`, `aarch64`, `x86_64`, `loongarch64`. `` should be one of `off`, `error`, `warn`, `info`, `debug`, `trace`. diff --git a/configs/platforms/loongarch64-qemu-virt.toml b/configs/platforms/loongarch64-qemu-virt.toml new file mode 100644 index 0000000000..e53934eadc --- /dev/null +++ b/configs/platforms/loongarch64-qemu-virt.toml @@ -0,0 +1,78 @@ + +# Architecture identifier. +arch = "loongarch64" +# Platform identifier. +platform = "loongarch64-qemu-virt" + +# +# Platform configs +# +[plat] +# Platform family. +family = "loongarch64-qemu-virt" + +# Base address of the whole physical memory. +phys-memory-base = 0x8000_0000 # uint +# Size of the whole physical memory. (128M) +phys-memory-size = 0x800_0000 # uint +# Base physical address of the kernel image. +kernel-base-paddr = 0x8000_0000 # uint + +# Base virtual address of the kernel image. +kernel-base-vaddr = "0xffff_0000_8000_0000" # uint +# Linear mapping offset, for quick conversions between physical and virtual +# addresses. +phys-virt-offset = "0xffff_0000_0000_0000" # uint +# Offset of bus address and phys address. some boards, the bus address is +# different from the physical address. +phys-bus-offset = 0 # uint +# Kernel address space base. +kernel-aspace-base = "0xffff_0000_0000_0000" # uint +# Kernel address space size. +kernel-aspace-size = "0x0000_ffff_ffff_f000" # uint + +# +# Device specifications +# +[devices] +# MMIO regions with format (`base_paddr`, `size`). +mmio-regions = [ + [0x100E_0000, 0x0000_1000], # GED + [0x1FE0_0000, 0x0000_1000], # UART + [0x2000_0000, 0x1000_0000], # PCI + [0x4000_0000, 0x0002_0000], # PCI RANGES +] # [(uint, uint)] +# VirtIO MMIO regions with format (`base_paddr`, `size`). +virtio-mmio-regions = [] # [(uint, uint)] +# Base physical address of the PCIe ECAM space. +pci-ecam-base = 0x2000_0000 # uint +# End PCI bus number. +pci-bus-end = 0 # uint +# PCI device memory ranges. +pci-ranges = [ + [0, 0], + [0x4000_0000, 0x0002_0000] +] # [(uint, uint)] +# poweroff { +# value = <0x00000034>; +# offset = <0x00000000>; +# compatible = "syscon-poweroff"; +# }; +# ged@100e001c { +# reg-io-width = <0x00000001>; +# reg-shift = <0x00000000>; +# reg = <0x00000000 0x100e001c 0x00000000 0x00000003>; +# compatible = "syscon"; +# }; +ged-paddr = 0x100E001C # uint +# serial@1fe001e0 { +# interrupt-parent = <0x00008003>; +# interrupts = <0x00000002 0x00000004>; +# clock-frequency = <0x05f5e100>; +# reg = <0x00000000 0x1fe001e0 0x00000000 0x00000100>; +# compatible = "ns16550a"; +# }; +uart-paddr = 0x1FE001E0 # uint + +# Timer interrupt frequency in Hz. +timer-frequency = 100_000_000 # uint diff --git a/modules/axhal/Cargo.toml b/modules/axhal/Cargo.toml index f1cc469045..d294a3c90f 100644 --- a/modules/axhal/Cargo.toml +++ b/modules/axhal/Cargo.toml @@ -59,5 +59,9 @@ arm_pl011 = "0.1" arm_pl031 = { version = "0.2", optional = true } dw_apb_uart = "0.1" +[target.'cfg(target_arch = "loongarch64")'.dependencies] +loongArch64 = "0.2.4" +ns16550a = "0.4.0" + [build-dependencies] axconfig = { workspace = true } diff --git a/modules/axhal/build.rs b/modules/axhal/build.rs index 56061e6434..e6ede5eead 100644 --- a/modules/axhal/build.rs +++ b/modules/axhal/build.rs @@ -5,6 +5,7 @@ const BUILTIN_PLATFORMS: &[&str] = &[ "aarch64-bsta1000b", "aarch64-qemu-virt", "aarch64-raspi4", + "loongarch64-qemu-virt", "riscv64-qemu-virt", "x86_64-pc-oslab", "x86_64-qemu-q35", @@ -15,6 +16,7 @@ const BUILTIN_PLATFORM_FAMILIES: &[&str] = &[ "aarch64-phytium-pi", "aarch64-qemu-virt", "aarch64-raspi", + "loongarch64-qemu-virt", "riscv64-qemu-virt", "x86-pc", ]; diff --git a/modules/axhal/linker.lds.S b/modules/axhal/linker.lds.S index aa3b3c9ca3..7a72d4f5e0 100644 --- a/modules/axhal/linker.lds.S +++ b/modules/axhal/linker.lds.S @@ -68,7 +68,7 @@ SECTIONS . = ALIGN(4K); _edata = .; - .bss : ALIGN(4K) { + .bss : AT(.) ALIGN(4K) { boot_stack = .; *(.bss.stack) . = ALIGN(4K); diff --git a/modules/axhal/src/arch/loongarch64/context.rs b/modules/axhal/src/arch/loongarch64/context.rs new file mode 100644 index 0000000000..4824e5961c --- /dev/null +++ b/modules/axhal/src/arch/loongarch64/context.rs @@ -0,0 +1,286 @@ +use core::arch::naked_asm; +use memory_addr::VirtAddr; +/// Saved registers when a trap (interrupt or exception) occurs. +#[repr(C)] +#[derive(Debug, Default, Clone, Copy)] +pub struct TrapFrame { + /// All general registers. + pub regs: [usize; 32], + /// Pre-exception Mode Information + pub prmd: usize, + /// Exception Return Address + pub era: usize, + /// Access Memory Address When Exception + pub badv: usize, + /// Current Mode Information + pub crmd: usize, +} + +impl TrapFrame { + /// Gets the 0th syscall argument. + pub const fn arg0(&self) -> usize { + self.regs[4] as _ + } + + /// Gets the 1st syscall argument. + pub const fn arg1(&self) -> usize { + self.regs[5] as _ + } + + /// Gets the 2nd syscall argument. + pub const fn arg2(&self) -> usize { + self.regs[6] as _ + } + + /// Gets the 3rd syscall argument. + pub const fn arg3(&self) -> usize { + self.regs[7] as _ + } + + /// Gets the 4th syscall argument. + pub const fn arg4(&self) -> usize { + self.regs[8] as _ + } + + /// Gets the 5th syscall argument. + pub const fn arg5(&self) -> usize { + self.regs[9] as _ + } +} + +/// Context to enter user space. +#[cfg(feature = "uspace")] +pub struct UspaceContext(TrapFrame); + +#[cfg(feature = "uspace")] +impl UspaceContext { + /// Creates an empty context with all registers set to zero. + pub const fn empty() -> Self { + unsafe { core::mem::MaybeUninit::zeroed().assume_init() } + } + + /// Creates a new context with the given entry point, user stack pointer, + /// and the argument. + pub fn new(entry: usize, ustack_top: VirtAddr, arg0: usize) -> Self { + let mut trap_frame = TrapFrame::default(); + const PPLV_UMODE: usize = 0b11; + const PIE: usize = 1 << 2; + trap_frame.regs[3] = ustack_top.as_usize(); + trap_frame.era = entry; + trap_frame.prmd = PPLV_UMODE | PIE; + trap_frame.regs[4] = arg0; + Self(trap_frame) + } + + /// Creates a new context from the given [`TrapFrame`]. + pub const fn from(trap_frame: &TrapFrame) -> Self { + Self(*trap_frame) + } + + /// Gets the instruction pointer. + pub const fn get_ip(&self) -> usize { + self.0.era + } + + /// Gets the stack pointer. + pub const fn get_sp(&self) -> usize { + self.0.regs[3] + } + + /// Sets the instruction pointer. + pub const fn set_ip(&mut self, pc: usize) { + self.0.era = pc; + } + + /// Sets the stack pointer. + pub const fn set_sp(&mut self, sp: usize) { + self.0.regs[3] = sp; + } + + /// Sets the return value register. + pub const fn set_retval(&mut self, a0: usize) { + self.0.regs[4] = a0; + } + + /// Enters user space. + /// + /// It restores the user registers and jumps to the user entry point + /// (saved in `era`). + /// When an exception or syscall occurs, the kernel stack pointer is + /// switched to `kstack_top`. + /// + /// # Safety + /// + /// This function is unsafe because it changes processor mode and the stack. + #[unsafe(no_mangle)] + pub unsafe fn enter_uspace(&self, kstack_top: VirtAddr) -> ! { + use loongArch64::register::{CpuMode, era, prmd}; + + super::disable_irqs(); + prmd::set_pplv(CpuMode::Ring3); + prmd::set_pie(true); + era::set_pc(self.get_ip()); + + unsafe { + core::arch::asm!( + r" + .equ KSAVE_KSP, 0x30 + .equ KSAVE_T0, 0x31 + .equ KSAVE_USP, 0x32 + .equ KSAVE_R21, 0x33 + .equ KSAVE_TP, 0x34 + .equ LA_CSR_EUEN, 0x2 + + move $sp, {tf} + csrwr $tp, KSAVE_TP + csrwr $r21, KSAVE_R21 + + csrwr {kstack_top}, KSAVE_KSP // save ksp into SAVE0 CSR + + ld.d $r1, $sp, 1*8 + ld.d $tp, $sp, 2*8 + ld.d $r4, $sp, 4*8 + ld.d $r5, $sp, 5*8 + ld.d $r6, $sp, 6*8 + ld.d $r7, $sp, 7*8 + ld.d $r8, $sp, 8*8 + ld.d $r9, $sp, 9*8 + ld.d $r10, $sp, 10*8 + ld.d $r11, $sp, 11*8 + ld.d $r12, $sp, 12*8 + ld.d $r13, $sp, 13*8 + ld.d $r14, $sp, 14*8 + ld.d $r15, $sp, 15*8 + ld.d $r16, $sp, 16*8 + ld.d $r17, $sp, 17*8 + ld.d $r18, $sp, 18*8 + ld.d $r19, $sp, 19*8 + ld.d $r20, $sp, 20*8 + ld.d $r21, $sp, 21*8 + ld.d $r22, $sp, 22*8 + ld.d $r23, $sp, 23*8 + ld.d $r24, $sp, 24*8 + ld.d $r25, $sp, 25*8 + ld.d $r26, $sp, 26*8 + ld.d $r27, $sp, 27*8 + ld.d $r28, $sp, 28*8 + ld.d $r29, $sp, 29*8 + ld.d $r30, $sp, 30*8 + ld.d $r31, $sp, 31*8 + + ld.d $sp, $sp, 3*8 // user sp + ertn + ", + tf = in (reg) &self.0, + kstack_top = in(reg) kstack_top.as_usize(), + options(noreturn), + ); + } + } +} + +/// Saved hardware states of a task. +/// +/// The context usually includes: +/// +/// - Callee-saved registers +/// - Stack pointer register +/// - Thread pointer register (for thread-local storage, currently unsupported) +/// - FP/SIMD registers +/// +/// On context switch, current task saves its context from CPU to memory, +/// and the next task restores its context from memory to CPU. +#[allow(missing_docs)] +#[repr(C)] +#[derive(Debug, Default)] +pub struct TaskContext { + pub ra: usize, // return address + pub sp: usize, // stack pointer + pub s: [usize; 10], // loongArch need to save 10 static registers from $r22 to $r31 + pub tp: usize, + #[cfg(feature = "uspace")] + pub pgdl: usize, +} + +impl TaskContext { + /// Creates a new default context for a new task. + pub const fn new() -> Self { + unsafe { core::mem::MaybeUninit::zeroed().assume_init() } + } + + /// Initializes the context for a new task, with the given entry point and + /// kernel stack. + pub fn init(&mut self, entry: usize, kstack_top: VirtAddr, tls_area: VirtAddr) { + self.sp = kstack_top.as_usize(); + self.ra = entry; + self.tp = tls_area.as_usize(); + } + + /// Changes the page table root (`pgdl` register for loongarch64). + /// + /// If not set, the kernel page table root is used (obtained by + /// [`axhal::paging::kernel_page_table_root`][1]). + /// + /// [1]: crate::paging::kernel_page_table_root + #[cfg(feature = "uspace")] + pub fn set_page_table_root(&mut self, pgdl: memory_addr::PhysAddr) { + self.pgdl = pgdl.as_usize(); + } + + /// Switches to another task. + /// + /// It first saves the current task's context from CPU to this place, and then + /// restores the next task's context from `next_ctx` to CPU. + pub fn switch_to(&mut self, next_ctx: &Self) { + #[cfg(feature = "tls")] + { + self.tp = super::read_thread_pointer(); + unsafe { super::write_thread_pointer(next_ctx.tp) }; + } + #[cfg(feature = "uspace")] + { + if self.pgdl != next_ctx.pgdl { + unsafe { super::write_page_table_root0(pa!(next_ctx.pgdl)) }; + } + } + unsafe { context_switch(self, next_ctx) } + } +} + +#[naked] +unsafe extern "C" fn context_switch(_current_task: &mut TaskContext, _next_task: &TaskContext) { + unsafe { + naked_asm!( + " + // save old context (callee-saved registers) + st.d $ra, $a0, 0 + st.d $sp, $a0, 1 * 8 + st.d $s0, $a0, 2 * 8 + st.d $s1, $a0, 3 * 8 + st.d $s2, $a0, 4 * 8 + st.d $s3, $a0, 5 * 8 + st.d $s4, $a0, 6 * 8 + st.d $s5, $a0, 7 * 8 + st.d $s6, $a0, 8 * 8 + st.d $s7, $a0, 9 * 8 + st.d $s8, $a0, 10 * 8 + st.d $fp, $a0, 11 * 8 + + // restore new context + ld.d $ra, $a1, 0 + ld.d $s0, $a1, 2 * 8 + ld.d $s1, $a1, 3 * 8 + ld.d $s2, $a1, 4 * 8 + ld.d $s3, $a1, 5 * 8 + ld.d $s4, $a1, 6 * 8 + ld.d $s5, $a1, 7 * 8 + ld.d $s6, $a1, 8 * 8 + ld.d $s7, $a1, 9 * 8 + ld.d $s8, $a1, 10 * 8 + ld.d $fp, $a1, 11 * 8 + ld.d $sp, $a1, 1 * 8 + + ret", + ) + } +} diff --git a/modules/axhal/src/arch/loongarch64/mod.rs b/modules/axhal/src/arch/loongarch64/mod.rs new file mode 100644 index 0000000000..2f2d4ae3a8 --- /dev/null +++ b/modules/axhal/src/arch/loongarch64/mod.rs @@ -0,0 +1,191 @@ +#[macro_use] +mod context; +mod trap; + +use core::arch::asm; +use loongArch64::register::{ + crmd, ecfg, eentry, euen, pgd, pgdh, pgdl, pwch, pwcl, stlbps, tlbidx, tlbrehi, tlbrentry, +}; +use memory_addr::{PhysAddr, VirtAddr}; + +pub use self::context::{TaskContext, TrapFrame}; + +#[cfg(feature = "uspace")] +pub use self::context::UspaceContext; + +/// Allows the current CPU to respond to interrupts. +#[inline] +pub fn enable_irqs() { + crmd::set_ie(true) +} + +/// Makes the current CPU to ignore interrupts. +#[inline] +pub fn disable_irqs() { + crmd::set_ie(false) +} + +/// Returns whether the current CPU is allowed to respond to interrupts. +#[inline] +pub fn irqs_enabled() -> bool { + crmd::read().ie() +} + +/// Relaxes the current CPU and waits for interrupts. +/// +/// It must be called with interrupts enabled, otherwise it will never return. +#[inline] +pub fn wait_for_irqs() { + unsafe { loongArch64::asm::idle() } +} + +/// Halt the current CPU. +#[inline] +pub fn halt() { + unsafe { loongArch64::asm::idle() } + disable_irqs(); +} + +/// Reads the register that stores the current page table root. +/// +/// Returns the physical address of the page table root. +#[inline] +pub fn read_page_table_root() -> PhysAddr { + PhysAddr::from(pgd::read().base()) +} + +/// Writes the `pgdl` register. +/// +/// # Safety +/// +/// This function is unsafe as it changes the virtual memory address space. +pub unsafe fn write_page_table_root0(root_paddr: PhysAddr) { + pgdl::set_base(root_paddr.as_usize() as _); + flush_tlb(None); +} + +/// Writes the register to update the current page table root. +/// +/// # Safety +/// +/// This function is unsafe as it changes the virtual memory address space. +/// NOTE: Compiler optimize inline on release mode, kernel raise error about +/// page table. So we prohibit inline operation. +#[inline(never)] +pub unsafe fn write_page_table_root(root_paddr: PhysAddr) { + let old_root = read_page_table_root(); + trace!("set page table root: {:#x} => {:#x}", old_root, root_paddr); + + pgdh::set_base(root_paddr.as_usize()); + flush_tlb(None); +} + +/// Flushes the TLB. +/// +/// If `vaddr` is [`None`], flushes the entire TLB. Otherwise, flushes the TLB +/// entry that maps the given virtual address. +#[inline] +pub fn flush_tlb(vaddr: Option) { + unsafe { + if let Some(vaddr) = vaddr { + // + // + // Only after all previous load/store access operations are completely + // executed, the DBAR 0 instruction can be executed; and only after the + // execution of DBAR 0 is completed, all subsequent load/store access + // operations can be executed. + // + // + // + // formats: invtlb op, asid, addr + // + // op 0x5: Clear all page table entries with G=0 and ASID equal to the + // register specified ASID, and VA equal to the register specified VA. + // + // When the operation indicated by op does not require an ASID, the + // general register rj should be set to r0. + asm!("dbar 0; invtlb 0x05, $r0, {reg}", reg = in(reg) vaddr.as_usize()); + } else { + // op 0x0: Clear all page table entries + asm!("dbar 0; invtlb 0x00, $r0, $r0"); + } + } +} + +/// Writes Exception Entry Base Address Register (`eentry`). +/// +/// - ecfg: +/// - eentry: +#[inline] +pub fn set_trap_vector_base(eentry: usize) { + ecfg::set_vs(0); + eentry::set_eentry(eentry); +} + +/// Writes TLB Refill Entry Address Register (`tlbrentry`) +/// +/// tlbrentry: +#[inline] +pub fn set_tlb_refill(tlbrentry: usize) { + tlbrentry::set_tlbrentry(tlbrentry); +} + +/// Init the TLB configuration and set tlb refill handler. +pub fn init_tlb() { + // Page Size 4KB + const PS_4K: usize = 0x0c; + tlbidx::set_ps(PS_4K); + stlbps::set_ps(PS_4K); + tlbrehi::set_ps(PS_4K); + + // Set Page table entry width + pwcl::set_pte_width(8); + // Set Page table width and offset + pwcl::set_ptbase(12); + pwcl::set_ptwidth(9); + pwcl::set_dir1_base(21); + pwcl::set_dir1_width(9); + pwcl::set_dir2_base(30); + pwcl::set_dir2_width(9); + pwch::set_dir3_base(39); + pwch::set_dir3_width(9); + + unsafe extern "C" { + fn handle_tlb_refill(); + } + let paddr = crate::mem::virt_to_phys(va!(handle_tlb_refill as usize)); + crate::arch::set_tlb_refill(paddr.as_usize()); +} + +/// Reads the thread pointer of the current CPU. +/// +/// It is used to implement TLS (Thread Local Storage). +#[inline] +pub fn read_thread_pointer() -> usize { + let tp; + unsafe { asm!("move {}, $tp", out(reg) tp) }; + tp +} + +/// Writes the thread pointer of the current CPU. +/// +/// It is used to implement TLS (Thread Local Storage). +/// +/// # Safety +/// +/// This function is unsafe as it changes the CPU states. +#[inline] +pub unsafe fn write_thread_pointer(tp: usize) { + unsafe { asm!("move $tp, {}", in(reg) tp) } +} + +/// Initializes CPU states on the current CPU. +pub fn cpu_init() { + // Enable floating point + euen::set_fpe(true); + + unsafe extern "C" { + fn trap_vector_base(); + } + set_trap_vector_base(trap_vector_base as usize); +} diff --git a/modules/axhal/src/arch/loongarch64/trap.S b/modules/axhal/src/arch/loongarch64/trap.S new file mode 100644 index 0000000000..cc0a12d034 --- /dev/null +++ b/modules/axhal/src/arch/loongarch64/trap.S @@ -0,0 +1,182 @@ +.equ KSAVE_KSP, 0x30 +.equ KSAVE_T0, 0x31 +.equ KSAVE_USP, 0x32 +.equ KSAVE_R21, 0x33 +.equ KSAVE_TP, 0x34 + +.macro SAVE_REGS + st.d $ra, $sp, 8 + csrrd $t0, KSAVE_T0 + st.d $t0, $sp, 12*8 + + st.d $a0, $sp, 4*8 + st.d $a1, $sp, 5*8 + st.d $a2, $sp, 6*8 + st.d $a3, $sp, 7*8 + st.d $a4, $sp, 8*8 + st.d $a5, $sp, 9*8 + st.d $a6, $sp, 10*8 + st.d $a7, $sp, 11*8 + st.d $t1, $sp, 13*8 + st.d $t2, $sp, 14*8 + st.d $t3, $sp, 15*8 + st.d $t4, $sp, 16*8 + st.d $t5, $sp, 17*8 + st.d $t6, $sp, 18*8 + st.d $t7, $sp, 19*8 + st.d $t8, $sp, 20*8 + + st.d $fp, $sp, 22*8 + st.d $s0, $sp, 23*8 + st.d $s1, $sp, 24*8 + st.d $s2, $sp, 25*8 + st.d $s3, $sp, 26*8 + st.d $s4, $sp, 27*8 + st.d $s5, $sp, 28*8 + st.d $s6, $sp, 29*8 + st.d $s7, $sp, 30*8 + st.d $s8, $sp, 31*8 +.endm + +.macro RESTORE_REGS + csrrd $t0, 0x1 + andi $t0, $t0, 0x3 + bnez $t0, .Ltmp_user + +.Ltmp_kernel: + b .Ltmp_common + +.Ltmp_user: + csrwr $tp, KSAVE_TP + csrwr $r21, KSAVE_R21 + + ld.d $tp, $sp, 2*8 + ld.d $r21, $sp, 21*8 + +.Ltmp_common: + ld.d $ra, $sp, 1*8 + ld.d $a0, $sp, 4*8 + ld.d $a1, $sp, 5*8 + ld.d $a2, $sp, 6*8 + ld.d $a3, $sp, 7*8 + ld.d $a4, $sp, 8*8 + ld.d $a5, $sp, 9*8 + ld.d $a6, $sp, 10*8 + ld.d $a7, $sp, 11*8 + ld.d $t0, $sp, 12*8 + ld.d $t1, $sp, 13*8 + ld.d $t2, $sp, 14*8 + ld.d $t3, $sp, 15*8 + ld.d $t4, $sp, 16*8 + ld.d $t5, $sp, 17*8 + ld.d $t6, $sp, 18*8 + ld.d $t7, $sp, 19*8 + ld.d $t8, $sp, 20*8 + + ld.d $fp, $sp, 22*8 + ld.d $s0, $sp, 23*8 + ld.d $s1, $sp, 24*8 + ld.d $s2, $sp, 25*8 + ld.d $s3, $sp, 26*8 + ld.d $s4, $sp, 27*8 + ld.d $s5, $sp, 28*8 + ld.d $s6, $sp, 29*8 + ld.d $s7, $sp, 30*8 + ld.d $s8, $sp, 31*8 +.endm + + +.section .text +.balign 4096 +.global trap_vector_base +trap_vector_base: + csrwr $t0, KSAVE_T0 + csrrd $t0, 0x1 + andi $t0, $t0, 0x3 + bnez $t0, .Lfrom_userspace + +.Lfrom_kernel: + move $t0, $sp + addi.d $sp, $sp, -{trapframe_size} // allocate space + // save kernel sp + st.d $t0, $sp, 3*8 + + b .Lcommon + +.Lfrom_userspace: + csrwr $sp, KSAVE_USP // save user sp into SAVE1 CSR + csrrd $sp, KSAVE_KSP // restore kernel sp + addi.d $sp, $sp, -{trapframe_size} // allocate space + + // switch tp and r21 + st.d $tp, $sp, 2*8 + st.d $r21, $sp, 21*8 + + csrrd $tp, KSAVE_TP + csrrd $r21, KSAVE_R21 + + // save user sp + csrrd $t0, KSAVE_USP + st.d $t0, $sp, 3*8 // sp + +.Lcommon: + // save the registers. + SAVE_REGS + + csrrd $t2, 0x1 + st.d $t2, $sp, 8*32 // prmd + csrrd $t1, 0x6 + st.d $t1, $sp, 8*33 // era + csrrd $t1, 0x7 + st.d $t1, $sp, 8*34 // badv + csrrd $t1, 0x0 + st.d $t1, $sp, 8*35 // crmd + + move $a0, $sp + csrrd $t0, 0x1 + andi $a1, $t0, 0x3 // if user or kernel + bl loongarch64_trap_handler + + // restore the registers. + ld.d $t1, $sp, 8*33 // era + csrwr $t1, 0x6 + ld.d $t2, $sp, 8*32 // prmd + csrwr $t2, 0x1 + + // Save kernel sp when exit kernel mode + addi.d $t1, $sp, {trapframe_size} + csrwr $t1, KSAVE_KSP + + RESTORE_REGS + + // restore sp + ld.d $sp, $sp, 3*8 + ertn + + +// TLB Refill handler +.equ LA_CSR_PGDL, 0x19 /* Page table base address when VA[47] = 0 */ +.equ LA_CSR_PGDH, 0x1a /* Page table base address when VA[47] = 1 */ +.equ LA_CSR_PGD, 0x1b /* Page table base */ +.equ LA_CSR_TLBRENTRY, 0x88 /* TLB refill exception entry */ +.equ LA_CSR_TLBRBADV, 0x89 /* TLB refill badvaddr */ +.equ LA_CSR_TLBRERA, 0x8a /* TLB refill ERA */ +.equ LA_CSR_TLBRSAVE, 0x8b /* KScratch for TLB refill exception */ +.equ LA_CSR_TLBRELO0, 0x8c /* TLB refill entrylo0 */ +.equ LA_CSR_TLBRELO1, 0x8d /* TLB refill entrylo1 */ +.equ LA_CSR_TLBREHI, 0x8e /* TLB refill entryhi */ + +.section .text +.balign 4096 +.global handle_tlb_refill +handle_tlb_refill: + csrwr $t0, LA_CSR_TLBRSAVE + csrrd $t0, LA_CSR_PGD + lddir $t0, $t0, 3 + lddir $t0, $t0, 2 + lddir $t0, $t0, 1 + ldpte $t0, 0 + ldpte $t0, 1 + tlbfill + csrrd $t0, LA_CSR_TLBRSAVE + ertn diff --git a/modules/axhal/src/arch/loongarch64/trap.rs b/modules/axhal/src/arch/loongarch64/trap.rs new file mode 100644 index 0000000000..d66a0a5004 --- /dev/null +++ b/modules/axhal/src/arch/loongarch64/trap.rs @@ -0,0 +1,65 @@ +use super::context::TrapFrame; +use loongArch64::register::{ + badv, + estat::{self, Exception, Trap}, +}; +use page_table_entry::MappingFlags; + +core::arch::global_asm!( + include_str!("trap.S"), + trapframe_size = const (core::mem::size_of::()), +); + +fn handle_breakpoint(era: &mut usize) { + debug!("Exception(Breakpoint) @ {:#x} ", era); + *era += 4; +} + +fn handle_page_fault(tf: &TrapFrame, mut access_flags: MappingFlags, is_user: bool) { + if is_user { + access_flags |= MappingFlags::USER; + } + let vaddr = va!(badv::read().raw()); + if !handle_trap!(PAGE_FAULT, vaddr, access_flags, is_user) { + panic!( + "Unhandled {} Page Fault @ {:#x}, fault_vaddr={:#x} ({:?}):\n{:#x?}", + if is_user { "User" } else { "Supervisor" }, + tf.badv, + vaddr, + access_flags, + tf, + ); + } +} + +#[unsafe(no_mangle)] +fn loongarch64_trap_handler(tf: &mut TrapFrame, from_user: bool) { + let estat = estat::read(); + + match estat.cause() { + #[cfg(feature = "uspace")] + Trap::Exception(Exception::Syscall) => { + tf.regs[4] = crate::trap::handle_syscall(tf, tf.regs[11]) as usize; + tf.era += 4; + } + Trap::Exception(Exception::LoadPageFault) | Trap::Exception(Exception::FetchPageFault) => { + handle_page_fault(tf, MappingFlags::READ, from_user) + } + Trap::Exception(Exception::StorePageFault) => { + handle_page_fault(tf, MappingFlags::WRITE, from_user) + } + Trap::Exception(Exception::Breakpoint) => handle_breakpoint(&mut tf.era), + Trap::Interrupt(_) => { + let irq_num: usize = estat.is().trailing_zeros() as usize; + handle_trap!(IRQ, irq_num); + } + _ => { + panic!( + "Unhandled trap {:?} @ {:#x}:\n{:#x?}", + estat.cause(), + tf.era, + tf + ); + } + } +} diff --git a/modules/axhal/src/arch/mod.rs b/modules/axhal/src/arch/mod.rs index b8bc0af3d0..45c8555177 100644 --- a/modules/axhal/src/arch/mod.rs +++ b/modules/axhal/src/arch/mod.rs @@ -10,5 +10,8 @@ cfg_if::cfg_if! { } else if #[cfg(target_arch = "aarch64")]{ mod aarch64; pub use self::aarch64::*; + } else if #[cfg(any(target_arch = "loongarch64"))] { + mod loongarch64; + pub use self::loongarch64::*; } } diff --git a/modules/axhal/src/cpu.rs b/modules/axhal/src/cpu.rs index adecced8a2..308107a6d3 100644 --- a/modules/axhal/src/cpu.rs +++ b/modules/axhal/src/cpu.rs @@ -56,6 +56,12 @@ pub fn current_task_ptr() -> *const T { use tock_registers::interfaces::Readable; aarch64_cpu::registers::SP_EL0.get() as _ } + #[cfg(target_arch = "loongarch64")] + unsafe { + // on LA64, reading `CURRENT_TASK_PTR` requires multiple instruction, so we disable local IRQs. + let _guard = kernel_guard::IrqSave::new(); + CURRENT_TASK_PTR.read_current_raw() as _ + } } /// Sets the pointer to the current task with preemption-safety. @@ -83,6 +89,11 @@ pub unsafe fn set_current_task_ptr(ptr: *const T) { CURRENT_TASK_PTR.write_current_raw(ptr as usize); cache_current_task_ptr(); } + #[cfg(target_arch = "loongarch64")] + { + let _guard = kernel_guard::IrqSave::new(); + unsafe { CURRENT_TASK_PTR.write_current_raw(ptr as usize) } + } } #[allow(dead_code)] diff --git a/modules/axhal/src/paging.rs b/modules/axhal/src/paging.rs index f09b1d8479..3321156d49 100644 --- a/modules/axhal/src/paging.rs +++ b/modules/axhal/src/paging.rs @@ -63,6 +63,9 @@ cfg_if::cfg_if! { } else if #[cfg(target_arch = "aarch64")]{ /// The architecture-specific page table. pub type PageTable = page_table_multiarch::aarch64::A64PageTable; + } else if #[cfg(target_arch = "loongarch64")] { + /// The architecture-specific page table. + pub type PageTable = page_table_multiarch::loongarch64::LA64PageTable; } } diff --git a/modules/axhal/src/platform/loongarch64_qemu_virt/boot.rs b/modules/axhal/src/platform/loongarch64_qemu_virt/boot.rs new file mode 100644 index 0000000000..52a4fba50e --- /dev/null +++ b/modules/axhal/src/platform/loongarch64_qemu_virt/boot.rs @@ -0,0 +1,121 @@ +use axconfig::TASK_STACK_SIZE; +use loongArch64::register::{pgdh, pgdl}; + +#[unsafe(link_section = ".bss.stack")] +static mut BOOT_STACK: [u8; TASK_STACK_SIZE] = [0; TASK_STACK_SIZE]; + +#[unsafe(link_section = ".data.boot_page_table")] +static mut BOOT_PT_L0: [u64; 512] = [0; 512]; + +#[unsafe(link_section = ".data.boot_page_table")] +static mut BOOT_PT_L1: [u64; 512] = [0; 512]; + +unsafe fn init_boot_page_table() { + // Huge Page Mapping Flags: V | D | HUGE | P | W + const HUGE_FLAGS: u64 = (1 << 0) | (1 << 1) | (1 << 6) | (1 << 7) | (1 << 8); + unsafe { + let l1_va = va!(&raw const BOOT_PT_L1 as usize); + // 0x0000_0000_0000 ~ 0x0080_0000_0000, table + BOOT_PT_L0[0] = crate::mem::virt_to_phys(l1_va).as_usize() as u64; + // 0x0000_0000..0x4000_0000, VRWX_GAD, 1G block + BOOT_PT_L1[0] = HUGE_FLAGS; + // 0x8000_0000..0xc000_0000, VRWX_GAD, 1G block + BOOT_PT_L1[2] = 0x8000_0000 | HUGE_FLAGS; + } +} + +unsafe fn init_mmu() { + crate::arch::init_tlb(); + + let paddr = crate::mem::virt_to_phys(va!(&raw const BOOT_PT_L0 as usize)); + pgdh::set_base(paddr.as_usize()); + pgdl::set_base(0); +} + +/// The earliest entry point for the primary CPU. +/// +/// We can't use bl to jump to higher address, so we use jirl to jump to higher address. +#[naked] +#[unsafe(no_mangle)] +#[unsafe(link_section = ".text.boot")] +unsafe extern "C" fn _start() -> ! { + unsafe { + core::arch::naked_asm!(" + ori $t0, $zero, 0x1 # CSR_DMW1_PLV0 + lu52i.d $t0, $t0, -2048 # UC, PLV0, 0x8000 xxxx xxxx xxxx + csrwr $t0, 0x180 # LOONGARCH_CSR_DMWIN0 + ori $t0, $zero, 0x11 # CSR_DMW1_MAT | CSR_DMW1_PLV0 + lu52i.d $t0, $t0, -1792 # CA, PLV0, 0x9000 xxxx xxxx xxxx + csrwr $t0, 0x181 # LOONGARCH_CSR_DMWIN1 + + # Setup Stack + la.global $sp, {boot_stack} + li.d $t0, {boot_stack_size} + add.d $sp, $sp, $t0 # setup boot stack + + # Init MMU + bl {init_boot_page_table} + bl {init_mmu} # setup boot page table and enabel MMU + invtlb 0x00, $r0, $r0 + + + # Enable PG + li.w $t0, 0xb0 # PLV=0, IE=0, PG=1 + csrwr $t0, 0x0 # LOONGARCH_CSR_CRMD + li.w $t0, 0x00 # PLV=0, PIE=0, PWE=0 + csrwr $t0, 0x1 # LOONGARCH_CSR_PRMD + li.w $t0, 0x00 # FPE=0, SXE=0, ASXE=0, BTE=0 + csrwr $t0, 0x2 # LOONGARCH_CSR_EUEN + + csrrd $a0, 0x20 # cpuid + la.global $t0, {entry} + jirl $zero, $t0, 0 + ", + boot_stack_size = const TASK_STACK_SIZE, + boot_stack = sym BOOT_STACK, + init_boot_page_table = sym init_boot_page_table, + init_mmu = sym init_mmu, + entry = sym super::rust_entry, + ) + } +} + +/// The earliest entry point for secondary CPUs. +#[cfg(feature = "smp")] +#[naked] +#[unsafe(no_mangle)] +#[unsafe(link_section = ".text.boot")] +unsafe extern "C" fn _start_secondary() -> ! { + unsafe { + core::arch::naked_asm!(" + ori $t0, $zero, 0x1 # CSR_DMW1_PLV0 + lu52i.d $t0, $t0, -2048 # UC, PLV0, 0x8000 xxxx xxxx xxxx + csrwr $t0, 0x180 # LOONGARCH_CSR_DMWIN0 + ori $t0, $zero, 0x11 # CSR_DMW1_MAT | CSR_DMW1_PLV0 + lu52i.d $t0, $t0, -1792 # CA, PLV0, 0x9000 xxxx xxxx xxxx + csrwr $t0, 0x181 # LOONGARCH_CSR_DMWIN1 + la.abs $t0, {sm_boot_stack_top} + ld.d $sp, $t0,0 # read boot stack top + + # Init MMU + bl {init_mmu} # setup boot page table and enabel MMU + invtlb 0x00, $r0, $r0 + + # Enable PG + li.w $t0, 0xb0 # PLV=0, IE=0, PG=1 + csrwr $t0, 0x0 # LOONGARCH_CSR_CRMD + li.w $t0, 0x00 # PLV=0, PIE=0, PWE=0 + csrwr $t0, 0x1 # LOONGARCH_CSR_PRMD + li.w $t0, 0x00 # FPE=0, SXE=0, ASXE=0, BTE=0 + csrwr $t0, 0x2 # LOONGARCH_CSR_EUEN + + csrrd $a0, 0x20 # cpuid + la.global $t0, {entry} + jirl $zero, $t0, 0 + ", + sm_boot_stack_top = sym super::mp::SMP_BOOT_STACK_TOP, + init_mmu = sym init_mmu, + entry = sym super::rust_entry_secondary, + ) + } +} diff --git a/modules/axhal/src/platform/loongarch64_qemu_virt/console.rs b/modules/axhal/src/platform/loongarch64_qemu_virt/console.rs new file mode 100644 index 0000000000..2a4bf9c227 --- /dev/null +++ b/modules/axhal/src/platform/loongarch64_qemu_virt/console.rs @@ -0,0 +1,44 @@ +use crate::mem::phys_to_virt; +use kspin::SpinNoIrq; +use lazyinit::LazyInit; +use memory_addr::PhysAddr; +use ns16550a::Uart; + +const UART_BASE: PhysAddr = pa!(axconfig::devices::UART_PADDR); + +static UART: LazyInit> = LazyInit::new(); + +/// Writes bytes to the console from input u8 slice. +pub fn write_bytes(bytes: &[u8]) { + let uart = UART.lock(); + for &c in bytes { + match c { + b'\n' => { + let _ = uart.put(b'\r'); + let _ = uart.put(b'\n'); + } + c => { + let _ = uart.put(c); + } + } + } +} + +/// Reads bytes from the console into the given mutable slice. +/// Returns the number of bytes read. +pub fn read_bytes(bytes: &mut [u8]) -> usize { + let uart = UART.lock(); + for (i, byte) in bytes.iter_mut().enumerate() { + match uart.get() { + Some(c) => *byte = c, + None => return i, + } + } + bytes.len() +} + +/// Early stage initialization for ns16550a +pub(super) fn init_early() { + let vaddr = phys_to_virt(UART_BASE); + UART.init_once(SpinNoIrq::new(Uart::new(vaddr.as_usize()))); +} diff --git a/modules/axhal/src/platform/loongarch64_qemu_virt/irq.rs b/modules/axhal/src/platform/loongarch64_qemu_virt/irq.rs new file mode 100644 index 0000000000..669bac9990 --- /dev/null +++ b/modules/axhal/src/platform/loongarch64_qemu_virt/irq.rs @@ -0,0 +1,70 @@ +use crate::irq::IrqHandler; +use lazyinit::LazyInit; +use loongArch64::register::{ + ecfg::{self, LineBasedInterrupt}, + ticlr, +}; + +/// The maximum number of IRQs. +pub const MAX_IRQ_COUNT: usize = 256; + +/// The Extend IRQ number. +pub const EXT_IRQ_NUM: usize = 2; + +/// The timer IRQ number. +pub const TIMER_IRQ_NUM: usize = 11; + +static TIMER_HANDLER: LazyInit = LazyInit::new(); + +macro_rules! with_cause { + ($cause: expr, @TIMER => $timer_op: expr, @EXT => $ext_op: expr $(,)?) => { + match $cause { + TIMER_IRQ_NUM => $timer_op, + EXT_IRQ_NUM => $ext_op, + _ => panic!("invalid trap cause: {:#x}", $cause), + } + }; +} + +/// Enables or disables the given IRQ. +pub fn set_enable(irq_num: usize, enabled: bool) { + if irq_num == TIMER_IRQ_NUM { + let old_value = ecfg::read().lie(); + let new_value = match enabled { + true => old_value | LineBasedInterrupt::TIMER, + false => old_value & !LineBasedInterrupt::TIMER, + }; + ecfg::set_lie(new_value); + } +} + +/// Registers an IRQ handler for the given IRQ. +pub fn register_handler(irq_num: usize, handler: crate::irq::IrqHandler) -> bool { + with_cause!( + irq_num, + @TIMER => if !TIMER_HANDLER.is_inited() { + log::debug!("timer init: {}", TIMER_HANDLER.is_inited()); + TIMER_HANDLER.init_once(handler); + true + } else { + false + }, + @EXT => crate::irq::register_handler_common(irq_num, handler), + ) +} + +/// Dispatches the IRQ. +/// +/// This function is called by the common interrupt handler. It looks +/// up in the IRQ handler table and calls the corresponding handler. If +/// necessary, it also acknowledges the interrupt controller after handling. +pub fn dispatch_irq(irq_num: usize) { + with_cause!( + irq_num, + @TIMER => { + ticlr::clear_timer_interrupt(); + TIMER_HANDLER(); + }, + @EXT => crate::irq::dispatch_irq_common(0), + ); +} diff --git a/modules/axhal/src/platform/loongarch64_qemu_virt/mem.rs b/modules/axhal/src/platform/loongarch64_qemu_virt/mem.rs new file mode 100644 index 0000000000..7bf656bd48 --- /dev/null +++ b/modules/axhal/src/platform/loongarch64_qemu_virt/mem.rs @@ -0,0 +1,4 @@ +/// Returns platform-specific memory regions. +pub(crate) fn platform_regions() -> impl Iterator { + crate::mem::default_free_regions().chain(crate::mem::default_mmio_regions()) +} diff --git a/modules/axhal/src/platform/loongarch64_qemu_virt/misc.rs b/modules/axhal/src/platform/loongarch64_qemu_virt/misc.rs new file mode 100644 index 0000000000..c3496aeac8 --- /dev/null +++ b/modules/axhal/src/platform/loongarch64_qemu_virt/misc.rs @@ -0,0 +1,15 @@ +use crate::mem::phys_to_virt; +use memory_addr::pa; + +const HALT_ADDR: *mut u8 = phys_to_virt(pa!(axconfig::devices::GED_PADDR)).as_mut_ptr(); + +/// Shutdown the whole system, including all CPUs. +pub fn terminate() -> ! { + info!("Shutting down..."); + unsafe { HALT_ADDR.write_volatile(0x34) }; + crate::arch::halt(); + warn!("It should shutdown!"); + loop { + crate::arch::halt(); + } +} diff --git a/modules/axhal/src/platform/loongarch64_qemu_virt/mod.rs b/modules/axhal/src/platform/loongarch64_qemu_virt/mod.rs new file mode 100644 index 0000000000..50a1fc7a4d --- /dev/null +++ b/modules/axhal/src/platform/loongarch64_qemu_virt/mod.rs @@ -0,0 +1,48 @@ +mod boot; + +pub mod console; +#[cfg(feature = "irq")] +pub mod irq; +pub mod mem; +pub mod misc; +#[cfg(feature = "smp")] +pub mod mp; +pub mod time; + +/// Initializes the platform devices for the primary CPU. +pub fn platform_init() {} + +/// Initializes the platform devices for secondary CPUs. +#[cfg(feature = "smp")] +pub fn platform_init_secondary() {} + +unsafe extern "C" { + fn rust_main(cpu_id: usize, dtb: usize); + #[cfg(feature = "smp")] + fn rust_main_secondary(cpu_id: usize); +} + +/// Rust temporary entry point +/// +/// This function will be called after assembly boot stage. +unsafe extern "C" fn rust_entry(cpu_id: usize) { + crate::mem::clear_bss(); + super::console::init_early(); + crate::cpu::init_primary(cpu_id); + super::time::init_percpu(); + + unsafe { + rust_main(cpu_id, 0); + } +} + +#[cfg(feature = "smp")] +/// The entry point for the second core. +pub(crate) extern "C" fn rust_entry_secondary(cpu_id: usize) { + crate::cpu::init_secondary(cpu_id); + super::time::init_percpu(); + + unsafe { + rust_main_secondary(cpu_id); + } +} diff --git a/modules/axhal/src/platform/loongarch64_qemu_virt/mp.rs b/modules/axhal/src/platform/loongarch64_qemu_virt/mp.rs new file mode 100644 index 0000000000..ad0962e769 --- /dev/null +++ b/modules/axhal/src/platform/loongarch64_qemu_virt/mp.rs @@ -0,0 +1,18 @@ +use loongArch64::ipi::{csr_mail_send, send_ipi_single}; + +use crate::mem::phys_to_virt; + +pub static mut SMP_BOOT_STACK_TOP: usize = 0; + +/// Starts the given secondary CPU with its boot stack. +pub fn start_secondary_cpu(cpu_id: usize, stack_top: crate::mem::PhysAddr) { + unsafe extern "C" { + fn _start_secondary(); + } + let stack_top_virt_addr = phys_to_virt(stack_top).as_usize(); + unsafe { + SMP_BOOT_STACK_TOP = stack_top_virt_addr; + } + csr_mail_send(_start_secondary as usize as _, cpu_id, 0); + send_ipi_single(cpu_id, 1); +} diff --git a/modules/axhal/src/platform/loongarch64_qemu_virt/time.rs b/modules/axhal/src/platform/loongarch64_qemu_virt/time.rs new file mode 100644 index 0000000000..29eed1b3f7 --- /dev/null +++ b/modules/axhal/src/platform/loongarch64_qemu_virt/time.rs @@ -0,0 +1,62 @@ +use loongArch64::time::Time; + +const NANOS_PER_TICK: u64 = crate::time::NANOS_PER_SEC / axconfig::devices::TIMER_FREQUENCY as u64; + +/// RTC wall time offset in nanoseconds at monotonic time base. +static mut RTC_EPOCHOFFSET_NANOS: u64 = 0; + +/// Returns the current clock time in hardware ticks. +#[inline] +pub fn current_ticks() -> u64 { + Time::read() as _ +} + +/// Return epoch offset in nanoseconds (wall time offset to monotonic clock start). +#[inline] +pub fn epochoffset_nanos() -> u64 { + unsafe { RTC_EPOCHOFFSET_NANOS } +} + +/// Converts hardware ticks to nanoseconds. +#[inline] +pub fn ticks_to_nanos(ticks: u64) -> u64 { + ticks * NANOS_PER_TICK +} + +/// Converts nanoseconds to hardware ticks. +#[inline] +pub fn nanos_to_ticks(nanos: u64) -> u64 { + nanos / NANOS_PER_TICK +} + +/// Set a one-shot timer. +/// +/// A timer interrupt will be triggered at the specified monotonic time deadline (in nanoseconds). +/// +/// LoongArch64 TCFG CSR: +#[cfg(feature = "irq")] +pub fn set_oneshot_timer(deadline_ns: u64) { + use loongArch64::register::tcfg; + + let ticks_now = current_ticks(); + let ticks_deadline = nanos_to_ticks(deadline_ns); + let init_value = ticks_deadline - ticks_now; + + // This initial value must be an integer multiple of 4. + tcfg::set_init_val(((init_value + 3) & !3) as _); + tcfg::set_periodic(false); + tcfg::set_en(true); +} + +pub(super) fn init_percpu() { + use loongArch64::register::tcfg; + + tcfg::set_init_val(0); + tcfg::set_periodic(false); + tcfg::set_en(true); + + #[cfg(feature = "irq")] + { + super::irq::set_enable(super::irq::TIMER_IRQ_NUM, true); + } +} diff --git a/modules/axhal/src/platform/mod.rs b/modules/axhal/src/platform/mod.rs index 349aac9ae0..cefb23b22a 100644 --- a/modules/axhal/src/platform/mod.rs +++ b/modules/axhal/src/platform/mod.rs @@ -25,6 +25,9 @@ cfg_if::cfg_if! { } else if #[cfg(all(target_arch = "aarch64", platform_family = "aarch64-phytium-pi"))] { mod aarch64_phytium_pi; pub use self::aarch64_phytium_pi::*; + } else if #[cfg(all(target_arch = "loongarch64", platform_family = "loongarch64-qemu-virt"))] { + mod loongarch64_qemu_virt; + pub use self::loongarch64_qemu_virt::*; } else { mod dummy; pub use self::dummy::*; diff --git a/modules/axhal/src/tls.rs b/modules/axhal/src/tls.rs index e9d12e1a24..331fda3446 100644 --- a/modules/axhal/src/tls.rs +++ b/modules/axhal/src/tls.rs @@ -68,6 +68,9 @@ cfg_if::cfg_if! { } else if #[cfg(target_arch = "riscv64")] { const TCB_SIZE: usize = 0; const GAP_ABOVE_TP: usize = 0; + } else if #[cfg(target_arch = "loongarch64")] { + const TCB_SIZE: usize = 0; + const GAP_ABOVE_TP: usize = 0; } } @@ -131,7 +134,11 @@ fn static_tls_size() -> usize { fn static_tls_offset() -> usize { if cfg!(target_arch = "x86_64") { 0 - } else if cfg!(any(target_arch = "aarch64", target_arch = "riscv64")) { + } else if cfg!(any( + target_arch = "aarch64", + target_arch = "riscv64", + target_arch = "loongarch64" + )) { TCB_SIZE + GAP_ABOVE_TP } else { unreachable!() @@ -141,7 +148,11 @@ fn static_tls_offset() -> usize { fn tp_offset() -> usize { if cfg!(target_arch = "x86_64") { static_tls_size() - } else if cfg!(any(target_arch = "aarch64", target_arch = "riscv64")) { + } else if cfg!(any( + target_arch = "aarch64", + target_arch = "riscv64", + target_arch = "loongarch64" + )) { TCB_SIZE } else { unreachable!() @@ -151,7 +162,11 @@ fn tp_offset() -> usize { fn tls_area_size() -> usize { if cfg!(target_arch = "x86_64") { static_tls_size() + TCB_SIZE - } else if cfg!(any(target_arch = "aarch64", target_arch = "riscv64")) { + } else if cfg!(any( + target_arch = "aarch64", + target_arch = "riscv64", + target_arch = "loongarch64" + )) { TCB_SIZE + GAP_ABOVE_TP + static_tls_size() } else { unreachable!() diff --git a/modules/axmm/src/lib.rs b/modules/axmm/src/lib.rs index d8e2668b37..ee2cc742f4 100644 --- a/modules/axmm/src/lib.rs +++ b/modules/axmm/src/lib.rs @@ -45,9 +45,10 @@ pub fn new_kernel_aspace() -> AxResult { /// Creates a new address space for user processes. pub fn new_user_aspace(base: VirtAddr, size: usize) -> AxResult { let mut aspace = AddrSpace::new_empty(base, size)?; - if !cfg!(target_arch = "aarch64") { - // ARMv8 use a separate page table (TTBR0_EL1) for user space, it - // doesn't need to copy the kernel portion to the user page table. + if !cfg!(target_arch = "aarch64") && !cfg!(target_arch = "loongarch64") { + // ARMv8 (aarch64) and LoongArch64 use separate page tables for user space + // (aarch64: TTBR0_EL1, LoongArch64: PGDL), so there is no need to copy the + // kernel portion to the user page table. aspace.copy_mappings_from(&kernel_aspace().lock())?; } Ok(aspace) diff --git a/scripts/make/platform.mk b/scripts/make/platform.mk index 9c85176421..9a9ad29ea2 100644 --- a/scripts/make/platform.mk +++ b/scripts/make/platform.mk @@ -8,8 +8,10 @@ ifeq ($(PLATFORM),) PLAT_NAME := aarch64-qemu-virt else ifeq ($(ARCH), riscv64) PLAT_NAME := riscv64-qemu-virt + else ifeq ($(ARCH), loongarch64) + PLAT_NAME := loongarch64-qemu-virt else - $(error "ARCH" must be one of "x86_64", "riscv64", or "aarch64") + $(error "ARCH" must be one of "x86_64", "riscv64", "aarch64" or "loongarch64") endif PLAT_CONFIG := configs/platforms/$(PLAT_NAME).toml else diff --git a/scripts/make/qemu.mk b/scripts/make/qemu.mk index b7b8db6cb1..911a1e80b6 100644 --- a/scripts/make/qemu.mk +++ b/scripts/make/qemu.mk @@ -21,6 +21,9 @@ else ifeq ($(ARCH), aarch64) else machine := virt endif +else ifeq ($(ARCH), loongarch64) + machine := virt + override MEM := 1G endif qemu_args-x86_64 := \ @@ -37,6 +40,9 @@ qemu_args-aarch64 := \ -machine $(machine) \ -kernel $(OUT_BIN) +qemu_args-loongarch64 := \ + -kernel $(OUT_ELF) + qemu_args-y := -m $(MEM) -smp $(SMP) $(qemu_args-$(ARCH)) qemu_args-$(BLK) += \ diff --git a/ulib/axlibc/include/float.h b/ulib/axlibc/include/float.h index 32ac40abfb..3a0da9cdfe 100644 --- a/ulib/axlibc/include/float.h +++ b/ulib/axlibc/include/float.h @@ -72,6 +72,23 @@ #define LDBL_MIN_10_EXP (-4931) #define LDBL_MAX_10_EXP 4932 +#define DECIMAL_DIG 36 +#elif defined(__loongarch__) +#define FLT_EVAL_METHOD 0 + +#define LDBL_TRUE_MIN 6.47517511943802511092443895822764655e-4966L +#define LDBL_MIN 3.36210314311209350626267781732175260e-4932L +#define LDBL_MAX 1.18973149535723176508575932662800702e+4932L +#define LDBL_EPSILON 1.92592994438723585305597794258492732e-34L + +#define LDBL_MANT_DIG 113 +#define LDBL_MIN_EXP (-16381) +#define LDBL_MAX_EXP 16384 + +#define LDBL_DIG 33 +#define LDBL_MIN_10_EXP (-4931) +#define LDBL_MAX_10_EXP 4932 + #define DECIMAL_DIG 36 #elif defined(__x86_64__) #ifdef __FLT_EVAL_METHOD__ diff --git a/ulib/axlibc/include/setjmp.h b/ulib/axlibc/include/setjmp.h index 70981dfb45..5f5ee0a076 100644 --- a/ulib/axlibc/include/setjmp.h +++ b/ulib/axlibc/include/setjmp.h @@ -9,6 +9,8 @@ typedef unsigned long __jmp_buf[22]; typedef unsigned long __jmp_buf[26]; #elif defined(__x86_64__) typedef unsigned long __jmp_buf[8]; +#elif defined(__loongarch__) +typedef unsigned long __jmp_buf[21]; #endif typedef struct __jmp_buf_tag { diff --git a/ulib/axlibc/include/stdint.h b/ulib/axlibc/include/stdint.h index 3081bf0116..89d87f2458 100644 --- a/ulib/axlibc/include/stdint.h +++ b/ulib/axlibc/include/stdint.h @@ -38,7 +38,7 @@ typedef int64_t intmax_t; * We use pointer types to represent addresses, * uintptr_t to represent the numerical values of addresses. * */ -#if __riscv_xlen == 64 || defined(__x86_64__) || defined(__aarch64__) +#if __riscv_xlen == 64 || defined(__x86_64__) || defined(__aarch64__) || defined(__loongarch__) typedef int64_t intptr_t; typedef uint64_t uintptr_t; #elif __riscv_xlen == 32 || defined(__i386__) diff --git a/ulib/axlibc/src/setjmp.rs b/ulib/axlibc/src/setjmp.rs index 043e92e601..077f813f17 100644 --- a/ulib/axlibc/src/setjmp.rs +++ b/ulib/axlibc/src/setjmp.rs @@ -106,10 +106,59 @@ pub unsafe extern "C" fn setjmp(_buf: *mut ctypes::__jmp_buf_tag) { li a0, 0 ret", ); + #[cfg(all(target_arch = "loongarch64", feature = "fp_simd"))] + core::arch::naked_asm!( + "st.d $ra, $a0, 0 + st.d $sp, $a0, 1 * 8 + st.d $s0, $a0, 2 * 8 + st.d $s1, $a0, 3 * 8 + st.d $s2, $a0, 4 * 8 + st.d $s3, $a0, 5 * 8 + st.d $s4, $a0, 6 * 8 + st.d $s5, $a0, 7 * 8 + st.d $s6, $a0, 8 * 8 + st.d $s7, $a0, 9 * 8 + st.d $s8, $a0, 10 * 8 + st.d $fp, $a0, 11 * 8 + st.d $r1, $a0, 12 * 8 + + fst.d $f24, $a0, 13 * 8 + fst.d $f25, $a0, 14 * 8 + fst.d $f26, $a0, 15 * 8 + fst.d $f27, $a0, 16 * 8 + fst.d $f28, $a0, 17 * 8 + fst.d $f29, $a0, 18 * 8 + fst.d $f30, $a0, 19 * 8 + fst.d $f31, $a0, 20 * 8 + + li.w $a0, 0 + ret + ", + ); + #[cfg(all(target_arch = "loongarch64", not(feature = "fp_simd")))] + core::arch::naked_asm!( + "st.d $ra, $a0, 0 + st.d $sp, $a0, 1 * 8 + st.d $s0, $a0, 2 * 8 + st.d $s1, $a0, 3 * 8 + st.d $s2, $a0, 4 * 8 + st.d $s3, $a0, 5 * 8 + st.d $s4, $a0, 6 * 8 + st.d $s5, $a0, 7 * 8 + st.d $s6, $a0, 8 * 8 + st.d $s7, $a0, 9 * 8 + st.d $s8, $a0, 10 * 8 + st.d $fp, $a0, 11 * 8 + st.d $r1, $a0, 12 * 8 + + li.w $a0, 0 + ret", + ); #[cfg(not(any( target_arch = "aarch64", target_arch = "x86_64", - target_arch = "riscv64" + target_arch = "riscv64", + target_arch = "loongarch64" )))] core::arch::naked_asm!("ret") } @@ -225,4 +274,56 @@ pub unsafe extern "C" fn longjmp(_buf: *mut ctypes::__jmp_buf_tag, _val: c_int) add a0, a0, a1 ret", ); + + #[cfg(all(target_arch = "loongarch64", feature = "fp_simd"))] + core::arch::naked_asm!( + "ld.d $ra, $a1, 0 + ld.d $s0, $a1, 2 * 8 + ld.d $s1, $a1, 3 * 8 + ld.d $s2, $a1, 4 * 8 + ld.d $s3, $a1, 5 * 8 + ld.d $s4, $a1, 6 * 8 + ld.d $s5, $a1, 7 * 8 + ld.d $s6, $a1, 8 * 8 + ld.d $s7, $a1, 9 * 8 + ld.d $s8, $a1, 10 * 8 + ld.d $fp, $a1, 11 * 8 + ld.d $sp, $a1, 1 * 8 + ld.d $r21, $a1, 12 * 8 + + fld.d $f24, $a0, 13 * 8 + fld.d $f25, $a0, 14 * 8 + fld.d $f26, $a0, 15 * 8 + fld.d $f27, $a0, 16 * 8 + fld.d $f28, $a0, 17 * 8 + fld.d $f29, $a0, 18 * 8 + fld.d $f30, $a0, 19 * 8 + fld.d $f31, $a0, 20 * 8 + + sltui $a0, $a1, 1 + add.d $a0, $a0, $a1 + jirl $zero,$ra, 0 + " + ); + #[cfg(all(target_arch = "loongarch64", not(feature = "fp_simd")))] + core::arch::naked_asm!( + "ld.d $ra, $a1, 0 + ld.d $s0, $a1, 2 * 8 + ld.d $s1, $a1, 3 * 8 + ld.d $s2, $a1, 4 * 8 + ld.d $s3, $a1, 5 * 8 + ld.d $s4, $a1, 6 * 8 + ld.d $s5, $a1, 7 * 8 + ld.d $s6, $a1, 8 * 8 + ld.d $s7, $a1, 9 * 8 + ld.d $s8, $a1, 10 * 8 + ld.d $fp, $a1, 11 * 8 + ld.d $sp, $a1, 1 * 8 + ld.d $r21, $a1, 12 * 8 + + sltui $a0, $a1, 1 + add.d $a0, $a0, $a1 + jirl $zero,$ra, 0 + ", + ); }