mirror of
https://github.com/memtest86plus/memtest86plus.git
synced 2025-02-25 18:55:23 -06:00
Add LoongArch support (#410)
* lib/assert: Add LoongArch assert support Added LoongArch break 3 assert instruction. Signed-off-by: Chao Li <lichao@loongson.cn> * lib/barrier: Add barrier method for LoongArch Added LoongArch barriers in barrier_spin_wait and barrier_halt_wait functions. Signed-off-by: Chao Li <lichao@loognson.cn> * lib/spinlock: Add LoongArch CPU pause Because the LoongArch haven't pause instruction, using eight nops to replace the pause. Signed-off-by: Chao Li <lichao@loongson.cn> * lib/string: Make LoongArch use the string function in the file Since LoongArch GCC doesn't have built-in string functions, use the string function instance in the sting.c Signed-off-by: Chao Li <lichao@loongson.cn> * lib/unistd: Add LoongArch CPU pause Because the LoongArch haven't pause instruction, using eight nops to replace the pause. Signed-off-by: Chao Li <lichao@loongson.cn> * system/acpi: Reduce the way of search RSDP for non-x86 ARCHs Searching RSDP from legacy BIOS EDBA and reserved areas is available only on i386 and x64. Signed-off-by: Chao Li <lichao@loongson.cn> * system/cache: Add LoongArch64 cache operations support Added cache operations support for LoongArch64. Signed-off-by: Chao Li <lichao@loongson.cn> * system/cpuid: Add the compile limit Make the `cpuid` function action only on i386/x64. Signed-off-by: Chao Li <lichao@loongson.cn> * system/heap: Add heap support for LoongArch64 LoongArch64 uses the low 256MB as the low memory. Signed-off-by: Chao Li <lichao@loongson.cn> * system/memrw: Add 8-bit and 16-bit memory operations Added 8-bit and 16-bit memory access operations, which 8-bit uses `movb` and 16-bit is `movw`. Signed-off-by: Chao Li <lichao@loongson.cn> * system/memrw: Add LoongArch memory access operations Added 8/16/32/64-bit memory access operations for LoongArch64. Signed-off-by: Chao Li <lichao@loongson.cn> * system: Add Loongson PCI vendor ID and Loongson 7A chipset EHCI workaround 1. Added Loongson PCI vendor ID. 2. Added Loongson 7A chipset ECHI workaround. Signed-off-by: Chao Li <lichao@loongson.cn> * system/io: Add LoongArch64 IO port operations Added IO port operations for LoongArch64. Signed-off-by: Chao Li <lichao@loongson.cn> * system/reloc64: Add LoongArch64 relocations support Added R_LARCH_RELATIVE and R_LARCH_NONE relocations support for LoongArch64. Signed-off-by: Chao Li <lichao@loongson.cn> * system/serial: Add Loongson CPU serial port support Add the serial port address perfix of Loongson CPU and obtain serial port clock method. Signed-off-by: Chao Li <lichao@loongson.cn> * system/smbus: Rename smbus.c to i2c_x86.c Renamed the smbus.c to i2c_x86.c in i386 and x64 platforms. Signed-off-by: Chao Li <lichao@loongson.cn> * system/smp: Add LoongArch SMP support Added LoongArch multi-core support and a way of map to node numbers if the NUMA is enabled. Signed-off-by: Chao Li <lichao@loongson.cn> * system/timers: Add LoongArch supports In LoongArch, there is a stable counter that is independent of other clocks, it like the TSC in x64. Using it to count the ticks per millisecond. Signed-off-by: Chao Li <lichao@loongson.cn> * system/tsc: Add LoongArch support Usually the frequency of stable counter is not same to CPU frequency, so using the performance counter for the delay operations. Signed-off-by: Chao Li <lichao@loongson.cn> * system/usbhcd: Add LoongArch MMIO perfix Added LoongArch64 MMIO address perfix, use for address the PCI memory space. Signed-off-by: Chao Li <lichao@loongson.cn> * system/usbhcd: Add Loongson 7A2000 chipset OHCI BAR offset fix If the BAR address is not fixed for the Loongson 7A2000 OHCI controller, some prots will not be usable, This change currently only affects the LoongArch platform. Signed-off-by: Chao Li <lichao@loongson.cn> * system: Add the way to IO access via MMIO Usually, it is access the IO like PCI IO via MMIO on non-X86 ARCHs, so a method to access IO via MMIO is added. Signed-off-by: Chao Li <lichao@loongson.cn> * system: Add the way to access PCI memory space via MMIO Some uniformly address ARCHs access the PCI memory depended the MMIO, so the method to access PCI memory via MMIO is added. Signed-off-by: Chao Li <lichao@loongson.cn> * app: Add LoongArch version support Reduced the version field by two characters to support ARCH name abbreviations with more than three characters, and added "la64" ARCH version display. Singed-off-by: Chao Li <lichao@loongson.cn> * test/block_move: Add block move test via ASM for LoongArch Add block move test inline assembly instance for LoongArch. Signed-off-by: Chao Li <lichao@loongson.cn> * test/mov_inv_fixed: Add LoongArch ASM version word write operation Add LoongArch ASM version word write cycle if it uses the HAND_OPTIMISED. Signed-off-by: Chao Li <lichao@loongson.cn> * boot: Adjust the AP stack size for LoongArch LoongArch exception will store all of the GP, FP and CSR on stack, it need more stack size, make LoongArch AP using 2KB stack size. Signed-off-by: Chao Li <lichao@loongson.cn> * boot/efisetup: Add LoongArch CPU halt instruction Add "idle 0" for LoongArch Signed-off-by: Chao Li <lichao@loongson.cn> * boot/efi: Limiting the ms_abi using scope Make the ms_abi only work on i386 and x64. Signed-off-by: Chao Li <lichao@loongson.cn> * system/imc/loongson: Add Loongson LoongArch IMC support Added the Loongson LoongArch CPU IMC instance, support read out the IMC sequence, currently only supports reading MC0. Signed-off-by: Chao Li <lichao@loongson.cn> * app/loongarch: Add intrrupt handler for LoongArch Added the LoongArch IRQ handler support. Signed-off-by: Chao Li <lichao@loongson.cn> * system/loongarch: Add LoongArch ARCH specific files Added LoongArch ARCH specific files: cpuid.c, cpuinfo.c, hwctrl.c, memctrl.c, temperature.c, vmem.c, registers.h They use the same pubilc API for i386 and x64 platforms. Signed-off-by: Chao Li <lichao@loongson.cn> * boot: Add LoongArch startup and header Added the header.S and startup64.S for LoongArch, CPU works on: 1. Page mode. 2. Load and store is cacheable. 3. Instructions is cacheable. 4. DMWn 0 and 1 is used. 5. To access non-cacheable areas, use the perfix 0x8000000000000000. Signed-off Chao Li <lichao@loongson.cn> * build64/la64: Add LoongArch64 build files Add infrastructure files to build memtest86 plus for LoongArch64 platform. Signed-off-by: Chao Li <lichao@loongson.cn> * workflows: Add LoongArch64 CI supports Adjust workflow logci, remvoe 32 and 64 wordsize, replace with "i386, x86_64 and la64", add LoongArch64 build CI check. Signed-off-by: Chao Li <lichao@loongson.cn> --------- Signed-off-by: Chao Li <lichao@loongson.cn> Signed-off-by: Chao Li <lichao@loognson.cn>
This commit is contained in:
parent
e38ad8e6a4
commit
97922cc4cf
37
.github/workflows/Linux.yml
vendored
37
.github/workflows/Linux.yml
vendored
@ -13,7 +13,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: ${{ matrix.os }} ${{ matrix.compiler }} ${{ matrix.wordsize }}
|
||||
name: ${{ matrix.os }} ${{ matrix.compiler }} ${{ matrix.arch }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
@ -21,7 +21,7 @@ jobs:
|
||||
matrix:
|
||||
compiler: [gcc]
|
||||
os: [ubuntu-20.04, ubuntu-22.04]
|
||||
wordsize: [32, 64]
|
||||
arch: [i386, x86_64, la64]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@ -32,11 +32,36 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install build-essential gcc-multilib clang libc6-dev-i386-cross dosfstools mtools xorriso -y
|
||||
if [ ${{ matrix.arch }} == 'la64' ]; then
|
||||
sudo mkdir /opt/LoongArch_Toolchains -p; cd /opt/LoongArch_Toolchains
|
||||
sudo wget https://github.com/YongbaoOS/Yongbao-Toolchains/releases/download/2024.8.6/x86_64-cross-tools-loongarch64-binutils_git60d4fed4e364-gcc_14.2.0.tar.xz
|
||||
sudo tar -xf x86_64-cross-tools-loongarch64-binutils_git60d4fed4e364-gcc_14.2.0.tar.xz
|
||||
sudo ln -s /opt/LoongArch_Toolchains/cross-tools/bin/loongarch64-unknown-linux-gnu-gcc /opt/LoongArch_Toolchains/cross-tools/bin/gcc
|
||||
sudo ln -s /opt/LoongArch_Toolchains/cross-tools/bin/loongarch64-unknown-linux-gnu-ld /opt/LoongArch_Toolchains/cross-tools/bin/ld
|
||||
sudo ln -s /opt/LoongArch_Toolchains/cross-tools/bin/loongarch64-unknown-linux-gnu-objcopy /opt/LoongArch_Toolchains/cross-tools/bin/objcopy
|
||||
fi
|
||||
|
||||
- name: Clean up
|
||||
working-directory: ./build${{matrix.wordsize}}
|
||||
run: make clean
|
||||
working-directory: ./
|
||||
run: |
|
||||
if [ ${{ matrix.arch }} == 'i386' ]; then
|
||||
cd build32
|
||||
elif [ ${{ matrix.arch }} == 'x86_64' ]; then
|
||||
cd build64
|
||||
elif [ ${{ matrix.arch }} == 'la64' ]; then
|
||||
cd build64/la64
|
||||
fi
|
||||
make clean
|
||||
|
||||
- name: Build
|
||||
working-directory: ./build${{matrix.wordsize}}
|
||||
run: make -j 2 CC="${{matrix.compiler}}" iso
|
||||
working-directory: ./
|
||||
run: |
|
||||
if [ ${{ matrix.arch }} == 'i386' ]; then
|
||||
cd build32
|
||||
elif [ ${{ matrix.arch }} == 'x86_64' ]; then
|
||||
cd build64
|
||||
elif [ ${{ matrix.arch }} == 'la64' ]; then
|
||||
export PATH=/opt/LoongArch_Toolchains/cross-tools/bin/:$PATH
|
||||
cd build64/la64
|
||||
fi
|
||||
make -j 2 CC="${{matrix.compiler}}" iso
|
||||
|
@ -144,11 +144,14 @@ void display_init(void)
|
||||
clear_screen_region(ROW_FOOTER, 0, ROW_FOOTER, SCREEN_WIDTH - 1);
|
||||
prints(ROW_FOOTER, 0, " <ESC> Exit <F1> Configuration <Space> Scroll Lock");
|
||||
prints(ROW_FOOTER, 64, MT_VERSION "." GIT_HASH);
|
||||
#if TESTWORD_WIDTH > 32
|
||||
prints(ROW_FOOTER, 76, ".x64");
|
||||
#else
|
||||
prints(ROW_FOOTER, 76, ".x32");
|
||||
#if defined (__x86_64__)
|
||||
prints(ROW_FOOTER, 74, ".x64");
|
||||
#elif defined (__i386__)
|
||||
prints(ROW_FOOTER, 74, ".x32");
|
||||
#elif defined (__loongarch_lp64)
|
||||
prints(ROW_FOOTER, 74, ".la64");
|
||||
#endif
|
||||
|
||||
set_foreground_colour(WHITE);
|
||||
set_background_colour(BLUE);
|
||||
|
||||
|
232
app/loongarch/interrupt.c
Normal file
232
app/loongarch/interrupt.c
Normal file
@ -0,0 +1,232 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2020-2022 Martin Whitaker.
|
||||
// Copyright (C) 2024 Loongson Technology Corporation Limited. All rights reserved.
|
||||
//
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#include <stdint.h>
|
||||
#include "hwctrl.h"
|
||||
#include "screen.h"
|
||||
#include "keyboard.h"
|
||||
#include "smp.h"
|
||||
#include "display.h"
|
||||
|
||||
#include <larchintrin.h>
|
||||
|
||||
#define INT_SIP0 0
|
||||
#define INT_SIP1 1
|
||||
#define INT_IP0 2
|
||||
#define INT_IP1 3
|
||||
#define INT_IP2 4
|
||||
#define INT_IP3 5
|
||||
#define INT_IP4 6
|
||||
#define INT_IP5 7
|
||||
#define INT_IP6 8
|
||||
#define INT_IP7 9
|
||||
#define INT_PMC 10
|
||||
#define INT_TIMER 11
|
||||
#define INT_IPI 12
|
||||
|
||||
|
||||
#define EXC_INT 0
|
||||
#define EXC_PIL 1
|
||||
#define EXC_PIS 2
|
||||
#define EXC_PIF 3
|
||||
#define EXC_PME 4
|
||||
#define EXC_PNR 5
|
||||
#define EXC_PNX 6
|
||||
#define EXC_PPI 7
|
||||
#define EXC_ADE 8
|
||||
#define EXC_ALE 9
|
||||
#define EXC_BCE 10
|
||||
#define EXC_SYS 11
|
||||
#define EXC_BRK 12
|
||||
#define EXC_INE 13
|
||||
#define EXC_IPE 14
|
||||
#define EXC_FPD 15
|
||||
#define EXC_SXD 16
|
||||
#define EXC_ASXD 17
|
||||
#define EXC_FPE 18
|
||||
|
||||
#define OP_IDLE 0x06488000
|
||||
#define OP_BGE 0x64000000
|
||||
|
||||
|
||||
#ifdef __loongarch_lp64
|
||||
typedef uint64_t reg_t;
|
||||
#define CSR_REG_DIGITS "16"
|
||||
#define GP_REG_DIGITS "16"
|
||||
#define ADR_DIGITS "12"
|
||||
#else
|
||||
typedef uint32_t reg_t;
|
||||
#define CSR_REG_DIGITS "8"
|
||||
#define GP_REG_DIGITS "8"
|
||||
#define ADR_DIGITS "8"
|
||||
#endif
|
||||
|
||||
static const char *exception_code[] = {
|
||||
"#INT - Interrupt(CSR.ECFG.VS=0)",
|
||||
"#PIL - Page invalid exception for Load option",
|
||||
"#PIS - Page invalid exception for Store operation",
|
||||
"#PIF - Page invalid exception for Fetch operation",
|
||||
"#PME - Page modification exception",
|
||||
"#PNR - Page non-readable exception",
|
||||
"#PNX - Page non-executable exception",
|
||||
"#PPI - Page privilege level illegal exception",
|
||||
"#ADE - Address error exception",
|
||||
"#ALE - Address alignment fault exception",
|
||||
"#BCE - Bound check exception",
|
||||
"#SYS - System call exception",
|
||||
"#BRK - Breakpoint exception",
|
||||
"#INE - Instruction non-defined exception",
|
||||
"#IPE - Instruction privilege error exception",
|
||||
"#FPD - Floating-point instruction disable exception",
|
||||
"#SXD - 128-bit vector (SIMD instructions) expansion instruction disable exception",
|
||||
"#ASXD - 256-bit vector (Advanced SIMD instructions) expansion instruction disable exception",
|
||||
"#FPE - Floating-Point error exception",
|
||||
"#TBR - TLB refill exception"
|
||||
};
|
||||
|
||||
|
||||
struct system_context {
|
||||
//
|
||||
// GP
|
||||
//
|
||||
reg_t r0;
|
||||
reg_t r1;
|
||||
reg_t r2;
|
||||
reg_t r3;
|
||||
reg_t r4;
|
||||
reg_t r5;
|
||||
reg_t r6;
|
||||
reg_t r7;
|
||||
reg_t r8;
|
||||
reg_t r9;
|
||||
reg_t r10;
|
||||
reg_t r11;
|
||||
reg_t r12;
|
||||
reg_t r13;
|
||||
reg_t r14;
|
||||
reg_t r15;
|
||||
reg_t r16;
|
||||
reg_t r17;
|
||||
reg_t r18;
|
||||
reg_t r19;
|
||||
reg_t r20;
|
||||
reg_t r21;
|
||||
reg_t r22;
|
||||
reg_t r23;
|
||||
reg_t r24;
|
||||
reg_t r25;
|
||||
reg_t r26;
|
||||
reg_t r27;
|
||||
reg_t r28;
|
||||
reg_t r29;
|
||||
reg_t r30;
|
||||
reg_t r31;
|
||||
|
||||
//
|
||||
// CSR
|
||||
//
|
||||
reg_t crmd;
|
||||
reg_t prmd;
|
||||
reg_t euen;
|
||||
reg_t misc;
|
||||
reg_t ecfg;
|
||||
reg_t estat;
|
||||
reg_t era;
|
||||
reg_t badv;
|
||||
reg_t badi;
|
||||
};
|
||||
|
||||
void interrupt(struct system_context *system_context)
|
||||
{
|
||||
uint8_t ecode;
|
||||
|
||||
if (system_context->estat & (1 << INT_IPI)) {
|
||||
uint32_t *pc = (uint32_t *)system_context->era;
|
||||
|
||||
//
|
||||
// Clean the mailbox 0 and 3
|
||||
//
|
||||
__iocsrwr_d(0x0, 0x1020);
|
||||
__iocsrwr_d(0x0, 0x1038);
|
||||
|
||||
//
|
||||
// Clean IPI
|
||||
//
|
||||
__iocsrwr_w(__iocsrrd_w(0x1000), 0x100c);
|
||||
__dbar(0);
|
||||
|
||||
if ((pc[-1] & ~0x7FFF) == OP_IDLE) {
|
||||
// Assume this is a barrier wakeup signal sent via IPI.
|
||||
system_context->era += 4;
|
||||
return;
|
||||
}
|
||||
|
||||
// Catch the rare case that a core will fail to reach the IDLE instruction before
|
||||
// its wakeup signal arrives. The barrier code contains an atomic decrement, a BLT
|
||||
// instruction, and a IDLE instruction. The atomic decrement must have completed if
|
||||
// another core has reached the point of sending the wakeup signals, so we should
|
||||
// find the IDLE opcode either at pc[0] or at pc[1]. If we find it, adjust the ERA
|
||||
// to point to the following instruction.
|
||||
if ((pc[0] & ~0x7FFF) == OP_IDLE) {
|
||||
system_context->era += 8;
|
||||
return;
|
||||
}
|
||||
if ((pc[0] & ~0x3FFFFFF) == OP_BGE && (pc[1] & ~0x7FFF) == OP_IDLE) {
|
||||
system_context->era += 12;
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ecode = (system_context->estat >> 16) & 0x3F;
|
||||
|
||||
spin_lock(error_mutex);
|
||||
|
||||
clear_message_area();
|
||||
|
||||
display_pinned_message(0, 0, "Unexpected interrupt on CPU %i", smp_my_cpu_num());
|
||||
if (__csrrd_w(0x8A) & 0x1) {
|
||||
display_pinned_message(2, 0, "Type: %s", exception_code[19]);
|
||||
} else if (ecode < 19) {
|
||||
display_pinned_message(2, 0, "Type: %s", exception_code[ecode]);
|
||||
} else {
|
||||
display_pinned_message(2, 0, "Type: %i", ecode);
|
||||
}
|
||||
display_pinned_message(3, 0, " BADV: %0" CSR_REG_DIGITS "x", (uintptr_t)system_context->badv);
|
||||
display_pinned_message(4, 0, " BADI: %0" CSR_REG_DIGITS "x", (uintptr_t)system_context->badi);
|
||||
display_pinned_message(5, 0, " ERA: %0" CSR_REG_DIGITS "x", (uintptr_t)system_context->era);
|
||||
display_pinned_message(6, 0, " EUEN: %0" CSR_REG_DIGITS "x", (uintptr_t)system_context->euen);
|
||||
display_pinned_message(7, 0, " ECFG: %0" CSR_REG_DIGITS "x", (uintptr_t)system_context->ecfg);
|
||||
display_pinned_message(8, 0, "ESTAT: %0" CSR_REG_DIGITS "x", (uintptr_t)system_context->estat);
|
||||
|
||||
display_pinned_message(3, 25, "RA: %0" GP_REG_DIGITS "x", (uintptr_t)system_context->r1);
|
||||
display_pinned_message(4, 25, "SP: %0" GP_REG_DIGITS "x", (uintptr_t)system_context->r3);
|
||||
display_pinned_message(5, 25, "A0: %0" GP_REG_DIGITS "x", (uintptr_t)system_context->r4);
|
||||
display_pinned_message(6, 25, "A1: %0" GP_REG_DIGITS "x", (uintptr_t)system_context->r5);
|
||||
display_pinned_message(7, 25, "A2: %0" GP_REG_DIGITS "x", (uintptr_t)system_context->r6);
|
||||
display_pinned_message(8, 25, "A3: %0" GP_REG_DIGITS "x", (uintptr_t)system_context->r7);
|
||||
display_pinned_message(9, 25, "A4: %0" GP_REG_DIGITS "x", (uintptr_t)system_context->r8);
|
||||
display_pinned_message(10, 25, "A5: %0" GP_REG_DIGITS "x", (uintptr_t)system_context->r9);
|
||||
display_pinned_message(11, 25, "T0: %0" GP_REG_DIGITS "x", (uintptr_t)system_context->r12);
|
||||
display_pinned_message(12, 25, "T1: %0" GP_REG_DIGITS "x", (uintptr_t)system_context->r13);
|
||||
display_pinned_message(13, 25, "T2: %0" GP_REG_DIGITS "x", (uintptr_t)system_context->r14);
|
||||
|
||||
display_pinned_message(0, 50, "Stack:");
|
||||
for (int i = 0; i < 12; i++) {
|
||||
uintptr_t addr = system_context->r3 + sizeof(reg_t)*(11 - i);
|
||||
reg_t data = *(reg_t *)addr;
|
||||
display_pinned_message(1 + i, 50, "%0" ADR_DIGITS "x %0" GP_REG_DIGITS "x", addr, (uintptr_t)data);
|
||||
}
|
||||
|
||||
clear_screen_region(ROW_FOOTER, 0, ROW_FOOTER, SCREEN_WIDTH - 1);
|
||||
prints(ROW_FOOTER, 0, "Press any key to reboot...");
|
||||
|
||||
while (get_key() == 0) { }
|
||||
reboot();
|
||||
}
|
@ -20,7 +20,11 @@
|
||||
#define MAX_APS 255 /* Maximum number of active APs */
|
||||
|
||||
#define BSP_STACK_SIZE 16384 /* Stack size for the BSP */
|
||||
#ifdef __loongarch_lp64
|
||||
#define AP_STACK_SIZE 2048 /* Stack size for each AP */
|
||||
#else
|
||||
#define AP_STACK_SIZE 1024 /* Stack size for each AP */
|
||||
#endif
|
||||
|
||||
#define STACKS_SIZE (BSP_STACK_SIZE + MAX_APS * AP_STACK_SIZE)
|
||||
|
||||
|
@ -69,7 +69,11 @@
|
||||
#define EFI_SYSTEM_TABLE_SIGNATURE UINT64_C(0x5453595320494249)
|
||||
#define EFI_RUNTIME_SERVICES_SIGNATURE UINT64_C(0x5652453544e5552)
|
||||
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
#define efiapi __attribute__((ms_abi))
|
||||
#else
|
||||
#define efiapi
|
||||
#endif
|
||||
|
||||
#if (ARCH_BITS == 64)
|
||||
typedef uint64_t uintn_t;
|
||||
|
@ -798,6 +798,10 @@ fail:
|
||||
print_string("efi_setup() failed\n");
|
||||
|
||||
while (1) {
|
||||
#if defined(__x86_64) || defined(__i386__)
|
||||
__asm__("hlt");
|
||||
#elif defined(__loongarch_lp64)
|
||||
__asm__("idle 0");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
155
boot/loongarch/header.S
Normal file
155
boot/loongarch/header.S
Normal file
@ -0,0 +1,155 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// header64.S supports booting directly from a LoongArcht64 UEFI BIOS or via
|
||||
// an intermediate bootloader that supports the Linux boot protocol. When booted
|
||||
// directly from the BIOS, it provides the MS-DOS & PE/COFF headers.
|
||||
//
|
||||
// Copyright (C) 2024 Loongson Technology Corporation Limited. All rights reserved.
|
||||
//
|
||||
|
||||
#define __ASSEMBLY__
|
||||
|
||||
#include "boot.h"
|
||||
#include "peimage.h"
|
||||
|
||||
# The EFI loader loads the header at ImageBase, so we have to locate the main program
|
||||
# after that. This means we can't load the main program at HIGH_LOAD_ADDR. Pick a load
|
||||
# address well away from HIGH_LOAD_ADDR, to avoid overlap when relocating the code.
|
||||
|
||||
#define IMAGE_BASE 0x200000
|
||||
|
||||
.section ".header", "ax", @progbits
|
||||
|
||||
.globl head
|
||||
head:
|
||||
# "MZ", the MS-DOS header signature.
|
||||
.byte 0x4d
|
||||
.byte 0x5a
|
||||
|
||||
# The PE header pointer.
|
||||
.org 0x3c
|
||||
.long pe_header
|
||||
|
||||
pe_header:
|
||||
.ascii "PE"
|
||||
.short 0
|
||||
|
||||
coff_header:
|
||||
.short IMAGE_FILE_MACHINE_LOONGARCH64 # Machine (LoongArch64)
|
||||
.short 3 # NumberOfSections
|
||||
.long 0 # TimeDateStamp
|
||||
.long 0 # PointerToSymbolTable
|
||||
.long 0 # NumberOfSymbols
|
||||
.short section_table - optional_header # SizeOfOptionalHeader
|
||||
.short IMAGE_FILE_DEBUG_STRIPPED \
|
||||
| IMAGE_FILE_LOCAL_SYMS_STRIPPED \
|
||||
| IMAGE_FILE_LINE_NUMS_STRIPPED \
|
||||
| IMAGE_FILE_EXECUTABLE_IMAGE # Characteristics
|
||||
|
||||
optional_header:
|
||||
.short IMAGE_NT_OPTIONAL_HDR64_MAGIC # PE32+ format
|
||||
.byte 0x02 # MajorLinkerVersion
|
||||
.byte 0x14 # MinorLinkerVersion
|
||||
|
||||
.long _virt_text_size # SizeOfCode
|
||||
.long _virt_sbat_size # SizeOfInitializedData
|
||||
.long 0 # SizeOfUninitializedData
|
||||
|
||||
.long _virt_text_start + 0x400 # AddressOfEntryPoint
|
||||
|
||||
.long _virt_text_start # BaseOfCode
|
||||
|
||||
extra_header_fields:
|
||||
.quad IMAGE_BASE # ImageBase
|
||||
.long 4096 # SectionAlignment
|
||||
.long 512 # FileAlignment
|
||||
.short 0 # MajorOperatingSystemVersion
|
||||
.short 0 # MinorOperatingSystemVersion
|
||||
.short 0 # MajorImageVersion
|
||||
.short 0 # MinorImageVersion
|
||||
.short 0 # MajorSubsystemVersion
|
||||
.short 0 # MinorSubsystemVersion
|
||||
.long 0 # Win32VersionValue
|
||||
|
||||
.long _virt_img_size # SizeOfImage
|
||||
.long _file_head_size # SizeOfHeaders
|
||||
.long 0 # CheckSum
|
||||
.short 10 # Subsystem (EFI application)
|
||||
.short 0 # DllCharacteristics
|
||||
.quad 0 # SizeOfStackReserve
|
||||
.quad 0 # SizeOfStackCommit
|
||||
.quad 0 # SizeOfHeapReserve
|
||||
.quad 0 # SizeOfHeapCommit
|
||||
.long 0 # LoaderFlags
|
||||
.long IMAGE_DIRECTORY_ENTRY_DEBUG # NumberOfRvaAndSizes
|
||||
|
||||
.long 0 # DataDirectory.Export.VirtualAddress
|
||||
.long 0 # DataDirectory.Export.Size
|
||||
.long 0 # DataDirectory.Import.VirtualAddress
|
||||
.long 0 # DataDirectory.Import.Size
|
||||
.long 0 # DataDirectory.Resource.VirtualAddress
|
||||
.long 0 # DataDirectory.Resource.Size
|
||||
.long 0 # DataDirectory.Exception.VirtualAddress
|
||||
.long 0 # DataDirectory.Exception.Size
|
||||
.long 0 # DataDirectory.Certs.VirtualAddress
|
||||
.long 0 # DataDirectory.Certs.Size
|
||||
.long _virt_reloc_start # DataDirectory.BaseReloc.VirtualAddress
|
||||
.long _real_reloc_size # DataDirectory.BaseReloc.Size
|
||||
|
||||
# Section table
|
||||
section_table:
|
||||
.ascii ".text"
|
||||
.byte 0
|
||||
.byte 0
|
||||
.byte 0
|
||||
.long _virt_text_size # VirtualSize
|
||||
.long _virt_text_start # VirtualAddress
|
||||
.long _file_text_size # SizeOfRawData
|
||||
.long _file_text_start # PointerToRawData
|
||||
.long 0 # PointerToRelocations
|
||||
.long 0 # PointerToLineNumbers
|
||||
.short 0 # NumberOfRelocations
|
||||
.short 0 # NumberOfLineNumbers
|
||||
.long IMAGE_SCN_MEM_READ \
|
||||
| IMAGE_SCN_MEM_WRITE \
|
||||
| IMAGE_SCN_MEM_EXECUTE \
|
||||
| IMAGE_SCN_CNT_CODE # Characteristics (section flags)
|
||||
|
||||
.ascii ".reloc"
|
||||
.byte 0
|
||||
.byte 0
|
||||
.long _virt_reloc_size # VirtualSize
|
||||
.long _virt_reloc_start # VirtualAddress
|
||||
.long _file_reloc_size # SizeOfRawData
|
||||
.long _file_reloc_start # PointerToRawData
|
||||
.long 0 # PointerToRelocations
|
||||
.long 0 # PointerToLineNumbers
|
||||
.short 0 # NumberOfRelocations
|
||||
.short 0 # NumberOfLineNumbers
|
||||
.long IMAGE_SCN_MEM_READ \
|
||||
| IMAGE_SCN_CNT_INITIALIZED_DATA # Characteristics (section flags)
|
||||
|
||||
.ascii ".sbat"
|
||||
.byte 0
|
||||
.byte 0
|
||||
.byte 0
|
||||
.long _virt_sbat_size # VirtualSize
|
||||
.long _virt_sbat_start # VirtualAddress
|
||||
.long _file_sbat_size # SizeOfRawData
|
||||
.long _file_sbat_start # PointerToRawData
|
||||
.long 0 # PointerToRelocations
|
||||
.long 0 # PointerToLineNumbers
|
||||
.short 0 # NumberOfRelocations
|
||||
.short 0 # NumberOfLineNumbers
|
||||
.long IMAGE_SCN_MEM_READ \
|
||||
| IMAGE_SCN_CNT_INITIALIZED_DATA # Characteristics (section flags)
|
||||
|
||||
.org 512
|
||||
|
||||
.section ".reloc"
|
||||
.long 0 // Page RVA
|
||||
.long 10 // Block Size (2*4+2)
|
||||
.short (IMAGE_REL_BASED_ABSOLUTE << 12) + 0 // reloc 0 -> 0
|
||||
|
||||
.section ".sbat", "a", @progbits
|
||||
.incbin "../boot/sbat.csv"
|
451
boot/loongarch/startup64.S
Normal file
451
boot/loongarch/startup64.S
Normal file
@ -0,0 +1,451 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// startup64.S contains the 64-bit startup code for both the BSP and APs.
|
||||
// It initialises stacks, memory management, and exception handling, clears
|
||||
// the BSS, completes relocation, and finally calls the main application.
|
||||
//
|
||||
// Copyright (C) 2020-2022 Martin Whitaker.
|
||||
// Copyright (C) 2024 Loongson Technology Corporation Limited. All rights reserved.
|
||||
//
|
||||
|
||||
#define __ASSEMBLY__
|
||||
|
||||
#include "boot.h"
|
||||
#inlucde "interrupt.h"
|
||||
#include "registers.h"
|
||||
|
||||
.text
|
||||
|
||||
exception_entry:
|
||||
csrwr $sp, LOONGARCH_CSR_KS0
|
||||
|
||||
csrrd $sp, LOONGARCH_CSR_KS0
|
||||
|
||||
//
|
||||
// Push GP registers
|
||||
//
|
||||
addi.d $sp, $sp, -(GP_REG_CONTEXT_SIZE + FP_REG_CONTEXT_SIZE + CSR_REG_CONTEXT_SIZE)
|
||||
st.d $zero, $sp, 0 * RSIZE
|
||||
st.d $ra, $sp, 1 * RSIZE
|
||||
st.d $tp, $sp, 2 * RSIZE
|
||||
st.d $a0, $sp, 4 * RSIZE
|
||||
st.d $a1, $sp, 5 * RSIZE
|
||||
st.d $a2, $sp, 6 * RSIZE
|
||||
st.d $a3, $sp, 7 * RSIZE
|
||||
st.d $a4, $sp, 8 * RSIZE
|
||||
st.d $a5, $sp, 9 * RSIZE
|
||||
st.d $a6, $sp, 10 * RSIZE
|
||||
st.d $a7, $sp, 11 * RSIZE
|
||||
st.d $t0, $sp, 12 * RSIZE
|
||||
st.d $t1, $sp, 13 * RSIZE
|
||||
st.d $t2, $sp, 14 * RSIZE
|
||||
st.d $t3, $sp, 15 * RSIZE
|
||||
st.d $t4, $sp, 16 * RSIZE
|
||||
st.d $t5, $sp, 17 * RSIZE
|
||||
st.d $t6, $sp, 18 * RSIZE
|
||||
st.d $t7, $sp, 19 * RSIZE
|
||||
st.d $t8, $sp, 20 * RSIZE
|
||||
st.d $r21, $sp, 21 * RSIZE
|
||||
st.d $fp, $sp, 22 * RSIZE
|
||||
st.d $s0, $sp, 23 * RSIZE
|
||||
st.d $s1, $sp, 24 * RSIZE
|
||||
st.d $s2, $sp, 25 * RSIZE
|
||||
st.d $s3, $sp, 26 * RSIZE
|
||||
st.d $s4, $sp, 27 * RSIZE
|
||||
st.d $s5, $sp, 28 * RSIZE
|
||||
st.d $s6, $sp, 29 * RSIZE
|
||||
st.d $s7, $sp, 30 * RSIZE
|
||||
st.d $s8, $sp, 31 * RSIZE
|
||||
csrrd $t0, LOONGARCH_CSR_KS0 // Read the old stack pointer.
|
||||
st.d $t0, $sp, 3 * RSIZE
|
||||
|
||||
//
|
||||
// Push CSR registers
|
||||
//
|
||||
addi.d $sp, $sp, GP_REG_CONTEXT_SIZE
|
||||
|
||||
csrrd $t0, LOONGARCH_CSR_CRMD
|
||||
st.d $t0, $sp, LOONGARCH_CSR_CRMD * RSIZE
|
||||
csrrd $t0, LOONGARCH_CSR_PRMD
|
||||
st.d $t0, $sp, LOONGARCH_CSR_PRMD * RSIZE
|
||||
csrrd $t0, LOONGARCH_CSR_EUEN
|
||||
st.d $t0, $sp, LOONGARCH_CSR_EUEN * RSIZE
|
||||
csrrd $t0, LOONGARCH_CSR_MISC
|
||||
st.d $t0, $sp, LOONGARCH_CSR_MISC * RSIZE
|
||||
csrrd $t0, LOONGARCH_CSR_ECFG
|
||||
st.d $t0, $sp, LOONGARCH_CSR_ECFG * RSIZE
|
||||
csrrd $t0, LOONGARCH_CSR_ESTAT
|
||||
st.d $t0, $sp, LOONGARCH_CSR_ESTAT * RSIZE
|
||||
csrrd $t0, LOONGARCH_CSR_ERA
|
||||
st.d $t0, $sp, LOONGARCH_CSR_ERA * RSIZE
|
||||
csrrd $t0, LOONGARCH_CSR_BADV
|
||||
st.d $t0, $sp, LOONGARCH_CSR_BADV * RSIZE
|
||||
csrrd $t0, LOONGARCH_CSR_BADI
|
||||
st.d $t0, $sp, LOONGARCH_CSR_BADI * RSIZE
|
||||
|
||||
//
|
||||
// Push FP registers
|
||||
//
|
||||
addi.d $sp, $sp, CSR_REG_CONTEXT_SIZE
|
||||
|
||||
csrrd $t0, LOONGARCH_CSR_EUEN
|
||||
andi $t0, $t0, 0x1
|
||||
beqz $t0, PushRegDone
|
||||
|
||||
fst.d $fa0, $sp, 0 * RSIZE
|
||||
fst.d $fa1, $sp, 1 * RSIZE
|
||||
fst.d $fa2, $sp, 2 * RSIZE
|
||||
fst.d $fa3, $sp, 3 * RSIZE
|
||||
fst.d $fa4, $sp, 4 * RSIZE
|
||||
fst.d $fa5, $sp, 5 * RSIZE
|
||||
fst.d $fa6, $sp, 6 * RSIZE
|
||||
fst.d $fa7, $sp, 7 * RSIZE
|
||||
fst.d $ft0, $sp, 8 * RSIZE
|
||||
fst.d $ft1, $sp, 9 * RSIZE
|
||||
fst.d $ft2, $sp, 10 * RSIZE
|
||||
fst.d $ft3, $sp, 11 * RSIZE
|
||||
fst.d $ft4, $sp, 12 * RSIZE
|
||||
fst.d $ft5, $sp, 13 * RSIZE
|
||||
fst.d $ft6, $sp, 14 * RSIZE
|
||||
fst.d $ft7, $sp, 15 * RSIZE
|
||||
fst.d $ft8, $sp, 16 * RSIZE
|
||||
fst.d $ft9, $sp, 17 * RSIZE
|
||||
fst.d $ft10, $sp, 18 * RSIZE
|
||||
fst.d $ft11, $sp, 19 * RSIZE
|
||||
fst.d $ft12, $sp, 20 * RSIZE
|
||||
fst.d $ft13, $sp, 21 * RSIZE
|
||||
fst.d $ft14, $sp, 22 * RSIZE
|
||||
fst.d $ft15, $sp, 23 * RSIZE
|
||||
fst.d $fs0, $sp, 24 * RSIZE
|
||||
fst.d $fs1, $sp, 25 * RSIZE
|
||||
fst.d $fs2, $sp, 26 * RSIZE
|
||||
fst.d $fs3, $sp, 27 * RSIZE
|
||||
fst.d $fs4, $sp, 28 * RSIZE
|
||||
fst.d $fs5, $sp, 29 * RSIZE
|
||||
fst.d $fs6, $sp, 30 * RSIZE
|
||||
fst.d $fs7, $sp, 31 * RSIZE
|
||||
|
||||
movfcsr2gr $t3, $fcsr0
|
||||
st.d $t3, $sp, 32 * RSIZE // Push the FCSR0 register.
|
||||
|
||||
//
|
||||
// Push the fcc0-fcc7 registers.
|
||||
//
|
||||
movcf2gr $t3, $fcc0
|
||||
move $t2, $t3
|
||||
movcf2gr $t3, $fcc1
|
||||
bstrins.d $t2, $t3, 0xf, 0x8
|
||||
movcf2gr $t3, $fcc2
|
||||
bstrins.d $t2, $t3, 0x17, 0x10
|
||||
movcf2gr $t3, $fcc3
|
||||
bstrins.d $t2, $t3, 0x1f, 0x18
|
||||
movcf2gr $t3, $fcc4
|
||||
bstrins.d $t2, $t3, 0x27, 0x20
|
||||
movcf2gr $t3, $fcc5
|
||||
bstrins.d $t2, $t3, 0x2f, 0x28
|
||||
movcf2gr $t3, $fcc6
|
||||
bstrins.d $t2, $t3, 0x37, 0x30
|
||||
movcf2gr $t3, $fcc7
|
||||
bstrins.d $t2, $t3, 0x3f, 0x38
|
||||
st.d $t2, $sp, 33 * RSIZE
|
||||
//
|
||||
// Push exception context down
|
||||
//
|
||||
|
||||
PushRegDone:
|
||||
addi.d $sp, $sp, -(GP_REG_CONTEXT_SIZE + CSR_REG_CONTEXT_SIZE)
|
||||
move $a0, $sp
|
||||
bl interrupt
|
||||
|
||||
# If returned, POP the REG
|
||||
|
||||
//
|
||||
// Pop CSR reigsters
|
||||
//
|
||||
addi.d $sp, $sp, GP_REG_CONTEXT_SIZE
|
||||
|
||||
ld.d $t0, $sp, LOONGARCH_CSR_CRMD * RSIZE
|
||||
csrwr $t0, LOONGARCH_CSR_CRMD
|
||||
ld.d $t0, $sp, LOONGARCH_CSR_PRMD * RSIZE
|
||||
csrwr $t0, LOONGARCH_CSR_PRMD
|
||||
ld.d $t0, $sp, LOONGARCH_CSR_ECFG * RSIZE
|
||||
csrwr $t0, LOONGARCH_CSR_ECFG
|
||||
ld.d $t0, $sp, LOONGARCH_CSR_ERA * RSIZE
|
||||
csrwr $t0, LOONGARCH_CSR_ERA
|
||||
|
||||
addi.d $sp, $sp, CSR_REG_CONTEXT_SIZE // Fource change the stack pointer befor pop the FP registers.
|
||||
|
||||
csrrd $t1, LOONGARCH_CSR_EUEN
|
||||
andi $t1, $t1, 0x1
|
||||
beqz $t1, PopGP // If the FPE not set, only pop the GP registers.
|
||||
|
||||
//
|
||||
// Pop FP registers
|
||||
//
|
||||
fld.d $fa0, $sp, 0 * RSIZE
|
||||
fld.d $fa1, $sp, 1 * RSIZE
|
||||
fld.d $fa2, $sp, 2 * RSIZE
|
||||
fld.d $fa3, $sp, 3 * RSIZE
|
||||
fld.d $fa4, $sp, 4 * RSIZE
|
||||
fld.d $fa5, $sp, 5 * RSIZE
|
||||
fld.d $fa6, $sp, 6 * RSIZE
|
||||
fld.d $fa7, $sp, 7 * RSIZE
|
||||
fld.d $ft0, $sp, 8 * RSIZE
|
||||
fld.d $ft1, $sp, 9 * RSIZE
|
||||
fld.d $ft2, $sp, 10 * RSIZE
|
||||
fld.d $ft3, $sp, 11 * RSIZE
|
||||
fld.d $ft4, $sp, 12 * RSIZE
|
||||
fld.d $ft5, $sp, 13 * RSIZE
|
||||
fld.d $ft6, $sp, 14 * RSIZE
|
||||
fld.d $ft7, $sp, 15 * RSIZE
|
||||
fld.d $ft8, $sp, 16 * RSIZE
|
||||
fld.d $ft9, $sp, 17 * RSIZE
|
||||
fld.d $ft10, $sp, 18 * RSIZE
|
||||
fld.d $ft11, $sp, 19 * RSIZE
|
||||
fld.d $ft12, $sp, 20 * RSIZE
|
||||
fld.d $ft13, $sp, 21 * RSIZE
|
||||
fld.d $ft14, $sp, 22 * RSIZE
|
||||
fld.d $ft15, $sp, 23 * RSIZE
|
||||
fld.d $fs0, $sp, 24 * RSIZE
|
||||
fld.d $fs1, $sp, 25 * RSIZE
|
||||
fld.d $fs2, $sp, 26 * RSIZE
|
||||
fld.d $fs3, $sp, 27 * RSIZE
|
||||
fld.d $fs4, $sp, 28 * RSIZE
|
||||
fld.d $fs5, $sp, 29 * RSIZE
|
||||
fld.d $fs6, $sp, 30 * RSIZE
|
||||
fld.d $fs7, $sp, 31 * RSIZE
|
||||
|
||||
ld.d $t0, $sp, 32 * RSIZE
|
||||
movgr2fcsr $fcsr0, $t0 // Pop the fcsr0 register.
|
||||
|
||||
//
|
||||
// Pop the fcc0-fcc7 registers.
|
||||
//
|
||||
ld.d $t0, $sp, 33 * RSIZE
|
||||
bstrpick.d $t1, $t0, 7, 0
|
||||
movgr2cf $fcc0, $t1
|
||||
bstrpick.d $t1, $t0, 15, 8
|
||||
movgr2cf $fcc1, $t1
|
||||
bstrpick.d $t1, $t0, 23, 16
|
||||
movgr2cf $fcc2, $t1
|
||||
bstrpick.d $t1, $t0, 31, 24
|
||||
movgr2cf $fcc3, $t1
|
||||
bstrpick.d $t1, $t0, 39, 32
|
||||
movgr2cf $fcc4, $t1
|
||||
bstrpick.d $t1, $t0, 47, 40
|
||||
movgr2cf $fcc5, $t1
|
||||
bstrpick.d $t1, $t0, 55, 48
|
||||
movgr2cf $fcc6, $t1
|
||||
bstrpick.d $t1, $t0, 63, 56
|
||||
movgr2cf $fcc7, $t1
|
||||
|
||||
PopGP:
|
||||
|
||||
//
|
||||
// Pop GP registers
|
||||
//
|
||||
addi.d $sp, $sp, -(GP_REG_CONTEXT_SIZE + CSR_REG_CONTEXT_SIZE)
|
||||
|
||||
ld.d $ra, $sp, 1 * RSIZE
|
||||
ld.d $tp, $sp, 2 * RSIZE
|
||||
ld.d $a0, $sp, 4 * RSIZE
|
||||
ld.d $a1, $sp, 5 * RSIZE
|
||||
ld.d $a2, $sp, 6 * RSIZE
|
||||
ld.d $a3, $sp, 7 * RSIZE
|
||||
ld.d $a4, $sp, 8 * RSIZE
|
||||
ld.d $a5, $sp, 9 * RSIZE
|
||||
ld.d $a6, $sp, 10 * RSIZE
|
||||
ld.d $a7, $sp, 11 * RSIZE
|
||||
ld.d $t0, $sp, 12 * RSIZE
|
||||
ld.d $t1, $sp, 13 * RSIZE
|
||||
ld.d $t2, $sp, 14 * RSIZE
|
||||
ld.d $t3, $sp, 15 * RSIZE
|
||||
ld.d $t4, $sp, 16 * RSIZE
|
||||
ld.d $t5, $sp, 17 * RSIZE
|
||||
ld.d $t6, $sp, 18 * RSIZE
|
||||
ld.d $t7, $sp, 19 * RSIZE
|
||||
ld.d $t8, $sp, 20 * RSIZE
|
||||
ld.d $r21, $sp, 21 * RSIZE
|
||||
ld.d $fp, $sp, 22 * RSIZE
|
||||
ld.d $s0, $sp, 23 * RSIZE
|
||||
ld.d $s1, $sp, 24 * RSIZE
|
||||
ld.d $s2, $sp, 25 * RSIZE
|
||||
ld.d $s3, $sp, 26 * RSIZE
|
||||
ld.d $s4, $sp, 27 * RSIZE
|
||||
ld.d $s5, $sp, 28 * RSIZE
|
||||
ld.d $s6, $sp, 29 * RSIZE
|
||||
ld.d $s7, $sp, 30 * RSIZE
|
||||
ld.d $s8, $sp, 31 * RSIZE
|
||||
ld.d $sp, $sp, 3 * RSIZE
|
||||
|
||||
ertn // Return from exception.
|
||||
|
||||
.globl startup32
|
||||
|
||||
startup32:
|
||||
break 0 # Should be unreachable.
|
||||
|
||||
# The EFI PE32+ boot entry point. The entry point for MP boot.
|
||||
|
||||
.org 0x400
|
||||
.globl efi_boot
|
||||
efi_boot:
|
||||
move $a2, $zero # the boot params pointer (0 = not yet allocated)
|
||||
|
||||
bl efi_setup
|
||||
|
||||
# Save the boot params pointer.
|
||||
la.pcrel $t0, boot_params_addr
|
||||
st.d $a0, $t0, 0x0
|
||||
b startup64
|
||||
|
||||
# The 64-bit boot entry point and the entry point for AP boot.
|
||||
|
||||
.org 0x440
|
||||
.globl startup64
|
||||
startup64:
|
||||
# Disable intrrupts globally
|
||||
li.w $t0, (0x1 << 2)
|
||||
csrxchg $zero, $t0, LOONGARCH_CSR_CRMD
|
||||
|
||||
# Init Core
|
||||
li.w $t0, 0xA8 # DA mode, load/store cacheable, instructions cacheable, interrupts disabled, DMWn has no effect.
|
||||
csrwr $t0, LOONGARCH_CSR_CRMD
|
||||
|
||||
//
|
||||
// Make sure the legacy boot mode works properly.
|
||||
//
|
||||
la.pcrel $t0, Jmp
|
||||
bstrins.d $t0, $zero, 63, 32
|
||||
jr $t0
|
||||
|
||||
Jmp:
|
||||
invtlb 0x0, $zero, $zero
|
||||
|
||||
# Set the PG and DMWn
|
||||
li.d $t0, 0x8000000000000001
|
||||
csrwr $t0, LOONGARCH_CSR_DMWIN0 # Uncache able address window, MMIO base: 0x8000000000000000
|
||||
li.d $t0, 0x11 # Cache able address window, PA == VA
|
||||
csrwr $t0, LOONGARCH_CSR_DMWIN1
|
||||
|
||||
li.w $t0, 0xB0 # PG mode, load/store cacheable, instructions cacheable, disable intrrupts, DMWn effected.
|
||||
csrwr $t0, LOONGARCH_CSR_CRMD
|
||||
|
||||
//
|
||||
// Set PMCNT0.
|
||||
//
|
||||
li.w $t0, (0x1 << 16)
|
||||
li.w $t1, ((0x1 << 16) | 0x3FF)
|
||||
csrxchg $t0, $t1, LOONGARCH_CSR_PERFCTRL0
|
||||
csrwr $zero, LOONGARCH_CSR_PERFCNTR0
|
||||
|
||||
# Enable all IPIs
|
||||
li.w $t0, 0xFFFFFFFF
|
||||
li.w $t1, 0x1004
|
||||
iocsrwr.w $t0, $t1
|
||||
|
||||
# Turn on FPE to enable FPU.
|
||||
li.w $t0, (0x1 << 0)
|
||||
csrxchg $t0, $t0, LOONGARCH_CSR_EUEN
|
||||
|
||||
b startup
|
||||
|
||||
# The 64-bit main entry point and for restart after relocation.
|
||||
|
||||
.org 0x500
|
||||
.globl startup
|
||||
startup:
|
||||
# Disable intrrupts globally
|
||||
li.w $t0, (0x1 << 2)
|
||||
csrxchg $zero, $t0, LOONGARCH_CSR_CRMD
|
||||
|
||||
# Some of the startup actions are not thread safe. Use a mutex
|
||||
# to protect this section of code.
|
||||
la.pcrel $t0, startup_mutex
|
||||
li.w $t1, 0x1
|
||||
Locked:
|
||||
amswap_db.w $t2, $t1, $t0
|
||||
bnez $t2, Locked
|
||||
|
||||
Critical:
|
||||
# Set cores stack
|
||||
csrrd $t0, LOONGARCH_CSR_CPUID
|
||||
li.d $t1, AP_STACK_SIZE
|
||||
mul.d $t0, $t0, $t1
|
||||
li.d $t1, BSP_STACK_SIZE - LOCALS_SIZE
|
||||
add.d $t0, $t0, $t1
|
||||
la.pcrel $sp, _stacks
|
||||
add.d $sp, $sp, $t0
|
||||
|
||||
SetException:
|
||||
# Only enable the IPI
|
||||
li.w $t0, 0x1FFF
|
||||
csrxchg $zero, $t0, LOONGARCH_CSR_ECFG
|
||||
li.w $t0, (0x1 << 12)
|
||||
csrxchg $t0, $t0, LOONGARCH_CSR_ECFG
|
||||
|
||||
# Set exception base
|
||||
csrrd $t0, LOONGARCH_CSR_ECFG
|
||||
bstrins.w $t0, $zero, 18, 16
|
||||
csrwr $t0, LOONGARCH_CSR_ECFG
|
||||
la.pcrel $t0, exception_entry
|
||||
csrwr $t0, LOONGARCH_CSR_EBASE # Exception and interrupts
|
||||
la.pcrel $t0, exception_entry
|
||||
csrwr $t0, LOONGARCH_CSR_TLBREBASE # TLB refill
|
||||
|
||||
# Enable intrrupts globally
|
||||
li.w $t0, (0x1 << 2)
|
||||
csrxchg $t0, $t0, LOONGARCH_CSR_CRMD
|
||||
|
||||
bl reloc
|
||||
|
||||
# Release the startup mutex.
|
||||
la.pcrel $t0, startup_mutex
|
||||
li.w $t1, 0x0
|
||||
amswap_db.w $zero, $t1, $t0
|
||||
|
||||
# Run the application.
|
||||
bl main
|
||||
|
||||
.previous
|
||||
|
||||
# Variables.
|
||||
|
||||
.data
|
||||
.align 4
|
||||
|
||||
.globl ap_startup_addr
|
||||
ap_startup_addr:
|
||||
.quad 0 # filled in at run time
|
||||
|
||||
.globl boot_params_addr
|
||||
boot_params_addr:
|
||||
.quad 0
|
||||
|
||||
startup_mutex:
|
||||
.long 0
|
||||
|
||||
first_boot:
|
||||
.long 1
|
||||
|
||||
.previous
|
||||
|
||||
# Startup stack.
|
||||
|
||||
.bss
|
||||
.align 16
|
||||
|
||||
startup_stack_base:
|
||||
. = . + 64
|
||||
startup_stack_top:
|
||||
|
||||
.previous
|
||||
|
||||
# Main stack area.
|
||||
|
||||
.section ".stacks", "aw", @nobits
|
||||
.align 16
|
||||
|
||||
. = . + STACKS_SIZE
|
||||
|
||||
.previous
|
@ -45,7 +45,7 @@ SYS_OBJS = system/acpi.o \
|
||||
system/screen.o \
|
||||
system/serial.o \
|
||||
system/smbios.o \
|
||||
system/smbus.o \
|
||||
system/i2c_x86.o \
|
||||
system/spd.o \
|
||||
system/smp.o \
|
||||
system/temperature.o \
|
||||
|
@ -45,7 +45,7 @@ SYS_OBJS = system/acpi.o \
|
||||
system/screen.o \
|
||||
system/serial.o \
|
||||
system/smbios.o \
|
||||
system/smbus.o \
|
||||
system/i2c_x86.o \
|
||||
system/spd.o \
|
||||
system/smp.o \
|
||||
system/temperature.o \
|
||||
|
205
build64/la64/Makefile
Normal file
205
build64/la64/Makefile
Normal file
@ -0,0 +1,205 @@
|
||||
CC ?= gcc
|
||||
LD ?= ld
|
||||
OBJCOPY ?= objcopy
|
||||
|
||||
GIT ?= git
|
||||
|
||||
ifeq ($(GIT),none)
|
||||
GIT_AVAILABLE = false
|
||||
else
|
||||
GIT_AVAILABLE = true
|
||||
endif
|
||||
|
||||
CFLAGS = -std=gnu11 -Wall -Wextra -Wshadow -march=loongarch64 -fpic -fno-builtin \
|
||||
-ffreestanding -fomit-frame-pointer -fno-stack-protector -DARCH_BITS=64
|
||||
|
||||
ifeq ($(DEBUG), 1)
|
||||
CFLAGS+=-ggdb3 -DDEBUG_GDB
|
||||
OPT_SMALL=-Og
|
||||
OPT_FAST=-Og
|
||||
MS_LDS=memtest_shared_debug.lds
|
||||
else
|
||||
OPT_SMALL=-Os
|
||||
OPT_FAST=-O3
|
||||
MS_LDS=ldscripts/memtest_shared.lds
|
||||
endif
|
||||
|
||||
INC_DIRS = -I../../boot -I../../system -I../../system/loongarch -I../../lib -I../../tests -I../../app -Iapp
|
||||
|
||||
SYS_OBJS = system/acpi.o \
|
||||
system/cpulocal.o \
|
||||
system/ehci.o \
|
||||
system/font.o \
|
||||
system/heap.o \
|
||||
system/hwquirks.o \
|
||||
system/keyboard.o \
|
||||
system/ohci.o \
|
||||
system/pci_mmio.o \
|
||||
system/pmem.o \
|
||||
system/reloc.o \
|
||||
system/screen.o \
|
||||
system/serial.o \
|
||||
system/smbios.o \
|
||||
system/spd.o \
|
||||
system/smp.o \
|
||||
system/timers.o \
|
||||
system/uhci.o \
|
||||
system/usbhcd.o \
|
||||
system/xhci.o \
|
||||
system/loongarch/i2c.o \
|
||||
system/loongarch/cpuid.o \
|
||||
system/loongarch/cpuinfo.o \
|
||||
system/loongarch/hwctrl.o \
|
||||
system/loongarch/memctrl.o \
|
||||
system/loongarch/temperature.o \
|
||||
system/loongarch/vmem.o
|
||||
|
||||
IMC_SRCS = $(wildcard ../../system/imc/loongson/*.c)
|
||||
IMC_OBJS = $(subst ../../,,$(IMC_SRCS:.c=.o))
|
||||
|
||||
LIB_OBJS = lib/barrier.o \
|
||||
lib/print.o \
|
||||
lib/read.o \
|
||||
lib/string.o \
|
||||
lib/unistd.o
|
||||
|
||||
TST_OBJS = tests/addr_walk1.o \
|
||||
tests/bit_fade.o \
|
||||
tests/block_move.o \
|
||||
tests/modulo_n.o \
|
||||
tests/mov_inv_fixed.o \
|
||||
tests/mov_inv_random.o \
|
||||
tests/mov_inv_walk1.o \
|
||||
tests/own_addr.o \
|
||||
tests/test_helper.o \
|
||||
tests/tests.o
|
||||
|
||||
APP_OBJS = app/badram.o \
|
||||
app/config.o \
|
||||
app/display.o \
|
||||
app/error.o \
|
||||
app/main.o \
|
||||
app/loongarch/interrupt.o
|
||||
|
||||
OBJS = boot/startup.o boot/efisetup.o $(SYS_OBJS) $(IMC_OBJS) $(LIB_OBJS) $(TST_OBJS) $(APP_OBJS)
|
||||
|
||||
all: memtest.efi
|
||||
|
||||
check:
|
||||
@if [ -z ${DEBUG} ]; then\
|
||||
echo "Macro DEBUG is not defined. Run debug_memtest.sh to invoke debug target";\
|
||||
exit 1;\
|
||||
fi
|
||||
|
||||
debug: check memtest.debug memtestloongarch.efi
|
||||
|
||||
-include boot/efisetup.d
|
||||
-include $(subst .o,.d,$(SYS_OBJS))
|
||||
-include $(subst .o,.d,$(IMC_OBJS))
|
||||
-include $(subst .o,.d,$(LIB_OBJS))
|
||||
-include $(subst .o,.d,$(TST_OBJS))
|
||||
-include $(subst .o,.d,$(APP_OBJS))
|
||||
|
||||
boot/header.o : | ../../boot/sbat.csv
|
||||
|
||||
boot/startup.o: ../../boot/loongarch/startup64.S ../../boot/boot.h
|
||||
@mkdir -p boot
|
||||
$(CC) -x assembler-with-cpp -c -I../../boot -I../../system/loongarch -o $@ $<
|
||||
|
||||
boot/%.o: ../../boot/%.S ../../boot/boot.h app/build_version.h
|
||||
@mkdir -p boot
|
||||
$(CC) -x assembler-with-cpp -c -I../../boot -Iapp -o $@ $<
|
||||
|
||||
boot/loongarch/%.o: ../../boot/loongarch/%.S ../../boot/boot.h app/build_version.h
|
||||
@mkdir -p boot/loongarch
|
||||
$(CC) -x assembler-with-cpp -c -I../../boot -Iapp -o $@ $<
|
||||
|
||||
boot/efisetup.o: ../../boot/efisetup.c
|
||||
@mkdir -p boot
|
||||
$(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
|
||||
|
||||
system/reloc.o: ../../system/reloc64.c
|
||||
@mkdir -p system
|
||||
$(CC) -c $(CFLAGS) -fno-strict-aliasing $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
|
||||
|
||||
system/%.o: ../../system/%.c
|
||||
@mkdir -p system
|
||||
$(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
|
||||
|
||||
system/loongarch/%.o: ../../system/loongarch/%.c
|
||||
@mkdir -p system/loongarch/
|
||||
$(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
|
||||
|
||||
system/imc/loongson/%.o: ../../system/imc/loongson/%.c
|
||||
@mkdir -p system/imc/loongson/
|
||||
$(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
|
||||
|
||||
lib/%.o: ../../lib/%.c
|
||||
@mkdir -p lib
|
||||
$(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
|
||||
|
||||
tests/%.o: ../../tests/%.c
|
||||
@mkdir -p tests
|
||||
$(CC) -c $(CFLAGS) $(OPT_FAST) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
|
||||
|
||||
app/%.o: ../../app/%.c app/build_version.h
|
||||
@mkdir -p app
|
||||
$(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
|
||||
|
||||
app/loongarch/%.o: ../../app/loongarch/%.c app/build_version.h
|
||||
@mkdir -p app/loongarch
|
||||
$(CC) -c $(CFLAGS) $(OPT_SMALL) $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)
|
||||
|
||||
app/build_version.h: FORCE
|
||||
@mkdir -p app
|
||||
@( \
|
||||
cp -f ../../app/version.h $@.tmp; \
|
||||
if $(GIT_AVAILABLE) && test -d ../../.git ; then \
|
||||
hash=`git rev-parse HEAD | cut -c1-7`; \
|
||||
sed -i 's/GIT_HASH\s\".*"/GIT_HASH "'$$hash'"/' $@.tmp; \
|
||||
else \
|
||||
sed -i 's/GIT_HASH\s\".*"/GIT_HASH "unknown"/' $@.tmp; \
|
||||
fi; \
|
||||
cmp $@ $@.tmp 2>/dev/null || cp -f $@.tmp $@; \
|
||||
rm -f $@.tmp; \
|
||||
)
|
||||
|
||||
FORCE:
|
||||
|
||||
# Link it statically once so I know I don't have undefined symbols and
|
||||
# then link it dynamically so I have full relocation information.
|
||||
|
||||
memtest_shared: $(OBJS) $(MS_LDS) Makefile
|
||||
$(LD) --warn-constructors --warn-common -static -T $(MS_LDS) -o $@ $(OBJS) && \
|
||||
$(LD) -shared -Bsymbolic -T $(MS_LDS) -o $@ $(OBJS)
|
||||
|
||||
memtest_shared.bin: memtest_shared
|
||||
$(OBJCOPY) -O binary $< memtest_shared.bin
|
||||
|
||||
memtest.debug: memtest_shared
|
||||
objcopy --only-keep-debug memtest_shared memtest.debug
|
||||
strip -R .eh_frame memtest_shared
|
||||
strip -R .comment memtest_shared
|
||||
|
||||
memtest.efi: memtest_shared.bin boot/loongarch/header.o ldscripts/memtest_efi.lds
|
||||
$(eval SIZES=$(shell size -B -d memtest_shared | grep memtest_shared))
|
||||
$(LD) --defsym=_bss_size=$(word 3,$(SIZES)) -T ldscripts/memtest_efi.lds boot/loongarch/header.o -b binary memtest_shared.bin -o memtest.efi
|
||||
|
||||
esp.img: memtest.efi
|
||||
@mkdir -p iso/EFI/BOOT
|
||||
cp memtest.efi iso/EFI/BOOT/BOOTLOONGARCH64.EFI
|
||||
@rm -f esp.img
|
||||
/sbin/mkdosfs -n MEMTEST-ESP -F12 -C esp.img 4096
|
||||
mcopy -s -i esp.img iso/EFI ::
|
||||
|
||||
memtest.iso: esp.img
|
||||
xorrisofs -pad -R -J -volid MT86PLUS_64 -graft-points \
|
||||
-part_like_isohybrid -iso_mbr_part_type 0x00 -append_partition 2 0xef ./esp.img \
|
||||
-o ./memtest.iso /EFI=./iso/EFI
|
||||
|
||||
iso: memtest.iso
|
||||
|
||||
clean:
|
||||
rm -rf boot system lib tests app *.img *.iso memtest* iso grub-*
|
||||
|
||||
# grub-memtest.iso will be added in future. TODO
|
56
build64/la64/ldscripts/memtest_efi.lds
Normal file
56
build64/la64/ldscripts/memtest_efi.lds
Normal file
@ -0,0 +1,56 @@
|
||||
OUTPUT_FORMAT("binary")
|
||||
OUTPUT_ARCH(loongarch)
|
||||
|
||||
ENTRY(head);
|
||||
SECTIONS {
|
||||
. = 0;
|
||||
.header : {
|
||||
*(.header)
|
||||
}
|
||||
. = ALIGN(4096);
|
||||
.text : {
|
||||
_file_text_start = . ;
|
||||
*(.data)
|
||||
_real_text_end = . ;
|
||||
. = ALIGN(512);
|
||||
_file_text_end = . ;
|
||||
}
|
||||
.reloc : {
|
||||
_file_reloc_start = . ;
|
||||
*(.reloc)
|
||||
_real_reloc_end = . ;
|
||||
. = ALIGN(512);
|
||||
_file_reloc_end = . ;
|
||||
}
|
||||
.sbat : {
|
||||
_file_sbat_start = . ;
|
||||
*(.sbat)
|
||||
_real_sbat_end = . ;
|
||||
. = ALIGN(512);
|
||||
_file_sbat_end = . ;
|
||||
}
|
||||
/DISCARD/ : { *(*) }
|
||||
|
||||
_real_text_size = _real_text_end - _file_text_start;
|
||||
_real_reloc_size = _real_reloc_end - _file_reloc_start;
|
||||
_real_sbat_size = _real_sbat_end - _file_sbat_start;
|
||||
|
||||
_file_head_size = _file_text_start;
|
||||
_file_text_size = _file_text_end - _file_text_start;
|
||||
_file_reloc_size = _file_reloc_end - _file_reloc_start;
|
||||
_file_sbat_size = _file_sbat_end - _file_sbat_start;
|
||||
|
||||
_sys_size = (_real_text_size + 15) >> 4;
|
||||
_init_size = _real_text_size + _bss_size;
|
||||
|
||||
_virt_head_size = _file_head_size;
|
||||
_virt_text_size = _init_size;
|
||||
_virt_reloc_size = _file_reloc_size;
|
||||
_virt_sbat_size = _file_sbat_size;
|
||||
|
||||
_virt_text_start = _virt_head_size;
|
||||
_virt_reloc_start = _virt_text_start + _virt_text_size;
|
||||
_virt_sbat_start = _virt_reloc_start + _virt_reloc_size;
|
||||
|
||||
_virt_img_size = _virt_sbat_start + _virt_sbat_size;
|
||||
}
|
56
build64/la64/ldscripts/memtest_shared.lds
Normal file
56
build64/la64/ldscripts/memtest_shared.lds
Normal file
@ -0,0 +1,56 @@
|
||||
OUTPUT_FORMAT("elf64-loongarch")
|
||||
OUTPUT_ARCH(loongarch);
|
||||
|
||||
ENTRY(startup64);
|
||||
SECTIONS {
|
||||
. = 0;
|
||||
.text : {
|
||||
_start = .;
|
||||
*(.text)
|
||||
*(.text.*)
|
||||
*(.plt)
|
||||
_etext = . ;
|
||||
} = 0x00004003
|
||||
.rodata : {
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
}
|
||||
.dynsym : { *(.dynsym) }
|
||||
.dynstr : { *(.dynstr) }
|
||||
.hash : { *(.hash) }
|
||||
.gnu.hash : { *(.gnu.hash) }
|
||||
.dynamic : { *(.dynamic) }
|
||||
|
||||
.rela.text : { *(.rela.text .rela.text.*) }
|
||||
.rela.rodata : { *(.rela.rodata .rela.rodata.*) }
|
||||
.rela.data : { *(.rela.data .rela.data.*) }
|
||||
.rela.got : { *(.rela.got .rela.got.*) }
|
||||
.rela.plt : { *(.rela.plt .rela.plt.*) }
|
||||
|
||||
. = ALIGN(4);
|
||||
.data : {
|
||||
_data = .;
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
}
|
||||
.got : {
|
||||
*(.got.plt)
|
||||
*(.got)
|
||||
_edata = . ;
|
||||
}
|
||||
. = ALIGN(4);
|
||||
.bss : {
|
||||
_bss = .;
|
||||
*(.dynbss)
|
||||
*(.bss)
|
||||
*(.bss.*)
|
||||
*(COMMON)
|
||||
. = ALIGN(16);
|
||||
_stacks = .;
|
||||
*(.stacks)
|
||||
/* _end must be at least 256 byte aligned */
|
||||
. = ALIGN(256);
|
||||
_end = .;
|
||||
}
|
||||
/DISCARD/ : { *(*) }
|
||||
}
|
@ -935,7 +935,7 @@ EXCLUDE_PATTERNS =
|
||||
# Note that the wildcards are matched against the file with absolute path, so to
|
||||
# exclude all test directories use the pattern */test/*
|
||||
|
||||
EXCLUDE_SYMBOLS = __MEMRW_*
|
||||
EXCLUDE_SYMBOLS = __MEMRW_* __MMIORW_*
|
||||
|
||||
# The EXAMPLE_PATH tag can be used to specify one or more files or directories
|
||||
# that contain example code fragments that are included (see the \include
|
||||
@ -2181,7 +2181,7 @@ PREDEFINED = __attribute__(x)=
|
||||
# definition found in the source code.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
EXPAND_AS_DEFINED = __MEMRW_READ_FUNC __MEMRW_WRITE_FUNC __MEMRW_FLUSH_FUNC
|
||||
EXPAND_AS_DEFINED = __MEMRW_READ_FUNC __MEMRW_WRITE_FUNC __MEMRW_FLUSH_FUNC __MMIORW_READ_FUNC __MMIORW_WRITE_FUNC
|
||||
|
||||
# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
|
||||
# remove all references to function-like macros that are alone on a line, have
|
||||
|
@ -18,7 +18,11 @@
|
||||
static inline void assert(int expr)
|
||||
{
|
||||
if (!expr) {
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
__asm__ __volatile__ ("int $3");
|
||||
#elif defined(__loongarch_lp64)
|
||||
__asm__ __volatile__ ("break 0");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,20 @@ void barrier_spin_wait(barrier_t *barrier)
|
||||
if (__sync_sub_and_fetch(&barrier->count, 1) != 0) {
|
||||
volatile bool *i_am_blocked = &waiting_flags[my_cpu].flag;
|
||||
while (*i_am_blocked) {
|
||||
#if defined(__x86_64) || defined(__i386__)
|
||||
__builtin_ia32_pause();
|
||||
#elif defined (__loongarch_lp64)
|
||||
__asm__ __volatile__ (
|
||||
"nop \n\t" \
|
||||
"nop \n\t" \
|
||||
"nop \n\t" \
|
||||
"nop \n\t" \
|
||||
"nop \n\t" \
|
||||
"nop \n\t" \
|
||||
"nop \n\t" \
|
||||
"nop \n\t" \
|
||||
);
|
||||
#endif
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -78,6 +91,7 @@ void barrier_halt_wait(barrier_t *barrier)
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
__asm__ goto ("\t"
|
||||
"lock decl %0 \n\t"
|
||||
"je 0f \n\t"
|
||||
@ -89,6 +103,23 @@ void barrier_halt_wait(barrier_t *barrier)
|
||||
: /* no clobbers */
|
||||
: end
|
||||
);
|
||||
#elif defined(__loongarch_lp64)
|
||||
__asm__ goto ("\t"
|
||||
"li.w $t0, -1\n\t" \
|
||||
"li.w $t2, 1\n\t" \
|
||||
"amadd_db.w $t1, $t0, %0\n\t" \
|
||||
"bge $t2, $t1, 0f\n\t" \
|
||||
"1: \n\t" \
|
||||
"idle 0x0\n\t" \
|
||||
"b 1b\n\t" \
|
||||
"bl %l[end]\n\t" \
|
||||
"0:\n\t" \
|
||||
: /* no outputs */
|
||||
: "r" (&(barrier->count))
|
||||
: "$t0", "t1", "$t2"
|
||||
: end
|
||||
);
|
||||
#endif
|
||||
// Last one here, so reset the barrier and wake the others.
|
||||
barrier->count = barrier->num_threads;
|
||||
__sync_synchronize();
|
||||
|
@ -17,6 +17,25 @@
|
||||
*/
|
||||
typedef volatile bool spinlock_t;
|
||||
|
||||
#ifdef __loongarch_lp64
|
||||
/**
|
||||
* LoongArch CPU pause.
|
||||
*/
|
||||
static inline void cpu_pause (void)
|
||||
{
|
||||
__asm__ __volatile__ (
|
||||
"nop \n\t" \
|
||||
"nop \n\t" \
|
||||
"nop \n\t" \
|
||||
"nop \n\t" \
|
||||
"nop \n\t" \
|
||||
"nop \n\t" \
|
||||
"nop \n\t" \
|
||||
"nop \n\t" \
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Spins until the mutex is unlocked.
|
||||
*/
|
||||
@ -24,7 +43,11 @@ static inline void spin_wait(spinlock_t *lock)
|
||||
{
|
||||
if (lock) {
|
||||
while (*lock) {
|
||||
#if defined(__x86_64) || defined(__i386__)
|
||||
__builtin_ia32_pause();
|
||||
#elif defined (__loongarch_lp64)
|
||||
cpu_pause();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -37,7 +60,11 @@ static inline void spin_lock(spinlock_t *lock)
|
||||
if (lock) {
|
||||
while (!__sync_bool_compare_and_swap(lock, false, true)) {
|
||||
do {
|
||||
#if defined(__x86_64) || defined(__i386__)
|
||||
__builtin_ia32_pause();
|
||||
#elif defined (__loongarch_lp64)
|
||||
cpu_pause();
|
||||
#endif
|
||||
} while (*lock);
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ void *memmove(void *dest, const void *src, size_t n)
|
||||
return dest;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_GDB
|
||||
#if (defined(DEBUG_GDB) || defined(__loongarch_lp64))
|
||||
|
||||
void *memcpy (void *dest, const void *src, size_t len)
|
||||
{
|
||||
@ -128,4 +128,4 @@ uint32_t hexstr2int(const char *hexstr) {
|
||||
ival = (ival << 4) | (b & 0xF);
|
||||
}
|
||||
return ival;
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ static inline int memcmp(const void *s1, const void *s2, size_t n)
|
||||
* not overlap.
|
||||
* void *memcpy(void *dst, const void *src, size_t n);
|
||||
*/
|
||||
#ifndef DEBUG_GDB
|
||||
#if !(defined(DEBUG_GDB) || defined(__loongarch_lp64))
|
||||
#define memcpy(d, s, n) __builtin_memcpy((d), (s), (n))
|
||||
#else
|
||||
void *memcpy (void *dest, const void *src, size_t len);
|
||||
@ -54,7 +54,7 @@ void *memmove(void *dest, const void *src, size_t n);
|
||||
* value c.
|
||||
* void *memset(void *s, int c, size_t n);
|
||||
*/
|
||||
#ifndef DEBUG_GDB
|
||||
#if !(defined(DEBUG_GDB) || defined(__loongarch_lp64))
|
||||
#define memset(s, c, n) __builtin_memset((s), (c), (n))
|
||||
#else
|
||||
void *memset (void *dest, int val, size_t len);
|
||||
|
13
lib/unistd.c
13
lib/unistd.c
@ -19,7 +19,20 @@ void usleep(unsigned int usec)
|
||||
uint64_t cycles = ((uint64_t)usec * clks_per_msec) / 1000;
|
||||
uint64_t t0 = get_tsc();
|
||||
do {
|
||||
#if defined(__x86_64) || defined(__i386__)
|
||||
__builtin_ia32_pause();
|
||||
#elif defined (__loongarch_lp64)
|
||||
__asm__ __volatile__ (
|
||||
"nop \n\t" \
|
||||
"nop \n\t" \
|
||||
"nop \n\t" \
|
||||
"nop \n\t" \
|
||||
"nop \n\t" \
|
||||
"nop \n\t" \
|
||||
"nop \n\t" \
|
||||
"nop \n\t" \
|
||||
);
|
||||
#endif
|
||||
} while ((get_tsc() - t0) < cycles);
|
||||
} else {
|
||||
// This will be highly inaccurate, but should give at least the requested delay.
|
||||
|
@ -83,6 +83,7 @@ acpi_t acpi_config = {0, 0, 0, 0, 0, /*0,*/ 0, 0, 0, false};
|
||||
// Private Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
static rsdp_t *scan_for_rsdp(uintptr_t addr, int length)
|
||||
{
|
||||
uint32_t *ptr = (uint32_t *)addr;
|
||||
@ -99,6 +100,7 @@ static rsdp_t *scan_for_rsdp(uintptr_t addr, int length)
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (ARCH_BITS == 64)
|
||||
static rsdp_t *find_rsdp_in_efi64_system_table(efi64_system_table_t *system_table)
|
||||
@ -168,6 +170,7 @@ static uintptr_t find_rsdp(void)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
if (rp == NULL) {
|
||||
// Search the BIOS EBDA area.
|
||||
uintptr_t address = *(uint16_t *)0x40E << 4;
|
||||
@ -181,6 +184,7 @@ static uintptr_t find_rsdp(void)
|
||||
rp = scan_for_rsdp(0xE0000, 0x20000);
|
||||
if (rp) rsdp_source = "BIOS reserved area";
|
||||
}
|
||||
#endif
|
||||
if (rp == NULL) {
|
||||
// RSDP not found, give up.
|
||||
return 0;
|
||||
|
@ -10,12 +10,23 @@
|
||||
* Copyright (C) 2020-2022 Martin Whitaker.
|
||||
*/
|
||||
|
||||
#ifdef __loongarch_lp64
|
||||
#include <larchintrin.h>
|
||||
#include "string.h"
|
||||
#define cache_op(op,addr) \
|
||||
__asm__ __volatile__( \
|
||||
"cacop %0, %1\n" \
|
||||
: \
|
||||
: "i" (op), "ZC" (*(unsigned char *)(addr)))
|
||||
static inline void cache_flush(void);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Disable the CPU caches.
|
||||
*/
|
||||
static inline void cache_off(void)
|
||||
{
|
||||
#ifdef __x86_64__
|
||||
#if defined(__x86_64__)
|
||||
__asm__ __volatile__ ("\t"
|
||||
"movq %%cr0, %%rax \n\t"
|
||||
"orl $0x40000000, %%eax \n\t" /* Set CD */
|
||||
@ -25,7 +36,7 @@ static inline void cache_off(void)
|
||||
: /* no inputs */
|
||||
: "rax", "memory"
|
||||
);
|
||||
#else
|
||||
#elif defined(__i386__)
|
||||
__asm__ __volatile__ ("\t"
|
||||
"movl %%cr0, %%eax \n\t"
|
||||
"orl $0x40000000, %%eax \n\t" /* Set CD */
|
||||
@ -35,6 +46,9 @@ static inline void cache_off(void)
|
||||
: /* no inputs */
|
||||
: "eax", "memory"
|
||||
);
|
||||
#elif defined(__loongarch_lp64)
|
||||
cache_flush();
|
||||
__csrxchg_d(0, 3 << 4, 0x181);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -43,7 +57,7 @@ static inline void cache_off(void)
|
||||
*/
|
||||
static inline void cache_on(void)
|
||||
{
|
||||
#ifdef __x86_64__
|
||||
#if defined(__x86_64__)
|
||||
__asm__ __volatile__ ("\t"
|
||||
"movq %%cr0, %%rax \n\t"
|
||||
"andl $0x9fffffff, %%eax \n\t" /* Clear CD and NW */
|
||||
@ -52,7 +66,7 @@ static inline void cache_on(void)
|
||||
: /* no inputs */
|
||||
: "rax", "memory"
|
||||
);
|
||||
#else
|
||||
#elif defined(__i386__)
|
||||
__asm__ __volatile__ ("\t"
|
||||
"movl %%cr0, %%eax \n\t"
|
||||
"andl $0x9fffffff, %%eax \n\t" /* Clear CD and NW */
|
||||
@ -61,6 +75,9 @@ static inline void cache_on(void)
|
||||
: /* no inputs */
|
||||
: "eax", "memory"
|
||||
);
|
||||
#elif defined(__loongarch_lp64)
|
||||
cache_flush();
|
||||
__csrxchg_d(1 << 4, 3 << 4, 0x181);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -69,12 +86,47 @@ static inline void cache_on(void)
|
||||
*/
|
||||
static inline void cache_flush(void)
|
||||
{
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
__asm__ __volatile__ ("\t"
|
||||
"wbinvd\n"
|
||||
: /* no outputs */
|
||||
: /* no inputs */
|
||||
: "memory"
|
||||
);
|
||||
#elif defined (__loongarch_lp64)
|
||||
if (!(__cpucfg(0x10) & (1 << 10))) {
|
||||
return; // No L3
|
||||
}
|
||||
uint64_t ways = (__cpucfg(0x14) & 0xFFFF) + 1;
|
||||
uint64_t sets = 1 << ((__cpucfg(0x14) >> 16) & 0xFF);
|
||||
uint64_t line_size = 1 << ((__cpucfg(0x14) >> 24) & 0x7F);
|
||||
uint64_t va, i, j;
|
||||
uint64_t cpu_module[1];
|
||||
va = 0;
|
||||
|
||||
cpu_module[0] = (uint64_t)__iocsrrd_d(0x20);
|
||||
if (strstr((const char *)cpu_module, "3A6000")) {
|
||||
uint8_t old_sc_cfg;
|
||||
old_sc_cfg = __iocsrrd_b(0x280);
|
||||
__iocsrwr_b(0x1, 0x280);
|
||||
for (i = 0; i < (ways * 3); i++) {
|
||||
for (j = 0; j < sets; j++) {
|
||||
*(volatile uint32_t *)va;
|
||||
va += line_size;
|
||||
}
|
||||
}
|
||||
__iocsrwr_b(old_sc_cfg, 0x280);
|
||||
} else {
|
||||
for (i = 0; i < sets; i++) {
|
||||
for (j = 0; j < ways; j++) {
|
||||
cache_op(0xB, va);
|
||||
va++;
|
||||
}
|
||||
va -= ways;
|
||||
va += line_size;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // CACHE_H
|
||||
|
@ -209,6 +209,7 @@ void cpuid_init(void);
|
||||
*/
|
||||
core_type_t get_ap_hybrid_type(void);
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
/**
|
||||
* Executes the cpuid instruction.
|
||||
*/
|
||||
@ -225,5 +226,6 @@ static inline void cpuid(uint32_t op, uint32_t count, uint32_t *eax, uint32_t *e
|
||||
"2" (*ecx)
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // CPUID_H
|
||||
|
@ -74,6 +74,10 @@
|
||||
#define IMC_K19_STK 0x81A0 // Zen4 (Storm Peak)
|
||||
#define IMC_K19_GRG 0x8150 // Zen5 (Granite Ridge)
|
||||
|
||||
#define IMC_LSLA 0xC000 // Loongson LoongArch family
|
||||
#define IMC_LA464 0xC010 // LA464 (Loongson 3th Gen)
|
||||
#define IMC_LA664 0xC011 // LA664 (Loongson 4th Gen)
|
||||
|
||||
/**
|
||||
* A string identifying the CPU make and model.
|
||||
*/
|
||||
|
@ -75,12 +75,18 @@ void heap_rewind(heap_type_t heap_id, uintptr_t mark)
|
||||
|
||||
void heap_init(void)
|
||||
{
|
||||
// Use the largest 20-bit addressable physical memory segment for the low-memory heap.
|
||||
// For x86_64 and i386 use the largest 20-bit addressable physical memory segment for the low-memory heap.
|
||||
// For loongarch use the largest 28-bit addressable physical memory segment for the low-memory heap.
|
||||
// Use the largest 32-bit addressable physical memory segment for the high-memory heap.
|
||||
// Exclude memory occupied by the program or below it in that segment.
|
||||
uintptr_t program_start = (uintptr_t)_start >> PAGE_SHIFT;
|
||||
uintptr_t program_end = program_start + num_pages(_end - _start);
|
||||
uintptr_t max_segment_size = 0;
|
||||
#if defined(__i386__) || defined (__x86_64__)
|
||||
uintptr_t low_memory_heap = PAGE_C(1, MB);
|
||||
#elif defined(__loongarch_lp64)
|
||||
uintptr_t low_memory_heap = PAGE_C(256, MB);
|
||||
#endif
|
||||
for (int i = 0; i < pm_map_size && pm_map[i].end <= PAGE_C(4,GB); i++) {
|
||||
uintptr_t try_heap_start = pm_map[i].start;
|
||||
uintptr_t try_heap_end = pm_map[i].end;
|
||||
@ -90,7 +96,7 @@ void heap_init(void)
|
||||
uintptr_t segment_size = try_heap_end - try_heap_start;
|
||||
if (segment_size >= max_segment_size) {
|
||||
max_segment_size = segment_size;
|
||||
if (try_heap_end <= PAGE_C(1,MB)) {
|
||||
if (try_heap_end <= low_memory_heap) {
|
||||
heaps[HEAP_TYPE_LM_1].segment = i;
|
||||
heaps[HEAP_TYPE_LM_1].start = try_heap_start;
|
||||
heaps[HEAP_TYPE_LM_1].end = try_heap_end;
|
||||
|
@ -16,6 +16,8 @@
|
||||
#include "cpuid.h"
|
||||
#include "config.h"
|
||||
#include "temperature.h"
|
||||
#include "memrw.h"
|
||||
#include "vmem.h"
|
||||
|
||||
quirk_t quirk;
|
||||
|
||||
@ -99,6 +101,22 @@ static void amd_k8_revfg_temp(void)
|
||||
cpu_temp_offset = 21.0f;
|
||||
}
|
||||
|
||||
static void loongson_7a00_ehci_workaround(void)
|
||||
{
|
||||
uintptr_t reg_addr = 0x10010000;
|
||||
|
||||
#if (ARCH_BITS == 64)
|
||||
reg_addr |= 0xEULL << 40;
|
||||
#endif
|
||||
reg_addr = map_region(reg_addr, 0x0, false);
|
||||
write8((uint8_t *)(reg_addr + 0x3820), 0xFF);
|
||||
write8((uint8_t *)(reg_addr + 0x3830), 0xFF);
|
||||
write32((uint32_t *)(reg_addr + 0x3100), 0xFFFFFFFF);
|
||||
write32((uint32_t *)(reg_addr + 0x3180), 0xFFFFFFFF);
|
||||
write8((uint8_t *)(reg_addr + 0x3820), 0x0);
|
||||
write8((uint8_t *)(reg_addr + 0x3830), 0x0);
|
||||
}
|
||||
|
||||
// ---------------------
|
||||
// -- Public function --
|
||||
// ---------------------
|
||||
@ -199,4 +217,13 @@ void quirks_init(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// -- Loongson 7A1000 and 7A2000 chipset USB 2.0 workaround --
|
||||
// -----------------------------------------------------------
|
||||
if (quirk.root_vid == PCI_VID_LOONGSON && quirk.root_did == 0x7a00) {
|
||||
quirk.id = QUIRK_LOONGSON7A00_EHCI_WORKARD;
|
||||
quirk.type |= QUIRK_TYPE_USB;
|
||||
quirk.process = loongson_7a00_ehci_workaround;
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,8 @@ typedef enum {
|
||||
QUIRK_X10SDV_NOSMP,
|
||||
QUIRK_K8_BSTEP_NOTEMP,
|
||||
QUIRK_K8_REVFG_TEMP,
|
||||
QUIRK_AMD_ERRATA_319
|
||||
QUIRK_AMD_ERRATA_319,
|
||||
QUIRK_LOONGSON7A00_EHCI_WORKARD
|
||||
} quirk_id_t;
|
||||
|
||||
typedef struct {
|
||||
|
@ -25,6 +25,9 @@ void get_imc_config_intel_icl(void);
|
||||
/* Memory configuration Detection for Intel Alder Lake */
|
||||
void get_imc_config_intel_adl(void);
|
||||
|
||||
/* Memory configuration Detection for Loongson LoongArch DDR4 CPU family */
|
||||
void get_imc_config_loongson_ddr4(void);
|
||||
|
||||
/**
|
||||
* ECC Polling Code for various IMCs
|
||||
*/
|
||||
|
171
system/imc/loongson/loongson_la.c
Normal file
171
system/imc/loongson/loongson_la.c
Normal file
@ -0,0 +1,171 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2024 Loongson Technology Corporation Limited. All rights reserved.
|
||||
//
|
||||
// Platform-specific code for Loongson LoongArch CPU
|
||||
//
|
||||
|
||||
#include "boot.h"
|
||||
#include "cpuinfo.h"
|
||||
#include "memctrl.h"
|
||||
#include "memrw.h"
|
||||
#include "cache.h"
|
||||
#include "cpuid.h"
|
||||
|
||||
#include <larchintrin.h>
|
||||
|
||||
#include "../imc.h"
|
||||
|
||||
#define MC_CONF_ADDRESS 0x800000000FF00000ULL
|
||||
#define CHIP_CONF_ADDRESS 0x800000001FE00000ULL
|
||||
|
||||
void read_imc_sequence(void)
|
||||
{
|
||||
imc.tCL = (uint16_t)read8((uint8_t *)(MC_CONF_ADDRESS + 0x1060));
|
||||
imc.tCL_dec = 0;
|
||||
imc.tRP = (uint16_t)read8((uint8_t *)(MC_CONF_ADDRESS + 0x1006));
|
||||
imc.tRCD = (uint16_t)read8((uint8_t *)(MC_CONF_ADDRESS + 0x1047));
|
||||
imc.tRAS = (uint16_t)read8((uint8_t *)(MC_CONF_ADDRESS + 0x1040));
|
||||
}
|
||||
|
||||
bool read_imc_info(uint8_t node_num, uint8_t max_mc, bool route_flag)
|
||||
{
|
||||
uint64_t fun_val;
|
||||
uint8_t i, j;
|
||||
uint8_t active_mc_num = 0;
|
||||
bool ret = false;
|
||||
|
||||
if (!max_mc) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (route_flag) {
|
||||
for (i = 0; i < max_mc; i++) {
|
||||
fun_val = read64((uint64_t *)(CHIP_CONF_ADDRESS | i << 16 | 0x180));
|
||||
write64((uint64_t *)(CHIP_CONF_ADDRESS | i << 16 | 0x180), (fun_val & (~(1 << 4))));
|
||||
|
||||
if (read8((uint8_t *)(MC_CONF_ADDRESS)) == 0xFF || read8((uint8_t *)(MC_CONF_ADDRESS)) == 0x00) {
|
||||
write64((uint64_t *)(CHIP_CONF_ADDRESS | i << 16 | 0x180), fun_val);
|
||||
continue;
|
||||
}
|
||||
|
||||
read_imc_sequence();
|
||||
imc.width = 64;
|
||||
|
||||
write64((uint64_t *)(CHIP_CONF_ADDRESS | i << 16 | 0x180), fun_val);
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
|
||||
for (j = 0; j < node_num; j++) {
|
||||
i = read8((uint8_t *)(CHIP_CONF_ADDRESS | 0x411 | ((uint64_t)j << 44)));
|
||||
if ((i & 0x3) == ((i >> 2) & 0x3)) {
|
||||
active_mc_num += 1;
|
||||
} else {
|
||||
if ((i & 0x3) == ((i >> 4) & 0x3)) {
|
||||
active_mc_num += 2;
|
||||
} else {
|
||||
active_mc_num += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < max_mc; i++) {
|
||||
fun_val = read64((uint64_t *)(CHIP_CONF_ADDRESS | 0x180));
|
||||
write64((uint64_t *)(CHIP_CONF_ADDRESS | 0x180), (fun_val & (~(1 << (4 + (i * 5))))));
|
||||
|
||||
if (read8((uint8_t *)(MC_CONF_ADDRESS)) == 0xFF || read8((uint8_t *)(MC_CONF_ADDRESS)) == 0x00) {
|
||||
write64((uint64_t *)(CHIP_CONF_ADDRESS | 0x180), fun_val);
|
||||
continue;
|
||||
}
|
||||
|
||||
read_imc_sequence();
|
||||
|
||||
write64((uint64_t *)(CHIP_CONF_ADDRESS | 0x180), fun_val);
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
|
||||
switch ((read8((uint8_t *)(MC_CONF_ADDRESS + 0x1203)) & 0x3)) {
|
||||
case 1:
|
||||
imc.width = 16;
|
||||
break;
|
||||
case 2:
|
||||
imc.width = 32;
|
||||
break;
|
||||
case 3:
|
||||
imc.width = 64;
|
||||
break;
|
||||
default:
|
||||
imc.width = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (((read64((uint64_t *)(CHIP_CONF_ADDRESS | 0x400))) >> 30) & 0x3) {
|
||||
case 1:
|
||||
case 2:
|
||||
active_mc_num = 1;
|
||||
break;
|
||||
case 3:
|
||||
active_mc_num = 2;
|
||||
break;
|
||||
default:
|
||||
active_mc_num = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
imc.width *= active_mc_num;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void get_imc_config_loongson_ddr4(void)
|
||||
{
|
||||
uint32_t val;
|
||||
uint16_t refc, loopc, div, div_mode, ref_clk;
|
||||
uint8_t max_mc, node_num;
|
||||
bool route_flag;
|
||||
|
||||
imc.type = "DDR4";
|
||||
node_num = 1;
|
||||
|
||||
if (strstr(cpuid_info.brand_id.str, "3C") ||
|
||||
(strstr(cpuid_info.brand_id.str, "3B6000") &&
|
||||
!strstr(cpuid_info.brand_id.str, "3B6000M"))) {
|
||||
route_flag = true;
|
||||
max_mc = 4;
|
||||
} else if (strstr(cpuid_info.brand_id.str, "3D")) {
|
||||
route_flag = true;
|
||||
max_mc = 8;
|
||||
node_num = 2;
|
||||
} else if (strstr(cpuid_info.brand_id.str, "3E")) {
|
||||
route_flag = true;
|
||||
max_mc = 8;
|
||||
node_num = 4;
|
||||
} else if (strstr(cpuid_info.brand_id.str, "3A") ||
|
||||
strstr(cpuid_info.brand_id.str, "3B")) {
|
||||
route_flag = false;
|
||||
max_mc = 2;
|
||||
} else if (strstr(cpuid_info.brand_id.str, "2K") ||
|
||||
strstr(cpuid_info.brand_id.str, "3B6000M")) {
|
||||
route_flag = false;
|
||||
max_mc = 1;
|
||||
} else {
|
||||
route_flag = false;
|
||||
max_mc = 0;
|
||||
node_num = 0;
|
||||
}
|
||||
|
||||
if (read_imc_info(node_num, max_mc, route_flag)) {
|
||||
val = __iocsrrd_w(0x1c0);
|
||||
|
||||
loopc = (val >> 14) & 0x3FF;
|
||||
div = (val >> 24) & 0x3F;
|
||||
div_mode = 0x1 << ((val >> 4) & 0x3);
|
||||
refc = (val >> 8) & 0x1f;
|
||||
ref_clk = (uint16_t)(((__cpucfg(0x4) * (__cpucfg(0x5) & 0xFFFF)) / ((__cpucfg(0x5) >> 16) & 0xFFFF)) / 1000000);
|
||||
imc.freq = (ref_clk * loopc / refc / div / div_mode) * 4;
|
||||
} else {
|
||||
imc.freq = 0;
|
||||
}
|
||||
}
|
59
system/io.h
59
system/io.h
@ -20,6 +20,8 @@
|
||||
* (original contained no copyright statement)
|
||||
*/
|
||||
|
||||
#if defined(__x86_64) || defined(__i386__)
|
||||
|
||||
#ifdef SLOW_IO_BY_JUMPING
|
||||
#define __SLOW_DOWN_IO __asm__ __volatile__("jmp 1f\n1:\tjmp 1f\n1:")
|
||||
#else
|
||||
@ -145,4 +147,61 @@ outb_p (unsigned char __value, unsigned short int __port)
|
||||
);
|
||||
}
|
||||
|
||||
#elif defined(__loongarch_lp64)
|
||||
|
||||
#define LOONGSON_IO_PROT_BASE 0x0efdfc000000
|
||||
|
||||
#define __IN(s, s1) \
|
||||
static inline RETURN_TYPE __in##s(unsigned short port) { \
|
||||
RETURN_TYPE _v; \
|
||||
__asm__ __volatile__ ( \
|
||||
"li.d $t0, 0x28\n\t" \
|
||||
"csrwr $t0, 0x0\n\t" \
|
||||
"ld." s1 " %0, %1, 0 \n\t" \
|
||||
"csrwr $t0, 0x0\n\t" \
|
||||
: "=r" (_v) \
|
||||
: "r" (LOONGSON_IO_PROT_BASE + port) \
|
||||
: "$t0" \
|
||||
); \
|
||||
return _v; \
|
||||
}
|
||||
#define RETURN_TYPE uint8_t
|
||||
__IN(b, "b");
|
||||
#undef RETURN_TYPE
|
||||
#define RETURN_TYPE uint16_t
|
||||
__IN(w, "h");
|
||||
#undef RETURN_TYPE
|
||||
#define RETURN_TYPE uint32_t
|
||||
__IN(l, "w");
|
||||
|
||||
#define __OUT(s, s1) \
|
||||
static __inline void __out##s (VALUE_TYPE val, unsigned short port) { \
|
||||
__asm__ __volatile__ ( \
|
||||
"li.d $t0, 0x28\n\t" \
|
||||
"csrwr $t0, 0x0\n\t" \
|
||||
"st." s1 " %z0, %1, 0 \n\t" \
|
||||
"csrwr $t0, 0x0\n\t" \
|
||||
: \
|
||||
: "Jr" (val), "r" (LOONGSON_IO_PROT_BASE + port) \
|
||||
: "$t0" \
|
||||
); \
|
||||
}
|
||||
#define VALUE_TYPE uint8_t
|
||||
__OUT(b, "b");
|
||||
#undef VALUE_TYPE
|
||||
#define VALUE_TYPE uint16_t
|
||||
__OUT(w, "h");
|
||||
#undef VALUE_TYPE
|
||||
#define VALUE_TYPE uint32_t
|
||||
__OUT(l, "w");
|
||||
|
||||
#define outb(val,port) __outb(val,port)
|
||||
#define inb(port) __inb(port)
|
||||
#define outw(val,port) __outw(val,port)
|
||||
#define inw(port) __inw(port)
|
||||
#define outl(val,port) __outl(val,port)
|
||||
#define inl(port) __inl(port)
|
||||
|
||||
#endif
|
||||
|
||||
#endif // IO_H
|
||||
|
85
system/loongarch/cpuid.c
Normal file
85
system/loongarch/cpuid.c
Normal file
@ -0,0 +1,85 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2024 Loongson Technology Corporation Limited. All rights reserved.
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "cpuid.h"
|
||||
|
||||
#include "string.h"
|
||||
|
||||
#include <larchintrin.h>
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Variables
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
cpuid_info_t cpuid_info;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void cpuid_init(void)
|
||||
{
|
||||
// Get the vendor ID.
|
||||
cpuid_info.vendor_id.raw[0] = (uint32_t)(__iocsrrd_w(0x10));
|
||||
cpuid_info.vendor_id.raw[1] = (uint32_t)(__iocsrrd_w(0x14));
|
||||
cpuid_info.vendor_id.raw[2] = (uint32_t)(__iocsrrd_w(0x18));
|
||||
cpuid_info.vendor_id.str[CPUID_VENDOR_STR_LENGTH - 1] = '\0';
|
||||
|
||||
cpuid_info.topology.core_count = -1;
|
||||
cpuid_info.topology.thread_count = -1;
|
||||
cpuid_info.topology.is_hybrid = 0;
|
||||
cpuid_info.topology.ecore_count = -1;
|
||||
cpuid_info.topology.pcore_count = -1;
|
||||
|
||||
// Get the brand ID.
|
||||
cpuid_info.brand_id.raw[0] = (uint32_t)(__iocsrrd_w(0x10));
|
||||
cpuid_info.brand_id.raw[1] = (uint32_t)(__iocsrrd_w(0x14));
|
||||
if (__iocsrrd_w(0x18) != 0x0) {
|
||||
cpuid_info.brand_id.raw[2] = (uint32_t)(__iocsrrd_w(0x18));
|
||||
} else {
|
||||
cpuid_info.brand_id.raw[2] = 0x20202020;
|
||||
}
|
||||
if (__iocsrrd_w(0x1c) != 0x0) {
|
||||
cpuid_info.brand_id.raw[3] = (uint32_t)(__iocsrrd_w(0x1c));
|
||||
} else {
|
||||
cpuid_info.brand_id.raw[3] = 0x20202020;
|
||||
}
|
||||
cpuid_info.brand_id.raw[4] = (uint32_t)(__iocsrrd_w(0x20));
|
||||
cpuid_info.brand_id.raw[5] = (uint32_t)(__iocsrrd_w(0x24));
|
||||
if (__iocsrrd_w(0x28) != 0x0) {
|
||||
cpuid_info.brand_id.raw[6] = (uint32_t)(__iocsrrd_w(0x28));
|
||||
} else {
|
||||
cpuid_info.brand_id.raw[6] = 0x20202020;
|
||||
}
|
||||
if (__iocsrrd_w(0x2c) != 0x0) {
|
||||
cpuid_info.brand_id.raw[7] = (uint32_t)(__iocsrrd_w(0x2c));
|
||||
} else {
|
||||
cpuid_info.brand_id.raw[7] = 0x20202020;
|
||||
}
|
||||
cpuid_info.brand_id.str[CPUID_BRAND_STR_LENGTH - 1] = '\0';
|
||||
|
||||
// Set correct HTT flag
|
||||
cpuid_info.flags.htt = false;
|
||||
if (strstr(cpuid_info.brand_id.str, "3A5") ||
|
||||
strstr(cpuid_info.brand_id.str, "3C5") ||
|
||||
strstr(cpuid_info.brand_id.str, "3D5") ||
|
||||
strstr(cpuid_info.brand_id.str, "3E5") ||
|
||||
strstr(cpuid_info.brand_id.str, "2K") ||
|
||||
strstr(cpuid_info.brand_id.str, "B6000M"))
|
||||
{
|
||||
cpuid_info.flags.htt = false;
|
||||
} else {
|
||||
cpuid_info.flags.htt = true;
|
||||
}
|
||||
}
|
||||
|
||||
core_type_t get_ap_hybrid_type(void)
|
||||
{
|
||||
//
|
||||
// Currently, return P-Core anyway.
|
||||
//
|
||||
return CORE_PCORE;
|
||||
}
|
280
system/loongarch/cpuinfo.c
Normal file
280
system/loongarch/cpuinfo.c
Normal file
@ -0,0 +1,280 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2020-2022 Martin Whitaker.
|
||||
// Copyright (C) 2004-2023 Sam Demeulemeester
|
||||
// Copyright (C) 2024 Loongson Technology Corporation Limited. All rights reserved.
|
||||
//
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <larchintrin.h>
|
||||
|
||||
#include "cpuid.h"
|
||||
#include "tsc.h"
|
||||
|
||||
#include "boot.h"
|
||||
#include "config.h"
|
||||
#include "pmem.h"
|
||||
#include "vmem.h"
|
||||
#include "memctrl.h"
|
||||
#include "memsize.h"
|
||||
#include "hwquirks.h"
|
||||
|
||||
#include "cpuinfo.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Constants
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#define BENCH_MIN_START_ADR 0x10000000 // 256MB
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Variables
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const char *cpu_model = NULL;
|
||||
|
||||
int l1_cache = 0;
|
||||
int l2_cache = 0;
|
||||
int l3_cache = 0;
|
||||
|
||||
uint32_t l1_cache_speed = 0;
|
||||
uint32_t l2_cache_speed = 0;
|
||||
uint32_t l3_cache_speed = 0;
|
||||
uint32_t ram_speed = 0;
|
||||
|
||||
uint32_t clks_per_msec = 0;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Private Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static void determine_cache_size()
|
||||
{
|
||||
uint32_t cache_info = 0;
|
||||
uint32_t cache_present = 0;
|
||||
uint32_t ways = 0;
|
||||
uint32_t index = 0;
|
||||
uint32_t line_size = 0;
|
||||
|
||||
// Get the persence of caches
|
||||
cache_present = __cpucfg(0x10);
|
||||
|
||||
// L1 D cache present
|
||||
if (cache_present & (0x1 << 0)) {
|
||||
cache_info = __cpucfg(0x12);
|
||||
ways = (cache_info & 0xFFFF) + 1;
|
||||
index = 1 << ((cache_info >> 16) & 0xFF);
|
||||
line_size = 1 << ((cache_info >> 24) & 0x7F);
|
||||
l1_cache = (ways * index * line_size) / 1024;
|
||||
}
|
||||
|
||||
// L2 cache present
|
||||
if (cache_present & (0x1 << 3)) {
|
||||
cache_info = __cpucfg(0x13);
|
||||
ways = (cache_info & 0xFFFF) + 1;
|
||||
index = 1 << ((cache_info >> 16) & 0xFF);
|
||||
line_size = 1 << ((cache_info >> 24) & 0x7F);
|
||||
l2_cache = (ways * index * line_size) / 1024;
|
||||
}
|
||||
|
||||
// L3 cache present
|
||||
if (cache_present & (0x1 << 10)) {
|
||||
cache_info = __cpucfg(0x14);
|
||||
ways = (cache_info & 0xFFFF) + 1;
|
||||
index = 1 << ((cache_info >> 16) & 0xFF);
|
||||
line_size = 1 << ((cache_info >> 24) & 0x7F);
|
||||
l3_cache = (ways * index * line_size) / 1024;
|
||||
}
|
||||
}
|
||||
|
||||
static void determine_imc(void)
|
||||
{
|
||||
// Check Loongson IMC
|
||||
if (cpuid_info.vendor_id.str[0] == 'L') {
|
||||
imc.family = IMC_LSLA;
|
||||
switch (cpuid_info.brand_id.str[0x12])
|
||||
{
|
||||
case '5':
|
||||
imc.family = IMC_LA464;
|
||||
break;
|
||||
case '6':
|
||||
imc.family = IMC_LA664;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void determine_cpu_model(void)
|
||||
{
|
||||
cpu_model = cpuid_info.brand_id.str;
|
||||
determine_imc();
|
||||
return;
|
||||
}
|
||||
|
||||
static uint32_t memspeed(uintptr_t src, uint32_t len, int iter)
|
||||
{
|
||||
uintptr_t dst;
|
||||
uintptr_t wlen;
|
||||
uint64_t start_time, end_time, run_time_clk, overhead;
|
||||
int i;
|
||||
|
||||
dst = src + len;
|
||||
|
||||
wlen = len / 8;
|
||||
// Get number of clock cycles due to overhead
|
||||
start_time = get_tsc();
|
||||
for (i = 0; i < iter; i++) {
|
||||
__asm__ __volatile__ (
|
||||
"move $t0, %0\n\t" \
|
||||
"move $t1, %1\n\t" \
|
||||
"move $t2, %2\n\t" \
|
||||
"2:\n\t" \
|
||||
"beqz $t2, 1f\n\t" \
|
||||
"addi.d $t0, $t0, 8\n\t" \
|
||||
"addi.d $t1, $t1, 8\n\t" \
|
||||
"addi.d $t2, $t2, -1\n\t" \
|
||||
"b 2b\n\t" \
|
||||
"1:\n\t" \
|
||||
:: "r" (src), "r" (dst), "r" (0)
|
||||
: "$t0", "$t1", "$t2"
|
||||
);
|
||||
}
|
||||
end_time = get_tsc();
|
||||
|
||||
overhead = (end_time - start_time);
|
||||
|
||||
// Prime the cache
|
||||
__asm__ __volatile__ (
|
||||
"move $t0, %0\n\t" \
|
||||
"move $t1, %1\n\t" \
|
||||
"move $t2, %2\n\t" \
|
||||
"2:\n\t" \
|
||||
"beqz $t2, 1f\n\t" \
|
||||
"ld.d $t3, $t0, 0x0\n\t" \
|
||||
"st.d $t3, $t1, 0x0\n\t" \
|
||||
"addi.d $t0, $t0, 8\n\t" \
|
||||
"addi.d $t1, $t1, 8\n\t" \
|
||||
"addi.d $t2, $t2, -1\n\t" \
|
||||
"b 2b\n\t" \
|
||||
"1:\n\t" \
|
||||
:: "r" (src), "r" (dst), "r" (wlen)
|
||||
: "$t0", "$t1", "$t2", "$t3"
|
||||
);
|
||||
|
||||
// Write these bytes
|
||||
start_time = get_tsc();
|
||||
for (i = 0; i < iter; i++) {
|
||||
__asm__ __volatile__ (
|
||||
"move $t0, %0\n\t" \
|
||||
"move $t1, %1\n\t" \
|
||||
"move $t2, %2\n\t" \
|
||||
"2:\n\t" \
|
||||
"beqz $t2, 1f\n\t" \
|
||||
"ld.d $t3, $t0, 0x0\n\t" \
|
||||
"st.d $t3, $t1, 0x0\n\t" \
|
||||
"addi.d $t0, $t0, 8\n\t" \
|
||||
"addi.d $t1, $t1, 8\n\t" \
|
||||
"addi.d $t2, $t2, -1\n\t" \
|
||||
"b 2b\n\t" \
|
||||
"1:\n\t" \
|
||||
:: "r" (src), "r" (dst), "r" (wlen)
|
||||
: "$t0", "$t1", "$t2", "$t3"
|
||||
);
|
||||
}
|
||||
end_time = get_tsc();
|
||||
if ((end_time - start_time) > overhead) {
|
||||
run_time_clk = (end_time - start_time) - overhead;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
run_time_clk = ((len * iter) / (double)run_time_clk) * clks_per_msec * 2;
|
||||
|
||||
return run_time_clk;
|
||||
}
|
||||
|
||||
static void measure_memory_bandwidth(void)
|
||||
{
|
||||
uintptr_t bench_start_adr = 0;
|
||||
size_t mem_test_len;
|
||||
|
||||
if (l3_cache) {
|
||||
mem_test_len = 4*l3_cache*1024;
|
||||
} else if (l2_cache) {
|
||||
mem_test_len = 4*l2_cache*1024;
|
||||
} else {
|
||||
return; // If we're not able to detect L2, don't start benchmark
|
||||
}
|
||||
|
||||
// Locate enough free space under the 4G address space for testing.
|
||||
for (int i = 0; i < pm_map_size && pm_map[i].start < PAGE_C(4,GB); i++) {
|
||||
uintptr_t try_start = pm_map[i].start << PAGE_SHIFT;
|
||||
uintptr_t try_end = try_start + mem_test_len * 2;
|
||||
|
||||
// No start address below BENCH_MIN_START_ADR
|
||||
if (try_start < BENCH_MIN_START_ADR) {
|
||||
if ((pm_map[i].end << PAGE_SHIFT) >= (BENCH_MIN_START_ADR + mem_test_len * 2)) {
|
||||
try_start = BENCH_MIN_START_ADR;
|
||||
try_end = BENCH_MIN_START_ADR + mem_test_len * 2;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Avoid the memory region where the program is currently located.
|
||||
if (try_start < (uintptr_t)_end && try_end > (uintptr_t)_start) {
|
||||
try_start = (uintptr_t)_end;
|
||||
try_end = try_start + mem_test_len * 2;
|
||||
}
|
||||
|
||||
uintptr_t end_limit = (pm_map[i].end < PAGE_C(4,GB) ? pm_map[i].end : PAGE_C(4,GB)) << PAGE_SHIFT;
|
||||
if (try_end <= end_limit) {
|
||||
bench_start_adr = try_start;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bench_start_adr == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Measure L1 BW using 1/3rd of the total L1 cache size
|
||||
if (l1_cache) {
|
||||
l1_cache_speed = memspeed(bench_start_adr, (l1_cache/3)*1024, 50);
|
||||
}
|
||||
|
||||
// Measure L2 BW using half the L2 cache size
|
||||
if (l2_cache) {
|
||||
l2_cache_speed = memspeed(bench_start_adr, l2_cache/2*1024, 50);
|
||||
}
|
||||
|
||||
// Measure L3 BW using half the L3 cache size
|
||||
if (l3_cache) {
|
||||
l3_cache_speed = memspeed(bench_start_adr, l3_cache/2*1024, 50);
|
||||
}
|
||||
|
||||
// Measure RAM BW
|
||||
ram_speed = memspeed(bench_start_adr, mem_test_len, 25);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void cpuinfo_init(void)
|
||||
{
|
||||
// Get cache sizes for most Loongson CPUs.
|
||||
determine_cache_size();
|
||||
|
||||
determine_cpu_model();
|
||||
}
|
||||
|
||||
void membw_init(void)
|
||||
{
|
||||
if(enable_bench) {
|
||||
measure_memory_bandwidth();
|
||||
}
|
||||
}
|
59
system/loongarch/hwctrl.c
Normal file
59
system/loongarch/hwctrl.c
Normal file
@ -0,0 +1,59 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2020-2022 Martin Whitaker.
|
||||
// Copyright (C) 2024 Loongson Technology Corporation Limited. All rights reserved.
|
||||
|
||||
#include "stddef.h"
|
||||
|
||||
#include "boot.h"
|
||||
#include "bootparams.h"
|
||||
#include "efi.h"
|
||||
|
||||
#include "unistd.h"
|
||||
|
||||
#include "hwctrl.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Private Variables
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static efi_runtime_services_t *efi_rs_table = NULL;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void hwctrl_init(void)
|
||||
{
|
||||
boot_params_t *boot_params = (boot_params_t *)boot_params_addr;
|
||||
if (boot_params->efi_info.loader_signature == EFI64_LOADER_SIGNATURE) {
|
||||
uintptr_t system_table_addr = (uintptr_t)boot_params->efi_info.sys_tab_hi << 32 | boot_params->efi_info.sys_tab;
|
||||
if (system_table_addr != 0) {
|
||||
efi64_system_table_t *sys_table = (efi64_system_table_t *)system_table_addr;
|
||||
efi_rs_table = (efi_runtime_services_t *)sys_table->runtime_services;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void reboot(void)
|
||||
{
|
||||
if (efi_rs_table != NULL) {
|
||||
efi_rs_table->reset_system(EFI_RESET_COLD, 0, 0);
|
||||
usleep(1000000);
|
||||
} else {
|
||||
while (1);
|
||||
}
|
||||
}
|
||||
|
||||
void floppy_off()
|
||||
{
|
||||
//
|
||||
// Do nothing
|
||||
//
|
||||
}
|
||||
|
||||
void cursor_off()
|
||||
{
|
||||
//
|
||||
// Do nothing
|
||||
//
|
||||
}
|
221
system/loongarch/i2c.c
Normal file
221
system/loongarch/i2c.c
Normal file
@ -0,0 +1,221 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2024 Loongson Technology Corporation Limited. All rights reserved.
|
||||
|
||||
#include "display.h"
|
||||
|
||||
#include "io.h"
|
||||
#include "string.h"
|
||||
|
||||
#include "cpuid.h"
|
||||
#include "memrw.h"
|
||||
#include "vmem.h"
|
||||
#include "smbus.h"
|
||||
#include "i2c.h"
|
||||
#include "spd.h"
|
||||
|
||||
i2c_param i2c_info;
|
||||
|
||||
uint8_t max_mc_nu = 0;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Private Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static bool determine_i2c_address(void)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
if (strstr(cpuid_info.vendor_id.str, "Loongson")) {
|
||||
if (strstr(cpuid_info.brand_id.str, "3A5") ||
|
||||
strstr(cpuid_info.brand_id.str, "3A6")) {
|
||||
max_mc_nu = 2;
|
||||
for(i = 0; i < max_mc_nu; i++) {
|
||||
i2c_info.i2c_mc[i].i2c_base = (uint8_t *)map_region(LOONGSON_I2C0_ADDR, 0x8, true);
|
||||
i2c_info.i2c_mc[i].devid.slot0_addr = i * 2 + 0x0;
|
||||
i2c_info.i2c_mc[i].devid.slot1_addr = i * 2 + 0x1;
|
||||
}
|
||||
} else if (strstr((const char *)cpuid_info.brand_id.str, "3C5")) {
|
||||
max_mc_nu = 4;
|
||||
for(i = 0; i < max_mc_nu; i++) {
|
||||
i2c_info.i2c_mc[i].i2c_base = (uint8_t *)map_region(LOONGSON_I2C0_ADDR, 0x8, true);
|
||||
}
|
||||
i2c_info.i2c_mc[0].devid.slot0_addr = 0x4;
|
||||
i2c_info.i2c_mc[0].devid.slot1_addr = 0x5;
|
||||
|
||||
i2c_info.i2c_mc[1].devid.slot0_addr = 0x0;
|
||||
i2c_info.i2c_mc[1].devid.slot1_addr = 0x1;
|
||||
|
||||
i2c_info.i2c_mc[2].devid.slot0_addr = 0x2;
|
||||
i2c_info.i2c_mc[2].devid.slot1_addr = 0x3;
|
||||
|
||||
i2c_info.i2c_mc[3].devid.slot0_addr = 0x6;
|
||||
i2c_info.i2c_mc[3].devid.slot1_addr = 0x7;
|
||||
} else if (strstr(cpuid_info.brand_id.str, "3D5")) {
|
||||
max_mc_nu = 4;
|
||||
i2c_info.i2c_mc[0].i2c_base = (uint8_t *)map_region(LOONGSON_I2C1_ADDR, 0x8, true);
|
||||
i2c_info.i2c_mc[1].i2c_base = (uint8_t *)map_region(LOONGSON_I2C1_ADDR, 0x8, true);
|
||||
|
||||
i2c_info.i2c_mc[2].i2c_base = (uint8_t *)map_region(LOONGSON_I2C2_ADDR, 0x8, true);
|
||||
i2c_info.i2c_mc[3].i2c_base = (uint8_t *)map_region(LOONGSON_I2C2_ADDR, 0x8, true);
|
||||
for(i = 0; i < max_mc_nu; i++) {
|
||||
i2c_info.i2c_mc[i].devid.slot0_addr = (i % 2 * 2 + 0x0);
|
||||
i2c_info.i2c_mc[i].devid.slot1_addr = (i % 2 * 2 + 0x1);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint8_t i2c_read_byte(uint8_t *base, uint8_t dev_addr, uint16_t offset)
|
||||
{
|
||||
uint8_t buf;
|
||||
volatile uint8_t spd_adr = (uint8_t)(offset & 0xff);
|
||||
|
||||
/* if addr less than 0x100 set to page0 as default status */
|
||||
if (offset & 0xff) {
|
||||
/*set page to 0*/
|
||||
|
||||
write8(base + TXR_REG, SPA0);
|
||||
|
||||
/*send device select code*/
|
||||
write8(base + CR_REG, CR_START | CR_WRITE);
|
||||
|
||||
/* wait send finished */
|
||||
while (read8(base + SR_REG) & SR_TIP);
|
||||
|
||||
/* i2c_stop */
|
||||
write8(base + CR_REG, CR_STOP);
|
||||
while (read8(base + SR_REG) & SR_BUSY);
|
||||
|
||||
/*set page to 0 end*/
|
||||
}
|
||||
|
||||
/* if addr large than 0xff set to page1 */
|
||||
if (offset & 0xff00) {
|
||||
/*set page to 1*/
|
||||
|
||||
write8(base + TXR_REG, SPA1);
|
||||
|
||||
/*send device select code*/
|
||||
write8(base + CR_REG, CR_START | CR_WRITE);
|
||||
|
||||
/* wait send finished */
|
||||
while(read8(base + SR_REG) & SR_TIP);
|
||||
|
||||
/* i2c_stop */
|
||||
write8(base + CR_REG, CR_STOP);
|
||||
while(read8(base + SR_REG) & SR_BUSY);
|
||||
|
||||
/*set page to 1 end*/
|
||||
}
|
||||
|
||||
/* load device address */
|
||||
write8(base + TXR_REG, dev_addr & 0xfe);
|
||||
|
||||
/* send start frame */
|
||||
write8(base + CR_REG, CR_START | CR_WRITE);
|
||||
|
||||
/* wait send finished */
|
||||
while (read8(base + SR_REG) & SR_TIP);
|
||||
|
||||
/* load data to be send */
|
||||
write8(base + TXR_REG, spd_adr);
|
||||
|
||||
/* send data frame */
|
||||
write8(base + CR_REG, CR_WRITE);
|
||||
|
||||
/* wait send finished */
|
||||
while (read8(base + SR_REG) & SR_TIP);
|
||||
|
||||
/* load device address */
|
||||
write8(base + TXR_REG, dev_addr | 0x1);
|
||||
|
||||
/* send start frame */
|
||||
write8(base + CR_REG, CR_START | CR_WRITE);
|
||||
|
||||
/* wait send finished */
|
||||
while (read8(base + SR_REG) & SR_TIP);
|
||||
|
||||
/* receive data to fifo */
|
||||
write8(base + CR_REG, CR_READ | CR_ACK);
|
||||
while (read8(base + SR_REG) & SR_TIP);
|
||||
|
||||
/* read data from fifo */
|
||||
buf = read8(base + RXR_REG);
|
||||
|
||||
/* free i2c bus */
|
||||
write8(base + CR_REG, CR_STOP);
|
||||
while (read8(base + SR_REG) & SR_BUSY);
|
||||
|
||||
/* if addr large than 0xff set to page0 as default status */
|
||||
if (offset & 0xff00) {
|
||||
/*set page to 0*/
|
||||
|
||||
write8(base + TXR_REG, SPA0);
|
||||
|
||||
/*send device select code*/
|
||||
write8(base + CR_REG, CR_START | CR_WRITE);
|
||||
|
||||
/* wait send finished */
|
||||
while (read8(base + SR_REG) & SR_TIP);
|
||||
|
||||
/* i2c_stop */
|
||||
write8(base + CR_REG, CR_STOP);
|
||||
while (read8(base + SR_REG) & SR_BUSY);
|
||||
|
||||
/*set page to 0 end*/
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
uint8_t get_spd(uint8_t slot_idx, uint16_t spd_adr)
|
||||
{
|
||||
uint8_t device_id, mc;
|
||||
|
||||
mc = slot_idx / 2;
|
||||
|
||||
if (strstr(cpuid_info.vendor_id.str, "Loongson")) {
|
||||
device_id = 0xa1;
|
||||
}
|
||||
|
||||
device_id |= (((slot_idx % 2) ?
|
||||
i2c_info.i2c_mc[mc].devid.slot1_addr :
|
||||
i2c_info.i2c_mc[mc].devid.slot0_addr) << 1);
|
||||
|
||||
return i2c_read_byte(i2c_info.i2c_mc[mc].i2c_base, device_id, spd_adr);
|
||||
}
|
||||
|
||||
void print_spd_startup_info(void)
|
||||
{
|
||||
uint8_t spdidx = 0, spd_line_idx = 0;
|
||||
|
||||
spd_info curspd;
|
||||
ram.freq = 0;
|
||||
curspd.isValid = false;
|
||||
|
||||
if (!determine_i2c_address()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (spdidx = 0; spdidx < max_mc_nu * 2; spdidx++) {
|
||||
parse_spd(&curspd, spdidx);
|
||||
|
||||
if (!curspd.isValid)
|
||||
continue;
|
||||
|
||||
if (spd_line_idx == 0) {
|
||||
prints(ROW_SPD-2, 0, "Memory SPD Information");
|
||||
prints(ROW_SPD-1, 0, "----------------------");
|
||||
}
|
||||
|
||||
print_spdi(curspd, ROW_SPD+spd_line_idx);
|
||||
spd_line_idx++;
|
||||
}
|
||||
}
|
69
system/loongarch/i2c.h
Normal file
69
system/loongarch/i2c.h
Normal file
@ -0,0 +1,69 @@
|
||||
/**
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* \file
|
||||
*
|
||||
* Provides functions for reading SPD via I2C
|
||||
* Copyright (C) 2024 Loongson Technology Corporation Limited. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _I2C_H_
|
||||
#define _I2C_H_
|
||||
|
||||
#define PRER_LO_REG 0x0
|
||||
#define PRER_HI_REG 0x1
|
||||
#define CTR_REG 0x2
|
||||
#define TXR_REG 0x3
|
||||
#define RXR_REG 0x3
|
||||
#define CR_REG 0x4
|
||||
#define SR_REG 0x4
|
||||
|
||||
#define CR_START 0x80
|
||||
#define CR_STOP 0x40
|
||||
#define CR_READ 0x20
|
||||
#define CR_WRITE 0x10
|
||||
#define CR_ACK 0x8
|
||||
#define CR_IACK 0x1
|
||||
|
||||
#define SR_NOACK 0x80
|
||||
#define SR_BUSY 0x40
|
||||
#define SR_AL 0x20
|
||||
#define SR_TIP 0x2
|
||||
#define SR_IF 0x1
|
||||
|
||||
#define SWP0 0x62
|
||||
#define SWP1 0x68
|
||||
#define SWP2 0x6a
|
||||
#define SWP3 0x60
|
||||
#define CWP 0x66
|
||||
#define RPS0 0x63
|
||||
#define RPS1 0x69
|
||||
#define RPS2 0x6b
|
||||
#define RPS3 0x61
|
||||
#define SPA0 0x6c
|
||||
#define SPA1 0x6e
|
||||
#define RPA 0x6d
|
||||
|
||||
#define LOONGSON_I2C0_ADDR 0x1fe00120
|
||||
#define LOONGSON_I2C1_ADDR 0x1fe00130
|
||||
#define LOONGSON_I2C2_ADDR 0x1fe00138
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
uint32_t slot0_addr : 8;
|
||||
uint32_t slot1_addr : 8;
|
||||
};
|
||||
uint32_t devid;
|
||||
} dev_id;
|
||||
|
||||
typedef struct {
|
||||
uint8_t *i2c_base;
|
||||
dev_id devid;
|
||||
} i2c_slot_param;
|
||||
|
||||
typedef struct {
|
||||
i2c_slot_param i2c_mc[4];
|
||||
} i2c_param;
|
||||
|
||||
#endif // I2C_H
|
51
system/loongarch/memctrl.c
Normal file
51
system/loongarch/memctrl.c
Normal file
@ -0,0 +1,51 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2004-2023 Sam Demeulemeester
|
||||
// Copyright (C) 2024 Loongson Technology Corporation Limited. All rights reserved.
|
||||
//
|
||||
// ------------------------
|
||||
//
|
||||
// Platform-specific code for IMC configuration, ECC support, etc.
|
||||
//
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "cpuinfo.h"
|
||||
|
||||
#include "memctrl.h"
|
||||
#include "imc/imc.h"
|
||||
|
||||
#include "display.h"
|
||||
|
||||
imc_info_t imc = {"UNDEF", 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
ecc_info_t ecc_status = {false, ECC_ERR_NONE, 0, 0, 0, 0};
|
||||
|
||||
// ---------------------
|
||||
// -- Public function --
|
||||
// ---------------------
|
||||
|
||||
void memctrl_init(void)
|
||||
{
|
||||
ecc_status.ecc_enabled = false;
|
||||
|
||||
if (!enable_mch_read) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch(imc.family) {
|
||||
case IMC_LA464:
|
||||
case IMC_LA664:
|
||||
get_imc_config_loongson_ddr4();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void memctrl_poll_ecc(void)
|
||||
{
|
||||
if (!ecc_status.ecc_enabled) {
|
||||
return;
|
||||
}
|
||||
}
|
50
system/loongarch/registers.h
Normal file
50
system/loongarch/registers.h
Normal file
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* \file
|
||||
*
|
||||
* Provides functions for reading SPD via I2C
|
||||
* Copyright (C) 2024 Loongson Technology Corporation Limited. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _REGISTERS_H_
|
||||
#define _REGISTERS_H_
|
||||
|
||||
#ifdef __loongarch_lp64
|
||||
#define RSIZE 8 // 64 bit mode register size
|
||||
#define GP_REG_CONTEXT_SIZE 32 * RSIZE // General-purpose registers size
|
||||
#define FP_REG_CONTEXT_SIZE 34 * RSIZE // Floating-point registers size
|
||||
#define CSR_REG_CONTEXT_SIZE 9 * RSIZE // CSR registers size
|
||||
#endif
|
||||
|
||||
//
|
||||
// CSR definitions
|
||||
//
|
||||
#define LOONGARCH_CSR_CRMD 0x0
|
||||
#define LOONGARCH_CSR_PRMD 0x1
|
||||
#define LOONGARCH_CSR_EUEN 0x2
|
||||
#define LOONGARCH_CSR_MISC 0x3
|
||||
#define LOONGARCH_CSR_ECFG 0x4
|
||||
#define LOONGARCH_CSR_ESTAT 0x5
|
||||
#define LOONGARCH_CSR_ERA 0x6
|
||||
#define LOONGARCH_CSR_BADV 0x7
|
||||
#define LOONGARCH_CSR_BADI 0x8
|
||||
#define LOONGARCH_CSR_EBASE 0xC // Exception entry base address
|
||||
#define LOONGARCH_CSR_CPUID 0x20 // CPU core ID
|
||||
#define LOONGARCH_CSR_KS0 0x30
|
||||
#define LOONGARCH_CSR_TLBREBASE 0x88 // TLB refill exception entry
|
||||
#define LOONGARCH_CSR_DMWIN0 0x180 // Direct map win0: MEM & IF
|
||||
#define LOONGARCH_CSR_DMWIN1 0x181 // Direct map win1: MEM & IF
|
||||
#define LOONGARCH_CSR_DMWIN2 0x182 // Direct map win2: MEM
|
||||
#define LOONGARCH_CSR_DMWIN3 0x183 // Direct map win3: MEM
|
||||
#define LOONGARCH_CSR_PERFCTRL0 0x200 // Perf event 0 config
|
||||
#define LOONGARCH_CSR_PERFCNTR0 0x201 // Perf event 0 count value
|
||||
#define LOONGARCH_CSR_PERFCTRL1 0x202 // Perf event 1 config
|
||||
#define LOONGARCH_CSR_PERFCNTR1 0x203 // Perf event 1 count value
|
||||
#define LOONGARCH_CSR_PERFCTRL2 0x204 // Perf event 2 config
|
||||
#define LOONGARCH_CSR_PERFCNTR2 0x205 // Perf event 2 count value
|
||||
#define LOONGARCH_CSR_PERFCTRL3 0x206 // Perf event 3 config
|
||||
#define LOONGARCH_CSR_PERFCNTR3 0x207 // Perf event 3 count value
|
||||
|
||||
#endif // REGISTERS_H
|
41
system/loongarch/temperature.c
Normal file
41
system/loongarch/temperature.c
Normal file
@ -0,0 +1,41 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2024 Loongson Technology Corporation Limited. All rights reserved.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "cpuid.h"
|
||||
#include "cpuinfo.h"
|
||||
#include "hwquirks.h"
|
||||
#include "memctrl.h"
|
||||
|
||||
#include "temperature.h"
|
||||
|
||||
#include <larchintrin.h>
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Variables
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
float cpu_temp_offset = 0;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void temperature_init(void)
|
||||
{
|
||||
if (!enable_temperature) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Process temperature-related quirks
|
||||
if (quirk.type & QUIRK_TYPE_TEMP) {
|
||||
quirk.process();
|
||||
}
|
||||
}
|
||||
|
||||
int get_cpu_temperature(void)
|
||||
{
|
||||
return (int)(__iocsrrd_w(0x428));
|
||||
}
|
73
system/loongarch/vmem.c
Normal file
73
system/loongarch/vmem.c
Normal file
@ -0,0 +1,73 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2024 Loongson Technology Corporation Limited. All rights reserved.
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <larchintrin.h>
|
||||
|
||||
#include "boot.h"
|
||||
|
||||
#include "cpuid.h"
|
||||
|
||||
#include "vmem.h"
|
||||
|
||||
extern bool map_numa_memory_range;
|
||||
extern uint8_t highest_map_bit;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Constants
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#define MAX_REGION_PAGES 256 // VM pages
|
||||
#define PCI_IO_PERFIX 0xEULL << 40
|
||||
#define DMW0_BASE 0x8000000000000000
|
||||
#define MMIO_BASE DMW0_BASE | PCI_IO_PERFIX
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
uintptr_t map_region(uintptr_t base_addr, size_t size __attribute__((unused)), bool only_for_startup __attribute__((unused)))
|
||||
{
|
||||
if (((base_addr < 0x80000000) && (base_addr > 0x30000000)) || ((base_addr & PCI_IO_PERFIX) != 0x0)) {
|
||||
return MMIO_BASE | base_addr;
|
||||
} else if ((base_addr < 0x20000000) && (base_addr > 0x10000000)) {
|
||||
return DMW0_BASE | base_addr;
|
||||
} else {
|
||||
return base_addr;
|
||||
}
|
||||
}
|
||||
|
||||
bool map_window(uintptr_t start_page __attribute__((unused)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void *first_word_mapping(uintptr_t page)
|
||||
{
|
||||
void *result;
|
||||
|
||||
if (map_numa_memory_range == true) {
|
||||
if ((page >> (highest_map_bit - PAGE_SHIFT)) & 0xF) {
|
||||
uintptr_t alias = (((uintptr_t)(page >> (highest_map_bit - PAGE_SHIFT)) & 0xF) << 32) ;
|
||||
alias |= page & ~(0xF << (highest_map_bit - PAGE_SHIFT));
|
||||
result = (void *)(alias << PAGE_SHIFT);
|
||||
} else {
|
||||
result = (void *)(page << PAGE_SHIFT);
|
||||
}
|
||||
} else {
|
||||
result = (void *)(page << PAGE_SHIFT);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void *last_word_mapping(uintptr_t page, size_t word_size)
|
||||
{
|
||||
return (uint8_t *)first_word_mapping(page) + (PAGE_SIZE - word_size);
|
||||
}
|
||||
|
||||
uintptr_t page_of(void *addr)
|
||||
{
|
||||
uintptr_t page = (uintptr_t)addr >> PAGE_SHIFT;
|
||||
return page;
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* Provides some 32/64-bit memory access functions. These stop the compiler
|
||||
* Provides some 8/16/32/64-bit memory access functions. These stop the compiler
|
||||
* optimizing accesses which need to be ordered and atomic. Mostly used
|
||||
* for accessing memory-mapped hardware registers.
|
||||
*
|
||||
@ -15,12 +15,28 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
|
||||
#define __MEMRW_SUFFIX_8BIT "b"
|
||||
#define __MEMRW_SUFFIX_16BIT "w"
|
||||
#define __MEMRW_SUFFIX_32BIT "l"
|
||||
#define __MEMRW_SUFFIX_64BIT "q"
|
||||
#define __MEMRW_READ_INSTRUCTIONS(bitwidth) "mov" __MEMRW_SUFFIX_##bitwidth##BIT " %1, %0"
|
||||
#define __MEMRW_WRITE_INSTRUCTIONS(bitwidth) "mov" __MEMRW_SUFFIX_##bitwidth##BIT " %1, %0"
|
||||
#define __MEMRW_FLUSH_INSTRUCTIONS(bitwidth) "mov" __MEMRW_SUFFIX_##bitwidth##BIT " %1, %0; mov" __MEMRW_SUFFIX_##bitwidth##BIT " %0, %1"
|
||||
|
||||
#elif defined(__loongarch_lp64)
|
||||
|
||||
#define __MEMRW_SUFFIX_8BIT "b"
|
||||
#define __MEMRW_SUFFIX_16BIT "h"
|
||||
#define __MEMRW_SUFFIX_32BIT "w"
|
||||
#define __MEMRW_SUFFIX_64BIT "d"
|
||||
#define __MEMRW_READ_INSTRUCTIONS(bitwidth) "ld." __MEMRW_SUFFIX_##bitwidth##BIT " %0, %1"
|
||||
#define __MEMRW_WRITE_INSTRUCTIONS(bitwidth) "st." __MEMRW_SUFFIX_##bitwidth##BIT " %1, %0"
|
||||
#define __MEMRW_FLUSH_INSTRUCTIONS(bitwidth) "st." __MEMRW_SUFFIX_##bitwidth##BIT " %1, %0; dbar 0"
|
||||
|
||||
#endif
|
||||
|
||||
#define __MEMRW_READ_FUNC(bitwidth) \
|
||||
static inline uint##bitwidth##_t read##bitwidth(const volatile uint##bitwidth##_t *ptr) \
|
||||
{ \
|
||||
@ -38,7 +54,7 @@ static inline uint##bitwidth##_t read##bitwidth(const volatile uint##bitwidth##_
|
||||
static inline void write##bitwidth(const volatile uint##bitwidth##_t *ptr, uint##bitwidth##_t val) \
|
||||
{ \
|
||||
__asm__ __volatile__( \
|
||||
__MEMRW_WRITE_INSTRUCTIONS(bitwidth) \
|
||||
__MEMRW_WRITE_INSTRUCTIONS(bitwidth) \
|
||||
: \
|
||||
: "m" (*ptr), \
|
||||
"r" (val) \
|
||||
@ -50,7 +66,7 @@ static inline void write##bitwidth(const volatile uint##bitwidth##_t *ptr, uint#
|
||||
static inline void flush##bitwidth(const volatile uint##bitwidth##_t *ptr, uint##bitwidth##_t val) \
|
||||
{ \
|
||||
__asm__ __volatile__( \
|
||||
__MEMRW_FLUSH_INSTRUCTIONS(bitwidth) \
|
||||
__MEMRW_FLUSH_INSTRUCTIONS(bitwidth) \
|
||||
: \
|
||||
: "m" (*ptr), \
|
||||
"r" (val) \
|
||||
@ -58,6 +74,14 @@ static inline void flush##bitwidth(const volatile uint##bitwidth##_t *ptr, uint#
|
||||
); \
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and returns the value stored in the 8-bit memory location pointed to by ptr.
|
||||
*/
|
||||
__MEMRW_READ_FUNC(8)
|
||||
/**
|
||||
* Reads and returns the value stored in the 16-bit memory location pointed to by ptr.
|
||||
*/
|
||||
__MEMRW_READ_FUNC(16)
|
||||
/**
|
||||
* Reads and returns the value stored in the 32-bit memory location pointed to by ptr.
|
||||
*/
|
||||
@ -67,6 +91,14 @@ __MEMRW_READ_FUNC(32)
|
||||
*/
|
||||
__MEMRW_READ_FUNC(64)
|
||||
|
||||
/**
|
||||
* Writes val to the 8-bit memory location pointed to by ptr.
|
||||
*/
|
||||
__MEMRW_WRITE_FUNC(8)
|
||||
/**
|
||||
* Writes val to the 16-bit memory location pointed to by ptr.
|
||||
*/
|
||||
__MEMRW_WRITE_FUNC(16)
|
||||
/**
|
||||
* Writes val to the 32-bit memory location pointed to by ptr.
|
||||
*/
|
||||
@ -76,6 +108,14 @@ __MEMRW_WRITE_FUNC(32)
|
||||
*/
|
||||
__MEMRW_WRITE_FUNC(64)
|
||||
|
||||
/**
|
||||
* Writes val to the 8-bit memory location pointed to by ptr. Only returns when the write is complete.
|
||||
*/
|
||||
__MEMRW_FLUSH_FUNC(8)
|
||||
/**
|
||||
* Writes val to the 16-bit memory location pointed to by ptr. Only returns when the write is complete.
|
||||
*/
|
||||
__MEMRW_FLUSH_FUNC(16)
|
||||
/**
|
||||
* Writes val to the 32-bit memory location pointed to by ptr. Only returns when the write is complete.
|
||||
*/
|
||||
|
94
system/mmio.h
Normal file
94
system/mmio.h
Normal file
@ -0,0 +1,94 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#ifndef MMIO_H
|
||||
#define MMIO_H
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* Provides some macro definitions for the 8/16/32/64-bit MMIO access.
|
||||
* These stop the compiler optimizing accesses which need to be ordered and
|
||||
* atomic. Only used for accessing memory-mapped hardware registers, IO and
|
||||
* memory spaces.
|
||||
*//*
|
||||
* Copyright (C) 2024 Loongson Technology Corporation Limited. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(__loongarch_lp64)
|
||||
|
||||
#define __MMIORW_SUFFIX_8BIT "b"
|
||||
#define __MMIORW_SUFFIX_16BIT "h"
|
||||
#define __MMIORW_SUFFIX_32BIT "w"
|
||||
#define __MMIORW_SUFFIX_64BIT "d"
|
||||
|
||||
#define __MMIORW_READ_INSTRUCTIONS(bitwidth) \
|
||||
"li.d $t0, 0x28; csrwr $t0, 0x0;" "ld." __MMIORW_SUFFIX_##bitwidth##BIT " %0, %1; csrwr $t0, 0x0"
|
||||
|
||||
#define __MMIORW_WRITE_INSTRUCTIONS(bitwidth) \
|
||||
"li.d $t0, 0x28; csrwr $t0, 0x0;" "st." __MMIORW_SUFFIX_##bitwidth##BIT " %1, %0; csrwr $t0, 0x0"
|
||||
|
||||
#define __MMIORW_READ_WRITE_CLOBBER "$t0", "memory"
|
||||
|
||||
#endif
|
||||
|
||||
#define __MMIORW_READ_FUNC(bitwidth) \
|
||||
static inline uint##bitwidth##_t mmio_read##bitwidth(const volatile uint##bitwidth##_t *ptr) \
|
||||
{ \
|
||||
uint##bitwidth##_t val; \
|
||||
__asm__ __volatile__ ( \
|
||||
__MMIORW_READ_INSTRUCTIONS(bitwidth) \
|
||||
: "=r" (val) \
|
||||
: "m" (*ptr) \
|
||||
: __MMIORW_READ_WRITE_CLOBBER \
|
||||
); \
|
||||
return val; \
|
||||
}
|
||||
|
||||
#define __MMIORW_WRITE_FUNC(bitwidth) \
|
||||
static inline void mmio_write##bitwidth(const volatile uint##bitwidth##_t *ptr, uint##bitwidth##_t val) \
|
||||
{ \
|
||||
__asm__ __volatile__ ( \
|
||||
__MMIORW_WRITE_INSTRUCTIONS(bitwidth) \
|
||||
: \
|
||||
: "m" (*ptr), \
|
||||
"r" (val) \
|
||||
: __MMIORW_READ_WRITE_CLOBBER \
|
||||
); \
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and returns the value stored in the 8-bit memory IO location pointed to by ptr.
|
||||
*/
|
||||
__MMIORW_READ_FUNC(8)
|
||||
/**
|
||||
* Reads and returns the value stored in the 16-bit memory IO location pointed to by ptr.
|
||||
*/
|
||||
__MMIORW_READ_FUNC(16)
|
||||
/**
|
||||
* Reads and returns the value stored in the 32-bit memory IO location pointed to by ptr.
|
||||
*/
|
||||
__MMIORW_READ_FUNC(32)
|
||||
/**
|
||||
* Reads and returns the value stored in the 64-bit memory IO location pointed to by ptr.
|
||||
*/
|
||||
__MMIORW_READ_FUNC(64)
|
||||
|
||||
/**
|
||||
* Writes val to the 8-bit memory IO location pointed to by ptr.
|
||||
*/
|
||||
__MMIORW_WRITE_FUNC(8)
|
||||
/**
|
||||
* Writes val to the 16-bit memory IO location pointed to by ptr.
|
||||
*/
|
||||
__MMIORW_WRITE_FUNC(16)
|
||||
/**
|
||||
* Writes val to the 32-bit memory IO location pointed to by ptr.
|
||||
*/
|
||||
__MMIORW_WRITE_FUNC(32)
|
||||
/**
|
||||
* Writes val to the 64-bit memory IO location pointed to by ptr.
|
||||
*/
|
||||
__MMIORW_WRITE_FUNC(64)
|
||||
|
||||
#endif // MMIO_H
|
@ -8,6 +8,7 @@
|
||||
*
|
||||
*//*
|
||||
* Copyright (C) 2020-2022 Martin Whitaker.
|
||||
* Copyright (C) 2024 Loongson Technology Corporation Limited. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
@ -18,6 +19,7 @@
|
||||
#define PCI_SUB_DID_REG 0x2E
|
||||
|
||||
/* Vendor IDs */
|
||||
#define PCI_VID_LOONGSON 0x0014
|
||||
#define PCI_VID_ATI 0x1002
|
||||
#define PCI_VID_AMD 0x1022
|
||||
#define PCI_VID_SIS 0x1039
|
||||
|
128
system/pci_mmio.c
Normal file
128
system/pci_mmio.c
Normal file
@ -0,0 +1,128 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2024 Loongson Technology Corporation Limited. All rights reserved.
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "boot.h"
|
||||
#include "bootparams.h"
|
||||
|
||||
#include "cpuid.h"
|
||||
#include "io.h"
|
||||
|
||||
#include "mmio.h"
|
||||
|
||||
#include "pci.h"
|
||||
#include "unistd.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Constants
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#define NB_PCI_MMIO_TYPE0_BASE 0x0EFE00000000
|
||||
#define NB_PCI_MMIO_TYPE1_BASE 0x0EFE10000000
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void pci_init(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t pci_config_type0_addr(int bus, int dev, int func, int reg)
|
||||
{
|
||||
uint64_t addr = NB_PCI_MMIO_TYPE0_BASE
|
||||
| (reg & 0xf00) << 16
|
||||
| (bus & 0xff) << 16
|
||||
| (dev & 0x1f) << 11
|
||||
| (func & 0x07) << 8
|
||||
| (reg & 0xff);
|
||||
return (addr);
|
||||
}
|
||||
|
||||
uint64_t pci_config_type1_addr(int bus, int dev, int func, int reg)
|
||||
{
|
||||
uint64_t addr = NB_PCI_MMIO_TYPE1_BASE
|
||||
| (reg & 0xf00) << 16
|
||||
| (bus & 0xff) << 16
|
||||
| (dev & 0x1f) << 11
|
||||
| (func & 0x07) << 8
|
||||
| (reg & 0xff);
|
||||
return (addr);
|
||||
}
|
||||
|
||||
uint8_t pci_config_read8(int bus, int dev, int func, int reg)
|
||||
{
|
||||
if (bus == 0) {
|
||||
return mmio_read8((uint8_t *)pci_config_type0_addr(bus, dev, func, reg));
|
||||
} else {
|
||||
return mmio_read8((uint8_t *)pci_config_type1_addr(bus, dev, func, reg));
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t pci_config_read16(int bus, int dev, int func, int reg)
|
||||
{
|
||||
if (bus == 0) {
|
||||
return mmio_read16((uint16_t *)pci_config_type0_addr(bus, dev, func, reg));
|
||||
} else {
|
||||
return mmio_read16((uint16_t *)pci_config_type1_addr(bus, dev, func, reg));
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t pci_config_read32(int bus, int dev, int func, int reg)
|
||||
{
|
||||
if (bus == 0) {
|
||||
return mmio_read32((uint32_t *)pci_config_type0_addr(bus, dev, func, reg));
|
||||
} else {
|
||||
return mmio_read32((uint32_t *)pci_config_type1_addr(bus, dev, func, reg));
|
||||
}
|
||||
}
|
||||
|
||||
void pci_config_write8(int bus, int dev, int func, int reg, uint8_t value)
|
||||
{
|
||||
if (bus == 0) {
|
||||
mmio_write8((uint8_t *)pci_config_type0_addr(bus, dev, func, reg), value);
|
||||
} else {
|
||||
mmio_write8((uint8_t *)pci_config_type1_addr(bus, dev, func, reg), value);
|
||||
}
|
||||
}
|
||||
|
||||
void pci_config_write16(int bus, int dev, int func, int reg, uint16_t value)
|
||||
{
|
||||
if (bus == 0) {
|
||||
mmio_write16((uint16_t *)pci_config_type0_addr(bus, dev, func, reg), value);
|
||||
} else {
|
||||
mmio_write16((uint16_t *)pci_config_type1_addr(bus, dev, func, reg), value);
|
||||
}
|
||||
}
|
||||
|
||||
void pci_config_write32(int bus, int dev, int func, int reg, uint32_t value)
|
||||
{
|
||||
if (bus == 0) {
|
||||
mmio_write32((uint32_t *)pci_config_type0_addr(bus, dev, func, reg), value);
|
||||
} else {
|
||||
mmio_write32((uint32_t *)pci_config_type1_addr(bus, dev, func, reg), value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -------------
|
||||
// LPC Functions
|
||||
// -------------
|
||||
|
||||
void lpc_outb(uint8_t cmd, uint8_t data)
|
||||
{
|
||||
outb(cmd, 0x2E);
|
||||
usleep(100);
|
||||
outb(data, 0x2F);
|
||||
usleep(100);
|
||||
}
|
||||
|
||||
uint8_t lpc_inb(uint8_t reg)
|
||||
{
|
||||
outb(reg, 0x2E);
|
||||
usleep(100);
|
||||
return inb(0x2F);
|
||||
}
|
@ -32,6 +32,8 @@
|
||||
|
||||
#define R_X86_64_NONE 0
|
||||
#define R_X86_64_RELATIVE 8
|
||||
#define R_LARCH_NONE 0
|
||||
#define R_LARCH_RELATIVE 3
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Types
|
||||
@ -70,12 +72,21 @@ typedef struct
|
||||
static inline Elf64_Addr __attribute__ ((unused)) get_load_address(void)
|
||||
{
|
||||
Elf64_Addr addr;
|
||||
#if defined(__x86_64__)
|
||||
__asm__ __volatile__ (
|
||||
"leaq _start(%%rip), %0"
|
||||
: "=r" (addr)
|
||||
:
|
||||
: "cc"
|
||||
);
|
||||
#elif defined(__loongarch_lp64)
|
||||
__asm__ __volatile__ (
|
||||
"la.pcrel %0, _start"
|
||||
: "=r" (addr)
|
||||
:
|
||||
: "memory"
|
||||
);
|
||||
#endif
|
||||
return addr;
|
||||
}
|
||||
|
||||
@ -86,12 +97,22 @@ static inline Elf64_Addr __attribute__ ((unused)) get_load_address(void)
|
||||
static inline Elf64_Addr __attribute__ ((unused)) get_dynamic_section_offset(void)
|
||||
{
|
||||
Elf64_Addr offs;
|
||||
#if defined(__x86_64__)
|
||||
__asm__ __volatile__ (
|
||||
"movq _GLOBAL_OFFSET_TABLE_(%%rip), %0"
|
||||
: "=r" (offs)
|
||||
:
|
||||
: "cc"
|
||||
);
|
||||
#elif defined(__loongarch_lp64)
|
||||
__asm__ __volatile__ (
|
||||
"la.pcrel $t0, _GLOBAL_OFFSET_TABLE_ \n\t"
|
||||
"ld.d %0, $t0, 0x0"
|
||||
: "=r" (offs)
|
||||
:
|
||||
: "$t0", "memory"
|
||||
);
|
||||
#endif
|
||||
return offs;
|
||||
}
|
||||
|
||||
@ -120,7 +141,8 @@ static void get_dynamic_info(Elf64_Dyn *dyn_section, Elf64_Addr load_offs, Elf64
|
||||
static void do_relocation(Elf64_Addr load_addr, Elf64_Addr load_offs, const Elf64_Rela *rel)
|
||||
{
|
||||
Elf64_Addr *target_addr = (Elf64_Addr *)(load_addr + rel->r_offset);
|
||||
if (ELF64_R_TYPE(rel->r_info) == R_X86_64_RELATIVE) {
|
||||
if ((ELF64_R_TYPE(rel->r_info) == R_X86_64_RELATIVE) ||
|
||||
(ELF64_R_TYPE(rel->r_info) == R_LARCH_RELATIVE)) {
|
||||
if (load_offs == load_addr) {
|
||||
*target_addr = load_addr + rel->r_addend;
|
||||
} else {
|
||||
@ -128,7 +150,8 @@ static void do_relocation(Elf64_Addr load_addr, Elf64_Addr load_offs, const Elf6
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (ELF64_R_TYPE(rel->r_info) == R_X86_64_NONE) {
|
||||
if ((ELF64_R_TYPE(rel->r_info) == R_X86_64_NONE) ||
|
||||
(ELF64_R_TYPE(rel->r_info) == R_LARCH_NONE)) {
|
||||
return;
|
||||
}
|
||||
assert(! "unexpected dynamic reloc type");
|
||||
|
@ -13,6 +13,11 @@
|
||||
#include "config.h"
|
||||
#include "display.h"
|
||||
|
||||
#ifdef __loongarch_lp64
|
||||
#include "vmem.h"
|
||||
#include <larchintrin.h>
|
||||
#endif
|
||||
|
||||
static struct serial_port console_serial;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -101,7 +106,13 @@ void tty_init(void)
|
||||
unsigned char lcr;
|
||||
|
||||
console_serial.enable = true;
|
||||
#ifdef __loongarch_lp64
|
||||
console_serial.base_addr = map_region(tty_address, 0x0, false);
|
||||
// By default, CPU UART0 is used, which uses the stable counter as the clock.
|
||||
tty_mmio_ref_clk = (__cpucfg(0x4) * (__cpucfg(0x5) & 0xFFFF)) / ((__cpucfg(0x5) >> 16) & 0xFFFF);
|
||||
#else
|
||||
console_serial.base_addr = tty_address;
|
||||
#endif
|
||||
console_serial.baudrate = tty_baud_rate;
|
||||
console_serial.parity = SERIAL_DEFAULT_PARITY;
|
||||
console_serial.bits = SERIAL_DEFAULT_BITS;
|
||||
|
203
system/smp.c
203
system/smp.c
@ -14,6 +14,11 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(__loongarch_lp64)
|
||||
#include "registers.h"
|
||||
#include <larchintrin.h>
|
||||
#endif
|
||||
|
||||
#include "acpi.h"
|
||||
#include "boot.h"
|
||||
#include "macros.h"
|
||||
@ -29,6 +34,7 @@
|
||||
#include "string.h"
|
||||
#include "unistd.h"
|
||||
#include "vmem.h"
|
||||
#include "pmem.h"
|
||||
|
||||
#include "smp.h"
|
||||
|
||||
@ -94,6 +100,7 @@
|
||||
|
||||
#define MADT_PROCESSOR 0
|
||||
#define MADT_LAPIC_ADDR 5
|
||||
#define MADT_CORE_PIC 17
|
||||
|
||||
// MADT processor flag values
|
||||
|
||||
@ -208,6 +215,7 @@ typedef struct {
|
||||
uint8_t length;
|
||||
} madt_entry_header_t;
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
typedef struct {
|
||||
uint8_t type;
|
||||
uint8_t length;
|
||||
@ -215,6 +223,18 @@ typedef struct {
|
||||
uint8_t apic_id;
|
||||
uint32_t flags;
|
||||
} madt_processor_entry_t;
|
||||
#elif defined(__loongarch_lp64)
|
||||
#pragma pack(1)
|
||||
typedef struct {
|
||||
uint8_t type;
|
||||
uint8_t length;
|
||||
uint8_t version;
|
||||
uint32_t processor_id;
|
||||
uint32_t core_id;
|
||||
uint32_t flags;
|
||||
} madt_processor_entry_t;
|
||||
#pragma pack ()
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint8_t type;
|
||||
@ -301,9 +321,11 @@ static uintptr_t alloc_addr = 0;
|
||||
// Variables
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int num_available_cpus = 1; // There is always at least one CPU, the BSP
|
||||
int num_memory_affinity_ranges = 0;
|
||||
int num_proximity_domains = 0;
|
||||
int num_available_cpus = 1; // There is always at least one CPU, the BSP
|
||||
int num_memory_affinity_ranges = 0;
|
||||
int num_proximity_domains = 0;
|
||||
bool map_numa_memory_range = false;
|
||||
uint8_t highest_map_bit = 0;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Private Functions
|
||||
@ -311,9 +333,14 @@ int num_proximity_domains = 0;
|
||||
|
||||
static int my_apic_id(void)
|
||||
{
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
return read32(&apic[APIC_REG_ID][0]) >> 24;
|
||||
#elif defined(__loongarch_lp64)
|
||||
return ((int)__csrrd_w(0x20));
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
static void apic_write(int reg, uint32_t val)
|
||||
{
|
||||
write32(&apic[reg][0], val);
|
||||
@ -445,6 +472,7 @@ static bool find_cpus_in_floating_mp_struct(void)
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool find_cpus_in_madt(void)
|
||||
{
|
||||
@ -470,6 +498,7 @@ static bool find_cpus_in_madt(void)
|
||||
uint8_t *mpc_table_end = (uint8_t *)mpc + mpc->h.length;
|
||||
while (tab_entry_ptr < mpc_table_end) {
|
||||
madt_entry_header_t *entry_header = (madt_entry_header_t *)tab_entry_ptr;
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
if (entry_header->type == MADT_PROCESSOR) {
|
||||
if (entry_header->length != sizeof(madt_processor_entry_t)) {
|
||||
return false;
|
||||
@ -493,6 +522,21 @@ static bool find_cpus_in_madt(void)
|
||||
madt_lapic_addr_entry_t *entry = (madt_lapic_addr_entry_t *)tab_entry_ptr;
|
||||
apic_addr = (uintptr_t)entry->lapic_addr;
|
||||
}
|
||||
#elif defined(__loongarch_lp64)
|
||||
if (entry_header->type == MADT_CORE_PIC) {
|
||||
madt_processor_entry_t *entry = (madt_processor_entry_t *)tab_entry_ptr;
|
||||
if (entry->flags & (MADT_PF_ENABLED|MADT_PF_ONLINE_CAPABLE)) {
|
||||
if (num_available_cpus < MAX_CPUS) {
|
||||
cpu_num_to_apic_id[found_cpus] = entry->core_id;
|
||||
// The first CPU is the BSP, don't increment.
|
||||
if (found_cpus > 0) {
|
||||
num_available_cpus++;
|
||||
}
|
||||
}
|
||||
found_cpus++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
tab_entry_ptr += entry_header->length;
|
||||
}
|
||||
|
||||
@ -682,11 +726,26 @@ static bool parse_slit(uintptr_t slit_addr)
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void send_ipi(int apic_id, int trigger, int level, int mode, uint8_t vector)
|
||||
static inline void send_ipi(int apic_id, int trigger __attribute__((unused)), int level __attribute__((unused)), int mode, uint8_t vector)
|
||||
{
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
apic_write(APIC_REG_ICRHI, apic_id << 24);
|
||||
|
||||
apic_write(APIC_REG_ICRLO, trigger << 15 | level << 14 | mode << 8 | vector);
|
||||
#elif defined(__loongarch_lp64)
|
||||
if (mode == APIC_DELMODE_STARTUP) {
|
||||
//
|
||||
// Set the AP mailbox0
|
||||
//
|
||||
__iocsrwr_d((1ULL << 31 | ((0x0 << 1) + 1) << 2 | apic_id << 16 | (((uintptr_t)ap_startup_addr) & 0xFFFFFFFF00000000ULL)), 0x1048);
|
||||
__iocsrwr_d((1ULL << 31 | (0x0 << 1) << 2 | apic_id << 16 | (((uintptr_t)ap_startup_addr << 32))), 0x1048);
|
||||
}
|
||||
|
||||
//
|
||||
// Trigger IPI
|
||||
//
|
||||
__iocsrwr_d((1<<31 | apic_id << 16 | vector), 0x1040);
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool send_ipi_and_wait(int apic_id, int trigger, int level, int mode, uint8_t vector, int delay_before_poll)
|
||||
@ -697,6 +756,7 @@ static bool send_ipi_and_wait(int apic_id, int trigger, int level, int mode, uin
|
||||
|
||||
// Wait for send complete or timeout after 100ms.
|
||||
int timeout = 1000;
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
while (timeout > 0) {
|
||||
bool send_pending = (apic_read(APIC_REG_ICRLO) & APIC_ICR_BUSY);
|
||||
if (!send_pending) {
|
||||
@ -706,8 +766,16 @@ static bool send_ipi_and_wait(int apic_id, int trigger, int level, int mode, uin
|
||||
timeout--;
|
||||
}
|
||||
return false;
|
||||
#elif defined(__loongarch_lp64)
|
||||
while (timeout > 0) {
|
||||
usleep(100);
|
||||
timeout--;
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
static uint32_t read_apic_esr(bool is_p5)
|
||||
{
|
||||
if (!is_p5) {
|
||||
@ -769,6 +837,108 @@ static bool start_cpu(int cpu_num)
|
||||
|
||||
return true;
|
||||
}
|
||||
#elif defined(__loongarch_lp64)
|
||||
static bool start_cpu(int cpu_num)
|
||||
{
|
||||
int apic_id = cpu_num_to_apic_id[cpu_num];
|
||||
bool use_long_delays = false;
|
||||
|
||||
// Send the STARTUP IPI.
|
||||
if (!send_ipi_and_wait(apic_id, 0, 0, APIC_DELMODE_STARTUP, 0, use_long_delays ? 300 : 10)) {
|
||||
return false;
|
||||
}
|
||||
// Give the other CPU some time to accept the IPI.
|
||||
usleep(use_long_delays ? 200 : 10);
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__loongarch_lp64)
|
||||
uint8_t checkout_max_memory_bits_of_this_numa_node(uint8_t range)
|
||||
{
|
||||
uint64_t max_memory_range = memory_affinity_ranges[range].end & (~(0xFULL << 44));
|
||||
uint8_t bits = 0;
|
||||
|
||||
if (max_memory_range > 0x0) {
|
||||
do {
|
||||
bits++;
|
||||
max_memory_range = max_memory_range >> 1;
|
||||
} while (max_memory_range > 0x1);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
void map_the_numa_memory_range(uint8_t highest_bit)
|
||||
{
|
||||
uint8_t i, node_nu;
|
||||
uint8_t node_offset = 44;
|
||||
|
||||
//
|
||||
// First step, map the pm_map.
|
||||
//
|
||||
for (i = 0; i < pm_map_size; i++) {
|
||||
node_nu = (pm_map[i].start >> (node_offset - PAGE_SHIFT)) & 0xF;
|
||||
if (node_nu != 0) {
|
||||
pm_map[i].start &= ~((uint64_t)0xF << (node_offset - PAGE_SHIFT));
|
||||
pm_map[i].start |= node_nu << (highest_bit - PAGE_SHIFT);
|
||||
|
||||
pm_map[i].end &= ~((uint64_t)0xF << (node_offset - PAGE_SHIFT));
|
||||
pm_map[i].end |= node_nu << (highest_bit - PAGE_SHIFT);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Second step, map the memory_affinity_ranges.
|
||||
//
|
||||
for (i = 0; i < num_memory_affinity_ranges; i++) {
|
||||
if (memory_affinity_ranges[i].proximity_domain_idx != 0) {
|
||||
node_nu = (memory_affinity_ranges[i].start >> node_offset) & 0xF;
|
||||
if (node_nu != 0) {
|
||||
memory_affinity_ranges[i].start &= ~((uint64_t)0xF << node_offset);
|
||||
memory_affinity_ranges[i].start |= node_nu << highest_bit;
|
||||
memory_affinity_ranges[i].end &= ~((uint64_t)0xF << node_offset);
|
||||
memory_affinity_ranges[i].end |= node_nu << highest_bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void check_if_needs_to_map(void)
|
||||
{
|
||||
uint8_t i, local_memory_area_bits;
|
||||
uint8_t max_memory_bits = 0x0;
|
||||
|
||||
if (num_proximity_domains == 0x0) {
|
||||
return;
|
||||
} else {
|
||||
for (i = 0; i < num_memory_affinity_ranges; i++) {
|
||||
if (memory_affinity_ranges[i].proximity_domain_idx != 0) {
|
||||
local_memory_area_bits = checkout_max_memory_bits_of_this_numa_node(i);
|
||||
if (max_memory_bits < local_memory_area_bits) {
|
||||
max_memory_bits = local_memory_area_bits;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (max_memory_bits > 0) {
|
||||
map_numa_memory_range = true;
|
||||
highest_map_bit = max_memory_bits + 1;
|
||||
map_the_numa_memory_range(highest_map_bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
void check_if_needs_to_map(void)
|
||||
{
|
||||
//
|
||||
// It is an empty function if not LoongArch64.
|
||||
//
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Functions
|
||||
@ -805,6 +975,7 @@ void smp_init(bool smp_enable)
|
||||
num_memory_affinity_ranges = 0;
|
||||
num_proximity_domains = 0;
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
if (cpuid_info.flags.x2apic) {
|
||||
uint32_t msrl, msrh;
|
||||
rdmsr(MSR_IA32_APIC_BASE, msrl, msrh);
|
||||
@ -813,6 +984,7 @@ void smp_init(bool smp_enable)
|
||||
smp_enable = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Process SMP Quirks
|
||||
if (quirk.type & QUIRK_TYPE_SMP) {
|
||||
@ -821,7 +993,11 @@ void smp_init(bool smp_enable)
|
||||
}
|
||||
|
||||
if (smp_enable) {
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
(void)(find_cpus_in_madt() || find_cpus_in_floating_mp_struct());
|
||||
#else
|
||||
find_cpus_in_madt();
|
||||
#endif
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_available_cpus; i++) {
|
||||
@ -829,7 +1005,9 @@ void smp_init(bool smp_enable)
|
||||
}
|
||||
|
||||
if (smp_enable) {
|
||||
if (!find_numa_nodes_in_srat()) {
|
||||
if (find_numa_nodes_in_srat()) {
|
||||
check_if_needs_to_map();
|
||||
} else {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
@ -843,12 +1021,17 @@ void smp_init(bool smp_enable)
|
||||
// These need to remain pinned in place during relocation.
|
||||
smp_heap_page = heap_alloc(HEAP_TYPE_LM_1, PAGE_SIZE, PAGE_SIZE) >> PAGE_SHIFT;
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
ap_startup_addr = (uintptr_t)startup;
|
||||
|
||||
size_t ap_trampoline_size = ap_trampoline_end - ap_trampoline;
|
||||
memcpy((uint8_t *)HEAP_BASE_ADDR, ap_trampoline, ap_trampoline_size);
|
||||
|
||||
alloc_addr = HEAP_BASE_ADDR + ap_trampoline_size;
|
||||
#elif defined(__loongarch_lp64)
|
||||
ap_startup_addr = (uintptr_t)startup64;
|
||||
alloc_addr = HEAP_BASE_ADDR;
|
||||
#endif
|
||||
}
|
||||
|
||||
int smp_start(cpu_state_t cpu_state[MAX_CPUS])
|
||||
@ -876,6 +1059,14 @@ int smp_start(cpu_state_t cpu_state[MAX_CPUS])
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__loongarch_lp64)
|
||||
//
|
||||
// MP sync the PMCNT0 with AP
|
||||
//
|
||||
__csrxchg_d(1 << 16, (1 << 16 | 0x3FF), LOONGARCH_CSR_PERFCTRL0);
|
||||
__csrwr_d(0x0, LOONGARCH_CSR_PERFCNTR0);
|
||||
#endif
|
||||
|
||||
#if SEQUENTIAL_AP_START
|
||||
return 0;
|
||||
#else
|
||||
@ -896,9 +1087,11 @@ int smp_start(cpu_state_t cpu_state[MAX_CPUS])
|
||||
|
||||
void smp_send_nmi(int cpu_num)
|
||||
{
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
while (apic_read(APIC_REG_ICRLO) & APIC_ICR_BUSY) {
|
||||
__builtin_ia32_pause();
|
||||
}
|
||||
#endif
|
||||
send_ipi(cpu_num_to_apic_id[cpu_num], 0, 0, APIC_DELMODE_NMI, 0);
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2020-2022 Martin Whitaker.
|
||||
// Copyright (C) 2004-2022 Sam Demeulemeester.
|
||||
// Copyright (C) 2024 Loongson Technology Corporation Limited. All rights reserved.
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
@ -12,6 +13,11 @@
|
||||
#include "io.h"
|
||||
#include "tsc.h"
|
||||
|
||||
#if defined(__loongarch_lp64)
|
||||
// LoongArch GCC builtin function
|
||||
#include <larchintrin.h>
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Constants
|
||||
//------------------------------------------------------------------------------
|
||||
@ -23,6 +29,7 @@
|
||||
// Private Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
static void correct_tsc(void)
|
||||
{
|
||||
uint32_t start_time, end_time, run_time, counter;
|
||||
@ -85,6 +92,33 @@ static void correct_tsc(void)
|
||||
clks_per_msec = run_time / 50;
|
||||
}
|
||||
}
|
||||
#elif defined(__loongarch_lp64)
|
||||
static void correct_tsc(void)
|
||||
{
|
||||
uint64_t start, end, excepted_ticks, current_ticks, calc_base_freq, clock_multiplier, clock_divide;
|
||||
uint64_t num_millisec = 50, millisec_div = 1000;
|
||||
|
||||
//
|
||||
// Get stable count frequency
|
||||
//
|
||||
calc_base_freq = __cpucfg(0x4);
|
||||
clock_multiplier = __cpucfg(0x5) & 0xFFFF;
|
||||
clock_divide = (__cpucfg(0x5) >> 16) & 0xFFFF;
|
||||
|
||||
excepted_ticks = (((calc_base_freq * clock_multiplier) / clock_divide) * num_millisec) / millisec_div;
|
||||
|
||||
__asm__ __volatile__("rdtime.d %0, $zero":"=r"(current_ticks):);
|
||||
excepted_ticks += current_ticks;
|
||||
|
||||
start = __csrrd_d(0x201);
|
||||
do {
|
||||
__asm__ __volatile__("rdtime.d %0, $zero":"=r"(current_ticks):);
|
||||
} while (current_ticks < excepted_ticks);
|
||||
end = __csrrd_d(0x201);
|
||||
|
||||
clks_per_msec = (end - start) / num_millisec;
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Functions
|
||||
|
15
system/tsc.h
15
system/tsc.h
@ -28,6 +28,7 @@
|
||||
/**
|
||||
* Reads and returns the timestamp counter value.
|
||||
*/
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
static inline uint64_t get_tsc(void)
|
||||
{
|
||||
uint32_t tl;
|
||||
@ -36,5 +37,19 @@ static inline uint64_t get_tsc(void)
|
||||
rdtsc(tl, th);
|
||||
return (uint64_t)th << 32 | (uint64_t)tl;
|
||||
}
|
||||
#else
|
||||
static inline uint64_t get_tsc(void)
|
||||
{
|
||||
uint64_t val = 0;
|
||||
|
||||
__asm__ __volatile__(
|
||||
"csrrd %0, 0x201\n\t"
|
||||
: "=r"(val)
|
||||
:
|
||||
);
|
||||
|
||||
return val;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // TSC_H
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "screen.h"
|
||||
#include "usb.h"
|
||||
#include "vmem.h"
|
||||
#include "hwquirks.h"
|
||||
|
||||
#include "ehci.h"
|
||||
#include "ohci.h"
|
||||
@ -433,6 +434,14 @@ static void reset_usb_controller(hci_info_t *hci)
|
||||
} else {
|
||||
mmio_size += (uintptr_t)0xffffffff << 32;
|
||||
}
|
||||
#if defined(__loongarch_lp64)
|
||||
base_addr |= (0xEULL << 40); // LoongArch64 64-bit PCI MMIO perfix
|
||||
|
||||
// Adjust Loongson7A2000 OHCI BAR offset.
|
||||
if ((device_id == 0x7a24) && (pci_config_read8(bus, dev, func, 0x08) == 0x2)) {
|
||||
base_addr += 0x1000;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
base_addr &= ~(uintptr_t)0xf;
|
||||
mmio_size &= ~(uintptr_t)0xf;
|
||||
@ -834,6 +843,10 @@ void find_usb_keyboards(bool pause_if_none)
|
||||
|
||||
hci_info_t hci_list[MAX_HCI];
|
||||
|
||||
if ((quirk.type & QUIRK_TYPE_USB) && (quirk.process != NULL)) {
|
||||
quirk.process();
|
||||
}
|
||||
|
||||
int num_hci = find_usb_controllers(hci_list);
|
||||
|
||||
// Take ownership of all controllers and reset them.
|
||||
|
@ -1,5 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2020-2022 Martin Whitaker.
|
||||
// Copyright (C) 2024 Loongson Technology Corporation Limited. All rights reserved.
|
||||
//
|
||||
// Derived from an extract of memtest86+ test.c:
|
||||
//
|
||||
@ -113,7 +114,7 @@ int test_block_move(int my_cpu, int iterations)
|
||||
continue;
|
||||
}
|
||||
test_addr[my_cpu] = (uintptr_t)p;
|
||||
#ifdef __x86_64__
|
||||
#if defined(__x86_64__)
|
||||
__asm__ __volatile__ (
|
||||
"cld\n"
|
||||
"jmp L110\n\t"
|
||||
@ -151,7 +152,7 @@ int test_block_move(int my_cpu, int iterations)
|
||||
:: "g" (p), "g" (pm), "g" (half_length)
|
||||
: "rdi", "rsi", "rcx"
|
||||
);
|
||||
#else
|
||||
#elif defined(__i386__)
|
||||
__asm__ __volatile__ (
|
||||
"cld\n"
|
||||
"jmp L110\n\t"
|
||||
@ -189,6 +190,59 @@ int test_block_move(int my_cpu, int iterations)
|
||||
:: "g" (p), "g" (pm), "g" (half_length)
|
||||
: "edi", "esi", "ecx"
|
||||
);
|
||||
#elif defined(__loongarch_lp64)
|
||||
__asm__ __volatile__ (
|
||||
"b L110\n"
|
||||
|
||||
".p2align 4,,8\n\t"
|
||||
"L110:\n\t"
|
||||
|
||||
// At the end of all this
|
||||
// - the second half equals the initial value of the first half
|
||||
// - the first half is right shifted 64-bytes (with wrapping)
|
||||
|
||||
// Move first half to second half
|
||||
"move $t1, %1\n\t" // Destination, pm (mid point)
|
||||
"move $t0, %0\n\t" // Source, p (start point)
|
||||
"move $t2, %2\n\t" // Length, half_length (size of a half in DWORDS)
|
||||
"first_loop:\n\t"
|
||||
"ld.d $t3, $t0, 0x0\n\t"
|
||||
"st.d $t3, $t1, 0x0\n\t"
|
||||
"addi.d $t0, $t0, 0x8\n\t"
|
||||
"addi.d $t1, $t1, 0x8\n\t"
|
||||
"addi.d $t2, $t2, -0x1\n\t"
|
||||
"bnez $t2, first_loop\n\t"
|
||||
|
||||
// Move the second half, less the last 64 bytes, to the first half, offset plus 64 bytes
|
||||
"move $t1, %0\n\t"
|
||||
"addi.d $t1, $t1, 64\n\t" // Destination, p (start-point) plus 32 bytes
|
||||
"move $t0, %1\n\t" // Source, pm (mid-point)
|
||||
"move $t2, %2\n\t"
|
||||
"addi.d $t2, $t2, -0x8\n\t" // Length, half_length (size of a half in QWORDS) minus 8 QWORDS (64 bytes)
|
||||
"second_loop:\n\t"
|
||||
"ld.d $t3, $t0, 0x0\n\t"
|
||||
"st.d $t3, $t1, 0x0\n\t"
|
||||
"addi.d $t0, $t0, 0x8\n\t"
|
||||
"addi.d $t1, $t1, 0x8\n\t"
|
||||
"addi.d $t2, $t2, -0x1\n\t"
|
||||
"bnez $t2, second_loop\n\t"
|
||||
|
||||
|
||||
// Move last 8 QWORDS (64 bytes) of the second half to the start of the first half
|
||||
"move $t1, %0\n\t" // Destination, p(start-point)
|
||||
// Source, 8 QWORDS from the end of the second half, left over by the last rep/movsl
|
||||
"li.d $t2, 0x8\n\t"
|
||||
"last_loop:\n\t"
|
||||
"ld.d $t3, $t0, 0x0\n\t"
|
||||
"st.d $t3, $t1, 0x0\n\t"
|
||||
"addi.d $t0, $t0, 0x8\n\t"
|
||||
"addi.d $t1, $t1, 0x8\n\t"
|
||||
"addi.d $t2, $t2, -0x1\n\t"
|
||||
"bnez $t2, last_loop\n\t"
|
||||
|
||||
:: "r" (p), "r" (pm), "r" (half_length)
|
||||
: "$t0", "$t1", "$t2", "$t3"
|
||||
);
|
||||
#endif
|
||||
do_tick(my_cpu);
|
||||
BAILOUT;
|
||||
|
@ -1,5 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2020-2022 Martin Whitaker.
|
||||
// Copyright (C) 2024 Loongson Technology Corporation Limited. All rights reserved.
|
||||
//
|
||||
// Derived from an extract of memtest86+ test.c:
|
||||
//
|
||||
@ -61,7 +62,7 @@ int test_mov_inv_fixed(int my_cpu, int iterations, testword_t pattern1, testword
|
||||
}
|
||||
test_addr[my_cpu] = (uintptr_t)p;
|
||||
#if HAND_OPTIMISED
|
||||
#ifdef __x86_64__
|
||||
#if defined(__x86_64__)
|
||||
uint64_t length = pe - p + 1;
|
||||
__asm__ __volatile__ ("\t"
|
||||
"rep \n\t"
|
||||
@ -71,7 +72,7 @@ int test_mov_inv_fixed(int my_cpu, int iterations, testword_t pattern1, testword
|
||||
:
|
||||
);
|
||||
p = pe;
|
||||
#else
|
||||
#elif defined(__i386__)
|
||||
uint32_t length = pe - p + 1;
|
||||
__asm__ __volatile__ ("\t"
|
||||
"rep \n\t"
|
||||
@ -81,6 +82,19 @@ int test_mov_inv_fixed(int my_cpu, int iterations, testword_t pattern1, testword
|
||||
:
|
||||
);
|
||||
p = pe;
|
||||
#elif defined(__loongarch_lp64)
|
||||
uint64_t length = pe - p + 1;
|
||||
__asm__ __volatile__ ("\t"
|
||||
"loop: \n\t"
|
||||
"st.d %2, %1, 0x0 \n\t"
|
||||
"addi.d %1, %1, 0x8 \n\t"
|
||||
"addi.d %0, %0, -0x1 \n\t"
|
||||
"bnez %0, loop \n\t"
|
||||
:
|
||||
: "r" (length), "r" (p), "r" (pattern1)
|
||||
: "memory"
|
||||
);
|
||||
p = pe;
|
||||
#endif
|
||||
#else
|
||||
do {
|
||||
|
Loading…
Reference in New Issue
Block a user