arch: qemu-rv: Add M-timer handling for BUILD_KERNEL

Summary:
- In RISC-V, BUILD_KERNEL uses S-mode and to use M-mode timer
  we need to handle it by using OpenSBI or self-implementation.
- This commit adds M-timer self-implementation for BUILD_KERNEL.

Impact:
- qemu-rv only

Testing:
- Tested with rv-virt:knsh64 on qemu-6.2

Signed-off-by: Masayuki Ishikawa <Masayuki.Ishikawa@jp.sony.com>
This commit is contained in:
Masayuki Ishikawa 2022-10-03 12:15:01 +09:00 committed by Xiang Xiao
parent 34b05804b0
commit 2fa872e304
5 changed files with 279 additions and 19 deletions

View File

@ -30,6 +30,7 @@ CHIP_CSRCS += qemu_rv_timerisr.c qemu_rv_allocateheap.c
ifeq ($(CONFIG_BUILD_KERNEL),y)
CHIP_CSRCS += qemu_rv_mm_init.c
CMN_ASRCS += qemu_rv_exception_m.S
endif
ifeq ($(CONFIG_MM_PGALLOC),y)

View File

@ -0,0 +1,105 @@
/****************************************************************************
* arch/risc-v/src/qemu-rv/qemu_rv_exception_m.S
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <arch/arch.h>
#include <arch/irq.h>
#include <arch/mode.h>
#include <sys/types.h>
#include "chip.h"
#include "riscv_macros.S"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Provide a default section for the exeception handler. */
#ifndef EXCEPTION_SECTION
# define EXCEPTION_SECTION .text
#endif
/****************************************************************************
* Public Symbols
****************************************************************************/
.section .text
.balign 8
.global __trap_vec_m
/****************************************************************************
* Name: __trap_vec_m
*
* Description:
* All M-mode exceptions and interrupts will be handled from here. If
* kernel is in S-mode delegated exceptions and interrupts are handled.
*
****************************************************************************/
__trap_vec_m:
j exception_m
/****************************************************************************
* Name: exception_m
*
* Description:
* Handles interrupts for m-mode
*
****************************************************************************/
.section EXCEPTION_SECTION
.global exception_m
.align 8
exception_m:
/* Swap mscratch with sp */
/* NOTE: mscratch has been set in up_mtimer_initialize() */
csrrw sp, mscratch, sp
/* Save the context */
save_ctx sp
/* Handle the mtimer interrupt */
/* NOTE: we assume exception/interrupt only happens for mtimer */
jal ra, qemu_rv_mtimer_interrupt
/* Restore the context */
load_ctx sp
/* Swap mscratch with sp */
csrrw sp, mscratch, sp
/* Return from exception */
mret

View File

@ -163,6 +163,14 @@ void up_enable_irq(int irq)
SET_CSR(CSR_IE, IE_TIE);
}
#ifdef CONFIG_BUILD_KERNEL
else if (irq == RISCV_IRQ_MTIMER)
{
/* Read m/sstatus & set timer interrupt enable in m/sie */
SET_CSR(mie, MIE_MTIE);
}
#endif
else if (irq > RISCV_IRQ_EXT)
{
extirq = irq - RISCV_IRQ_EXT;

View File

@ -49,6 +49,34 @@
# error "Target requires kernel in S-mode, enable CONFIG_ARCH_USE_S_MODE"
#endif
/****************************************************************************
* Extern Function Declarations
****************************************************************************/
#ifdef CONFIG_BUILD_KERNEL
extern void __trap_vec(void);
extern void __trap_vec_m(void);
extern void up_mtimer_initialize(void);
#endif
/****************************************************************************
* Name: qemu_rv_clear_bss
****************************************************************************/
void qemu_rv_clear_bss(void)
{
uint32_t *dest;
/* Clear .bss. We'll do this inline (vs. calling memset) just to be
* certain that there are no issues with the state of global variables.
*/
for (dest = (uint32_t *)_sbss; dest < (uint32_t *)_ebss; )
{
*dest++ = 0;
}
}
/****************************************************************************
* Public Data
****************************************************************************/
@ -69,8 +97,6 @@ uintptr_t g_idle_topstack = QEMU_RV_IDLESTACK_TOP;
void qemu_rv_start(int mhartid)
{
uint32_t *dest;
/* Configure FPU */
riscv_fpuconfig();
@ -80,14 +106,9 @@ void qemu_rv_start(int mhartid)
goto cpux;
}
/* Clear .bss. We'll do this inline (vs. calling memset) just to be
* certain that there are no issues with the state of global variables.
*/
for (dest = (uint32_t *)_sbss; dest < (uint32_t *)_ebss; )
{
*dest++ = 0;
}
#ifndef CONFIG_BUILD_KERNEL
qemu_rv_clear_bss();
#endif
showprogress('A');
@ -99,12 +120,6 @@ void qemu_rv_start(int mhartid)
/* Do board initialization */
#ifdef CONFIG_ARCH_USE_S_MODE
/* Initialize the per CPU areas */
riscv_percpu_add_hart(mhartid);
#endif
showprogress('C');
#ifdef CONFIG_BUILD_KERNEL
@ -116,6 +131,7 @@ void qemu_rv_start(int mhartid)
nx_start();
cpux:
#ifdef CONFIG_SMP
riscv_cpu_boot(mhartid);
#endif
@ -126,9 +142,16 @@ cpux:
}
}
#ifdef CONFIG_ARCH_USE_S_MODE
#ifdef CONFIG_BUILD_KERNEL
void qemu_rv_start_s(int mhartid)
{
qemu_rv_clear_bss();
/* Initialize the per CPU areas */
riscv_percpu_add_hart(mhartid);
/* Disable MMU and enable PMP */
WRITE_CSR(satp, 0x0);
@ -151,13 +174,27 @@ void qemu_rv_start_s(int mhartid)
/* Set the trap vector for S-mode */
extern void __trap_vec(void);
WRITE_CSR(stvec, (uintptr_t)__trap_vec);
/* Set the trap vector for M-mode */
WRITE_CSR(mtvec, (uintptr_t)__trap_vec_m);
/* Initialize mtimer before entering to S-mode */
up_mtimer_initialize();
/* Set mepc to the entry */
WRITE_CSR(mepc, (uintptr_t)qemu_rv_start);
asm volatile("mret");
/* Set a0 to mhartid explicitly and enter to S-mode */
asm volatile (
"mv a0, %0 \n"
"mret \n"
:: "r" (mhartid)
);
}
#endif

