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_log.h>
16 #include <esp_cpu.h>
17 #include "esp_rom_uart.h"
18 
19 #include "esp_mcuboot_image.h"
20 #include "esp_memory_utils.h"
21 #include "hw_init.h"
22 
23 #define TAG "amp"
24 
25 /* AMP support */
26 #ifdef CONFIG_SOC_ENABLE_APPCPU
27 
28 #include "bootloader_flash_priv.h"
29 
30 #define sys_mmap   bootloader_mmap
31 #define sys_munmap bootloader_munmap
32 
esp_appcpu_start(void * entry_point)33 void esp_appcpu_start(void *entry_point)
34 {
35 	esp_cpu_unstall(1);
36 
37 	if (!REG_GET_BIT(SYSTEM_CORE_1_CONTROL_0_REG, SYSTEM_CONTROL_CORE_1_CLKGATE_EN)) {
38 		REG_SET_BIT(SYSTEM_CORE_1_CONTROL_0_REG, SYSTEM_CONTROL_CORE_1_CLKGATE_EN);
39 		REG_CLR_BIT(SYSTEM_CORE_1_CONTROL_0_REG, SYSTEM_CONTROL_CORE_1_RUNSTALL);
40 		REG_SET_BIT(SYSTEM_CORE_1_CONTROL_0_REG, SYSTEM_CONTROL_CORE_1_RESETTING);
41 		REG_CLR_BIT(SYSTEM_CORE_1_CONTROL_0_REG, SYSTEM_CONTROL_CORE_1_RESETTING);
42 	}
43 
44 	esp_rom_ets_set_appcpu_boot_addr((void *)entry_point);
45 
46 	esp_cpu_reset(1);
47 }
48 
load_segment(uint32_t src_addr,uint32_t src_len,uint32_t dst_addr)49 static int load_segment(uint32_t src_addr, uint32_t src_len, uint32_t dst_addr)
50 {
51 	const uint32_t *data = (const uint32_t *)sys_mmap(src_addr, src_len);
52 
53 	if (!data) {
54 		ESP_EARLY_LOGE(TAG, "%s: mmap failed", __func__);
55 		return -1;
56 	}
57 
58 	volatile uint32_t *dst = (volatile uint32_t *)dst_addr;
59 
60 	for (int i = 0; i < src_len / 4; i++) {
61 		dst[i] = data[i];
62 	}
63 
64 	sys_munmap(data);
65 
66 	return 0;
67 }
68 
esp_appcpu_image_load(unsigned int hdr_offset,unsigned int * entry_addr)69 int IRAM_ATTR esp_appcpu_image_load(unsigned int hdr_offset, unsigned int *entry_addr)
70 {
71 	const uint32_t fa_offset = FIXED_PARTITION_OFFSET(slot0_appcpu_partition);
72 	const uint32_t fa_size = FIXED_PARTITION_SIZE(slot0_appcpu_partition);
73 	const uint8_t fa_id = FIXED_PARTITION_ID(slot0_appcpu_partition);
74 
75 	if (entry_addr == NULL) {
76 		ESP_EARLY_LOGE(TAG, "Can't return the entry address. Aborting!");
77 		abort();
78 		return -1;
79 	}
80 
81 	uint32_t mcuboot_header[8] = {0};
82 	esp_image_load_header_t image_header = {0};
83 
84 	const uint32_t *data = (const uint32_t *)sys_mmap(fa_offset, 0x80);
85 
86 	memcpy((void *)&mcuboot_header, data, sizeof(mcuboot_header));
87 	memcpy((void *)&image_header, data + (hdr_offset / sizeof(uint32_t)),
88 	       sizeof(esp_image_load_header_t));
89 
90 	sys_munmap(data);
91 
92 	if (image_header.header_magic == ESP_LOAD_HEADER_MAGIC) {
93 		ESP_EARLY_LOGI(TAG,
94 			"APPCPU image, area id: %d, offset: 0x%x, hdr.off: 0x%x, size: %d kB",
95 			fa_id, fa_offset, hdr_offset, fa_size / 1024);
96 	} else if ((image_header.header_magic & 0xff) == 0xE9) {
97 		ESP_EARLY_LOGE(TAG, "ESP image format is not supported");
98 		abort();
99 	} else {
100 		ESP_EARLY_LOGE(TAG, "Unknown or empty image detected. Aborting!");
101 		abort();
102 	}
103 
104 	if (!esp_ptr_in_iram((void *)image_header.iram_dest_addr) ||
105 	    !esp_ptr_in_iram((void *)(image_header.iram_dest_addr + image_header.iram_size))) {
106 		ESP_EARLY_LOGE(TAG, "IRAM region in load header is not valid. Aborting");
107 		abort();
108 	}
109 
110 	if (!esp_ptr_in_dram((void *)image_header.dram_dest_addr) ||
111 	    !esp_ptr_in_dram((void *)(image_header.dram_dest_addr + image_header.dram_size))) {
112 		ESP_EARLY_LOGE(TAG, "DRAM region in load header is not valid. Aborting");
113 		abort();
114 	}
115 
116 	if (!esp_ptr_in_iram((void *)image_header.entry_addr)) {
117 		ESP_EARLY_LOGE(TAG, "Application entry point (%xh) is not in IRAM. Aborting",
118 			   image_header.entry_addr);
119 		abort();
120 	}
121 
122 	ESP_EARLY_LOGI(TAG, "IRAM segment: paddr=%08xh, vaddr=%08xh, size=%05xh (%6d) load",
123 		   (fa_offset + image_header.iram_flash_offset), image_header.iram_dest_addr,
124 		   image_header.iram_size, image_header.iram_size);
125 
126 	load_segment(fa_offset + image_header.iram_flash_offset, image_header.iram_size,
127 		     image_header.iram_dest_addr);
128 
129 	ESP_EARLY_LOGI(TAG, "DRAM segment: paddr=%08xh, vaddr=%08xh, size=%05xh (%6d) load",
130 		   (fa_offset + image_header.dram_flash_offset), image_header.dram_dest_addr,
131 		   image_header.dram_size, image_header.dram_size);
132 
133 	load_segment(fa_offset + image_header.dram_flash_offset, image_header.dram_size,
134 		     image_header.dram_dest_addr);
135 
136 	ESP_EARLY_LOGI(TAG, "IROM segment: paddr=%08xh, vaddr=%08xh, size=%05xh (%6d) map",
137 		   (fa_offset + image_header.irom_flash_offset), image_header.irom_map_addr,
138 		   image_header.irom_size, image_header.irom_size);
139 
140 	ESP_EARLY_LOGI(TAG, "DROM segment: paddr=%08xh, vaddr=%08xh, size=%05xh (%6d) map",
141 		   (fa_offset + image_header.drom_flash_offset), image_header.drom_map_addr,
142 		   image_header.drom_size, image_header.drom_size);
143 
144 	struct rom_segments rom = {
145 		image_header.drom_map_addr,
146 		image_header.drom_flash_offset + fa_offset,
147 		image_header.drom_size,
148 		image_header.irom_map_addr,
149 		image_header.irom_flash_offset + fa_offset,
150 		image_header.irom_size,
151 	};
152 
153 	map_rom_segments(1, &rom);
154 
155 	ESP_EARLY_LOGI(TAG, "Application start=%xh\n\n", image_header.entry_addr);
156 	esp_rom_uart_tx_wait_idle(0);
157 
158 	assert(entry_addr != NULL);
159 	*entry_addr = image_header.entry_addr;
160 
161 	return 0;
162 }
163 
esp_appcpu_image_stop(void)164 void esp_appcpu_image_stop(void)
165 {
166 	esp_cpu_stall(1);
167 }
168 
esp_appcpu_image_start(unsigned int hdr_offset)169 void esp_appcpu_image_start(unsigned int hdr_offset)
170 {
171 	static int started;
172 	unsigned int entry_addr = 0;
173 
174 	if (started) {
175 		printk("APPCPU already started.\r\n");
176 		return;
177 	}
178 
179 	/* Input image meta header, output appcpu entry point */
180 	esp_appcpu_image_load(hdr_offset, &entry_addr);
181 
182 	esp_appcpu_start((void *)entry_addr);
183 }
184 
esp_appcpu_init(void)185 int esp_appcpu_init(void)
186 {
187 	/* Load APPCPU image using image header offset
188 	 * (skipping the MCUBoot header)
189 	 */
190 	esp_appcpu_image_start(0x20);
191 
192 	return 0;
193 }
194 
195 #if !defined(CONFIG_MCUBOOT)
196 extern int esp_appcpu_init(void);
197 SYS_INIT(esp_appcpu_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
198 #endif
199 
200 #endif /* CONFIG_SOC_ENABLE_APPCPU */
201