Merge branch 'contrib/github_pr_12179_v5.1' into 'release/v5.1'

fix(uart): Fix uart_ll_set_baudrate div-by-zero crash due to uint32_t overflow (backport v5.1)

See merge request espressif/esp-idf!26014
This commit is contained in:
morris
2023-09-20 16:13:20 +08:00
9 changed files with 43 additions and 38 deletions

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -7,11 +7,13 @@
// The LL layer for UART register operations.
// Note that most of the register operations in this layer are non-atomic operations.
#pragma once
#include <stdlib.h>
#include "hal/uart_types.h"
#include "soc/uart_periph.h"
#include "hal/clk_tree_ll.h"
#include "hal/misc.h"
#include "esp_attr.h"
#ifdef __cplusplus
@ -156,14 +158,16 @@ FORCE_INLINE_ATTR void uart_ll_set_baudrate(uart_dev_t *hw, uint32_t baud, uint3
{
#define DIV_UP(a, b) (((a) + (b) - 1) / (b))
const uint32_t max_div = BIT(12) - 1; // UART divider integer part only has 12 bits
int sclk_div = DIV_UP(sclk_freq, max_div * baud);
uint32_t sclk_div = DIV_UP(sclk_freq, (uint64_t)max_div * baud);
if (sclk_div == 0) abort();
uint32_t clk_div = ((sclk_freq) << 4) / (baud * sclk_div);
// The baud rate configuration register is divided into
// an integer part and a fractional part.
hw->clk_div.div_int = clk_div >> 4;
hw->clk_div.div_frag = clk_div & 0xf;
hw->clk_conf.sclk_div_num = sclk_div - 1;
hw->clk_div.div_frag = clk_div & 0xf;
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clk_conf, sclk_div_num, sclk_div - 1);
#undef DIV_UP
}
@ -179,7 +183,8 @@ FORCE_INLINE_ATTR uint32_t uart_ll_get_baudrate(uart_dev_t *hw, uint32_t sclk_fr
{
typeof(hw->clk_div) div_reg;
div_reg.val = hw->clk_div.val;
return ((sclk_freq << 4)) / (((div_reg.div_int << 4) | div_reg.div_frag) * (hw->clk_conf.sclk_div_num + 1));
return ((sclk_freq << 4)) /
(((div_reg.div_int << 4) | div_reg.div_frag) * (HAL_FORCE_READ_U32_REG_FIELD(hw->clk_conf, sclk_div_num) + 1));
}
/**
@ -464,7 +469,7 @@ FORCE_INLINE_ATTR void uart_ll_set_tx_idle_num(uart_dev_t *hw, uint32_t idle_num
FORCE_INLINE_ATTR void uart_ll_tx_break(uart_dev_t *hw, uint32_t break_num)
{
if (break_num > 0) {
hw->txbrk_conf.tx_brk_num = break_num;
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->txbrk_conf, tx_brk_num, break_num);
hw->conf0.txd_brk = 1;
} else {
hw->conf0.txd_brk = 0;
@ -531,8 +536,8 @@ FORCE_INLINE_ATTR void uart_ll_set_sw_flow_ctrl(uart_dev_t *hw, uart_sw_flowctrl
hw->flow_conf.sw_flow_con_en = 1;
hw->swfc_conf1.xon_threshold = flow_ctrl->xon_thrd;
hw->swfc_conf0.xoff_threshold = flow_ctrl->xoff_thrd;
hw->swfc_conf1.xon_char = flow_ctrl->xon_char;
hw->swfc_conf0.xoff_char = flow_ctrl->xoff_char;
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->swfc_conf1, xon_char, flow_ctrl->xon_char);
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->swfc_conf0, xoff_char, flow_ctrl->xoff_char);
} else {
hw->flow_conf.sw_flow_con_en = 0;
hw->flow_conf.xonoff_del = 0;
@ -554,11 +559,11 @@ FORCE_INLINE_ATTR void uart_ll_set_sw_flow_ctrl(uart_dev_t *hw, uart_sw_flowctrl
*/
FORCE_INLINE_ATTR void uart_ll_set_at_cmd_char(uart_dev_t *hw, uart_at_cmd_t *cmd_char)
{
hw->at_cmd_char.data = cmd_char->cmd_char;
hw->at_cmd_char.char_num = cmd_char->char_num;
hw->at_cmd_postcnt.post_idle_num = cmd_char->post_idle;
hw->at_cmd_precnt.pre_idle_num = cmd_char->pre_idle;
hw->at_cmd_gaptout.rx_gap_tout = cmd_char->gap_tout;
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->at_cmd_char, data, cmd_char->cmd_char);
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->at_cmd_char, char_num, cmd_char->char_num);
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->at_cmd_postcnt, post_idle_num, cmd_char->post_idle);
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->at_cmd_precnt, pre_idle_num, cmd_char->pre_idle);
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->at_cmd_gaptout, rx_gap_tout, cmd_char->gap_tout);
}
/**
@ -747,8 +752,8 @@ FORCE_INLINE_ATTR void uart_ll_set_mode(uart_dev_t *hw, uart_mode_t mode)
*/
FORCE_INLINE_ATTR void uart_ll_get_at_cmd_char(uart_dev_t *hw, uint8_t *cmd_char, uint8_t *char_num)
{
*cmd_char = hw->at_cmd_char.data;
*char_num = hw->at_cmd_char.char_num;
*cmd_char = HAL_FORCE_READ_U32_REG_FIELD(hw->at_cmd_char, data);
*char_num = HAL_FORCE_READ_U32_REG_FIELD(hw->at_cmd_char, char_num);
}
/**

View File

@ -10,6 +10,7 @@
#pragma once
#include <stdlib.h>
#include "hal/misc.h"
#include "hal/uart_types.h"
#include "soc/uart_periph.h"
@ -158,13 +159,15 @@ FORCE_INLINE_ATTR void uart_ll_set_baudrate(uart_dev_t *hw, uint32_t baud, uint3
{
#define DIV_UP(a, b) (((a) + (b) - 1) / (b))
const uint32_t max_div = BIT(12) - 1; // UART divider integer part only has 12 bits
int sclk_div = DIV_UP(sclk_freq, max_div * baud);
uint32_t sclk_div = DIV_UP(sclk_freq, (uint64_t)max_div * baud);
if (sclk_div == 0) abort();
uint32_t clk_div = ((sclk_freq) << 4) / (baud * sclk_div);
// The baud rate configuration register is divided into
// an integer part and a fractional part.
hw->clk_div.div_int = clk_div >> 4;
hw->clk_div.div_frag = clk_div & 0xf;
hw->clk_div.div_frag = clk_div & 0xf;
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clk_conf, sclk_div_num, sclk_div - 1);
#undef DIV_UP
}

View File

@ -10,6 +10,7 @@
#pragma once
#include <stdlib.h>
#include "esp_attr.h"
#include "hal/misc.h"
#include "hal/uart_types.h"
@ -190,7 +191,9 @@ FORCE_INLINE_ATTR void uart_ll_set_baudrate(uart_dev_t *hw, uint32_t baud, uint3
{
#define DIV_UP(a, b) (((a) + (b) - 1) / (b))
const uint32_t max_div = BIT(12) - 1; // UART divider integer part only has 12 bits
int sclk_div = DIV_UP(sclk_freq, max_div * baud);
uint32_t sclk_div = DIV_UP(sclk_freq, (uint64_t)max_div * baud);
if (sclk_div == 0) abort();
uint32_t clk_div = ((sclk_freq) << 4) / (baud * sclk_div);
// The baud rate configuration register is divided into

View File

@ -10,6 +10,7 @@
#pragma once
#include <stdlib.h>
#include "esp_attr.h"
#include "hal/misc.h"
#include "hal/uart_types.h"
@ -191,7 +192,9 @@ FORCE_INLINE_ATTR void uart_ll_set_baudrate(uart_dev_t *hw, uint32_t baud, uint3
{
#define DIV_UP(a, b) (((a) + (b) - 1) / (b))
const uint32_t max_div = BIT(12) - 1; // UART divider integer part only has 12 bits
int sclk_div = DIV_UP(sclk_freq, max_div * baud);
uint32_t sclk_div = DIV_UP(sclk_freq, (uint64_t)max_div * baud);
if (sclk_div == 0) abort();
uint32_t clk_div = ((sclk_freq) << 4) / (baud * sclk_div);
// The baud rate configuration register is divided into

View File

@ -10,6 +10,7 @@
#pragma once
#include <stdlib.h>
#include "hal/misc.h"
#include "hal/uart_types.h"
#include "soc/uart_periph.h"
@ -130,13 +131,15 @@ FORCE_INLINE_ATTR void uart_ll_set_baudrate(uart_dev_t *hw, uint32_t baud, uint3
{
#define DIV_UP(a, b) (((a) + (b) - 1) / (b))
const uint32_t max_div = BIT(12) - 1; // UART divider integer part only has 12 bits
int sclk_div = DIV_UP(sclk_freq, max_div * baud);
uint32_t sclk_div = DIV_UP(sclk_freq, (uint64_t)max_div * baud);
if (sclk_div == 0) abort();
uint32_t clk_div = ((sclk_freq) << 4) / (baud * sclk_div);
// The baud rate configuration register is divided into
// an integer part and a fractional part.
hw->clkdiv.clkdiv = clk_div >> 4;
hw->clkdiv.clkdiv_frag = clk_div & 0xf;
hw->clkdiv.clkdiv_frag = clk_div & 0xf;
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clk_conf, sclk_div_num, sclk_div - 1);
#undef DIV_UP
}

View File

@ -139,6 +139,5 @@ The table below provides more information on pin usage, and please note the comm
- SPI0/1: GPIO15-21 are usually used for SPI flash and not recommended for other uses.
- USB-Serial-JTAG: GPIO 26 and 27 are used by USB-Serial-JTAG by default. In order to use them as GPIOs, USB-Serial-JTAG will be disabled by the drivers.
- For chip variants with an SiP flash built in, GPIO15 ~ GPIO21 are dedicated to connecting the SiP flash and are not fan-out to the external pins. In addition, GPIO6 ~ GPIO7 are also not fan-out to the external pins. In conclusion, only GPIO0~ GPIO5, GPIO8~ GPIO14, GPIO22~ GPIO27 are available to users.
- For chip variant without SiP flash, apart from the flash IOs mentioned above, GPIO22 is not fan-out to the external pin, thus they're not available to users.
---

View File

@ -92,13 +92,7 @@ Dynamic Frequency Scaling and Peripheral Drivers
When DFS is enabled, the APB frequency can be changed multiple times within a single RTOS tick. The APB frequency change does not affect the operation of some peripherals, while other peripherals may have issues. For example, Timer Group peripheral timers will keep counting, however, the speed at which they count will change proportionally to the APB frequency.
The following peripherals work normally even when the APB frequency is changing:
- **UART**: if REF_TICK or XTAL is used as a clock source. See :cpp:member:`uart_config_t::source_clk`.
- **LEDC**: if REF_TICK is used as a clock source. See :cpp:func:`ledc_timer_config` function.
- **RMT**: if REF_TICK or XTAL is used as a clock source. See :cpp:member:`rmt_config_t::flags` and macro `RMT_CHANNEL_FLAGS_AWARE_DFS`.
- **GPTimer**: if APB is used as the clock source. See :cpp:member:`gptimer_config_t::clk_src`.
- **TSENS**: XTAL or RTC_8M is used as a clock source. So, APB frequency changing will not influence it.
Peripheral clock sources such as ``REF_TICK``, ``XTAL``, ``RC_FAST`` (i.e. ``RTC_8M``), their frequencies will not be inflenced by APB frequency. And therefore, to ensure the peripheral behaves consistently during DFS, it is recommanded to select one of these clocks as the peripheral clock source. For more specific guidelines, please refer to the "Power Management" section of each peripheral's "API Reference > Peripherals API" page.
Currently, the following peripheral drivers are aware of DFS and will use the ``ESP_PM_APB_FREQ_MAX`` lock for the duration of the transaction:
@ -112,6 +106,7 @@ The following drivers will hold the ``ESP_PM_APB_FREQ_MAX`` lock while the drive
.. list::
- **SPI slave**: between calls to :cpp:func:`spi_slave_initialize` and :cpp:func:`spi_slave_free`.
- **GPTimer**: between calls to :cpp:func:`gptimer_enable` and :cpp:func:`gptimer_disable`.
- **Ethernet**: between calls to :cpp:func:`esp_eth_driver_install` and :cpp:func:`esp_eth_driver_uninstall`.
- **WiFi**: between calls to :cpp:func:`esp_wifi_start` and :cpp:func:`esp_wifi_stop`. If modem sleep is enabled, the lock will be released for the periods of time when radio is disabled.
:SOC_TWAI_SUPPORTED: - **TWAI**: between calls to :cpp:func:`twai_driver_install` and :cpp:func:`twai_driver_uninstall` (only when the clock source is set to :cpp:enumerator:`TWAI_CLK_SRC_APB`).

View File

@ -141,6 +141,5 @@
- SPI0/1: GPIO15-21 通常用于 SPI flash, 不推荐用于其他用途。
- USB-Serial-JTAG: GPIO26 GPIO27 默认用于 USB-Serial-JTAG。用做 GPIO 时驱动程序将禁用 USB-Serial-JTAG。
- 对于合封了 flash 的芯片型号, GPIO15 ~ GPIO21 专门用于连接该 flash, 并未引出至芯片管脚。且 GPIO6 ~ GPIO7 也未引出至芯片管脚,用户不可用。用户可配置使用其他剩余的 19 GPIO 管脚, 编号为: GPIO0 ~ GPIO5、GPIO8 ~ GPIO14、GPIO22 ~ GPIO27。
- 对于未合封 flash 的芯片型号, 除了以上提到的给 Flash 专用的 GPIO 以外, GPIO22 也并未引出至芯片管脚,用户不可用。
---

View File

@ -92,13 +92,7 @@ ESP-IDF 中集成的电源管理算法可以根据应用程序组件的需求,
启用动态调频后APB 频率可在一个 RTOS 滴答周期内多次更改。有些外设不受 APB 频率变更的影响但有些外设可能会出现问题。例如Timer Group 外设定时器会继续计数,但定时器计数的速度将随 APB 频率的变更而变更。
以下外设不受 APB 频率变更的影响:
- **UART**:如果 REF_TICK 或者 XTAL 用作时钟源,则 UART 不受 APB 频率变更影响。请查看 :cpp:member:`uart_config_t::source_clk`
- **LEDC**:如果 REF_TICK 用作时钟源,则 LEDC 不受 APB 频率变更影响。请查看 :cpp:func:`ledc_timer_config` 函数。
- **RMT**:如果 REF_TICK 或者 XTAL 被用作时钟源,则 RMT 不受 APB 频率变更影响。请查看 :cpp:member:`rmt_config_t::flags` 以及 `RMT_CHANNEL_FLAGS_AWARE_DFS` 宏。
- **GPTimer**:如果 XTAL 用作时钟源,则 GPTimer 不受 APB 频率变更影响。请查看 :cpp:member:`gptimer_config_t::clk_src`
- **TSENS**XTAL 或 RTC_8M 用作时钟源,因此不受 APB 频率变化影响。
时钟频率不受 APB 频率影响的外设时钟源通常有 ``REF_TICK``, ``XTAL``, ``RC_FAST`` (i.e. ``RTC_8M``)。因此,为了保证外设在 DFS 期间的所有行为一致,建议在上述时钟中选择其一作为外设的时钟源。如果想要了解更多详情可以浏览每个外设 ”API 参考 > 外设 API“ 页面的 “电源管理” 章节。
目前以下外设驱动程序可感知动态调频,并在调频期间使用 ``ESP_PM_APB_FREQ_MAX`` 锁:
@ -112,6 +106,7 @@ ESP-IDF 中集成的电源管理算法可以根据应用程序组件的需求,
.. list::
- **SPI slave**:从调用 :cpp:func:`spi_slave_initialize`:cpp:func:`spi_slave_free` 期间。
- **GPTimer**:从调用 :cpp:func:`gptimer_enable`:cpp:func:`gptimer_disable` 期间。
- **Ethernet**:从调用 :cpp:func:`esp_eth_driver_install`:cpp:func:`esp_eth_driver_uninstall` 期间。
- **WiFi**:从调用 :cpp:func:`esp_wifi_start`:cpp:func:`esp_wifi_stop` 期间。如果启用了调制解调器睡眠模式,广播关闭时将释放此管理锁。
:SOC_TWAI_SUPPORTED: - **TWAI**:从调用 :cpp:func:`twai_driver_install` 至 :cpp:func:`twai_driver_uninstall` 期间 (只有在 TWAI 时钟源选择为 :cpp:enumerator:`TWAI_CLK_SRC_APB` 的时候生效)。