riscv: Initial support for debug trigger module

Implement up_debugpoint_add/up_debugpoint_remove for riscv.

Signed-off-by: Huang Qi <huangqi3@xiaomi.com>
This commit is contained in:
Huang Qi 2024-06-24 17:41:46 +08:00 committed by Xiang Xiao
parent a8523f30ea
commit e047ab9c70
5 changed files with 427 additions and 0 deletions

View File

@ -89,6 +89,7 @@ config ARCH_CHIP_ESP32C3
select ARCH_HAVE_TEXT_HEAP
select ARCH_HAVE_BOOTLOADER
select ARCH_HAVE_PERF_EVENTS
select ARCH_HAVE_DEBUG
---help---
Espressif ESP32-C3 (RV32IMC).
@ -118,6 +119,7 @@ config ARCH_CHIP_ESP32C3_GENERIC
select LIBC_ARCH_STRNLEN
select ESPRESSIF_SOC_RTC_MEM_SUPPORTED
select ARCH_CHIP_ESPRESSIF
select ARCH_HAVE_DEBUG
---help---
ESP32-C3 chip with a single RISC-V IMC core, no embedded Flash memory
@ -237,6 +239,7 @@ config ARCH_CHIP_QEMU_RV
select ARCH_HAVE_ELF_EXECUTABLE
select ONESHOT
select ALARM_ARCH
select ARCH_HAVE_DEBUG
---help---
QEMU Generic RV32/RV64 processor

View File

@ -311,6 +311,11 @@
#define CSR_SCONTEXT 0x5a8 /* Supervisor Context */
#define CSR_HCONTEXT 0x5aa /* Hypervisor Context */
/* In tcontrol register */
#define CSR_TCONTROL_MTE (0x1 << 3) /* M-mode trigger enable */
#define CSR_TCONTROL_MPTE (0x1 << 7) /* M-mode previous trigger enable */
/* Debug interface CSRs */
#define CSR_DCSR 0x7b0 /* Debug Control and Status */

View File

@ -48,6 +48,10 @@ if(CONFIG_RISCV_MISALIGNED_HANDLER)
list(APPEND SRCS riscv_misaligned.c)
endif()
if(CONFIG_ARCH_HAVE_DEBUG)
list(APPEND SRCS riscv_debug.c)
endif()
if(NOT CONFIG_BUILD_FLAT)
list(APPEND SRCS riscv_task_start.c riscv_pthread_start.c
riscv_signal_dispatch.c)

View File

@ -50,6 +50,10 @@ ifeq ($(CONFIG_RISCV_MISALIGNED_HANDLER),y)
CMN_CSRCS += riscv_misaligned.c
endif
ifeq ($(CONFIG_ARCH_HAVE_DEBUG),y)
CMN_CSRCS += riscv_debug.c
endif
ifneq ($(CONFIG_BUILD_FLAT),y)
CMN_CSRCS += riscv_task_start.c
CMN_CSRCS += riscv_pthread_start.c

View File

