Creating ESP32C3 custom panic handler for "ecall" calls

User avatar
EUtrilla2002
Posts: 18
Joined: Mon Mar 31, 2025 10:23 am

Creating ESP32C3 custom panic handler for "ecall" calls

Postby EUtrilla2002 » Fri Apr 25, 2025 11:30 am

Hi
I'm trying to be able to use ecall functions (or at least, fake them) inside my esp32c3 device.
At first, when we are flashing this type of assembly code

Code: Select all

.data
    msg:  .string "Hello world "

.text
main:
    la a0, msg
    li a7, 4
    ecall
     jr ra
it returs this kind of error:

Code: Select all

Guru Meditation Error: Core  0 panic'ed (Environment call from M-mode). Exception was unhandled.
My idea is to catch this interruptiomn, print the message (as it should do this type of system call) and return to the main program

Can someone give me an idea from where to start?

MicroController
Posts: 2661
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Creating ESP32C3 custom panic handler for "ecall" calls

Postby MicroController » Sun Apr 27, 2025 8:00 am

Core 0 panic'ed (Environment call from M-mode).
suggests that ecalls aren't supposed to be made in M-mode. By default, all of an IDF application runs in unrestricted M-mode; and that's what the IDF relies on.
I believe the C3 does support U-mode and ecall-switching to M-mode, but that doesn't seem to be supported by the IDF and its FreeRTOS at all.

MicroController
Posts: 2661
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Creating ESP32C3 custom panic handler for "ecall" calls

Postby MicroController » Mon Apr 28, 2025 12:54 pm

I have to correct myself: In the C3's TRM the mcause exception codes are documented:
0x8 ECALL from U mode
0xb ECALL from M mode
So, by the hardware, ecall should work in both modes, but apparently the IDF's default trap handler doesn't dispatch those mcauses anywhere and just treats them like a fault.

User avatar
EUtrilla2002
Posts: 18
Joined: Mon Mar 31, 2025 10:23 am

Re: Creating ESP32C3 custom panic handler for "ecall" calls

Postby EUtrilla2002 » Wed Apr 30, 2025 7:45 am

Thanks for the clarification!
So, the thing is that esp-idf does not have a trap handler for this interruptions. Cool.

Where should i start in order to implementate it, at least to trap it and print or read whatever i need in order to "simulate" ecall's use?

I tried to point mtvec to a custom function. This makes that guru's meditation dont appear, but ignores my code inside the trap handler.

Code: Select all

extern void exception_handler(void);

void setup_my_trap_handler() {
    uintptr_t handler_addr = (uintptr_t)exception_handler;
    __asm__ volatile("csrw mtvec, %0" :: "r"(handler_addr));
}

Code: Select all

.section .text
.globl exception_handler
exception_handler:
    # Look up exception 
    csrr t1, mcause

    # Ecall
    li t2, 11
    beq t1, t2, handle_ecall

    #Not ecall --> Prints A
    li t0, 0x60000000         # Dirección base UART0 (FIFO)
    li t3, 'A'                # Carácter a enviar
    sb t3, 0(t0)              # Escribir el byte
    j .

handle_ecall:
    # Prints 'E'
    li t0, 0x60000000         # Dirección base UART0 (FIFO)
    li t3, 'E'                
    sb t3, 0(t0)              

    # Return
    mret
Any idea??

MicroController
Posts: 2661
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Creating ESP32C3 custom panic handler for "ecall" calls

Postby MicroController » Wed Apr 30, 2025 9:44 am

1. mtvec is the address of the interrupt/trap vector table not a single handler, c.f. https://github.com/espressif/esp-idf/bl ... intc.S#L35
2. on the C3, the vector table must be 256-byte aligned.

Panic handler is here: https://github.com/espressif/esp-idf/bl ... ors.S#L211

You may have to modify the IDF panic handler, or 'wrap' it in a custom handler which delegates to the original panic handler if the mcause is not ecall-related.

User avatar
EUtrilla2002
Posts: 18
Joined: Mon Mar 31, 2025 10:23 am

Re: Creating ESP32C3 custom panic handler for "ecall" calls

