main.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. /*
  2. * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Unlicense OR CC0-1.0
  5. */
  6. #include "freertos/FreeRTOS.h"
  7. #include "freertos/task.h"
  8. #include "unity.h"
  9. #include <stdlib.h>
  10. #include <stdio.h>
  11. #include <stdint.h>
  12. #include <string.h>
  13. #include <assert.h>
  14. #include <fcntl.h>
  15. #include <unistd.h>
  16. #include <errno.h>
  17. #include <esp_log.h>
  18. #include <esp_attr.h>
  19. #include "esp_flash.h"
  20. #include <esp_partition.h>
  21. #include "nvs_flash.h"
  22. #include "nvs.h"
  23. #include "esp_vfs.h"
  24. #include "esp_vfs_fat.h"
  25. #include "esp_spiffs.h"
  26. #include "esp_heap_caps.h"
  27. #include "esp_flash_encrypt.h"
  28. #include "esp_efuse_table.h"
  29. static const char* TAG = "test_readonly_partition_feature";
  30. #define NUM_OF_READONLY_PARTITIONS 4
  31. const esp_partition_t* readonly_partitions[NUM_OF_READONLY_PARTITIONS];
  32. // Partition names
  33. const char *nvs_partition_name = "nvs_ro";
  34. const char *fatfs_wl_partition_name = "fatfs_ro";
  35. const char *fatfs_raw_partition_name = "fatfs_raw_ro";
  36. const char *spiffs_partition_name = "spiffs_ro";
  37. // Mount paths for partitions
  38. #define FATFS_WL_BASE_PATH "/fatfs_wl"
  39. #define FATFS_RAW_BASE_PATH "/fatfs_raw"
  40. #define SPIFFS_BASE_PATH "/spiffs"
  41. // Handle of the wear levelling library instance
  42. static wl_handle_t s_wl_handle = WL_INVALID_HANDLE;
  43. // Data in each filesystem partition
  44. const char* cmp_string = "This is a file cointained in the generated filesystem image on the host and flashed to the ESP device";
  45. #define CMP_STRING_LEN 102 // 101 + '\0'
  46. static void fill_array_of_readonly_data_partitions(void)
  47. {
  48. // This finds read-only partitions defined in the partition table
  49. const esp_partition_t* part_nvs = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
  50. ESP_PARTITION_SUBTYPE_ANY, nvs_partition_name);
  51. const esp_partition_t* part_fatfs_wl = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
  52. ESP_PARTITION_SUBTYPE_ANY, fatfs_wl_partition_name);
  53. const esp_partition_t* part_fatfs_raw = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
  54. ESP_PARTITION_SUBTYPE_ANY, fatfs_raw_partition_name);
  55. const esp_partition_t* part_spiffs = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
  56. ESP_PARTITION_SUBTYPE_ANY, spiffs_partition_name);
  57. TEST_ASSERT_NOT_NULL(part_nvs); // NULL means partition table set wrong
  58. TEST_ASSERT_NOT_NULL(part_fatfs_wl);
  59. TEST_ASSERT_NOT_NULL(part_fatfs_raw);
  60. TEST_ASSERT_NOT_NULL(part_spiffs);
  61. readonly_partitions[0] = part_nvs;
  62. readonly_partitions[1] = part_fatfs_wl;
  63. readonly_partitions[2] = part_fatfs_raw;
  64. readonly_partitions[3] = part_spiffs;
  65. }
  66. #if CONFIG_IDF_TARGET_ESP32
  67. #define TARGET_CRYPT_CNT_EFUSE ESP_EFUSE_FLASH_CRYPT_CNT
  68. #define TARGET_CRYPT_CNT_WIDTH 7
  69. #else
  70. #define TARGET_CRYPT_CNT_EFUSE ESP_EFUSE_SPI_BOOT_CRYPT_CNT
  71. #define TARGET_CRYPT_CNT_WIDTH 3
  72. #endif
  73. static void example_print_flash_encryption_status(void)
  74. {
  75. uint32_t flash_crypt_cnt = 0;
  76. esp_efuse_read_field_blob(TARGET_CRYPT_CNT_EFUSE, &flash_crypt_cnt, TARGET_CRYPT_CNT_WIDTH);
  77. printf("FLASH_CRYPT_CNT eFuse value is %" PRIu32 "\n", flash_crypt_cnt);
  78. esp_flash_enc_mode_t mode = esp_get_flash_encryption_mode();
  79. if (mode == ESP_FLASH_ENC_MODE_DISABLED) {
  80. printf("Flash encryption feature is disabled\n");
  81. } else {
  82. printf("Flash encryption feature is enabled in %s mode\n",
  83. mode == ESP_FLASH_ENC_MODE_DEVELOPMENT ? "DEVELOPMENT" : "RELEASE");
  84. }
  85. }
  86. void app_main(void)
  87. {
  88. example_print_flash_encryption_status();
  89. fill_array_of_readonly_data_partitions();
  90. unity_run_menu();
  91. }
  92. TEST_CASE("Read-only partition - SPI flash API", "[spi_flash]")
  93. {
  94. esp_err_t err;
  95. char buf[11] = {0};
  96. const char some_data[] = "0123456789";
  97. for (int i = 0; i < NUM_OF_READONLY_PARTITIONS; i++) {
  98. const esp_partition_t *part = readonly_partitions[i];
  99. // Writing to the SPI flash on address overlapping read-only partition shouldn't be possible
  100. // and should return ESP_ERR_NOT_ALLOWED error
  101. err = esp_flash_write(part->flash_chip, some_data, part->address, strlen(some_data));
  102. ESP_LOGD(TAG, "Writing %u bytes to partition %s at 0x%lx, should return %s and returned %s (0x%x)",
  103. strlen(some_data), part->label, part->address, esp_err_to_name(ESP_ERR_NOT_ALLOWED), esp_err_to_name(err), err);
  104. TEST_ASSERT_EQUAL(ESP_ERR_NOT_ALLOWED, err);
  105. // Reading the SPI flash on address overlapping read-only partition should be possible without an error
  106. TEST_ESP_OK(esp_flash_read(part->flash_chip, &buf, part->address, strlen(some_data)));
  107. }
  108. }
  109. TEST_CASE("Read-only partition - Partition API", "[partition]")
  110. {
  111. esp_err_t err;
  112. // Writing to the partition should not be possible and should return ESP_ERR_NOT_ALLOWED error
  113. const char some_data[] = "0123456789";
  114. for (int i = 0; i < NUM_OF_READONLY_PARTITIONS; i++) {
  115. err = esp_partition_write(readonly_partitions[i], 0, some_data, strlen(some_data));
  116. ESP_LOGD(TAG, "esp_partition_write on readonly_partitions[%d] should return %s and returned %s (0x%x)",
  117. i, esp_err_to_name(ESP_ERR_NOT_ALLOWED), esp_err_to_name(err), err);
  118. TEST_ASSERT_EQUAL(ESP_ERR_NOT_ALLOWED, err);
  119. }
  120. // Reading the partition should be possible without an error
  121. char buf[strlen(some_data)];
  122. for (int i = 0; i < NUM_OF_READONLY_PARTITIONS; i++) {
  123. err = esp_partition_read(readonly_partitions[i], 0, buf, sizeof(buf));
  124. TEST_ESP_OK(err);
  125. }
  126. }
  127. TEST_CASE("Read-only partition - NVS API", "[nvs]")
  128. {
  129. nvs_handle_t handle;
  130. esp_err_t err;
  131. err = nvs_flash_init_partition(nvs_partition_name);
  132. TEST_ESP_OK(err);
  133. // NVS partition flagged as read-only should be possible to open in read-only mode
  134. err = nvs_open_from_partition(nvs_partition_name, "storage", NVS_READONLY, &handle);
  135. TEST_ESP_OK(err);
  136. // Read test
  137. int32_t i32_val = 0;
  138. err = nvs_get_i32(handle, "i32_key", &i32_val);
  139. TEST_ESP_OK(err);
  140. TEST_ASSERT_EQUAL(-2147483648, i32_val);
  141. nvs_close(handle);
  142. // NVS partition flagged as read-only shouln't be possible to open in read-write mode
  143. err = nvs_open_from_partition(nvs_partition_name, "storage", NVS_READWRITE, &handle);
  144. TEST_ASSERT_EQUAL(ESP_ERR_NOT_ALLOWED, err);
  145. nvs_close(handle);
  146. }
  147. void test_c_api_common(const char* base_path)
  148. {
  149. char hello_txt[64];
  150. char new_txt[64];
  151. snprintf(hello_txt, sizeof(hello_txt), "%s%s", base_path, "/hello.txt");
  152. snprintf(new_txt, sizeof(new_txt), "%s%s", base_path, "/new.txt");
  153. FILE *f;
  154. int fd, status;
  155. char buf[CMP_STRING_LEN] = {0};
  156. // Test write mode is not possible
  157. f = fopen(hello_txt, "w");
  158. TEST_ASSERT_NULL(f);
  159. fd = open(hello_txt, O_CREAT|O_WRONLY, 0666);
  160. TEST_ASSERT_EQUAL(-1, fd);
  161. TEST_ASSERT_EQUAL(EROFS, errno);
  162. f = fopen(hello_txt, "w+");
  163. TEST_ASSERT_NULL(f);
  164. fd = open(hello_txt, O_CREAT|O_RDWR, 0666);
  165. TEST_ASSERT_EQUAL(-1, fd);
  166. TEST_ASSERT_EQUAL(EROFS, errno);
  167. f = fopen(hello_txt, "a");
  168. TEST_ASSERT_NULL(f);
  169. fd = open(hello_txt, O_CREAT|O_WRONLY|O_APPEND, 0666);
  170. TEST_ASSERT_EQUAL(-1, fd);
  171. TEST_ASSERT_EQUAL(EROFS, errno);
  172. f = fopen(hello_txt, "a+");
  173. TEST_ASSERT_NULL(f);
  174. fd = open(hello_txt, O_CREAT|O_RDWR|O_APPEND, 0666);
  175. TEST_ASSERT_EQUAL(-1, fd);
  176. TEST_ASSERT_EQUAL(EROFS, errno);
  177. f = fopen(hello_txt, "r+");
  178. TEST_ASSERT_NULL(f);
  179. fd = open(hello_txt, O_RDWR);
  180. TEST_ASSERT_EQUAL(-1, fd);
  181. TEST_ASSERT_EQUAL(EROFS, errno);
  182. fd = creat(new_txt, 0666); // == open(new_txt, O_WRONLY|O_CREAT|O_TRUNC, 0666)
  183. TEST_ASSERT_EQUAL(-1, fd);
  184. TEST_ASSERT_EQUAL(EROFS, errno);
  185. status = link(hello_txt, new_txt);
  186. TEST_ASSERT_EQUAL(-1, status);
  187. TEST_ASSERT_EQUAL(EROFS, errno);
  188. status = rename(hello_txt, new_txt);
  189. TEST_ASSERT_EQUAL(-1, status);
  190. TEST_ASSERT_EQUAL(EROFS, errno);
  191. status = unlink(hello_txt);
  192. TEST_ASSERT_EQUAL(-1, status);
  193. TEST_ASSERT_EQUAL(EROFS, errno);
  194. status = truncate(hello_txt, 10);
  195. TEST_ASSERT_EQUAL(-1, status);
  196. TEST_ASSERT_EQUAL(EROFS, errno);
  197. // Test read is still possible
  198. fd = open(hello_txt, O_RDONLY);
  199. TEST_ASSERT_GREATER_THAN(0, fd);
  200. status = ftruncate(fd, 10);
  201. TEST_ASSERT_EQUAL(-1, status);
  202. TEST_ASSERT_EQUAL(EROFS, errno);
  203. close(fd);
  204. f = fopen(hello_txt, "r");
  205. TEST_ASSERT_NOT_NULL(f);
  206. fread(buf, 1, sizeof(buf) - 1, f);
  207. ESP_LOGD(TAG, "Read from file: %s", buf);
  208. TEST_ASSERT_EQUAL(0, strcmp(buf, cmp_string));
  209. memset(buf, 0, sizeof(buf));
  210. char str[] = "Should not be written";
  211. fseek(f, 0, SEEK_SET);
  212. status = fwrite(str, 1, sizeof(str), f); // Writing should do nothing
  213. TEST_ASSERT_EQUAL(0, status);
  214. TEST_ASSERT_EQUAL(EBADF, errno);
  215. fread(buf, 1, sizeof(buf) - 1, f);
  216. ESP_LOGD(TAG, "Read from file: %s", buf);
  217. TEST_ASSERT_EQUAL(0, strcmp(buf, cmp_string)); // Test if the file content is still the same
  218. fclose(f);
  219. }
  220. TEST_CASE("Read-only partition - C file I/O API (using FATFS WL)", "[vfs][fatfs]")
  221. {
  222. const esp_vfs_fat_mount_config_t mount_config = {
  223. .max_files = 4,
  224. .format_if_mount_failed = false,
  225. .allocation_unit_size = CONFIG_WL_SECTOR_SIZE
  226. };
  227. esp_err_t err;
  228. int status;
  229. err = esp_vfs_fat_spiflash_mount_rw_wl(FATFS_WL_BASE_PATH, fatfs_wl_partition_name, &mount_config, &s_wl_handle);
  230. TEST_ESP_OK(err);
  231. // FATFS WL itself is read-write capable, but we are restricting it to read-only mode via esp_partition layer
  232. // Opening a file in a write mode on read-only partition is checked in vfs
  233. test_c_api_common(FATFS_WL_BASE_PATH);
  234. // Test directories
  235. DIR *dir;
  236. status = mkdir(FATFS_WL_BASE_PATH "/dir1", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
  237. TEST_ASSERT_EQUAL(-1, status);
  238. TEST_ASSERT_EQUAL(EROFS, errno);
  239. dir = opendir(FATFS_WL_BASE_PATH "/dir1");
  240. TEST_ASSERT_NULL(dir);
  241. status = rmdir(FATFS_WL_BASE_PATH "/dir");
  242. TEST_ASSERT_EQUAL(-1, status);
  243. TEST_ASSERT_EQUAL(EROFS, errno);
  244. dir = opendir(FATFS_WL_BASE_PATH "/dir");
  245. TEST_ASSERT_NOT_NULL(dir);
  246. closedir(dir);
  247. TEST_ESP_OK(esp_vfs_fat_spiflash_unmount_rw_wl(FATFS_WL_BASE_PATH, s_wl_handle));
  248. }
  249. TEST_CASE("Read-only partition - C file I/O API (using FATFS RAW)", "[vfs][fatfs]")
  250. {
  251. const esp_vfs_fat_mount_config_t mount_config = {
  252. .max_files = 4,
  253. .format_if_mount_failed = false,
  254. .allocation_unit_size = CONFIG_WL_SECTOR_SIZE
  255. };
  256. esp_err_t err;
  257. int status;
  258. err = esp_vfs_fat_spiflash_mount_ro(FATFS_RAW_BASE_PATH, fatfs_raw_partition_name, &mount_config);
  259. TEST_ESP_OK(err);
  260. // FATFS RAW is read-only itself, but esp_parition read-only adds another layer
  261. // Opening a file in a write mode on read-only partition is checked in vfs
  262. test_c_api_common(FATFS_RAW_BASE_PATH);
  263. // Test directories
  264. DIR *dir;
  265. status = mkdir(FATFS_RAW_BASE_PATH "/dir1", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
  266. TEST_ASSERT_EQUAL(-1, status);
  267. TEST_ASSERT_EQUAL(EROFS, errno);
  268. dir = opendir(FATFS_RAW_BASE_PATH "/dir1");
  269. TEST_ASSERT_NULL(dir);
  270. status = rmdir(FATFS_RAW_BASE_PATH "/dir");
  271. TEST_ASSERT_EQUAL(-1, status);
  272. TEST_ASSERT_EQUAL(EROFS, errno);
  273. dir = opendir(FATFS_RAW_BASE_PATH "/dir");
  274. TEST_ASSERT_NOT_NULL(dir);
  275. closedir(dir);
  276. TEST_ESP_OK(esp_vfs_fat_spiflash_unmount_ro(FATFS_RAW_BASE_PATH, fatfs_raw_partition_name));
  277. }
  278. TEST_CASE("Read-only partition - C file I/O API (using SPIFFS)", "[vfs][spiffs]")
  279. {
  280. esp_vfs_spiffs_conf_t conf = {
  281. .base_path = SPIFFS_BASE_PATH,
  282. .partition_label = spiffs_partition_name,
  283. .max_files = 5,
  284. .format_if_mount_failed = false
  285. };
  286. esp_err_t err;
  287. err = esp_vfs_spiffs_register(&conf);
  288. TEST_ESP_OK(err);
  289. // SPIFFS is read-write capable, but we are restricting it to read-only mode via esp_partition layer
  290. test_c_api_common(SPIFFS_BASE_PATH);
  291. // SPIFFS doesn't support directories
  292. TEST_ESP_OK(esp_vfs_spiffs_unregister(spiffs_partition_name));
  293. }