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:
parent
9ffa7dde85
commit
817e629743
8
Makefile
8
Makefile
@ -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
193
boot_client/boot_send.py
Executable 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)
|
2
boot_client/requirements.txt
Normal file
2
boot_client/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
certifi==2019.6.16
|
||||||
|
pyserial==3.4
|
12
hello.c
12
hello.c
@ -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");
|
||||||
}
|
}
|
||||||
|
2
rpi3.ld
2
rpi3.ld
@ -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
44
static_tools.c
Normal 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
3
static_tools.h
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
int __attribute__((section("static_tools"))) copy_kernel();
|
Loading…
Reference in New Issue
Block a user