blob: aae7393aeef63a69862f3d4a46e36dbff1f09d76 [file] [log] [blame]
Luka Perkovd131ad62012-05-27 11:44:51 +00001/*
Stefan Roese84899e22014-10-22 12:13:21 +02002 * Boot a Marvell SoC, with Xmodem over UART0.
Pali Rohár0b5909d2022-03-02 11:49:26 +01003 * supports Kirkwood, Dove, Avanta, Armada 370, Armada XP, Armada 375,
4 * Armada 38x and Armada 39x.
Luka Perkovd131ad62012-05-27 11:44:51 +00005 *
6 * (c) 2012 Daniel Stodden <daniel.stodden@gmail.com>
Pali Rohárcf8c9322021-09-24 23:07:14 +02007 * (c) 2021 Pali Rohár <pali@kernel.org>
Marek Behún61143f72022-06-01 17:17:06 +02008 * (c) 2021 Marek Behún <kabel@kernel.org>
Luka Perkovd131ad62012-05-27 11:44:51 +00009 *
Pali Rohárf4fa9622022-03-02 11:49:27 +010010 * References:
11 * - "88F6180, 88F6190, 88F6192, and 88F6281: Integrated Controller: Functional
12 * Specifications" December 2, 2008. Chapter 24.2 "BootROM Firmware".
13 * https://web.archive.org/web/20130730091033/https://www.marvell.com/embedded-processors/kirkwood/assets/FS_88F6180_9x_6281_OpenSource.pdf
14 * - "88AP510: High-Performance SoC with Integrated CPU, 2D/3D Graphics
15 * Processor, and High-Definition Video Decoder: Functional Specifications"
16 * August 3, 2011. Chapter 5 "BootROM Firmware"
17 * https://web.archive.org/web/20120130172443/https://www.marvell.com/application-processors/armada-500/assets/Armada-510-Functional-Spec.pdf
Pali Rohár29b92bb2023-01-08 13:34:24 +010018 * - "88F6665, 88F6660, 88F6658, 88F6655, 88F6655F, 88F6650, 88F6650F, 88F6610,
19 * and 88F6610F Avanta LP Family Integrated Single/Dual CPU Ecosystem for
20 * Gateway (GW), Home Gateway Unit (HGU), and Single Family Unit (SFU)
21 * Functional Specifications" Doc. No. MV-S108952-00, Rev. A. November 7, 2013.
22 * Chapter 7 "Boot Flow"
23 * CONFIDENTIAL, no public documentation available
Pali Rohárf4fa9622022-03-02 11:49:27 +010024 * - "88F6710, 88F6707, and 88F6W11: ARMADA(R) 370 SoC: Functional Specifications"
25 * May 26, 2014. Chapter 6 "BootROM Firmware".
26 * https://web.archive.org/web/20140617183701/https://www.marvell.com/embedded-processors/armada-300/assets/ARMADA370-FunctionalSpec-datasheet.pdf
27 * - "MV78230, MV78260, and MV78460: ARMADA(R) XP Family of Highly Integrated
28 * Multi-Core ARMv7 Based SoC Processors: Functional Specifications"
29 * May 29, 2014. Chapter 6 "BootROM Firmware".
30 * https://web.archive.org/web/20180829171131/https://www.marvell.com/embedded-processors/armada-xp/assets/ARMADA-XP-Functional-SpecDatasheet.pdf
Pali Rohár29b92bb2023-01-08 13:34:24 +010031 * - "BobCat2 Control and Management Subsystem Functional Specifications"
32 * Doc. No. MV-S109400-00, Rev. A. December 4, 2014.
33 * Chapter 1.6 BootROM Firmware
34 * CONFIDENTIAL, no public documentation available
35 * - "AlleyCat3 and PONCat3 Highly Integrated 1/10 Gigabit Ethernet Switch
36 * Control and Management Subsystem: Functional Specifications"
37 * Doc. No. MV-S109693-00, Rev. A. May 20, 2014.
38 * Chapter 1.6 BootROM Firmware
39 * CONFIDENTIAL, no public documentation available
Pali Rohárf4fa9622022-03-02 11:49:27 +010040 * - "ARMADA(R) 375 Value-Performance Dual Core CPU System on Chip: Functional
41 * Specifications" Doc. No. MV-S109377-00, Rev. A. September 18, 2013.
42 * Chapter 7 "Boot Sequence"
43 * CONFIDENTIAL, no public documentation available
44 * - "88F6810, 88F6811, 88F6821, 88F6W21, 88F6820, and 88F6828: ARMADA(R) 38x
45 * Family High-Performance Single/Dual CPU System on Chip: Functional
46 * Specifications" Doc. No. MV-S109094-00, Rev. C. August 2, 2015.
47 * Chapter 7 "Boot Flow"
48 * CONFIDENTIAL, no public documentation available
49 * - "88F6920, 88F6925 and 88F6928: ARMADA(R) 39x High-Performance Dual Core CPU
50 * System on Chip Functional Specifications" Doc. No. MV-S109896-00, Rev. B.
51 * December 22, 2015. Chapter 7 "Boot Flow"
52 * CONFIDENTIAL, no public documentation available
Pali Rohár29b92bb2023-01-08 13:34:24 +010053 * - "Marvell boot image parser", Marvell U-Boot 2013.01, version 18.06. September 17, 2015.
54 * https://github.com/MarvellEmbeddedProcessors/u-boot-marvell/blob/u-boot-2013.01-armada-18.06/tools/marvell/doimage_mv/hdrparser.c
55 * - "Marvell doimage Tool", Marvell U-Boot 2013.01, version 18.06. August 30, 2015.
56 * https://github.com/MarvellEmbeddedProcessors/u-boot-marvell/blob/u-boot-2013.01-armada-18.06/tools/marvell/doimage_mv/doimage.c
Luka Perkovd131ad62012-05-27 11:44:51 +000057 */
58
Stefan Roesef4db6c92016-01-07 14:12:04 +010059#include "kwbimage.h"
60#include "mkimage.h"
Pali Rohára050a862021-09-24 23:06:42 +020061#include "version.h"
Stefan Roesef4db6c92016-01-07 14:12:04 +010062
Luka Perkovd131ad62012-05-27 11:44:51 +000063#include <stdlib.h>
64#include <stdio.h>
65#include <string.h>
66#include <stdarg.h>
Stefan Roesef4db6c92016-01-07 14:12:04 +010067#include <image.h>
Luka Perkovd131ad62012-05-27 11:44:51 +000068#include <libgen.h>
69#include <fcntl.h>
70#include <errno.h>
71#include <unistd.h>
72#include <stdint.h>
Marek Behún12df7b72021-09-24 23:06:52 +020073#include <time.h>
Luka Perkovd131ad62012-05-27 11:44:51 +000074#include <sys/stat.h>
Pali Rohár913866a2022-03-02 11:49:21 +010075#include <pthread.h>
Luka Perkovd131ad62012-05-27 11:44:51 +000076
Pali Rohár93b55632021-09-24 23:07:06 +020077#ifdef __linux__
78#include "termios_linux.h"
79#else
80#include <termios.h>
81#endif
82
Luka Perkovd131ad62012-05-27 11:44:51 +000083/*
Pali Roháre8d26e82022-03-02 11:49:23 +010084 * These functions are in <term.h> header file, but this header file conflicts
85 * with "termios_linux.h" header file. So declare these functions manually.
86 */
87extern int setupterm(const char *, int, int *);
88extern char *tigetstr(const char *);
89
90/*
Luka Perkovd131ad62012-05-27 11:44:51 +000091 * Marvell BootROM UART Sensing
92 */
93
94static unsigned char kwboot_msg_boot[] = {
95 0xBB, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77
96};
97
Stefan Roese84899e22014-10-22 12:13:21 +020098static unsigned char kwboot_msg_debug[] = {
99 0xDD, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77
100};
101
102/* Defines known to work on Kirkwood */
Luka Perkovd131ad62012-05-27 11:44:51 +0000103#define KWBOOT_MSG_RSP_TIMEO 50 /* ms */
104
Stefan Roese84899e22014-10-22 12:13:21 +0200105/* Defines known to work on Armada XP */
Stefan Roeseca076d92022-08-19 09:43:59 +0200106#define KWBOOT_MSG_RSP_TIMEO_AXP 10 /* ms */
Stefan Roese84899e22014-10-22 12:13:21 +0200107
Luka Perkovd131ad62012-05-27 11:44:51 +0000108/*
109 * Xmodem Transfers
110 */
111
112#define SOH 1 /* sender start of block header */
113#define EOT 4 /* sender end of block transfer */
114#define ACK 6 /* target block ack */
115#define NAK 21 /* target block negative ack */
Luka Perkovd131ad62012-05-27 11:44:51 +0000116
Pali Rohár2ef87f72021-09-24 23:06:48 +0200117#define KWBOOT_XM_BLKSZ 128 /* xmodem block size */
118
Luka Perkovd131ad62012-05-27 11:44:51 +0000119struct kwboot_block {
120 uint8_t soh;
121 uint8_t pnum;
122 uint8_t _pnum;
Pali Rohár2ef87f72021-09-24 23:06:48 +0200123 uint8_t data[KWBOOT_XM_BLKSZ];
Luka Perkovd131ad62012-05-27 11:44:51 +0000124 uint8_t csum;
Pali Rohára107c612021-07-23 11:14:14 +0200125} __packed;
Luka Perkovd131ad62012-05-27 11:44:51 +0000126
Pali Roháref951432022-01-25 18:13:00 +0100127#define KWBOOT_BLK_RSP_TIMEO 2000 /* ms */
Marek Behún12df7b72021-09-24 23:06:52 +0200128#define KWBOOT_HDR_RSP_TIMEO 10000 /* ms */
Luka Perkovd131ad62012-05-27 11:44:51 +0000129
Pali Rohár8dbe0272021-10-27 20:57:02 +0200130/* ARM code to change baudrate */
Pali Rohárca272042021-09-24 23:07:05 +0200131static unsigned char kwboot_baud_code[] = {
132 /* ; #define UART_BASE 0xd0012000 */
Pali Rohárca272042021-09-24 23:07:05 +0200133 /* ; #define DLL 0x00 */
134 /* ; #define DLH 0x04 */
135 /* ; #define LCR 0x0c */
136 /* ; #define DLAB 0x80 */
137 /* ; #define LSR 0x14 */
Pali Rohárca272042021-09-24 23:07:05 +0200138 /* ; #define TEMT 0x40 */
139 /* ; #define DIV_ROUND(a, b) ((a + b/2) / b) */
140 /* ; */
141 /* ; u32 set_baudrate(u32 old_b, u32 new_b) { */
Pali Rohárca272042021-09-24 23:07:05 +0200142 /* ; while */
143 /* ; (!(readl(UART_BASE + LSR) & TEMT)); */
144 /* ; u32 lcr = readl(UART_BASE + LCR); */
145 /* ; writel(UART_BASE + LCR, lcr | DLAB); */
146 /* ; u8 old_dll = readl(UART_BASE + DLL); */
147 /* ; u8 old_dlh = readl(UART_BASE + DLH); */
148 /* ; u16 old_dl = old_dll | (old_dlh << 8); */
149 /* ; u32 clk = old_b * old_dl; */
150 /* ; u16 new_dl = DIV_ROUND(clk, new_b); */
151 /* ; u8 new_dll = new_dl & 0xff; */
152 /* ; u8 new_dlh = (new_dl >> 8) & 0xff; */
153 /* ; writel(UART_BASE + DLL, new_dll); */
154 /* ; writel(UART_BASE + DLH, new_dlh); */
155 /* ; writel(UART_BASE + LCR, lcr & ~DLAB); */
Pali Rohár56452292021-10-27 20:57:00 +0200156 /* ; msleep(5); */
Pali Rohárca272042021-09-24 23:07:05 +0200157 /* ; return 0; */
158 /* ; } */
159
Pali Rohárca272042021-09-24 23:07:05 +0200160 /* ; r0 = UART_BASE */
Pali Rohár558176d2021-10-27 20:57:01 +0200161 0x0d, 0x02, 0xa0, 0xe3, /* mov r0, #0xd0000000 */
162 0x12, 0x0a, 0x80, 0xe3, /* orr r0, r0, #0x12000 */
Pali Rohárca272042021-09-24 23:07:05 +0200163
Pali Rohárca272042021-09-24 23:07:05 +0200164 /* ; Wait until Transmitter FIFO is Empty */
165 /* .Lloop_txempty: */
166 /* ; r1 = UART_BASE[LSR] & TEMT */
167 0x14, 0x10, 0x90, 0xe5, /* ldr r1, [r0, #0x14] */
168 0x40, 0x00, 0x11, 0xe3, /* tst r1, #0x40 */
169 0xfc, 0xff, 0xff, 0x0a, /* beq .Lloop_txempty */
170
171 /* ; Set Divisor Latch Access Bit */
172 /* ; UART_BASE[LCR] |= DLAB */
173 0x0c, 0x10, 0x90, 0xe5, /* ldr r1, [r0, #0x0c] */
174 0x80, 0x10, 0x81, 0xe3, /* orr r1, r1, #0x80 */
175 0x0c, 0x10, 0x80, 0xe5, /* str r1, [r0, #0x0c] */
176
177 /* ; Read current Divisor Latch */
178 /* ; r1 = UART_BASE[DLH]<<8 | UART_BASE[DLL] */
179 0x00, 0x10, 0x90, 0xe5, /* ldr r1, [r0, #0x00] */
180 0xff, 0x10, 0x01, 0xe2, /* and r1, r1, #0xff */
181 0x01, 0x20, 0xa0, 0xe1, /* mov r2, r1 */
182 0x04, 0x10, 0x90, 0xe5, /* ldr r1, [r0, #0x04] */
183 0xff, 0x10, 0x01, 0xe2, /* and r1, r1, #0xff */
184 0x41, 0x14, 0xa0, 0xe1, /* asr r1, r1, #8 */
185 0x02, 0x10, 0x81, 0xe1, /* orr r1, r1, r2 */
186
187 /* ; Read old baudrate value */
188 /* ; r2 = old_baudrate */
Pali Rohár62a98f42021-11-01 14:00:02 +0100189 0x74, 0x20, 0x9f, 0xe5, /* ldr r2, old_baudrate */
Pali Rohárca272042021-09-24 23:07:05 +0200190
191 /* ; Calculate base clock */
192 /* ; r1 = r2 * r1 */
193 0x92, 0x01, 0x01, 0xe0, /* mul r1, r2, r1 */
194
195 /* ; Read new baudrate value */
Pali Rohár56452292021-10-27 20:57:00 +0200196 /* ; r2 = new_baudrate */
Pali Rohár62a98f42021-11-01 14:00:02 +0100197 0x70, 0x20, 0x9f, 0xe5, /* ldr r2, new_baudrate */
Pali Rohárca272042021-09-24 23:07:05 +0200198
199 /* ; Calculate new Divisor Latch */
200 /* ; r1 = DIV_ROUND(r1, r2) = */
201 /* ; = (r1 + r2/2) / r2 */
202 0xa2, 0x10, 0x81, 0xe0, /* add r1, r1, r2, lsr #1 */
203 0x02, 0x40, 0xa0, 0xe1, /* mov r4, r2 */
204 0xa1, 0x00, 0x54, 0xe1, /* cmp r4, r1, lsr #1 */
205 /* .Lloop_div1: */
206 0x84, 0x40, 0xa0, 0x91, /* movls r4, r4, lsl #1 */
207 0xa1, 0x00, 0x54, 0xe1, /* cmp r4, r1, lsr #1 */
208 0xfc, 0xff, 0xff, 0x9a, /* bls .Lloop_div1 */
209 0x00, 0x30, 0xa0, 0xe3, /* mov r3, #0 */
210 /* .Lloop_div2: */
211 0x04, 0x00, 0x51, 0xe1, /* cmp r1, r4 */
212 0x04, 0x10, 0x41, 0x20, /* subhs r1, r1, r4 */
213 0x03, 0x30, 0xa3, 0xe0, /* adc r3, r3, r3 */
214 0xa4, 0x40, 0xa0, 0xe1, /* mov r4, r4, lsr #1 */
215 0x02, 0x00, 0x54, 0xe1, /* cmp r4, r2 */
216 0xf9, 0xff, 0xff, 0x2a, /* bhs .Lloop_div2 */
217 0x03, 0x10, 0xa0, 0xe1, /* mov r1, r3 */
218
219 /* ; Set new Divisor Latch Low */
220 /* ; UART_BASE[DLL] = r1 & 0xff */
221 0x01, 0x20, 0xa0, 0xe1, /* mov r2, r1 */
222 0xff, 0x20, 0x02, 0xe2, /* and r2, r2, #0xff */
223 0x00, 0x20, 0x80, 0xe5, /* str r2, [r0, #0x00] */
224
225 /* ; Set new Divisor Latch High */
226 /* ; UART_BASE[DLH] = r1>>8 & 0xff */
227 0x41, 0x24, 0xa0, 0xe1, /* asr r2, r1, #8 */
228 0xff, 0x20, 0x02, 0xe2, /* and r2, r2, #0xff */
229 0x04, 0x20, 0x80, 0xe5, /* str r2, [r0, #0x04] */
230
231 /* ; Clear Divisor Latch Access Bit */
232 /* ; UART_BASE[LCR] &= ~DLAB */
233 0x0c, 0x10, 0x90, 0xe5, /* ldr r1, [r0, #0x0c] */
234 0x80, 0x10, 0xc1, 0xe3, /* bic r1, r1, #0x80 */
235 0x0c, 0x10, 0x80, 0xe5, /* str r1, [r0, #0x0c] */
236
Pali Rohár56452292021-10-27 20:57:00 +0200237 /* ; Loop 0x2dc000 (2998272) cycles */
238 /* ; which is about 5ms on 1200 MHz CPU */
239 /* ; r1 = 0x2dc000 */
240 0xb7, 0x19, 0xa0, 0xe3, /* mov r1, #0x2dc000 */
Pali Rohárca272042021-09-24 23:07:05 +0200241 /* .Lloop_sleep: */
242 0x01, 0x10, 0x41, 0xe2, /* sub r1, r1, #1 */
243 0x00, 0x00, 0x51, 0xe3, /* cmp r1, #0 */
244 0xfc, 0xff, 0xff, 0x1a, /* bne .Lloop_sleep */
245
Pali Rohár62a98f42021-11-01 14:00:02 +0100246 /* ; Jump to the end of execution */
247 0x01, 0x00, 0x00, 0xea, /* b end */
Pali Rohárca272042021-09-24 23:07:05 +0200248
249 /* ; Placeholder for old baudrate value */
250 /* old_baudrate: */
251 0x00, 0x00, 0x00, 0x00, /* .word 0 */
252
253 /* ; Placeholder for new baudrate value */
254 /* new_baudrate: */
255 0x00, 0x00, 0x00, 0x00, /* .word 0 */
Pali Rohár8dbe0272021-10-27 20:57:02 +0200256
257 /* end: */
Pali Rohárca272042021-09-24 23:07:05 +0200258};
259
Pali Rohár62a98f42021-11-01 14:00:02 +0100260/* ARM code from binary header executed by BootROM before changing baudrate */
Pali Rohár8dbe0272021-10-27 20:57:02 +0200261static unsigned char kwboot_baud_code_binhdr_pre[] = {
Pali Rohár62a98f42021-11-01 14:00:02 +0100262 /* ; #define UART_BASE 0xd0012000 */
263 /* ; #define THR 0x00 */
264 /* ; #define LSR 0x14 */
265 /* ; #define THRE 0x20 */
266 /* ; */
267 /* ; void send_preamble(void) { */
268 /* ; const u8 *str = "$baudratechange"; */
269 /* ; u8 c; */
270 /* ; do { */
271 /* ; while */
272 /* ; ((readl(UART_BASE + LSR) & THRE)); */
273 /* ; c = *str++; */
274 /* ; writel(UART_BASE + THR, c); */
275 /* ; } while (c); */
276 /* ; } */
277
278 /* ; Preserve registers for BootROM */
Pali Rohár8dbe0272021-10-27 20:57:02 +0200279 0xfe, 0x5f, 0x2d, 0xe9, /* push { r1 - r12, lr } */
Pali Rohár62a98f42021-11-01 14:00:02 +0100280
281 /* ; r0 = UART_BASE */
282 0x0d, 0x02, 0xa0, 0xe3, /* mov r0, #0xd0000000 */
283 0x12, 0x0a, 0x80, 0xe3, /* orr r0, r0, #0x12000 */
284
285 /* ; r2 = address of preamble string */
286 0x00, 0x20, 0x8f, 0xe2, /* adr r2, .Lstr_preamble */
287
288 /* ; Skip preamble data section */
289 0x03, 0x00, 0x00, 0xea, /* b .Lloop_preamble */
290
291 /* ; Preamble string */
292 /* .Lstr_preamble: */
293 0x24, 0x62, 0x61, 0x75, /* .asciz "$baudratechange" */
294 0x64, 0x72, 0x61, 0x74,
295 0x65, 0x63, 0x68, 0x61,
296 0x6e, 0x67, 0x65, 0x00,
297
298 /* ; Send preamble string over UART */
299 /* .Lloop_preamble: */
300 /* */
301 /* ; Wait until Transmitter Holding is Empty */
302 /* .Lloop_thre: */
303 /* ; r1 = UART_BASE[LSR] & THRE */
304 0x14, 0x10, 0x90, 0xe5, /* ldr r1, [r0, #0x14] */
305 0x20, 0x00, 0x11, 0xe3, /* tst r1, #0x20 */
306 0xfc, 0xff, 0xff, 0x0a, /* beq .Lloop_thre */
307
308 /* ; Put character into Transmitter FIFO */
309 /* ; r1 = *r2++ */
310 0x01, 0x10, 0xd2, 0xe4, /* ldrb r1, [r2], #1 */
311 /* ; UART_BASE[THR] = r1 */
312 0x00, 0x10, 0x80, 0xe5, /* str r1, [r0, #0x0] */
313
314 /* ; Loop until end of preamble string */
315 0x00, 0x00, 0x51, 0xe3, /* cmp r1, #0 */
316 0xf8, 0xff, 0xff, 0x1a, /* bne .Lloop_preamble */
Pali Rohár8dbe0272021-10-27 20:57:02 +0200317};
318
Pali Rohár62a98f42021-11-01 14:00:02 +0100319/* ARM code for returning from binary header back to BootROM */
Pali Rohár8dbe0272021-10-27 20:57:02 +0200320static unsigned char kwboot_baud_code_binhdr_post[] = {
321 /* ; Return 0 - no error */
322 0x00, 0x00, 0xa0, 0xe3, /* mov r0, #0 */
323 0xfe, 0x9f, 0xbd, 0xe8, /* pop { r1 - r12, pc } */
324};
325
326/* ARM code for jumping to the original image exec_addr */
327static unsigned char kwboot_baud_code_data_jump[] = {
328 0x04, 0xf0, 0x1f, 0xe5, /* ldr pc, exec_addr */
329 /* ; Placeholder for exec_addr */
330 /* exec_addr: */
331 0x00, 0x00, 0x00, 0x00, /* .word 0 */
332};
Pali Rohárca272042021-09-24 23:07:05 +0200333
334static const char kwb_baud_magic[16] = "$baudratechange";
335
Luka Perkovd131ad62012-05-27 11:44:51 +0000336static int kwboot_verbose;
337
Stefan Roese84899e22014-10-22 12:13:21 +0200338static int msg_rsp_timeo = KWBOOT_MSG_RSP_TIMEO;
Kevin Smith7497a6a2016-02-16 21:28:19 +0000339static int blk_rsp_timeo = KWBOOT_BLK_RSP_TIMEO;
Stefan Roese84899e22014-10-22 12:13:21 +0200340
Marek Behúne453bb42021-09-24 23:06:41 +0200341static ssize_t
342kwboot_write(int fd, const char *buf, size_t len)
343{
Pali Rohár6ba7d632022-01-25 18:13:10 +0100344 ssize_t tot = 0;
Marek Behúne453bb42021-09-24 23:06:41 +0200345
346 while (tot < len) {
347 ssize_t wr = write(fd, buf + tot, len - tot);
348
Pali Rohár6ba7d632022-01-25 18:13:10 +0100349 if (wr < 0 && errno == EINTR)
350 continue;
351 else if (wr < 0)
352 return wr;
Marek Behúne453bb42021-09-24 23:06:41 +0200353
354 tot += wr;
355 }
356
357 return tot;
358}
359
Luka Perkovd131ad62012-05-27 11:44:51 +0000360static void
361kwboot_printv(const char *fmt, ...)
362{
363 va_list ap;
364
365 if (kwboot_verbose) {
366 va_start(ap, fmt);
367 vprintf(fmt, ap);
368 va_end(ap);
369 fflush(stdout);
370 }
371}
372
373static void
374__spinner(void)
375{
376 const char seq[] = { '-', '\\', '|', '/' };
377 const int div = 8;
378 static int state, bs;
379
380 if (state % div == 0) {
381 fputc(bs, stdout);
382 fputc(seq[state / div % sizeof(seq)], stdout);
383 fflush(stdout);
384 }
385
386 bs = '\b';
387 state++;
388}
389
390static void
391kwboot_spinner(void)
392{
393 if (kwboot_verbose)
394 __spinner();
395}
396
397static void
398__progress(int pct, char c)
399{
400 const int width = 70;
401 static const char *nl = "";
402 static int pos;
403
404 if (pos % width == 0)
405 printf("%s%3d %% [", nl, pct);
406
407 fputc(c, stdout);
408
409 nl = "]\n";
Pali Rohár5a1f8cb2021-09-24 23:06:46 +0200410 pos = (pos + 1) % width;
Luka Perkovd131ad62012-05-27 11:44:51 +0000411
412 if (pct == 100) {
Pali Rohár5a1f8cb2021-09-24 23:06:46 +0200413 while (pos && pos++ < width)
Luka Perkovd131ad62012-05-27 11:44:51 +0000414 fputc(' ', stdout);
415 fputs(nl, stdout);
Pali Rohár5a1f8cb2021-09-24 23:06:46 +0200416 nl = "";
417 pos = 0;
Luka Perkovd131ad62012-05-27 11:44:51 +0000418 }
419
420 fflush(stdout);
421
422}
423
424static void
425kwboot_progress(int _pct, char c)
426{
427 static int pct;
428
429 if (_pct != -1)
430 pct = _pct;
431
432 if (kwboot_verbose)
433 __progress(pct, c);
Pali Rohár5a1f8cb2021-09-24 23:06:46 +0200434
435 if (pct == 100)
436 pct = 0;
Luka Perkovd131ad62012-05-27 11:44:51 +0000437}
438
439static int
440kwboot_tty_recv(int fd, void *buf, size_t len, int timeo)
441{
442 int rc, nfds;
443 fd_set rfds;
444 struct timeval tv;
445 ssize_t n;
446
447 rc = -1;
448
449 FD_ZERO(&rfds);
450 FD_SET(fd, &rfds);
451
452 tv.tv_sec = 0;
453 tv.tv_usec = timeo * 1000;
454 if (tv.tv_usec > 1000000) {
455 tv.tv_sec += tv.tv_usec / 1000000;
456 tv.tv_usec %= 1000000;
457 }
458
459 do {
460 nfds = select(fd + 1, &rfds, NULL, NULL, &tv);
Pali Rohár91fb0952022-01-25 18:13:11 +0100461 if (nfds < 0 && errno == EINTR)
462 continue;
463 else if (nfds < 0)
Luka Perkovd131ad62012-05-27 11:44:51 +0000464 goto out;
Pali Rohár91fb0952022-01-25 18:13:11 +0100465 else if (!nfds) {
Luka Perkovd131ad62012-05-27 11:44:51 +0000466 errno = ETIMEDOUT;
467 goto out;
468 }
469
470 n = read(fd, buf, len);
Pali Rohár91fb0952022-01-25 18:13:11 +0100471 if (n < 0 && errno == EINTR)
472 continue;
473 else if (n <= 0)
Luka Perkovd131ad62012-05-27 11:44:51 +0000474 goto out;
475
476 buf = (char *)buf + n;
477 len -= n;
478 } while (len > 0);
479
480 rc = 0;
481out:
482 return rc;
483}
484
485static int
Pali Rohárcab817d2021-10-27 20:56:59 +0200486kwboot_tty_send(int fd, const void *buf, size_t len, int nodrain)
Luka Perkovd131ad62012-05-27 11:44:51 +0000487{
Stefan Roese84899e22014-10-22 12:13:21 +0200488 if (!buf)
489 return 0;
490
Marek Behúne453bb42021-09-24 23:06:41 +0200491 if (kwboot_write(fd, buf, len) < 0)
492 return -1;
Luka Perkovd131ad62012-05-27 11:44:51 +0000493
Pali Rohárcab817d2021-10-27 20:56:59 +0200494 if (nodrain)
495 return 0;
496
Marek Behúne453bb42021-09-24 23:06:41 +0200497 return tcdrain(fd);
Luka Perkovd131ad62012-05-27 11:44:51 +0000498}
499
500static int
501kwboot_tty_send_char(int fd, unsigned char c)
502{
Pali Rohárcab817d2021-10-27 20:56:59 +0200503 return kwboot_tty_send(fd, &c, 1, 0);
Luka Perkovd131ad62012-05-27 11:44:51 +0000504}
505
506static speed_t
Pali Rohárca272042021-09-24 23:07:05 +0200507kwboot_tty_baudrate_to_speed(int baudrate)
Luka Perkovd131ad62012-05-27 11:44:51 +0000508{
509 switch (baudrate) {
Pali Rohárca272042021-09-24 23:07:05 +0200510#ifdef B4000000
511 case 4000000:
512 return B4000000;
513#endif
514#ifdef B3500000
515 case 3500000:
516 return B3500000;
517#endif
518#ifdef B3000000
519 case 3000000:
520 return B3000000;
521#endif
522#ifdef B2500000
523 case 2500000:
524 return B2500000;
525#endif
526#ifdef B2000000
527 case 2000000:
528 return B2000000;
529#endif
530#ifdef B1500000
531 case 1500000:
532 return B1500000;
533#endif
534#ifdef B1152000
535 case 1152000:
536 return B1152000;
537#endif
538#ifdef B1000000
539 case 1000000:
540 return B1000000;
541#endif
542#ifdef B921600
543 case 921600:
544 return B921600;
545#endif
546#ifdef B614400
547 case 614400:
548 return B614400;
549#endif
550#ifdef B576000
551 case 576000:
552 return B576000;
553#endif
554#ifdef B500000
555 case 500000:
556 return B500000;
557#endif
558#ifdef B460800
559 case 460800:
560 return B460800;
561#endif
562#ifdef B307200
563 case 307200:
564 return B307200;
565#endif
566#ifdef B230400
567 case 230400:
568 return B230400;
569#endif
570#ifdef B153600
571 case 153600:
572 return B153600;
573#endif
574#ifdef B115200
Luka Perkovd131ad62012-05-27 11:44:51 +0000575 case 115200:
576 return B115200;
Pali Rohárca272042021-09-24 23:07:05 +0200577#endif
578#ifdef B76800
579 case 76800:
580 return B76800;
581#endif
582#ifdef B57600
Luka Perkovd131ad62012-05-27 11:44:51 +0000583 case 57600:
584 return B57600;
Pali Rohárca272042021-09-24 23:07:05 +0200585#endif
586#ifdef B38400
Luka Perkovd131ad62012-05-27 11:44:51 +0000587 case 38400:
588 return B38400;
Pali Rohárca272042021-09-24 23:07:05 +0200589#endif
590#ifdef B19200
Luka Perkovd131ad62012-05-27 11:44:51 +0000591 case 19200:
592 return B19200;
Pali Rohárca272042021-09-24 23:07:05 +0200593#endif
594#ifdef B9600
Luka Perkovd131ad62012-05-27 11:44:51 +0000595 case 9600:
596 return B9600;
Pali Rohárca272042021-09-24 23:07:05 +0200597#endif
598#ifdef B4800
599 case 4800:
600 return B4800;
601#endif
602#ifdef B2400
603 case 2400:
604 return B2400;
605#endif
606#ifdef B1800
607 case 1800:
608 return B1800;
609#endif
610#ifdef B1200
611 case 1200:
612 return B1200;
613#endif
614#ifdef B600
615 case 600:
616 return B600;
617#endif
618#ifdef B300
619 case 300:
620 return B300;
621#endif
622#ifdef B200
623 case 200:
624 return B200;
625#endif
626#ifdef B150
627 case 150:
628 return B150;
629#endif
630#ifdef B134
631 case 134:
632 return B134;
633#endif
634#ifdef B110
635 case 110:
636 return B110;
637#endif
638#ifdef B75
639 case 75:
640 return B75;
641#endif
642#ifdef B50
643 case 50:
644 return B50;
645#endif
646 default:
Pali Rohár93b55632021-09-24 23:07:06 +0200647#ifdef BOTHER
648 return BOTHER;
649#else
Pali Rohárca272042021-09-24 23:07:05 +0200650 return B0;
Pali Rohár93b55632021-09-24 23:07:06 +0200651#endif
Luka Perkovd131ad62012-05-27 11:44:51 +0000652 }
Luka Perkovd131ad62012-05-27 11:44:51 +0000653}
654
655static int
Marek Behún99a3d0232021-09-24 23:07:07 +0200656_is_within_tolerance(int value, int reference, int tolerance)
657{
658 return 100 * value >= reference * (100 - tolerance) &&
659 100 * value <= reference * (100 + tolerance);
660}
661
662static int
Pali Rohárca272042021-09-24 23:07:05 +0200663kwboot_tty_change_baudrate(int fd, int baudrate)
664{
665 struct termios tio;
666 speed_t speed;
667 int rc;
668
669 rc = tcgetattr(fd, &tio);
670 if (rc)
671 return rc;
672
673 speed = kwboot_tty_baudrate_to_speed(baudrate);
674 if (speed == B0) {
675 errno = EINVAL;
676 return -1;
677 }
678
Pali Rohár93b55632021-09-24 23:07:06 +0200679#ifdef BOTHER
680 if (speed == BOTHER)
681 tio.c_ospeed = tio.c_ispeed = baudrate;
682#endif
683
Pali Rohárca272042021-09-24 23:07:05 +0200684 rc = cfsetospeed(&tio, speed);
685 if (rc)
686 return rc;
687
688 rc = cfsetispeed(&tio, speed);
689 if (rc)
690 return rc;
691
692 rc = tcsetattr(fd, TCSANOW, &tio);
693 if (rc)
694 return rc;
695
Marek Behún99a3d0232021-09-24 23:07:07 +0200696 rc = tcgetattr(fd, &tio);
697 if (rc)
698 return rc;
699
700 if (cfgetospeed(&tio) != speed || cfgetispeed(&tio) != speed)
701 goto baud_fail;
702
703#ifdef BOTHER
704 /*
705 * Check whether set baudrate is within 3% tolerance.
706 * If BOTHER is defined, Linux always fills out c_ospeed / c_ispeed
707 * with real values.
708 */
709 if (!_is_within_tolerance(tio.c_ospeed, baudrate, 3))
710 goto baud_fail;
711
712 if (!_is_within_tolerance(tio.c_ispeed, baudrate, 3))
713 goto baud_fail;
714#endif
715
Pali Rohárca272042021-09-24 23:07:05 +0200716 return 0;
Marek Behún99a3d0232021-09-24 23:07:07 +0200717
718baud_fail:
719 fprintf(stderr, "Could not set baudrate to requested value\n");
720 errno = EINVAL;
721 return -1;
Pali Rohárca272042021-09-24 23:07:05 +0200722}
723
724static int
725kwboot_open_tty(const char *path, int baudrate)
Luka Perkovd131ad62012-05-27 11:44:51 +0000726{
Pali Rohár911515b2021-09-24 23:07:10 +0200727 int rc, fd, flags;
Luka Perkovd131ad62012-05-27 11:44:51 +0000728 struct termios tio;
729
730 rc = -1;
731
Marek Behún5fa04f42021-09-24 23:07:11 +0200732 fd = open(path, O_RDWR | O_NOCTTY | O_NDELAY);
Luka Perkovd131ad62012-05-27 11:44:51 +0000733 if (fd < 0)
734 goto out;
735
Pali Rohárc704e0e2021-09-24 23:07:08 +0200736 rc = tcgetattr(fd, &tio);
737 if (rc)
738 goto out;
Luka Perkovd131ad62012-05-27 11:44:51 +0000739
Pali Rohárc704e0e2021-09-24 23:07:08 +0200740 cfmakeraw(&tio);
Marek Behún5fa04f42021-09-24 23:07:11 +0200741 tio.c_cflag |= CREAD | CLOCAL;
Pali Rohár2ecca3d2021-10-25 15:12:53 +0200742 tio.c_cflag &= ~(CSTOPB | HUPCL | CRTSCTS);
Luka Perkovd131ad62012-05-27 11:44:51 +0000743 tio.c_cc[VMIN] = 1;
Pali Rohár24a471b2021-09-24 23:07:09 +0200744 tio.c_cc[VTIME] = 0;
Luka Perkovd131ad62012-05-27 11:44:51 +0000745
Luka Perkovd131ad62012-05-27 11:44:51 +0000746 rc = tcsetattr(fd, TCSANOW, &tio);
747 if (rc)
748 goto out;
749
Pali Rohár911515b2021-09-24 23:07:10 +0200750 flags = fcntl(fd, F_GETFL);
751 if (flags < 0)
752 goto out;
753
754 rc = fcntl(fd, F_SETFL, flags & ~O_NDELAY);
755 if (rc)
756 goto out;
757
Pali Rohárca272042021-09-24 23:07:05 +0200758 rc = kwboot_tty_change_baudrate(fd, baudrate);
759 if (rc)
760 goto out;
761
Luka Perkovd131ad62012-05-27 11:44:51 +0000762 rc = fd;
763out:
764 if (rc < 0) {
765 if (fd >= 0)
766 close(fd);
767 }
768
769 return rc;
770}
771
Pali Rohár913866a2022-03-02 11:49:21 +0100772static void *
773kwboot_msg_write_handler(void *arg)
774{
775 int tty = *(int *)((void **)arg)[0];
776 const void *msg = ((void **)arg)[1];
777 int rsp_timeo = msg_rsp_timeo;
778 int i, dummy_oldtype;
779
780 /* allow to cancel this thread at any time */
781 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &dummy_oldtype);
782
783 while (1) {
784 /* write 128 samples of message pattern into the output queue without waiting */
785 for (i = 0; i < 128; i++) {
786 if (kwboot_tty_send(tty, msg, 8, 1) < 0) {
787 perror("\nFailed to send message pattern");
788 exit(1);
789 }
790 }
791 /* wait until output queue is transmitted and then make pause */
792 if (tcdrain(tty) < 0) {
793 perror("\nFailed to send message pattern");
794 exit(1);
795 }
796 /* BootROM requires pause on UART after it detects message pattern */
797 usleep(rsp_timeo * 1000);
798 }
799}
800
801static int
802kwboot_msg_start_thread(pthread_t *thread, int *tty, void *msg)
803{
804 void *arg[2];
805 int rc;
806
807 arg[0] = tty;
808 arg[1] = msg;
809 rc = pthread_create(thread, NULL, kwboot_msg_write_handler, arg);
810 if (rc) {
811 errno = rc;
812 return -1;
813 }
814
815 return 0;
816}
817
818static int
819kwboot_msg_stop_thread(pthread_t thread)
820{
821 int rc;
822
823 rc = pthread_cancel(thread);
824 if (rc) {
825 errno = rc;
826 return -1;
827 }
828
829 rc = pthread_join(thread, NULL);
830 if (rc) {
831 errno = rc;
832 return -1;
833 }
834
835 return 0;
836}
837
Luka Perkovd131ad62012-05-27 11:44:51 +0000838static int
Pali Rohárc1d911f2022-03-02 11:49:20 +0100839kwboot_bootmsg(int tty)
Luka Perkovd131ad62012-05-27 11:44:51 +0000840{
Pali Rohár2bcd5b12022-01-25 18:13:08 +0100841 struct kwboot_block block;
Pali Rohár913866a2022-03-02 11:49:21 +0100842 pthread_t write_thread;
843 int rc, err;
Luka Perkovd131ad62012-05-27 11:44:51 +0000844 char c;
Pali Rohár913866a2022-03-02 11:49:21 +0100845
846 /* flush input and output queue */
847 tcflush(tty, TCIOFLUSH);
848
849 rc = kwboot_msg_start_thread(&write_thread, &tty, kwboot_msg_boot);
850 if (rc) {
851 perror("Failed to start write thread");
852 return rc;
853 }
Luka Perkovd131ad62012-05-27 11:44:51 +0000854
Pali Rohárc1d911f2022-03-02 11:49:20 +0100855 kwboot_printv("Sending boot message. Please reboot the target...");
Luka Perkovd131ad62012-05-27 11:44:51 +0000856
Pali Rohár913866a2022-03-02 11:49:21 +0100857 err = 0;
858 while (1) {
Luka Perkovd131ad62012-05-27 11:44:51 +0000859 kwboot_spinner();
860
Pali Rohár913866a2022-03-02 11:49:21 +0100861 rc = kwboot_tty_recv(tty, &c, 1, msg_rsp_timeo);
862 if (rc && errno == ETIMEDOUT) {
863 continue;
864 } else if (rc) {
865 err = errno;
866 break;
867 }
868
869 if (c == NAK)
870 break;
871 }
Luka Perkovd131ad62012-05-27 11:44:51 +0000872
873 kwboot_printv("\n");
874
Pali Rohár913866a2022-03-02 11:49:21 +0100875 rc = kwboot_msg_stop_thread(write_thread);
876 if (rc) {
877 perror("Failed to stop write thread");
Pali Rohár2bcd5b12022-01-25 18:13:08 +0100878 return rc;
Pali Rohár913866a2022-03-02 11:49:21 +0100879 }
880
881 if (err) {
882 errno = err;
883 perror("Failed to read response for boot message pattern");
884 return -1;
885 }
Pali Rohár2bcd5b12022-01-25 18:13:08 +0100886
887 /*
888 * At this stage we have sent more boot message patterns and BootROM
889 * (at least on Armada XP and 385) started interpreting sent bytes as
890 * part of xmodem packets. If BootROM is expecting SOH byte as start of
891 * a xmodem packet and it receives byte 0xff, then it throws it away and
892 * sends a NAK reply to host. If BootROM does not receive any byte for
893 * 2s when expecting some continuation of the xmodem packet, it throws
894 * away the partially received xmodem data and sends NAK reply to host.
895 *
896 * Therefore for starting xmodem transfer we have two options: Either
897 * wait 2s or send 132 0xff bytes (which is the size of xmodem packet)
898 * to ensure that BootROM throws away any partially received data.
899 */
900
901 /* flush output queue with remaining boot message patterns */
Pali Rohárd8865f82022-03-02 11:49:18 +0100902 rc = tcflush(tty, TCOFLUSH);
903 if (rc) {
904 perror("Failed to flush output queue");
905 return rc;
906 }
Pali Rohár2bcd5b12022-01-25 18:13:08 +0100907
908 /* send one xmodem packet with 0xff bytes to force BootROM to re-sync */
909 memset(&block, 0xff, sizeof(block));
Pali Rohárd8865f82022-03-02 11:49:18 +0100910 rc = kwboot_tty_send(tty, &block, sizeof(block), 0);
911 if (rc) {
912 perror("Failed to send sync sequence");
913 return rc;
914 }
Pali Rohár2bcd5b12022-01-25 18:13:08 +0100915
916 /*
917 * Sending 132 bytes via 115200B/8-N-1 takes 11.45 ms, reading 132 bytes
918 * takes 11.45 ms, so waiting for 30 ms should be enough.
919 */
920 usleep(30 * 1000);
921
922 /* flush remaining NAK replies from input queue */
Pali Rohárd8865f82022-03-02 11:49:18 +0100923 rc = tcflush(tty, TCIFLUSH);
924 if (rc) {
925 perror("Failed to flush input queue");
926 return rc;
927 }
Pali Rohár2bcd5b12022-01-25 18:13:08 +0100928
929 return 0;
Luka Perkovd131ad62012-05-27 11:44:51 +0000930}
931
932static int
Pali Rohárc1d911f2022-03-02 11:49:20 +0100933kwboot_debugmsg(int tty)
Stefan Roese84899e22014-10-22 12:13:21 +0200934{
Pali Rohár93976af2022-03-02 11:49:22 +0100935 unsigned char buf[8192];
936 pthread_t write_thread;
937 int rc, err, i, pos;
938 size_t off;
939
940 /* flush input and output queue */
941 tcflush(tty, TCIOFLUSH);
942
943 rc = kwboot_msg_start_thread(&write_thread, &tty, kwboot_msg_debug);
944 if (rc) {
945 perror("Failed to start write thread");
946 return rc;
947 }
Stefan Roese84899e22014-10-22 12:13:21 +0200948
949 kwboot_printv("Sending debug message. Please reboot the target...");
Pali Rohár93976af2022-03-02 11:49:22 +0100950 kwboot_spinner();
Stefan Roese84899e22014-10-22 12:13:21 +0200951
Pali Rohár93976af2022-03-02 11:49:22 +0100952 err = 0;
953 off = 0;
954 while (1) {
955 /* Read immediately all bytes in queue without waiting */
956 rc = read(tty, buf + off, sizeof(buf) - off);
957 if ((rc < 0 && errno == EINTR) || rc == 0) {
958 continue;
959 } else if (rc < 0) {
960 err = errno;
Stefan Roese84899e22014-10-22 12:13:21 +0200961 break;
Pali Rohár93976af2022-03-02 11:49:22 +0100962 }
963 off += rc - 1;
Stefan Roese84899e22014-10-22 12:13:21 +0200964
965 kwboot_spinner();
966
Pali Rohár93976af2022-03-02 11:49:22 +0100967 /*
968 * Check if we received at least 4 debug message patterns
969 * (console echo from BootROM) in cyclic buffer
970 */
971
972 for (pos = 0; pos < sizeof(kwboot_msg_debug); pos++)
973 if (buf[off] == kwboot_msg_debug[(pos + off) % sizeof(kwboot_msg_debug)])
974 break;
975
976 for (i = off; i >= 0; i--)
977 if (buf[i] != kwboot_msg_debug[(pos + i) % sizeof(kwboot_msg_debug)])
978 break;
979
980 off -= i;
981
982 if (off >= 4 * sizeof(kwboot_msg_debug))
983 break;
984
985 /* If not move valid suffix from end of the buffer to the beginning of buffer */
986 memmove(buf, buf + i + 1, off);
987 }
Stefan Roese84899e22014-10-22 12:13:21 +0200988
989 kwboot_printv("\n");
990
Pali Rohár93976af2022-03-02 11:49:22 +0100991 rc = kwboot_msg_stop_thread(write_thread);
992 if (rc) {
993 perror("Failed to stop write thread");
994 return rc;
995 }
996
997 if (err) {
998 errno = err;
999 perror("Failed to read response for debug message pattern");
1000 return -1;
1001 }
1002
1003 /* flush output queue with remaining debug message patterns */
1004 rc = tcflush(tty, TCOFLUSH);
1005 if (rc) {
1006 perror("Failed to flush output queue");
1007 return rc;
1008 }
1009
1010 kwboot_printv("Clearing input buffer...\n");
1011
1012 /*
1013 * Wait until BootROM transmit all remaining echo characters.
1014 * Experimentally it was measured that for Armada 385 BootROM
1015 * it is required to wait at least 0.415s. So wait 0.5s.
1016 */
1017 usleep(500 * 1000);
1018
1019 /*
1020 * In off variable is stored number of characters received after the
1021 * successful detection of echo reply. So these characters are console
1022 * echo for other following debug message patterns. BootROM may have in
1023 * its output queue other echo characters which were being transmitting
1024 * before above sleep call. So read remaining number of echo characters
1025 * sent by the BootROM now.
1026 */
1027 while ((rc = kwboot_tty_recv(tty, &buf[0], 1, 0)) == 0)
1028 off++;
1029 if (errno != ETIMEDOUT) {
1030 perror("Failed to read response");
1031 return rc;
1032 }
1033
1034 /*
1035 * Clear every echo character set by the BootROM by backspace byte.
1036 * This is required prior writing any command to the BootROM debug
1037 * because BootROM command line buffer has limited size. If length
1038 * of the command is larger than buffer size then it looks like
1039 * that Armada 385 BootROM crashes after sending ENTER. So erase it.
1040 * Experimentally it was measured that for Armada 385 BootROM it is
1041 * required to send at least 3 backspace bytes for one echo character.
1042 * This is unknown why. But lets do it.
1043 */
1044 off *= 3;
1045 memset(buf, '\x08', sizeof(buf));
1046 while (off > sizeof(buf)) {
1047 rc = kwboot_tty_send(tty, buf, sizeof(buf), 1);
1048 if (rc) {
1049 perror("Failed to send clear sequence");
1050 return rc;
1051 }
1052 off -= sizeof(buf);
1053 }
1054 rc = kwboot_tty_send(tty, buf, off, 0);
1055 if (rc) {
1056 perror("Failed to send clear sequence");
1057 return rc;
1058 }
1059
1060 usleep(msg_rsp_timeo * 1000);
1061 rc = tcflush(tty, TCIFLUSH);
1062 if (rc) {
1063 perror("Failed to flush input queue");
1064 return rc;
1065 }
1066
1067 return 0;
Stefan Roese84899e22014-10-22 12:13:21 +02001068}
1069
Pali Rohárc5d666a2021-09-24 23:06:44 +02001070static size_t
Luka Perkovd131ad62012-05-27 11:44:51 +00001071kwboot_xm_makeblock(struct kwboot_block *block, const void *data,
1072 size_t size, int pnum)
1073{
Marek Behúnd8cc8512021-09-24 23:06:45 +02001074 size_t i, n;
Luka Perkovd131ad62012-05-27 11:44:51 +00001075
Stefan Roese84899e22014-10-22 12:13:21 +02001076 block->soh = SOH;
Luka Perkovd131ad62012-05-27 11:44:51 +00001077 block->pnum = pnum;
1078 block->_pnum = ~block->pnum;
1079
Pali Rohár2ef87f72021-09-24 23:06:48 +02001080 n = size < KWBOOT_XM_BLKSZ ? size : KWBOOT_XM_BLKSZ;
Luka Perkovd131ad62012-05-27 11:44:51 +00001081 memcpy(&block->data[0], data, n);
Pali Rohár2ef87f72021-09-24 23:06:48 +02001082 memset(&block->data[n], 0, KWBOOT_XM_BLKSZ - n);
Luka Perkovd131ad62012-05-27 11:44:51 +00001083
1084 block->csum = 0;
1085 for (i = 0; i < n; i++)
1086 block->csum += block->data[i];
1087
1088 return n;
1089}
1090
Marek Behún12df7b72021-09-24 23:06:52 +02001091static uint64_t
1092_now(void)
1093{
1094 struct timespec ts;
1095
1096 if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
1097 static int err_print;
1098
1099 if (!err_print) {
1100 perror("clock_gettime() does not work");
1101 err_print = 1;
1102 }
1103
1104 /* this will just make the timeout not work */
1105 return -1ULL;
1106 }
1107
1108 return ts.tv_sec * 1000ULL + (ts.tv_nsec + 500000) / 1000000;
1109}
1110
Luka Perkovd131ad62012-05-27 11:44:51 +00001111static int
Marek Behún408ea612021-09-24 23:06:49 +02001112_is_xm_reply(char c)
1113{
Pali Rohár94c906a2022-01-25 18:13:03 +01001114 return c == ACK || c == NAK;
Marek Behún408ea612021-09-24 23:06:49 +02001115}
1116
1117static int
Pali Rohár9cdc2642021-09-24 23:06:54 +02001118_xm_reply_to_error(int c)
1119{
1120 int rc = -1;
1121
1122 switch (c) {
1123 case ACK:
1124 rc = 0;
1125 break;
1126 case NAK:
1127 errno = EBADMSG;
1128 break;
Pali Rohár9cdc2642021-09-24 23:06:54 +02001129 default:
1130 errno = EPROTO;
1131 break;
1132 }
1133
1134 return rc;
1135}
1136
1137static int
Pali Rohárca272042021-09-24 23:07:05 +02001138kwboot_baud_magic_handle(int fd, char c, int baudrate)
1139{
1140 static size_t rcv_len;
1141
1142 if (rcv_len < sizeof(kwb_baud_magic)) {
1143 /* try to recognize whole magic word */
1144 if (c == kwb_baud_magic[rcv_len]) {
1145 rcv_len++;
1146 } else {
1147 printf("%.*s%c", (int)rcv_len, kwb_baud_magic, c);
1148 fflush(stdout);
1149 rcv_len = 0;
1150 }
1151 }
1152
1153 if (rcv_len == sizeof(kwb_baud_magic)) {
1154 /* magic word received */
1155 kwboot_printv("\nChanging baudrate to %d Bd\n", baudrate);
1156
1157 return kwboot_tty_change_baudrate(fd, baudrate) ? : 1;
1158 } else {
1159 return 0;
1160 }
1161}
1162
1163static int
Pali Rohár950ed242022-01-25 18:13:04 +01001164kwboot_xm_recv_reply(int fd, char *c, int stop_on_non_xm,
Pali Rohár82a9e132022-01-25 18:13:02 +01001165 int ignore_nak_reply,
Pali Rohára6fcac22021-10-25 15:13:04 +02001166 int allow_non_xm, int *non_xm_print,
Pali Rohárca272042021-09-24 23:07:05 +02001167 int baudrate, int *baud_changed)
Pali Rohár48b3ea62021-09-24 23:06:50 +02001168{
Marek Behún12df7b72021-09-24 23:06:52 +02001169 int timeout = allow_non_xm ? KWBOOT_HDR_RSP_TIMEO : blk_rsp_timeo;
Marek Behún819cd322021-09-24 23:06:53 +02001170 uint64_t recv_until = _now() + timeout;
Pali Rohár48b3ea62021-09-24 23:06:50 +02001171 int rc;
1172
1173 while (1) {
Marek Behún12df7b72021-09-24 23:06:52 +02001174 rc = kwboot_tty_recv(fd, c, 1, timeout);
Pali Rohár48b3ea62021-09-24 23:06:50 +02001175 if (rc) {
1176 if (errno != ETIMEDOUT)
1177 return rc;
Marek Behún819cd322021-09-24 23:06:53 +02001178 else if (allow_non_xm && *non_xm_print)
Marek Behún12df7b72021-09-24 23:06:52 +02001179 return -1;
1180 else
1181 *c = NAK;
Pali Rohár48b3ea62021-09-24 23:06:50 +02001182 }
1183
1184 /* If received xmodem reply, end. */
Pali Rohár82a9e132022-01-25 18:13:02 +01001185 if (_is_xm_reply(*c)) {
1186 if (*c == NAK && ignore_nak_reply) {
1187 timeout = recv_until - _now();
1188 if (timeout >= 0)
1189 continue;
1190 }
Pali Rohár48b3ea62021-09-24 23:06:50 +02001191 break;
Pali Rohár82a9e132022-01-25 18:13:02 +01001192 }
Pali Rohár48b3ea62021-09-24 23:06:50 +02001193
1194 /*
Pali Rohárca272042021-09-24 23:07:05 +02001195 * If receiving/printing non-xmodem text output is allowed and
1196 * such a byte was received, we want to increase receiving time
1197 * and either:
1198 * - print the byte, if it is not part of baudrate change magic
1199 * sequence while baudrate change was requested (-B option)
1200 * - change baudrate
Marek Behún819cd322021-09-24 23:06:53 +02001201 * Otherwise decrease timeout by time elapsed.
Pali Rohár48b3ea62021-09-24 23:06:50 +02001202 */
1203 if (allow_non_xm) {
Marek Behún12df7b72021-09-24 23:06:52 +02001204 recv_until = _now() + timeout;
Pali Rohárca272042021-09-24 23:07:05 +02001205
1206 if (baudrate && !*baud_changed) {
1207 rc = kwboot_baud_magic_handle(fd, *c, baudrate);
1208 if (rc == 1)
1209 *baud_changed = 1;
1210 else if (!rc)
1211 *non_xm_print = 1;
1212 else
1213 return rc;
1214 } else if (!baudrate || !*baud_changed) {
1215 putchar(*c);
1216 fflush(stdout);
1217 *non_xm_print = 1;
1218 }
Marek Behún819cd322021-09-24 23:06:53 +02001219 } else {
Pali Rohár950ed242022-01-25 18:13:04 +01001220 if (stop_on_non_xm)
Pali Rohára6fcac22021-10-25 15:13:04 +02001221 break;
Marek Behún819cd322021-09-24 23:06:53 +02001222 timeout = recv_until - _now();
1223 if (timeout < 0) {
1224 errno = ETIMEDOUT;
1225 return -1;
1226 }
Pali Rohár48b3ea62021-09-24 23:06:50 +02001227 }
1228 }
1229
1230 return 0;
1231}
1232
1233static int
1234kwboot_xm_sendblock(int fd, struct kwboot_block *block, int allow_non_xm,
Pali Rohár5875ad42022-01-25 18:13:05 +01001235 int *done_print, int baudrate, int allow_retries)
Luka Perkovd131ad62012-05-27 11:44:51 +00001236{
Pali Rohárca272042021-09-24 23:07:05 +02001237 int non_xm_print, baud_changed;
1238 int rc, err, retries;
Luka Perkovd131ad62012-05-27 11:44:51 +00001239 char c;
1240
Pali Rohár48b3ea62021-09-24 23:06:50 +02001241 *done_print = 0;
Pali Rohár455c0d22021-10-27 20:56:58 +02001242 non_xm_print = 0;
1243 baud_changed = 0;
Pali Rohár48b3ea62021-09-24 23:06:50 +02001244
Pali Rohárd14a3422021-10-25 15:13:03 +02001245 retries = 0;
Luka Perkovd131ad62012-05-27 11:44:51 +00001246 do {
Pali Rohárcab817d2021-10-27 20:56:59 +02001247 rc = kwboot_tty_send(fd, block, sizeof(*block), 1);
Luka Perkovd131ad62012-05-27 11:44:51 +00001248 if (rc)
Pali Rohár94c906a2022-01-25 18:13:03 +01001249 goto err;
Luka Perkovd131ad62012-05-27 11:44:51 +00001250
Pali Rohár48b3ea62021-09-24 23:06:50 +02001251 if (allow_non_xm && !*done_print) {
1252 kwboot_progress(100, '.');
1253 kwboot_printv("Done\n");
1254 *done_print = 1;
1255 }
Stefan Roese84899e22014-10-22 12:13:21 +02001256
Pali Rohára6fcac22021-10-25 15:13:04 +02001257 rc = kwboot_xm_recv_reply(fd, &c, retries < 3,
Pali Rohár82a9e132022-01-25 18:13:02 +01001258 retries > 8,
Pali Rohára6fcac22021-10-25 15:13:04 +02001259 allow_non_xm, &non_xm_print,
Pali Rohárca272042021-09-24 23:07:05 +02001260 baudrate, &baud_changed);
Pali Rohár48b3ea62021-09-24 23:06:50 +02001261 if (rc)
Pali Rohár94c906a2022-01-25 18:13:03 +01001262 goto err;
Stefan Roese84899e22014-10-22 12:13:21 +02001263
Pali Rohár5d8aa4c2022-01-25 18:13:06 +01001264 if (!allow_non_xm && c != ACK) {
1265 if (c == NAK && allow_retries && retries + 1 < 16)
1266 kwboot_progress(-1, '+');
1267 else
1268 kwboot_progress(-1, 'E');
1269 }
Pali Rohár5875ad42022-01-25 18:13:05 +01001270 } while (c == NAK && allow_retries && retries++ < 16);
Luka Perkovd131ad62012-05-27 11:44:51 +00001271
Marek Behún2e81b3a2021-09-24 23:06:51 +02001272 if (non_xm_print)
1273 kwboot_printv("\n");
1274
Pali Rohárca272042021-09-24 23:07:05 +02001275 if (allow_non_xm && baudrate && !baud_changed) {
1276 fprintf(stderr, "Baudrate was not changed\n");
Pali Rohárca272042021-09-24 23:07:05 +02001277 errno = EPROTO;
Pali Rohár94c906a2022-01-25 18:13:03 +01001278 return -1;
Pali Rohárca272042021-09-24 23:07:05 +02001279 }
1280
Pali Rohár9cdc2642021-09-24 23:06:54 +02001281 return _xm_reply_to_error(c);
Pali Rohár94c906a2022-01-25 18:13:03 +01001282err:
Pali Rohárca272042021-09-24 23:07:05 +02001283 err = errno;
Pali Rohárca272042021-09-24 23:07:05 +02001284 kwboot_printv("\n");
1285 errno = err;
1286 return rc;
Pali Rohár9cdc2642021-09-24 23:06:54 +02001287}
Luka Perkovd131ad62012-05-27 11:44:51 +00001288
Pali Rohár9cdc2642021-09-24 23:06:54 +02001289static int
1290kwboot_xm_finish(int fd)
1291{
1292 int rc, retries;
1293 char c;
Luka Perkovd131ad62012-05-27 11:44:51 +00001294
Pali Rohár9cdc2642021-09-24 23:06:54 +02001295 kwboot_printv("Finishing transfer\n");
1296
Pali Rohárd14a3422021-10-25 15:13:03 +02001297 retries = 0;
Pali Rohár9cdc2642021-09-24 23:06:54 +02001298 do {
1299 rc = kwboot_tty_send_char(fd, EOT);
1300 if (rc)
1301 return rc;
1302
Pali Rohára6fcac22021-10-25 15:13:04 +02001303 rc = kwboot_xm_recv_reply(fd, &c, retries < 3,
Pali Rohár82a9e132022-01-25 18:13:02 +01001304 retries > 8,
Pali Rohára6fcac22021-10-25 15:13:04 +02001305 0, NULL, 0, NULL);
Pali Rohár9cdc2642021-09-24 23:06:54 +02001306 if (rc)
1307 return rc;
Pali Rohárd14a3422021-10-25 15:13:03 +02001308 } while (c == NAK && retries++ < 16);
Pali Rohár9cdc2642021-09-24 23:06:54 +02001309
1310 return _xm_reply_to_error(c);
Luka Perkovd131ad62012-05-27 11:44:51 +00001311}
1312
1313static int
Pali Rohár2ef87f72021-09-24 23:06:48 +02001314kwboot_xmodem_one(int tty, int *pnum, int header, const uint8_t *data,
Pali Rohárca272042021-09-24 23:07:05 +02001315 size_t size, int baudrate)
Luka Perkovd131ad62012-05-27 11:44:51 +00001316{
Pali Rohár48b3ea62021-09-24 23:06:50 +02001317 int done_print = 0;
Pali Rohár2ef87f72021-09-24 23:06:48 +02001318 size_t sent, left;
1319 int rc;
Luka Perkovd131ad62012-05-27 11:44:51 +00001320
Pali Rohár2ef87f72021-09-24 23:06:48 +02001321 kwboot_printv("Sending boot image %s (%zu bytes)...\n",
1322 header ? "header" : "data", size);
Luka Perkovd131ad62012-05-27 11:44:51 +00001323
Pali Rohár2ef87f72021-09-24 23:06:48 +02001324 left = size;
1325 sent = 0;
Luka Perkovd131ad62012-05-27 11:44:51 +00001326
Pali Rohár2ef87f72021-09-24 23:06:48 +02001327 while (sent < size) {
Luka Perkovd131ad62012-05-27 11:44:51 +00001328 struct kwboot_block block;
Pali Rohár48b3ea62021-09-24 23:06:50 +02001329 int last_block;
Pali Rohár2ef87f72021-09-24 23:06:48 +02001330 size_t blksz;
Luka Perkovd131ad62012-05-27 11:44:51 +00001331
Pali Rohár2ef87f72021-09-24 23:06:48 +02001332 blksz = kwboot_xm_makeblock(&block, data, left, (*pnum)++);
1333 data += blksz;
Luka Perkovd131ad62012-05-27 11:44:51 +00001334
Pali Rohár48b3ea62021-09-24 23:06:50 +02001335 last_block = (left <= blksz);
1336
Pali Rohár5875ad42022-01-25 18:13:05 +01001337 /*
1338 * Handling of repeated xmodem packets is completely broken in
1339 * Armada 385 BootROM - it completely ignores xmodem packet
1340 * numbers, they are only used for checksum verification.
1341 * BootROM can handle a retry of the xmodem packet only during
1342 * the transmission of kwbimage header and only if BootROM
1343 * itself sent NAK response to previous attempt (it does it on
1344 * checksum failure). During the transmission of kwbimage data
1345 * part, BootROM always expects next xmodem packet, even if it
1346 * sent NAK to previous attempt - there is absolutely no way to
1347 * repair incorrectly transmitted xmodem packet during kwbimage
1348 * data part upload. Also, if kwboot receives non-ACK/NAK
1349 * response (meaning that original BootROM response was damaged
1350 * on UART) there is no way to detect if BootROM accepted xmodem
1351 * packet or not and no way to check if kwboot could repeat the
1352 * packet or not.
1353 *
1354 * Stop transfer and return failure if kwboot receives unknown
1355 * reply if non-xmodem reply is not allowed (for all xmodem
1356 * packets except the last header packet) or when non-ACK reply
1357 * is received during data part transfer.
1358 */
Pali Rohár48b3ea62021-09-24 23:06:50 +02001359 rc = kwboot_xm_sendblock(tty, &block, header && last_block,
Pali Rohár5875ad42022-01-25 18:13:05 +01001360 &done_print, baudrate, header);
Luka Perkovd131ad62012-05-27 11:44:51 +00001361 if (rc)
1362 goto out;
1363
Pali Rohár2ef87f72021-09-24 23:06:48 +02001364 sent += blksz;
1365 left -= blksz;
Luka Perkovd131ad62012-05-27 11:44:51 +00001366
Pali Rohár48b3ea62021-09-24 23:06:50 +02001367 if (!done_print)
1368 kwboot_progress(sent * 100 / size, '.');
Pali Rohár2ef87f72021-09-24 23:06:48 +02001369 }
Luka Perkovd131ad62012-05-27 11:44:51 +00001370
Pali Rohár48b3ea62021-09-24 23:06:50 +02001371 if (!done_print)
1372 kwboot_printv("Done\n");
Pali Rohár2ef87f72021-09-24 23:06:48 +02001373
1374 return 0;
Luka Perkovd131ad62012-05-27 11:44:51 +00001375out:
Pali Rohárd5ba8db2021-09-24 23:06:47 +02001376 kwboot_printv("\n");
Luka Perkovd131ad62012-05-27 11:44:51 +00001377 return rc;
Pali Rohár2ef87f72021-09-24 23:06:48 +02001378}
Luka Perkovd131ad62012-05-27 11:44:51 +00001379
Pali Rohár2ef87f72021-09-24 23:06:48 +02001380static int
Pali Rohárca272042021-09-24 23:07:05 +02001381kwboot_xmodem(int tty, const void *_img, size_t size, int baudrate)
Pali Rohár2ef87f72021-09-24 23:06:48 +02001382{
1383 const uint8_t *img = _img;
1384 int rc, pnum;
1385 size_t hdrsz;
1386
Marek Behúnfe2fd732021-09-24 23:07:01 +02001387 hdrsz = kwbheader_size(img);
Pali Rohár2ef87f72021-09-24 23:06:48 +02001388
Pali Rohárf8017c32021-11-05 23:29:58 +01001389 /*
1390 * If header size is not aligned to xmodem block size (which applies
1391 * for all images in kwbimage v0 format) then we have to ensure that
1392 * the last xmodem block of header contains beginning of the data
1393 * followed by the header. So align header size to xmodem block size.
1394 */
1395 hdrsz += (KWBOOT_XM_BLKSZ - hdrsz % KWBOOT_XM_BLKSZ) % KWBOOT_XM_BLKSZ;
1396
Pali Rohár2ef87f72021-09-24 23:06:48 +02001397 pnum = 1;
1398
Pali Rohárca272042021-09-24 23:07:05 +02001399 rc = kwboot_xmodem_one(tty, &pnum, 1, img, hdrsz, baudrate);
Pali Rohár2ef87f72021-09-24 23:06:48 +02001400 if (rc)
1401 return rc;
1402
Pali Rohárf8017c32021-11-05 23:29:58 +01001403 /*
1404 * If we have already sent image data as a part of the last
1405 * xmodem header block then we have nothing more to send.
1406 */
1407 if (hdrsz < size) {
1408 img += hdrsz;
1409 size -= hdrsz;
1410 rc = kwboot_xmodem_one(tty, &pnum, 0, img, size, 0);
1411 if (rc)
1412 return rc;
1413 }
Pali Rohár2ef87f72021-09-24 23:06:48 +02001414
Pali Rohárca272042021-09-24 23:07:05 +02001415 rc = kwboot_xm_finish(tty);
1416 if (rc)
1417 return rc;
1418
1419 if (baudrate) {
Pali Rohárca272042021-09-24 23:07:05 +02001420 kwboot_printv("\nChanging baudrate back to 115200 Bd\n\n");
1421 rc = kwboot_tty_change_baudrate(tty, 115200);
1422 if (rc)
1423 return rc;
1424 }
1425
1426 return 0;
Luka Perkovd131ad62012-05-27 11:44:51 +00001427}
1428
1429static int
Pali Roháre8d26e82022-03-02 11:49:23 +01001430kwboot_term_pipe(int in, int out, const char *quit, int *s, const char *kbs, int *k)
Luka Perkovd131ad62012-05-27 11:44:51 +00001431{
Pali Rohárde751402022-02-03 17:45:20 +01001432 char buf[128];
Pali Roháre8d26e82022-03-02 11:49:23 +01001433 ssize_t nin, noff;
Luka Perkovd131ad62012-05-27 11:44:51 +00001434
Pali Rohárde751402022-02-03 17:45:20 +01001435 nin = read(in, buf, sizeof(buf));
Willy Tarreau4469bd72018-07-03 12:10:31 -04001436 if (nin <= 0)
Luka Perkovd131ad62012-05-27 11:44:51 +00001437 return -1;
1438
Pali Roháre8d26e82022-03-02 11:49:23 +01001439 noff = 0;
1440
1441 if (quit || kbs) {
Luka Perkovd131ad62012-05-27 11:44:51 +00001442 int i;
1443
1444 for (i = 0; i < nin; i++) {
Pali Roháre8d26e82022-03-02 11:49:23 +01001445 if ((quit || kbs) &&
1446 (!quit || buf[i] != quit[*s]) &&
1447 (!kbs || buf[i] != kbs[*k])) {
1448 const char *prefix;
1449 int plen;
1450
1451 if (quit && kbs) {
1452 prefix = (*s >= *k) ? quit : kbs;
1453 plen = (*s >= *k) ? *s : *k;
1454 } else if (quit) {
1455 prefix = quit;
1456 plen = *s;
1457 } else {
1458 prefix = kbs;
1459 plen = *k;
1460 }
1461
1462 if (plen > i && kwboot_write(out, prefix, plen - i) < 0)
1463 return -1;
1464 }
1465
1466 if (quit && buf[i] == quit[*s]) {
Luka Perkovd131ad62012-05-27 11:44:51 +00001467 (*s)++;
Pali Rohárde751402022-02-03 17:45:20 +01001468 if (!quit[*s]) {
Pali Rohár7938b3b2022-02-18 12:24:13 +01001469 nin = (i > *s) ? (i - *s) : 0;
Pali Rohárde751402022-02-03 17:45:20 +01001470 break;
1471 }
Pali Roháre8d26e82022-03-02 11:49:23 +01001472 } else if (quit) {
Marek Behúne453bb42021-09-24 23:06:41 +02001473 *s = 0;
Pali Rohárb943eee2021-07-23 11:14:20 +02001474 }
Pali Roháre8d26e82022-03-02 11:49:23 +01001475
1476 if (kbs && buf[i] == kbs[*k]) {
1477 (*k)++;
1478 if (!kbs[*k]) {
1479 if (i > *k + noff &&
1480 kwboot_write(out, buf + noff, i - *k - noff) < 0)
1481 return -1;
1482 /*
1483 * Replace backspace key by '\b' (0x08)
1484 * byte which is the only recognized
1485 * backspace byte by Marvell BootROM.
1486 */
1487 if (write(out, "\x08", 1) < 0)
1488 return -1;
1489 noff = i + 1;
1490 *k = 0;
1491 }
1492 } else if (kbs) {
1493 *k = 0;
1494 }
Luka Perkovd131ad62012-05-27 11:44:51 +00001495 }
Pali Rohárde751402022-02-03 17:45:20 +01001496
Pali Roháre8d26e82022-03-02 11:49:23 +01001497 if (i == nin) {
1498 i = 0;
1499 if (quit && i < *s)
1500 i = *s;
1501 if (kbs && i < *k)
1502 i = *k;
1503 nin -= (nin > i) ? i : nin;
1504 }
Luka Perkovd131ad62012-05-27 11:44:51 +00001505 }
1506
Pali Roháre8d26e82022-03-02 11:49:23 +01001507 if (nin > noff && kwboot_write(out, buf + noff, nin - noff) < 0)
Marek Behúne453bb42021-09-24 23:06:41 +02001508 return -1;
Luka Perkovd131ad62012-05-27 11:44:51 +00001509
1510 return 0;
1511}
1512
1513static int
1514kwboot_terminal(int tty)
1515{
Pali Roháre8d26e82022-03-02 11:49:23 +01001516 int rc, in, s, k;
1517 const char *kbs = NULL;
Marek Behún46237e62021-09-24 23:06:40 +02001518 const char *quit = "\34c";
Luka Perkovd131ad62012-05-27 11:44:51 +00001519 struct termios otio, tio;
1520
1521 rc = -1;
1522
1523 in = STDIN_FILENO;
1524 if (isatty(in)) {
1525 rc = tcgetattr(in, &otio);
1526 if (!rc) {
1527 tio = otio;
1528 cfmakeraw(&tio);
1529 rc = tcsetattr(in, TCSANOW, &tio);
1530 }
1531 if (rc) {
1532 perror("tcsetattr");
1533 goto out;
1534 }
1535
Pali Roháre8d26e82022-03-02 11:49:23 +01001536 /*
1537 * Get sequence for backspace key used by the current
1538 * terminal. Every occurrence of this sequence will be
1539 * replaced by '\b' byte which is the only recognized
1540 * backspace byte by Marvell BootROM.
1541 *
1542 * Note that we cannot read this sequence from termios
1543 * c_cc[VERASE] as VERASE is valid only when ICANON is
1544 * set in termios c_lflag, which is not case for us.
1545 *
1546 * Also most terminals do not set termios c_cc[VERASE]
1547 * as c_cc[VERASE] can specify only one-byte sequence
1548 * and instead let applications to read (possible
1549 * multi-byte) sequence for backspace key from "kbs"
1550 * terminfo database based on $TERM env variable.
1551 *
1552 * So read "kbs" from terminfo database via tigetstr()
1553 * call after successful setupterm(). Most terminals
1554 * use byte 0x7F for backspace key, so replacement with
1555 * '\b' is required.
1556 */
1557 if (setupterm(NULL, STDOUT_FILENO, &rc) == 0) {
1558 kbs = tigetstr("kbs");
1559 if (kbs == (char *)-1)
1560 kbs = NULL;
1561 }
1562
Luka Perkovd131ad62012-05-27 11:44:51 +00001563 kwboot_printv("[Type Ctrl-%c + %c to quit]\r\n",
Marek Behún5fa04f42021-09-24 23:07:11 +02001564 quit[0] | 0100, quit[1]);
Luka Perkovd131ad62012-05-27 11:44:51 +00001565 } else
1566 in = -1;
1567
1568 rc = 0;
1569 s = 0;
Pali Roháre8d26e82022-03-02 11:49:23 +01001570 k = 0;
Luka Perkovd131ad62012-05-27 11:44:51 +00001571
1572 do {
1573 fd_set rfds;
1574 int nfds = 0;
1575
Pali Rohár0a143412021-10-25 15:12:52 +02001576 FD_ZERO(&rfds);
Luka Perkovd131ad62012-05-27 11:44:51 +00001577 FD_SET(tty, &rfds);
1578 nfds = nfds < tty ? tty : nfds;
1579
1580 if (in >= 0) {
1581 FD_SET(in, &rfds);
1582 nfds = nfds < in ? in : nfds;
1583 }
1584
1585 nfds = select(nfds + 1, &rfds, NULL, NULL, NULL);
1586 if (nfds < 0)
1587 break;
1588
1589 if (FD_ISSET(tty, &rfds)) {
Pali Roháre8d26e82022-03-02 11:49:23 +01001590 rc = kwboot_term_pipe(tty, STDOUT_FILENO, NULL, NULL, NULL, NULL);
Luka Perkovd131ad62012-05-27 11:44:51 +00001591 if (rc)
1592 break;
1593 }
1594
Marek Behúnf30cb0d2021-09-24 23:06:39 +02001595 if (in >= 0 && FD_ISSET(in, &rfds)) {
Pali Roháre8d26e82022-03-02 11:49:23 +01001596 rc = kwboot_term_pipe(in, tty, quit, &s, kbs, &k);
Luka Perkovd131ad62012-05-27 11:44:51 +00001597 if (rc)
1598 break;
1599 }
1600 } while (quit[s] != 0);
1601
Pali Rohárec0fe5b2021-07-23 11:14:18 +02001602 if (in >= 0)
1603 tcsetattr(in, TCSANOW, &otio);
Pali Rohár49a0a3b2021-07-23 11:14:19 +02001604 printf("\n");
Luka Perkovd131ad62012-05-27 11:44:51 +00001605out:
1606 return rc;
1607}
1608
1609static void *
Pali Rohár04ced022021-09-24 23:07:03 +02001610kwboot_read_image(const char *path, size_t *size, size_t reserve)
Luka Perkovd131ad62012-05-27 11:44:51 +00001611{
Pali Rohárddc04fa2021-09-24 23:06:55 +02001612 int rc, fd;
Luka Perkovd131ad62012-05-27 11:44:51 +00001613 void *img;
Pali Rohára339d6c2022-04-06 15:18:59 +02001614 off_t len;
Pali Rohár04ced022021-09-24 23:07:03 +02001615 off_t tot;
Luka Perkovd131ad62012-05-27 11:44:51 +00001616
1617 rc = -1;
Luka Perkovd131ad62012-05-27 11:44:51 +00001618 img = NULL;
1619
1620 fd = open(path, O_RDONLY);
1621 if (fd < 0)
1622 goto out;
1623
Pali Rohára339d6c2022-04-06 15:18:59 +02001624 len = lseek(fd, 0, SEEK_END);
1625 if (len == (off_t)-1)
Luka Perkovd131ad62012-05-27 11:44:51 +00001626 goto out;
1627
Pali Rohára339d6c2022-04-06 15:18:59 +02001628 if (lseek(fd, 0, SEEK_SET) == (off_t)-1)
1629 goto out;
1630
1631 img = malloc(len + reserve);
Pali Rohár04ced022021-09-24 23:07:03 +02001632 if (!img)
Luka Perkovd131ad62012-05-27 11:44:51 +00001633 goto out;
Pali Rohár04ced022021-09-24 23:07:03 +02001634
1635 tot = 0;
Pali Rohára339d6c2022-04-06 15:18:59 +02001636 while (tot < len) {
1637 ssize_t rd = read(fd, img + tot, len - tot);
Pali Rohár04ced022021-09-24 23:07:03 +02001638
1639 if (rd < 0)
1640 goto out;
1641
1642 tot += rd;
1643
Pali Rohára339d6c2022-04-06 15:18:59 +02001644 if (!rd && tot < len) {
Pali Rohár04ced022021-09-24 23:07:03 +02001645 errno = EIO;
1646 goto out;
1647 }
Luka Perkovd131ad62012-05-27 11:44:51 +00001648 }
1649
1650 rc = 0;
Pali Rohára339d6c2022-04-06 15:18:59 +02001651 *size = len;
Luka Perkovd131ad62012-05-27 11:44:51 +00001652out:
1653 if (rc && img) {
Pali Rohár04ced022021-09-24 23:07:03 +02001654 free(img);
Luka Perkovd131ad62012-05-27 11:44:51 +00001655 img = NULL;
1656 }
1657 if (fd >= 0)
1658 close(fd);
1659
1660 return img;
1661}
1662
1663static uint8_t
Marek Behúnfe2fd732021-09-24 23:07:01 +02001664kwboot_hdr_csum8(const void *hdr)
Luka Perkovd131ad62012-05-27 11:44:51 +00001665{
Marek Behúnfe2fd732021-09-24 23:07:01 +02001666 const uint8_t *data = hdr;
1667 uint8_t csum;
1668 size_t size;
1669
1670 size = kwbheader_size_for_csum(hdr);
Luka Perkovd131ad62012-05-27 11:44:51 +00001671
1672 for (csum = 0; size-- > 0; data++)
1673 csum += *data;
1674
1675 return csum;
1676}
1677
Pali Rohárad9a3ac2021-10-25 15:12:55 +02001678static uint32_t *
1679kwboot_img_csum32_ptr(void *img)
1680{
1681 struct main_hdr_v1 *hdr = img;
1682 uint32_t datasz;
1683
1684 datasz = le32_to_cpu(hdr->blocksize) - sizeof(uint32_t);
1685
1686 return img + le32_to_cpu(hdr->srcaddr) + datasz;
1687}
1688
1689static uint32_t
1690kwboot_img_csum32(const void *img)
1691{
1692 const struct main_hdr_v1 *hdr = img;
1693 uint32_t datasz, csum = 0;
1694 const uint32_t *data;
1695
1696 datasz = le32_to_cpu(hdr->blocksize) - sizeof(csum);
1697 if (datasz % sizeof(uint32_t))
1698 return 0;
1699
1700 data = img + le32_to_cpu(hdr->srcaddr);
1701 while (datasz > 0) {
1702 csum += le32_to_cpu(*data++);
1703 datasz -= 4;
1704 }
1705
1706 return cpu_to_le32(csum);
1707}
1708
Luka Perkovd131ad62012-05-27 11:44:51 +00001709static int
Pali Rohár550c9302021-09-24 23:06:57 +02001710kwboot_img_is_secure(void *img)
1711{
1712 struct opt_hdr_v1 *ohdr;
1713
1714 for_each_opt_hdr_v1 (ohdr, img)
1715 if (ohdr->headertype == OPT_HDR_V1_SECURE_TYPE)
1716 return 1;
1717
1718 return 0;
1719}
1720
Pali Rohárca272042021-09-24 23:07:05 +02001721static void *
Pali Rohár063cb352021-10-25 15:12:56 +02001722kwboot_img_grow_data_right(void *img, size_t *size, size_t grow)
Pali Rohárca272042021-09-24 23:07:05 +02001723{
Pali Rohárca272042021-09-24 23:07:05 +02001724 struct main_hdr_v1 *hdr = img;
Pali Rohár063cb352021-10-25 15:12:56 +02001725 void *result;
Pali Rohárca272042021-09-24 23:07:05 +02001726
Pali Rohár063cb352021-10-25 15:12:56 +02001727 /*
1728 * 32-bit checksum comes after end of image code, so we will be putting
1729 * new code there. So we get this pointer and then increase data size
1730 * (since increasing data size changes kwboot_img_csum32_ptr() return
1731 * value).
1732 */
1733 result = kwboot_img_csum32_ptr(img);
Pali Rohárca272042021-09-24 23:07:05 +02001734 hdr->blocksize = cpu_to_le32(le32_to_cpu(hdr->blocksize) + grow);
Pali Rohár063cb352021-10-25 15:12:56 +02001735 *size += grow;
Pali Rohárca272042021-09-24 23:07:05 +02001736
Pali Rohár063cb352021-10-25 15:12:56 +02001737 return result;
Pali Rohárca272042021-09-24 23:07:05 +02001738}
1739
Pali Rohár04ced022021-09-24 23:07:03 +02001740static void
1741kwboot_img_grow_hdr(void *img, size_t *size, size_t grow)
1742{
1743 uint32_t hdrsz, datasz, srcaddr;
1744 struct main_hdr_v1 *hdr = img;
Pali Rohárd656f5a2021-10-25 15:13:02 +02001745 struct opt_hdr_v1 *ohdr;
Pali Rohár04ced022021-09-24 23:07:03 +02001746 uint8_t *data;
1747
1748 srcaddr = le32_to_cpu(hdr->srcaddr);
1749
Pali Rohárd656f5a2021-10-25 15:13:02 +02001750 /* calculate real used space in kwbimage header */
1751 if (kwbimage_version(img) == 0) {
1752 hdrsz = kwbheader_size(img);
1753 } else {
1754 hdrsz = sizeof(*hdr);
1755 for_each_opt_hdr_v1 (ohdr, hdr)
1756 hdrsz += opt_hdr_v1_size(ohdr);
1757 }
1758
Pali Rohár04ced022021-09-24 23:07:03 +02001759 data = (uint8_t *)img + srcaddr;
1760 datasz = *size - srcaddr;
1761
1762 /* only move data if there is not enough space */
1763 if (hdrsz + grow > srcaddr) {
1764 size_t need = hdrsz + grow - srcaddr;
1765
1766 /* move data by enough bytes */
1767 memmove(data + need, data, datasz);
1768
1769 hdr->srcaddr = cpu_to_le32(srcaddr + need);
1770 *size += need;
1771 }
1772
1773 if (kwbimage_version(img) == 1) {
1774 hdrsz += grow;
Pali Rohárd656f5a2021-10-25 15:13:02 +02001775 if (hdrsz > kwbheader_size(img)) {
1776 hdr->headersz_msb = hdrsz >> 16;
1777 hdr->headersz_lsb = cpu_to_le16(hdrsz & 0xffff);
1778 }
Pali Rohár04ced022021-09-24 23:07:03 +02001779 }
1780}
1781
Pali Rohárca272042021-09-24 23:07:05 +02001782static void *
1783kwboot_add_bin_ohdr_v1(void *img, size_t *size, uint32_t binsz)
1784{
1785 struct main_hdr_v1 *hdr = img;
1786 struct opt_hdr_v1 *ohdr;
Pali Rohára85a71d2021-10-21 16:46:06 +02001787 uint32_t num_args;
1788 uint32_t offset;
Pali Rohárca272042021-09-24 23:07:05 +02001789 uint32_t ohdrsz;
Pali Roháre511cc32021-10-25 15:13:01 +02001790 uint8_t *prev_ext;
Pali Rohárca272042021-09-24 23:07:05 +02001791
Pali Rohár44691032022-01-12 18:20:52 +01001792 if (hdr->ext) {
Pali Rohárca272042021-09-24 23:07:05 +02001793 for_each_opt_hdr_v1 (ohdr, img)
1794 if (opt_hdr_v1_next(ohdr) == NULL)
1795 break;
1796
Pali Roháre511cc32021-10-25 15:13:01 +02001797 prev_ext = opt_hdr_v1_ext(ohdr);
1798 ohdr = _opt_hdr_v1_next(ohdr);
Pali Rohárca272042021-09-24 23:07:05 +02001799 } else {
Pali Rohárca272042021-09-24 23:07:05 +02001800 ohdr = (void *)(hdr + 1);
Pali Roháre511cc32021-10-25 15:13:01 +02001801 prev_ext = &hdr->ext;
Pali Rohárca272042021-09-24 23:07:05 +02001802 }
1803
Pali Rohára85a71d2021-10-21 16:46:06 +02001804 /*
1805 * ARM executable code inside the BIN header on some mvebu platforms
1806 * (e.g. A370, AXP) must always be aligned with the 128-bit boundary.
1807 * This requirement can be met by inserting dummy arguments into
1808 * BIN header, if needed.
1809 */
1810 offset = &ohdr->data[4] - (char *)img;
1811 num_args = ((16 - offset % 16) % 16) / sizeof(uint32_t);
1812
1813 ohdrsz = sizeof(*ohdr) + 4 + 4 * num_args + binsz + 4;
1814 kwboot_img_grow_hdr(hdr, size, ohdrsz);
1815
Pali Rohár44691032022-01-12 18:20:52 +01001816 *prev_ext = 1;
Pali Roháre511cc32021-10-25 15:13:01 +02001817
Pali Rohárca272042021-09-24 23:07:05 +02001818 ohdr->headertype = OPT_HDR_V1_BINARY_TYPE;
1819 ohdr->headersz_msb = ohdrsz >> 16;
1820 ohdr->headersz_lsb = cpu_to_le16(ohdrsz & 0xffff);
1821
1822 memset(&ohdr->data[0], 0, ohdrsz - sizeof(*ohdr));
Pali Rohára85a71d2021-10-21 16:46:06 +02001823 *(uint32_t *)&ohdr->data[0] = cpu_to_le32(num_args);
Pali Rohárca272042021-09-24 23:07:05 +02001824
Pali Rohára85a71d2021-10-21 16:46:06 +02001825 return &ohdr->data[4 + 4 * num_args];
Pali Rohárca272042021-09-24 23:07:05 +02001826}
1827
1828static void
Pali Rohár8dbe0272021-10-27 20:57:02 +02001829_inject_baudrate_change_code(void *img, size_t *size, int for_data,
Pali Rohár063cb352021-10-25 15:12:56 +02001830 int old_baud, int new_baud)
Pali Rohárca272042021-09-24 23:07:05 +02001831{
Pali Rohár063cb352021-10-25 15:12:56 +02001832 struct main_hdr_v1 *hdr = img;
Pali Rohár8dbe0272021-10-27 20:57:02 +02001833 uint32_t orig_datasz;
1834 uint32_t codesz;
Pali Rohár063cb352021-10-25 15:12:56 +02001835 uint8_t *code;
Pali Rohárca272042021-09-24 23:07:05 +02001836
Pali Rohár8dbe0272021-10-27 20:57:02 +02001837 if (for_data) {
Pali Rohár063cb352021-10-25 15:12:56 +02001838 orig_datasz = le32_to_cpu(hdr->blocksize) - sizeof(uint32_t);
1839
Pali Rohár8dbe0272021-10-27 20:57:02 +02001840 codesz = sizeof(kwboot_baud_code) +
1841 sizeof(kwboot_baud_code_data_jump);
1842 code = kwboot_img_grow_data_right(img, size, codesz);
Pali Rohár063cb352021-10-25 15:12:56 +02001843 } else {
Pali Rohár8dbe0272021-10-27 20:57:02 +02001844 codesz = sizeof(kwboot_baud_code_binhdr_pre) +
1845 sizeof(kwboot_baud_code) +
1846 sizeof(kwboot_baud_code_binhdr_post);
Pali Rohár063cb352021-10-25 15:12:56 +02001847 code = kwboot_add_bin_ohdr_v1(img, size, codesz);
Pali Rohár8dbe0272021-10-27 20:57:02 +02001848
1849 codesz = sizeof(kwboot_baud_code_binhdr_pre);
1850 memcpy(code, kwboot_baud_code_binhdr_pre, codesz);
1851 code += codesz;
Pali Rohárca272042021-09-24 23:07:05 +02001852 }
1853
Pali Rohár8dbe0272021-10-27 20:57:02 +02001854 codesz = sizeof(kwboot_baud_code) - 2 * sizeof(uint32_t);
1855 memcpy(code, kwboot_baud_code, codesz);
1856 code += codesz;
1857 *(uint32_t *)code = cpu_to_le32(old_baud);
1858 code += sizeof(uint32_t);
1859 *(uint32_t *)code = cpu_to_le32(new_baud);
1860 code += sizeof(uint32_t);
1861
1862 if (for_data) {
1863 codesz = sizeof(kwboot_baud_code_data_jump) - sizeof(uint32_t);
1864 memcpy(code, kwboot_baud_code_data_jump, codesz);
1865 code += codesz;
1866 *(uint32_t *)code = hdr->execaddr;
1867 code += sizeof(uint32_t);
1868 hdr->execaddr = cpu_to_le32(le32_to_cpu(hdr->destaddr) + orig_datasz);
1869 } else {
1870 codesz = sizeof(kwboot_baud_code_binhdr_post);
1871 memcpy(code, kwboot_baud_code_binhdr_post, codesz);
1872 code += codesz;
1873 }
Pali Rohárca272042021-09-24 23:07:05 +02001874}
1875
Pali Rohár550c9302021-09-24 23:06:57 +02001876static int
Pali Rohárca272042021-09-24 23:07:05 +02001877kwboot_img_patch(void *img, size_t *size, int baudrate)
Luka Perkovd131ad62012-05-27 11:44:51 +00001878{
Stefan Roesee29f1db2015-09-29 09:19:59 +02001879 struct main_hdr_v1 *hdr;
Pali Rohár792e4232021-09-24 23:06:58 +02001880 uint32_t srcaddr;
Luka Perkovd131ad62012-05-27 11:44:51 +00001881 uint8_t csum;
Marek Behún5c8f8122021-09-24 23:07:04 +02001882 size_t hdrsz;
Stefan Roesee29f1db2015-09-29 09:19:59 +02001883 int image_ver;
Pali Rohár550c9302021-09-24 23:06:57 +02001884 int is_secure;
Luka Perkovd131ad62012-05-27 11:44:51 +00001885
Luka Perkovd131ad62012-05-27 11:44:51 +00001886 hdr = img;
1887
Marek Behúnb4eea8f2021-09-24 23:07:12 +02001888 if (*size < sizeof(struct main_hdr_v1))
1889 goto err;
Luka Perkovd131ad62012-05-27 11:44:51 +00001890
Marek Behúnacb0b382021-09-24 23:07:00 +02001891 image_ver = kwbimage_version(img);
Pali Rohár5029d7b2021-07-23 11:14:22 +02001892 if (image_ver != 0 && image_ver != 1) {
Stefan Roesee29f1db2015-09-29 09:19:59 +02001893 fprintf(stderr, "Invalid image header version\n");
Marek Behúnb4eea8f2021-09-24 23:07:12 +02001894 goto err;
Stefan Roesee29f1db2015-09-29 09:19:59 +02001895 }
1896
Marek Behúnfe2fd732021-09-24 23:07:01 +02001897 hdrsz = kwbheader_size(hdr);
Stefan Roesee29f1db2015-09-29 09:19:59 +02001898
Marek Behúnb4eea8f2021-09-24 23:07:12 +02001899 if (*size < hdrsz)
1900 goto err;
Pali Rohár825a2ca2021-07-23 11:14:21 +02001901
Marek Behúnfe2fd732021-09-24 23:07:01 +02001902 csum = kwboot_hdr_csum8(hdr) - hdr->checksum;
Marek Behúnb4eea8f2021-09-24 23:07:12 +02001903 if (csum != hdr->checksum)
1904 goto err;
Luka Perkovd131ad62012-05-27 11:44:51 +00001905
Pali Rohár792e4232021-09-24 23:06:58 +02001906 srcaddr = le32_to_cpu(hdr->srcaddr);
1907
1908 switch (hdr->blockid) {
1909 case IBR_HDR_SATA_ID:
Pali Roháre1c4ed52023-01-21 13:45:36 +01001910 hdr->srcaddr = cpu_to_le32(srcaddr * 512);
Pali Rohár792e4232021-09-24 23:06:58 +02001911 break;
1912
Pali Rohár792e4232021-09-24 23:06:58 +02001913 case IBR_HDR_PEX_ID:
1914 if (srcaddr == 0xFFFFFFFF)
1915 hdr->srcaddr = cpu_to_le32(hdrsz);
1916 break;
Pali Rohárf2c644e2021-09-24 23:06:59 +02001917
1918 case IBR_HDR_SPI_ID:
1919 if (hdr->destaddr == cpu_to_le32(0xFFFFFFFF)) {
1920 kwboot_printv("Patching destination and execution addresses from SPI/NOR XIP area to DDR area 0x00800000\n");
1921 hdr->destaddr = cpu_to_le32(0x00800000);
1922 hdr->execaddr = cpu_to_le32(0x00800000);
1923 }
1924 break;
Pali Rohár792e4232021-09-24 23:06:58 +02001925 }
1926
Pali Rohár04ced022021-09-24 23:07:03 +02001927 if (hdrsz > le32_to_cpu(hdr->srcaddr) ||
Marek Behúnb4eea8f2021-09-24 23:07:12 +02001928 *size < le32_to_cpu(hdr->srcaddr) + le32_to_cpu(hdr->blocksize))
1929 goto err;
Pali Rohár04ced022021-09-24 23:07:03 +02001930
Pali Rohárad9a3ac2021-10-25 15:12:55 +02001931 if (kwboot_img_csum32(img) != *kwboot_img_csum32_ptr(img))
1932 goto err;
1933
Pali Rohár550c9302021-09-24 23:06:57 +02001934 is_secure = kwboot_img_is_secure(img);
Luka Perkovd131ad62012-05-27 11:44:51 +00001935
Pali Rohár550c9302021-09-24 23:06:57 +02001936 if (hdr->blockid != IBR_HDR_UART_ID) {
1937 if (is_secure) {
1938 fprintf(stderr,
1939 "Image has secure header with signature for non-UART booting\n");
Marek Behúnb4eea8f2021-09-24 23:07:12 +02001940 goto err;
Pali Rohár550c9302021-09-24 23:06:57 +02001941 }
1942
1943 kwboot_printv("Patching image boot signature to UART\n");
1944 hdr->blockid = IBR_HDR_UART_ID;
1945 }
Luka Perkovd131ad62012-05-27 11:44:51 +00001946
Pali Rohár0089f612021-10-22 12:37:47 +02001947 if (!is_secure) {
Pali Rohár4bebab62021-10-25 15:12:58 +02001948 if (image_ver == 1) {
1949 /*
1950 * Tell BootROM to send BootROM messages to UART port
1951 * number 0 (used also for UART booting) with default
1952 * baudrate (which should be 115200) and do not touch
1953 * UART MPP configuration.
1954 */
Pali Rohárffccee22022-01-25 18:13:13 +01001955 hdr->flags |= 0x1;
Pali Rohár4bebab62021-10-25 15:12:58 +02001956 hdr->options &= ~0x1F;
1957 hdr->options |= MAIN_HDR_V1_OPT_BAUD_DEFAULT;
1958 hdr->options |= 0 << 3;
1959 }
Pali Rohár0089f612021-10-22 12:37:47 +02001960 if (image_ver == 0)
1961 ((struct main_hdr_v0 *)img)->nandeccmode = IBR_HDR_ECC_DISABLED;
1962 hdr->nandpagesize = 0;
1963 }
1964
Pali Rohárca272042021-09-24 23:07:05 +02001965 if (baudrate) {
Pali Rohárca272042021-09-24 23:07:05 +02001966 if (image_ver == 0) {
1967 fprintf(stderr,
1968 "Cannot inject code for changing baudrate into v0 image header\n");
Marek Behúnb4eea8f2021-09-24 23:07:12 +02001969 goto err;
Pali Rohárca272042021-09-24 23:07:05 +02001970 }
1971
1972 if (is_secure) {
1973 fprintf(stderr,
1974 "Cannot inject code for changing baudrate into image with secure header\n");
Marek Behúnb4eea8f2021-09-24 23:07:12 +02001975 goto err;
Pali Rohárca272042021-09-24 23:07:05 +02001976 }
1977
1978 /*
1979 * First inject code that changes the baudrate from the default
1980 * value of 115200 Bd to requested value. This code is inserted
1981 * as a new opt hdr, so it is executed by BootROM after the
1982 * header part is received.
1983 */
1984 kwboot_printv("Injecting binary header code for changing baudrate to %d Bd\n",
1985 baudrate);
Pali Rohár063cb352021-10-25 15:12:56 +02001986 _inject_baudrate_change_code(img, size, 0, 115200, baudrate);
Pali Rohárca272042021-09-24 23:07:05 +02001987
1988 /*
1989 * Now inject code that changes the baudrate back to 115200 Bd.
Pali Rohár063cb352021-10-25 15:12:56 +02001990 * This code is appended after the data part of the image, and
1991 * execaddr is changed so that it is executed before U-Boot
1992 * proper.
Pali Rohárca272042021-09-24 23:07:05 +02001993 */
1994 kwboot_printv("Injecting code for changing baudrate back\n");
Pali Rohár063cb352021-10-25 15:12:56 +02001995 _inject_baudrate_change_code(img, size, 1, baudrate, 115200);
Pali Rohárca272042021-09-24 23:07:05 +02001996
Pali Rohár82c5a0a2021-10-25 15:12:57 +02001997 /* Update the 32-bit data checksum */
1998 *kwboot_img_csum32_ptr(img) = kwboot_img_csum32(img);
1999
Pali Rohárca272042021-09-24 23:07:05 +02002000 /* recompute header size */
2001 hdrsz = kwbheader_size(hdr);
2002 }
2003
Pali Rohár04ced022021-09-24 23:07:03 +02002004 if (hdrsz % KWBOOT_XM_BLKSZ) {
Pali Roháred792c22021-10-25 15:13:00 +02002005 size_t grow = KWBOOT_XM_BLKSZ - hdrsz % KWBOOT_XM_BLKSZ;
Pali Rohár04ced022021-09-24 23:07:03 +02002006
2007 if (is_secure) {
2008 fprintf(stderr, "Cannot align image with secure header\n");
Marek Behúnb4eea8f2021-09-24 23:07:12 +02002009 goto err;
Pali Rohár04ced022021-09-24 23:07:03 +02002010 }
2011
2012 kwboot_printv("Aligning image header to Xmodem block size\n");
Pali Roháred792c22021-10-25 15:13:00 +02002013 kwboot_img_grow_hdr(img, size, grow);
Pali Rohár04ced022021-09-24 23:07:03 +02002014 }
2015
Marek Behúnfe2fd732021-09-24 23:07:01 +02002016 hdr->checksum = kwboot_hdr_csum8(hdr) - csum;
Luka Perkovd131ad62012-05-27 11:44:51 +00002017
Pali Rohár04ced022021-09-24 23:07:03 +02002018 *size = le32_to_cpu(hdr->srcaddr) + le32_to_cpu(hdr->blocksize);
Marek Behúnb4eea8f2021-09-24 23:07:12 +02002019 return 0;
2020err:
2021 errno = EINVAL;
2022 return -1;
Luka Perkovd131ad62012-05-27 11:44:51 +00002023}
2024
2025static void
2026kwboot_usage(FILE *stream, char *progname)
2027{
2028 fprintf(stream,
Pali Rohárbdc4dba2022-03-02 11:49:24 +01002029 "Usage: %s [OPTIONS] [-b <image> | -D <image> | -b | -d ] [-B <baud> ] [-t] <TTY>\n",
Stefan Roese84899e22014-10-22 12:13:21 +02002030 progname);
Luka Perkovd131ad62012-05-27 11:44:51 +00002031 fprintf(stream, "\n");
Stefan Roese84899e22014-10-22 12:13:21 +02002032 fprintf(stream,
Pali Rohár0b5909d2022-03-02 11:49:26 +01002033 " -b <image>: boot <image> with preamble (Kirkwood, Avanta, Armada 370/XP/375/38x/39x)\n");
Stefan Roese84899e22014-10-22 12:13:21 +02002034 fprintf(stream,
2035 " -D <image>: boot <image> without preamble (Dove)\n");
Pali Rohárbdc4dba2022-03-02 11:49:24 +01002036 fprintf(stream, " -b: enter xmodem boot mode\n");
2037 fprintf(stream, " -d: enter console debug mode\n");
Stefan Roese84899e22014-10-22 12:13:21 +02002038 fprintf(stream, " -a: use timings for Armada XP\n");
Stefan Roese1c0df9e2015-05-29 13:25:04 +02002039 fprintf(stream, " -s <resp-timeo>: use specific response-timeout\n");
Kevin Smith7497a6a2016-02-16 21:28:19 +00002040 fprintf(stream,
2041 " -o <block-timeo>: use specific xmodem block timeout\n");
Luka Perkovd131ad62012-05-27 11:44:51 +00002042 fprintf(stream, "\n");
2043 fprintf(stream, " -t: mini terminal\n");
2044 fprintf(stream, "\n");
2045 fprintf(stream, " -B <baud>: set baud rate\n");
2046 fprintf(stream, "\n");
2047}
2048
2049int
2050main(int argc, char **argv)
2051{
2052 const char *ttypath, *imgpath;
Pali Rohárddc04fa2021-09-24 23:06:55 +02002053 int rv, rc, tty, term;
Pali Rohárc1d911f2022-03-02 11:49:20 +01002054 int bootmsg;
2055 int debugmsg;
Luka Perkovd131ad62012-05-27 11:44:51 +00002056 void *img;
2057 size_t size;
Pali Rohárca272042021-09-24 23:07:05 +02002058 size_t after_img_rsv;
2059 int baudrate;
Pali Rohárc513fe42022-01-25 18:13:07 +01002060 int prev_optind;
2061 int c;
Luka Perkovd131ad62012-05-27 11:44:51 +00002062
2063 rv = 1;
2064 tty = -1;
Pali Rohárc1d911f2022-03-02 11:49:20 +01002065 bootmsg = 0;
2066 debugmsg = 0;
Luka Perkovd131ad62012-05-27 11:44:51 +00002067 imgpath = NULL;
2068 img = NULL;
2069 term = 0;
Luka Perkovd131ad62012-05-27 11:44:51 +00002070 size = 0;
Pali Rohárca272042021-09-24 23:07:05 +02002071 after_img_rsv = KWBOOT_XM_BLKSZ;
2072 baudrate = 115200;
Luka Perkovd131ad62012-05-27 11:44:51 +00002073
Pali Rohár75176dc2021-11-05 23:30:42 +01002074 printf("kwboot version %s\n", PLAIN_VERSION);
2075
Luka Perkovd131ad62012-05-27 11:44:51 +00002076 kwboot_verbose = isatty(STDOUT_FILENO);
2077
2078 do {
Pali Rohárc513fe42022-01-25 18:13:07 +01002079 prev_optind = optind;
2080 c = getopt(argc, argv, "hbptaB:dD:q:s:o:");
Luka Perkovd131ad62012-05-27 11:44:51 +00002081 if (c < 0)
2082 break;
2083
2084 switch (c) {
2085 case 'b':
Pali Rohárc513fe42022-01-25 18:13:07 +01002086 if (imgpath || bootmsg || debugmsg)
2087 goto usage;
Pali Rohárc1d911f2022-03-02 11:49:20 +01002088 bootmsg = 1;
Pali Rohárc513fe42022-01-25 18:13:07 +01002089 if (prev_optind == optind)
2090 goto usage;
Pali Rohárc497ae72022-03-07 19:03:09 +01002091 /* Option -b could have optional argument which specify image path */
2092 if (optind < argc && argv[optind] && argv[optind][0] != '-')
Pali Rohárc513fe42022-01-25 18:13:07 +01002093 imgpath = argv[optind++];
Luka Perkovd131ad62012-05-27 11:44:51 +00002094 break;
2095
Stefan Roese84899e22014-10-22 12:13:21 +02002096 case 'D':
Pali Rohárc513fe42022-01-25 18:13:07 +01002097 if (imgpath || bootmsg || debugmsg)
2098 goto usage;
Pali Rohárc1d911f2022-03-02 11:49:20 +01002099 bootmsg = 0;
Stefan Roese84899e22014-10-22 12:13:21 +02002100 imgpath = optarg;
2101 break;
2102
2103 case 'd':
Pali Rohárc513fe42022-01-25 18:13:07 +01002104 if (imgpath || bootmsg || debugmsg)
2105 goto usage;
Pali Rohárc1d911f2022-03-02 11:49:20 +01002106 debugmsg = 1;
Stefan Roese84899e22014-10-22 12:13:21 +02002107 break;
2108
Luka Perkovd131ad62012-05-27 11:44:51 +00002109 case 'p':
Pali Rohárddc04fa2021-09-24 23:06:55 +02002110 /* nop, for backward compatibility */
Luka Perkovd131ad62012-05-27 11:44:51 +00002111 break;
2112
2113 case 't':
2114 term = 1;
2115 break;
2116
Stefan Roese84899e22014-10-22 12:13:21 +02002117 case 'a':
Stefan Roese84899e22014-10-22 12:13:21 +02002118 msg_rsp_timeo = KWBOOT_MSG_RSP_TIMEO_AXP;
2119 break;
2120
Stefan Roese1c0df9e2015-05-29 13:25:04 +02002121 case 'q':
Pali Rohár132016e2022-03-02 11:49:19 +01002122 /* nop, for backward compatibility */
Stefan Roese1c0df9e2015-05-29 13:25:04 +02002123 break;
2124
2125 case 's':
2126 msg_rsp_timeo = atoi(optarg);
2127 break;
2128
Kevin Smith7497a6a2016-02-16 21:28:19 +00002129 case 'o':
2130 blk_rsp_timeo = atoi(optarg);
2131 break;
2132
Luka Perkovd131ad62012-05-27 11:44:51 +00002133 case 'B':
Pali Rohárca272042021-09-24 23:07:05 +02002134 baudrate = atoi(optarg);
Luka Perkovd131ad62012-05-27 11:44:51 +00002135 break;
2136
2137 case 'h':
2138 rv = 0;
2139 default:
2140 goto usage;
2141 }
2142 } while (1);
2143
Pali Rohára3c64962022-01-25 18:13:12 +01002144 if (!bootmsg && !term && !debugmsg && !imgpath)
Luka Perkovd131ad62012-05-27 11:44:51 +00002145 goto usage;
2146
Pali Rohárc497ae72022-03-07 19:03:09 +01002147 /*
2148 * If there is no remaining argument but optional imgpath was parsed
2149 * then it means that optional imgpath was eaten by getopt parser.
2150 * Reassing imgpath to required ttypath argument.
2151 */
2152 if (optind == argc && imgpath) {
2153 ttypath = imgpath;
2154 imgpath = NULL;
2155 } else if (optind + 1 == argc) {
2156 ttypath = argv[optind];
2157 } else {
Pali Rohárc513fe42022-01-25 18:13:07 +01002158 goto usage;
Pali Rohárc497ae72022-03-07 19:03:09 +01002159 }
Pali Rohárc513fe42022-01-25 18:13:07 +01002160
Pali Rohára79dea22022-03-07 19:03:07 +01002161 /* boot and debug message use baudrate 115200 */
2162 if (((bootmsg && !imgpath) || debugmsg) && baudrate != 115200) {
2163 fprintf(stderr, "Baudrate other than 115200 cannot be used for this operation.\n");
2164 goto usage;
2165 }
2166
Pali Rohár3782f552022-03-07 19:03:08 +01002167 tty = kwboot_open_tty(ttypath, baudrate);
Luka Perkovd131ad62012-05-27 11:44:51 +00002168 if (tty < 0) {
2169 perror(ttypath);
2170 goto out;
2171 }
2172
Pali Rohár3782f552022-03-07 19:03:08 +01002173 /*
2174 * initial baudrate for image transfer is always 115200,
2175 * the change to different baudrate is done only after the header is sent
2176 */
2177 if (imgpath && baudrate != 115200) {
2178 rc = kwboot_tty_change_baudrate(tty, 115200);
2179 if (rc) {
2180 perror(ttypath);
2181 goto out;
2182 }
2183 }
2184
Pali Rohárca272042021-09-24 23:07:05 +02002185 if (baudrate == 115200)
2186 /* do not change baudrate during Xmodem to the same value */
2187 baudrate = 0;
2188 else
2189 /* ensure we have enough space for baudrate change code */
Pali Rohár8dbe0272021-10-27 20:57:02 +02002190 after_img_rsv += sizeof(struct opt_hdr_v1) + 8 + 16 +
2191 sizeof(kwboot_baud_code_binhdr_pre) +
Pali Rohár5923ef62021-10-25 15:12:54 +02002192 sizeof(kwboot_baud_code) +
Pali Rohár8dbe0272021-10-27 20:57:02 +02002193 sizeof(kwboot_baud_code_binhdr_post) +
2194 KWBOOT_XM_BLKSZ +
2195 sizeof(kwboot_baud_code) +
2196 sizeof(kwboot_baud_code_data_jump) +
Pali Rohár5923ef62021-10-25 15:12:54 +02002197 KWBOOT_XM_BLKSZ;
Pali Rohárca272042021-09-24 23:07:05 +02002198
Luka Perkovd131ad62012-05-27 11:44:51 +00002199 if (imgpath) {
Pali Rohárca272042021-09-24 23:07:05 +02002200 img = kwboot_read_image(imgpath, &size, after_img_rsv);
Luka Perkovd131ad62012-05-27 11:44:51 +00002201 if (!img) {
2202 perror(imgpath);
2203 goto out;
2204 }
Luka Perkovd131ad62012-05-27 11:44:51 +00002205
Pali Rohárca272042021-09-24 23:07:05 +02002206 rc = kwboot_img_patch(img, &size, baudrate);
Luka Perkovd131ad62012-05-27 11:44:51 +00002207 if (rc) {
2208 fprintf(stderr, "%s: Invalid image.\n", imgpath);
2209 goto out;
2210 }
2211 }
2212
Stefan Roese84899e22014-10-22 12:13:21 +02002213 if (debugmsg) {
Pali Rohárc1d911f2022-03-02 11:49:20 +01002214 rc = kwboot_debugmsg(tty);
Pali Rohár93976af2022-03-02 11:49:22 +01002215 if (rc)
Stefan Roese84899e22014-10-22 12:13:21 +02002216 goto out;
Willy Tarreau3475a712018-07-03 12:10:30 -04002217 } else if (bootmsg) {
Pali Rohárc1d911f2022-03-02 11:49:20 +01002218 rc = kwboot_bootmsg(tty);
Pali Rohár913866a2022-03-02 11:49:21 +01002219 if (rc)
Luka Perkovd131ad62012-05-27 11:44:51 +00002220 goto out;
Luka Perkovd131ad62012-05-27 11:44:51 +00002221 }
2222
2223 if (img) {
Pali Rohárca272042021-09-24 23:07:05 +02002224 rc = kwboot_xmodem(tty, img, size, baudrate);
Luka Perkovd131ad62012-05-27 11:44:51 +00002225 if (rc) {
2226 perror("xmodem");
2227 goto out;
2228 }
2229 }
2230
2231 if (term) {
2232 rc = kwboot_terminal(tty);
2233 if (rc && !(errno == EINTR)) {
2234 perror("terminal");
2235 goto out;
2236 }
2237 }
2238
2239 rv = 0;
2240out:
2241 if (tty >= 0)
2242 close(tty);
2243
2244 if (img)
Pali Rohár04ced022021-09-24 23:07:03 +02002245 free(img);
Luka Perkovd131ad62012-05-27 11:44:51 +00002246
2247 return rv;
2248
2249usage:
2250 kwboot_usage(rv ? stderr : stdout, basename(argv[0]));
2251 goto out;
2252}