Skip to content

La32r Setup

Toolchains

We recommend to use chenyy/la32r-env for uCore or Linux build.

$ docker pull chenyy/la32r-env:latest

Then we can launch the container based on the image.

$ docker run -dit \
    --name la32r-docker \
    --net=host \
    -e LANG=en_US.UTF-8 \
    -e LANGUAGE=en_US.UTF-8 \
    -e LC_ALL=en_US.UTF-8 \
    chenyy/la32r-env:latest

Now you can enter the container and use la32r toolchains.

$ docker exec -it la32r-docker /bin/zsh

uCore boot

First, we need to build uCore & CEMU.

$ git clone https://github.com/cyyself/ucore-loongarch32.git
$ pushd ucore-loongarch32
$ make
$ loongarch32r-linux-gnusf-objcopy -O binary obj/ucore-kernel-initrd obj/ucore-kernel-initrd.bin
$ popd
$ git clone https://github.com/cyyself/cemu.git
$ cd cemu

Save the following content to src/main.cpp

src/main.cpp
#include <iostream>
#include <bitset>
#include <termios.h>
#include <unistd.h>
#include <thread>
#include <signal.h>

#include "device/nscscc_confreg.hpp"
#include "device/uart8250.hpp"
#include "memory/memory_bus.hpp"
#include "memory/ram.hpp"
#include "core/la32r/la32r_core.hpp"

void uart_input(uart8250 &uart) {
    termios tmp;
    tcgetattr(STDIN_FILENO, &tmp);
    tmp.c_lflag &= (~ICANON & ~ECHO);
    tcsetattr(STDIN_FILENO, TCSANOW, &tmp);
    while (true) {
        char c = getchar();
        if (c == 10) c = 13; // convert lf to cr
        uart.putc(c);
    }
}

bool send_ctrl_c;

void sigint_handler(int x) {
    static time_t last_time;
    if (time(NULL) - last_time < 1) exit(0);
    last_time = time(NULL);
    send_ctrl_c = true;
}

int main(int argc, char **argv) {
    signal(SIGINT, sigint_handler);

    memory_bus cemu_mmio;

    ram cemu_memory(128 * 1024 * 1024);
    cemu_memory.load_binary(0x000000, "../ucore-loongarch32/obj/ucore-kernel-initrd.bin");
    assert(cemu_mmio.add_dev(0, 128 * 1024 * 1024, &cemu_memory));

    uart8250 uart;
    std::thread *uart_input_thread = new std::thread(uart_input, std::ref(uart));
    assert(cemu_mmio.add_dev(0x1fe001e0, 0x10, &uart));

    la32r_core<32> core(0, cemu_mmio, false);
    core.csr_cfg(0x180, 0xa0000011u);
    core.csr_cfg(0x181, 0x80000001u);
    core.csr_cfg(0x0, 0xb0);
    core.jump(0xa0000000u);
    while (true) {
        core.step(uart.irq() << 1);
        while (uart.exist_tx()) {
            char c = uart.getc();
            if (c != '\r') {
                putchar(c);
                fflush(stdout);
            }
        }
        if (send_ctrl_c) {
            uart.putc(3);
            send_ctrl_c = false;
        }
    }
    return 0;
}

Then build and run cemu.

$ make  # in cemu project folder
$ ./cemu
++setup timer interrupts
(THU.CST) os is loading ...

Special kernel symbols:
  entry  0xA0000120 (phys)
  etext 0xA0022000 (phys)
  edata 0xA025CEA0 (phys)
  end   0xA0260180 (phys)
Kernel executable memory footprint: 2297KB
memory management: default_pmm_manager
memory map:
    [A0000000, A2000000]

freemem start at: A02A1000
free pages: 00001D5F
## 00000020
check_alloc_page() succeeded!
check_pgdir() succeeded!
check_boot_pgdir() succeeded!
check_slab() succeeded!
kmalloc_init() succeeded!
check_vma_struct() succeeded!
check_pgfault() succeeded!
check_vmm() succeeded.
sched class: stride_scheduler
proc_init succeeded
Initrd: 0xa00601c0 - 0xa02541bf, size: 0x001f4000, magic: 0x2f8dbe2a
ramdisk_init(): initrd found, magic: 0x2f8dbe2a, 0x00000fa0 secs
sfs: mount: 'simple file system' (352/148/500)
vfs: mount disk0.
kernel_execve: pid = 2, name = "sh".
user sh is running!!!
$

Linux boot

Download Prebuilt Busybox Ramdisk

Use the following command to download prebuilt busybox ramdisk.

$ mkdir -p initrd_d
$ wget -qO- https://gitee.com/loongson-edu/la32r-Linux/releases/download/v0.2/initrd_d.tar.gz | tar -xv -C initrd_d --strip-components 1

