1# Set some global esptool.py variables
2#
3# Many of these are read when generating flash_app_args & flash_project_args
4idf_build_get_property(target IDF_TARGET)
5idf_build_get_property(python PYTHON)
6idf_build_get_property(idf_path IDF_PATH)
7
8set(chip_model ${target})
9if(target STREQUAL "esp32h2")
10    set(chip_model esp32h2beta1)
11endif()
12
13set(ESPTOOLPY ${python} "$ENV{ESPTOOL_WRAPPER}" "${CMAKE_CURRENT_LIST_DIR}/esptool/esptool.py" --chip ${chip_model})
14set(ESPSECUREPY ${python} "${CMAKE_CURRENT_LIST_DIR}/esptool/espsecure.py")
15set(ESPEFUSEPY ${python} "${CMAKE_CURRENT_LIST_DIR}/esptool/espefuse.py")
16set(ESPMONITOR ${python} "${idf_path}/tools/idf_monitor.py")
17
18set(ESPFLASHMODE ${CONFIG_ESPTOOLPY_FLASHMODE})
19set(ESPFLASHFREQ ${CONFIG_ESPTOOLPY_FLASHFREQ})
20set(ESPFLASHSIZE ${CONFIG_ESPTOOLPY_FLASHSIZE})
21
22set(ESPTOOLPY_CHIP "${chip_model}")
23
24set(ESPTOOLPY_FLASH_OPTIONS
25    --flash_mode ${ESPFLASHMODE}
26    --flash_freq ${ESPFLASHFREQ}
27    --flash_size ${ESPFLASHSIZE}
28    )
29
30if(NOT BOOTLOADER_BUILD)
31    set(esptool_elf2image_args --elf-sha256-offset 0xb0)
32endif()
33
34if(NOT CONFIG_SECURE_BOOT_ALLOW_SHORT_APP_PARTITION AND
35    NOT BOOTLOADER_BUILD)
36    if(CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME)
37        list(APPEND esptool_elf2image_args --secure-pad)
38    elseif(CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME)
39        list(APPEND esptool_elf2image_args --secure-pad-v2)
40    endif()
41endif()
42
43if(CONFIG_ESP32_REV_MIN)
44    set(min_rev ${CONFIG_ESP32_REV_MIN})
45endif()
46if(CONFIG_ESP32C3_REV_MIN)
47    set(min_rev ${CONFIG_ESP32C3_REV_MIN})
48endif()
49
50if(min_rev)
51    list(APPEND esptool_elf2image_args --min-rev ${min_rev})
52    set(monitor_rev_args "--revision;${min_rev}")
53    unset(min_rev)
54endif()
55
56if(CONFIG_ESPTOOLPY_FLASHSIZE_DETECT)
57    # Set ESPFLASHSIZE to 'detect' *after* elf2image options are generated,
58    # as elf2image can't have 'detect' as an option...
59    set(ESPFLASHSIZE detect)
60
61    # Flash size detection updates the image header which would invalidate the appended
62    # SHA256 digest. Therefore, a digest is not appended in that case.
63    # This argument requires esptool>=4.1.
64    list(APPEND esptool_elf2image_args --dont-append-digest)
65endif()
66
67if(CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME)
68    set(ESPFLASHSIZE keep)
69endif()
70
71idf_build_get_property(build_dir BUILD_DIR)
72
73idf_build_get_property(elf_name EXECUTABLE_NAME GENERATOR_EXPRESSION)
74idf_build_get_property(elf EXECUTABLE GENERATOR_EXPRESSION)
75idf_build_get_property(elf_dir EXECUTABLE_DIR GENERATOR_EXPRESSION)
76
77if(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES AND NOT BOOTLOADER_BUILD)
78    set(unsigned_project_binary "${elf_name}-unsigned.bin")
79else()
80    set(unsigned_project_binary "${elf_name}.bin")
81endif()
82
83set(PROJECT_BIN "${elf_name}.bin")
84
85#
86# Add 'app.bin' target - generates with elf2image
87#
88if(CONFIG_APP_BUILD_GENERATE_BINARIES)
89    add_custom_command(OUTPUT "${build_dir}/.bin_timestamp"
90        COMMAND ${ESPTOOLPY} elf2image ${ESPTOOLPY_FLASH_OPTIONS} ${esptool_elf2image_args}
91            -o "${build_dir}/${unsigned_project_binary}" "${elf_dir}/${elf}"
92        COMMAND ${CMAKE_COMMAND} -E echo "Generated ${build_dir}/${unsigned_project_binary}"
93        COMMAND ${CMAKE_COMMAND} -E md5sum "${build_dir}/${unsigned_project_binary}" > "${build_dir}/.bin_timestamp"
94        DEPENDS ${elf}
95        VERBATIM
96        WORKING_DIRECTORY ${build_dir}
97        COMMENT "Generating binary image from built executable"
98        )
99    add_custom_target(gen_project_binary DEPENDS "${build_dir}/.bin_timestamp")
100endif()
101
102set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
103    APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES
104    "${build_dir}/${unsigned_project_binary}"
105    )
106
107if(CONFIG_APP_BUILD_GENERATE_BINARIES)
108    add_custom_target(app ALL DEPENDS gen_project_binary)
109endif()
110
111if(CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME)
112    set(secure_boot_version "1")
113elseif(CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME)
114    set(secure_boot_version "2")
115endif()
116
117if(NOT BOOTLOADER_BUILD AND CONFIG_SECURE_SIGNED_APPS)
118    if(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES)
119        # for locally signed secure boot image, add a signing step to get from unsigned app to signed app
120        add_custom_command(OUTPUT "${build_dir}/.signed_bin_timestamp"
121            COMMAND ${ESPSECUREPY} sign_data --version ${secure_boot_version} --keyfile ${secure_boot_signing_key}
122                -o "${build_dir}/${PROJECT_BIN}" "${build_dir}/${unsigned_project_binary}"
123            COMMAND ${CMAKE_COMMAND} -E echo "Generated signed binary image ${build_dir}/${PROJECT_BIN}"
124                                    "from ${build_dir}/${unsigned_project_binary}"
125            COMMAND ${CMAKE_COMMAND} -E md5sum "${build_dir}/${PROJECT_BIN}" > "${build_dir}/.signed_bin_timestamp"
126            DEPENDS "${build_dir}/.bin_timestamp"
127            VERBATIM
128            COMMENT "Generating signed binary image"
129            )
130        add_custom_target(gen_signed_project_binary DEPENDS "${build_dir}/.signed_bin_timestamp")
131        add_dependencies(gen_project_binary gen_signed_project_binary)
132
133        set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
134            APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES
135            "${build_dir}/${PROJECT_BIN}"
136            )
137    else()
138        string(REPLACE ";" " " espsecurepy "${ESPSECUREPY}")
139        add_custom_command(TARGET app POST_BUILD
140            COMMAND ${CMAKE_COMMAND} -E echo
141                "App built but not signed. Sign app before flashing"
142            COMMAND ${CMAKE_COMMAND} -E echo
143                "\t${espsecurepy} sign_data --keyfile KEYFILE --version ${secure_boot_version} \
144                ${build_dir}/${PROJECT_BIN}"
145            VERBATIM)
146    endif()
147endif()
148
149add_custom_target(erase_flash
150    COMMAND ${CMAKE_COMMAND}
151    -D IDF_PATH="${idf_path}"
152    -D SERIAL_TOOL="${ESPTOOLPY}"
153    -D SERIAL_TOOL_ARGS="erase_flash"
154    -P run_serial_tool.cmake
155    WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
156    USES_TERMINAL
157    )
158
159add_custom_target(monitor
160    COMMAND ${CMAKE_COMMAND}
161    -D IDF_PATH="${idf_path}"
162    -D SERIAL_TOOL="${ESPMONITOR}"
163    -D SERIAL_TOOL_ARGS="--target;${target};${monitor_rev_args};${elf_dir}/${elf}"
164    -D WORKING_DIRECTORY="${build_dir}"
165    -P run_serial_tool.cmake
166    WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
167    USES_TERMINAL
168    )
169
170set(esptool_flash_main_args "--before=${CONFIG_ESPTOOLPY_BEFORE}")
171
172if(CONFIG_SECURE_BOOT OR CONFIG_SECURE_FLASH_ENC_ENABLED)
173    # If security enabled then override post flash option
174    list(APPEND esptool_flash_main_args "--after=no_reset")
175else()
176    list(APPEND esptool_flash_main_args "--after=${CONFIG_ESPTOOLPY_AFTER}")
177endif()
178
179if(CONFIG_ESPTOOLPY_NO_STUB)
180    list(APPEND esptool_flash_main_args "--no-stub")
181endif()
182
183idf_component_set_property(esptool_py FLASH_ARGS "${esptool_flash_main_args}")
184idf_component_set_property(esptool_py FLASH_SUB_ARGS "${ESPTOOLPY_FLASH_OPTIONS}")
185
186function(esptool_py_partition_needs_encryption retencrypted partition_name)
187    # Check if encryption is enabled
188    if(CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT)
189        # Encryption is enabled, get partition type, subtype and encrypted flag
190        # to determine whether it needs encryption or not.
191        partition_table_get_partition_info(type "--partition-name ${partition_name}" "type")
192        partition_table_get_partition_info(subtype "--partition-name ${partition_name}" "subtype")
193        partition_table_get_partition_info(encrypted "--partition-name ${partition_name}" "encrypted")
194
195        # As defined in gen_esp32part.py file:
196        # Types:
197        #   - APP  0x00
198        #   - DATA 0x01
199        # Subtypes:
200        #   - ota      0x00
201        #   - nvs      0x02
202        # If the partition is an app, an OTA or an NVS partition, then it should
203        # be encrypted
204        if(
205                (${type} EQUAL 0) OR
206                (${type} EQUAL 1 AND ${subtype} EQUAL 0) OR
207                (${type} EQUAL 1 AND ${subtype} EQUAL 2)
208          )
209            set(encrypted TRUE)
210        endif()
211
212        # Return 'encrypted' value to the caller
213        set(${retencrypted} ${encrypted} PARENT_SCOPE)
214    else()
215        # Encryption not enabled, return false
216        set(${retencrypted} FALSE PARENT_SCOPE)
217    endif()
218
219endfunction()
220
221function(esptool_py_flash_to_partition target_name partition_name binary_path)
222    # Retrieve the offset for the partition to flash the image on
223    partition_table_get_partition_info(offset "--partition-name ${partition_name}" "offset")
224
225    if(NOT offset)
226        message(FATAL_ERROR "Could not find offset of partition ${partition_name}")
227    endif()
228
229    # Check whether the partition needs encryption or not
230    esptool_py_partition_needs_encryption(encrypted ${partition_name})
231
232    # If the image should not be encrypted, we pass the option
233    # ALWAYS_PLAINTEXT to the function esptool_py_flash_target_image
234    if(NOT ${encrypted})
235        set(option ALWAYS_PLAINTEXT)
236    endif()
237
238    # The image name is also the partition name
239    esptool_py_flash_target_image(${target_name} ${partition_name} ${offset}
240                                  ${binary_path} ${option})
241endfunction()
242
243# This function takes a fifth optional named parameter: "ALWAYS_PLAINTEXT". As
244# its name states, it marks whether the image should be flashed as plain text or
245# not. If build macro CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT is set and
246# this parameter is provided, then the image will be flahsed as plain text
247# (not encrypted) on the target. This parameter will be ignored if build macro
248# CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT is not set.
249function(esptool_py_flash_target_image target_name image_name offset image)
250    set(options ALWAYS_PLAINTEXT)
251    idf_build_get_property(build_dir BUILD_DIR)
252    file(RELATIVE_PATH image ${build_dir} ${image})
253    cmake_parse_arguments(arg "${options}" "" "" "${ARGN}")
254
255    # Check if the image has to be plain text or not, depending on the macro
256    # CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT and the parameter
257    # ALWAYS_PLAINTEXT
258    set(encrypted false)
259    if(CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT AND
260       NOT arg_ALWAYS_PLAINTEXT)
261        set(encrypted true)
262    endif()
263
264    # In the following snippet of code, some properties are defined for our
265    # current target. These properties will be used to generate the actual
266    # target_name_args files using the target_name_args.in files.
267    # Please see function esptool_py_flash_target above for more information
268    # about these properties and how they are used.
269
270    # Add the image file, with its offset, to the list of files to
271    # flash to the target. No matter whether flash encryption is
272    # enabled or not, plain binaries (non-encrypted) need to be
273    # generated
274    set_property(TARGET ${target_name} APPEND PROPERTY FLASH_FILE
275                "\"${offset}\" : \"${image}\"")
276    set_property(TARGET ${target_name} APPEND PROPERTY FLASH_ENTRY
277                "\"${image_name}\" : { \"offset\" : \"${offset}\", \"file\" : \"${image}\",\
278 \"encrypted\" : \"${encrypted}\" }")
279    set_property(TARGET ${target_name} APPEND PROPERTY IMAGES "${offset} ${image}")
280
281    if(CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT)
282        # When flash encryption mode is enabled, if the current binary needs to
283        # be encrypted, do the same as previously but prefixing target names
284        # with "encrypted-".
285        if(encrypted)
286            set_property(TARGET encrypted-${target_name} APPEND PROPERTY FLASH_FILE
287                         "\"${offset}\" : \"${image}\"")
288            set_property(TARGET encrypted-${target_name} APPEND PROPERTY FLASH_ENTRY
289                         "\"${image_name}\" : { \"offset\" : \"${offset}\", \"file\" : \"${image}\" , \
290\"encrypted\" : \"${encrypted}\"  }")
291            set_property(TARGET encrypted-${target_name} APPEND PROPERTY ENCRYPTED_IMAGES "${offset} ${image}")
292        else()
293            # The target doesn't need to be encrypted, thus, add the current
294            # file to the NON_ENCRYPTED_IMAGES property
295            set_property(TARGET encrypted-${target_name} APPEND PROPERTY NON_ENCRYPTED_IMAGES "${offset} ${image}")
296        endif()
297    endif()
298endfunction()
299
300# Use this function to generate a ternary expression that will be evaluated.
301# - retexpr is the expression returned by the function
302# - condition is the expression evaluated to a boolean
303# - condtrue is the expression to evaluate if condition is true
304# - condfalse is the expression to evaluate if condition is false
305# This function can be summarized as:
306#   retexpr = condition ? condtrue : condfalse
307function(if_expr_generator retexpr condition condtrue condfalse)
308    # CMake version 3.8 and above provide a ternary operator for expression
309    # generator. For version under, we must simulate this behaviour
310    if(${CMAKE_VERSION} VERSION_LESS "3.8.0")
311
312        # If condtrue is not empty, then we have to do something in case the
313        # condition is true. Generate the expression that will be used in that
314        # case
315        if(condtrue)
316            set(iftrue "$<${condition}:${condtrue}>")
317        endif()
318
319        # Same for condfalse. If it is empty, it is useless to create an
320        # expression that will be evaluated later
321        if(condfalse)
322            set(iffalse "$<$<NOT:${condition}>:${condfalse}>")
323        endif()
324
325        # Concatenate the previously generated expressions. If one of them was
326        # not initialized (because of empty condtrue/condfalse) it will be
327        # replaced by an empty string
328        set(${retexpr} "${iftrue}${iffalse}" PARENT_SCOPE)
329
330    else()
331        # CMake 3.8 and above implement what we want, making the expression
332        # simpler
333        set(${retexpr} "$<IF:${condition},${condtrue},${condfalse}>" PARENT_SCOPE)
334    endif()
335endfunction()
336
337
338function(esptool_py_flash_target target_name main_args sub_args)
339    set(single_value OFFSET IMAGE) # template file to use to be able to
340                                   # flash the image individually using esptool
341    set(options ALWAYS_PLAINTEXT)
342    cmake_parse_arguments(_ "${options}" "${single_value}" "" "${ARGN}")
343
344    idf_build_get_property(idf_path IDF_PATH)
345    idf_build_get_property(build_dir BUILD_DIR)
346    idf_component_get_property(esptool_py_dir esptool_py COMPONENT_DIR)
347
348    add_custom_target(${target_name}
349        COMMAND ${CMAKE_COMMAND}
350        -D IDF_PATH="${idf_path}"
351        -D SERIAL_TOOL="${ESPTOOLPY}"
352        -D SERIAL_TOOL_ARGS="${main_args};write_flash;@${target_name}_args"
353        -D WORKING_DIRECTORY="${build_dir}"
354        -P ${esptool_py_dir}/run_serial_tool.cmake
355        WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
356        USES_TERMINAL
357        )
358
359    set_target_properties(${target_name} PROPERTIES SUB_ARGS "${sub_args}")
360
361    # Create the expression that contains the list of file names to pass
362    # to esptool script
363    set(flash_args_content "$<JOIN:$<TARGET_PROPERTY:${target_name},SUB_ARGS>, >\n\
364$<JOIN:$<TARGET_PROPERTY:${target_name},IMAGES>,\n>")
365
366    # Write the previous expression to the target_name_args.in file
367    file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${target_name}_args.in"
368         CONTENT "${flash_args_content}")
369
370    # Generate the actual expression value from the content of the file
371    # we just wrote
372    file(GENERATE OUTPUT "${build_dir}/${target_name}_args"
373                INPUT "${CMAKE_CURRENT_BINARY_DIR}/${target_name}_args.in")
374
375    # Check if the target has to be plain text or not, depending on the macro
376    # CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT and the parameter
377    # ALWAYS_PLAINTEXT
378    set(encrypted FALSE)
379    if(CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT AND
380       NOT __ALWAYS_PLAINTEXT)
381        set(encrypted TRUE)
382    endif()
383
384    # If the file needs to be encrypted, create a target file that lets the user
385    # flash this partition independently from other files.
386    # For example, if 'target_name' is app-flash and 'encrypted' is TRUE,
387    # 'build' directory will contain a file name 'encrypted_app-flash_args'
388    if(${encrypted})
389        add_custom_target(encrypted-${target_name}
390            COMMAND ${CMAKE_COMMAND}
391            -D IDF_PATH="${idf_path}"
392            -D SERIAL_TOOL="${ESPTOOLPY}"
393            -D SERIAL_TOOL_ARGS="${main_args};write_flash;@encrypted_${target_name}_args"
394            -D WORKING_DIRECTORY="${build_dir}"
395            -P ${esptool_py_dir}/run_serial_tool.cmake
396            WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
397            USES_TERMINAL
398            )
399
400        # Generate the parameters for esptool.py command
401        # In case we have both non encrypted and encrypted files to flash, we
402        # can use --encrypt-files parameter to specify which ones should be
403        # encrypted.
404        # If we only have encrypted images to flash, we must use legacy
405        # --encrypt parameter.
406        # As the properties ENCRYPTED_IMAGES and NON_ENCRYPTED_IMAGES have not
407        # been geenrated yet, we must use CMake expression generator to test
408        # which esptool.py options we can use.
409
410        # The variable has_non_encrypted_image will be evaluated to "1" if some
411        # images must not be encrypted. This variable will be used in the next
412        # expression
413        set(has_non_encrypted_images "$<BOOL:$<TARGET_PROPERTY:\
414encrypted-${target_name},NON_ENCRYPTED_IMAGES>>")
415
416        # Prepare esptool arguments (--encrypt or --encrypt-files)
417        if_expr_generator(if_non_enc_expr ${has_non_encrypted_images}
418                          "" "--encrypt")
419        set_target_properties(encrypted-${target_name} PROPERTIES SUB_ARGS
420                             "${sub_args}; ${if_non_enc_expr}")
421
422        # Generate the list of files to pass to esptool
423        set(encrypted_files "$<JOIN:$<TARGET_PROPERTY\
424:encrypted-${target_name},ENCRYPTED_IMAGES>,\n>")
425        set(non_encrypted_files "$<JOIN:$<TARGET_PROPERTY:\
426encrypted-${target_name},NON_ENCRYPTED_IMAGES>,\n>")
427
428        # Put both lists together, use --encrypted-files if we do also have
429        # plain images to flash
430        if_expr_generator(if_enc_expr ${has_non_encrypted_images}
431                          "--encrypt-files\n" "")
432        set(flash_args_content "$<JOIN:$<TARGET_PROPERTY:\
433encrypted-${target_name},SUB_ARGS>, >\
434${non_encrypted_files}\n\
435${if_enc_expr}\
436${encrypted_files}")
437
438        # The expression is ready to be geenrated, write it to the file which
439        # extension is .in
440        file_generate("${CMAKE_CURRENT_BINARY_DIR}/encrypted_${target_name}_args.in"
441                      CONTENT "${flash_args_content}")
442
443        # Generate the actual string from the content of the file we just wrote
444        file_generate("${build_dir}/encrypted_${target_name}_args"
445                      INPUT "${CMAKE_CURRENT_BINARY_DIR}/encrypted_${target_name}_args.in")
446    else()
447        fail_target(encrypted-${target_name} "Error: The target encrypted-${target_name} requires"
448                    "CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT to be enabled.")
449
450    endif()
451endfunction()
452
453
454function(esptool_py_custom_target target_name flasher_filename dependencies)
455    idf_component_get_property(main_args esptool_py FLASH_ARGS)
456    idf_component_get_property(sub_args esptool_py FLASH_SUB_ARGS)
457
458    idf_build_get_property(build_dir BUILD_DIR)
459
460    esptool_py_flash_target(${target_name} "${main_args}" "${sub_args}")
461
462    # Copy the file to flash_xxx_args for compatibility for select target
463    file_generate("${build_dir}/flash_${flasher_filename}_args"
464                INPUT "${build_dir}/${target_name}_args")
465
466    add_dependencies(${target_name} ${dependencies})
467
468    if(CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT)
469        file_generate("${build_dir}/flash_encrypted_${flasher_filename}_args"
470                    INPUT "${build_dir}/encrypted_${target_name}_args")
471
472        add_dependencies(encrypted-${target_name} ${dependencies})
473    endif()
474endfunction()
475
476if(NOT BOOTLOADER_BUILD)
477    set(flash_deps "partition_table_bin")
478
479    if(CONFIG_APP_BUILD_GENERATE_BINARIES)
480        list(APPEND flash_deps "app")
481    endif()
482
483    if(CONFIG_APP_BUILD_BOOTLOADER)
484        list(APPEND flash_deps "bootloader")
485    endif()
486
487    esptool_py_custom_target(flash project "${flash_deps}")
488endif()
489