feat(esp_hw_support): Add esp_cpu.h support for linux builds

This commit is contained in:
Guillaume Souchere
2026-03-16 09:11:14 +01:00
parent 642a2d2f54
commit 5415465164
7 changed files with 461 additions and 16 deletions
@@ -16,6 +16,7 @@
#include "soc/spi_periph.h"
#include "soc/soc_memory_layout.h"
#include "esp_private/cache_utils.h"
#include "esp_memory_utils.h"
#include "esp_private/spi_common_internal.h"
#include "esp_private/esp_clk.h"
#include "esp_private/sleep_cpu.h"
+4 -1
View File
@@ -3,9 +3,12 @@ idf_build_get_property(non_os_build NON_OS_BUILD)
# On Linux, we only support a few features, hence this simple component registration
if(${target} STREQUAL "linux")
# "include/linux" MUST come before "include" so that Linux-specific header
# overrides (e.g. esp_cpu.h) take precedence over the generic versions.
idf_component_register(SRCS "port/linux/esp_random.c"
"port/linux/chip_info.c"
INCLUDE_DIRS "include" "etm/include")
"port/linux/cpu.c"
INCLUDE_DIRS "include/linux" "include" "etm/include")
return()
endif()
@@ -0,0 +1,371 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* Linux/macOS host-only version of esp_cpu.h.
*
* This header provides stub/minimal implementations of the esp_cpu_*
* API for host builds. It is placed in an include directory that takes
* precedence over the generic esp_cpu.h for CONFIG_IDF_TARGET_LINUX.
*/
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include "soc/soc_caps.h"
#include "esp_err.h"
#include "esp_attr.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief CPU cycle count type
*/
typedef uint32_t esp_cpu_cycle_count_t;
/**
* @brief CPU interrupt type
*/
typedef enum {
ESP_CPU_INTR_TYPE_LEVEL = 0,
ESP_CPU_INTR_TYPE_EDGE,
ESP_CPU_INTR_TYPE_NA,
} esp_cpu_intr_type_t;
/**
* @brief CPU interrupt descriptor
*/
typedef struct {
int priority;
esp_cpu_intr_type_t type;
uint32_t flags;
} esp_cpu_intr_desc_t;
#define ESP_CPU_INTR_DESC_FLAG_SPECIAL 0x01
#define ESP_CPU_INTR_DESC_FLAG_RESVD 0x02
/**
* @brief CPU interrupt handler type
*/
typedef void (*esp_cpu_intr_handler_t)(void *arg);
/**
* @brief CPU watchpoint trigger type
*/
typedef enum {
ESP_CPU_WATCHPOINT_LOAD,
ESP_CPU_WATCHPOINT_STORE,
ESP_CPU_WATCHPOINT_ACCESS,
} esp_cpu_watchpoint_trigger_t;
/* --------------------------------------------------- CPU Control ----------------------------------------------------- */
/**
* @brief Stall a CPU core
*
* @param core_id The core's ID
*/
void esp_cpu_stall(int core_id);
/**
* @brief Resume a previously stalled CPU core
*
* @param core_id The core's ID
*/
void esp_cpu_unstall(int core_id);
/**
* @brief Reset a CPU core
*
* @param core_id The core's ID
*/
void esp_cpu_reset(int core_id);
/**
* @brief Wait for Interrupt
*
* This function causes the current CPU core to execute its Wait For Interrupt
* (WFI or equivalent) instruction. After executing this function, the CPU core
* will stop execution until an interrupt occurs.
*/
void esp_cpu_wait_for_intr(void);
/* -------------------------------------------------- CPU Registers ---------------------------------------------------- */
/**
* @brief Get the current core's ID
*
* @return Always 0 on Linux host (single core)
*/
FORCE_INLINE_ATTR __attribute__((pure)) int esp_cpu_get_core_id(void)
{
return 0;
}
/**
* @brief Get the current CPU core's privilege level
*
* @return -1 (not applicable on host)
*/
FORCE_INLINE_ATTR __attribute__((always_inline)) int esp_cpu_get_curr_privilege_level(void)
{
return -1;
}
/**
* @brief Read the current stack pointer address
*/
FORCE_INLINE_ATTR void *esp_cpu_get_sp(void)
{
void *sp;
#if defined(__x86_64__)
__asm__ __volatile__ ("mov %%rsp, %0" : "=r"(sp));
#elif defined(__aarch64__)
__asm__ __volatile__ ("mov %0, sp" : "=r"(sp));
#else
sp = __builtin_frame_address(0);
#endif
return sp;
}
/**
* @brief Get the current CPU core's cycle count
*
* @return Always 0 (no hardware cycle counter on host)
*/
FORCE_INLINE_ATTR esp_cpu_cycle_count_t esp_cpu_get_cycle_count(void)
{
return 0;
}
/**
* @brief Set the current CPU core's cycle count (no-op on host)
*/
FORCE_INLINE_ATTR void esp_cpu_set_cycle_count(esp_cpu_cycle_count_t cycle_count)
{
(void)cycle_count;
}
/**
* @brief Convert a program counter (PC) value to address
*/
FORCE_INLINE_ATTR __attribute__((pure)) void *esp_cpu_pc_to_addr(uint32_t pc)
{
return (void *)(uintptr_t)pc;
}
/**
* @brief Set the current CPU core's thread pointer (no-op on host)
*/
FORCE_INLINE_ATTR void esp_cpu_set_threadptr(void *threadptr)
{
(void)threadptr;
}
/**
* @brief Get the current CPU core's thread pointer
*
* @return NULL on host
*/
FORCE_INLINE_ATTR void *esp_cpu_get_threadptr(void)
{
return NULL;
}
/* ------------------------------------------------- CPU Interrupts ---------------------------------------------------- */
/**
* @brief Get a CPU interrupt's descriptor
*
* Each CPU interrupt has a descriptor describing the interrupt's capabilities
* and restrictions. This function gets the descriptor of a particular interrupt
* on a particular CPU.
*
* @param[in] core_id The core's ID
* @param[in] intr_num Interrupt number
* @param[out] intr_desc_ret The interrupt's descriptor
*/
void esp_cpu_intr_get_desc(int core_id, int intr_num, esp_cpu_intr_desc_t *intr_desc_ret);
/**
* @brief Set the base address of the current CPU's Interrupt Vector Table (IVT)
*
* @param ivt_addr Interrupt Vector Table's base address
*/
FORCE_INLINE_ATTR void esp_cpu_intr_set_ivt_addr(const void *ivt_addr)
{
(void)ivt_addr;
}
/**
* @brief Check if a particular interrupt already has a handler function
*
* @note This function simply checks if the IVT of the current CPU already has
* a handler assigned.
* @param intr_num Interrupt number (from 0 to 31)
* @return True if the interrupt has a handler function, false otherwise.
*/
FORCE_INLINE_ATTR bool esp_cpu_intr_has_handler(int intr_num)
{
(void)intr_num;
return false;
}
/**
* @brief Set the handler function of a particular interrupt
*
* Assign a handler function (i.e., ISR) to a particular interrupt on the
* current CPU.
*
* @note This function simply sets the handler function (in the IVT) and does
* not actually enable the interrupt.
* @param intr_num Interrupt number (from 0 to 31)
* @param handler Handler function
* @param handler_arg Argument passed to the handler function
*/
FORCE_INLINE_ATTR void esp_cpu_intr_set_handler(int intr_num, esp_cpu_intr_handler_t handler, void *handler_arg)
{
(void)intr_num;
(void)handler;
(void)handler_arg;
}
/**
* @brief Get a handler function's argument of
*
* Get the argument of a previously assigned handler function on the current CPU.
*
* @param intr_num Interrupt number (from 0 to 31)
* @return The the argument passed to the handler function
*/
FORCE_INLINE_ATTR void *esp_cpu_intr_get_handler_arg(int intr_num)
{
(void)intr_num;
return NULL;
}
/**
* @brief Enable particular interrupts on the current CPU
*
* @param intr_mask Bit mask of the interrupts to enable
*/
FORCE_INLINE_ATTR void esp_cpu_intr_enable(uint32_t intr_mask)
{
(void)intr_mask;
}
/**
* @brief Disable particular interrupts on the current CPU
*
* @param intr_mask Bit mask of the interrupts to disable
*/
FORCE_INLINE_ATTR void esp_cpu_intr_disable(uint32_t intr_mask)
{
(void)intr_mask;
}
/**
* @brief Get the enabled interrupts on the current CPU
*
* @return Bit mask of the enabled interrupts
*/
FORCE_INLINE_ATTR uint32_t esp_cpu_intr_get_enabled_mask(void)
{
return 0;
}
/**
* @brief Acknowledge an edge interrupt
*
* @param intr_num Interrupt number (from 0 to 31)
*/
FORCE_INLINE_ATTR void esp_cpu_intr_edge_ack(int intr_num)
{
(void)intr_num;
}
/* -------------------------------------------------- Memory Ports ----------------------------------------------------- */
/**
* @brief Configure the CPU to disable access to invalid memory regions
*/
void esp_cpu_configure_region_protection(void);
/* ---------------------------------------------------- Debugging ------------------------------------------------------ */
/**
* @brief Set and enable a hardware watchpoint on the current CPU
*
* @param wp_num Hardware watchpoint number
* @param wp_addr Watchpoint's base address
* @param size Size of the region to watch
* @param trigger Trigger type
* @return ESP_ERR_NOT_SUPPORTED on host
*/
esp_err_t esp_cpu_set_watchpoint(int wp_num, const void *wp_addr, size_t size, esp_cpu_watchpoint_trigger_t trigger);
/**
* @brief Clear a hardware watchpoint on the current CPU
*
* @param wp_num Hardware watchpoint number
* @return ESP_ERR_NOT_SUPPORTED on host
*/
esp_err_t esp_cpu_clear_watchpoint(int wp_num);
/**
* @brief Check if the current CPU has a debugger attached
*
* @return True if debugger is attached, false otherwise
*/
FORCE_INLINE_ATTR bool esp_cpu_dbgr_is_attached(void)
{
return false;
}
/**
* @brief Trigger a call to the current CPU's attached debugger
*/
FORCE_INLINE_ATTR void esp_cpu_dbgr_break(void)
{
__builtin_trap();
}
/**
* @brief Given the return address, calculate the address of the preceding call instruction
* This is typically used to answer the question "where was the function called from?"
* @param return_address The value of the return address register.
* Typically set to the value of __builtin_return_address(0).
* @return Address of the call instruction preceding the return address.
*/
FORCE_INLINE_ATTR intptr_t esp_cpu_get_call_addr(intptr_t return_address)
{
#if defined(__x86_64__)
return return_address - 5;
#else
return return_address - 4;
#endif
}
/* ------------------------------------------------------ Misc --------------------------------------------------------- */
/**
* @brief Atomic compare-and-set operation
*
* @param addr Address of atomic variable
* @param compare_value Value to compare the atomic variable to
* @param new_value New value to set the atomic variable to
* @return Whether the atomic variable was set or not
*/
bool esp_cpu_compare_and_set(volatile uint32_t *addr, uint32_t compare_value, uint32_t new_value);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,84 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* Stub implementations of non-inline esp_cpu_* functions for Linux/macOS
* host builds. These provide no-op or minimal behaviour since hardware
* CPU control, interrupt descriptors, breakpoints, watchpoints, and memory
* region protection don't exist on the host.
*/
#include <stdint.h>
#include <stdbool.h>
#include <stdatomic.h>
#include "esp_cpu.h"
#include "esp_err.h"
/* --------------------------------------------------- CPU Control ----------------------------------------------------- */
void esp_cpu_stall(int core_id)
{
(void)core_id;
}
void esp_cpu_unstall(int core_id)
{
(void)core_id;
}
void esp_cpu_reset(int core_id)
{
(void)core_id;
}
void esp_cpu_wait_for_intr(void)
{
/* No WFI equivalent on host; just return */
}
/* ------------------------------------------------- CPU Interrupts ---------------------------------------------------- */
void esp_cpu_intr_get_desc(int core_id, int intr_num, esp_cpu_intr_desc_t *intr_desc_ret)
{
(void)core_id;
(void)intr_num;
if (intr_desc_ret) {
intr_desc_ret->priority = -1;
intr_desc_ret->type = ESP_CPU_INTR_TYPE_NA;
intr_desc_ret->flags = 0;
}
}
/* -------------------------------------------------- Memory Ports ----------------------------------------------------- */
void esp_cpu_configure_region_protection(void)
{
/* No memory protection unit on host */
}
/* ---------------------------------------------------- Debugging ------------------------------------------------------ */
esp_err_t esp_cpu_set_watchpoint(int wp_num, const void *wp_addr, size_t size, esp_cpu_watchpoint_trigger_t trigger)
{
(void)wp_num;
(void)wp_addr;
(void)size;
(void)trigger;
return ESP_ERR_NOT_SUPPORTED;
}
esp_err_t esp_cpu_clear_watchpoint(int wp_num)
{
(void)wp_num;
return ESP_ERR_NOT_SUPPORTED;
}
/* ------------------------------------------------------ Misc --------------------------------------------------------- */
bool esp_cpu_compare_and_set(volatile uint32_t *addr, uint32_t compare_value, uint32_t new_value)
{
return atomic_compare_exchange_strong((_Atomic uint32_t *)addr, &compare_value, new_value);
}
+1 -11
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -9,18 +9,8 @@
#include "esp_err.h"
#include "esp_rom_sys.h"
#ifndef CONFIG_IDF_TARGET_LINUX
#include "esp_private/cache_utils.h"
#include "esp_cpu.h"
#else
/* esp_cpu.h isn't available when building for Linux */
static intptr_t esp_cpu_get_call_addr(intptr_t return_address)
{
/* on x86, there is no hope to get the address of the previous instruction */
return return_address;
}
#endif
static void esp_error_check_failed_print(const char *msg, esp_err_t rc, const char *file, int line, const char *function, const char *expression, intptr_t addr)
{
@@ -9,10 +9,7 @@
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_bit_defs.h"
#if !CONFIG_IDF_TARGET_LINUX
#include "esp_cpu.h"
#endif
#include "soc/soc_caps.h"
#include "sdkconfig.h"
@@ -11,7 +11,6 @@
#include <stddef.h>
#include <stdint.h>
#include "esp_err.h"
#include "esp_memory_utils.h"
#ifdef __cplusplus
extern "C" {