Build Linux Kernel

First, we need to clone la32r-Linux repository.

$ git clone --depth 1 https://gitee.com/loongson-edu/la32r-Linux.git
$ pushd la32r-Linux

Save the following content to file cemu.patch.

cemu.patch
From 78c2470efcd6b285e3d0e73893d53b32c51b809b Mon Sep 17 00:00:00 2001
From: coekjan <cn_yzr@qq.com>
Date: Fri, 12 May 2023 23:44:37 +0800
Subject: [PATCH] Patch For CEMU

---
 .../boot/dts/loongson/loongson32_ls.dts       | 43 -------------------
 arch/loongarch/include/asm/time.h             |  2 +-
 la_build.sh                                   |  5 ++-
 3 files changed, 4 insertions(+), 46 deletions(-)

diff --git a/arch/loongarch/boot/dts/loongson/loongson32_ls.dts b/arch/loongarch/boot/dts/loongson/loongson32_ls.dts
index f33c0ce23..947605d40 100644
--- a/arch/loongarch/boot/dts/loongson/loongson32_ls.dts
+++ b/arch/loongarch/boot/dts/loongson/loongson32_ls.dts
@@ -56,49 +56,6 @@ cpu_uart0: serial@0x1fe001e0 {
                         no-loopback-test;
                 };

-        gmac0: dmfe@0x1ff00000{
-                        compatible = "dmfe";
-                        reg = <0x1ff00000 0x10000>;
-                        interrupt-parent = <&cpuic>;
-                        interrupts = <3>;
-                        interrupt-names = "macirq";
-                        mac-address = [ 64 48 48 48 48 60 ];/* [>mac 64:48:48:48:48:60 <]*/
-                        phy-mode = "rgmii";
-                        bus_id = <0x0>;
-                        phy_addr = <0xffffffff>;
-                        dma-mask = <0xffffffff 0xffffffff>;
-                };
-#if 0
-            ahci@0x1fe30000{
-                compatible = "snps,spear-ahci";
-                reg = <0x1fe30000 0x10000>;
-                interrupt-parent = <&cpuic>;
-                interrupts = <4>;
-                dma-mask = <0x0 0xffffffff>;
-            };
-#endif
-
-        nand@0x1fe78000{
-             #address-cells = <1>;
-             #size-cells = <1>;
-             compatible = "ls1a-nand";
-             reg = <0x1fe78000 0x4000
-                 0x1fd01160 0x0>;
-             interrupt-parent = <&cpuic>;
-             interrupts = <4>;
-             interrupt-names = "nand_irq";
-
-             number-of-parts = <0x2>;
-             partition@0 {
-                 label = "kernel_partition";
-                 reg = <0x0000000 0x01400000>;
-             };
-
-             partition@0x01400000 {
-                 label = "os_partition";
-                 reg = <0x01400000 0x0>;
-             };
-         };

     };
 };
diff --git a/arch/loongarch/include/asm/time.h b/arch/loongarch/include/asm/time.h
index d4cc3876d..1a348b561 100644
--- a/arch/loongarch/include/asm/time.h
+++ b/arch/loongarch/include/asm/time.h
@@ -41,7 +41,7 @@ static inline unsigned int calc_const_freq(void)
 #define LOONGARCH32_FREQ_REG 0x9fd0f030
 static inline unsigned int calc_const_freq(void)
 {
-    unsigned int freq = *(int *)LOONGARCH32_FREQ_REG;
+    unsigned int freq = 33333333;
    return freq;
 }
 #endif
diff --git a/la_build.sh b/la_build.sh
index 7ce234a08..b32815540 100755
--- a/la_build.sh
+++ b/la_build.sh
@@ -1,6 +1,6 @@
 #!/bin/bash

-export CROSS_COMPILE=~/install-32-glibc-loongarch-novec-reduce-linux-5-14/bin/loongarch32-linux-gnu-
+export CROSS_COMPILE=loongarch32r-linux-gnusf-
 export ARCH=loongarch
 OUT=la_build

@@ -9,7 +9,8 @@ if [ ! -d la_build ] ;then
     make la32_defconfig O=${OUT}
 fi

+sed -i "/^CONFIG_INITRAMFS_SOURCE/s,=.*$,=\"$(realpath ../initrd_d)\"," ./la_build/.config
+
 echo "----------------output ${OUT}----------------"

-make menuconfig O=${OUT}
 make vmlinux -j  O=${OUT} 2>&1 | tee -a build_error.log
-- 
2.40.1

Then, apply the patch and build kernel.

$ git apply cemu.patch
$ ./la_build.sh
$ loongarch32r-linux-gnusf-objcopy -O binary la_build/vmlinux la_build/vmlinux.bin
$ popd

Build & RUN CEMU

