1 /*
2  * Copyright (c) 2024 Espressif Systems (Shanghai) Co., Ltd.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/device.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/spinlock.h>
10 #include <zephyr/kernel_structs.h>
11 #include <zephyr/storage/flash_map.h>
12 #include <zephyr/drivers/interrupt_controller/intc_esp32.h>
13 
14 #include <soc.h>
15 #include <esp_cpu.h>
16 #include "esp_rom_uart.h"
17 
18 #include "esp_mcuboot_image.h"
19 #include "esp_memory_utils.h"
20 
21 /* AMP support */
22 #ifdef CONFIG_SOC_ENABLE_APPCPU
23 
24 #include "bootloader_flash_priv.h"
25 
26 #define sys_mmap   bootloader_mmap
27 #define sys_munmap bootloader_munmap
28 
esp_appcpu_start(void * entry_point)29 void esp_appcpu_start(void *entry_point)
30 {
31 	esp_cpu_unstall(1);
32 
33 	if (!REG_GET_BIT(SYSTEM_CORE_1_CONTROL_0_REG, SYSTEM_CONTROL_CORE_1_CLKGATE_EN)) {
34 		REG_SET_BIT(SYSTEM_CORE_1_CONTROL_0_REG, SYSTEM_CONTROL_CORE_1_CLKGATE_EN);
35 		REG_CLR_BIT(SYSTEM_CORE_1_CONTROL_0_REG, SYSTEM_CONTROL_CORE_1_RUNSTALL);
36 		REG_SET_BIT(SYSTEM_CORE_1_CONTROL_0_REG, SYSTEM_CONTROL_CORE_1_RESETTING);
37 		REG_CLR_BIT(SYSTEM_CORE_1_CONTROL_0_REG, SYSTEM_CONTROL_CORE_1_RESETTING);
38 	}
39 
40 	esp_rom_ets_set_appcpu_boot_addr((void *)entry_point);
41 
42 	esp_cpu_reset(1);
43 }
44 
load_segment(uint32_t src_addr,uint32_t src_len,uint32_t dst_addr)45 static int load_segment(uint32_t src_addr, uint32_t src_len, uint32_t dst_addr)
46 {
47 	const uint32_t *data = (const uint32_t *)sys_mmap(src_addr, src_len);
48 
49 	if (!data) {
50 		ets_printf("%s: mmap failed", __func__);
51 		return -1;
52 	}
53 
54 	volatile uint32_t *dst = (volatile uint32_t *)dst_addr;
55 
56 	for (int i = 0; i < src_len / 4; i++) {
57 		dst[i] = data[i];
58 	}
59 
60 	sys_munmap(data);
61 
62 	return 0;
63 }
64 
esp_appcpu_image_load(unsigned int hdr_offset,unsigned int * entry_addr)65 int IRAM_ATTR esp_appcpu_image_load(unsigned int hdr_offset, unsigned int *entry_addr)
66 {
67 	const uint32_t img_off = FIXED_PARTITION_OFFSET(slot0_appcpu_partition);
68 	const uint32_t fa_size = FIXED_PARTITION_SIZE(slot0_appcpu_partition);
69 	const uint8_t fa_id = FIXED_PARTITION_ID(slot0_appcpu_partition);
70 
71 	if (entry_addr == NULL) {
72 		ets_printf("Can't return the entry address. Aborting!\n");
73 		abort();
74 		return -1;
75 	}
76 
77 	uint32_t mcuboot_header[8] = {0};
78 	esp_image_load_header_t image_header = {0};
79 
80 	const uint32_t *data = (const uint32_t *)sys_mmap(img_off, 0x40);
81 
82 	memcpy((void *)&mcuboot_header, data, sizeof(mcuboot_header));
83 	memcpy((void *)&image_header, data + (hdr_offset / sizeof(uint32_t)),
84 	       sizeof(esp_image_load_header_t));
85 
86 	sys_munmap(data);
87 
88 	if (image_header.header_magic == ESP_LOAD_HEADER_MAGIC) {
89 		ets_printf("APPCPU image, area id: %d, offset: 0x%x, hdr.off: 0x%x, size: %d kB\n",
90 			   fa_id, img_off, hdr_offset, fa_size / 1024);
91 	} else if ((image_header.header_magic & 0xff) == 0xE9) {
92 		ets_printf("ESP image format is not supported\n");
93 		abort();
94 	} else {
95 		ets_printf("Unknown or empty image detected. Aborting!\n");
96 		abort();
97 	}
98 
99 	if (!esp_ptr_in_iram((void *)image_header.iram_dest_addr) ||
100 	    !esp_ptr_in_iram((void *)(image_header.iram_dest_addr + image_header.iram_size))) {
101 		ets_printf("IRAM region in load header is not valid. Aborting");
102 		abort();
103 	}
104 
105 	if (!esp_ptr_in_dram((void *)image_header.dram_dest_addr) ||
106 	    !esp_ptr_in_dram((void *)(image_header.dram_dest_addr + image_header.dram_size))) {
107 		ets_printf("DRAM region in load header is not valid. Aborting");
108 		abort();
109 	}
110 
111 	if (!esp_ptr_in_iram((void *)image_header.entry_addr)) {
112 		ets_printf("Application entry point (%xh) is not in IRAM. Aborting",
113 			   image_header.entry_addr);
114 		abort();
115 	}
116 
117 	ets_printf("IRAM segment: paddr=%08xh, vaddr=%08xh, size=%05xh (%6d) load\n",
118 		   (img_off + image_header.iram_flash_offset), image_header.iram_dest_addr,
119 		   image_header.iram_size, image_header.iram_size);
120 
121 	load_segment(img_off + image_header.iram_flash_offset, image_header.iram_size,
122 		     image_header.iram_dest_addr);
123 
124 	ets_printf("DRAM segment: paddr=%08xh, vaddr=%08xh, size=%05xh (%6d) load\n",
125 		   (img_off + image_header.dram_flash_offset), image_header.dram_dest_addr,
126 		   image_header.dram_size, image_header.dram_size);
127 
128 	load_segment(img_off + image_header.dram_flash_offset, image_header.dram_size,
129 		     image_header.dram_dest_addr);
130 
131 	ets_printf("Application start=%xh\n\n", image_header.entry_addr);
132 	esp_rom_uart_tx_wait_idle(0);
133 
134 	assert(entry_addr != NULL);
135 	*entry_addr = image_header.entry_addr;
136 
137 	return 0;
138 }
139 
esp_appcpu_image_stop(void)140 void esp_appcpu_image_stop(void)
141 {
142 	esp_cpu_stall(1);
143 }
144 
esp_appcpu_image_start(unsigned int hdr_offset)145 void esp_appcpu_image_start(unsigned int hdr_offset)
146 {
147 	static int started;
148 	unsigned int entry_addr = 0;
149 
150 	if (started) {
151 		printk("APPCPU already started.\r\n");
152 		return;
153 	}
154 
155 	/* Input image meta header, output appcpu entry point */
156 	esp_appcpu_image_load(hdr_offset, &entry_addr);
157 
158 	esp_appcpu_start((void *)entry_addr);
159 }
160 
esp_appcpu_init(void)161 int esp_appcpu_init(void)
162 {
163 	/* Load APPCPU image using image header offset
164 	 * (skipping the MCUBoot header)
165 	 */
166 	esp_appcpu_image_start(0x20);
167 
168 	return 0;
169 }
170 #endif /* CONFIG_SOC_ENABLE_APPCPU */
171