Add way to upload kernel by serial link

For this code before __ld_kernel_begin should not be modifier (e.g.
crt0.S and static_tools.c).

Inspired/Taken by/from https://github.com/nicolasmesa/PiOS
This commit is contained in:
Mathieu Maret 2022-03-21 23:21:12 +01:00
parent 9ffa7dde85
commit 817e629743
7 changed files with 263 additions and 1 deletions

View File

@ -6,6 +6,7 @@ LD=$(CROSS)ld
CFLAGS=-Wall -Wextra -ffreestanding -march=armv8-a+crc -mcpu=cortex-a53 CFLAGS=-Wall -Wextra -ffreestanding -march=armv8-a+crc -mcpu=cortex-a53
DEBUG_FLAGS += -g -Og -DDEBUG -fno-omit-frame-pointer -fno-inline DEBUG_FLAGS += -g -Og -DDEBUG -fno-omit-frame-pointer -fno-inline
LDSCRIPT=rpi3.ld LDSCRIPT=rpi3.ld
TTY?=/dev/ttyUSB0
gasmsrc=$(wildcard *.S) gasmsrc=$(wildcard *.S)
gasmobj=$(gasmsrc:%.S=%.o) gasmobj=$(gasmsrc:%.S=%.o)
@ -28,11 +29,16 @@ font_psf.o: font.psf
$(LD) -r -b binary -o font_psf.o font.psf $(LD) -r -b binary -o font_psf.o font.psf
clean: clean:
rm -rf $(cobj) $(gasmobj) $(deps) *.bin *.elf *.map rm -rf $(cobj) $(gasmobj) $(deps) font_psf.o *.sym *.bin *.elf *.map
run: $(KERNEL) run: $(KERNEL)
qemu-system-aarch64 -machine raspi3b -kernel $< -serial stdio qemu-system-aarch64 -machine raspi3b -kernel $< -serial stdio
update_serial:
$(eval skip := $(shell $(CROSS)objdump -t kernel.elf | grep __ld_kernel_begin | cut -d " " -f 1))
$(eval skip := $(shell printf "%d" $$((0x$(skip)- 0x80000))))
./boot_client/boot_send.py -d $(TTY) -b 115200 -k kernel.bin -i -s $(skip)
debug: CFLAGS += $(DEBUG_FLAGS) debug: CFLAGS += $(DEBUG_FLAGS)
debug: CXXFLAGS += $(DEBUG_FLAGS) debug: CXXFLAGS += $(DEBUG_FLAGS)
debug:$(KERNEL) debug:$(KERNEL)

193
boot_client/boot_send.py Executable file
View File

@ -0,0 +1,193 @@
#!/usr/bin/env python3
import argparse
import os
import select
import serial
import sys
import time
import tty
# TODO: Make ti work with contexts (with UartConnection() as u)
class UartConnection:
def __init__(self, file_path, baud_rate):
self.serial = serial.Serial(file_path, baud_rate)
def send_line(self, line):
if not line.endswith("\n"):
# Intentionally not adding the \r for now
line += "\n"
return self.send_string(line)
def send_string(self, string):
return self.send_bytes(bytes(string, "ascii"))
def send_bytes(self, bytes_to_send):
return self.serial.write(bytes_to_send)
def send_int(self, number):
if number > 2 ** 32 - 1:
raise 'Number can only be 4 bytes long'
number_in_bytes = number.to_bytes(4, byteorder='big')
return self.send_bytes(number_in_bytes)
def read(self, max_len):
return self.serial.read(max_len)
def read_buffer(self):
return self.read(self.serial.in_waiting)
def read_buffer_string(self):
return self._decode_bytes(self.read_buffer())
def read_line(self):
return self._decode_bytes(self.serial.readline())
def read_int(self):
bytes_to_read = 4
number_bytes = self.read(bytes_to_read)
return int.from_bytes(number_bytes, byteorder='big')
def start_interactive(self, input_file, output_file):
try:
# Make the tty cbreak
# https://www.oreilly.com/library/view/python-standard-library/0596000960/ch12s08.html
tty.setcbreak(input_file.fileno())
while True:
rfd, _, _ = select.select([self.serial, input_file], [], [])
if self.serial in rfd:
r = self.read_buffer_string()
output_file.write(r)
output_file.flush()
if input_file in rfd:
r = input_file.read(1)
self.send_string(r)
except KeyboardInterrupt:
print("Got keyboard interrupt. Terminating...")
return
except OSError as e:
print("Got OSError. Terminating...")
return
finally:
os.system("stty sane")
def close(self):
self.serial.close()
def _decode_bytes(self, bytes_to_decode):
return bytes_to_decode.decode("ascii")
def compute_kernel_checksum(kernel_bytes):
num = 0
for b in kernel_bytes:
num = (num + b) % (2 ** 32)
return num
def send_kernel_debug(uart_connection, kernel):
for i, b in enumerate(kernel):
print(i, 'Sending byte', b)
uart_connection.send_bytes(bytes([b]))
read_byte = uart_connection.read(1)[0]
print(i, 'Received byte', read_byte)
if b != read_byte:
print(i, 'Sent', b, 'but got', read_byte)
return False
return True
def send_kernel(path, uart_connection, skip=0, debug=False):
with open(path, mode='rb') as f:
uart_connection.send_line("kernel")
time.sleep(1)
print("Skipping", skip)
f.seek(skip)
kernel = f.read()
size = len(kernel)
checksum = compute_kernel_checksum(kernel)
print("Sending kernel with size", size, "and checksum", checksum)
uart_connection.send_int(size)
uart_connection.send_int(checksum)
time.sleep(1)
if debug:
print("Starting debug workflow")
uart_connection.send_int(1)
send_kernel_debug(uart_connection, kernel)
else:
uart_connection.send_int(0)
uart_connection.send_bytes(kernel)
time.sleep(1)
#Print("Validating checksum...")
#Checksum_confirmation = uart_connection.read_int()
#If checksum_confirmation != checksum:
# print("Expected checksum to be", checksum,
# "but was", checksum_confirmation)
# return False
#Line = uart_connection.read_line()
#Print("Received: ", line)
#If not line.startswith("Done"):
# print("Didn't get confirmation for the kernel. Got", line)
# return False
return True
def main(argv):
ap = argparse.ArgumentParser(
description="""
Utility program to send the kernel to the RPI over UART and to start an interactive session over uart.
Sample usage:
The following command will setup a serial connection and will send the kernel over it. Then, it
will start an interactive session over the serial connection.
python boot_send.py -d /dev/cu.SLAB_USBtoUART -b 115200 -k ../kernel8.img -i
"""
)
ap.add_argument('-d', '--device', help='path to RPI UART device', required=True,
default='/dev/cu.SLAB_USBtoUART')
ap.add_argument('-b', '--baud-rate',
help='baud rate to use for the UART communication', required=True, type=int,
default=115200)
ap.add_argument('-k', '--kernel', help='file path to the kernel', required=False,
type=str)
ap.add_argument('-i', '--interactive', help='start interactive session',
action='store_const', const=True, default=False)
ap.add_argument('-dd', '--debug', const=True, default=False, action='store_const')
ap.add_argument('-s', '--skip',
help='Skip first n bytes of the kernel (i.e. offset of symbole __ld_kernel_begin)', required=False, type=lambda x: int(x, 0),
default=0x100)
args = ap.parse_args(argv[1:])
if not args.kernel and not args.interactive:
print("At least one of '--kernel' or '--interactive' are required")
sys.exit(1)
uart_connection = UartConnection(args.device, args.baud_rate)
time.sleep(1)
if args.kernel:
success = send_kernel(args.kernel, uart_connection, args.skip, args.debug)
if not success:
sys.exit(1)
time.sleep(1)
if args.interactive:
print("Making it interactive. Press ctrl-c to exit. You may need to press enter...")
uart_connection.start_interactive(sys.stdin, sys.stdout)
uart_connection.close()
if __name__ == '__main__':
main(sys.argv)

