Use uint64_t instead of uint32_t for the FLASH_STM32_EX_OP_SECTOR_WP extended operation. Some chips have two banks, more than 16 sectors each e.g. stm32h7a3xx chips, so more than 32 bits are required. Signed-off-by: Dawid Niedzwiecki <dawidn@google.com>
295 lines
6.8 KiB
C
295 lines
6.8 KiB
C
/*
|
|
* Copyright (c) 2023 Google Inc
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/drivers/flash/stm32_flash_api_extensions.h>
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/logging/log.h>
|
|
|
|
#ifdef CONFIG_USERSPACE
|
|
#include <zephyr/syscall.h>
|
|
#include <zephyr/internal/syscall_handler.h>
|
|
#endif
|
|
|
|
#include <soc.h>
|
|
#include "flash_stm32.h"
|
|
|
|
LOG_MODULE_REGISTER(flash_stm32_ex_op, CONFIG_FLASH_LOG_LEVEL);
|
|
|
|
#if defined(CONFIG_FLASH_STM32_WRITE_PROTECT)
|
|
int flash_stm32_ex_op_sector_wp(const struct device *dev, const uintptr_t in,
|
|
void *out)
|
|
{
|
|
const struct flash_stm32_ex_op_sector_wp_in *request =
|
|
(const struct flash_stm32_ex_op_sector_wp_in *)in;
|
|
struct flash_stm32_ex_op_sector_wp_out *result =
|
|
(struct flash_stm32_ex_op_sector_wp_out *)out;
|
|
uint64_t change_mask;
|
|
int rc = 0, rc2 = 0;
|
|
#ifdef CONFIG_USERSPACE
|
|
bool syscall_trap = z_syscall_trap();
|
|
#endif
|
|
|
|
if (request != NULL) {
|
|
#ifdef CONFIG_USERSPACE
|
|
struct flash_stm32_ex_op_sector_wp_in in_copy;
|
|
|
|
if (syscall_trap) {
|
|
K_OOPS(k_usermode_from_copy(&in_copy, request,
|
|
sizeof(in_copy)));
|
|
request = &in_copy;
|
|
}
|
|
#endif
|
|
change_mask = request->enable_mask;
|
|
|
|
if (!IS_ENABLED(
|
|
CONFIG_FLASH_STM32_WRITE_PROTECT_DISABLE_PREVENTION)) {
|
|
change_mask |= request->disable_mask;
|
|
}
|
|
|
|
rc = flash_stm32_option_bytes_lock(dev, false);
|
|
if (rc == 0) {
|
|
rc = flash_stm32_update_wp_sectors(
|
|
dev, change_mask, request->enable_mask);
|
|
}
|
|
|
|
rc2 = flash_stm32_option_bytes_lock(dev, true);
|
|
if (!rc) {
|
|
rc = rc2;
|
|
}
|
|
}
|
|
|
|
if (result != NULL) {
|
|
#ifdef CONFIG_USERSPACE
|
|
struct flash_stm32_ex_op_sector_wp_out out_copy;
|
|
|
|
if (syscall_trap) {
|
|
result = &out_copy;
|
|
}
|
|
#endif
|
|
rc2 = flash_stm32_get_wp_sectors(dev, &result->protected_mask);
|
|
if (!rc) {
|
|
rc = rc2;
|
|
}
|
|
|
|
#ifdef CONFIG_USERSPACE
|
|
if (syscall_trap) {
|
|
K_OOPS(k_usermode_to_copy(out, result, sizeof(out_copy)));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
#endif /* CONFIG_FLASH_STM32_WRITE_PROTECT */
|
|
|
|
#if defined(CONFIG_FLASH_STM32_READOUT_PROTECTION)
|
|
int flash_stm32_ex_op_update_rdp(const struct device *dev, bool enable,
|
|
bool permanent)
|
|
{
|
|
uint8_t current_level, target_level;
|
|
|
|
current_level = flash_stm32_get_rdp_level(dev);
|
|
target_level = current_level;
|
|
|
|
/*
|
|
* 0xAA = RDP level 0 (no protection)
|
|
* 0xCC = RDP level 2 (permanent protection)
|
|
* others = RDP level 1 (protection active)
|
|
*/
|
|
switch (current_level) {
|
|
case FLASH_STM32_RDP2:
|
|
if (!enable || !permanent) {
|
|
LOG_DBG("RDP level 2 is permanent and can't be changed!");
|
|
return -ENOTSUP;
|
|
}
|
|
break;
|
|
case FLASH_STM32_RDP0:
|
|
if (enable) {
|
|
target_level = FLASH_STM32_RDP1;
|
|
if (permanent) {
|
|
#if defined(CONFIG_FLASH_STM32_READOUT_PROTECTION_PERMANENT_ALLOW)
|
|
target_level = FLASH_STM32_RDP2;
|
|
#else
|
|
LOG_DBG("Permanent readout protection (RDP "
|
|
"level 0 -> 2) not allowed");
|
|
return -ENOTSUP;
|
|
#endif
|
|
}
|
|
}
|
|
break;
|
|
default: /* FLASH_STM32_RDP1 */
|
|
if (enable && permanent) {
|
|
#if defined(CONFIG_FLASH_STM32_READOUT_PROTECTION_PERMANENT_ALLOW)
|
|
target_level = FLASH_STM32_RDP2;
|
|
#else
|
|
LOG_DBG("Permanent readout protection (RDP "
|
|
"level 1 -> 2) not allowed");
|
|
return -ENOTSUP;
|
|
#endif
|
|
}
|
|
if (!enable) {
|
|
#if defined(CONFIG_FLASH_STM32_READOUT_PROTECTION_DISABLE_ALLOW)
|
|
target_level = FLASH_STM32_RDP0;
|
|
#else
|
|
LOG_DBG("Disabling readout protection (RDP "
|
|
"level 1 -> 0) not allowed");
|
|
return -EACCES;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/* Update RDP level if needed */
|
|
if (current_level != target_level) {
|
|
LOG_INF("RDP changed from 0x%02x to 0x%02x", current_level,
|
|
target_level);
|
|
|
|
flash_stm32_set_rdp_level(dev, target_level);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int flash_stm32_ex_op_rdp(const struct device *dev, const uintptr_t in,
|
|
void *out)
|
|
{
|
|
const struct flash_stm32_ex_op_rdp *request =
|
|
(const struct flash_stm32_ex_op_rdp *)in;
|
|
struct flash_stm32_ex_op_rdp *result =
|
|
(struct flash_stm32_ex_op_rdp *)out;
|
|
uint8_t current_level;
|
|
|
|
#ifdef CONFIG_USERSPACE
|
|
struct flash_stm32_ex_op_rdp copy;
|
|
bool syscall_trap = z_syscall_trap();
|
|
#endif
|
|
int rc = 0, rc2 = 0;
|
|
|
|
if (request != NULL) {
|
|
#ifdef CONFIG_USERSPACE
|
|
if (syscall_trap) {
|
|
K_OOPS(k_usermode_from_copy(©, request, sizeof(copy)));
|
|
request = ©
|
|
}
|
|
#endif
|
|
rc = flash_stm32_option_bytes_lock(dev, false);
|
|
if (rc == 0) {
|
|
rc = flash_stm32_ex_op_update_rdp(dev, request->enable,
|
|
request->permanent);
|
|
}
|
|
|
|
rc2 = flash_stm32_option_bytes_lock(dev, true);
|
|
if (!rc) {
|
|
rc = rc2;
|
|
}
|
|
}
|
|
|
|
if (result != NULL) {
|
|
#ifdef CONFIG_USERSPACE
|
|
if (syscall_trap) {
|
|
result = ©
|
|
}
|
|
#endif
|
|
|
|
current_level = flash_stm32_get_rdp_level(dev);
|
|
|
|
/*
|
|
* 0xAA = RDP level 0 (no protection)
|
|
* 0xCC = RDP level 2 (permanent protection)
|
|
* others = RDP level 1 (protection active)
|
|
*/
|
|
switch (current_level) {
|
|
case FLASH_STM32_RDP2:
|
|
result->enable = true;
|
|
result->permanent = true;
|
|
break;
|
|
case FLASH_STM32_RDP0:
|
|
result->enable = false;
|
|
result->permanent = false;
|
|
break;
|
|
default: /* FLASH_STM32_RDP1 */
|
|
result->enable = true;
|
|
result->permanent = false;
|
|
}
|
|
|
|
#ifdef CONFIG_USERSPACE
|
|
if (syscall_trap) {
|
|
K_OOPS(k_usermode_to_copy(out, result, sizeof(copy)));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
#endif /* CONFIG_FLASH_STM32_READOUT_PROTECTION */
|
|
|
|
int flash_stm32_ex_op(const struct device *dev, uint16_t code,
|
|
const uintptr_t in, void *out)
|
|
{
|
|
int rv = -ENOTSUP;
|
|
|
|
flash_stm32_sem_take(dev);
|
|
|
|
switch (code) {
|
|
#if defined(CONFIG_FLASH_STM32_WRITE_PROTECT)
|
|
case FLASH_STM32_EX_OP_SECTOR_WP:
|
|
rv = flash_stm32_ex_op_sector_wp(dev, in, out);
|
|
break;
|
|
#endif /* CONFIG_FLASH_STM32_WRITE_PROTECT */
|
|
#if defined(CONFIG_FLASH_STM32_READOUT_PROTECTION)
|
|
case FLASH_STM32_EX_OP_RDP:
|
|
rv = flash_stm32_ex_op_rdp(dev, in, out);
|
|
break;
|
|
#endif /* CONFIG_FLASH_STM32_READOUT_PROTECTION */
|
|
#if defined(CONFIG_FLASH_STM32_BLOCK_REGISTERS)
|
|
case FLASH_STM32_EX_OP_BLOCK_OPTION_REG:
|
|
rv = flash_stm32_option_bytes_disable(dev);
|
|
break;
|
|
case FLASH_STM32_EX_OP_BLOCK_CONTROL_REG:
|
|
rv = flash_stm32_control_register_disable(dev);
|
|
break;
|
|
#endif /* CONFIG_FLASH_STM32_BLOCK_REGISTERS */
|
|
#if defined(CONFIG_FLASH_STM32_OPTION_BYTES) && ( \
|
|
defined(CONFIG_DT_HAS_ST_STM32F4_FLASH_CONTROLLER_ENABLED) || \
|
|
defined(CONFIG_DT_HAS_ST_STM32F7_FLASH_CONTROLLER_ENABLED) || \
|
|
defined(CONFIG_DT_HAS_ST_STM32G4_FLASH_CONTROLLER_ENABLED) || \
|
|
defined(CONFIG_DT_HAS_ST_STM32L4_FLASH_CONTROLLER_ENABLED))
|
|
case FLASH_STM32_EX_OP_OPTB_READ:
|
|
if (out == NULL) {
|
|
rv = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
*(uint32_t *)out = flash_stm32_option_bytes_read(dev);
|
|
rv = 0;
|
|
|
|
break;
|
|
case FLASH_STM32_EX_OP_OPTB_WRITE:
|
|
int rv2;
|
|
|
|
rv = flash_stm32_option_bytes_lock(dev, false);
|
|
if (rv > 0) {
|
|
break;
|
|
}
|
|
|
|
rv2 = flash_stm32_option_bytes_write(dev, UINT32_MAX, (uint32_t)in);
|
|
/* returned later, we always re-lock */
|
|
|
|
rv = flash_stm32_option_bytes_lock(dev, true);
|
|
if (rv > 0) {
|
|
break;
|
|
}
|
|
|
|
rv = rv2;
|
|
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
flash_stm32_sem_give(dev);
|
|
|
|
return rv;
|
|
}
|