diff --git a/.gitlab/ci/common.yml b/.gitlab/ci/common.yml index fdb2b9f0911c..b107e0d171e5 100644 --- a/.gitlab/ci/common.yml +++ b/.gitlab/ci/common.yml @@ -40,7 +40,7 @@ variables: GIT_FETCH_EXTRA_FLAGS: "--no-recurse-submodules --prune --prune-tags" # we're using .cache folder for caches GIT_CLEAN_FLAGS: -ffdx -e .cache/ - LATEST_GIT_TAG: v5.5.1 + LATEST_GIT_TAG: v5.5.2 SUBMODULE_FETCH_TOOL: "tools/ci/ci_fetch_submodule.py" # by default we will fetch all submodules diff --git a/components/bt/controller/lib_esp32c3_family b/components/bt/controller/lib_esp32c3_family index 099a7e1ab87d..9b50531537e7 160000 --- a/components/bt/controller/lib_esp32c3_family +++ b/components/bt/controller/lib_esp32c3_family @@ -1 +1 @@ -Subproject commit 099a7e1ab87dd977754fc4ad35678ab7ebf2f2a2 +Subproject commit 9b50531537e755792ac827d00d233eab499a0b37 diff --git a/components/bt/host/bluedroid/api/esp_gap_ble_api.c b/components/bt/host/bluedroid/api/esp_gap_ble_api.c index 3c3a48e5b937..d592b6b77fd0 100644 --- a/components/bt/host/bluedroid/api/esp_gap_ble_api.c +++ b/components/bt/host/bluedroid/api/esp_gap_ble_api.c @@ -111,6 +111,10 @@ esp_err_t esp_ble_gap_start_advertising(esp_ble_adv_params_t *adv_params) ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (adv_params == NULL) { + return ESP_ERR_INVALID_ARG; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; msg.act = BTC_GAP_BLE_ACT_START_ADV; @@ -171,6 +175,10 @@ esp_err_t esp_ble_gap_set_pkt_data_len(esp_bd_addr_t remote_device, uint16_t tx_ ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (remote_device == NULL) { + return ESP_ERR_INVALID_ARG; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; msg.act = BTC_GAP_BLE_ACT_SET_PKT_DATA_LEN; @@ -182,6 +190,9 @@ esp_err_t esp_ble_gap_set_pkt_data_len(esp_bd_addr_t remote_device, uint16_t tx_ esp_err_t esp_ble_gap_addr_create_static(esp_bd_addr_t rand_addr) { + if (rand_addr == NULL) { + return ESP_ERR_INVALID_ARG; + } // Static device address: First two bits are '11', rest is random rand_addr[0] = 0xC0 | (esp_random() & 0x3F); for (int i = 1; i < 6; i++) { @@ -192,6 +203,9 @@ esp_err_t esp_ble_gap_addr_create_static(esp_bd_addr_t rand_addr) esp_err_t esp_ble_gap_addr_create_nrpa(esp_bd_addr_t rand_addr) { + if (rand_addr == NULL) { + return ESP_ERR_INVALID_ARG; + } // Non-resolvable private address: First two bits are '00', rest is random rand_addr[0] = (esp_random() & 0x3F); for (int i = 1; i < 6; i++) { @@ -207,6 +221,10 @@ esp_err_t esp_ble_gap_set_rand_addr(esp_bd_addr_t rand_addr) ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (rand_addr == NULL) { + return ESP_ERR_INVALID_ARG; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; msg.act = BTC_GAP_BLE_ACT_SET_RAND_ADDRESS; @@ -239,7 +257,7 @@ esp_err_t esp_ble_gap_add_device_to_resolving_list(esp_bd_addr_t peer_addr, uint { ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); - if (addr_type > BLE_ADDR_TYPE_RANDOM ||!peer_addr || (addr_type && ((peer_addr[0] & 0xC0) != 0xC0))) { + if (addr_type > BLE_ADDR_TYPE_RANDOM || !peer_addr || !peer_irk || (addr_type && ((peer_addr[0] & 0xC0) != 0xC0))) { return ESP_ERR_INVALID_ARG; } @@ -357,6 +375,10 @@ esp_err_t esp_ble_gap_set_prefer_conn_params(esp_bd_addr_t bd_addr, return ESP_ERR_INVALID_STATE; } + if (bd_addr == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (ESP_BLE_IS_VALID_PARAM(min_conn_int, ESP_BLE_CONN_INT_MIN, ESP_BLE_CONN_INT_MAX) && ESP_BLE_IS_VALID_PARAM(max_conn_int, ESP_BLE_CONN_INT_MIN, ESP_BLE_CONN_INT_MAX) && ESP_BLE_IS_VALID_PARAM(supervision_tout, ESP_BLE_CONN_SUP_TOUT_MIN, ESP_BLE_CONN_SUP_TOUT_MAX) && @@ -424,6 +446,9 @@ esp_err_t esp_ble_gap_get_local_used_addr(esp_bd_addr_t local_used_addr, uint8_t LOG_ERROR("%s, bluedroid status error", __func__); return ESP_FAIL; } + if (local_used_addr == NULL || addr_type == NULL) { + return ESP_ERR_INVALID_ARG; + } if(!BTM_BleGetCurrentAddress(local_used_addr, addr_type)) { return ESP_FAIL; } @@ -436,13 +461,6 @@ uint8_t *esp_ble_resolve_adv_data_by_type( uint8_t *adv_data, uint16_t adv_data_ return NULL; } - if (((type < ESP_BLE_AD_TYPE_FLAG) || (type > ESP_BLE_AD_TYPE_128SERVICE_DATA)) && - (type != ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE)) { - LOG_ERROR("The advertising data type is not defined, type = %x", type); - *length = 0; - return NULL; - } - if (adv_data_len == 0) { *length = 0; return NULL; @@ -495,6 +513,10 @@ esp_err_t esp_ble_gap_read_rssi(esp_bd_addr_t remote_addr) return ESP_ERR_INVALID_STATE; } + if (remote_addr == NULL) { + return ESP_ERR_INVALID_ARG; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; msg.act = BTC_GAP_BLE_ACT_READ_RSSI; @@ -645,6 +667,10 @@ esp_err_t esp_ble_set_encryption(esp_bd_addr_t bd_addr, esp_ble_sec_act_t sec_ac ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (bd_addr == NULL) { + return ESP_ERR_INVALID_ARG; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; msg.act = BTC_GAP_BLE_SET_ENCRYPTION_EVT; @@ -662,6 +688,10 @@ esp_err_t esp_ble_gap_security_rsp(esp_bd_addr_t bd_addr, bool accept) ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (bd_addr == NULL) { + return ESP_ERR_INVALID_ARG; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; msg.act = BTC_GAP_BLE_SECURITY_RSP_EVT; @@ -680,6 +710,10 @@ esp_err_t esp_ble_passkey_reply(esp_bd_addr_t bd_addr, bool accept, uint32_t pas ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (bd_addr == NULL) { + return ESP_ERR_INVALID_ARG; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; msg.act = BTC_GAP_BLE_PASSKEY_REPLY_EVT; @@ -698,6 +732,10 @@ esp_err_t esp_ble_confirm_reply(esp_bd_addr_t bd_addr, bool accept) ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (bd_addr == NULL) { + return ESP_ERR_INVALID_ARG; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; msg.act = BTC_GAP_BLE_CONFIRM_REPLY_EVT; @@ -712,6 +750,11 @@ esp_err_t esp_ble_remove_bond_device(esp_bd_addr_t bd_addr) { btc_msg_t msg = {0}; btc_ble_gap_args_t arg; + + if (bd_addr == NULL) { + return ESP_ERR_INVALID_ARG; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; msg.act = BTC_GAP_BLE_REMOVE_BOND_DEV_EVT; @@ -757,6 +800,10 @@ esp_err_t esp_ble_oob_req_reply(esp_bd_addr_t bd_addr, uint8_t *TK, uint8_t len) return ESP_ERR_INVALID_ARG; } + if (bd_addr == NULL || TK == NULL) { + return ESP_ERR_INVALID_ARG; + } + btc_msg_t msg = {0}; btc_ble_gap_args_t arg; @@ -775,7 +822,7 @@ esp_err_t esp_ble_oob_req_reply(esp_bd_addr_t bd_addr, uint8_t *TK, uint8_t len) esp_err_t esp_ble_sc_oob_req_reply(esp_bd_addr_t bd_addr, uint8_t p_c[16], uint8_t p_r[16]) { - if (!p_c || !p_r) { + if (!bd_addr || !p_c || !p_r) { return ESP_ERR_INVALID_ARG; } @@ -836,6 +883,10 @@ esp_err_t esp_ble_gap_disconnect(esp_bd_addr_t remote_device) ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (remote_device == NULL) { + return ESP_ERR_INVALID_ARG; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; msg.act = BTC_GAP_BLE_DISCONNECT_EVT; @@ -864,6 +915,10 @@ esp_err_t esp_gap_ble_set_channels(esp_gap_ble_channels channels) return ESP_ERR_INVALID_STATE; } + if (channels == NULL) { + return ESP_ERR_INVALID_ARG; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GAP_BLE; msg.act = BTC_GAP_BLE_SET_AFH_CHANNELS; diff --git a/components/bt/host/bluedroid/api/esp_gattc_api.c b/components/bt/host/bluedroid/api/esp_gattc_api.c index 83481a85c618..00c6a96e0bf2 100644 --- a/components/bt/host/bluedroid/api/esp_gattc_api.c +++ b/components/bt/host/bluedroid/api/esp_gattc_api.c @@ -173,6 +173,10 @@ esp_err_t esp_ble_gattc_enh_open(esp_gatt_if_t gattc_if, esp_ble_gatt_creat_conn #if (BLE_42_FEATURE_SUPPORT == TRUE) esp_err_t esp_ble_gattc_open(esp_gatt_if_t gattc_if, esp_bd_addr_t remote_bda, esp_ble_addr_type_t remote_addr_type, bool is_direct) { + if (remote_bda == NULL) { + return ESP_ERR_INVALID_ARG; + } + esp_ble_gatt_creat_conn_params_t creat_conn_params = {0}; memcpy(creat_conn_params.remote_bda, remote_bda, ESP_BD_ADDR_LEN); creat_conn_params.remote_addr_type = remote_addr_type; @@ -187,6 +191,10 @@ esp_err_t esp_ble_gattc_open(esp_gatt_if_t gattc_if, esp_bd_addr_t remote_bda, e #if (BLE_50_FEATURE_SUPPORT == TRUE) esp_err_t esp_ble_gattc_aux_open(esp_gatt_if_t gattc_if, esp_bd_addr_t remote_bda, esp_ble_addr_type_t remote_addr_type, bool is_direct) { + if (remote_bda == NULL) { + return ESP_ERR_INVALID_ARG; + } + esp_ble_gatt_creat_conn_params_t creat_conn_params = {0}; memcpy(creat_conn_params.remote_bda, remote_bda, ESP_BD_ADDR_LEN); creat_conn_params.remote_addr_type = remote_addr_type; @@ -521,6 +529,10 @@ esp_err_t esp_ble_gattc_read_multiple(esp_gatt_if_t gattc_if, ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (read_multi == NULL) { + return ESP_ERR_INVALID_ARG; + } + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(conn_id); if (!gatt_check_connection_state_by_tcb(p_tcb)) { LOG_WARN("%s, The connection not created.", __func__); @@ -557,6 +569,10 @@ esp_err_t esp_ble_gattc_read_multiple_variable(esp_gatt_if_t gattc_if, ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (read_multi == NULL) { + return ESP_ERR_INVALID_ARG; + } + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(conn_id); if (!gatt_check_connection_state_by_tcb(p_tcb)) { LOG_WARN("%s, The connection not created.", __func__); @@ -810,6 +826,10 @@ esp_err_t esp_ble_gattc_register_for_notify (esp_gatt_if_t gattc_if, ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (server_bda == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (handle == 0) { return ESP_GATT_INVALID_HANDLE; } @@ -832,6 +852,10 @@ esp_err_t esp_ble_gattc_unregister_for_notify (esp_gatt_if_t gattc_if, ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (server_bda == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (handle == 0) { return ESP_GATT_INVALID_HANDLE; } @@ -852,6 +876,10 @@ esp_err_t esp_ble_gattc_cache_refresh(esp_bd_addr_t remote_bda) ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (remote_bda == NULL) { + return ESP_ERR_INVALID_ARG; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_CACHE_REFRESH; @@ -867,6 +895,10 @@ esp_err_t esp_ble_gattc_cache_clean(esp_bd_addr_t remote_bda) ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (remote_bda == NULL) { + return ESP_ERR_INVALID_ARG; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_CACHE_CLEAN; @@ -882,6 +914,10 @@ esp_err_t esp_ble_gattc_cache_assoc(esp_gatt_if_t gattc_if, esp_bd_addr_t src_ad ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (src_addr == NULL || assoc_addr == NULL) { + return ESP_ERR_INVALID_ARG; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTC; msg.act = BTC_GATTC_ACT_CACHE_ASSOC; diff --git a/components/bt/host/bluedroid/api/esp_gatts_api.c b/components/bt/host/bluedroid/api/esp_gatts_api.c index 03c81860995a..1d7c69612694 100644 --- a/components/bt/host/bluedroid/api/esp_gatts_api.c +++ b/components/bt/host/bluedroid/api/esp_gatts_api.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -78,6 +78,10 @@ esp_err_t esp_ble_gatts_create_service(esp_gatt_if_t gatts_if, ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (service_id == NULL) { + return ESP_ERR_INVALID_ARG; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_CREATE_SERVICE; @@ -98,6 +102,10 @@ esp_err_t esp_ble_gatts_create_attr_tab(const esp_gatts_attr_db_t *gatts_attr_db ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (gatts_attr_db == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (max_nb_attr > ESP_GATT_ATTR_HANDLE_MAX) { LOG_ERROR("The number of attribute should not be greater than CONFIG_BT_GATT_MAX_SR_ATTRIBUTES\n"); return ESP_ERR_INVALID_ARG; @@ -143,6 +151,10 @@ esp_err_t esp_ble_gatts_add_char(uint16_t service_handle, esp_bt_uuid_t *char_ ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (char_uuid == NULL) { + return ESP_ERR_INVALID_ARG; + } + /* parameter validation check */ status = esp_ble_gatts_add_char_desc_param_check(char_val, control); if (status != ESP_OK){ @@ -183,6 +195,10 @@ esp_err_t esp_ble_gatts_add_char_descr (uint16_t service_handle, ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (descr_uuid == NULL) { + return ESP_ERR_INVALID_ARG; + } + /* parameter validation check */ status = esp_ble_gatts_add_char_desc_param_check(char_descr_val, control); if (status != ESP_OK){ @@ -344,6 +360,10 @@ esp_gatt_status_t esp_ble_gatts_get_attr_value(uint16_t attr_handle, uint16_t *l { ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (length == NULL || value == NULL) { + return ESP_GATT_INVALID_PDU; + } + if (attr_handle == ESP_GATT_ILLEGAL_HANDLE) { *length = 0; return ESP_GATT_INVALID_HANDLE; @@ -359,6 +379,10 @@ esp_err_t esp_ble_gatts_open(esp_gatt_if_t gatts_if, esp_bd_addr_t remote_bda, b ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + if (remote_bda == NULL) { + return ESP_ERR_INVALID_ARG; + } + msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; msg.act = BTC_GATTS_ACT_OPEN; diff --git a/components/bt/host/bluedroid/bta/gatt/bta_gatts_api.c b/components/bt/host/bluedroid/bta/gatt/bta_gatts_api.c index 4e1f76f63694..b712da9b7967 100644 --- a/components/bt/host/bluedroid/bta/gatt/bta_gatts_api.c +++ b/components/bt/host/bluedroid/bta/gatt/bta_gatts_api.c @@ -241,7 +241,7 @@ void BTA_GATTS_AddCharacteristic (UINT16 service_id, const tBT_UUID * p_char_u p_buf->attr_val.attr_max_len = attr_val->attr_max_len; p_buf->attr_val.attr_val = (uint8_t *)osi_malloc(len); if(p_buf->attr_val.attr_val != NULL){ - memcpy(p_buf->attr_val.attr_val, attr_val->attr_val, attr_val->attr_len); + memcpy(p_buf->attr_val.attr_val, attr_val->attr_val, len); } } @@ -411,6 +411,14 @@ void BTA_GATTS_StopService(UINT16 service_id) void BTA_GATTS_HandleValueIndication (UINT16 conn_id, UINT16 attr_id, UINT16 data_len, UINT8 *p_data, BOOLEAN need_confirm) { + + /* Validate data length against buffer size */ + if (data_len > BTA_GATT_MAX_ATTR_LEN) { + APPL_TRACE_ERROR("GATT indication data too large: %u > %u", + data_len, BTA_GATT_MAX_ATTR_LEN); + return; + } + tBTA_GATTS_API_INDICATION *p_buf; UINT16 len = sizeof(tBTA_GATTS_API_INDICATION); diff --git a/components/bt/host/bluedroid/bta/hd/bta_hd_api.c b/components/bt/host/bluedroid/bta/hd/bta_hd_api.c index 33875b957a49..36aa540d2950 100644 --- a/components/bt/host/bluedroid/bta/hd/bta_hd_api.c +++ b/components/bt/host/bluedroid/bta/hd/bta_hd_api.c @@ -89,6 +89,20 @@ void BTA_HdDisable(void) ******************************************************************************/ extern void BTA_HdRegisterApp(tBTA_HD_APP_INFO *p_app_info, tBTA_HD_QOS_INFO *p_in_qos, tBTA_HD_QOS_INFO *p_out_qos) { + + /* Validate descriptor length before copying */ + if (p_app_info->descriptor.dl_len > BTA_HD_APP_DESCRIPTOR_LEN) { + APPL_TRACE_ERROR("HID descriptor too large: %u > %u", + p_app_info->descriptor.dl_len, BTA_HD_APP_DESCRIPTOR_LEN); + return; + } + + /* Validate descriptor data pointer */ + if (p_app_info->descriptor.dl_len > 0 && p_app_info->descriptor.dsc_list == NULL) { + APPL_TRACE_ERROR("HID descriptor data NULL but length > 0: %u", p_app_info->descriptor.dl_len); + return; + } + tBTA_HD_REGISTER_APP *p_buf; APPL_TRACE_API("%s", __func__); if ((p_buf = (tBTA_HD_REGISTER_APP *)osi_malloc(sizeof(tBTA_HD_REGISTER_APP))) != NULL) { @@ -158,6 +172,13 @@ extern void BTA_HdSendReport(tBTA_HD_REPORT *p_report) __func__, p_report->len, BTA_HD_REPORT_LEN); return; } + + /* Validate report data pointer */ + if (p_report->len > 0 && p_report->p_data == NULL) { + APPL_TRACE_ERROR("HID report data pointer NULL but length > 0: %d", p_report->len); + return; + } + if ((p_buf = (tBTA_HD_SEND_REPORT *)osi_malloc(sizeof(tBTA_HD_SEND_REPORT))) != NULL) { p_buf->hdr.event = BTA_HD_API_SEND_REPORT_EVT; p_buf->use_intr = p_report->use_intr; diff --git a/components/bt/host/bluedroid/bta/hh/bta_hh_utils.c b/components/bt/host/bluedroid/bta/hh/bta_hh_utils.c index 6dc3ec02dd63..7c89ed004d75 100644 --- a/components/bt/host/bluedroid/bta/hh/bta_hh_utils.c +++ b/components/bt/host/bluedroid/bta/hh/bta_hh_utils.c @@ -263,6 +263,18 @@ void bta_hh_parse_keybd_rpt(tBTA_HH_BOOT_RPT *p_kb_data, UINT8 *p_report, UINT16 xx, yy, key_idx = 0; UINT8 this_report[BTA_HH_MAX_RPT_CHARS]; + /* Validate report length before processing */ + if (report_len > BTA_HH_MAX_RPT_CHARS) { + APPL_TRACE_ERROR("HID report length exceeds maximum: %u > %u", + report_len, BTA_HH_MAX_RPT_CHARS); + return; + } + + if (report_len == 0 || p_report == NULL) { + APPL_TRACE_ERROR("Invalid HID report data"); + return; + } + #if BTA_HH_DEBUG APPL_TRACE_DEBUG("bta_hh_parse_keybd_rpt: (report=%p, report_len=%d) called", p_report, report_len); @@ -463,7 +475,7 @@ void bta_hh_cleanup_disable(tBTA_HH_STATUS status) if (bta_hh_cb.p_cback) { (*bta_hh_cb.p_cback)(BTA_HH_DISABLE_EVT, (tBTA_HH*)&status); - /* all connections are down, no waiting for diconnect */ + /* all connections are down, no waiting for disconnect */ memset(&bta_hh_cb, 0, sizeof(tBTA_HH_CB)); } } diff --git a/components/bt/host/bluedroid/btc/core/btc_ble_storage.c b/components/bt/host/bluedroid/btc/core/btc_ble_storage.c index f20a7241e174..aef3863111f5 100644 --- a/components/bt/host/bluedroid/btc/core/btc_ble_storage.c +++ b/components/bt/host/bluedroid/btc/core/btc_ble_storage.c @@ -16,7 +16,7 @@ #if (SMP_INCLUDED == TRUE) //the maximum number of bonded devices -#define BONED_DEVICES_MAX_COUNT (BTM_SEC_MAX_DEVICE_RECORDS) +#define BONED_DEVICES_MAX_COUNT (BTM_SEC_MAX_BONDS) static void _btc_storage_save(void) { diff --git a/components/bt/host/bluedroid/btc/core/btc_storage.c b/components/bt/host/bluedroid/btc/core/btc_storage.c index 1923dea0da63..bee44e18d383 100644 --- a/components/bt/host/bluedroid/btc/core/btc_storage.c +++ b/components/bt/host/bluedroid/btc/core/btc_storage.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -40,7 +40,7 @@ bt_status_t btc_storage_add_bonded_device(bt_bdaddr_t *remote_bd_addr, bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); /* device not in bond list and exceed the maximum number of bonded devices, delete the inactive bonded device */ - if (btc_storage_get_num_all_bond_devices() >= BTM_SEC_MAX_DEVICE_RECORDS && !btc_config_has_section(bdstr)) { + if (btc_storage_get_num_all_bond_devices() >= BTM_SEC_MAX_BONDS && !btc_config_has_section(bdstr)) { const btc_config_section_iter_t *iter = btc_config_section_begin(); const btc_config_section_iter_t *remove_iter = iter; /* find the first device(the last node) */ @@ -57,7 +57,7 @@ bt_status_t btc_storage_add_bonded_device(bt_bdaddr_t *remote_bd_addr, // delete config info if (btc_config_remove_section(remove_section)) { - BTC_TRACE_WARNING("exceeded the maximum nubmer of bonded devices, delete the first device info : %02x:%02x:%02x:%02x:%02x:%02x", + BTC_TRACE_WARNING("exceeded the maximum number of bonded devices, delete the first device info : %02x:%02x:%02x:%02x:%02x:%02x", bd_addr.address[0], bd_addr.address[1], bd_addr.address[2], bd_addr.address[3], bd_addr.address[4], bd_addr.address[5]); } } @@ -141,8 +141,8 @@ static bt_status_t btc_in_fetch_bonded_devices(int add) continue; } dev_cnt ++; - /* if the number of device stored in nvs not exceed to BTM_SEC_MAX_DEVICE_RECORDS, load it */ - if (dev_cnt <= BTM_SEC_MAX_DEVICE_RECORDS) { + /* if the number of device stored in nvs not exceed to BTM_SEC_MAX_BONDS, load it */ + if (dev_cnt <= BTM_SEC_MAX_BONDS) { if (btc_config_exist(name, BTC_STORAGE_LINK_KEY_TYPE_STR) && btc_config_exist(name, BTC_STORAGE_PIN_LENGTH_STR) && btc_config_exist(name, BTC_STORAGE_SC_SUPPORT) && btc_config_exist(name, BTC_STORAGE_LINK_KEY_STR)) { /* load bt device */ diff --git a/components/bt/host/bluedroid/common/include/common/bt_target.h b/components/bt/host/bluedroid/common/include/common/bt_target.h index 23fca3f3d067..6ec7ec52ba71 100644 --- a/components/bt/host/bluedroid/common/include/common/bt_target.h +++ b/components/bt/host/bluedroid/common/include/common/bt_target.h @@ -1132,11 +1132,11 @@ /* The number of security records for peer devices. 15 AS Default*/ #ifndef BTM_SEC_MAX_DEVICE_RECORDS -#if (UC_BT_SMP_MAX_BONDS < UC_BT_ACL_CONNECTIONS) -#define BTM_SEC_MAX_DEVICE_RECORDS UC_BT_ACL_CONNECTIONS -#else -#define BTM_SEC_MAX_DEVICE_RECORDS UC_BT_SMP_MAX_BONDS +#define BTM_SEC_MAX_DEVICE_RECORDS (UC_BT_SMP_MAX_BONDS + UC_BT_ACL_CONNECTIONS) #endif + +#ifndef BTM_SEC_MAX_BONDS +#define BTM_SEC_MAX_BONDS UC_BT_SMP_MAX_BONDS #endif #if BTA_SDP_INCLUDED diff --git a/components/bt/host/bluedroid/hci/hci_hal_h4.c b/components/bt/host/bluedroid/hci/hci_hal_h4.c index 5baf97fc0810..f91a2260d973 100644 --- a/components/bt/host/bluedroid/hci/hci_hal_h4.c +++ b/components/bt/host/bluedroid/hci/hci_hal_h4.c @@ -508,9 +508,10 @@ static void hci_hal_h4_hdl_rx_packet(BT_HDR *packet) STREAM_TO_UINT8(length, stream); } - if ((length + hdr_size) != packet->len) { - HCI_TRACE_ERROR("Wrong packet length type=%d hdr_len=%d pd_len=%d " - "pkt_len=%d", type, hdr_size, length, packet->len); + // Prevents integer wrap-around when calculating (length + hdr_size). + if (length != (packet->len - hdr_size)) { + HCI_TRACE_ERROR("%s: SECURITY: parameter length (%d) exceeds packet bounds (%d)", + __func__, length, packet->len - hdr_size); osi_free(packet); return; } diff --git a/components/bt/host/bluedroid/hci/hci_layer.c b/components/bt/host/bluedroid/hci/hci_layer.c index e5cd263fbc19..34bdb570938e 100644 --- a/components/bt/host/bluedroid/hci/hci_layer.c +++ b/components/bt/host/bluedroid/hci/hci_layer.c @@ -452,10 +452,12 @@ static bool filter_incoming_event(BT_HDR *packet) STREAM_TO_UINT8(hci_host_env.command_credits, stream); STREAM_TO_UINT16(opcode, stream); wait_entry = get_waiting_command(opcode); - metadata = (hci_cmd_metadata_t *)(wait_entry->data); if (!wait_entry) { HCI_TRACE_WARNING("%s command complete event with no matching command. opcode: 0x%x.", __func__, opcode); - } else if (metadata->command_complete_cb) { + goto intercepted; + } + metadata = (hci_cmd_metadata_t *)(wait_entry->data); + if (metadata->command_complete_cb) { metadata->command_complete_cb(packet, metadata->context); #if (BLE_50_FEATURE_SUPPORT == TRUE) BlE_SYNC *sync_info = btsnd_hcic_ble_get_sync_info(); @@ -482,10 +484,12 @@ static bool filter_incoming_event(BT_HDR *packet) // If a command generates a command status event, it won't be getting a command complete event wait_entry = get_waiting_command(opcode); - metadata = (hci_cmd_metadata_t *)(wait_entry->data); if (!wait_entry) { HCI_TRACE_WARNING("%s command status event with no matching command. opcode: 0x%x", __func__, opcode); - } else if (metadata->command_status_cb) { + goto intercepted; + } + metadata = (hci_cmd_metadata_t *)(wait_entry->data); + if (metadata->command_status_cb) { metadata->command_status_cb(status, &metadata->command, metadata->context); } diff --git a/components/bt/host/bluedroid/hci/hci_packet_parser.c b/components/bt/host/bluedroid/hci/hci_packet_parser.c index b254c9be2aaa..560ee7b4a15f 100644 --- a/components/bt/host/bluedroid/hci/hci_packet_parser.c +++ b/components/bt/host/bluedroid/hci/hci_packet_parser.c @@ -164,7 +164,7 @@ static void parse_ble_read_buffer_size_response_v2 ( uint8_t *iso_pkt_num_ptr) { - uint8_t *stream = read_command_complete_header(response, HCI_BLE_READ_BUFFER_SZIE_V2, 3 /* bytes after */); + uint8_t *stream = read_command_complete_header(response, HCI_BLE_READ_BUFFER_SZIE_V2, 6 /* bytes after: 2+1+2+1 */); assert(stream != NULL); STREAM_TO_UINT16(*data_size_ptr, stream); STREAM_TO_UINT8(*acl_buffer_count_ptr, stream); diff --git a/components/bt/host/bluedroid/hci/packet_fragmenter.c b/components/bt/host/bluedroid/hci/packet_fragmenter.c index d8f2ff918fa3..9b444ab69cd6 100644 --- a/components/bt/host/bluedroid/hci/packet_fragmenter.c +++ b/components/bt/host/bluedroid/hci/packet_fragmenter.c @@ -161,6 +161,13 @@ static void reassemble_and_dispatch(BT_HDR *packet) osi_free(partial_packet); } + /* Check for integer overflow in length calculation */ + if (l2cap_length > (UINT16_MAX - L2CAP_HEADER_SIZE - HCI_ACL_PREAMBLE_SIZE)) { + HCI_TRACE_ERROR("L2CAP length too large: %u", l2cap_length); + osi_free(packet); + return; + } + uint16_t full_length = l2cap_length + L2CAP_HEADER_SIZE + HCI_ACL_PREAMBLE_SIZE; if (full_length <= packet->len) { if (full_length < packet->len) { @@ -200,6 +207,20 @@ static void reassemble_and_dispatch(BT_HDR *packet) packet->offset += HCI_ACL_PREAMBLE_SIZE; // skip ACL preamble packet->len -= HCI_ACL_PREAMBLE_SIZE; + + // CVE-2020-0022 (BlueFrag) Fix: Prevent integer underflow + if (partial_packet->offset > partial_packet->len) { + HCI_TRACE_ERROR("%s offset exceeds expected length. Dropping packet.\n", __func__); + osi_free(packet); + return; + } + + if (packet->len > UINT16_MAX - partial_packet->offset) { + HCI_TRACE_ERROR("%s: packet->len too large, would overflow. Dropping packet.\n", __func__); + osi_free(packet); + return; + } + uint16_t projected_offset = partial_packet->offset + packet->len; if (projected_offset > partial_packet->len) { // len stores the expected length HCI_TRACE_ERROR("%s got packet which would exceed expected length of %d. Truncating.\n", __func__, partial_packet->len); diff --git a/components/bt/host/bluedroid/stack/btm/btm_ble.c b/components/bt/host/bluedroid/stack/btm/btm_ble.c index 8f676a655a6d..27ebe49dcfeb 100644 --- a/components/bt/host/bluedroid/stack/btm/btm_ble.c +++ b/components/bt/host/bluedroid/stack/btm/btm_ble.c @@ -1704,6 +1704,9 @@ tBTM_STATUS btm_ble_start_encrypt(BD_ADDR bda, BOOLEAN use_stk, BT_OCTET16 stk) #if (SMP_INCLUDED == TRUE) void btm_ble_link_encrypted(BD_ADDR bd_addr, UINT8 encr_enable) { +#if BLE_INCLUDED == TRUE + l2cble_notify_le_connection(bd_addr); +#endif // BLE_INCLUDED == TRUE tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); BOOLEAN enc_cback; diff --git a/components/bt/host/bluedroid/stack/btm/btm_ble_5_gap.c b/components/bt/host/bluedroid/stack/btm/btm_ble_5_gap.c index 76fe63c08a9e..e82d95f61eab 100644 --- a/components/bt/host/bluedroid/stack/btm/btm_ble_5_gap.c +++ b/components/bt/host/bluedroid/stack/btm/btm_ble_5_gap.c @@ -1254,6 +1254,11 @@ void btm_ble_adv_set_terminated_evt(tBTM_BLE_ADV_TERMINAT *params) return; } + if (params->adv_handle >= MAX_BLE_ADV_INSTANCE) { + BTM_TRACE_ERROR("%s, Invalid adv_handle %d, max is %d.", __func__, params->adv_handle, MAX_BLE_ADV_INSTANCE); + return; + } + // adv terminated due to connection, save the adv handle and connection handle if(params->status == 0x00) { adv_record[params->adv_handle].ter_con_handle = params->conn_handle; diff --git a/components/bt/host/bluedroid/stack/btm/btm_ble_gap.c b/components/bt/host/bluedroid/stack/btm/btm_ble_gap.c index faf2e1848f0a..973f32754a3a 100644 --- a/components/bt/host/bluedroid/stack/btm/btm_ble_gap.c +++ b/components/bt/host/bluedroid/stack/btm/btm_ble_gap.c @@ -3112,6 +3112,13 @@ void btm_ble_cache_adv_data(BD_ADDR bda, tBTM_INQ_RESULTS *p_cur, UINT8 data_len p_cur->scan_rsp_len = 0; } + /* Additional validation to prevent potential integer overflow */ + if (data_len > BTM_BLE_CACHE_ADV_DATA_MAX) { + BTM_TRACE_ERROR("BLE advertising data length exceeds maximum: %u > %u", + data_len, BTM_BLE_CACHE_ADV_DATA_MAX); + return; + } + if (data_len > 0) { p_cache = &p_le_inq_cb->adv_data_cache[p_le_inq_cb->adv_len]; if((data_len + p_le_inq_cb->adv_len) <= BTM_BLE_CACHE_ADV_DATA_MAX) { diff --git a/components/bt/host/bluedroid/stack/gap/gap_ble.c b/components/bt/host/bluedroid/stack/gap/gap_ble.c index e32151298e04..bf3d97d666cd 100644 --- a/components/bt/host/bluedroid/stack/gap/gap_ble.c +++ b/components/bt/host/bluedroid/stack/gap/gap_ble.c @@ -104,7 +104,7 @@ tGAP_CLCB *gap_ble_find_clcb_by_conn_id(UINT16 conn_id) } } - return p_clcb; + return NULL; } /******************************************************************************* @@ -126,10 +126,10 @@ tGAP_CLCB *gap_clcb_alloc (BD_ADDR bda) memset(p_clcb, 0, sizeof(tGAP_CLCB)); p_clcb->in_use = TRUE; memcpy (p_clcb->bda, bda, BD_ADDR_LEN); - break; + return p_clcb; } } - return p_clcb; + return NULL; } /******************************************************************************* @@ -665,6 +665,12 @@ static void gap_ble_c_cmpl_cback (UINT16 conn_id, tGATTC_OPTYPE op, tGATT_STATUS switch (op_type) { case GATT_UUID_GAP_PREF_CONN_PARAM: GAP_TRACE_EVENT ("GATT_UUID_GAP_PREF_CONN_PARAM"); + /* Verify sufficient data length before reading connection parameters */ + if (p_data->att_value.len < 8) { + GAP_TRACE_ERROR ("GATT_UUID_GAP_PREF_CONN_PARAM: insufficient data length %d", p_data->att_value.len); + gap_ble_cl_op_cmpl(p_clcb, FALSE, 0, NULL); + break; + } /* Extract the peripheral preferred connection parameters and save them */ STREAM_TO_UINT16 (min, pp); @@ -679,7 +685,8 @@ static void gap_ble_c_cmpl_cback (UINT16 conn_id, tGATTC_OPTYPE op, tGATT_STATUS case GATT_UUID_GAP_DEVICE_NAME: GAP_TRACE_EVENT ("GATT_UUID_GAP_DEVICE_NAME\n"); - len = (UINT16)strlen((char *)pp); + /* Use att_value.len instead of strlen to avoid reading beyond buffer */ + len = p_data->att_value.len; if (len > GAP_CHAR_DEV_NAME_SIZE) { len = GAP_CHAR_DEV_NAME_SIZE; } @@ -687,6 +694,12 @@ static void gap_ble_c_cmpl_cback (UINT16 conn_id, tGATTC_OPTYPE op, tGATT_STATUS break; case GATT_UUID_GAP_CENTRAL_ADDR_RESOL: + /* Verify sufficient data length */ + if (p_data->att_value.len < 1) { + GAP_TRACE_ERROR ("GATT_UUID_GAP_CENTRAL_ADDR_RESOL: insufficient data length"); + gap_ble_cl_op_cmpl(p_clcb, FALSE, 0, NULL); + break; + } gap_ble_cl_op_cmpl(p_clcb, TRUE, 1, pp); break; } diff --git a/components/bt/host/bluedroid/stack/gap/gap_conn.c b/components/bt/host/bluedroid/stack/gap/gap_conn.c index db9065de8181..cdd6a2d3a9b5 100644 --- a/components/bt/host/bluedroid/stack/gap/gap_conn.c +++ b/components/bt/host/bluedroid/stack/gap/gap_conn.c @@ -148,7 +148,7 @@ UINT16 GAP_ConnOpen (const char *p_serv_name, UINT8 service_id, BOOLEAN is_serve memcpy (&p_ccb->rem_dev_address[0], p_rem_bda, BD_ADDR_LEN); } else if (!is_server) { - /* remore addr is not specified and is not a server -> bad */ + /* remote addr is not specified and is not a server -> bad */ return (GAP_INVALID_HANDLE); } @@ -775,7 +775,9 @@ static void gap_checks_con_flags (tGAP_CCB *p_ccb) if ((p_ccb->con_flags & GAP_CCB_FLAGS_CONN_DONE) == GAP_CCB_FLAGS_CONN_DONE) { p_ccb->con_state = GAP_CCB_STATE_CONNECTED; - p_ccb->p_callback (p_ccb->gap_handle, GAP_EVT_CONN_OPENED); + if (p_ccb->p_callback) { + p_ccb->p_callback (p_ccb->gap_handle, GAP_EVT_CONN_OPENED); + } } } @@ -933,7 +935,9 @@ static void gap_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg) gap_checks_con_flags (p_ccb); } else { - p_ccb->p_callback (p_ccb->gap_handle, GAP_EVT_CONN_CLOSED); + if (p_ccb->p_callback) { + p_ccb->p_callback (p_ccb->gap_handle, GAP_EVT_CONN_CLOSED); + } gap_release_ccb (p_ccb); } } @@ -964,7 +968,9 @@ static void gap_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed) L2CA_DISCONNECT_RSP (l2cap_cid); } - p_ccb->p_callback (p_ccb->gap_handle, GAP_EVT_CONN_CLOSED); + if (p_ccb->p_callback) { + p_ccb->p_callback (p_ccb->gap_handle, GAP_EVT_CONN_CLOSED); + } gap_release_ccb (p_ccb); } @@ -997,7 +1003,9 @@ static void gap_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg) p_ccb->rx_queue_size, p_msg->len); */ - p_ccb->p_callback (p_ccb->gap_handle, GAP_EVT_CONN_DATA_AVAIL); + if (p_ccb->p_callback) { + p_ccb->p_callback (p_ccb->gap_handle, GAP_EVT_CONN_DATA_AVAIL); + } } else { osi_free (p_msg); } @@ -1030,7 +1038,9 @@ static void gap_congestion_ind (UINT16 lcid, BOOLEAN is_congested) p_ccb->is_congested = is_congested; event = (is_congested) ? GAP_EVT_CONN_CONGESTED : GAP_EVT_CONN_UNCONGESTED; - p_ccb->p_callback (p_ccb->gap_handle, event); + if (p_ccb->p_callback) { + p_ccb->p_callback (p_ccb->gap_handle, event); + } if (!is_congested) { while ((p_buf = (BT_HDR *)fixed_queue_dequeue(p_ccb->tx_queue, 0)) != NULL) { diff --git a/components/bt/host/bluedroid/stack/gatt/att_protocol.c b/components/bt/host/bluedroid/stack/gatt/att_protocol.c index 310a9756efea..9b3a2507a3f8 100644 --- a/components/bt/host/bluedroid/stack/gatt/att_protocol.c +++ b/components/bt/host/bluedroid/stack/gatt/att_protocol.c @@ -33,7 +33,7 @@ #define GATT_HDR_FIND_TYPE_VALUE_LEN 21 #define GATT_OP_CODE_SIZE 1 /********************************************************************** -** ATT protocl message building utility * +** ATT protocol message building utility * ***********************************************************************/ /******************************************************************************* ** @@ -285,57 +285,89 @@ BT_HDR *attp_build_opcode_cmd(UINT8 op_code) ** Returns None. ** *******************************************************************************/ -BT_HDR *attp_build_value_cmd (UINT16 payload_size, UINT8 op_code, UINT16 handle, - UINT16 offset, UINT16 len, UINT8 *p_data) +BT_HDR *attp_build_value_cmd(UINT16 payload_size, UINT8 op_code, + UINT16 handle, UINT16 offset, + UINT16 len, UINT8 *p_data) { - BT_HDR *p_buf = NULL; - UINT8 *p, *pp, pair_len, *p_pair_len; + BT_HDR *p_buf = NULL; + UINT8 *p = NULL, *pp = NULL, *p_pair_len = NULL; + size_t pair_len = 0; + size_t size_now = 1; /* track current buffer size including op_code */ + +#define CHECK_SIZE() \ + do { \ + if (size_now > payload_size) { \ + GATT_TRACE_ERROR("payload size too small"); \ + osi_free(p_buf); \ + return NULL; \ + } \ + } while (0) + + /* allocate buffer with extra space for L2CAP offset */ + p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + payload_size + L2CAP_MIN_OFFSET); + if (!p_buf) return NULL; + + p = pp = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + CHECK_SIZE(); + UINT8_TO_STREAM(p, op_code); + p_buf->offset = L2CAP_MIN_OFFSET; + + /* handle Read By Type response: reserve space for pair_len */ + if (op_code == GATT_RSP_READ_BY_TYPE) { + p_pair_len = p++; + pair_len = len + 2; /* handle(2 bytes) + value length */ + size_now += 1; + CHECK_SIZE(); + /* pair_len will be backfilled after value is written */ + } - if ((p_buf = (BT_HDR *)osi_malloc((UINT16)(sizeof(BT_HDR) + payload_size + L2CAP_MIN_OFFSET))) != NULL) { - p = pp = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + /* write handle if needed */ + if (op_code != GATT_RSP_READ_BLOB && op_code != GATT_RSP_READ && + op_code != GATT_HANDLE_MULTI_VALUE_NOTIF) { + size_now += 2; + CHECK_SIZE(); + UINT16_TO_STREAM(p, handle); + } - UINT8_TO_STREAM (p, op_code); - p_buf->offset = L2CAP_MIN_OFFSET; - p_buf->len = 1; + /* write offset for prepare write requests */ + if (op_code == GATT_REQ_PREPARE_WRITE || op_code == GATT_RSP_PREPARE_WRITE) { + size_now += 2; + CHECK_SIZE(); + UINT16_TO_STREAM(p, offset); + } - if (op_code == GATT_RSP_READ_BY_TYPE) { - p_pair_len = p; - pair_len = len + 2; - UINT8_TO_STREAM (p, pair_len); - p_buf->len += 1; - } - if (op_code != GATT_RSP_READ_BLOB && op_code != GATT_RSP_READ && op_code != GATT_HANDLE_MULTI_VALUE_NOTIF) { - UINT16_TO_STREAM (p, handle); - p_buf->len += 2; + /* write value data, ensure it does not exceed payload_size */ + if (len > 0 && p_data != NULL) { + if (payload_size - size_now < len) { + len = payload_size - size_now; + if (op_code == GATT_RSP_READ_BY_TYPE) { + pair_len = len + 2; + } + GATT_TRACE_WARNING("attribute value too long, truncated to %d", len); } - if (op_code == GATT_REQ_PREPARE_WRITE || op_code == GATT_RSP_PREPARE_WRITE ) { - UINT16_TO_STREAM (p, offset); - p_buf->len += 2; - } + size_now += len; + CHECK_SIZE(); + + ARRAY_TO_STREAM(p, p_data, len); + } - if(payload_size < GATT_DEF_BLE_MTU_SIZE || payload_size > GATT_MAX_MTU_SIZE) { - GATT_TRACE_ERROR("invalid payload_size %d", payload_size); + /* backfill pair_len for Read By Type response */ + if (op_code == GATT_RSP_READ_BY_TYPE) { + if (pair_len > UINT8_MAX) { + GATT_TRACE_ERROR("pair_len > UINT8_MAX"); osi_free(p_buf); return NULL; } + *p_pair_len = (UINT8)pair_len; + } - if (len > 0 && p_data != NULL) { - /* ensure data not exceed MTU size */ - if (payload_size - p_buf->len < len) { - len = payload_size - p_buf->len; - /* update handle value pair length */ - if (op_code == GATT_RSP_READ_BY_TYPE) { - *p_pair_len = (len + 2); - } + /* update buffer length */ + p_buf->len = (UINT16)size_now; - GATT_TRACE_WARNING("attribute value too long, to be truncated to %d", len); - } +#undef CHECK_SIZE - ARRAY_TO_STREAM (p, p_data, len); - p_buf->len += len; - } - } return p_buf; } @@ -462,7 +494,7 @@ BT_HDR *attp_build_sr_msg(tGATT_TCB *p_tcb, UINT8 op_code, tGATT_SR_MSG *p_msg) ** Description This function sends the server response or indication message ** to client. ** -** Parameter p_tcb: pointer to the connecton control block. +** Parameter p_tcb: pointer to the connection control block. ** p_msg: pointer to message parameters structure. ** ** Returns GATT_SUCCESS if successfully sent; otherwise error code. diff --git a/components/bt/host/bluedroid/stack/gatt/gatt_cl.c b/components/bt/host/bluedroid/stack/gatt/gatt_cl.c index 12168dc00d22..ddd089f4e022 100644 --- a/components/bt/host/bluedroid/stack/gatt/gatt_cl.c +++ b/components/bt/host/bluedroid/stack/gatt/gatt_cl.c @@ -66,7 +66,7 @@ static const UINT16 disc_type_to_uuid[GATT_DISC_MAX] = { 0 /* no type filtering for DISC_CHAR_DSCPT */ }; -// Use for GATTC discover infomation print +// Use for GATTC discover information print #define GATT_DISC_INFO(fmt, args...) {if (gatt_cb.auto_disc == FALSE) BT_PRINT_I("BT_GATT", fmt, ## args);} /******************************************************************************* @@ -604,7 +604,7 @@ void gatt_process_prep_write_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op GATT_TRACE_DEBUG("value resp op_code = %s len = %d", gatt_dbg_op_name(op_code), len); - if (len < GATT_PREP_WRITE_RSP_MIN_LEN) { + if ((len < GATT_PREP_WRITE_RSP_MIN_LEN) || ((len - 4) > GATT_MAX_ATTR_LEN)) { GATT_TRACE_ERROR("illegal prepare write response length, discard"); gatt_end_operation(p_clcb, GATT_INVALID_PDU, &value); return; @@ -701,7 +701,7 @@ void gatt_process_notification(tGATT_TCB *p_tcb, UINT8 op_code, } p_tcb->ind_count = 0; - /* should notify all registered client with the handle value notificaion/indication + /* should notify all registered client with the handle value notification/indication Note: need to do the indication count and start timer first then do callback */ for (i = 0, p_reg = gatt_cb.cl_rcb; i < GATT_MAX_APPS; i++, p_reg++) { @@ -800,7 +800,7 @@ void gatt_process_read_by_type_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 handle_len = 4; } - value_len -= handle_len; /* substract the handle pairs bytes */ + value_len -= handle_len; /* subtract the handle pairs bytes */ len -= 1; while (len >= (handle_len + value_len)) { @@ -887,7 +887,7 @@ void gatt_process_read_by_type_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 gatt_end_operation(p_clcb, GATT_SUCCESS, (void *)p); } return; - } else { /* discover characterisitic */ + } else { /* discover characteristic */ STREAM_TO_UINT8 (record_value.dclr_value.char_prop, p); STREAM_TO_UINT16(record_value.dclr_value.val_handle, p); if (!GATT_HANDLE_IS_VALID(record_value.dclr_value.val_handle)) { @@ -979,7 +979,7 @@ void gatt_process_read_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code, if (len == (p_tcb->payload_size - 1) && /* full packet for read or read blob rsp */ len + offset < GATT_MAX_ATTR_LEN) { - GATT_TRACE_DEBUG("full pkt issue read blob for remianing bytes old offset=%d len=%d new offset=%d", + GATT_TRACE_DEBUG("full pkt issue read blob for remaining bytes old offset=%d len=%d new offset=%d", offset, len, p_clcb->counter); gatt_act_read(p_clcb, p_clcb->counter); } else { /* end of request, send callback */ diff --git a/components/bt/host/bluedroid/stack/gatt/gatt_db.c b/components/bt/host/bluedroid/stack/gatt/gatt_db.c index 8844ef5dc8e7..6c578e678f12 100644 --- a/components/bt/host/bluedroid/stack/gatt/gatt_db.c +++ b/components/bt/host/bluedroid/stack/gatt/gatt_db.c @@ -1039,20 +1039,23 @@ tGATT_STATUS gatts_write_attr_value_by_handle(tGATT_SVC_DB *p_db, return GATT_APP_RSP; } - if ((p_attr->p_value != NULL) && - (p_attr->p_value->attr_val.attr_max_len >= offset + len) && - p_attr->p_value->attr_val.attr_val != NULL) { - memcpy(p_attr->p_value->attr_val.attr_val + offset, p_value, len); - p_attr->p_value->attr_val.attr_len = len + offset; - return GATT_SUCCESS; - } else if (p_attr->p_value && p_attr->p_value->attr_val.attr_max_len < offset + len){ - GATT_TRACE_DEBUG("Remote device try to write with a length larger then attribute's max length\n"); - return GATT_INVALID_ATTR_LEN; - } else if ((p_attr->p_value == NULL) || (p_attr->p_value->attr_val.attr_val == NULL)){ + if (p_attr->p_value == NULL || p_attr->p_value->attr_val.attr_val == NULL) { GATT_TRACE_ERROR("Error in %s, line=%d, %s should not be NULL here\n", __func__, __LINE__, \ (p_attr->p_value == NULL) ? "p_value" : "attr_val.attr_val"); return GATT_UNKNOWN_ERROR; } + + /* Check for integer overflow: offset + len must not overflow UINT16 + * and must not exceed attr_max_len */ + if (offset > p_attr->p_value->attr_val.attr_max_len || + len > p_attr->p_value->attr_val.attr_max_len - offset) { + GATT_TRACE_DEBUG("Remote device try to write with a length larger than attribute's max length\n"); + return GATT_INVALID_ATTR_LEN; + } + + memcpy(p_attr->p_value->attr_val.attr_val + offset, p_value, len); + p_attr->p_value->attr_val.attr_len = offset + len; + return GATT_SUCCESS; } p_attr = (tGATT_ATTR16 *)p_attr->p_next; diff --git a/components/bt/host/bluedroid/stack/l2cap/l2c_link.c b/components/bt/host/bluedroid/stack/l2cap/l2c_link.c index fbad1cde151e..9cb412e7488b 100644 --- a/components/bt/host/bluedroid/stack/l2cap/l2c_link.c +++ b/components/bt/host/bluedroid/stack/l2cap/l2c_link.c @@ -377,6 +377,9 @@ BOOLEAN l2c_link_hci_disc_comp (UINT16 handle, UINT8 reason) #endif ///BLE_INCLUDED == TRUE status = FALSE; } else { +#if (BLE_INCLUDED == TRUE) + tL2C_LINK_STATE link_state_temp = p_lcb->link_state; +#endif // (BLE_INCLUDED == TRUE) /* There can be a case when we rejected PIN code authentication */ /* otherwise save a new reason */ if (btm_cb.acl_disc_reason != HCI_ERR_HOST_REJECT_SECURITY) { @@ -471,46 +474,54 @@ BOOLEAN l2c_link_hci_disc_comp (UINT16 handle, UINT8 reason) p_lcb->p_pending_ccb = NULL; #if (BLE_INCLUDED == TRUE) - if(reason == HCI_ERR_CONN_FAILED_ESTABLISHMENT && p_lcb->transport == BT_TRANSPORT_LE) { - #if (GATTC_CONNECT_RETRY_EN == TRUE) - if(p_lcb->link_role == HCI_ROLE_MASTER && p_lcb->retry_create_con < GATTC_CONNECT_RETRY_COUNT) { - L2CAP_TRACE_DEBUG("master retry connect, retry count %d reason 0x%x\n", p_lcb->retry_create_con, reason); - p_lcb->retry_create_con ++; - // create connection retry - if (l2cu_create_conn(p_lcb, BT_TRANSPORT_LE)) { - btm_acl_removed (p_lcb->remote_bd_addr, BT_TRANSPORT_LE); - lcb_is_free = FALSE; /* still using this lcb */ - } else { - L2CAP_TRACE_ERROR("master retry connect failed"); + if(p_lcb->transport == BT_TRANSPORT_LE) { + // for legacy adv, adv restart in gatt_le_connect_cback->gatt_cleanup_upon_disc->BTM_Recovery_Pre_State + if (reason == HCI_ERR_CONN_FAILED_ESTABLISHMENT) { + + #if (BLE_42_FEATURE_SUPPORT == TRUE) + #if (BLE_42_ADV_EN == TRUE) + if(!btm_ble_inter_get() && p_lcb->link_role == HCI_ROLE_SLAVE) { + L2CAP_TRACE_DEBUG("slave resatrt adv, retry count %d reason 0x%x\n", p_lcb->retry_create_con, reason); + tBTM_STATUS start_adv_status = btm_ble_start_adv(); + if (start_adv_status != BTM_SUCCESS) { + L2CAP_TRACE_ERROR("slave resatrt adv failed (err 0x%x)", start_adv_status); + } } + #endif // #if (BLE_42_ADV_EN == TRUE) + #endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) } - #endif // (GATTC_CONNECT_RETRY_EN == TRUE) - - #if (BLE_50_FEATURE_SUPPORT == TRUE) - #if (BLE_50_EXTEND_ADV_EN == TRUE) - if(btm_ble_inter_get() && p_lcb->link_role == HCI_ROLE_SLAVE) { - p_lcb->retry_create_con ++; - L2CAP_TRACE_DEBUG("slave restart extend adv, retry count %d reason 0x%x\n", p_lcb->retry_create_con, reason); - tBTM_STATUS start_adv_status = BTM_BleStartExtAdvRestart(handle); - if (start_adv_status != BTM_SUCCESS) { - L2CAP_TRACE_ERROR("slave restart extend adv failed (err 0x%x)", start_adv_status); + // try to reconnect for master + if ((reason == HCI_ERR_CONN_FAILED_ESTABLISHMENT) || (link_state_temp == LST_CONNECTING)) { + + #if (GATTC_CONNECT_RETRY_EN == TRUE) + if(p_lcb->link_role == HCI_ROLE_MASTER && p_lcb->retry_create_con < GATTC_CONNECT_RETRY_COUNT) { + L2CAP_TRACE_DEBUG("master retry connect, retry count %d reason 0x%x\n", p_lcb->retry_create_con, reason); + p_lcb->retry_create_con ++; + // create connection retry + if (l2cu_create_conn(p_lcb, BT_TRANSPORT_LE)) { + btm_acl_removed (p_lcb->remote_bd_addr, BT_TRANSPORT_LE); + lcb_is_free = FALSE; /* still using this lcb */ + } else { + L2CAP_TRACE_ERROR("master retry connect failed"); + } } + #endif // (GATTC_CONNECT_RETRY_EN == TRUE) } - #endif // #if (BLE_50_EXTEND_ADV_EN == TRUE) - #endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) - - #if (BLE_42_FEATURE_SUPPORT == TRUE) - #if (BLE_42_ADV_EN == TRUE) - if(!btm_ble_inter_get() && p_lcb->link_role == HCI_ROLE_SLAVE) { - p_lcb->retry_create_con ++; - L2CAP_TRACE_DEBUG("slave resatrt adv, retry count %d reason 0x%x\n", p_lcb->retry_create_con, reason); - tBTM_STATUS start_adv_status = btm_ble_start_adv(); - if (start_adv_status != BTM_SUCCESS) { - L2CAP_TRACE_ERROR("slave resatrt adv failed (err 0x%x)", start_adv_status); - } + // try to restart extended adv for slave + if ((reason == HCI_ERR_CONN_FAILED_ESTABLISHMENT) || (link_state_temp == LST_DISCONNECTED)) { + #if (BLE_50_FEATURE_SUPPORT == TRUE) + #if (BLE_50_EXTEND_ADV_EN == TRUE) + if(btm_ble_inter_get() && p_lcb->link_role == HCI_ROLE_SLAVE) { + L2CAP_TRACE_DEBUG("slave restart extend adv, retry count %d reason 0x%x\n", p_lcb->retry_create_con, reason); + tBTM_STATUS start_adv_status = BTM_BleStartExtAdvRestart(handle); + if (start_adv_status != BTM_SUCCESS) { + L2CAP_TRACE_ERROR("slave restart extend adv failed (err 0x%x)", start_adv_status); + } + } + #endif // #if (BLE_50_EXTEND_ADV_EN == TRUE) + #endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) } - #endif // #if (BLE_42_ADV_EN == TRUE) - #endif // #if (BLE_42_FEATURE_SUPPORT == TRUE) + } diff --git a/components/bt/host/bluedroid/stack/smp/include/smp_int.h b/components/bt/host/bluedroid/stack/smp/include/smp_int.h index f3ab7ad44585..ca2dbfaf07b5 100644 --- a/components/bt/host/bluedroid/stack/smp/include/smp_int.h +++ b/components/bt/host/bluedroid/stack/smp/include/smp_int.h @@ -197,7 +197,7 @@ enum { }; typedef UINT8 tSMP_BR_STATE; -/* random and encrption activity state */ +/* random and encryption activity state */ enum { SMP_GEN_COMPARE = 1, SMP_GEN_CONFIRM, @@ -363,6 +363,9 @@ extern tSMP_CB *smp_cb_ptr; /* Functions provided by att_main.c */ extern void smp_init (void); +/* SMP command sizes per spec - defined in smp_utils.c */ +extern const UINT8 smp_cmd_size_per_spec[]; + /* smp main */ extern void smp_sm_event(tSMP_CB *p_cb, tSMP_EVENT event, void *p_data); diff --git a/components/bt/host/bluedroid/stack/smp/smp_l2c.c b/components/bt/host/bluedroid/stack/smp/smp_l2c.c index a6f0670598e2..1f3db502e23e 100644 --- a/components/bt/host/bluedroid/stack/smp/smp_l2c.c +++ b/components/bt/host/bluedroid/stack/smp/smp_l2c.c @@ -156,15 +156,31 @@ static void smp_connect_callback (UINT16 channel, BD_ADDR bd_addr, BOOLEAN conne static void smp_data_received(UINT16 channel, BD_ADDR bd_addr, BT_HDR *p_buf) { tSMP_CB *p_cb = &smp_cb; - UINT8 *p = (UINT8 *)(p_buf + 1) + p_buf->offset; - UINT8 cmd ; + UINT8 *p; + UINT8 cmd; SMP_TRACE_EVENT ("\nSMDBG l2c %s\n", __FUNCTION__); + /* Validate packet length before accessing data to prevent out-of-bounds read */ + if (p_buf->len < 1) { + SMP_TRACE_WARNING ("Ignore empty SMP packet (len=%d)\n", p_buf->len); + osi_free (p_buf); + return; + } + + p = (UINT8 *)(p_buf + 1) + p_buf->offset; STREAM_TO_UINT8(cmd, p); /* sanity check */ if ((SMP_OPCODE_MAX < cmd) || (SMP_OPCODE_MIN > cmd)) { - SMP_TRACE_WARNING( "Ignore received command with RESERVED code 0x%02x\n", cmd); + SMP_TRACE_WARNING ("Ignore received command with RESERVED code 0x%02x\n", cmd); + osi_free (p_buf); + return; + } + + /* Validate command length to prevent out-of-bounds read in handler functions */ + if (p_buf->len != smp_cmd_size_per_spec[cmd]) { + SMP_TRACE_WARNING ("Ignore SMP cmd 0x%02x with invalid length %d (expected %d)\n", + cmd, p_buf->len, smp_cmd_size_per_spec[cmd]); osi_free (p_buf); return; } diff --git a/components/bt/host/bluedroid/stack/smp/smp_utils.c b/components/bt/host/bluedroid/stack/smp/smp_utils.c index 48ba6c0ae302..8ec4d548acec 100644 --- a/components/bt/host/bluedroid/stack/smp/smp_utils.c +++ b/components/bt/host/bluedroid/stack/smp/smp_utils.c @@ -55,7 +55,7 @@ #define SMP_PAIR_KEYPR_NOTIF_SIZE (1 /* opcode */ + 1 /*Notif Type*/) /* SMP command sizes per spec */ -static const UINT8 smp_cmd_size_per_spec[] = { +const UINT8 smp_cmd_size_per_spec[] = { 0, SMP_PAIRING_REQ_SIZE, /* 0x01: pairing request */ SMP_PAIRING_REQ_SIZE, /* 0x02: pairing response */ diff --git a/components/bt/host/nimble/nimble b/components/bt/host/nimble/nimble index 6f9ff29d2619..9551ac31af03 160000 --- a/components/bt/host/nimble/nimble +++ b/components/bt/host/nimble/nimble @@ -1 +1 @@ -Subproject commit 6f9ff29d261939945c4f2fa74c01763009cb2cd0 +Subproject commit 9551ac31af0348d7de6cbe7527de3e5ba205460d diff --git a/components/esp_coex/lib b/components/esp_coex/lib index adb3badffe12..63e292b57b2c 160000 --- a/components/esp_coex/lib +++ b/components/esp_coex/lib @@ -1 +1 @@ -Subproject commit adb3badffe1201033d23c0a3ab596faadd673539 +Subproject commit 63e292b57b2cda9f9496a71a04bec43e1f0caeba diff --git a/components/esp_common/include/esp_idf_version.h b/components/esp_common/include/esp_idf_version.h index 30015cf1d7af..966577bb6b4d 100644 --- a/components/esp_common/include/esp_idf_version.h +++ b/components/esp_common/include/esp_idf_version.h @@ -15,7 +15,7 @@ extern "C" { /** Minor version number (x.X.x) */ #define ESP_IDF_VERSION_MINOR 5 /** Patch version number (x.x.X) */ -#define ESP_IDF_VERSION_PATCH 1 +#define ESP_IDF_VERSION_PATCH 2 /** * Macro to convert IDF version number into an integer diff --git a/components/esp_driver_mcpwm/test_apps/mcpwm/sdkconfig.defaults.esp32p4 b/components/esp_driver_mcpwm/test_apps/mcpwm/sdkconfig.defaults.esp32c5 similarity index 68% rename from components/esp_driver_mcpwm/test_apps/mcpwm/sdkconfig.defaults.esp32p4 rename to components/esp_driver_mcpwm/test_apps/mcpwm/sdkconfig.defaults.esp32c5 index d2699b2221b8..db575808cf98 100644 --- a/components/esp_driver_mcpwm/test_apps/mcpwm/sdkconfig.defaults.esp32p4 +++ b/components/esp_driver_mcpwm/test_apps/mcpwm/sdkconfig.defaults.esp32c5 @@ -1,3 +1,2 @@ CONFIG_SPIRAM=y -CONFIG_SPIRAM_MODE_HEX=y CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=0 diff --git a/components/esp_hw_support/clk_utils.c b/components/esp_hw_support/clk_utils.c index 4415d4175445..99eabf816ef7 100644 --- a/components/esp_hw_support/clk_utils.c +++ b/components/esp_hw_support/clk_utils.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "sdkconfig.h" #include "esp_check.h" #include "esp_log.h" @@ -17,25 +18,31 @@ #include "esp_private/mspi_timing_tuning.h" #include "esp_private/esp_clk_utils.h" +// Not directly divide to avoid truncation issue +// DIG-498 +#if CONFIG_IDF_TARGET_ESP32P4 +#define BELOW_FREQ_THRESHOLD(freq) ((freq) < CONFIG_SPIRAM_SPEED) +#elif CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61 || CONFIG_IDF_TARGET_ESP32H4 +#define BELOW_FREQ_THRESHOLD(freq) ((freq) * 8 < CONFIG_SPIRAM_SPEED) +#endif + #if !CONFIG_APP_BUILD_TYPE_PURE_RAM_APP void esp_clk_utils_mspi_speed_mode_sync_before_cpu_freq_switching(uint32_t target_cpu_src_freq, uint32_t target_cpu_freq) { #if MSPI_TIMING_LL_FLASH_CPU_CLK_SRC_BINDED (void) target_cpu_freq; + /* For ESP32S3, the clock source of MSPI is same as the CPU. When CPU use XTAL as clock source, we need to sync the + * MSPI speed mode. */ if (target_cpu_src_freq <= clk_ll_xtal_load_freq_mhz()) { mspi_timing_change_speed_mode_cache_safe(true); } -#elif CONFIG_IDF_TARGET_ESP32P4 - (void) target_cpu_src_freq; - /** - * Workaround for ESP32P4, - * f_cpu >= f_mspi +#elif SOC_SPI_MEM_PSRAM_FREQ_AXI_CONSTRAINED && CONFIG_SPIRAM + /* On chips with AXI bus, currently there is a restriction that AXI frequency (usually equals to a portion of CPU + * frequency) needs to be greater than or equal to MSPI PSRAM frequency to avoid writing MSPI FIFO overflow. */ - if (((target_cpu_freq) < CONFIG_ESPTOOLPY_FLASHFREQ_VAL) -#if CONFIG_SPIRAM - || ((target_cpu_freq) < CONFIG_SPIRAM_SPEED) -#endif - ) { + if (BELOW_FREQ_THRESHOLD(target_cpu_freq)) { + // Before switching to low speed mode, verify CPU frequency meets the constraint + assert(target_cpu_freq >= mspi_timing_get_psram_low_speed_freq_mhz()); mspi_timing_change_speed_mode_cache_safe(true); } #else @@ -51,17 +58,11 @@ void esp_clk_utils_mspi_speed_mode_sync_after_cpu_freq_switching(uint32_t target if (target_cpu_src_freq > clk_ll_xtal_load_freq_mhz()) { mspi_timing_change_speed_mode_cache_safe(false); } -#elif CONFIG_IDF_TARGET_ESP32P4 - (void) target_cpu_src_freq; - /** - * Workaround for ESP32P4, - * f_cpu >= f_mspi +#elif SOC_SPI_MEM_PSRAM_FREQ_AXI_CONSTRAINED && CONFIG_SPIRAM + /* On chips with AXI bus, currently there is a restriction that AXI frequency (usually equals to a portion of CPU + * frequency) needs to be greater than or equal to MSPI PSRAM frequency to avoid writing MSPI FIFO overflow. */ - if (((target_cpu_freq) >= CONFIG_ESPTOOLPY_FLASHFREQ_VAL) -#if CONFIG_SPIRAM - && ((target_cpu_freq) >= CONFIG_SPIRAM_SPEED) -#endif - ) { + if (!BELOW_FREQ_THRESHOLD(target_cpu_freq)) { mspi_timing_change_speed_mode_cache_safe(false); } #else diff --git a/components/esp_hw_support/include/esp_private/sleep_retention.h b/components/esp_hw_support/include/esp_private/sleep_retention.h index 6603c5f0d768..0b211ff885d1 100644 --- a/components/esp_hw_support/include/esp_private/sleep_retention.h +++ b/components/esp_hw_support/include/esp_private/sleep_retention.h @@ -276,6 +276,15 @@ void sleep_retention_do_extra_retention(bool backup_or_restore); void sleep_retention_do_system_retention(bool backup_or_restore); #endif +#if SOC_PM_SUPPORT_PMU_MODEM_STATE +/** + * @brief Software trigger REGDMA to do phy linked list retention + * + * @param backup_or_restore true for backup register context to memory + * or false for restore to register from memory + */ +void sleep_retention_do_phy_retention(bool backup_or_restore); +#endif /*SOC_PM_SUPPORT_PMU_MODEM_STATE */ #endif // SOC_PAU_SUPPORTED #ifdef __cplusplus diff --git a/components/esp_hw_support/mspi_timing_tuning/include/esp_private/mspi_timing_tuning.h b/components/esp_hw_support/mspi_timing_tuning/include/esp_private/mspi_timing_tuning.h index c1e999cc181e..be59ef7dfb6c 100644 --- a/components/esp_hw_support/mspi_timing_tuning/include/esp_private/mspi_timing_tuning.h +++ b/components/esp_hw_support/mspi_timing_tuning/include/esp_private/mspi_timing_tuning.h @@ -24,6 +24,12 @@ extern "C" { */ void mspi_timing_enter_low_speed_mode(bool control_spi1); +/** + * @brief Get PSRAM frequency in low speed mode (MHz) + * @return PSRAM frequency in MHz when in low speed mode + */ +uint32_t mspi_timing_get_psram_low_speed_freq_mhz(void); + /** * @brief Make MSPI work under the frequency as users set, may add certain delays to MSPI RX direction to meet timing requirements. * @param control_spi1 Select whether to control SPI1. For tuning, we need to use SPI1. After tuning (during startup stage), let the flash driver to control SPI1 @@ -54,7 +60,6 @@ void mspi_timing_psram_tuning(void); */ void mspi_timing_set_pin_drive_strength(void); - #ifdef __cplusplus } #endif diff --git a/components/esp_hw_support/mspi_timing_tuning/mspi_timing_tuning.c b/components/esp_hw_support/mspi_timing_tuning/mspi_timing_tuning.c index 97517c565ecf..e5451d521657 100644 --- a/components/esp_hw_support/mspi_timing_tuning/mspi_timing_tuning.c +++ b/components/esp_hw_support/mspi_timing_tuning/mspi_timing_tuning.c @@ -481,6 +481,11 @@ void mspi_timing_psram_tuning(void) /*------------------------------------------------------------------------------ * APIs to make SPI0 (and SPI1) FLASH work for high/low freq *----------------------------------------------------------------------------*/ +uint32_t mspi_timing_get_psram_low_speed_freq_mhz(void) +{ + return 20; +} + void mspi_timing_enter_low_speed_mode(bool control_spi1) { #if MSPI_TIMING_LL_FLASH_CLK_SRC_CHANGEABLE @@ -500,14 +505,14 @@ void mspi_timing_enter_low_speed_mode(bool control_spi1) * Should be extended to other no-timing-tuning chips if needed. e.g.: * we still need to turn down Flash / PSRAM clock speed at a certain period of time */ - mspi_timing_config_set_flash_clock(20, MSPI_TIMING_SPEED_MODE_LOW_PERF, control_spi1); - mspi_timing_config_set_psram_clock(20, MSPI_TIMING_SPEED_MODE_LOW_PERF, control_spi1); -#endif //#if SOC_SPI_MEM_SUPPORT_TIMING_TUNING - + uint32_t low_speed_freq_mhz = mspi_timing_get_psram_low_speed_freq_mhz(); + mspi_timing_config_set_flash_clock(low_speed_freq_mhz, MSPI_TIMING_SPEED_MODE_LOW_PERF, control_spi1); + mspi_timing_config_set_psram_clock(low_speed_freq_mhz, MSPI_TIMING_SPEED_MODE_LOW_PERF, control_spi1); #if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING mspi_timing_flash_config_clear_tuning_regs(control_spi1); mspi_timing_psram_config_clear_tuning_regs(control_spi1); #endif //#if MSPI_TIMING_FLASH_NEEDS_TUNING || MSPI_TIMING_PSRAM_NEEDS_TUNING +#endif //#if SOC_SPI_MEM_SUPPORT_TIMING_TUNING } /** @@ -568,10 +573,24 @@ void mspi_timing_change_speed_mode_cache_safe(bool switch_down) if (switch_down) { //enter MSPI low speed mode, extra delays should be removed +#if CONFIG_IDF_TARGET_ESP32C5 + // ESP32-C5 needs to perform encrypted flash writes even when CPU frequency is reduced. + // Since encrypted writes use SPI1, we need to configure SPI1 timing registers as well + // during runtime frequency switching to ensure proper operation. + mspi_timing_enter_low_speed_mode(true); +#else mspi_timing_enter_low_speed_mode(false); +#endif } else { //enter MSPI high speed mode, extra delays should be considered +#if CONFIG_IDF_TARGET_ESP32C5 + // ESP32-C5 needs to perform encrypted flash writes even when CPU frequency is reduced. + // Since encrypted writes use SPI1, we need to configure SPI1 timing registers as well + // during runtime frequency switching to ensure proper operation. + mspi_timing_enter_high_speed_mode(true); +#else mspi_timing_enter_high_speed_mode(false); +#endif } #if SOC_CACHE_FREEZE_SUPPORTED diff --git a/components/esp_hw_support/sleep_modem.c b/components/esp_hw_support/sleep_modem.c index 12269b777c3a..58e7abfbb749 100644 --- a/components/esp_hw_support/sleep_modem.c +++ b/components/esp_hw_support/sleep_modem.c @@ -173,10 +173,8 @@ __attribute__((unused)) void sleep_modem_wifi_modem_state_deinit(void) void IRAM_ATTR sleep_modem_wifi_do_phy_retention(bool restore) { - if (restore) { - pau_regdma_trigger_modem_link_restore(); - } else { - pau_regdma_trigger_modem_link_backup(); + sleep_retention_do_phy_retention(!restore); + if (!restore) { s_sleep_modem.wifi.modem_state_phy_done = 1; } } diff --git a/components/esp_hw_support/sleep_modes.c b/components/esp_hw_support/sleep_modes.c index 391a7d4063d0..cdedc68a04d4 100644 --- a/components/esp_hw_support/sleep_modes.c +++ b/components/esp_hw_support/sleep_modes.c @@ -2754,6 +2754,9 @@ static SLEEP_FN_ATTR uint32_t get_power_down_flags(void) // TOP power domain depends on the RTC_PERIPH power domain on ESP32C6, RTC_PERIPH should only be disabled when the TOP domain is down. pd_flags &= ~RTC_SLEEP_PD_RTC_PERIPH; } +#elif CONFIG_IDF_TARGET_ESP32C5 + // TODO: [ESP32C5] PM-642 RNG module depends on LP PERIPH domain, force it on temporary. + pd_flags &= ~RTC_SLEEP_PD_RTC_PERIPH; #endif return pd_flags; } diff --git a/components/esp_hw_support/sleep_retention.c b/components/esp_hw_support/sleep_retention.c index 8afa63d6a8be..003d1f1f1d1f 100644 --- a/components/esp_hw_support/sleep_retention.c +++ b/components/esp_hw_support/sleep_retention.c @@ -1005,3 +1005,23 @@ void IRAM_ATTR sleep_retention_do_system_retention(bool backup_or_restore) } } #endif + +#if SOC_PM_SUPPORT_PMU_MODEM_STATE +void IRAM_ATTR sleep_retention_do_phy_retention(bool backup_or_restore) +{ +/* since the PHY link and other module links are within the sleep-retention entry (4) context, +* add mutex protection to avoid data race. +*/ +#if SOC_PM_PAU_REGDMA_COMMON_PHY_LINK_ENTRY + _lock_acquire_recursive(&s_retention.lock); +#endif + if (backup_or_restore) { + pau_regdma_trigger_modem_link_backup(); + } else { + pau_regdma_trigger_modem_link_restore(); + } +#if SOC_PM_PAU_REGDMA_COMMON_PHY_LINK_ENTRY + _lock_release_recursive(&s_retention.lock); +#endif +} +#endif /*SOC_PM_SUPPORT_PMU_MODEM_STATE */ diff --git a/components/esp_phy/lib b/components/esp_phy/lib index cb9f62b42096..3d57415af6e4 160000 --- a/components/esp_phy/lib +++ b/components/esp_phy/lib @@ -1 +1 @@ -Subproject commit cb9f62b42096ec1e20c05f2aa57fdd0d04df4d33 +Subproject commit 3d57415af6e4c92eff2c4c3463e20a51d7340aba diff --git a/components/esp_pm/CMakeLists.txt b/components/esp_pm/CMakeLists.txt index 27ce9ada3cfe..e540b64b3242 100644 --- a/components/esp_pm/CMakeLists.txt +++ b/components/esp_pm/CMakeLists.txt @@ -1,10 +1,21 @@ idf_build_get_property(target IDF_TARGET) +set(priv_requires esp_system esp_driver_gpio esp_timer) if(${target} STREQUAL "linux") return() # This component is not supported by the POSIX/Linux simulator endif() -idf_component_register(SRCS "pm_locks.c" "pm_trace.c" "pm_impl.c" +set(srcs "pm_locks.c" "pm_trace.c" "pm_impl.c") +if(CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED) + list(APPEND srcs "pm_c5_flash_freq_limit.c") +endif() + +if(${target} STREQUAL "esp32c5") + # pm_c5_flash_freq_limit.c needs spi_flash header when CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED is enabled + list(APPEND priv_requires spi_flash) +endif() + +idf_component_register(SRCS ${srcs} INCLUDE_DIRS include - PRIV_REQUIRES esp_system esp_driver_gpio esp_timer + PRIV_REQUIRES "${priv_requires}" LDFRAGMENTS linker.lf) diff --git a/components/esp_pm/Kconfig b/components/esp_pm/Kconfig index ee4ec6f5604d..ca843fff4895 100644 --- a/components/esp_pm/Kconfig +++ b/components/esp_pm/Kconfig @@ -210,4 +210,12 @@ menu "Power Management" NOTE: Enabling these callbacks may change sleep duration calculations based on time spent in callback and hence it is highly recommended to keep them as short as possible + config PM_WORKAROUND_FREQ_LIMIT_ENABLED + bool + default y if SPI_FLASH_FREQ_LIMIT_C5_240MHZ + help + Workaround for frequency limit during encrypted flash writes. + Only enabled when SPI_FLASH_FREQ_LIMIT_C5_240MHZ is enabled. + This is an internal configuration, automatically set based on SPI Flash configuration. + endmenu # "Power Management" diff --git a/components/esp_pm/include/esp_private/pm_impl.h b/components/esp_pm/include/esp_private/pm_impl.h index 02364050c7cd..56fab2fec98a 100644 --- a/components/esp_pm/include/esp_private/pm_impl.h +++ b/components/esp_pm/include/esp_private/pm_impl.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2016-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2016-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -15,7 +15,6 @@ #include "soc/rtc.h" #include "esp_pm.h" -#include "esp_timer.h" #include "sdkconfig.h" #ifdef __cplusplus @@ -26,11 +25,11 @@ extern "C" { * This is an enum of possible power modes supported by the implementation */ typedef enum { - PM_MODE_LIGHT_SLEEP,//!< Light sleep - PM_MODE_APB_MIN, //!< Idle (no CPU frequency or APB frequency locks) - PM_MODE_APB_MAX, //!< Maximum APB frequency mode - PM_MODE_CPU_MAX, //!< Maximum CPU frequency mode - PM_MODE_COUNT //!< Number of items + PM_MODE_LIGHT_SLEEP, //!< Light sleep + PM_MODE_APB_MIN, //!< Idle (no CPU frequency or APB frequency locks) + PM_MODE_APB_MAX, //!< Maximum APB frequency mode + PM_MODE_CPU_MAX, //!< Maximum CPU frequency mode + PM_MODE_COUNT //!< Number of items } pm_mode_t; /** @@ -141,8 +140,18 @@ esp_err_t esp_pm_register_skip_light_sleep_callback(skip_light_sleep_cb_t cb); */ esp_err_t esp_pm_unregister_skip_light_sleep_callback(skip_light_sleep_cb_t cb); + +/** + * @brief Initialize flash frequency limit + * + * This function initializes the flash frequency limit. + * @note This function is only available when CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED is enabled. + */ +void esp_pm_flash_freq_limit_init(void); + #ifdef CONFIG_PM_PROFILING #define WITH_PROFILING +#include "esp_timer.h" #endif #ifdef WITH_PROFILING diff --git a/components/esp_pm/include/esp_private/pm_impl_freq_limit.h b/components/esp_pm/include/esp_private/pm_impl_freq_limit.h new file mode 100644 index 000000000000..252ca780d7bb --- /dev/null +++ b/components/esp_pm/include/esp_private/pm_impl_freq_limit.h @@ -0,0 +1,59 @@ +/* + * SPDX-FileCopyrightText: 2016-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "sdkconfig.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED + +/** + * @brief Initialize and pre-calculate forced CPU_MAX frequency configuration (private function for spi_flash) + * + * This function pre-calculates and stores the forced CPU_MAX frequency configuration + * based on the given frequency limit. The configuration is computed once during + * initialization and reused during runtime for better performance. + * + * @param limit_freq_mhz Frequency limit in MHz + * @note This is a private function, only for use by spi_flash component. + * @note Must be called during initialization before esp_pm_impl_cpu_max_freq_force(). + * @note This function is only available when CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED is enabled and CONFIG_PM_ENABLE is enabled. + */ +void esp_pm_impl_cpu_max_freq_force_init(uint32_t limit_freq_mhz); + +/** + * @brief Force CPU_MAX frequency to pre-configured limit (private function for spi_flash) + * + * This function activates the pre-configured forced CPU_MAX frequency limit. + * When forced, all reads of CPU_MAX frequency will use the pre-configured value + * instead of the configured value. + * + * @note This is a private function, only for use by spi_flash component. + * @note The forced frequency configuration must be pre-calculated during initialization. + * @note This function is only available when CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED is enabled and CONFIG_PM_ENABLE is enabled. + */ +void esp_pm_impl_cpu_max_freq_force(void); + +/** + * @brief Unforce CPU_MAX frequency (private function for spi_flash) + * + * This function removes the forced CPU_MAX frequency, allowing the configured + * value to be used again. + * @note This is a private function, only for use by spi_flash component. + * @note This function is only available when CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED is enabled and CONFIG_PM_ENABLE is enabled. + */ +void esp_pm_impl_cpu_max_freq_unforce(void); + +#endif // CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_pm/pm_c5_flash_freq_limit.c b/components/esp_pm/pm_c5_flash_freq_limit.c new file mode 100644 index 000000000000..f07701565033 --- /dev/null +++ b/components/esp_pm/pm_c5_flash_freq_limit.c @@ -0,0 +1,168 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + + //This file implements esp_flash_freq_limit_cb and esp_flash_freq_unlimit_cb callbacks to limit and unlimit CPU + //frequency when encrypted flash writes are performed. + +#include "sdkconfig.h" + +#include "esp_log.h" +#include "esp_attr.h" +#include "esp_rom_sys.h" +#include "soc/chip_revision.h" +#include "soc/rtc.h" +#include "hal/efuse_hal.h" + +#include "esp_private/pm_impl_freq_limit.h" +#include "esp_private/spi_flash_freq_limit_cbs.h" +#include "esp_private/esp_clk_utils.h" + +/** + * @brief Get the frequency limit for flash encryption lock based on chip revision + * + * @return uint32_t Frequency limit (MHz) + * + * Logic: + * - v1.2+: limit to 160MHz + * - v1.0: limit to 80MHz + */ +static uint32_t IRAM_ATTR get_encrypt_lock_freq_limit(void) +{ + unsigned int chip_revision = efuse_hal_chip_revision(); + if (ESP_CHIP_REV_ABOVE(chip_revision, 102)) { + return 160; + } else { + return 80; + } +} + +void esp_pm_flash_freq_limit_init(void) +{ + uint32_t limit_freq_mhz = get_encrypt_lock_freq_limit(); + ESP_EARLY_LOGW("spi_flash", "CPU frequency is set to 240MHz. esp_flash_write_encrypted() will automatically limit CPU frequency to %dMHz during execution.", limit_freq_mhz); +#ifdef CONFIG_PM_ENABLE + /* Pre-calculate and store forced frequency configuration during initialization. + * This is done here to avoid runtime calculation overhead in lock/unlock functions. + */ + esp_pm_impl_cpu_max_freq_force_init(limit_freq_mhz); +#endif +} + +#if !CONFIG_PM_ENABLE +/* Saved original frequency for !PM_ENABLE case (0 means no change was made) */ +static uint32_t s_saved_freq_mhz = 0; + +// Switch CPU frequency with all necessary synchronization (for !PM_ENABLE case) +// Similar to do_switch() but without PM-specific state management +static void IRAM_ATTR esp_pm_impl_switch_cpu_freq(const rtc_cpu_freq_config_t *old_config, const rtc_cpu_freq_config_t *new_config) +{ + if (new_config->freq_mhz != old_config->freq_mhz) { + //No need to run on_freq_update for ccount, since that's not supported on C5. +#if !CONFIG_APP_BUILD_TYPE_PURE_RAM_APP + /* Synchronize MSPI speed mode before CPU frequency switching */ + esp_clk_utils_mspi_speed_mode_sync_before_cpu_freq_switching(new_config->source_freq_mhz, new_config->freq_mhz); +#endif + + //Not taking s_time_update_lock since C5 have fixed divider for systimer + rtc_clk_cpu_freq_set_config_fast(new_config); + +#if !CONFIG_APP_BUILD_TYPE_PURE_RAM_APP + /* Synchronize MSPI speed mode after CPU frequency switching */ + esp_clk_utils_mspi_speed_mode_sync_after_cpu_freq_switching(new_config->source_freq_mhz, new_config->freq_mhz); +#endif + } +} + +/** + * @brief Limit CPU frequency to target frequency (for !PM_ENABLE case only) + * + * This function automatically reads the current CPU frequency, saves it, + * and switches to the target frequency if current frequency is higher. + * The original frequency can be restored by calling unlimit_cpu_freq(). + * + * @param target_freq_mhz Target frequency limit in MHz + * @note limit->unlimit cannot be nested, and this function must not be called concurrently + * @note This is a private function, only for use by spi_flash component. + * @note This function is only available when CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED is enabled and CONFIG_PM_ENABLE is not set. + */ +static void IRAM_ATTR limit_cpu_freq(uint32_t target_freq_mhz) +{ + /* PM not enabled, directly switch frequency and save original frequency */ + rtc_cpu_freq_config_t old_config, new_config; + rtc_clk_cpu_freq_get_config(&old_config); + + if (old_config.freq_mhz > target_freq_mhz) { + s_saved_freq_mhz = old_config.freq_mhz; + if (rtc_clk_cpu_freq_mhz_to_config(target_freq_mhz, &new_config)) { + /* Use PM implementation function to switch frequency with all necessary synchronization */ + esp_pm_impl_switch_cpu_freq(&old_config, &new_config); + const unsigned wait_clock_stable_us = 10; + esp_rom_delay_us(wait_clock_stable_us); + } + } else { + s_saved_freq_mhz = 0; /* No change needed */ + } +} + +/** + * @brief Unlimit CPU frequency, restoring to original frequency (for !PM_ENABLE case only) + * + * This function restores the CPU frequency to the value saved by + * limit_cpu_freq(). If no frequency change was made, this function + * does nothing. + * + * @note limit->unlimit cannot be nested, and this function must not be called concurrently + * @note This is a private function, only for use by spi_flash component. + * @note This function is only available when CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED is enabled and CONFIG_PM_ENABLE is not set. + */ +static void IRAM_ATTR unlimit_cpu_freq(void) +{ + /* PM not enabled, restore to original frequency */ + if (s_saved_freq_mhz > 0) { + rtc_cpu_freq_config_t old_config, new_config; + rtc_clk_cpu_freq_get_config(&old_config); + if (rtc_clk_cpu_freq_mhz_to_config(s_saved_freq_mhz, &new_config)) { + /* Use PM implementation function to switch frequency with all necessary synchronization */ + esp_pm_impl_switch_cpu_freq(&old_config, &new_config); + const unsigned wait_clock_stable_us = 10; + esp_rom_delay_us(wait_clock_stable_us); + } + } + /* If s_saved_freq_mhz == 0, no change was made, so no need to restore */ + s_saved_freq_mhz = 0; +} +#endif // !CONFIG_PM_ENABLE + +void IRAM_ATTR esp_flash_freq_limit_cb(void) +{ + /* Limit the frequency */ +#ifdef CONFIG_PM_ENABLE + /* PM enabled, use force mechanism */ + esp_pm_impl_cpu_max_freq_force(); +#else + /* PM not enabled, use limit mechanism */ + uint32_t limit_freq_mhz = get_encrypt_lock_freq_limit(); + /* Call PM implementation function to limit CPU frequency. + * This function automatically reads current frequency, saves it, and switches to target frequency. + */ + limit_cpu_freq(limit_freq_mhz); +#endif +} + +void IRAM_ATTR esp_flash_freq_unlimit_cb(void) +{ + /* Restore the frequency */ +#ifdef CONFIG_PM_ENABLE + /* PM enabled, use unforce mechanism */ + esp_pm_impl_cpu_max_freq_unforce(); +#else + /* PM not enabled, use unlimit mechanism */ + /* Call PM implementation function to unlimit CPU frequency. + * This function restores the original frequency saved by limit_cpu_freq(). + */ + unlimit_cpu_freq(); +#endif +} diff --git a/components/esp_pm/pm_impl.c b/components/esp_pm/pm_impl.c index 3e33182d918f..9d48750c73f6 100644 --- a/components/esp_pm/pm_impl.c +++ b/components/esp_pm/pm_impl.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "sdkconfig.h" #include "esp_attr.h" @@ -34,6 +35,7 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "freertos/portmacro.h" #if CONFIG_FREERTOS_SYSTICK_USES_CCOUNT #include "xtensa_timer.h" #include "xtensa/core-macros.h" @@ -42,6 +44,7 @@ #include "esp_private/pm_impl.h" #include "esp_private/pm_trace.h" #include "esp_private/esp_timer_private.h" +#include "esp_timer.h" #include "esp_private/esp_clk.h" #include "esp_private/esp_clk_tree_common.h" #include "esp_private/sleep_cpu.h" @@ -51,7 +54,7 @@ #include "esp_private/esp_clk_utils.h" #include "esp_sleep.h" #include "esp_memory_utils.h" - +#include "esp_rom_sys.h" #if SOC_PERIPH_CLK_CTRL_SHARED #define HP_UART_SRC_CLK_ATOMIC() PERIPH_RCC_ATOMIC() @@ -168,6 +171,16 @@ static esp_pm_lock_handle_t s_rtos_lock_handle[CONFIG_FREERTOS_NUMBER_OF_CORES]; */ static rtc_cpu_freq_config_t s_cpu_freq_by_mode[PM_MODE_COUNT]; +#if CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED +/* Forced CPU_MAX frequency configuration. + * s_cpu_max_freq_forced indicates whether CPU_MAX frequency is forced. + * s_cpu_max_freq_force_config stores the forced frequency configuration. + * Protected by s_switch_lock, same as s_cpu_freq_by_mode. + */ +static bool s_cpu_max_freq_forced = false; +static rtc_cpu_freq_config_t s_cpu_max_freq_force_config; +#endif + /* Whether automatic light sleep is enabled */ static bool s_light_sleep_en = false; @@ -393,6 +406,32 @@ static esp_err_t esp_pm_sleep_configure(const esp_pm_config_t *config) return err; } +/** + * @brief Get frequency configuration for a given mode, considering forced frequency + * + * This function returns a pointer to the frequency configuration for the given mode. + * For PM_MODE_CPU_MAX, if s_cpu_max_freq_forced is true, it returns + * a pointer to s_cpu_max_freq_force_config. Otherwise, it returns a pointer to + * s_cpu_freq_by_mode[mode]. + * + * @param mode Power mode to get configuration for + * @note Must be called with s_switch_lock held + * @note Must be in IRAM when called from ISR context (e.g., do_switch) + * @return rtc_cpu_freq_config_t* Pointer to frequency configuration for the given mode + */ +static inline IRAM_ATTR rtc_cpu_freq_config_t *get_cpu_freq_config_by_mode(pm_mode_t mode) +{ +#if CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED + if (mode == PM_MODE_CPU_MAX && s_cpu_max_freq_forced) { + return &s_cpu_max_freq_force_config; + } else { + return &s_cpu_freq_by_mode[mode]; + } +#else + return &s_cpu_freq_by_mode[mode]; +#endif +} + esp_err_t esp_pm_configure(const void* vconfig) { #ifndef CONFIG_PM_ENABLE @@ -642,18 +681,19 @@ static void IRAM_ATTR do_switch(pm_mode_t new_mode) s_is_switching = true; bool config_changed = s_config_changed; s_config_changed = false; - portENTER_CRITICAL_ISR(&s_cpu_freq_switch_lock[core_id]); - portEXIT_CRITICAL_ISR(&s_switch_lock); - rtc_cpu_freq_config_t new_config = s_cpu_freq_by_mode[new_mode]; + rtc_cpu_freq_config_t new_config = *get_cpu_freq_config_by_mode(new_mode); rtc_cpu_freq_config_t old_config; if (!config_changed) { - old_config = s_cpu_freq_by_mode[s_mode]; + old_config = *get_cpu_freq_config_by_mode(s_mode); } else { rtc_clk_cpu_freq_get_config(&old_config); } + portENTER_CRITICAL_ISR(&s_cpu_freq_switch_lock[core_id]); + portEXIT_CRITICAL_ISR(&s_switch_lock); + if (new_config.freq_mhz != old_config.freq_mhz) { uint32_t old_ticks_per_us = old_config.freq_mhz; uint32_t new_ticks_per_us = new_config.freq_mhz; @@ -866,6 +906,7 @@ void vApplicationSleep( TickType_t xExpectedIdleTime ) void esp_pm_impl_dump_stats(FILE* out) { pm_time_t time_in_mode[PM_MODE_COUNT]; + uint32_t freq_mhz_by_mode[PM_MODE_COUNT]; portENTER_CRITICAL_ISR(&s_switch_lock); memcpy(time_in_mode, s_time_in_mode, sizeof(time_in_mode)); @@ -875,6 +916,10 @@ void esp_pm_impl_dump_stats(FILE* out) bool light_sleep_en = s_light_sleep_en; uint32_t light_sleep_counts = s_light_sleep_counts; uint32_t light_sleep_reject_counts = s_light_sleep_reject_counts; + // Read all frequency configs while holding s_switch_lock + for (int i = 0; i < PM_MODE_COUNT; ++i) { + freq_mhz_by_mode[i] = get_cpu_freq_config_by_mode(i)->freq_mhz; + } portEXIT_CRITICAL_ISR(&s_switch_lock); time_in_mode[cur_mode] += now - last_mode_change_time; @@ -888,7 +933,7 @@ void esp_pm_impl_dump_stats(FILE* out) } fprintf(out, "%-8s %-3"PRIu32"M%-7s %-10lld %-2d%%\n", s_mode_names[i], - s_cpu_freq_by_mode[i].freq_mhz, + freq_mhz_by_mode[i], "", //Empty space to align columns time_in_mode[i], (int) (time_in_mode[i] * 100 / now)); @@ -905,7 +950,7 @@ int esp_pm_impl_get_cpu_freq(pm_mode_t mode) int freq_mhz; if (mode >= PM_MODE_LIGHT_SLEEP && mode < PM_MODE_COUNT) { portENTER_CRITICAL(&s_switch_lock); - freq_mhz = s_cpu_freq_by_mode[mode].freq_mhz; + freq_mhz = get_cpu_freq_config_by_mode(mode)->freq_mhz; portEXIT_CRITICAL(&s_switch_lock); } else { abort(); @@ -1051,3 +1096,42 @@ void esp_pm_impl_waiti(void) esp_cpu_wait_for_intr(); #endif // CONFIG_FREERTOS_USE_TICKLESS_IDLE } + +#if CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED && CONFIG_PM_ENABLE +void esp_pm_impl_cpu_max_freq_force_init(uint32_t limit_freq_mhz) +{ + // Pre-calculate frequency config (done outside critical section) + rtc_cpu_freq_config_t force_config; + bool res = rtc_clk_cpu_freq_mhz_to_config(limit_freq_mhz, &force_config); + assert(res && "Failed to convert forced CPU_MAX frequency to config"); + + // Store the pre-calculated config in critical section + portENTER_CRITICAL(&s_switch_lock); + s_cpu_max_freq_force_config = force_config; + portEXIT_CRITICAL(&s_switch_lock); +} + +void IRAM_ATTR esp_pm_impl_cpu_max_freq_force(void) +{ + portENTER_CRITICAL_SAFE(&s_switch_lock); + // Activate pre-configured forced frequency (no calculation needed at runtime) + s_cpu_max_freq_forced = true; + s_config_changed = true; + portEXIT_CRITICAL_SAFE(&s_switch_lock); + do_switch(PM_MODE_CPU_MAX); + const unsigned wait_clock_stable_us = 10; + esp_rom_delay_us(wait_clock_stable_us); +} + +void IRAM_ATTR esp_pm_impl_cpu_max_freq_unforce(void) +{ + portENTER_CRITICAL_SAFE(&s_switch_lock); + s_cpu_max_freq_forced = false; + s_config_changed = true; + portEXIT_CRITICAL_SAFE(&s_switch_lock); + do_switch(PM_MODE_CPU_MAX); + const unsigned wait_clock_stable_us = 10; + esp_rom_delay_us(wait_clock_stable_us); +} + +#endif // CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED && CONFIG_PM_ENABLE diff --git a/components/esp_system/port/soc/esp32c5/Kconfig.cpu b/components/esp_system/port/soc/esp32c5/Kconfig.cpu index cf4222222941..95c3b92cbcda 100644 --- a/components/esp_system/port/soc/esp32c5/Kconfig.cpu +++ b/components/esp_system/port/soc/esp32c5/Kconfig.cpu @@ -20,6 +20,13 @@ choice ESP_DEFAULT_CPU_FREQ_MHZ # Please see SoC Errata document for details. depends on !SECURE_FLASH_ENC_ENABLED bool "240 MHz" + help + When 240MHz is selected, esp_flash_write_encrypted() will automatically limit CPU frequency during + execution: + - v1.2 and above chips: limited to 160MHz + - v1.0 and below chips: limited to 80MHz + When 160MHz or lower is selected, no frequency limiting occurs during encrypted writes. Please refer to + the ESP32-C5 SoC Errata document for more details. endchoice config ESP_DEFAULT_CPU_FREQ_MHZ diff --git a/components/esp_system/startup_funcs.c b/components/esp_system/startup_funcs.c index 204108f1286b..cbfbc2838b6a 100644 --- a/components/esp_system/startup_funcs.c +++ b/components/esp_system/startup_funcs.c @@ -33,7 +33,7 @@ #include "private/esp_coexist_internal.h" #endif -#if CONFIG_PM_ENABLE +#if CONFIG_PM_ENABLE || CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED #include "esp_pm.h" #include "esp_private/pm_impl.h" #endif @@ -122,6 +122,9 @@ ESP_SYSTEM_INIT_FN(init_flash, CORE, BIT(0), 130) #endif // CONFIG_SPI_FLASH_BROWNOUT_RESET // The log library will call the registered callback function to check if the cache is disabled. esp_log_util_set_cache_enabled_cb(spi_flash_cache_enabled); +#if CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED + esp_pm_flash_freq_limit_init(); +#endif // CONFIG_PM_WORKAROUND_FREQ_LIMIT_ENABLED return ESP_OK; } #endif // !CONFIG_APP_BUILD_TYPE_PURE_RAM_APP diff --git a/components/esp_wifi/lib b/components/esp_wifi/lib index 10bed0821c16..01d52d9e6903 160000 --- a/components/esp_wifi/lib +++ b/components/esp_wifi/lib @@ -1 +1 @@ -Subproject commit 10bed0821c16e428211a3c5de9375dbc510c7544 +Subproject commit 01d52d9e69032c486015dc28b08c3bf6aaf348a9 diff --git a/components/hal/esp32p4/include/hal/parlio_ll.h b/components/hal/esp32p4/include/hal/parlio_ll.h index 8166a8bd92b5..a9bce617b431 100644 --- a/components/hal/esp32p4/include/hal/parlio_ll.h +++ b/components/hal/esp32p4/include/hal/parlio_ll.h @@ -43,6 +43,7 @@ #if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300 #define PARLIO_LL_TX_VALID_MAX_DELAY 32767 +#define PARLIO_LL_SUPPORT_TX_EOF_FROM_DMA 1 // Support to treat DMA EOF as TX unit EOF #endif #ifdef __cplusplus diff --git a/components/ieee802154/driver/esp_ieee802154_dev.c b/components/ieee802154/driver/esp_ieee802154_dev.c index c40505578a2e..a6d7bdec93b9 100644 --- a/components/ieee802154/driver/esp_ieee802154_dev.c +++ b/components/ieee802154/driver/esp_ieee802154_dev.c @@ -651,7 +651,7 @@ static IRAM_ATTR void isr_handle_ed_done(void) if (s_ieee802154_state == IEEE802154_STATE_CCA) { esp_ieee802154_cca_done(ieee802154_ll_is_cca_busy()); } else if (s_ieee802154_state == IEEE802154_STATE_ED) { - ieee802154_inner_energy_detect_done(ieee802154_ll_get_ed_rss()); + ieee802154_inner_energy_detect_done(ieee802154_ll_get_ed_rss() + IEEE802154_RSSI_COMPENSATION_VALUE); } NEEDS_NEXT_OPT(true); diff --git a/components/mbedtls/port/aes/dma/esp_aes_dma_core.c b/components/mbedtls/port/aes/dma/esp_aes_dma_core.c index 05f70e2cd195..a066122d95f3 100644 --- a/components/mbedtls/port/aes/dma/esp_aes_dma_core.c +++ b/components/mbedtls/port/aes/dma/esp_aes_dma_core.c @@ -1020,11 +1020,12 @@ int esp_aes_process_dma(esp_aes_context *ctx, const unsigned char *input, unsign #if (CONFIG_SPIRAM && SOC_PSRAM_DMA_CAPABLE) #ifdef SOC_GDMA_EXT_MEM_ENC_ALIGNMENT if (efuse_hal_flash_encryption_enabled()) { - if (esp_ptr_external_ram(input) || esp_ptr_external_ram(output) || esp_ptr_in_drom(input) || esp_ptr_in_drom(output)) { + if (esp_ptr_external_ram(input) || esp_ptr_in_drom(input)) { if (((intptr_t)(input) & (SOC_GDMA_EXT_MEM_ENC_ALIGNMENT - 1)) != 0) { input_needs_realloc = true; } - + } + if (esp_ptr_external_ram(output) || esp_ptr_in_drom(output)) { if (((intptr_t)(output) & (SOC_GDMA_EXT_MEM_ENC_ALIGNMENT - 1)) != 0) { output_needs_realloc = true; } diff --git a/components/openthread/Kconfig b/components/openthread/Kconfig index b113cfd1f4c0..2a89bf47fc79 100644 --- a/components/openthread/Kconfig +++ b/components/openthread/Kconfig @@ -45,9 +45,9 @@ menu "OpenThread" endmenu menu "Thread Console" + depends on OPENTHREAD_ENABLED config OPENTHREAD_CONSOLE_ENABLE bool "Enable OpenThread console" - depends on OPENTHREAD_ENABLED default y help Enable the OpenThread-specific console provided by the SDK. This only controls whether @@ -430,7 +430,7 @@ menu "OpenThread" config OPENTHREAD_PLATFORM_MSGPOOL_MANAGEMENT bool 'Allocate message pool buffer from PSRAM' - default n + default y help If enabled, the message pool is managed by platform defined logic. endmenu @@ -464,7 +464,8 @@ menu "OpenThread" config OPENTHREAD_NUM_MESSAGE_BUFFERS int "The number of openthread message buffers" - default 65 + default 65 if !OPENTHREAD_PLATFORM_MSGPOOL_MANAGEMENT + default 1024 if OPENTHREAD_PLATFORM_MSGPOOL_MANAGEMENT config OPENTHREAD_XTAL_ACCURACY int "The accuracy of the XTAL" diff --git a/components/openthread/lib b/components/openthread/lib index 755f91eb8a13..e9980c565588 160000 --- a/components/openthread/lib +++ b/components/openthread/lib @@ -1 +1 @@ -Subproject commit 755f91eb8a138da577ea353c08c33eb700f9ac50 +Subproject commit e9980c56558876ae1102be8ae9dc70821aded72c diff --git a/components/openthread/src/port/esp_openthread_settings.c b/components/openthread/src/port/esp_openthread_settings.c index f9064ee437fa..e2cc930d2672 100644 --- a/components/openthread/src/port/esp_openthread_settings.c +++ b/components/openthread/src/port/esp_openthread_settings.c @@ -130,9 +130,18 @@ static esp_err_t erase_all_key(uint16_t aKey) void otPlatSettingsInit(otInstance *aInstance, const uint16_t *aSensitiveKeys, uint16_t aSensitiveKeysLength) { - esp_err_t err = nvs_open(OT_NAMESPACE, NVS_READWRITE, &s_ot_nvs_handle); + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aSensitiveKeys); + OT_UNUSED_VARIABLE(aSensitiveKeysLength); + esp_err_t err = ESP_OK; + if (s_storage_name != NULL) { + err = nvs_open_from_partition(s_storage_name, OT_NAMESPACE, NVS_READWRITE, &s_ot_nvs_handle); + } else { + err = nvs_open(OT_NAMESPACE, NVS_READWRITE, &s_ot_nvs_handle); + } + if (err != ESP_OK) { - ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to open NVS namespace (0x%x)", err); + ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to open %s namespace (0x%x)", s_storage_name == NULL ? "nvs" : s_storage_name, err); assert(0); } } diff --git a/components/openthread/src/port/esp_openthread_trel.c b/components/openthread/src/port/esp_openthread_trel.c index eae1c04219f1..8eca69ce4e65 100644 --- a/components/openthread/src/port/esp_openthread_trel.c +++ b/components/openthread/src/port/esp_openthread_trel.c @@ -66,30 +66,51 @@ static void trel_browse_notifier(mdns_result_t *result) while (result) { if (result->addr && result->addr->addr.type == IPADDR_TYPE_V6) { otPlatTrelPeerInfo info; - uint8_t trel_txt[1024] = {0}; - uint16_t trel_txt_len = 0; - size_t index = 0; - while (index < result->txt_count) { - trel_txt[trel_txt_len++] = strlen(result->txt[index].key) + result->txt_value_len[index] + 1; - memcpy((trel_txt + trel_txt_len), (void *)result->txt[index].key, strlen(result->txt[index].key)); - trel_txt_len += (strlen(result->txt[index].key)); - trel_txt[trel_txt_len++] = '='; - memcpy((trel_txt + trel_txt_len), (void *)result->txt[index].value, result->txt_value_len[index]); - trel_txt_len += result->txt_value_len[index]; - index++; - } + uint8_t *trel_txt = NULL; + size_t trel_txt_len = 0; + if (!s_trel_netif) { s_trel_netif = result->esp_netif; + } else if (s_trel_netif != result->esp_netif) { + result = result->next; + continue; + } + + for (size_t index = 0; index < result->txt_count; index++) { + size_t key_len = strlen(result->txt[index].key); + size_t value_len = result->txt_value_len[index]; + trel_txt_len += 1 + key_len + 1 + value_len; // txt_len + key + `=` + value + } + + if (trel_txt_len == 0) { + result = result->next; + continue; + } + trel_txt = malloc(trel_txt_len); + ESP_RETURN_ON_FALSE(trel_txt != NULL, , OT_PLAT_LOG_TAG, "Failed to malloc buffer for TREL TXT"); + + size_t offset = 0; + for (size_t index = 0; index < result->txt_count; index++) { + size_t key_len = strlen(result->txt[index].key); + size_t value_len = result->txt_value_len[index]; + + trel_txt[offset++] = key_len + value_len + 1; + memcpy(trel_txt + offset, result->txt[index].key, key_len); + offset += key_len; + trel_txt[offset++] = '='; + memcpy(trel_txt + offset, result->txt[index].value, value_len); + offset += value_len; } info.mTxtData = trel_txt; info.mTxtLength = trel_txt_len; info.mSockAddr.mPort = result->port; memcpy(info.mSockAddr.mAddress.mFields.m32, result->addr->addr.u_addr.ip6.addr, OT_IP6_ADDRESS_SIZE); info.mRemoved = (result->ttl == 0); - ESP_LOGI(OT_PLAT_LOG_TAG, "%s TREL peer: address: %s, port:%d", info.mRemoved ? "Remove" : "Found", ip6addr_ntoa(((ip6_addr_t*)(&result->addr->addr.u_addr.ip6))), info.mSockAddr.mPort); + ESP_LOGI(OT_PLAT_LOG_TAG, "%s TREL peer: address: %s, port:%d", info.mRemoved ? "Remove" : "Found", ip6addr_ntoa((ip6_addr_t*)(&result->addr->addr.u_addr.ip6)), info.mSockAddr.mPort); esp_openthread_task_switching_lock_acquire(portMAX_DELAY); otPlatTrelHandleDiscoveredPeerInfo(esp_openthread_get_instance(), &info); esp_openthread_task_switching_lock_release(); + free(trel_txt); } result = result->next; } diff --git a/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in index 3c63b50a5ceb..a04e289fbfca 100644 --- a/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in @@ -1259,6 +1259,10 @@ config SOC_SPI_MEM_SUPPORT_TSUS_TRES_SEPERATE_CTR bool default y +config SOC_SPI_MEM_PSRAM_FREQ_AXI_CONSTRAINED + bool + default y + config SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY bool default y @@ -1739,6 +1743,10 @@ config SOC_PM_PAU_REGDMA_LINK_IDX_WIFIMAC int default 4 +config SOC_PM_PAU_REGDMA_COMMON_PHY_LINK_ENTRY + bool + default y + config SOC_PM_PMU_MIN_SLP_SLOW_CLK_CYCLE_FIXED bool default y diff --git a/components/soc/esp32c5/include/soc/soc_caps.h b/components/soc/esp32c5/include/soc/soc_caps.h index ad186c54e058..33ab9edf3033 100644 --- a/components/soc/esp32c5/include/soc/soc_caps.h +++ b/components/soc/esp32c5/include/soc/soc_caps.h @@ -484,6 +484,7 @@ #define SOC_SPI_MEM_SUPPORT_CACHE_32BIT_ADDR_MAP (1) #define SOC_SPI_MEM_SUPPORT_TIMING_TUNING (1) #define SOC_SPI_MEM_SUPPORT_TSUS_TRES_SEPERATE_CTR (1) +#define SOC_SPI_MEM_PSRAM_FREQ_AXI_CONSTRAINED (1) #define SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY (1) #define SOC_MEMSPI_SRC_FREQ_120M_SUPPORTED 1 @@ -668,6 +669,7 @@ #define SOC_PM_PAU_LINK_NUM (5) #define SOC_PM_PAU_REGDMA_LINK_CONFIGURABLE (1) #define SOC_PM_PAU_REGDMA_LINK_IDX_WIFIMAC (4) // The range of values for the link index is [0, SOC_PM_PAU_LINK_NUM) +#define SOC_PM_PAU_REGDMA_COMMON_PHY_LINK_ENTRY (1) #define SOC_PM_PMU_MIN_SLP_SLOW_CLK_CYCLE_FIXED (1) diff --git a/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in index 7469604368a3..11bdae667842 100644 --- a/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in @@ -859,6 +859,10 @@ config SOC_SPI_MEM_SUPPORT_TSUS_TRES_SEPERATE_CTR bool default y +config SOC_SPI_MEM_PSRAM_FREQ_AXI_CONSTRAINED + bool + default y + config SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY bool default y diff --git a/components/soc/esp32c61/include/soc/soc_caps.h b/components/soc/esp32c61/include/soc/soc_caps.h index 6ca25d6febb9..24c7fba93508 100644 --- a/components/soc/esp32c61/include/soc/soc_caps.h +++ b/components/soc/esp32c61/include/soc/soc_caps.h @@ -348,6 +348,7 @@ #define SOC_SPI_MEM_SUPPORT_WRAP (1) #define SOC_SPI_MEM_SUPPORT_TIMING_TUNING (1) #define SOC_SPI_MEM_SUPPORT_TSUS_TRES_SEPERATE_CTR (1) +#define SOC_SPI_MEM_PSRAM_FREQ_AXI_CONSTRAINED (1) #define SOC_MEMSPI_TIMING_TUNING_BY_MSPI_DELAY (1) #define SOC_MEMSPI_SRC_FREQ_80M_SUPPORTED 1 diff --git a/components/soc/esp32h4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32h4/include/soc/Kconfig.soc_caps.in index 4a736c4ad4ef..8e15f46312ec 100644 --- a/components/soc/esp32h4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32h4/include/soc/Kconfig.soc_caps.in @@ -267,6 +267,10 @@ config SOC_SPI_MEM_SUPPORT_WRAP bool default y +config SOC_SPI_MEM_PSRAM_FREQ_AXI_CONSTRAINED + bool + default y + config SOC_MEMSPI_SRC_FREQ_64M_SUPPORTED bool default y diff --git a/components/soc/esp32h4/include/soc/soc_caps.h b/components/soc/esp32h4/include/soc/soc_caps.h index 9166123814ab..868180cf2320 100644 --- a/components/soc/esp32h4/include/soc/soc_caps.h +++ b/components/soc/esp32h4/include/soc/soc_caps.h @@ -414,6 +414,7 @@ #define SOC_SPI_MEM_SUPPORT_SW_SUSPEND (1) #define SOC_SPI_MEM_SUPPORT_CHECK_SUS (1) #define SOC_SPI_MEM_SUPPORT_WRAP (1) +#define SOC_SPI_MEM_PSRAM_FREQ_AXI_CONSTRAINED (1) #define SOC_MEMSPI_SRC_FREQ_64M_SUPPORTED 1 #define SOC_MEMSPI_SRC_FREQ_32M_SUPPORTED 1 diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index 2abc997658ae..c096e00cb6b8 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -1659,6 +1659,10 @@ config SOC_SPI_MEM_SUPPORT_CACHE_32BIT_ADDR_MAP bool default y +config SOC_SPI_MEM_PSRAM_FREQ_AXI_CONSTRAINED + bool + default y + config SOC_SPI_MEM_SUPPORT_TSUS_TRES_SEPERATE_CTR bool default y diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index af7dfdaabe86..43311df230cb 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -609,6 +609,7 @@ #define SOC_MEMSPI_TIMING_TUNING_BY_DQS (1) #define SOC_MEMSPI_TIMING_TUNING_BY_FLASH_DELAY (1) #define SOC_SPI_MEM_SUPPORT_CACHE_32BIT_ADDR_MAP (1) +#define SOC_SPI_MEM_PSRAM_FREQ_AXI_CONSTRAINED (1) #define SOC_SPI_MEM_SUPPORT_TSUS_TRES_SEPERATE_CTR (1) #define SOC_SPI_PERIPH_SUPPORT_CONTROL_DUMMY_OUT (1) diff --git a/components/soc/esp32p4/register/hw_ver3/soc/ahb_dma_struct.h b/components/soc/esp32p4/register/hw_ver3/soc/ahb_dma_struct.h index a7540860931d..a650777a0457 100644 --- a/components/soc/esp32p4/register/hw_ver3/soc/ahb_dma_struct.h +++ b/components/soc/esp32p4/register/hw_ver3/soc/ahb_dma_struct.h @@ -604,6 +604,7 @@ typedef union { * task. */ uint32_t out_etm_en_chn: 1; + uint32_t reserved_7: 1; /** out_data_burst_mode_sel_chn : R/W; bitpos: [9:8]; default: 1; * Configures max burst size for TX channeln. * 2'b00: single diff --git a/components/spi_flash/Kconfig b/components/spi_flash/Kconfig index aaab20eaa488..79dc5c50d796 100644 --- a/components/spi_flash/Kconfig +++ b/components/spi_flash/Kconfig @@ -465,4 +465,11 @@ menu "SPI Flash driver" application is not using flash encryption feature and is in need of some additional memory from IRAM region (~1KB) then this config can be disabled. + config SPI_FLASH_FREQ_LIMIT_C5_240MHZ + bool + default y if IDF_TARGET_ESP32C5 && ESP_DEFAULT_CPU_FREQ_MHZ_240 + help + Enable frequency limit workaround for encrypted flash writes on ESP32-C5 at 240MHz default CPU frequency. + This is an internal configuration, automatically set based on target chip and CPU frequency. + endmenu diff --git a/components/spi_flash/esp_flash_api.c b/components/spi_flash/esp_flash_api.c index 63c1dba50f1f..4473d24a3ec6 100644 --- a/components/spi_flash/esp_flash_api.c +++ b/components/spi_flash/esp_flash_api.c @@ -148,15 +148,6 @@ static const char io_mode_str[][IO_STR_LEN] = { _Static_assert(sizeof(io_mode_str)/IO_STR_LEN == SPI_FLASH_READ_MODE_MAX, "the io_mode_str should be consistent with the esp_flash_io_mode_t defined in spi_flash_types.h"); -esp_err_t esp_flash_read_chip_id(esp_flash_t* chip, uint32_t* flash_id); - -#if !CONFIG_SPI_FLASH_ROM_IMPL || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV -static esp_err_t spiflash_start_default(esp_flash_t *chip); -static esp_err_t spiflash_end_default(esp_flash_t *chip, esp_err_t err); -static esp_err_t check_chip_pointer_default(esp_flash_t **inout_chip); -static esp_err_t flash_end_flush_cache(esp_flash_t* chip, esp_err_t err, bool bus_acquired, uint32_t address, uint32_t length); -#endif // !CONFIG_SPI_FLASH_ROM_IMPL || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV - typedef struct { esp_err_t (*start)(esp_flash_t *chip); esp_err_t (*end)(esp_flash_t *chip, esp_err_t err); @@ -164,19 +155,70 @@ typedef struct { esp_err_t (*flash_end_flush_cache)(esp_flash_t* chip, esp_err_t err, bool bus_acquired, uint32_t address, uint32_t length); } rom_spiflash_api_func_t; + +esp_err_t esp_flash_read_chip_id(esp_flash_t* chip, uint32_t* flash_id); + +#if CONFIG_SPI_FLASH_ROM_IMPL +extern rom_spiflash_api_func_t *esp_flash_api_funcs; +#define rom_spiflash_api_funcs esp_flash_api_funcs +#else +#define rom_spiflash_api_funcs esp_flash_api_funcs_patched_ptr +#endif + #if !CONFIG_SPI_FLASH_ROM_IMPL || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV +// API funcs case 1 & 2 +static esp_err_t spiflash_start_default(esp_flash_t *chip); +static esp_err_t spiflash_end_default(esp_flash_t *chip, esp_err_t err); +static esp_err_t check_chip_pointer_default(esp_flash_t **inout_chip); +static esp_err_t flash_end_flush_cache(esp_flash_t* chip, esp_err_t err, bool bus_acquired, uint32_t address, uint32_t length); + // These functions can be placed in the ROM. For now we use the code in IDF. -DRAM_ATTR static rom_spiflash_api_func_t default_spiflash_rom_api = { +DRAM_ATTR static rom_spiflash_api_func_t esp_flash_api_funcs_patched = { .start = spiflash_start_default, .end = spiflash_end_default, .chip_check = check_chip_pointer_default, .flash_end_flush_cache = flash_end_flush_cache, }; -DRAM_ATTR rom_spiflash_api_func_t *rom_spiflash_api_funcs = &default_spiflash_rom_api; -#else -extern rom_spiflash_api_func_t *esp_flash_api_funcs; -#define rom_spiflash_api_funcs esp_flash_api_funcs +# if !CONFIG_SPI_FLASH_ROM_IMPL +// API funcs case 1: Not using ROM - define our own pointer and all functions +DRAM_ATTR static rom_spiflash_api_func_t *esp_flash_api_funcs_patched_ptr = &esp_flash_api_funcs_patched; + +# else // CONFIG_SPI_FLASH_ROM_IMPL +// API funcs case 2: Using ROM APIs but patch all api_funcs by updating esp_flash_api_funcs from ROM +void esp_flash_rom_api_funcs_init(void) +{ + // Point esp_flash_api_funcs to our default structure + esp_flash_api_funcs = &esp_flash_api_funcs_patched; +} + +# endif // CONFIG_SPI_FLASH_ROM_IMPL + +#else // CONFIG_SPI_FLASH_ROM_IMPL && !ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV +// Using ROM implementation + +# if CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ +// API funcs case 3: Using ROM APIs but patch start function to support flags parameter +static esp_err_t spiflash_start_default(esp_flash_t *chip); +DRAM_ATTR static rom_spiflash_api_func_t esp_flash_api_funcs_patched; + +// Copy ROM structure to RAM and patch start function to support flags +void esp_flash_rom_api_funcs_init(void) +{ + rom_spiflash_api_func_t *rom_ptr = esp_flash_api_funcs; + memcpy(&esp_flash_api_funcs_patched, rom_ptr, sizeof(rom_spiflash_api_func_t)); + esp_flash_api_funcs_patched.start = spiflash_start_default; + esp_flash_api_funcs = &esp_flash_api_funcs_patched; +} + +# else +// API funcs case 4: Using All ROM APIs directly +void esp_flash_rom_api_funcs_init(void) +{ + // Do nothing +} + +# endif // CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ #endif // !CONFIG_SPI_FLASH_ROM_IMPL || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV /* Static function to notify OS of a new SPI flash operation. @@ -184,11 +226,13 @@ extern rom_spiflash_api_func_t *esp_flash_api_funcs; If returns an error result, caller must abort. If returns ESP_OK, caller must call rom_spiflash_api_funcs->end() before returning. */ -#if !CONFIG_SPI_FLASH_ROM_IMPL || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV -static esp_err_t spiflash_start_default(esp_flash_t *chip) +#if !CONFIG_SPI_FLASH_ROM_IMPL || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV || CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ +//Avoid constprop issue that place this function into flash. +__attribute__((optimize("O0"))) //IDF-14941 +static esp_err_t spiflash_start_core(esp_flash_t *chip, uint32_t flags) { if (chip->os_func != NULL && chip->os_func->start != NULL) { - esp_err_t err = chip->os_func->start(chip->os_func_data); + esp_err_t err = chip->os_func->start(chip->os_func_data, flags); if (err != ESP_OK) { return err; } @@ -197,6 +241,13 @@ static esp_err_t spiflash_start_default(esp_flash_t *chip) return ESP_OK; } +static esp_err_t spiflash_start_default(esp_flash_t *chip) +{ + return spiflash_start_core(chip, 0); +} +#endif //!CONFIG_SPI_FLASH_ROM_IMPL || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV || CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ + +#if !CONFIG_SPI_FLASH_ROM_IMPL || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV /* Static function to notify OS that SPI flash operation is complete. */ static esp_err_t spiflash_end_default(esp_flash_t *chip, esp_err_t err) @@ -1248,13 +1299,20 @@ esp_err_t esp_flash_set_io_mode(esp_flash_t* chip, bool qe) } #endif //CONFIG_SPI_FLASH_ROM_IMPL -#if !(CONFIG_SPI_FLASH_ROM_IMPL && !ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV) -// use `esp_flash_write_encrypted` ROM version on chips later than C3 and S3 -FORCE_INLINE_ATTR esp_err_t s_encryption_write_lock(esp_flash_t *chip) { +#if !(CONFIG_SPI_FLASH_ROM_IMPL && !ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV) || CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ +// use `esp_flash_write_encrypted` ROM version on chips later than C3, S3 +// For ESP32-C5, use IDF implementation when CPU frequency is 240MHz (calling start() with arg is required) +FORCE_INLINE_ATTR esp_err_t s_encryption_write_lock(esp_flash_t *chip) +{ #if CONFIG_IDF_TARGET_ESP32S2 esp_crypto_dma_lock_acquire(); #endif //CONFIG_IDF_TARGET_ESP32S2 +#if CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ + // Use start_core with LIMIT_CPU_FREQ flag to trigger freq_limit_lock in OS layer + return spiflash_start_core(chip, ESP_FLASH_START_FLAG_LIMIT_CPU_FREQ); +#else return rom_spiflash_api_funcs->start(chip); +#endif } FORCE_INLINE_ATTR esp_err_t s_encryption_write_unlock(esp_flash_t *chip) { @@ -1506,7 +1564,7 @@ esp_err_t IRAM_ATTR esp_flash_write_encrypted(esp_flash_t *chip, uint32_t addres } return rom_esp_flash_write_encrypted(chip, address, buffer, length); } -#endif // !(CONFIG_SPI_FLASH_ROM_IMPL && !ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV) +#endif // !(CONFIG_SPI_FLASH_ROM_IMPL && !ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV) || CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ //init suspend mode cmd, uses internal. esp_err_t esp_flash_suspend_cmd_init(esp_flash_t* chip) diff --git a/components/spi_flash/esp_flash_spi_init.c b/components/spi_flash/esp_flash_spi_init.c index cd86296aceb4..0e46bcde4797 100644 --- a/components/spi_flash/esp_flash_spi_init.c +++ b/components/spi_flash/esp_flash_spi_init.c @@ -142,6 +142,19 @@ esp_flash_t *esp_flash_default_chip = NULL; #endif //!CONFIG_SPI_FLASH_AUTO_SUSPEND #endif // Other target +// Dynamic flash configuration is only needed when: +// 1. Frequency limit workaround is enabled (CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ) +// 2. Flash frequency requires timing tuning (80MHz or 120MHz, i.e., > 40MHz) +// 3. CPU frequency reduction will trigger MSPI timing tuning to enter low speed mode +// This happens when: SOC_SPI_MEM_PSRAM_FREQ_AXI_CONSTRAINED && CONFIG_SPIRAM && +// (target_cpu_freq < CONFIG_SPIRAM_SPEED) +// Note: The runtime check for CPU freq < PSRAM speed is done in clk_utils.c, +// which calls mspi_timing_change_speed_mode_cache_safe(true) to enter low speed mode. +// For ESP32-C5, if PSRAM is enabled and CPU freq < PSRAM speed, timing tuning will be disabled. +#define C5_NEEDS_DYNAMIC_CONFIG (CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ && CONFIG_SPIRAM && \ + (CONFIG_ESPTOOLPY_FLASHFREQ_80M || CONFIG_ESPTOOLPY_FLASHFREQ_120M)) + + static IRAM_ATTR NOINLINE_ATTR void cs_initialize(esp_flash_t *chip, const esp_flash_spi_device_config_t *config, bool cs_use_iomux, int cs_id) { //Not using spicommon_cs_initialize since we don't want to put the whole @@ -157,7 +170,7 @@ static IRAM_ATTR NOINLINE_ATTR void cs_initialize(esp_flash_t *chip, const esp_f //To avoid the panic caused by flash data line conflicts during cs line //initialization, disable the cache temporarily - chip->os_func->start(chip->os_func_data); + chip->os_func->start(chip->os_func_data, 0); gpio_hal_input_enable(&gpio_hal, cs_io_num); if (cs_use_iomux) { gpio_hal_func_sel(&gpio_hal, cs_io_num, spics_func); @@ -547,6 +560,16 @@ esp_err_t esp_flash_init_default_chip(void) if (err != ESP_OK) { return err; } +#if C5_NEEDS_DYNAMIC_CONFIG + err = memspi_host_init_c5_dynamic_config(&esp_flash_default_host); + if (err != ESP_OK) { + return err; + } +#endif + +#if CONFIG_SPI_FLASH_ROM_IMPL + esp_flash_rom_api_funcs_init(); +#endif // CONFIG_SPI_FLASH_ROM_IMPL // ROM TODO: account for non-standard default pins in efuse // ROM TODO: to account for chips which are slow to power on, maybe keep probing in a loop here @@ -608,6 +631,7 @@ esp_err_t esp_flash_app_init(void) spi_flash_init_lock(); spi_flash_guard_set(&g_flash_guard_default_ops); + #if CONFIG_SPI_FLASH_ENABLE_COUNTERS esp_flash_reset_counters(); #endif diff --git a/components/spi_flash/include/esp_flash.h b/components/spi_flash/include/esp_flash.h index 31c68611d45a..d502052e1385 100644 --- a/components/spi_flash/include/esp_flash.h +++ b/components/spi_flash/include/esp_flash.h @@ -37,11 +37,17 @@ typedef struct { * risk. */ typedef struct { + /** + * Flags for start function + */ + /** Limit CPU frequency during flash operations (ESP32-C5 only, 240MHz). + */ + #define ESP_FLASH_START_FLAG_LIMIT_CPU_FREQ BIT(0) /** * Called before commencing any flash operation. Does not need to be * recursive (ie is called at most once for each call to 'end'). */ - esp_err_t (*start)(void *arg); + esp_err_t (*start)(void *arg, uint32_t flags); /** Called after completing any flash operation. */ esp_err_t (*end)(void *arg); diff --git a/components/spi_flash/include/esp_flash_internal.h b/components/spi_flash/include/esp_flash_internal.h index d07b3e229007..b3a87206bfbe 100644 --- a/components/spi_flash/include/esp_flash_internal.h +++ b/components/spi_flash/include/esp_flash_internal.h @@ -117,6 +117,16 @@ esp_err_t esp_flash_app_disable_os_functions(esp_flash_t* chip); */ esp_err_t esp_flash_set_dangerous_write_protection(esp_flash_t *chip, const bool protect); +#if CONFIG_SPI_FLASH_ROM_IMPL +/** + * @brief Initialize ROM API functions structure + * + * This function initializes the ROM API functions structure, either by pointing + * to a custom structure or by patching the ROM structure in RAM. + */ +void esp_flash_rom_api_funcs_init(void); +#endif // CONFIG_SPI_FLASH_ROM_IMPL + #ifdef __cplusplus } #endif diff --git a/components/spi_flash/include/esp_private/spi_flash_freq_limit_cbs.h b/components/spi_flash/include/esp_private/spi_flash_freq_limit_cbs.h new file mode 100644 index 000000000000..3e95f36965ab --- /dev/null +++ b/components/spi_flash/include/esp_private/spi_flash_freq_limit_cbs.h @@ -0,0 +1,49 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "sdkconfig.h" +#include "esp_flash.h" +#include "esp_attr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ +/** + * @brief Callback to limit CPU frequency for flash encryption writes (ESP32-C5 only, 240MHz) + * + * This function limits CPU frequency to <= 160MHz during encrypted flash writes. + * It should be called before starting an encrypted flash write operation. + * + * @note This is an internal function, not exposed to users. + * @note This function is placed in IRAM (implementation uses IRAM_ATTR). + * @note This function is called from spi_flash_os_func_app.c::spi1_start() after cache + * and scheduler locks are acquired. As a result, there is no concurrency concern + * and no need for internal locking or reference counting. + */ +void esp_flash_freq_limit_cb(void); + +/** + * @brief Callback to unlimit CPU frequency after flash encryption writes (ESP32-C5 only, 240MHz) + * + * This function restores the CPU frequency after an encrypted flash write operation. + * It should be called after completing an encrypted flash write operation. + * + * @note This is an internal function, not exposed to users. + * @note This function is placed in IRAM (implementation uses IRAM_ATTR). + * @note This function is called from spi_flash_os_func_app.c::spi1_end() before cache + * and scheduler locks are released. As a result, there is no concurrency concern + * and no need for internal locking or reference counting. + */ +void esp_flash_freq_unlimit_cb(void); +#endif // CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ + +#ifdef __cplusplus +} +#endif diff --git a/components/spi_flash/include/memspi_host_driver.h b/components/spi_flash/include/memspi_host_driver.h index 29379e21a2e4..269ea554c389 100644 --- a/components/spi_flash/include/memspi_host_driver.h +++ b/components/spi_flash/include/memspi_host_driver.h @@ -48,6 +48,17 @@ typedef spi_flash_hal_context_t memspi_host_inst_t; */ esp_err_t memspi_host_init_pointers(memspi_host_inst_t *host, const memspi_host_config_t *cfg); +/** + * Initialize the memory SPI host for ESP32-C5 dynamic configuration. + * + * @param host Pointer to the host structure. + * + * @note This function is available only when CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ is defined. + * @note This function can only be called once at startup, after memspi_host_init_pointers() is called. + * @return always return ESP_OK + */ +esp_err_t memspi_host_init_c5_dynamic_config(memspi_host_inst_t *host); + /******************************************************************************* * NOTICE * Rest part of this file are part of the HAL layer @@ -161,7 +172,7 @@ int memspi_host_read_data_slicer(spi_flash_host_inst_t *host, uint32_t address, /** * @brief Slicer for write data used in non-encrypted regions. This slicer limit the length to the - * maximum size the host supports, and truncate if the write data lie accross the page boundary + * maximum size the host supports, and truncate if the write data lie across the page boundary * (256 bytes) * * @param address Flash address to write diff --git a/components/spi_flash/linker.lf b/components/spi_flash/linker.lf index 95d2aaa45c3a..e034bad491ac 100644 --- a/components/spi_flash/linker.lf +++ b/components/spi_flash/linker.lf @@ -45,12 +45,15 @@ entries: if SPI_FLASH_VERIFY_WRITE = y: esp_flash_api: s_verify_write (noflash) - if SPI_FLASH_ROM_IMPL = n || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV = y: + if SPI_FLASH_ROM_IMPL = n || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV = y || SPI_FLASH_FREQ_LIMIT_C5_240MHZ = y: esp_flash_api: spiflash_start_default (noflash) + esp_flash_api: spiflash_start_core (noflash) + esp_flash_api: esp_flash_write_encrypted (noflash) + + if SPI_FLASH_ROM_IMPL = n || ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV = y: esp_flash_api: spiflash_end_default (noflash) esp_flash_api: check_chip_pointer_default (noflash) esp_flash_api: flash_end_flush_cache (noflash) - esp_flash_api: esp_flash_write_encrypted (noflash) if SPI_FLASH_ROM_IMPL = n: esp_flash_api: esp_flash_get_size (noflash) diff --git a/components/spi_flash/memspi_host_driver.c b/components/spi_flash/memspi_host_driver.c index d8210f81fc02..8f690ad084b9 100644 --- a/components/spi_flash/memspi_host_driver.c +++ b/components/spi_flash/memspi_host_driver.c @@ -4,6 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include "sdkconfig.h" #include "soc/soc_caps.h" #include "spi_flash_defs.h" #include "memspi_host_driver.h" @@ -12,12 +13,13 @@ #include "esp_private/cache_utils.h" #include "esp_flash_partitions.h" #include "esp_memory_utils.h" +#include "hal/mspi_ll.h" #define SPI_FLASH_HAL_MAX_WRITE_BYTES 64 #define SPI_FLASH_HAL_MAX_READ_BYTES 64 -DRAM_ATTR static const spi_flash_host_driver_t esp_flash_default_host = ESP_FLASH_DEFAULT_HOST_DRIVER(); +DRAM_ATTR static spi_flash_host_driver_t esp_flash_default_host = ESP_FLASH_DEFAULT_HOST_DRIVER(); #if SOC_MEMSPI_IS_INDEPENDENT extern void spi_flash_hal_gpspi_poll_cmd_done(spi_flash_host_inst_t *host); @@ -71,9 +73,9 @@ esp_err_t memspi_host_init_pointers(memspi_host_inst_t *host, const memspi_host_ } #if SOC_MEMSPI_IS_INDEPENDENT - if (cfg->host_id == SPI1_HOST) + if (cfg->host_id == SPI1_HOST) { host->inst.driver = &esp_flash_default_host; - else { + } else { host->inst.driver = &esp_flash_gpspi_host; } #else @@ -253,3 +255,52 @@ int memspi_host_read_data_slicer(spi_flash_host_inst_t *host, uint32_t address, } #endif // CONFIG_SPI_FLASH_ROM_IMPL + +#if CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ +// Dynamic flash configuration based on timing tuning state: +// 1. The timing tuning system automatically sets core clock and timing parameters (including din_mode/num, etc.). +// It also generates configurations such as clock configuration and dummy configuration for driver use. +// Without calling this function, the driver gets these configurations during initialization and uses them forever. +// The timing tuning provides clock configuration (frequency division) and dummy count. +// 2. When dynamic configuration is needed, call this function. The function supports two states: +// - Timing tuned state (high speed): Restore the values generated by the timing tuning system to the driver, +// as if there was no frequency reduction. +// - Without timing tuning state (low speed): Set configuration to maximum frequency (40 MHz on C5), dummy=0. +static uint32_t s_high_speed_clock_reg; +static uint32_t s_high_speed_extra_dummy; + +ESP_STATIC_ASSERT(MSPI_TIMING_LL_CORE_CLOCK_MHZ_DEFAULT == 80); +#define LOW_SPEED_DIV (MSPI_TIMING_LL_CORE_CLOCK_MHZ_DEFAULT/40) +static esp_err_t spi_flash_hal_device_config_c5(spi_flash_host_inst_t *host) +{ + spi_flash_hal_context_t* ctx = (spi_flash_hal_context_t*)host; + + // Note: SPI0 and SPI1 have separate TIMING_CALI_REG registers, but SPI0's register reflects the timing tuning state + // since SPI0 and SPI1 share din_mode/din_num registers and timing tuning is configured on SPI0 + uint32_t timing_cali_reg = REG_READ(SPI_MEM_TIMING_CALI_REG(MSPI_TIMING_LL_MSPI_ID_0)); + bool is_low_speed = !(timing_cali_reg & SPI_MEM_TIMING_CALI_M); + + if (is_low_speed) { + // Low speed mode: Set to safe division, dummy=0 + uint32_t low_speed_clock_reg = mspi_timing_ll_calculate_clock_reg(LOW_SPEED_DIV); + ctx->clock_conf.spimem = low_speed_clock_reg; + ctx->extra_dummy = 0; + } else { + // High speed mode: Restore timing tuning values + ctx->clock_conf.spimem = s_high_speed_clock_reg; + ctx->extra_dummy = s_high_speed_extra_dummy; + } + return spi_flash_hal_device_config(host); +} + +esp_err_t memspi_host_init_c5_dynamic_config(memspi_host_inst_t *host) +{ + assert(s_high_speed_clock_reg == 0 && s_high_speed_extra_dummy == 0); + + spi_flash_hal_context_t* ctx = (spi_flash_hal_context_t*)host; + s_high_speed_clock_reg = ctx->clock_conf.spimem; + s_high_speed_extra_dummy = ctx->extra_dummy; + esp_flash_default_host.dev_config = spi_flash_hal_device_config_c5; + return ESP_OK; +} +#endif diff --git a/components/spi_flash/spi_flash_os_func_app.c b/components/spi_flash/spi_flash_os_func_app.c index 0b658ab811d7..45551914d948 100644 --- a/components/spi_flash/spi_flash_os_func_app.c +++ b/components/spi_flash/spi_flash_os_func_app.c @@ -20,9 +20,12 @@ #include "esp_rom_sys.h" #include "esp_private/spi_flash_os.h" #include "esp_private/cache_utils.h" - #include "esp_private/spi_share_hw_ctrl.h" +// For C5 encrypted write workaround +// Functions are only available when CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ is true +#include "esp_private/spi_flash_freq_limit_cbs.h" + #define SPI_FLASH_CACHE_NO_DISABLE (CONFIG_SPI_FLASH_AUTO_SUSPEND || (CONFIG_SPIRAM_FETCH_INSTRUCTIONS && CONFIG_SPIRAM_RODATA) || CONFIG_APP_BUILD_TYPE_RAM) static const char TAG[] = "spi_flash"; @@ -53,6 +56,7 @@ typedef struct { bool no_protect; //to decide whether to check protected region (for the main chip) or not. uint32_t acquired_since_us; // Time since last explicit yield() uint32_t released_since_us; // Time since last end() (implicit yield) + uint32_t start_flags; // Flags passed to start() function, used to determine if freq_limit was called } app_func_arg_t; static inline void on_spi_released(app_func_arg_t* ctx); @@ -90,21 +94,27 @@ static IRAM_ATTR esp_err_t release_spi_bus_lock(void *arg) return spi_bus_lock_acquire_end(((app_func_arg_t *)arg)->dev_lock); } -static esp_err_t spi23_start(void *arg){ +static esp_err_t spi23_start(void *arg, uint32_t flags) +{ + (void)flags; esp_err_t ret = acquire_spi_bus_lock(arg); on_spi_acquired((app_func_arg_t*)arg); return ret; } -static esp_err_t spi23_end(void *arg){ +static esp_err_t spi23_end(void *arg) +{ esp_err_t ret = release_spi_bus_lock(arg); on_spi_released((app_func_arg_t*)arg); return ret; } -static IRAM_ATTR esp_err_t spi1_start(void *arg) +static IRAM_ATTR esp_err_t spi1_start(void *arg, uint32_t flags) { esp_err_t ret = ESP_OK; + app_func_arg_t* ctx = (app_func_arg_t*)arg; + ctx->start_flags = flags; + /** * There are three ways for ESP Flash API lock: * 1. spi bus lock, this is used when SPI1 is shared with GPSPI Master Driver @@ -136,13 +146,28 @@ static IRAM_ATTR esp_err_t spi1_start(void *arg) } #endif // CONFIG_SPI_FLASH_DISABLE_SCHEDULER_IN_SUSPEND - on_spi_acquired((app_func_arg_t*)arg); +#if CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ + if (flags & ESP_FLASH_START_FLAG_LIMIT_CPU_FREQ) { + esp_flash_freq_limit_cb(); + } +#endif + + on_spi_acquired(ctx); return ret; } static IRAM_ATTR esp_err_t spi1_end(void *arg) { esp_err_t ret = ESP_OK; + app_func_arg_t* ctx = (app_func_arg_t*)arg; + + // Call freq_limit_unlock if needed, before releasing the lock +#if CONFIG_SPI_FLASH_FREQ_LIMIT_C5_240MHZ + uint32_t flags = ctx->start_flags; + if (flags & ESP_FLASH_START_FLAG_LIMIT_CPU_FREQ) { + esp_flash_freq_unlimit_cb(); + } +#endif /** * There are three ways for ESP Flash API lock, see `spi1_start` @@ -166,7 +191,7 @@ static IRAM_ATTR esp_err_t spi1_end(void *arg) } #endif // CONFIG_SPI_FLASH_DISABLE_SCHEDULER_IN_SUSPEND - on_spi_released((app_func_arg_t*)arg); + on_spi_released(ctx); return ret; } diff --git a/components/spi_flash/spi_flash_os_func_noos.c b/components/spi_flash/spi_flash_os_func_noos.c index e65efd6c1b2e..0ba7c006c5fe 100644 --- a/components/spi_flash/spi_flash_os_func_noos.c +++ b/components/spi_flash/spi_flash_os_func_noos.c @@ -15,7 +15,7 @@ #include "hal/cache_ll.h" #include "soc/soc_caps.h" -static IRAM_ATTR esp_err_t start(void *arg) +static IRAM_ATTR esp_err_t start(void *arg, uint32_t flags) { #if SOC_BRANCH_PREDICTOR_SUPPORTED //branch predictor will start cache request as well diff --git a/components/spi_flash/test_apps/.build-test-rules.yml b/components/spi_flash/test_apps/.build-test-rules.yml index 740a6b9f1de3..1981f5fbb03f 100644 --- a/components/spi_flash/test_apps/.build-test-rules.yml +++ b/components/spi_flash/test_apps/.build-test-rules.yml @@ -15,6 +15,15 @@ components/spi_flash/test_apps/esp_flash: - esp_driver_spi - esptool_py # Some flash related kconfigs are listed here. +components/spi_flash/test_apps/esp_flash_freq_limit: + enable: + - if: IDF_TARGET == "esp32c5" + depends_components: + - spi_flash + - esp_pm + - esp_driver_gptimer + - esp_hw_support + components/spi_flash/test_apps/esp_flash_stress: disable: - if: IDF_TARGET == "esp32h4" diff --git a/components/spi_flash/test_apps/esp_flash_freq_limit/CMakeLists.txt b/components/spi_flash/test_apps/esp_flash_freq_limit/CMakeLists.txt new file mode 100644 index 000000000000..71d8608b0dca --- /dev/null +++ b/components/spi_flash/test_apps/esp_flash_freq_limit/CMakeLists.txt @@ -0,0 +1,17 @@ +# This is the project CMakeLists.txt file for the test subproject +cmake_minimum_required(VERSION 3.22) + +set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components") + +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. We also depend on +# esptool_py as we set CONFIG_ESPTOOLPY_... options. +set(COMPONENTS main esptool_py) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +project(test_esp_flash_freq_limit) + +message(STATUS "Checking memspi registers are not read-write by half-word") +include($ENV{IDF_PATH}/tools/ci/check_register_rw_half_word.cmake) +check_register_rw_half_word(SOC_MODULES "spi_mem*" "spi1_mem*" + HAL_MODULES "spimem_flash") diff --git a/components/spi_flash/test_apps/esp_flash_freq_limit/README.md b/components/spi_flash/test_apps/esp_flash_freq_limit/README.md new file mode 100644 index 000000000000..1d5f1c0a5724 --- /dev/null +++ b/components/spi_flash/test_apps/esp_flash_freq_limit/README.md @@ -0,0 +1,2 @@ +| Supported Targets | ESP32-C5 | +| ----------------- | -------- | diff --git a/components/spi_flash/test_apps/esp_flash_freq_limit/main/CMakeLists.txt b/components/spi_flash/test_apps/esp_flash_freq_limit/main/CMakeLists.txt new file mode 100644 index 000000000000..801934c79b97 --- /dev/null +++ b/components/spi_flash/test_apps/esp_flash_freq_limit/main/CMakeLists.txt @@ -0,0 +1,9 @@ +set(srcs "test_app_main.c" + "test_esp_flash_freq_limit.c") + +# In order for the cases defined by `TEST_CASE` to be linked into the final elf, +# the component can be registered as WHOLE_ARCHIVE +idf_component_register(SRCS ${srcs} + PRIV_REQUIRES unity test_utils spi_flash esp_pm bootloader_support freertos esp_timer + esp_driver_gptimer esp_hw_support esp_psram + WHOLE_ARCHIVE) diff --git a/components/spi_flash/test_apps/esp_flash_freq_limit/main/test_app_main.c b/components/spi_flash/test_apps/esp_flash_freq_limit/main/test_app_main.c new file mode 100644 index 000000000000..888d52aeabfe --- /dev/null +++ b/components/spi_flash/test_apps/esp_flash_freq_limit/main/test_app_main.c @@ -0,0 +1,25 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "unity_test_utils.h" + +#define TEST_MEMORY_LEAK_THRESHOLD (700) + +void setUp(void) +{ + unity_utils_record_free_mem(); +} + +void tearDown(void) +{ + unity_utils_evaluate_leaks_direct(TEST_MEMORY_LEAK_THRESHOLD); +} + +void app_main(void) +{ + unity_run_menu(); +} diff --git a/components/spi_flash/test_apps/esp_flash_freq_limit/main/test_esp_flash_freq_limit.c b/components/spi_flash/test_apps/esp_flash_freq_limit/main/test_esp_flash_freq_limit.c new file mode 100644 index 000000000000..c76fa6250824 --- /dev/null +++ b/components/spi_flash/test_apps/esp_flash_freq_limit/main/test_esp_flash_freq_limit.c @@ -0,0 +1,1250 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sdkconfig.h" + +#if CONFIG_IDF_TARGET_ESP32C5 + +#include +#include +#include +#include +#include "unity.h" +#include "esp_flash.h" +#include "soc/rtc.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_pm.h" +#include "driver/gptimer.h" +#include "esp_private/esp_clk.h" +#include "esp_log.h" +#include "test_utils.h" +#include "esp_partition.h" +#include "spi_flash_mmap.h" // For SPI_FLASH_SEC_SIZE + +#define TAG "test_freq_limit" + +/** + * Note: All tests in this file do NOT enable flash encryption. + * Even without encryption enabled, esp_flash_write_encrypted() will still trigger + * frequency switching (which is what we're testing). + * This test does not verify the correctness of encrypted writes. + */ + +// Use test data partition for flash operations +#define TEST_DATA_SIZE 64 +#define FREQ_LIMIT_MHZ 160 +#define MHZ (1000000) + +static uint8_t test_write_buf[TEST_DATA_SIZE]; +static uint32_t s_test_flash_offset = 0; // Will be initialized from test partition +static uint32_t s_test_flash_offset_area_a = 0; // Area A: for normal write/read test +static uint32_t s_test_flash_offset_area_b = 0; // Area B: for encrypted write test (no verification) + +// Note: No erase needed for these tests. +// The tests focus on frequency behavior during esp_flash_write_encrypted, not data integrity or encryption functionality. +// esp_flash_write_encrypted overwrites existing data. +// Erase operations are blocking and may disable interrupts/task switching, potentially interfering with concurrency test scenarios. + +static uint32_t get_cpu_freq_mhz(void) +{ + rtc_cpu_freq_config_t config; + rtc_clk_cpu_freq_get_config(&config); + return config.freq_mhz; +} + +static void verify_freq_restored(uint32_t original_freq) +{ + uint32_t freq = get_cpu_freq_mhz(); + TEST_ASSERT_EQUAL_UINT32(original_freq, freq); +} + +static esp_flash_t* get_test_flash_chip(void) +{ + // Initialize test flash offset from test data partition if not already initialized + if (s_test_flash_offset == 0) { + const esp_partition_t *test_part = get_test_data_partition(); + TEST_ASSERT_NOT_NULL(test_part); + s_test_flash_offset = test_part->address; + // Area A: start from partition start + s_test_flash_offset_area_a = test_part->address; + // Area B: start from partition start + TEST_DATA_SIZE (aligned to sector) + uint32_t sector_aligned_size = ((TEST_DATA_SIZE + SPI_FLASH_SEC_SIZE - 1) / SPI_FLASH_SEC_SIZE) * SPI_FLASH_SEC_SIZE; + s_test_flash_offset_area_b = test_part->address + sector_aligned_size; + ESP_LOGI(TAG, "Using test partition at offset 0x%x, size 0x%x", + test_part->address, test_part->size); + ESP_LOGI(TAG, "Area A: 0x%x, Area B: 0x%x", s_test_flash_offset_area_a, s_test_flash_offset_area_b); + } + return esp_flash_default_chip; +} + +static void prepare_test_data(void) +{ + for (int i = 0; i < TEST_DATA_SIZE; i++) { + test_write_buf[i] = (uint8_t)(i & 0xFF); + } +} + +#if CONFIG_PM_ENABLE +// Test frequency configurations: dynamically built based on CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ +// Only include frequencies that are <= CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ +#if CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ >= 240 +// Support 240MHz: test default (0), 240MHz, 160MHz, 80MHz +static const uint32_t s_test_max_freqs[] = {0, 240, 160, 80}; +static const uint32_t s_test_min_freqs[] = {0, 80, 80, 80}; +#elif CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ >= 160 +// Support up to 160MHz: test default (0), 160MHz, 80MHz +static const uint32_t s_test_max_freqs[] = {0, 160, 80}; +static const uint32_t s_test_min_freqs[] = {0, 80, 80}; +#elif CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ >= 80 +// Support up to 80MHz: test default (0), 80MHz +static const uint32_t s_test_max_freqs[] = {0, 80}; +static const uint32_t s_test_min_freqs[] = {0, 80}; +#else +// Support lower frequencies: test default (0) only +static const uint32_t s_test_max_freqs[] = {0}; +static const uint32_t s_test_min_freqs[] = {0}; +#endif +#define NUM_TEST_CONFIGS (sizeof(s_test_max_freqs) / sizeof(s_test_max_freqs[0])) + +/** + * Setup PM configuration for testing + * @param max_freq_mhz Maximum CPU frequency in MHz. If 0, configure with CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ. + * @param min_freq_mhz Minimum CPU frequency in MHz. If max_freq_mhz is 0, use XTAL frequency as min. + * @return Expected frequency after setup (max_freq_mhz if configured, CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ if max_freq_mhz is 0) + */ +static uint32_t setup_pm_config(uint32_t max_freq_mhz, uint32_t min_freq_mhz) +{ + uint32_t actual_max_freq = max_freq_mhz; + uint32_t actual_min_freq = min_freq_mhz; + + if (max_freq_mhz == 0) { + // Configure with default values (same as CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ) + actual_max_freq = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ; + if (min_freq_mhz == 0) { + // Use XTAL frequency as min when not specified + actual_min_freq = esp_clk_xtal_freq() / MHZ; + } + } + + // Configure PM with specific frequencies + esp_pm_config_t pm_config = { + .max_freq_mhz = actual_max_freq, + .min_freq_mhz = actual_min_freq, + .light_sleep_enable = false + }; + TEST_ESP_OK(esp_pm_configure(&pm_config)); + + // Wait a bit for PM to stabilize + vTaskDelay(pdMS_TO_TICKS(100)); + + uint32_t current_freq = get_cpu_freq_mhz(); + ESP_LOGI(TAG, "PM configured: max=%"PRIu32" MHz, min=%"PRIu32" MHz, current=%"PRIu32" MHz", + actual_max_freq, actual_min_freq, current_freq); + + // Verify current frequency matches max_freq_mhz (since no locks are held) + TEST_ASSERT_EQUAL_UINT32(actual_max_freq, current_freq); + return actual_max_freq; +} +#endif // CONFIG_PM_ENABLE + +// Note: We do NOT erase flash before write_encrypted operations in these tests. +// Reasons: +// 1. These tests focus on frequency behavior, not data integrity or encryption functionality. +// 2. We do not perform data verification (no read_encrypted or data comparison). +// 3. esp_flash_write_encrypted will overwrite existing data anyway. +// 4. Erase operations are blocking and may disable interrupts/task switching, which could +// interfere with concurrent testing scenarios (interrupts, task scheduling). +// 5. Skipping erase operations simplifies the tests and avoids potential timing issues. + +/** + * Helper function: Test frequency limit basic behavior with PM configuration + * @param max_freq_mhz Maximum CPU frequency in MHz. If 0, configure with CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ. + * @param min_freq_mhz Minimum CPU frequency in MHz. If max_freq_mhz is 0, use XTAL frequency as min. + */ +static void test_freq_limit_basic_behavior_common(uint32_t max_freq_mhz, uint32_t min_freq_mhz) +{ + esp_flash_t* chip = get_test_flash_chip(); + +#if CONFIG_PM_ENABLE + setup_pm_config(max_freq_mhz, min_freq_mhz); +#else + TEST_ASSERT_EQUAL_UINT32(CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ, get_cpu_freq_mhz()); +#endif + + uint32_t original_freq = get_cpu_freq_mhz(); + ESP_LOGI(TAG, "Original frequency: %"PRIu32" MHz", original_freq); + + prepare_test_data(); + + // Step 1: Erase and write known data to Area A, then verify + uint32_t erase_size = ((TEST_DATA_SIZE + SPI_FLASH_SEC_SIZE - 1) / SPI_FLASH_SEC_SIZE) * SPI_FLASH_SEC_SIZE; + ESP_LOGI(TAG, "Step 1: Erasing Area A at 0x%x, size %"PRIu32" bytes", s_test_flash_offset_area_a, erase_size); + esp_err_t ret = esp_flash_erase_region(chip, s_test_flash_offset_area_a, erase_size); + if (ret != ESP_OK) { + ESP_LOGW(TAG, "Flash erase failed (0x%x), continuing anyway.", ret); + } else { + ESP_LOGI(TAG, "Area A erased successfully"); + } + + ESP_LOGI(TAG, "Writing known data to Area A at 0x%x", s_test_flash_offset_area_a); + ret = esp_flash_write(chip, test_write_buf, s_test_flash_offset_area_a, TEST_DATA_SIZE); + TEST_ESP_OK(ret); + ESP_LOGI(TAG, "Data written to Area A successfully"); + + // Verify written data in Area A + uint8_t read_buf[TEST_DATA_SIZE]; + ESP_LOGI(TAG, "Reading back data from Area A at 0x%x", s_test_flash_offset_area_a); + ret = esp_flash_read(chip, read_buf, s_test_flash_offset_area_a, TEST_DATA_SIZE); + TEST_ESP_OK(ret); + ESP_LOGI(TAG, "Verifying data integrity in Area A"); + ESP_LOG_BUFFER_HEXDUMP(TAG, test_write_buf, TEST_DATA_SIZE, ESP_LOG_INFO); + ESP_LOG_BUFFER_HEXDUMP(TAG, read_buf, TEST_DATA_SIZE, ESP_LOG_INFO); + TEST_ASSERT_EQUAL_HEX8_ARRAY(test_write_buf, read_buf, TEST_DATA_SIZE); + ESP_LOGI(TAG, "Area A data verification passed"); + + // Step 2: Encrypted write to Area B (no verification) + uint32_t freq_before = get_cpu_freq_mhz(); + ESP_LOGI(TAG, "Step 2: Frequency before write_encrypted: %"PRIu32" MHz", freq_before); + ESP_LOGI(TAG, "About to call esp_flash_write_encrypted to Area B, addr=0x%x, len=%d", s_test_flash_offset_area_b, TEST_DATA_SIZE); + // Note: This test does not verify the correctness of encrypted writes + ret = esp_flash_write_encrypted(chip, s_test_flash_offset_area_b, test_write_buf, TEST_DATA_SIZE); + ESP_LOGI(TAG, "esp_flash_write_encrypted returned: %d", ret); + TEST_ESP_OK(ret); + + // Wait for flash operation to complete + vTaskDelay(pdMS_TO_TICKS(10)); + + // Frequency should be restored after the call completes (or unchanged if already <= 160MHz) + uint32_t freq_after = get_cpu_freq_mhz(); + ESP_LOGI(TAG, "Frequency after write_encrypted: %"PRIu32" MHz", freq_after); + + // Verify frequency behavior: + // - If original frequency > 160MHz: should be restored to original + // - If original frequency <= 160MHz: should remain unchanged + if (original_freq > FREQ_LIMIT_MHZ) { + verify_freq_restored(original_freq); + ESP_LOGI(TAG, "Frequency restored to original %"PRIu32" MHz (was > 160MHz)", original_freq); + } else { + TEST_ASSERT_EQUAL_UINT32(original_freq, freq_after); + ESP_LOGI(TAG, "Frequency unchanged at %"PRIu32" MHz (was <= 160MHz)", original_freq); + } + + // Step 3: Read and verify Area A again to confirm read API still works after encrypted write + ESP_LOGI(TAG, "Step 3: Reading Area A again at 0x%x to verify read API still works", s_test_flash_offset_area_a); + ret = esp_flash_read(chip, read_buf, s_test_flash_offset_area_a, TEST_DATA_SIZE); + TEST_ESP_OK(ret); + ESP_LOGI(TAG, "Verifying data integrity in Area A after encrypted write"); + ESP_LOG_BUFFER_HEXDUMP(TAG, test_write_buf, TEST_DATA_SIZE, ESP_LOG_INFO); + ESP_LOG_BUFFER_HEXDUMP(TAG, read_buf, TEST_DATA_SIZE, ESP_LOG_INFO); + TEST_ASSERT_EQUAL_HEX8_ARRAY(test_write_buf, read_buf, TEST_DATA_SIZE); + ESP_LOGI(TAG, "Area A data verification passed after encrypted write - read API is still functional"); +} + +/** + * Test: Frequency limit: basic behavior + * Target: Verify that esp_flash_write_encrypted properly limits CPU frequency to <= 160MHz during execution + * and restores the original frequency after completion, with different PM configurations. + * Expected: + * - If original frequency > 160MHz: frequency should be limited to 160MHz during write, restored to original after + * - If original frequency <= 160MHz: frequency should remain unchanged + * - Written data should be verified by reading it back + */ +TEST_CASE("Frequency limit: basic behavior", "[esp_flash_freq_limit]") +{ +#if CONFIG_PM_ENABLE + for (int i = 0; i < NUM_TEST_CONFIGS; i++) { + ESP_LOGI(TAG, "Testing with PM config: max=%"PRIu32" MHz, min=%"PRIu32" MHz", + s_test_max_freqs[i], s_test_min_freqs[i]); + test_freq_limit_basic_behavior_common(s_test_max_freqs[i], s_test_min_freqs[i]); + } +#else + test_freq_limit_basic_behavior_common(0, 0); +#endif +} + +/** + * Helper function: Test read Flash ID after write_encrypted with PM configuration + * @param max_freq_mhz Maximum CPU frequency in MHz. If 0, configure with CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ. + * @param min_freq_mhz Minimum CPU frequency in MHz. If max_freq_mhz is 0, use XTAL frequency as min. + */ +static void test_read_flash_id_after_write_common(uint32_t max_freq_mhz, uint32_t min_freq_mhz) +{ + esp_flash_t* chip = get_test_flash_chip(); + +#if CONFIG_PM_ENABLE + setup_pm_config(max_freq_mhz, min_freq_mhz); +#else + TEST_ASSERT_EQUAL_UINT32(CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ, get_cpu_freq_mhz()); +#endif + + uint32_t original_freq = get_cpu_freq_mhz(); + ESP_LOGI(TAG, "Original frequency: %"PRIu32" MHz", original_freq); + + // Read Flash ID before write_encrypted + uint32_t flash_id_before = 0; + ESP_LOGI(TAG, "Reading Flash ID before write_encrypted"); + esp_err_t ret = esp_flash_read_id(chip, &flash_id_before); + TEST_ESP_OK(ret); + ESP_LOGI(TAG, "Flash ID before write_encrypted: 0x%08"PRIx32, flash_id_before); + TEST_ASSERT_NOT_EQUAL(0, flash_id_before); + TEST_ASSERT_NOT_EQUAL(0xFFFFFFFF, flash_id_before); + + prepare_test_data(); + + // Erase the test region before writing + // esp_flash_erase_region requires size to be aligned to sector size (4KB) + // Calculate the number of sectors needed (round up) + uint32_t erase_size = ((TEST_DATA_SIZE + SPI_FLASH_SEC_SIZE - 1) / SPI_FLASH_SEC_SIZE) * SPI_FLASH_SEC_SIZE; + ESP_LOGI(TAG, "Erasing flash region at 0x%x, size %d (aligned to %"PRIu32" bytes)", + s_test_flash_offset, TEST_DATA_SIZE, erase_size); + ret = esp_flash_erase_region(chip, s_test_flash_offset, erase_size); + // If erase fails, it might be because the region is already erased or in use + // Try to continue anyway - write_encrypted will overwrite the data + if (ret != ESP_OK) { + ESP_LOGW(TAG, "Flash erase failed (0x%x), continuing anyway", ret); + } else { + ESP_LOGI(TAG, "Flash region erased successfully"); + } + + // Perform write_encrypted operation + uint32_t freq_before = get_cpu_freq_mhz(); + ESP_LOGI(TAG, "Frequency before write_encrypted: %"PRIu32" MHz", freq_before); + + ESP_LOGI(TAG, "Calling esp_flash_write_encrypted, addr=0x%x, len=%d", s_test_flash_offset, TEST_DATA_SIZE); + ret = esp_flash_write_encrypted(chip, s_test_flash_offset, test_write_buf, TEST_DATA_SIZE); + TEST_ESP_OK(ret); + + // Verify frequency is restored + uint32_t freq_after = get_cpu_freq_mhz(); + ESP_LOGI(TAG, "Frequency after write_encrypted: %"PRIu32" MHz", freq_after); + if (original_freq > FREQ_LIMIT_MHZ) { + verify_freq_restored(original_freq); + } else { + TEST_ASSERT_EQUAL_UINT32(original_freq, freq_after); + } + + // Read Flash ID after write_encrypted to verify driver is still functional + // This uses a real read operation that accesses the Flash hardware + uint32_t flash_id_after = 0; + ESP_LOGI(TAG, "Reading Flash ID after write_encrypted"); + ret = esp_flash_read_id(chip, &flash_id_after); + TEST_ESP_OK(ret); + ESP_LOGI(TAG, "Flash ID after write_encrypted: 0x%08"PRIx32, flash_id_after); + + // Verify Flash ID matches (driver should still be functional) + TEST_ASSERT_EQUAL_UINT32(flash_id_before, flash_id_after); + ESP_LOGI(TAG, "Flash ID verification passed - driver is still functional"); +} + +/** + * Helper function: Test read Flash ID only (no write operations) + * @param max_freq_mhz Maximum CPU frequency in MHz. If 0, configure with CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ. + * @param min_freq_mhz Minimum CPU frequency in MHz. If max_freq_mhz is 0, use XTAL frequency as min. + */ +#if CONFIG_PM_ENABLE +static void test_read_flash_id_only_common(uint32_t max_freq_mhz, uint32_t min_freq_mhz) +{ + esp_flash_t* chip = get_test_flash_chip(); + + setup_pm_config(max_freq_mhz, min_freq_mhz); + + uint32_t current_freq = get_cpu_freq_mhz(); + ESP_LOGI(TAG, "Current frequency: %"PRIu32" MHz", current_freq); + + // Read Flash ID multiple times to verify consistency + // Note: At high frequencies (240MHz, 160MHz), Flash timing may not be properly configured, + // causing incorrect Flash ID reads. We need to temporarily lower CPU frequency to 80MHz + // to read Flash ID correctly, then restore original frequency. + uint32_t flash_id = 0; + + // If CPU frequency is high (>= 160MHz), temporarily lower CPU frequency to 80MHz for reading Flash ID + uint32_t original_max_freq = 0; + uint32_t original_min_freq = 0; + bool freq_was_lowered = false; + if (current_freq >= 160) { + ESP_LOGI(TAG, "CPU frequency is %"PRIu32" MHz, temporarily lowering to 80MHz for Flash ID read", current_freq); + + // Save current PM config + esp_pm_config_t pm_config; + esp_pm_get_configuration(&pm_config); + original_max_freq = pm_config.max_freq_mhz; + original_min_freq = pm_config.min_freq_mhz; + + // Temporarily set max and min to 80MHz + esp_pm_config_t temp_pm_config = { + .max_freq_mhz = 80, + .min_freq_mhz = 80, + .light_sleep_enable = false + }; + TEST_ESP_OK(esp_pm_configure(&temp_pm_config)); + vTaskDelay(pdMS_TO_TICKS(100)); // Wait for frequency to stabilize + + uint32_t temp_freq = get_cpu_freq_mhz(); + ESP_LOGI(TAG, "Frequency lowered to %"PRIu32" MHz", temp_freq); + TEST_ASSERT_EQUAL_UINT32(80, temp_freq); + freq_was_lowered = true; + } + + extern esp_flash_t *esp_flash_default_chip; + uint32_t expect_chip_id = esp_flash_default_chip->chip_id; + for (int i = 0; i < 5; i++) { + ESP_LOGI(TAG, "Reading Flash ID (attempt %d)", i + 1); + esp_err_t ret = esp_flash_read_id(chip, &flash_id); + TEST_ESP_OK(ret); + uint32_t read_freq = get_cpu_freq_mhz(); + ESP_LOGI(TAG, "Flash ID at %"PRIu32" MHz: 0x%08"PRIx32, read_freq, flash_id); + TEST_ASSERT_EQUAL_UINT32(expect_chip_id, flash_id); + vTaskDelay(pdMS_TO_TICKS(10)); + } + + // Restore original frequency if we lowered it + if (freq_was_lowered) { + ESP_LOGI(TAG, "Restoring original frequency: max=%"PRIu32" MHz, min=%"PRIu32" MHz", + original_max_freq, original_min_freq); + esp_pm_config_t restore_pm_config = { + .max_freq_mhz = original_max_freq, + .min_freq_mhz = original_min_freq, + .light_sleep_enable = false + }; + TEST_ESP_OK(esp_pm_configure(&restore_pm_config)); + vTaskDelay(pdMS_TO_TICKS(100)); // Wait for frequency to stabilize + + uint32_t restored_freq = get_cpu_freq_mhz(); + ESP_LOGI(TAG, "Frequency restored to %"PRIu32" MHz", restored_freq); + TEST_ASSERT_EQUAL_UINT32(original_max_freq, restored_freq); + } + + ESP_LOGI(TAG, "Final Flash ID: 0x%08"PRIx32, flash_id); + TEST_ASSERT_EQUAL_HEX32(expect_chip_id, flash_id); +} + +/** + * Test: Frequency limit: read Flash ID only + * Target: Verify Flash ID reading at different frequencies + * Expected: + * - Flash ID should be readable at all frequencies + * - Flash ID should be same as the default chip id + */ +TEST_CASE("Frequency limit: read Flash ID only", "[esp_flash_freq_limit]") +{ + for (int i = 0; i < NUM_TEST_CONFIGS; i++) { + ESP_LOGI(TAG, "Testing read Flash ID only with PM config: max=%"PRIu32" MHz, min=%"PRIu32" MHz", + s_test_max_freqs[i], s_test_min_freqs[i]); + test_read_flash_id_only_common(s_test_max_freqs[i], s_test_min_freqs[i]); + } +} +#endif // CONFIG_PM_ENABLE + +/** + * Test: Frequency limit: read Flash ID after write_encrypted + * Target: Verify that the Flash driver remains functional after esp_flash_write_encrypted + * by reading the Flash ID using a real read operation. + * Expected: + * - Flash ID should be readable before and after write_encrypted + * - Flash ID should match before and after (driver state should be consistent) + * - This verifies that the driver is still in a normal state after write_encrypted + */ +TEST_CASE("Frequency limit: read Flash ID after write_encrypted", "[esp_flash_freq_limit]") +{ +#if CONFIG_PM_ENABLE + for (int i = 0; i < NUM_TEST_CONFIGS; i++) { + ESP_LOGI(TAG, "Testing read Flash ID with PM config: max=%"PRIu32" MHz, min=%"PRIu32" MHz", + s_test_max_freqs[i], s_test_min_freqs[i]); + test_read_flash_id_after_write_common(s_test_max_freqs[i], s_test_min_freqs[i]); + } +#else + test_read_flash_id_after_write_common(0, 0); +#endif +} + +/** + * Helper function: Test multiple write_encrypted calls with PM configuration + * @param max_freq_mhz Maximum CPU frequency in MHz. If 0, configure with CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ. + * @param min_freq_mhz Minimum CPU frequency in MHz. If max_freq_mhz is 0, use XTAL frequency as min. + */ +static void test_multiple_write_encrypted_calls_common(uint32_t max_freq_mhz, uint32_t min_freq_mhz) +{ + esp_flash_t* chip = get_test_flash_chip(); + +#if CONFIG_PM_ENABLE + setup_pm_config(max_freq_mhz, min_freq_mhz); +#else + TEST_ASSERT_EQUAL_UINT32(CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ, get_cpu_freq_mhz()); +#endif + + uint32_t original_freq = get_cpu_freq_mhz(); + ESP_LOGI(TAG, "Original frequency: %"PRIu32" MHz", original_freq); + + prepare_test_data(); + // Note: No erase needed - see comment above erase_test_region function + + for (int i = 0; i < 3; i++) { + uint32_t offset = s_test_flash_offset + (i * TEST_DATA_SIZE); + uint32_t freq_before = get_cpu_freq_mhz(); + + esp_err_t ret = esp_flash_write_encrypted(chip, offset, test_write_buf, TEST_DATA_SIZE); + TEST_ESP_OK(ret); + + uint32_t freq_after = get_cpu_freq_mhz(); + verify_freq_restored(original_freq); + + ESP_LOGI(TAG, "Iteration %d: freq_before=%"PRIu32" MHz, freq_after=%"PRIu32" MHz", i, freq_before, freq_after); + } +} + +/** + * Test: Frequency limit: multiple write_encrypted calls + * Target: Verify that frequency limiting works correctly across multiple consecutive write_encrypted calls. + * Expected: Frequency should be limited during each call and restored after each call completes. + */ +TEST_CASE("Frequency limit: multiple write_encrypted calls", "[esp_flash_freq_limit]") +{ +#if CONFIG_PM_ENABLE + for (int i = 0; i < NUM_TEST_CONFIGS; i++) { + ESP_LOGI(TAG, "Testing with PM config: max=%"PRIu32" MHz, min=%"PRIu32" MHz", + s_test_max_freqs[i], s_test_min_freqs[i]); + test_multiple_write_encrypted_calls_common(s_test_max_freqs[i], s_test_min_freqs[i]); + } +#else + test_multiple_write_encrypted_calls_common(0, 0); +#endif +} + +/** + * Test: Frequency limit: zero length write + * Target: Verify that zero-length write_encrypted calls do not trigger frequency limiting (early return). + * Expected: Frequency should remain unchanged when length is 0. + */ +TEST_CASE("Frequency limit: zero length write", "[esp_flash_freq_limit]") +{ + esp_flash_t* chip = get_test_flash_chip(); +#if CONFIG_PM_ENABLE + // Initialize PM config to ensure frequency is at max + setup_pm_config(0, 0); // Use default max frequency +#endif + uint32_t original_freq = get_cpu_freq_mhz(); + + // Verify initial frequency matches CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ + TEST_ASSERT_EQUAL_UINT32(CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ, original_freq); + + ESP_LOGI(TAG, "Original frequency: %"PRIu32" MHz", original_freq); + + esp_err_t ret = esp_flash_write_encrypted(chip, s_test_flash_offset, test_write_buf, 0); + TEST_ESP_OK(ret); + + uint32_t freq_after = get_cpu_freq_mhz(); + + TEST_ASSERT_EQUAL_UINT32(original_freq, freq_after); + ESP_LOGI(TAG, "Zero length write: freq unchanged (%"PRIu32" MHz)", freq_after); +} + +/** + * Test: Frequency limit: misaligned address + * Target: Verify that frequency limiting is not triggered when write fails due to misaligned address. + * Expected: Function should return ESP_ERR_INVALID_ARG and frequency should remain unchanged. + */ +TEST_CASE("Frequency limit: misaligned address", "[esp_flash_freq_limit]") +{ + esp_flash_t* chip = get_test_flash_chip(); +#if CONFIG_PM_ENABLE + // Initialize PM config to ensure frequency is at max + setup_pm_config(0, 0); // Use default max frequency +#endif + uint32_t original_freq = get_cpu_freq_mhz(); + + // Verify initial frequency matches CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ + TEST_ASSERT_EQUAL_UINT32(CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ, original_freq); + + ESP_LOGI(TAG, "Original frequency: %"PRIu32" MHz", original_freq); + + prepare_test_data(); + + esp_err_t ret = esp_flash_write_encrypted(chip, s_test_flash_offset + 1, test_write_buf, TEST_DATA_SIZE); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, ret); + + uint32_t freq_after = get_cpu_freq_mhz(); + + TEST_ASSERT_EQUAL_UINT32(original_freq, freq_after); + ESP_LOGI(TAG, "Misaligned address: freq unchanged (%"PRIu32" MHz)", freq_after); +} + +/** + * Test: Frequency limit: misaligned length + * Target: Verify that frequency limiting is not triggered when write fails due to misaligned length. + * Expected: Function should return ESP_ERR_INVALID_SIZE and frequency should remain unchanged. + */ +TEST_CASE("Frequency limit: misaligned length", "[esp_flash_freq_limit]") +{ + esp_flash_t* chip = get_test_flash_chip(); +#if CONFIG_PM_ENABLE + // Initialize PM config to ensure frequency is at max + setup_pm_config(0, 0); // Use default max frequency +#endif + uint32_t original_freq = get_cpu_freq_mhz(); + + // Verify initial frequency matches CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ + TEST_ASSERT_EQUAL_UINT32(CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ, original_freq); + + ESP_LOGI(TAG, "Original frequency: %"PRIu32" MHz", original_freq); + + prepare_test_data(); + + esp_err_t ret = esp_flash_write_encrypted(chip, s_test_flash_offset, test_write_buf, TEST_DATA_SIZE + 1); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_SIZE, ret); + + uint32_t freq_after = get_cpu_freq_mhz(); + + TEST_ASSERT_EQUAL_UINT32(original_freq, freq_after); + ESP_LOGI(TAG, "Misaligned length: freq unchanged (%"PRIu32" MHz)", freq_after); +} + +/** + * Test: Frequency limit: write failure recovery + * Target: Verify that frequency is properly restored even when write_encrypted fails with an error. + * Expected: Function should return error and frequency should be restored to original value. + */ +TEST_CASE("Frequency limit: write failure recovery", "[esp_flash_freq_limit]") +{ + esp_flash_t* chip = get_test_flash_chip(); +#if CONFIG_PM_ENABLE + // Initialize PM config to ensure frequency is at max + setup_pm_config(0, 0); // Use default max frequency +#endif + uint32_t original_freq = get_cpu_freq_mhz(); + + // Verify initial frequency matches CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ + TEST_ASSERT_EQUAL_UINT32(CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ, original_freq); + + ESP_LOGI(TAG, "Original frequency: %"PRIu32" MHz", original_freq); + + prepare_test_data(); + + esp_err_t ret = esp_flash_write_encrypted(chip, s_test_flash_offset + 1, test_write_buf, TEST_DATA_SIZE); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, ret); + + uint32_t freq_after = get_cpu_freq_mhz(); + + TEST_ASSERT_EQUAL_UINT32(original_freq, freq_after); + ESP_LOGI(TAG, "Write failure: freq restored to original (%"PRIu32" MHz)", freq_after); +} + +/** + * Helper function: Test multiple write_encrypted operations with PM configuration + * @param max_freq_mhz Maximum CPU frequency in MHz. If 0, configure with CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ. + * @param min_freq_mhz Minimum CPU frequency in MHz. If max_freq_mhz is 0, use XTAL frequency as min. + */ +static void test_multiple_write_encrypted_operations_common(uint32_t max_freq_mhz, uint32_t min_freq_mhz) +{ + esp_flash_t* chip = get_test_flash_chip(); + +#if CONFIG_PM_ENABLE + setup_pm_config(max_freq_mhz, min_freq_mhz); +#else + TEST_ASSERT_EQUAL_UINT32(CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ, get_cpu_freq_mhz()); +#endif + + uint32_t original_freq = get_cpu_freq_mhz(); + ESP_LOGI(TAG, "Original frequency: %"PRIu32" MHz", original_freq); + + prepare_test_data(); + // Note: No erase needed - see comment above erase_test_region function + + // First write - frequency should be limited during call and restored after + esp_err_t ret = esp_flash_write_encrypted(chip, s_test_flash_offset, test_write_buf, TEST_DATA_SIZE); + TEST_ESP_OK(ret); + verify_freq_restored(original_freq); + + // Second write - frequency should be limited during call and restored after + ret = esp_flash_write_encrypted(chip, s_test_flash_offset + TEST_DATA_SIZE, test_write_buf, TEST_DATA_SIZE); + TEST_ESP_OK(ret); + verify_freq_restored(original_freq); + + ESP_LOGI(TAG, "Multiple operations: freq properly restored after each (%"PRIu32" MHz)", original_freq); +} + +/** + * Test: Frequency limit: multiple write_encrypted operations + * Target: Verify that frequency limiting and restoration work correctly for multiple independent write operations. + * Expected: Frequency should be limited during each write and restored after each write completes. + */ +TEST_CASE("Frequency limit: multiple write_encrypted operations", "[esp_flash_freq_limit]") +{ +#if CONFIG_PM_ENABLE + for (int i = 0; i < NUM_TEST_CONFIGS; i++) { + ESP_LOGI(TAG, "Testing with PM config: max=%"PRIu32" MHz, min=%"PRIu32" MHz", + s_test_max_freqs[i], s_test_min_freqs[i]); + test_multiple_write_encrypted_operations_common(s_test_max_freqs[i], s_test_min_freqs[i]); + } +#else + test_multiple_write_encrypted_operations_common(0, 0); +#endif +} + +// ============================================================================ +// Interrupt concurrency test cases +// ============================================================================ + +#if CONFIG_PM_ENABLE + +/** + * Helper function: Test CPU lock held before encrypt with PM configuration + * @param max_freq_mhz Maximum CPU frequency in MHz. If 0, configure with CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ. + * @param min_freq_mhz Minimum CPU frequency in MHz. If max_freq_mhz is 0, use XTAL frequency as min. + */ +static void test_cpu_lock_held_before_encrypt_common(uint32_t max_freq_mhz, uint32_t min_freq_mhz) +{ + esp_flash_t* chip = get_test_flash_chip(); + +#if CONFIG_PM_ENABLE + uint32_t configured_max_freq = setup_pm_config(max_freq_mhz, min_freq_mhz); +#else + TEST_ASSERT_EQUAL_UINT32(CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ, get_cpu_freq_mhz()); + uint32_t configured_max_freq = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ; +#endif + + uint32_t original_freq = get_cpu_freq_mhz(); + ESP_LOGI(TAG, "Original frequency: %"PRIu32" MHz", original_freq); + + prepare_test_data(); + // Note: No erase needed - see comment above erase_test_region function + + // Create and acquire CPU lock BEFORE encrypt + esp_pm_lock_handle_t cpu_lock = NULL; + TEST_ESP_OK(esp_pm_lock_create(ESP_PM_CPU_FREQ_MAX, 0, "test_lock", &cpu_lock)); + TEST_ASSERT_NOT_NULL(cpu_lock); + + TEST_ESP_OK(esp_pm_lock_acquire(cpu_lock)); + + // Verify frequency is at max + uint32_t freq_after_lock = get_cpu_freq_mhz(); + ESP_LOGI(TAG, "Frequency after acquiring CPU lock: %"PRIu32" MHz", freq_after_lock); + TEST_ASSERT_EQUAL_UINT32(configured_max_freq, freq_after_lock); + + // Perform encrypt operation - frequency should still be limited to 160MHz + // even though CPU lock is held (encrypt lock has higher priority) + esp_err_t ret = esp_flash_write_encrypted(chip, s_test_flash_offset, test_write_buf, TEST_DATA_SIZE); + TEST_ESP_OK(ret); + + // Verify frequency is restored to max (CPU lock still held) + uint32_t freq_after_encrypt = get_cpu_freq_mhz(); + ESP_LOGI(TAG, "Frequency after encrypt (lock still held): %"PRIu32" MHz", freq_after_encrypt); + TEST_ASSERT_EQUAL_UINT32(freq_after_lock, freq_after_encrypt); + + // Release CPU lock + esp_pm_lock_release(cpu_lock); + + // Verify frequency restored + verify_freq_restored(original_freq); + + // Clean up + esp_pm_lock_delete(cpu_lock); +} + +/** + * Test: Frequency limit: CPU lock held before encrypt + * Target: Verify that frequency limiting works correctly when a PM CPU lock is already held before encrypt execution. + * The encrypt frequency limit lock (PM_MODE_CPU_ENCRYPT_LOCK, limits to <= 160MHz) has higher priority than CPU lock (PM_MODE_CPU_MAX). + * Expected: Frequency should be limited to 160MHz during encrypt (even with CPU lock held), then restored to max + * after encrypt completes (CPU lock still held). + */ +TEST_CASE("Frequency limit: CPU lock held before encrypt", "[esp_flash_freq_limit]") +{ +#if CONFIG_PM_ENABLE + for (int i = 0; i < NUM_TEST_CONFIGS; i++) { + ESP_LOGI(TAG, "Testing with PM config: max=%"PRIu32" MHz, min=%"PRIu32" MHz", + s_test_max_freqs[i], s_test_min_freqs[i]); + test_cpu_lock_held_before_encrypt_common(s_test_max_freqs[i], s_test_min_freqs[i]); + } +#else + test_cpu_lock_held_before_encrypt_common(0, 0); +#endif +} + +#endif // CONFIG_PM_ENABLE + +// ============================================================================ +// Interrupt concurrency test cases +// ============================================================================ + +// Shared state for interrupt tests +static volatile bool s_interrupt_occurred = false; +static gptimer_handle_t s_test_timer = NULL; + +#if CONFIG_PM_ENABLE +// PM lock handles for ISR tests (must be set before interrupt) +static esp_pm_lock_handle_t s_isr_pm_lock_cpu = NULL; +static esp_pm_lock_handle_t s_isr_pm_lock_apb = NULL; +static volatile bool s_isr_lock_acquired = false; +static volatile bool s_isr_lock_released = false; +#endif // CONFIG_PM_ENABLE + +// ISR callback - must be IRAM-safe (declared before use) +static bool IRAM_ATTR timer_isr_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data) +{ + s_interrupt_occurred = true; + return false; +} + +#if CONFIG_PM_ENABLE + +// ISR callback for test 3: acquire and release lock in ISR +static bool IRAM_ATTR timer_isr_callback_acquire_release(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data) +{ + s_interrupt_occurred = true; + + // Acquire lock in ISR + if (s_isr_pm_lock_cpu != NULL) { + esp_pm_lock_acquire(s_isr_pm_lock_cpu); + s_isr_lock_acquired = true; + // Release immediately + esp_pm_lock_release(s_isr_pm_lock_cpu); + s_isr_lock_released = true; + } + if (s_isr_pm_lock_apb != NULL) { + esp_pm_lock_acquire(s_isr_pm_lock_apb); + s_isr_lock_acquired = true; + // Release immediately + esp_pm_lock_release(s_isr_pm_lock_apb); + s_isr_lock_released = true; + } + + return false; +} + +// ISR callback for test 4: acquire lock in ISR, release after write completes +static bool IRAM_ATTR timer_isr_callback_acquire(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data) +{ + s_interrupt_occurred = true; + + // Acquire lock in ISR (will be released after write completes in test code) + if (s_isr_pm_lock_cpu != NULL) { + esp_pm_lock_acquire(s_isr_pm_lock_cpu); + s_isr_lock_acquired = true; + } + if (s_isr_pm_lock_apb != NULL) { + esp_pm_lock_acquire(s_isr_pm_lock_apb); + s_isr_lock_acquired = true; + } + + return false; +} + +// ISR callback for test 5: release lock that was acquired before encrypt +static bool IRAM_ATTR timer_isr_callback_release(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data) +{ + s_interrupt_occurred = true; + + // Release lock in ISR (was acquired before encrypt) + if (s_isr_pm_lock_cpu != NULL) { + esp_pm_lock_release(s_isr_pm_lock_cpu); + s_isr_lock_released = true; + } + if (s_isr_pm_lock_apb != NULL) { + esp_pm_lock_release(s_isr_pm_lock_apb); + s_isr_lock_released = true; + } + + return false; +} + +#endif // CONFIG_PM_ENABLE + +// Helper function to setup GPTimer for interrupt during encrypt +static void setup_interrupt_timer(uint32_t delay_us, gptimer_alarm_cb_t callback) +{ + // Clean up any existing timer first to avoid resource leaks + if (s_test_timer != NULL) { + gptimer_stop(s_test_timer); + gptimer_disable(s_test_timer); + gptimer_del_timer(s_test_timer); + s_test_timer = NULL; + } + + gptimer_config_t timer_config = { + .clk_src = GPTIMER_CLK_SRC_DEFAULT, + .direction = GPTIMER_COUNT_UP, + .resolution_hz = 1000000, // 1MHz, 1 tick = 1us + }; + TEST_ESP_OK(gptimer_new_timer(&timer_config, &s_test_timer)); + + gptimer_event_callbacks_t cbs = { + .on_alarm = callback, + }; + TEST_ESP_OK(gptimer_register_event_callbacks(s_test_timer, &cbs, NULL)); + TEST_ESP_OK(gptimer_enable(s_test_timer)); + + gptimer_alarm_config_t alarm_config = { + .reload_count = 0, + .alarm_count = delay_us, + .flags.auto_reload_on_alarm = false, + }; + TEST_ESP_OK(gptimer_set_alarm_action(s_test_timer, &alarm_config)); + + s_interrupt_occurred = false; +#if CONFIG_PM_ENABLE + s_isr_lock_acquired = false; + s_isr_lock_released = false; +#endif // CONFIG_PM_ENABLE + TEST_ESP_OK(gptimer_start(s_test_timer)); +} + +static void cleanup_interrupt_timer(void) +{ + if (s_test_timer != NULL) { + gptimer_stop(s_test_timer); + gptimer_disable(s_test_timer); + gptimer_del_timer(s_test_timer); + s_test_timer = NULL; + } +} + + +/** + * Helper function: Test interrupt during encrypt with PM configuration + * Common test logic for both PM enabled and disabled scenarios + * @param max_freq_mhz Maximum CPU frequency in MHz. If 0, configure with CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ. + * @param min_freq_mhz Minimum CPU frequency in MHz. If max_freq_mhz is 0, use XTAL frequency as min. + */ +static void test_interrupt_during_encrypt_common(uint32_t max_freq_mhz, uint32_t min_freq_mhz) +{ + esp_flash_t* chip = get_test_flash_chip(); + +#if CONFIG_PM_ENABLE + setup_pm_config(max_freq_mhz, min_freq_mhz); +#else + TEST_ASSERT_EQUAL_UINT32(CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ, get_cpu_freq_mhz()); +#endif + + uint32_t original_freq = get_cpu_freq_mhz(); + prepare_test_data(); + + // Setup interrupt to trigger during encrypt (ensure it happens before write completes) + setup_interrupt_timer(50, timer_isr_callback); // 50us delay - should trigger during write + + // Perform encrypt operation - interrupt should occur during this call + esp_err_t ret = esp_flash_write_encrypted(chip, s_test_flash_offset, test_write_buf, TEST_DATA_SIZE); + TEST_ESP_OK(ret); + + // Wait for interrupt to be processed + vTaskDelay(pdMS_TO_TICKS(10)); + + // Verify interrupt occurred + TEST_ASSERT_TRUE(s_interrupt_occurred); + + // Verify frequency was restored + verify_freq_restored(original_freq); + + cleanup_interrupt_timer(); +} + +/** + * Test: Frequency limit: interrupt during encrypt (PM disabled) + * Target: Verify that frequency limiting works correctly when an interrupt occurs during encrypt execution with PM disabled. + * Expected: Interrupt should occur during write, frequency should be limited during write, and restored after write completes. + */ +TEST_CASE("Frequency limit: interrupt during encrypt (PM disabled)", "[esp_flash_freq_limit]") +{ + test_interrupt_during_encrypt_common(0, 0); +} + +#if CONFIG_PM_ENABLE +/** + * Test: Frequency limit: interrupt during encrypt (PM enabled) + * Target: Verify that frequency limiting works correctly when an interrupt occurs during encrypt execution with PM enabled. + * Expected: Interrupt should occur during write, frequency should be limited during write, and restored after write completes. + */ +TEST_CASE("Frequency limit: interrupt during encrypt (PM enabled)", "[esp_flash_freq_limit]") +{ + for (int i = 0; i < NUM_TEST_CONFIGS; i++) { + ESP_LOGI(TAG, "Testing with PM config: max=%"PRIu32" MHz, min=%"PRIu32" MHz", + s_test_max_freqs[i], s_test_min_freqs[i]); + test_interrupt_during_encrypt_common(s_test_max_freqs[i], s_test_min_freqs[i]); + } +} + +/** + * Helper function: Test interrupt with PM lock acquire/release with PM configuration + * @param lock_type Lock type (CPU or APB) + * @param lock_name Lock name for logging + * @param max_freq_mhz Maximum CPU frequency in MHz. If 0, configure with CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ. + * @param min_freq_mhz Minimum CPU frequency in MHz. If max_freq_mhz is 0, use XTAL frequency as min. + */ +static void test_interrupt_with_pm_lock_acquire_release_common(esp_pm_lock_type_t lock_type, const char* lock_name, uint32_t max_freq_mhz, uint32_t min_freq_mhz) +{ + esp_flash_t* chip = get_test_flash_chip(); + +#if CONFIG_PM_ENABLE + setup_pm_config(max_freq_mhz, min_freq_mhz); +#else + TEST_ASSERT_EQUAL_UINT32(CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ, get_cpu_freq_mhz()); +#endif + + uint32_t original_freq = get_cpu_freq_mhz(); + prepare_test_data(); + + // Create PM lock + esp_pm_lock_handle_t pm_lock = NULL; + TEST_ESP_OK(esp_pm_lock_create(lock_type, 0, "isr_test", &pm_lock)); + TEST_ASSERT_NOT_NULL(pm_lock); + + // Set lock handle for ISR callback + if (lock_type == ESP_PM_CPU_FREQ_MAX) { + s_isr_pm_lock_cpu = pm_lock; + s_isr_pm_lock_apb = NULL; + } else { + s_isr_pm_lock_cpu = NULL; + s_isr_pm_lock_apb = pm_lock; + } + + // Setup interrupt to trigger during encrypt (50us delay) + // ISR will acquire and release lock + setup_interrupt_timer(50, timer_isr_callback_acquire_release); + + // Perform encrypt operation - interrupt will trigger PM lock acquire/release in ISR + esp_err_t ret = esp_flash_write_encrypted(chip, s_test_flash_offset, test_write_buf, TEST_DATA_SIZE); + TEST_ESP_OK(ret); + + // Wait for interrupt to be processed + vTaskDelay(pdMS_TO_TICKS(10)); + + // Verify interrupt occurred and lock operations happened + TEST_ASSERT_TRUE(s_interrupt_occurred); + TEST_ASSERT_TRUE(s_isr_lock_acquired); + TEST_ASSERT_TRUE(s_isr_lock_released); + + // Verify frequency was restored (pm_impl.c assertions will verify frequency during encrypt) + verify_freq_restored(original_freq); + + // Cleanup + s_isr_pm_lock_cpu = NULL; + s_isr_pm_lock_apb = NULL; + esp_pm_lock_delete(pm_lock); + cleanup_interrupt_timer(); +} + +/** + * Test: Frequency limit: interrupt with CPU lock acquire/release + * Target: Same as test_interrupt_with_pm_lock_acquire_release, but specifically for CPU lock (ESP_PM_CPU_FREQ_MAX). + */ +TEST_CASE("Frequency limit: interrupt with CPU lock acquire/release", "[esp_flash_freq_limit]") +{ + for (int i = 0; i < NUM_TEST_CONFIGS; i++) { + ESP_LOGI(TAG, "Testing CPU lock with PM config: max=%"PRIu32" MHz, min=%"PRIu32" MHz", + s_test_max_freqs[i], s_test_min_freqs[i]); + test_interrupt_with_pm_lock_acquire_release_common(ESP_PM_CPU_FREQ_MAX, "CPU", s_test_max_freqs[i], s_test_min_freqs[i]); + } +} + +/** + * Test: Frequency limit: interrupt with APB lock acquire/release + * Target: Same as test_interrupt_with_pm_lock_acquire_release, but specifically for APB lock (ESP_PM_APB_FREQ_MAX). + */ +TEST_CASE("Frequency limit: interrupt with APB lock acquire/release", "[esp_flash_freq_limit]") +{ + for (int i = 0; i < NUM_TEST_CONFIGS; i++) { + ESP_LOGI(TAG, "Testing APB lock with PM config: max=%"PRIu32" MHz, min=%"PRIu32" MHz", + s_test_max_freqs[i], s_test_min_freqs[i]); + test_interrupt_with_pm_lock_acquire_release_common(ESP_PM_APB_FREQ_MAX, "APB", s_test_max_freqs[i], s_test_min_freqs[i]); + } +} + +/** + * Helper function: Test interrupt with PM lock held after ISR with PM configuration + * @param lock_type Lock type (CPU or APB) + * @param lock_name Lock name for logging + * @param max_freq_mhz Maximum CPU frequency in MHz. If 0, configure with CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ. + * @param min_freq_mhz Minimum CPU frequency in MHz. If max_freq_mhz is 0, use XTAL frequency as min. + */ +static void test_interrupt_with_pm_lock_held_common(esp_pm_lock_type_t lock_type, const char* lock_name, uint32_t max_freq_mhz, uint32_t min_freq_mhz) +{ + esp_flash_t* chip = get_test_flash_chip(); + +#if CONFIG_PM_ENABLE + uint32_t configured_max_freq = setup_pm_config(max_freq_mhz, min_freq_mhz); +#else + TEST_ASSERT_EQUAL_UINT32(CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ, get_cpu_freq_mhz()); + uint32_t configured_max_freq = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ; +#endif + + uint32_t original_freq = get_cpu_freq_mhz(); + prepare_test_data(); + + // Create PM lock + esp_pm_lock_handle_t pm_lock = NULL; + TEST_ESP_OK(esp_pm_lock_create(lock_type, 0, "hold_test", &pm_lock)); + TEST_ASSERT_NOT_NULL(pm_lock); + + // Set lock handle for ISR callback + if (lock_type == ESP_PM_CPU_FREQ_MAX) { + s_isr_pm_lock_cpu = pm_lock; + s_isr_pm_lock_apb = NULL; + } else { + s_isr_pm_lock_cpu = NULL; + s_isr_pm_lock_apb = pm_lock; + } + + // Setup interrupt to trigger during encrypt + // ISR will acquire lock, we'll release it after write completes + setup_interrupt_timer(50, timer_isr_callback_acquire); + + // Perform encrypt operation - interrupt will trigger PM lock acquire in ISR + esp_err_t ret = esp_flash_write_encrypted(chip, s_test_flash_offset, test_write_buf, TEST_DATA_SIZE); + TEST_ESP_OK(ret); + + // Wait for interrupt to be processed + vTaskDelay(pdMS_TO_TICKS(10)); + + // Verify interrupt occurred and lock was acquired + TEST_ASSERT_TRUE(s_interrupt_occurred); + TEST_ASSERT_TRUE(s_isr_lock_acquired); + + // Check frequency after write completes but before releasing lock + // Note: PM_MODE_CPU_ENCRYPT_LOCK lock (frequency limit) is released when write_encrypted returns, + // so frequency should be restored to the mode corresponding to the lock held in ISR + // If ISR acquired ESP_PM_CPU_FREQ_MAX lock, frequency should be configured max freq (PM_MODE_CPU_MAX) + // If ISR acquired ESP_PM_APB_FREQ_MAX lock, frequency depends on PM configuration + uint32_t freq_after_write = get_cpu_freq_mhz(); + uint32_t expected_freq; + if (lock_type == ESP_PM_CPU_FREQ_MAX) { + // CPU lock held -> PM_MODE_CPU_MAX -> configured max frequency + expected_freq = configured_max_freq; + } else { + // APB lock held -> depends on PM config, but typically >= 80MHz + // For simplicity, just verify frequency is restored (not limited to 160MHz) + expected_freq = configured_max_freq; // APB lock held, should be restored to configured max freq + } + ESP_LOGI(TAG, "Frequency after write (lock still held): %"PRIu32" MHz (expected: %"PRIu32" MHz)", + freq_after_write, expected_freq); + TEST_ASSERT_EQUAL_UINT32(expected_freq, freq_after_write); + + // Release lock after write completes (as per requirement) + esp_pm_lock_release(pm_lock); + + // Verify frequency was restored after lock release + verify_freq_restored(original_freq); + + // Cleanup + s_isr_pm_lock_cpu = NULL; + s_isr_pm_lock_apb = NULL; + esp_pm_lock_delete(pm_lock); + cleanup_interrupt_timer(); +} + +/** + * Test: Frequency limit: interrupt with CPU lock held after ISR + * Target: Same as test_interrupt_with_pm_lock_held, but specifically for CPU lock (ESP_PM_CPU_FREQ_MAX). + */ +TEST_CASE("Frequency limit: interrupt with CPU lock held after ISR", "[esp_flash_freq_limit]") +{ + for (int i = 0; i < NUM_TEST_CONFIGS; i++) { + ESP_LOGI(TAG, "Testing CPU lock with PM config: max=%"PRIu32" MHz, min=%"PRIu32" MHz", + s_test_max_freqs[i], s_test_min_freqs[i]); + test_interrupt_with_pm_lock_held_common(ESP_PM_CPU_FREQ_MAX, "CPU", s_test_max_freqs[i], s_test_min_freqs[i]); + } +} + +/** + * Test: Frequency limit: interrupt with APB lock held after ISR + * Target: Same as test_interrupt_with_pm_lock_held, but specifically for APB lock (ESP_PM_APB_FREQ_MAX). + */ +TEST_CASE("Frequency limit: interrupt with APB lock held after ISR", "[esp_flash_freq_limit]") +{ + for (int i = 0; i < NUM_TEST_CONFIGS; i++) { + ESP_LOGI(TAG, "Testing APB lock with PM config: max=%"PRIu32" MHz, min=%"PRIu32" MHz", + s_test_max_freqs[i], s_test_min_freqs[i]); + test_interrupt_with_pm_lock_held_common(ESP_PM_APB_FREQ_MAX, "APB", s_test_max_freqs[i], s_test_min_freqs[i]); + } +} + +/** + * Helper function: Test PM lock released in ISR with PM configuration + * @param lock_type Lock type (CPU or APB) + * @param lock_name Lock name for logging + * @param max_freq_mhz Maximum CPU frequency in MHz. If 0, configure with CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ. + * @param min_freq_mhz Minimum CPU frequency in MHz. If max_freq_mhz is 0, use XTAL frequency as min. + */ +static void test_lock_released_in_isr_common(esp_pm_lock_type_t lock_type, const char* lock_name, uint32_t max_freq_mhz, uint32_t min_freq_mhz) +{ + esp_flash_t* chip = get_test_flash_chip(); + +#if CONFIG_PM_ENABLE + setup_pm_config(max_freq_mhz, min_freq_mhz); +#else + TEST_ASSERT_EQUAL_UINT32(CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ, get_cpu_freq_mhz()); +#endif + + uint32_t original_freq = get_cpu_freq_mhz(); + prepare_test_data(); + + // Create and acquire PM lock BEFORE encrypt + esp_pm_lock_handle_t pm_lock = NULL; + TEST_ESP_OK(esp_pm_lock_create(lock_type, 0, "release_test", &pm_lock)); + TEST_ASSERT_NOT_NULL(pm_lock); + TEST_ESP_OK(esp_pm_lock_acquire(pm_lock)); + + // Set lock handle for ISR callback + if (lock_type == ESP_PM_CPU_FREQ_MAX) { + s_isr_pm_lock_cpu = pm_lock; + s_isr_pm_lock_apb = NULL; + } else { + s_isr_pm_lock_cpu = NULL; + s_isr_pm_lock_apb = pm_lock; + } + + // Setup interrupt to trigger during encrypt + // ISR will release the lock that was acquired before encrypt + setup_interrupt_timer(50, timer_isr_callback_release); + + // Perform encrypt operation - lock is already held, ISR will release it + esp_err_t ret = esp_flash_write_encrypted(chip, s_test_flash_offset, test_write_buf, TEST_DATA_SIZE); + TEST_ESP_OK(ret); + + // Wait for interrupt to be processed + vTaskDelay(pdMS_TO_TICKS(10)); + + // Verify interrupt occurred and lock was released + TEST_ASSERT_TRUE(s_interrupt_occurred); + TEST_ASSERT_TRUE(s_isr_lock_released); + + // Check frequency after write completes and after ISR released lock + // Frequency should be restored because lock was released in ISR + uint32_t freq_after_write = get_cpu_freq_mhz(); + ESP_LOGI(TAG, "Frequency after write (lock released in ISR): %"PRIu32" MHz (original: %"PRIu32" MHz)", + freq_after_write, original_freq); + TEST_ASSERT_EQUAL_UINT32(original_freq, freq_after_write); + + // Verify frequency was restored (pm_impl.c assertions will verify frequency during encrypt) + verify_freq_restored(original_freq); + + // Cleanup + s_isr_pm_lock_cpu = NULL; + s_isr_pm_lock_apb = NULL; + esp_pm_lock_delete(pm_lock); + cleanup_interrupt_timer(); +} + +TEST_CASE("Frequency limit: CPU lock released in ISR", "[esp_flash_freq_limit]") +{ + for (int i = 0; i < NUM_TEST_CONFIGS; i++) { + ESP_LOGI(TAG, "Testing CPU lock with PM config: max=%"PRIu32" MHz, min=%"PRIu32" MHz", + s_test_max_freqs[i], s_test_min_freqs[i]); + test_lock_released_in_isr_common(ESP_PM_CPU_FREQ_MAX, "CPU", s_test_max_freqs[i], s_test_min_freqs[i]); + } +} + +TEST_CASE("Frequency limit: APB lock released in ISR", "[esp_flash_freq_limit]") +{ + for (int i = 0; i < NUM_TEST_CONFIGS; i++) { + ESP_LOGI(TAG, "Testing APB lock with PM config: max=%"PRIu32" MHz, min=%"PRIu32" MHz", + s_test_max_freqs[i], s_test_min_freqs[i]); + test_lock_released_in_isr_common(ESP_PM_APB_FREQ_MAX, "APB", s_test_max_freqs[i], s_test_min_freqs[i]); + } +} +#endif // CONFIG_PM_ENABLE + + +#endif // CONFIG_IDF_TARGET_ESP32C5 diff --git a/components/spi_flash/test_apps/esp_flash_freq_limit/partitions.csv b/components/spi_flash/test_apps/esp_flash_freq_limit/partitions.csv new file mode 100644 index 000000000000..9903f621fb7e --- /dev/null +++ b/components/spi_flash/test_apps/esp_flash_freq_limit/partitions.csv @@ -0,0 +1,5 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap +nvs, data, nvs, 0x9000, 0x6000, +factory, 0, 0, 0x10000, 1M +flash_test, data, fat, , 700K diff --git a/components/spi_flash/test_apps/esp_flash_freq_limit/pytest_esp_flash_freq_limit.py b/components/spi_flash/test_apps/esp_flash_freq_limit/pytest_esp_flash_freq_limit.py new file mode 100644 index 000000000000..aa30ef27b8ab --- /dev/null +++ b/components/spi_flash/test_apps/esp_flash_freq_limit/pytest_esp_flash_freq_limit.py @@ -0,0 +1,24 @@ +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +import pytest +from pytest_embedded import Dut +from pytest_embedded_idf.utils import idf_parametrize + + +@pytest.mark.generic +@pytest.mark.parametrize( + 'config', + [ + 'pm_disabled_240mhz', + 'pm_disabled_160mhz', + 'pm_enabled_240mhz', + 'pm_enabled_160mhz', + 'pm_disabled_240mhz_xip_psram', + 'pm_enabled_240mhz_xip_psram', + 'pm_enabled_240mhz_xip_psram_rom_impl', + ], + indirect=True, +) +@idf_parametrize('target', ['esp32c5'], indirect=['target']) +def test_esp_flash_freq_limit(dut: Dut) -> None: + dut.run_all_single_board_cases(group='esp_flash_freq_limit', timeout=10) diff --git a/components/spi_flash/test_apps/esp_flash_freq_limit/sdkconfig.ci.pm_disabled_160mhz b/components/spi_flash/test_apps/esp_flash_freq_limit/sdkconfig.ci.pm_disabled_160mhz new file mode 100644 index 000000000000..0314f5699b09 --- /dev/null +++ b/components/spi_flash/test_apps/esp_flash_freq_limit/sdkconfig.ci.pm_disabled_160mhz @@ -0,0 +1,2 @@ +CONFIG_PM_ENABLE=n +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160=y diff --git a/components/spi_flash/test_apps/esp_flash_freq_limit/sdkconfig.ci.pm_disabled_240mhz b/components/spi_flash/test_apps/esp_flash_freq_limit/sdkconfig.ci.pm_disabled_240mhz new file mode 100644 index 000000000000..478b872a3ad7 --- /dev/null +++ b/components/spi_flash/test_apps/esp_flash_freq_limit/sdkconfig.ci.pm_disabled_240mhz @@ -0,0 +1,2 @@ +CONFIG_PM_ENABLE=n +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y diff --git a/components/spi_flash/test_apps/esp_flash_freq_limit/sdkconfig.ci.pm_disabled_240mhz_xip_psram b/components/spi_flash/test_apps/esp_flash_freq_limit/sdkconfig.ci.pm_disabled_240mhz_xip_psram new file mode 100644 index 000000000000..1173f62ef05d --- /dev/null +++ b/components/spi_flash/test_apps/esp_flash_freq_limit/sdkconfig.ci.pm_disabled_240mhz_xip_psram @@ -0,0 +1,8 @@ +CONFIG_PM_ENABLE=n +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_BOOT_HW_INIT=y +CONFIG_SPIRAM_BOOT_INIT=y +CONFIG_SPIRAM_XIP_FROM_PSRAM=y +CONFIG_SPIRAM_SPEED_80M=y diff --git a/components/spi_flash/test_apps/esp_flash_freq_limit/sdkconfig.ci.pm_enabled_160mhz b/components/spi_flash/test_apps/esp_flash_freq_limit/sdkconfig.ci.pm_enabled_160mhz new file mode 100644 index 000000000000..48f203634409 --- /dev/null +++ b/components/spi_flash/test_apps/esp_flash_freq_limit/sdkconfig.ci.pm_enabled_160mhz @@ -0,0 +1,6 @@ +CONFIG_FREERTOS_USE_TICKLESS_IDLE=y +CONFIG_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y +CONFIG_PM_ENABLE=y +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160=y diff --git a/components/spi_flash/test_apps/esp_flash_freq_limit/sdkconfig.ci.pm_enabled_240mhz b/components/spi_flash/test_apps/esp_flash_freq_limit/sdkconfig.ci.pm_enabled_240mhz new file mode 100644 index 000000000000..e1433523a3fa --- /dev/null +++ b/components/spi_flash/test_apps/esp_flash_freq_limit/sdkconfig.ci.pm_enabled_240mhz @@ -0,0 +1,6 @@ +CONFIG_FREERTOS_USE_TICKLESS_IDLE=y +CONFIG_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y +CONFIG_PM_ENABLE=y +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y diff --git a/components/spi_flash/test_apps/esp_flash_freq_limit/sdkconfig.ci.pm_enabled_240mhz_xip_psram b/components/spi_flash/test_apps/esp_flash_freq_limit/sdkconfig.ci.pm_enabled_240mhz_xip_psram new file mode 100644 index 000000000000..65c01ff2fa34 --- /dev/null +++ b/components/spi_flash/test_apps/esp_flash_freq_limit/sdkconfig.ci.pm_enabled_240mhz_xip_psram @@ -0,0 +1,12 @@ +CONFIG_FREERTOS_USE_TICKLESS_IDLE=y +CONFIG_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y +CONFIG_PM_ENABLE=y +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_BOOT_HW_INIT=y +CONFIG_SPIRAM_BOOT_INIT=y +CONFIG_SPIRAM_XIP_FROM_PSRAM=y +CONFIG_SPIRAM_SPEED_80M=y diff --git a/components/spi_flash/test_apps/esp_flash_freq_limit/sdkconfig.ci.pm_enabled_240mhz_xip_psram_rom_impl b/components/spi_flash/test_apps/esp_flash_freq_limit/sdkconfig.ci.pm_enabled_240mhz_xip_psram_rom_impl new file mode 100644 index 000000000000..c70fcf47418b --- /dev/null +++ b/components/spi_flash/test_apps/esp_flash_freq_limit/sdkconfig.ci.pm_enabled_240mhz_xip_psram_rom_impl @@ -0,0 +1,13 @@ +CONFIG_FREERTOS_USE_TICKLESS_IDLE=y +CONFIG_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y +CONFIG_PM_ENABLE=y +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_SPIRAM=y +CONFIG_SPIRAM_BOOT_HW_INIT=y +CONFIG_SPIRAM_BOOT_INIT=y +CONFIG_SPIRAM_XIP_FROM_PSRAM=y +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_SPI_FLASH_ROM_IMPL=y diff --git a/components/spi_flash/test_apps/esp_flash_freq_limit/sdkconfig.defaults b/components/spi_flash/test_apps/esp_flash_freq_limit/sdkconfig.defaults new file mode 100644 index 000000000000..91d7574d37e5 --- /dev/null +++ b/components/spi_flash/test_apps/esp_flash_freq_limit/sdkconfig.defaults @@ -0,0 +1,2 @@ +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" diff --git a/docs/en/api-reference/peripherals/parlio/parlio_tx.rst b/docs/en/api-reference/peripherals/parlio/parlio_tx.rst index 716dad3b4be9..3567f67d8c76 100644 --- a/docs/en/api-reference/peripherals/parlio/parlio_tx.rst +++ b/docs/en/api-reference/peripherals/parlio/parlio_tx.rst @@ -325,7 +325,7 @@ The waveform of the external clock input is shown below: .. note:: - Due to hardware limitations, the bitstream generated by the BitScrambler cannot change the length compared to the original bitstream, otherwise transmission blocking or data loss may occur. + Due to hardware limitations, on chip versions prior to rev3.0, the bitstream generated by the bitscrambler must have the same length as the original bitstream, otherwise transmission blocking or data loss may occur. :cpp:func:`parlio_tx_unit_decorate_bitscrambler` and :cpp:func:`parlio_tx_unit_undecorate_bitscrambler` need to be used in pairs. When deleting the TX unit, you need to call :cpp:func:`parlio_tx_unit_undecorate_bitscrambler` first to remove the BitScrambler. diff --git a/docs/en/migration-guides/release-5.x/5.5/peripherals.rst b/docs/en/migration-guides/release-5.x/5.5/peripherals.rst index d7348c3970d5..531cb83139ec 100644 --- a/docs/en/migration-guides/release-5.x/5.5/peripherals.rst +++ b/docs/en/migration-guides/release-5.x/5.5/peripherals.rst @@ -59,3 +59,8 @@ Peripherals - The new and legacy drivers are not compatible and must not be used together. Mixing them will trigger warnings during startup, and may even cause crashes and system reboots. To suppress this compatibility check, you may enable the configuration option :ref:`CONFIG_TWAI_SKIP_LEGACY_CONFLICT_CHECK`. - The legacy driver will no longer receive new features, such as TWAI FD (Flexible Data-rate) support. + +SPI Flash Driver +---------------- + +- New argument ``flags`` is added to ``esp_flash_os_functions_t::start``. Caller and implementer should handle this argument properly. diff --git a/docs/zh_CN/api-reference/peripherals/parlio/parlio_tx.rst b/docs/zh_CN/api-reference/peripherals/parlio/parlio_tx.rst index f7bae42759a3..b3c7a765107d 100644 --- a/docs/zh_CN/api-reference/peripherals/parlio/parlio_tx.rst +++ b/docs/zh_CN/api-reference/peripherals/parlio/parlio_tx.rst @@ -325,7 +325,7 @@ TX 单元可以选择各种不同的时钟源,其中外部时钟源较为特 .. note:: - 由于硬件限制,使用比特调节器生成的比特流与原本比特流相比,长度不能发生变化,否则可能会发生传输阻塞或数据丢失。 + 由于硬件限制,在 rev3.0 之前的芯片版本上,使用比特调节器生成的比特流与原本比特流相比,长度不能发生变化,否则可能会发生传输阻塞或数据丢失。 :cpp:func:`parlio_tx_unit_decorate_bitscrambler` 和 :cpp:func:`parlio_tx_unit_undecorate_bitscrambler` 需要成对使用。在删除 TX 单元时,需要先调用 :cpp:func:`parlio_tx_unit_undecorate_bitscrambler` 移除比特调节器。 diff --git a/docs/zh_CN/migration-guides/release-5.x/5.5/peripherals.rst b/docs/zh_CN/migration-guides/release-5.x/5.5/peripherals.rst index bd457e8c4c62..7ebb9ef63a7a 100644 --- a/docs/zh_CN/migration-guides/release-5.x/5.5/peripherals.rst +++ b/docs/zh_CN/migration-guides/release-5.x/5.5/peripherals.rst @@ -59,3 +59,8 @@ - 新旧驱动不互相兼容,不可混合使用,否则将在启动时打印警告,甚至崩溃并重启。可打开配置项 :ref:`CONFIG_TWAI_SKIP_LEGACY_CONFLICT_CHECK` 以屏蔽该检查。 - 旧版驱动将不再添加新特性(如 TWAIFD)的支持。 + +SPI flash 驱动 +-------------- + +- ``esp_flash_os_functions_t::start`` 新增了一个参数 ``flags``。调用者和实现者应正确处理此参数。 diff --git a/examples/bluetooth/bluedroid/ble/ble_ancs/main/ble_ancs.c b/examples/bluetooth/bluedroid/ble/ble_ancs/main/ble_ancs.c index 379d1ceda5b3..813a677b51db 100644 --- a/examples/bluetooth/bluedroid/ble/ble_ancs/main/ble_ancs.c +++ b/examples/bluetooth/bluedroid/ble/ble_ancs/main/ble_ancs.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -126,14 +126,24 @@ void esp_receive_apple_data_source(uint8_t *message, uint16_t message_len) switch (Command_id) { case CommandIDGetNotificationAttributes: { + // Security fix: Check minimum message length before accessing message[1..4] + if (message_len < 5) { + ESP_LOGE(BLE_ANCS_TAG, "Message too short for NotificationAttributes"); + break; + } uint32_t NotificationUID = (message[1]) | (message[2]<< 8) | (message[3]<< 16) | (message[4] << 24); uint32_t remian_attr_len = message_len - 5; uint8_t *attrs = &message[5]; ESP_LOGI(BLE_ANCS_TAG, "recevice Notification Attributes response Command_id %d NotificationUID %" PRIu32, Command_id, NotificationUID); while(remian_attr_len > 0) { + // Security fix: Need at least 3 bytes for AttributeID(1) + len(2) + if (remian_attr_len < 3) { + ESP_LOGE(BLE_ANCS_TAG, "Incomplete attribute header"); + break; + } uint8_t AttributeID = attrs[0]; uint16_t len = attrs[1] | (attrs[2] << 8); - if(len > (remian_attr_len -3)) { + if(len > (remian_attr_len - 3)) { ESP_LOGE(BLE_ANCS_TAG, "data error"); break; } diff --git a/examples/bluetooth/bluedroid/ble/ble_ancs/main/ble_ancs_demo.c b/examples/bluetooth/bluedroid/ble/ble_ancs/main/ble_ancs_demo.c index 04994799e875..4dfb0bd4b180 100644 --- a/examples/bluetooth/bluedroid/ble/ble_ancs/main/ble_ancs_demo.c +++ b/examples/bluetooth/bluedroid/ble/ble_ancs/main/ble_ancs_demo.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -31,6 +31,8 @@ #define ADV_CONFIG_FLAG (1 << 0) #define SCAN_RSP_CONFIG_FLAG (1 << 1) #define INVALID_HANDLE 0 +#define ANCS_CMD_BUFFER_MAX_SIZE 600 + static uint8_t adv_config_done = 0; static bool get_service = false; static esp_gattc_char_elem_t *char_elem_result = NULL; @@ -168,15 +170,26 @@ esp_noti_attr_list_t p_attr[8] = { void esp_get_notification_attributes(uint8_t *notificationUID, uint8_t num_attr, esp_noti_attr_list_t *p_attr) { - uint8_t cmd[600] = {0}; + uint8_t cmd[ANCS_CMD_BUFFER_MAX_SIZE] = {0}; uint32_t index = 0; + cmd[0] = CommandIDGetNotificationAttributes; index ++; memcpy(&cmd[index], notificationUID, ESP_NOTIFICATIONUID_LEN); index += ESP_NOTIFICATIONUID_LEN; while(num_attr > 0) { + // Security fix: Check buffer boundary before writing + if (index >= ANCS_CMD_BUFFER_MAX_SIZE) { + ESP_LOGE(BLE_ANCS_TAG, "Command buffer overflow in get_notification_attributes"); + return; + } cmd[index ++] = p_attr->noti_attribute_id; if (p_attr->attribute_len > 0) { + // Need 2 more bytes for attribute_len + if ((index + 2) > ANCS_CMD_BUFFER_MAX_SIZE) { + ESP_LOGE(BLE_ANCS_TAG, "Command buffer overflow in get_notification_attributes"); + return; + } cmd[index ++] = p_attr->attribute_len; cmd[index ++] = (p_attr->attribute_len << 8); } @@ -195,8 +208,15 @@ void esp_get_notification_attributes(uint8_t *notificationUID, uint8_t num_attr, void esp_get_app_attributes(uint8_t *appidentifier, uint16_t appidentifier_len, uint8_t num_attr, uint8_t *p_app_attrs) { - uint8_t buffer[600] = {0}; + uint8_t buffer[ANCS_CMD_BUFFER_MAX_SIZE] = {0}; uint32_t index = 0; + + // Security fix: Check buffer boundary before memcpy + if ((1 + appidentifier_len + num_attr) > ANCS_CMD_BUFFER_MAX_SIZE) { + ESP_LOGE(BLE_ANCS_TAG, "Buffer overflow in get_app_attributes"); + return; + } + buffer[0] = CommandIDGetAppAttributes; index ++; memcpy(&buffer[index], appidentifier, appidentifier_len); @@ -215,7 +235,7 @@ void esp_get_app_attributes(uint8_t *appidentifier, uint16_t appidentifier_len, void esp_perform_notification_action(uint8_t *notificationUID, uint8_t ActionID) { - uint8_t buffer[600] = {0}; + uint8_t buffer[ANCS_CMD_BUFFER_MAX_SIZE] = {0}; uint32_t index = 0; buffer[0] = CommandIDPerformNotificationAction; index ++; @@ -517,6 +537,12 @@ static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ esp_get_notification_attributes(notificationUID, sizeof(p_attr)/sizeof(esp_noti_attr_list_t), p_attr); } } else if (param->notify.handle == gl_profile_tab[PROFILE_A_APP_ID].data_source_handle) { + if ((data_buffer.len + param->notify.value_len) > sizeof(data_buffer.buffer)) { + ESP_LOGE(BLE_ANCS_TAG, "Data source buffer overflow detected, discarding data"); + memset(data_buffer.buffer, 0, sizeof(data_buffer.buffer)); + data_buffer.len = 0; + break; + } memcpy(&data_buffer.buffer[data_buffer.len], param->notify.value, param->notify.value_len); data_buffer.len += param->notify.value_len; if (param->notify.value_len == (gl_profile_tab[PROFILE_A_APP_ID].MTU_size - 3)) { diff --git a/examples/bluetooth/bluedroid/ble/ble_eddystone_receiver/main/esp_eddystone_api.c b/examples/bluetooth/bluedroid/ble/ble_eddystone_receiver/main/esp_eddystone_api.c index 2832116506b5..e3051316e98a 100644 --- a/examples/bluetooth/bluedroid/ble/ble_eddystone_receiver/main/esp_eddystone_api.c +++ b/examples/bluetooth/bluedroid/ble/ble_eddystone_receiver/main/esp_eddystone_api.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -21,6 +21,7 @@ #include "esp_eddystone_protocol.h" #include "esp_eddystone_api.h" +#define EDDYSTONE_URL_BUF_SIZE 100 /* Declare static functions */ static esp_err_t esp_eddystone_uid_received(const uint8_t* buf, uint8_t len, esp_eddystone_result_t* res); @@ -101,18 +102,33 @@ static esp_err_t esp_eddystone_uid_received(const uint8_t* buf, uint8_t len, esp static char* esp_eddystone_resolve_url_scheme(const uint8_t *url_start, const uint8_t *url_end) { int pos = 0; - static char url_buf[100] = {0}; + static char url_buf[EDDYSTONE_URL_BUF_SIZE] = {0}; const uint8_t *p = url_start; + int written; - pos += sprintf(&url_buf[pos], "%s", eddystone_url_prefix[*p++]); + // Security fix: Use snprintf instead of sprintf to prevent buffer overflow + written = snprintf(&url_buf[pos], EDDYSTONE_URL_BUF_SIZE - pos, "%s", eddystone_url_prefix[*p++]); + if (written < 0 || written >= (EDDYSTONE_URL_BUF_SIZE - pos)) { + url_buf[EDDYSTONE_URL_BUF_SIZE - 1] = '\0'; + return url_buf; + } + pos += written; for (; p <= url_end; p++) { + if (pos >= EDDYSTONE_URL_BUF_SIZE - 1) { + break; + } if (esp_eddystone_is_char_invalid((*p))) { - pos += sprintf(&url_buf[pos], "%s", eddystone_url_encoding[*p]); + written = snprintf(&url_buf[pos], EDDYSTONE_URL_BUF_SIZE - pos, "%s", eddystone_url_encoding[*p]); } else { - pos += sprintf(&url_buf[pos], "%c", *p); + written = snprintf(&url_buf[pos], EDDYSTONE_URL_BUF_SIZE - pos, "%c", *p); + } + if (written < 0 || written >= (EDDYSTONE_URL_BUF_SIZE - pos)) { + break; } + pos += written; } + url_buf[EDDYSTONE_URL_BUF_SIZE - 1] = '\0'; return url_buf; } diff --git a/examples/bluetooth/bluedroid/ble/ble_spp_server/main/ble_spp_server_demo.c b/examples/bluetooth/bluedroid/ble/ble_spp_server/main/ble_spp_server_demo.c index d2796402cf1e..8e4351231e93 100644 --- a/examples/bluetooth/bluedroid/ble/ble_spp_server/main/ble_spp_server_demo.c +++ b/examples/bluetooth/bluedroid/ble/ble_spp_server/main/ble_spp_server_demo.c @@ -277,20 +277,25 @@ static bool store_wr_buffer(esp_ble_gatts_cb_param_t *p_data) ESP_LOGI(GATTS_TABLE_TAG, "malloc error %s %d", __func__, __LINE__); return false; } - if(temp_spp_recv_data_node_p2 != NULL){ - temp_spp_recv_data_node_p2->next_node = temp_spp_recv_data_node_p1; - } + temp_spp_recv_data_node_p1->len = p_data->write.len; - SppRecvDataBuff.buff_size += p_data->write.len; temp_spp_recv_data_node_p1->next_node = NULL; temp_spp_recv_data_node_p1->node_buff = (uint8_t *)malloc(p_data->write.len); - temp_spp_recv_data_node_p2 = temp_spp_recv_data_node_p1; if (temp_spp_recv_data_node_p1->node_buff == NULL) { ESP_LOGI(GATTS_TABLE_TAG, "malloc error %s %d\n", __func__, __LINE__); - temp_spp_recv_data_node_p1->len = 0; - } else { - memcpy(temp_spp_recv_data_node_p1->node_buff,p_data->write.value,p_data->write.len); + // Security fix: Free the node and return false to prevent memory leak + free(temp_spp_recv_data_node_p1); + temp_spp_recv_data_node_p1 = NULL; + return false; } + memcpy(temp_spp_recv_data_node_p1->node_buff, p_data->write.value, p_data->write.len); + + // Security fix: Link to list only after successful allocation + if(temp_spp_recv_data_node_p2 != NULL){ + temp_spp_recv_data_node_p2->next_node = temp_spp_recv_data_node_p1; + } + temp_spp_recv_data_node_p2 = temp_spp_recv_data_node_p1; + SppRecvDataBuff.buff_size += p_data->write.len; if(SppRecvDataBuff.node_num == 0){ SppRecvDataBuff.first_node = temp_spp_recv_data_node_p1; diff --git a/examples/bluetooth/bluedroid/ble/ble_spp_server/main/ble_spp_server_demo.h b/examples/bluetooth/bluedroid/ble/ble_spp_server/main/ble_spp_server_demo.h index 9ca806588993..91829ea5dfc6 100644 --- a/examples/bluetooth/bluedroid/ble/ble_spp_server/main/ble_spp_server_demo.h +++ b/examples/bluetooth/bluedroid/ble/ble_spp_server/main/ble_spp_server_demo.h @@ -16,7 +16,6 @@ //#define SUPPORT_HEARTBEAT //#define SPP_DEBUG_MODE -#define spp_sprintf(s,...) sprintf((char*)(s), ##__VA_ARGS__) #define SPP_DATA_MAX_LEN (512) #define SPP_CMD_MAX_LEN (20) #define SPP_STATUS_MAX_LEN (20) diff --git a/examples/bluetooth/bluedroid/ble/gatt_server/main/gatts_demo.c b/examples/bluetooth/bluedroid/ble/gatt_server/main/gatts_demo.c index a6d8912fffaf..a8b8254bc40f 100644 --- a/examples/bluetooth/bluedroid/ble/gatt_server/main/gatts_demo.c +++ b/examples/bluetooth/bluedroid/ble/gatt_server/main/gatts_demo.c @@ -282,7 +282,8 @@ void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare } } - esp_gatt_rsp_t *gatt_rsp = (esp_gatt_rsp_t *)malloc(sizeof(esp_gatt_rsp_t)); + // Security fix: Use calloc to ensure memory is zero-initialized + esp_gatt_rsp_t *gatt_rsp = (esp_gatt_rsp_t *)calloc(1, sizeof(esp_gatt_rsp_t)); if (gatt_rsp) { gatt_rsp->attr_value.len = param->write.len; gatt_rsp->attr_value.handle = param->write.handle; diff --git a/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/main.c b/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/main.c index e4456ea16539..12e8bdd8b114 100644 --- a/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/main.c +++ b/examples/bluetooth/bluedroid/coex/a2dp_gatts_coex/main/main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ @@ -131,8 +131,24 @@ static struct gatts_profile_inst gl_profile_tab[PROFILE_NUM] = { }; static void ble_init_adv_data(const char *name) { - int len = strlen(name); - uint8_t raw_adv_data[len+5]; + if (name == NULL) { + ESP_LOGE(BT_BLE_COEX_TAG, "ble_init_adv_data: name is NULL"); + return; + } + + size_t len = strlen(name); + // ADV data max is 31 bytes; overhead is 5 bytes (Flags: 3, Name header: 2) + #define ADV_DATA_MAX_LEN 31 + #define ADV_DATA_OVERHEAD 5 + + if (len > (ADV_DATA_MAX_LEN - ADV_DATA_OVERHEAD)) { + ESP_LOGW(BT_BLE_COEX_TAG, "ADV name too long (%d), truncating to %d", (int)len, ADV_DATA_MAX_LEN - ADV_DATA_OVERHEAD); + len = ADV_DATA_MAX_LEN - ADV_DATA_OVERHEAD; + } + + uint8_t raw_adv_data[ADV_DATA_MAX_LEN]; + size_t adv_data_len = len + ADV_DATA_OVERHEAD; + //flag raw_adv_data[0] = 2; raw_adv_data[1] = ESP_BT_EIR_TYPE_FLAGS; @@ -140,16 +156,14 @@ static void ble_init_adv_data(const char *name) //adv name raw_adv_data[3] = len + 1; raw_adv_data[4] = ESP_BLE_AD_TYPE_NAME_CMPL; - for (int i = 0;i < len;i++) - { - raw_adv_data[i+5] = *(name++); - } + memcpy(&raw_adv_data[5], name, len); + //The length of adv data must be less than 31 bytes - esp_err_t raw_adv_ret = esp_ble_gap_config_adv_data_raw(raw_adv_data, sizeof(raw_adv_data)); + esp_err_t raw_adv_ret = esp_ble_gap_config_adv_data_raw(raw_adv_data, adv_data_len); if (raw_adv_ret){ ESP_LOGE(BT_BLE_COEX_TAG, "config raw adv data failed, error code = 0x%x ", raw_adv_ret); } - esp_err_t raw_scan_ret = esp_ble_gap_config_scan_rsp_data_raw(raw_adv_data, sizeof(raw_adv_data)); + esp_err_t raw_scan_ret = esp_ble_gap_config_scan_rsp_data_raw(raw_adv_data, adv_data_len); if (raw_scan_ret){ ESP_LOGE(BT_BLE_COEX_TAG, "config raw scan rsp data failed, error code = 0x%x", raw_scan_ret); } diff --git a/examples/bluetooth/blufi/main/blufi_security.c b/examples/bluetooth/blufi/main/blufi_security.c index 8b62777bba3b..123f506336a4 100644 --- a/examples/bluetooth/blufi/main/blufi_security.c +++ b/examples/bluetooth/blufi/main/blufi_security.c @@ -41,6 +41,7 @@ struct blufi_security { #define DH_SELF_PUB_KEY_LEN 128 +#define DH_PARAM_LEN_MAX 1024 uint8_t self_public_key[DH_SELF_PUB_KEY_LEN]; #define SHARE_KEY_LEN 128 uint8_t share_key[SHARE_KEY_LEN]; @@ -83,6 +84,13 @@ void blufi_dh_negotiate_data_handler(uint8_t *data, int len, uint8_t **output_da switch (type) { case SEC_TYPE_DH_PARAM_LEN: blufi_sec->dh_param_len = ((data[1]<<8)|data[2]); + // Security fix: Limit DH param length to prevent DoS via large memory allocation + if (blufi_sec->dh_param_len == 0 || blufi_sec->dh_param_len > DH_PARAM_LEN_MAX) { + BLUFI_ERROR("%s, invalid dh param len %d\n", __func__, blufi_sec->dh_param_len); + blufi_sec->dh_param_len = 0; + btc_blufi_report_error(ESP_BLUFI_DH_PARAM_ERROR); + return; + } if (blufi_sec->dh_param) { free(blufi_sec->dh_param); blufi_sec->dh_param = NULL; diff --git a/examples/bluetooth/esp_hid_device/main/esp_hid_gap.c b/examples/bluetooth/esp_hid_device/main/esp_hid_gap.c index 6cc854920bb6..9c87942f883e 100644 --- a/examples/bluetooth/esp_hid_device/main/esp_hid_gap.c +++ b/examples/bluetooth/esp_hid_device/main/esp_hid_gap.c @@ -278,6 +278,9 @@ static void handle_bt_device_result(struct disc_res_param *disc_res) GAP_DBG_PRINTF(", %s: ", gap_bt_prop_type_names[prop->type]); } if (prop->type == ESP_BT_GAP_DEV_PROP_BDNAME) { + if (prop->val == NULL) { + continue; + } name = (uint8_t *)prop->val; name_len = strlen((const char *)name); GAP_DBG_PRINTF("%s", (const char *)name); diff --git a/examples/bluetooth/esp_hid_host/main/esp_hid_host_main.c b/examples/bluetooth/esp_hid_host/main/esp_hid_host_main.c index 03e255d3abf4..8934b60c1aa7 100644 --- a/examples/bluetooth/esp_hid_host/main/esp_hid_host_main.c +++ b/examples/bluetooth/esp_hid_host/main/esp_hid_host_main.c @@ -164,7 +164,7 @@ void hid_demo_task(void *pvParameters) printf("] srv 0x%03x, ", r->bt.cod.service); print_uuid(&r->bt.uuid); printf(", "); - if (strncmp(r->name, remote_device_name, strlen(remote_device_name)) == 0) { + if (r->name && strncmp(r->name, remote_device_name, strlen(remote_device_name)) == 0) { break; } } @@ -175,7 +175,7 @@ void hid_demo_task(void *pvParameters) } #if CONFIG_BT_HID_HOST_ENABLED - if (cr && strncmp(cr->name, remote_device_name, strlen(remote_device_name)) == 0) { + if (cr && cr->name && strncmp(cr->name, remote_device_name, strlen(remote_device_name)) == 0) { esp_hidh_dev_open(cr->bda, cr->transport, cr->ble.addr_type); } #else diff --git a/examples/openthread/ot_ci_function.py b/examples/openthread/ot_ci_function.py index 5a706852e6b5..3b7c45d74836 100644 --- a/examples/openthread/ot_ci_function.py +++ b/examples/openthread/ot_ci_function.py @@ -141,11 +141,15 @@ def joinThreadNetwork(dut: IdfDut, thread: thread_parameter) -> None: def wait_for_join(dut: IdfDut, role: str) -> bool: + clean_buffer(dut) for _ in range(1, 30): - if getDeviceRole(dut) == role: - wait(dut, 5) + time.sleep(1) + execute_command(dut, 'state') + try: + dut.expect(re.compile(role), timeout=5) return True - wait(dut, 1) + except Exception: + continue return False diff --git a/examples/openthread/pytest_otbr.py b/examples/openthread/pytest_otbr.py index 6379f83d5e32..9d5072337c67 100644 --- a/examples/openthread/pytest_otbr.py +++ b/examples/openthread/pytest_otbr.py @@ -228,11 +228,14 @@ def test_Bidirectional_IPv6_connectivity(Init_interface: bool, dut: Tuple[IdfDut onlinkprefix = ocf.get_onlinkprefix(br) pattern = rf'\W+({onlinkprefix}(?:\w+:){{3}}\w+)\W+' host_global_unicast_addr = re.findall(pattern, out_str) + logging.info(f'host_global_unicast_addr: {host_global_unicast_addr}') + if host_global_unicast_addr is None: + raise Exception(f'onlinkprefix: {onlinkprefix}, host_global_unicast_addr: {host_global_unicast_addr}') rx_nums = 0 for ip_addr in host_global_unicast_addr: txrx_nums = ocf.ot_ping(cli, str(ip_addr), count=10) rx_nums = rx_nums + int(txrx_nums[1]) - logging.debug(f'rx_nums: {rx_nums}') + logging.info(f'rx_nums: {rx_nums}') assert rx_nums != 0 finally: ocf.stop_thread(cli) diff --git a/tools/cmake/version.cmake b/tools/cmake/version.cmake index cc62efdf3b32..844f75fd2e4f 100644 --- a/tools/cmake/version.cmake +++ b/tools/cmake/version.cmake @@ -1,5 +1,5 @@ set(IDF_VERSION_MAJOR 5) set(IDF_VERSION_MINOR 5) -set(IDF_VERSION_PATCH 1) +set(IDF_VERSION_PATCH 2) set(ENV{IDF_VERSION} "${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}.${IDF_VERSION_PATCH}") diff --git a/tools/test_apps/build_system/ldgen_test/check_placements.py b/tools/test_apps/build_system/ldgen_test/check_placements.py index c465f7bf0ea7..ed213683930e 100644 --- a/tools/test_apps/build_system/ldgen_test/check_placements.py +++ b/tools/test_apps/build_system/ldgen_test/check_placements.py @@ -8,13 +8,13 @@ import argparse import subprocess -from pyparsing import alphanums -from pyparsing import hexnums from pyparsing import LineEnd from pyparsing import LineStart from pyparsing import Literal from pyparsing import Optional from pyparsing import Word +from pyparsing import alphanums +from pyparsing import hexnums argparser = argparse.ArgumentParser() @@ -29,21 +29,26 @@ def check_location(symbol, expected): - pattern = (LineStart() + Word(hexnums).setResultsName('address') - + Optional(Word(alphanums, exact=1)) - + Optional(Word(alphanums,exact=1)) - + Word(alphanums + '._*').setResultsName('actual') - + Word(hexnums) - + Literal(symbol) - + LineEnd()) + pattern = ( + LineStart() + + Word(hexnums).set_results_name('address') + + Optional(Word(alphanums, exact=1)) + + Optional(Word(alphanums, exact=1)) + + Word(alphanums + '._*').set_results_name('actual') + + Word(hexnums) + + Literal(symbol) + + LineEnd() + ) try: - results = pattern.searchString(contents)[0] + results = pattern.search_string(contents)[0] except IndexError: raise Exception("check placement fail: '%s' was not found" % (symbol)) if results.actual != expected: - raise Exception("check placement fail: '%s' was placed in '%s', not in '%s'" % (symbol, results.actual, expected)) + raise Exception( + "check placement fail: '%s' was placed in '%s', not in '%s'" % (symbol, results.actual, expected) + ) print("check placement pass: '%s' was successfully placed in '%s'" % (symbol, results.actual)) return int(results.address, 16)