Clone CEMU repository.

$ git clone https://github.com/cyyself/cemu.git
$ cd cemu

Fetch init_5f.txt from chiplab.

$ wget -q https://gitee.com/loongson-edu/chiplab/raw/chiplab_diff/software/linux/init_5f.txt

Save the following content to src/main.cpp

src/main.cpp
#include <iostream>
#include <bitset>
#include <termios.h>
#include <unistd.h>
#include <thread>
#include <signal.h>

#include "device/nscscc_confreg.hpp"
#include "device/uart8250.hpp"
#include "memory/memory_bus.hpp"
#include "memory/ram.hpp"
#include "core/la32r/la32r_core.hpp"

void uart_input(uart8250 &uart) {
    termios tmp;
    tcgetattr(STDIN_FILENO, &tmp);
    tmp.c_lflag &= (~ICANON & ~ECHO);
    tcsetattr(STDIN_FILENO, TCSANOW, &tmp);
    while (true) {
        char c = getchar();
        if (c == 10) c = 13; // convert lf to cr
        uart.putc(c);
    }
}

bool send_ctrl_c;

void sigint_handler(int x) {
    static time_t last_time;
    if (time(NULL) - last_time < 1) exit(0);
    last_time = time(NULL);
    send_ctrl_c = true;
}

int main(int argc, char **argv) {
    signal(SIGINT, sigint_handler);

    memory_bus cemu_mmio;

    ram cemu_memory(128 * 1024 * 1024);
    cemu_memory.load_binary(0x300000, "../la32r-Linux/la_build/vmlinux.bin");
    cemu_memory.load_text(0x5f00000, "./init_5f.txt");
    assert(cemu_mmio.add_dev(0, 128 * 1024 * 1024, &cemu_memory));

    uart8250 uart;
    std::thread *uart_input_thread = new std::thread(uart_input, std::ref(uart));
    assert(cemu_mmio.add_dev(0x1fe001e0, 0x10, &uart));

    la32r_core<32> core(0, cemu_mmio, false);
    core.csr_cfg(0x180, 0xa0000001u);
    core.csr_cfg(0x181, 0x00000001u);
    core.csr_cfg(0x0, 0x10);
    core.reg_cfg(4, 2);
    core.reg_cfg(5, 0xa5f00000u);
    core.reg_cfg(6, 0xa5f00080u);
    core.jump(0xa07c5c28u);

    while (true) {
        core.step(uart.irq() << 1);
        while (uart.exist_tx()) {
            char c = uart.getc();
            if (c != '\r') {
                putchar(c);
                fflush(stdout);
            }
        }
        if (send_ctrl_c) {
            uart.putc(3);
            send_ctrl_c = false;
        }
    }
    return 0;
}

Finally, build and run CEMU.