View File

@ -0,0 +1,2 @@
certifi==2019.6.16
pyserial==3.4

12
hello.c
View File

@ -1,17 +1,29 @@
#include "fb.h" #include "fb.h"
#include "mbox.h" #include "mbox.h"
#include "static_tools.h"
#include "uart.h" #include "uart.h"
#include "utils.h" #include "utils.h"
#define FB_WIDTH 640 #define FB_WIDTH 640
#define FB_HEIGHT 480 #define FB_HEIGHT 480
#define BUFF_SIZE 100
extern unsigned long int __ld_kernel_begin;
int kernelmain(void) int kernelmain(void)
{ {
struct fbst *fb; struct fbst *fb;
unsigned char *ptr; unsigned char *ptr;
uart_init(); uart_init();
puts("starting kernel\n");
char buffer[BUFF_SIZE];
readline(buffer, BUFF_SIZE);
if (strcmp(buffer, "kernel") == 0) {
if(copy_kernel()){
puts("Checksum error");
}
}
if (fb_init(FB_WIDTH, FB_HEIGHT) == 0) { if (fb_init(FB_WIDTH, FB_HEIGHT) == 0) {
puts("Fail to init framebuffer"); puts("Fail to init framebuffer");
} }

View File

@ -8,6 +8,8 @@ MEMORY
SECTIONS SECTIONS
{ {
.text : { *(.text.boot) .text : { *(.text.boot)
*(static_tools)
__ld_kernel_begin = .;
*(.text) *(.text)
} }
.rodata : { *(.rodata .rodata.*) } .rodata : { *(.rodata .rodata.*) }

44
static_tools.c Normal file
View File

@ -0,0 +1,44 @@
#define IO_BASE 0x3f000000
#define UART0_BASE (IO_BASE + 0x201000)
#define UART0_DR (*(volatile unsigned *)(UART0_BASE + 0x0))
#define UART0_FR (*(volatile unsigned *)(UART0_BASE + 0x18))
extern unsigned long int __ld_kernel_begin;
static __attribute__((section("static_tools"))) char uart_recv(void)
{
while (UART0_FR & (1 << 4)) {
}
return UART0_DR & 0xFF;
}
static int __attribute__((section("static_tools"))) uart_read_int() {
int num = 0;
for (int i = 0; i < 4; i++) {
char c = uart_recv();
num = num << 8;
num += (int)c;
}
return num;
}
int __attribute__((section("static_tools"))) copy_kernel()
{
unsigned int kernel_size = uart_read_int();
int expected_checksum = uart_read_int();
int debug = uart_read_int();
char *kernel = (char *)&__ld_kernel_begin;
int checksum = 0;
(void)debug;
for (unsigned int i = 0; i < kernel_size; i++) {
char c = uart_recv();
checksum += c;
kernel[i] = c;
}
if(checksum != expected_checksum)
return -1;
return 0;
}

3
static_tools.h Normal file
View File

@ -0,0 +1,3 @@
#pragma once
int __attribute__((section("static_tools"))) copy_kernel();