Postby EUtrilla2002 » Mon May 05, 2025 11:42 am

hi! Thank you so so much for your help!!

Im having a little problem during this.

in panic_handler.c I modified xt_unhandled_exception like this

Code: Select all

void IRAM_ATTR xt_unhandled_exception(void *frame)
{
#if !CONFIG_APP_BUILD_TYPE_PURE_RAM_APP
    panic_enable_cache();
#endif
    int cause = panic_get_cause(frame);
    if (cause == 11 || cause == 8) { 
        uint32_t mepc = RV_READ_CSR(mepc);
        //panic_print_str("Excepción: ECALL detectada!\n");
        track_ecall(frame);  // Llamar a track_ecall
        return;
    }
    else 
    { 
        panic_handler(frame, false);
    }
}
and i made my own track ecall (and it kinda works, prints whatever is on a0)

Code: Select all


void track_ecall(const void *frame) {
    // Assuming the frame is a structure that contains CPU registers
    RvExcFrame *regs = (RvExcFrame *) frame;
    uint32_t a7 = regs->a7;

    //ESP_LOGI(TAG, "Panic handler invoked. Value of a7: %u", (unsigned int)a7);


    switch (a7){
        case 4:
            uint32_t a0 = regs->a0;
            if (a0 >= RAM_START && a0 < RAM_END) {
                const char *text = (const char *)a0;

                esp_rom_printf("%s\n", text);

                uint32_t mepc = RV_READ_CSR(mepc);
                uint32_t mepc_orig = mepc;
                
                uint16_t inst = *(uint16_t *)mepc;
                if ((inst & 0x3) != 0x3) {
                    mepc += 2;
                } else {
                    mepc += 4;
                }
                
                if (mepc != mepc_orig) {
                    ESP_LOGI(TAG, "si");
                } else {
                    ESP_LOGI(TAG, "no");
                }
                
                RV_WRITE_CSR(mepc, mepc);
            } else {
                ESP_LOGW(TAG, "a0 apunta a una dirección no válida: 0x%08X", (unsigned int)a0);
            }
            break;
        default:
            ESP_LOGI(TAG, "No special condition met for a7.");
            break;
    }

    // Continue with the default panic handler or halt
    //esp_restart(); // Restart the system after handling the panic
    return;
}
However, asi it is an exception after all, it beguins a loop inside the ecall instruction and i can't get out of it. What should i make in order to go to the following instruction of my code?

MicroController
Posts: 2661
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Creating ESP32C3 custom panic handler for "ecall" calls

Postby MicroController » Mon May 05, 2025 9:12 pm

You may be able to glean one way to wrap/extend the panic handler from e.g. the "memprot" test app in the IDF. (Done there via linker flag in the CMakeLists.txt.)
(I think for an ecall you have to always "return" to (mepc+4).)

User avatar
EUtrilla2002
Posts: 18
Joined: Mon Mar 31, 2025 10:23 am

Re: Creating ESP32C3 custom panic handler for "ecall" calls

Postby EUtrilla2002 » Fri May 09, 2025 7:14 am

Hi!!
Thank you so so much for your help :) i was able to finish it.I just added the file "return from panic" inside my custom panic handler hard-codded in my panic_handler.c. At least i have something to work on even though is hard-codded, im so grateful for that :)

However,the wrapper does not work for me , if i simulate the structure of the memprop_app and adding the linker in the outside CMakeFile

Code: Select all

main/
├── CMakeLists.txt
├── custom_panic.c
├── ecall_panic.c
├── main.c
├── program.s
├── return_from_panic.s
CMakeLists.txt
The outside CMake looks like this

Code: Select all

cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
target_link_libraries(${project_elf} PRIVATE
"-Wl,--wrap=esp_panic_handler")
project(hello_world)
It gives me this problem

Code: Select all