$ make  # in cemu project folder
$ ./cemu
[    0.000000] Linux version 5.14.0-rc2-gbb6b2ca56ade-dirty (@Inspiron-Manjaro) (loongarch32r-linux-gnusf-gcc (GCC) 8.3.0, GNU ld (GNU Binutils) 2.31.1.20190122) #3 PREEMPT Fri May 12 16:08:23 UTC 2023
[    0.000000] Standard 32-bit Loongson Processor probed
[    0.000000] the link is empty!
[    0.000000] Scan bootparam failed
[    0.000000] printk: bootconsole [early0] enabled
[    0.000000] initrd start < PAGE_OFFSET
[    0.000000] Can't find EFI system table.
[    0.000000] start_pfn=0x0, end_pfn=0x8000, num_physpages:0x8000
[    0.000000] The BIOS Version: (null)
[    0.000000] Initrd not found or empty - disabling initrd
[    0.000000] CPU0 revision is: 00004200 (Loongson-32bit)
[    0.000000] Primary instruction cache 8kB, 2-way, VIPT, linesize 16 bytes.
[    0.000000] Primary data cache 8kB, 2-way, VIPT, no aliases, linesize 16 bytes
[    0.000000] Zone ranges:
[    0.000000]   DMA32    [mem 0x0000000000000000-0x00000000ffffffff]
[    0.000000]   Normal   empty
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   0: [mem 0x0000000000000000-0x0000000007ffffff]
[    0.000000] Initmem setup node 0 [mem 0x0000000000000000-0x0000000007ffffff]
[    0.000000] eentry = 0xa0210000,tlbrentry = 0xa0201000
[    0.000000] pcpu-alloc: s0 r0 d32768 u32768 alloc=1*32768
[    0.000000] pcpu-alloc: [0] 0 
[    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 32480
[    0.000000] Kernel command line: =/init loglevel=8
[    0.000000] Dentry cache hash table entries: 16384 (order: 4, 65536 bytes, linear)
[    0.000000] Inode-cache hash table entries: 8192 (order: 3, 32768 bytes, linear)
[    0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off
[    0.000000] Memory: 117736K/131072K available (4926K kernel code, 1114K rwdata, 944K rodata, 2480K init, 473K bss, 13336K reserved, 0K cma-reserved)
[    0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[    0.000000] rcu: Preemptible hierarchical RCU implementation.
[    0.000000] rcu:     RCU event tracing is enabled.
[    0.000000]  Trampoline variant of Tasks RCU enabled.
[    0.000000]  Tracing variant of Tasks RCU enabled.
[    0.000000] rcu: RCU calculated value of scheduler-enlistment delay is 25 jiffies.
[    0.000000] NR_IRQS: 320
[    0.000000] Constant clock event device register
[    0.000000] clocksource: Constant: mask: 0xffffffffffffffff max_cycles: 0x7b00c4bad, max_idle_ns: 440795202744 ns
[    0.000000] Constant clock source device register
[    0.004000] Console: colour dummy device 80x25
[    0.004000] Calibrating delay loop (skipped), value calculated using timer frequency.. 66.66 BogoMIPS (lpj=133333)
[    0.004000] pid_max: default: 32768 minimum: 301
[    0.008000] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
[    0.008000] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
[    0.036000] rcu: Hierarchical SRCU implementation.
[    0.040000] devtmpfs: initialized
[    0.052000] random: get_random_u32 called from bucket_table_alloc.isra.34+0x68/0x1d8 with crng_init=0
[    0.056000] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645041785100000 ns
[    0.056000] futex hash table entries: 256 (order: -1, 3072 bytes, linear)
[    0.064000] NET: Registered PF_NETLINK/PF_ROUTE protocol family
[    0.248000] pps_core: LinuxPPS API ver. 1 registered
[    0.248000] pps_core: Software ver. 5.3.6 - Copyright 2005-2007 Rodolfo Giometti <giometti@linux.it>
[    0.264000] clocksource: Switched to clocksource Constant
[    0.268000] FS-Cache: Loaded
[    0.384000] NET: Registered PF_INET protocol family
[    0.384000] IP idents hash table entries: 2048 (order: 2, 16384 bytes, linear)
[    0.400000] tcp_listen_portaddr_hash hash table entries: 512 (order: 0, 4096 bytes, linear)
[    0.400000] TCP established hash table entries: 1024 (order: 0, 4096 bytes, linear)
[    0.400000] TCP bind hash table entries: 1024 (order: 0, 4096 bytes, linear)
[    0.400000] TCP: Hash tables configured (established 1024 bind 1024)
[    0.400000] UDP hash table entries: 256 (order: 0, 4096 bytes, linear)
[    0.400000] UDP-Lite hash table entries: 256 (order: 0, 4096 bytes, linear)
[    0.404000] NET: Registered PF_UNIX/PF_LOCAL protocol family
[    0.428000] workingset: timestamp_bits=14 max_order=15 bucket_order=1
[    0.620000] IPMI message handler: version 39.2
[    0.620000] ipmi device interface
[    0.620000] ipmi_si: IPMI System Interface driver
[    0.628000] ipmi_si: Unable to find any System Interface(s)
[    0.732000] Serial: 8250/16550 driver, 16 ports, IRQ sharing enabled
[    0.816000] printk: console [ttyS0] disabled
[    0.816000] 1fe001e0.serial: ttyS0 at MMIO 0x1fe001e0 (irq = 18, base_baud = 2062500) is a 16550A
[    0.816000] printk: console [ttyS0] enabled
[    0.816000] printk: console [ttyS0] enabled
[    0.816000] printk: bootconsole [early0] disabled
[    0.816000] printk: bootconsole [early0] disabled
[    0.840000] ls1a-nand driver initializing
[    0.840000] ITC MAC 10/100M Fast Ethernet Adapter driver 1.0 init
[    0.864000] libphy: Fixed MDIO Bus: probed
[    0.872000] mousedev: PS/2 mouse device common for all mice
[    0.888000] IR MCE Keyboard/mouse protocol handler initialized
[    0.888000] hid: raw HID events driver (C) Jiri Kosina
[    0.920000] NET: Registered PF_INET6 protocol family
[    0.948000] Segment Routing with IPv6
[    0.948000] sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver
[    1.016000] random: fast init done
[    5.424000] Warning: unable to open an initial console.
[    5.752000] Freeing unused kernel image (initmem) memory: 2480K
[    5.752000] This architecture does not have kernel memory protection.
[    5.752000] Run /init as init process
[    5.752000]   with arguments:
[    5.752000]     /init
[    5.752000]   with environment:
[    5.752000]     HOME=/
[    5.752000]     TERM=linux

Processing /etc/profile... Done

/ # 

Last update: March 5, 2024