@ -0,0 +1,411 @@
/****************************************************************************
* arch/risc-v/src/common/riscv_debug.c
*
* 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.
*
****************************************************************************/
/****************************************************************************
* Notice:
*
* This driver is based on the RISC-V Debug Specification, version 0.13.2.
* The latest version of the specification can be found at:
* https://github.com/riscv/riscv-debug-spec
*
* The 1.0 version of the specification is still in RC phase, so there are
* no chips that support it yet. The 0.13.2 version is the latest stable
* version and some chips support it (e.g. QEMU RV, ESP32C3, BL602 etc).
*
* So this driver may needs to be updated when there is a new chip that
* supports the 1.0 version of the specification.
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/arch.h>
#include <nuttx/kmalloc.h>
#include <arch/chip/chip.h>
#include <arch/csr.h>
#include <stdbool.h>
#include "riscv_internal.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Check the essential definition that must from chip vendor */
#define TRIGGER_TYPE_NONE 0 /* There is no trigger at this tselect */
#define TRIGGER_TYPE_LEGACY_SIFIVE 1 /* Legacy SiFive address match trigger */
#define TRIGGER_TYPE_ADDRESS_DATA 2 /* Address/data match trigger */
#define TRIGGER_TYPE_ICOUNT 3 /* Instruction count trigger */
#define TRIGGER_TYPE_ITRIGGER 4 /* Interrupt trigger */
#define TRIGGER_TYPE_ETRIGGER 5 /* Exception trigger */
#define MATCH_TYPE_EQUAL 0 /* Value equals to tdata2 */
#define MATCH_TYPE_TOPBITS 1 /* Match top M bits of tdata2 */
#define MATCH_TYPE_GREAT 2 /* Value great than tdata2 */
#define MATCH_TYPE_LESS 3 /* Value less than tdata2 */
#define MATCH_TYPE_LOWERHALF 4 /* Lower half of the value equals */
#define MATCH_TYPE_UPPERHALF 5 /* Upper half of the value equals */
#define ACTION_TYPE_EXCEPTION 0 /* Raise a breakpoint exception */
#define ACTION_TYPE_DEBUGMODE 1 /* Enter debug mode */
#define DMODE_TYPE_BOTH 0 /* Both Debug and M-mode can write the tdata */
#define DMODE_TYPE_ONLY 1 /* Only Debug Mode can write the tdata */
/****************************************************************************
* Private Type
****************************************************************************/
/* Trigger Match Control, from version 0.13.2.
* Read https://riscv.org/wp-content/uploads/2019/03/riscv-debug-release.pdf
* for more information
*/
union mcontrol
{
uintptr_t reg;
struct
{
uintptr_t load : 1;
uintptr_t store : 1;
uintptr_t execute : 1;
uintptr_t u : 1;
uintptr_t s : 1;
uintptr_t reserved0 : 1;
uintptr_t m : 1;
uintptr_t match : 4;
uintptr_t chain : 1;
uintptr_t action : 4;
uintptr_t sizelo : 2;
uintptr_t timing : 1;
uintptr_t select : 1;
uintptr_t hit : 1;
#ifdef CONFIG_ARCH_RV64
uintptr_t sizehi : 2;
uintptr_t reserved1 : 30;
#endif
uintptr_t maskmax : 6;
uintptr_t dmode : 1;
uintptr_t type : 4;
};
};
struct riscv_debug_trigger
{
int type; /* Trigger type */
void *address; /* Trigger address */
size_t size; /* Trigger region size */
debug_callback_t callback; /* Debug callback */
void *arg; /* Debug callback argument */
};
/****************************************************************************
* Private Data
****************************************************************************/
/* Save the trigger address info */
static int g_trigger_count = 0;
static struct riscv_debug_trigger *g_trigger_map;
static bool g_support_napot = false;
static bool g_debug_initiliazed = false;
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: riscv_debug_find_slot
*
* Description:
* Find the trigger slot by type, address and size, return the index of the
* slot or -ENOENT if not found. And if address is NULL meand to find the
* first empty slot.
****************************************************************************/
static int riscv_debug_find_slot(int type, void *address, size_t size)
{
int i;
for (i = 0; i < g_trigger_count; i++)
{
if (g_trigger_map[i].type == type &&
g_trigger_map[i].address == address &&
g_trigger_map[i].size == size)
{
return i;
}
}
return -ENOENT;
}
static int riscv_debug_handler(int irq, void *context, void *arg)
{
/* Get the trigger index */
int slot = READ_CSR(CSR_TSELECT);
DEBUGASSERT(slot >= 0);
DEBUGASSERT(slot < g_trigger_count);
/* Call the trigger callback */
if (g_trigger_map[slot].callback)
{
g_trigger_map[slot].callback(g_trigger_map[slot].type,
g_trigger_map[slot].address,
g_trigger_map[slot].size,
g_trigger_map[slot].arg);
}
return 0;
}
/****************************************************************************
* Name: riscv_debug_init
****************************************************************************/
static int riscv_debug_init(void)
{
union mcontrol mc;
/* Attach the debug exception handler */
irq_attach(RISCV_IRQ_BPOINT, riscv_debug_handler, NULL);
/* Detect the number of triggers by write a huge value
* to tselect and read it back
*/
WRITE_CSR(CSR_TSELECT, 0xffffffff);
g_trigger_count = READ_CSR(CSR_TSELECT);
if (g_trigger_count == 0)
{
return -ENOENT;
}
/* Allocate the trigger map */
g_trigger_map = kmm_zalloc(sizeof(struct riscv_debug_trigger) *
g_trigger_count);
if (!g_trigger_map)
{
return -ENOMEM;
}
/* Detect the support of NAPOT by trigger 0 */
WRITE_CSR(CSR_TSELECT, 0);
mc.reg = READ_CSR(CSR_TDATA1);
/* REVISIT: NAPOT match is supported and tested on
* QEMU and ESP32C3, prefer to use it.
*/
mc.match = MATCH_TYPE_TOPBITS;
/* Write it to tdata1 and read back
* to check if the NAPOT is supported
*/
WRITE_CSR(CSR_TDATA1, mc.reg);
mc.reg = READ_CSR(CSR_TDATA1);
if (mc.match == MATCH_TYPE_TOPBITS)
{
g_support_napot = true;
}
/* Special handling for QEMU since it does not implement
* the TCONTROL register, verified on QEMU 9.0.1.
*/
#ifndef CONFIG_ARCH_CHIP_QEMU_RV
/* Enable trigger in M-mode */
WRITE_CSR(CSR_TCONTROL, CSR_TCONTROL_MPTE | CSR_TCONTROL_MTE);
#endif
return 0;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: up_debugpoint_add
****************************************************************************/
int up_debugpoint_add(int type, void *addr, size_t size,
debug_callback_t callback, void *arg)
{
int slot;
union mcontrol mc;
int ret = OK;
uintptr_t addr_napot;
/* Initialize the debug module if it is not initialized yet */
if (g_debug_initiliazed == false)
{
ret = riscv_debug_init();
if (ret < 0)
{
return ret;
}
g_debug_initiliazed = true;
}
/* Find a free slot */
slot = riscv_debug_find_slot(0, 0, 0);
if (slot < 0)
{
return slot;
}
/* Select the trigger */
WRITE_CSR(CSR_TSELECT, slot);
/* Fetch the current setting from tdata1 */
mc.reg = READ_CSR(CSR_TDATA1);
/* Configure trigger */
mc.m = 1;
mc.u = 1;
mc.hit = 0;
mc.dmode = DMODE_TYPE_BOTH;
mc.action = ACTION_TYPE_EXCEPTION;
mc.execute = 0;
mc.load = 0;
mc.store = 0;
if (type == DEBUGPOINT_BREAKPOINT)
{
mc.execute = 1;
}
else if (type == DEBUGPOINT_WATCHPOINT_RO)
{
mc.load = 1;
}
else if (type == DEBUGPOINT_WATCHPOINT_WO)
{
mc.store = 1;
}
else if (type == DEBUGPOINT_WATCHPOINT_RW)
{
mc.load = 1;
mc.store = 1;
}
else
{
/* DEBUGPOINT_STEPPOINT is not supported since current test platform
* such as QEMU don't implemented yet.
*/
return -ENOTSUP;
}
/* From RISC-V Debug Specification:
* tdata1(mcontrol) match = 0 : Exact byte match
*
* tdata1(mcontrol) match = 1 : NAPOT (Naturally Aligned Power-Of-Two):
*
* Examples for understanding how to calculate match pattern to tdata2:
*
* nnnn...nnnnn 1-byte Exact byte match
* nnnn...nnnn0 2-byte NAPOT range
* nnnn...nnn01 4-byte NAPOT range
* nnnn...nn011 8-byte NAPOT range
* nnnn...n0111 16-byte NAPOT range
* nnnn...01111 32-byte NAPOT range
* ...
* n011...11111 2^31 byte NAPOT range
* where n are bits from original address
*/
if (size > 1 && g_support_napot)
{
mc.match = MATCH_TYPE_TOPBITS;
addr_napot = ((uintptr_t)addr & ~(size - 1)) |
((size - 1) >> 1);
WRITE_CSR(CSR_TDATA2, addr_napot);
}
else
{
mc.match = MATCH_TYPE_EQUAL;
WRITE_CSR(CSR_TDATA2, (uintptr_t)addr);
}
/* Register the callback and arg */
g_trigger_map[slot].type = type;
g_trigger_map[slot].address = addr;
g_trigger_map[slot].size = size;
g_trigger_map[slot].callback = callback;
g_trigger_map[slot].arg = arg;
WRITE_CSR(CSR_TDATA1, mc.reg);
return 0;
}
/****************************************************************************
* Name: up_debugpoint_remove
****************************************************************************/
int up_debugpoint_remove(int type, void *addr, size_t size)
{
int slot;
/* Find the existing debugpoint */
slot = riscv_debug_find_slot(type, addr, size);
if (slot < 0)
{
return slot;
}
/* Select the trigger and clear setting by write 0 to tdata1 */
WRITE_CSR(CSR_TSELECT, slot);
WRITE_CSR(CSR_TDATA1, 0);
/* Clear the callback and arg */
memset(&g_trigger_map[slot], 0, sizeof(g_trigger_map[slot]));
return 0;
}