/home/{myname}/.espressif/tools/riscv32-esp-elf/esp-13.2.0_20240530/riscv32-esp-elf/bin/../lib/gcc/riscv32-esp-elf/13.2.0/../../../../riscv32-esp-elf/bin/ld: esp-idf/esp_system/libesp_system.a(panic_handler.c.obj): in function panic_handler':
/home/{myname}/esp/v5.3/esp-idf/components/esp_system/port/panic_handler.c:220:(.text.panic_handler+0x42): undefined reference to __wrap_esp_panic_handler'
collect2: error: ld returned 1 exit status
I tried also to insert the wrapper in the inside CMakeFile, but it's kinda silly, the wrapper does not create.
It looks like this

Code: Select all

idf_component_register(
   SRCS
      "program.s"
      "return_from_panic.S"
      "custom_panic.c"
      "ecall_panic.c"
 
   INCLUDE_DIRS
      "..")

# target_link_libraries(${project_elf} PRIVATE
# "-Wl,--wrap=esp_panic_handler"
# )

The wrapper code looks like this

Code: Select all

 #include "riscv/rvruntime-frames.h"
 #include "esp_private/panic_internal.h"
 #include "esp_private/panic_reason.h"
 #include "hal/wdt_hal.h"
 #include "creator/custom_panic.h"
 #include <stdio.h>
 extern void esp_panic_handler(panic_info_t *info);
 void return_from_panic_handler(RvExcFrame *frm) __attribute__((noreturn));
 
 void __real_esp_panic_handler(panic_info_t *info); 
 
 void __wrap_esp_panic_handler(panic_info_t *info)
 {
    printf("Panic handler personalizado llamado\n"); 
    RvExcFrame *frm = (RvExcFrame *)info->frame;
     if ( frm->mcause == 8 || frm->mcause == 11 ) {
         /* Return from exception to the return address that called the faulting function.*/
         track_ecall(frm);
         frm->mepc = frm->mepc + 4;
         /* Restore the CPU state and return from the exception.*/
         return_from_panic_handler(frm);
     } else {
         __real_esp_panic_handler(info);
     }
 }
What can be the cause of failing???

User avatar
EUtrilla2002
Posts: 18
Joined: Mon Mar 31, 2025 10:23 am

Re: Creating ESP32C3 custom panic handler for "ecall" calls

Postby EUtrilla2002 » Mon Sep 08, 2025 8:54 am

Hi
Two moths after i saw the error. I'm boo boo the fool.

In the outside CMakeList file, you chould add this:

Code: Select all

# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(hello_world)
target_link_libraries(${project_elf} PRIVATE "-Wl,--wrap=esp_panic_handler")
and on the inside CMakeList file, you should add the wrapper files.

Making this, maybe you can have problems with the watchdog. I directly disabled it, asi it's for educational purposes and i think (i hope) the student won't enter massive programs.

User avatar
EUtrilla2002
Posts: 18
Joined: Mon Mar 31, 2025 10:23 am

Re: Creating ESP32C3 custom panic handler for "ecall" calls

Postby EUtrilla2002 » Fri Oct 31, 2025 11:47 am

Hi again:

Just to inform you (and if someone can help me, is much thanked). There has been changes in panic.c that make this implementation troublesome with write-on-terminal versions.

In other words: My ecall implementation has a case like this

Code: Select all

            case 5: { // Read int
                int number_read = read_int(); //Function that ask uart a number + busy wait
                frm->a0 = number_read;
                break;
            }
as it seems it makes a double panic handle access, it appears this error while monitoring the program

Code: Select all

Panic handler entered multiple times. Abort panic handling. Rebooting ...
This is because panic.c has changed since v.5.3.2 (the version i was working here) due to this function

Code: Select all

void esp_panic_handler_increment_entry_count(void)
{
    int core_id = esp_cpu_get_core_id();

    g_panic_entry_count[core_id]++;
    if (g_panic_entry_count[core_id] > PANIC_ENTRY_COUNT_MAX) {
        /* If we have already panicked multiple times, chances are
         * that the panic handler itself is broken. In this case, we
         * should just reset the system.
         */
        panic_print_str("Panic handler entered multiple times. Abort panic handling. Rebooting ...\r\n");
        panic_restart();
    }
}
Here is the question: How can i make this ecall work for the new panic handler version the safest form possible?

Who is online

Users browsing this forum: PerplexityBot and 6 guests