View File

@ -37,6 +37,7 @@
#include "riscv_internal.h"
#include "riscv_mtimer.h"
#include "riscv_percpu.h"
#include "hardware/qemu_rv_memorymap.h"
#include "hardware/qemu_rv_clint.h"
@ -45,6 +46,55 @@
****************************************************************************/
#define MTIMER_FREQ 10000000
#define TICK_COUNT (10000000 / TICK_PER_SEC)
/****************************************************************************
* Private Functions
****************************************************************************/
#ifdef CONFIG_BUILD_KERNEL
/****************************************************************************
* Name: qemu_rv_ssoft_interrupt
*
* Description:
* This function is S-mode software interrupt handler to proceed
* the OS timer
*
****************************************************************************/
static int qemu_rv_ssoft_interrupt(int irq, void *context, void *arg)
{
/* Cleaer Supervisor Software Interrupt */
CLEAR_CSR(sip, SIP_SSIP);
/* Proceed the OS timer */
nxsched_process_timer();
return 0;
}
/****************************************************************************
* Name: qemu_rv_reload_mtimecmp
*
* Description:
* This function is called during start-up to initialize mtimecmp
* for CONFIG_BUILD_KERNEL=y
*
****************************************************************************/
static void qemu_rv_reload_mtimecmp(void)
{
uint64_t current;
uint64_t next;
current = READ_CSR(time);
next = current + TICK_COUNT;
putreg64(next, QEMU_RV_CLINT_MTIMECMP);
}
#endif /* CONFIG_BUILD_KERNEL */
/****************************************************************************
* Public Functions
@ -69,5 +119,64 @@ void up_timer_initialize(void)
DEBUGASSERT(lower);
up_alarm_set_lowerhalf(lower);
#else
/* NOTE: This function is called in S-mode */
irq_attach(RISCV_IRQ_SSOFT, qemu_rv_ssoft_interrupt, NULL);
up_enable_irq(RISCV_IRQ_SSOFT);
#endif
}
#ifdef CONFIG_BUILD_KERNEL
/****************************************************************************
* Name: up_mtimer_initialize
*
* Description:
* This function is called during start-up to initialize the M-mode timer
*
****************************************************************************/
void up_mtimer_initialize(void)
{
uintptr_t irqstacktop = riscv_percpu_get_irqstack();
/* Set the irq stack base to mscratch */
WRITE_CSR(mscratch,
irqstacktop - STACK_ALIGN_DOWN(CONFIG_ARCH_INTERRUPTSTACK));
/* NOTE: we do not attach a handler for mtimer,
* because it is handled in the exception_m directly
*/
up_enable_irq(RISCV_IRQ_MTIMER);
qemu_rv_reload_mtimecmp();
}
/****************************************************************************
* Name: qemu_rv_mtimer_interrupt
*
* Description:
* In RISC-V with S-mode, M-mode timer must be handled in M-mode
* This function is called from exception_m in M-mode directly
*
****************************************************************************/
void qemu_rv_mtimer_interrupt(void)
{
uint64_t current;
uint64_t next;
/* Update mtimercmp */
current = getreg64(QEMU_RV_CLINT_MTIMECMP);
next = current + TICK_COUNT;
putreg64(next, QEMU_RV_CLINT_MTIMECMP);
/* Post Supervisor Software Interrupt */
SET_CSR(sip, SIP_SSIP);
}
#endif /* CONFIG_BUILD_KERNEL */