Add raspbootin64 tutorial and reshuffle order

pull/4/head
Andre Richter 6 years ago
parent 32ef64b36e
commit 7273745da6

@ -1,43 +0,0 @@
#
# Copyright (C) 2018 bzt (bztsrc@github)
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use, copy,
# modify, merge, publish, distribute, sublicense, and/or sell copies
# of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
#
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
CFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles
all: clean kernel8.img
start.o: start.S
aarch64-elf-gcc $(CFLAGS) -c start.S -o start.o
%.o: %.c
aarch64-elf-gcc $(CFLAGS) -c $< -o $@
kernel8.img: start.o $(OBJS)
aarch64-elf-ld -nostdlib -nostartfiles start.o $(OBJS) -T link.ld -o kernel8.elf
aarch64-elf-objcopy -O binary kernel8.elf kernel8.img
clean:
rm kernel8.elf *.o >/dev/null 2>/dev/null || true

@ -1,45 +0,0 @@
Oktatóanyag 14 - Raspbootin64
=============================
Mivel folyton iragatni az SD kártyát strapás, és nem tesz jót a kártyának, ezért egy olyan kernel8.img-t készítünk,
ami a soros vonalról fogja betölteni az igazi kernel8.img-t.
Ez az oktatóanyag a jól ismert [raspbootin](https://github.com/mrvn/raspbootin) átírása 64 bitre.
A betöltőprogram egyik felét adja csak, a kernel fogadót, ami az RPi-n fut. A másik fél, a PC-n futó küldő,
megtalálható az eredeti forrásban [raspbootcom](https://github.com/mrvn/raspbootin/blob/master/raspbootcom/raspbootcom.cc) néven.
Ha Windowsos gépekről is szeretnél kernelt küldeni, akkor javaslom inkább a John Cronin féle átiratot, a
[raspbootin-server](https://github.com/jncronin/rpi-boot/blob/master/raspbootin-server.c)-t, ami natív Win32 API-t használ.
Ezen kvül, [@milanvidakovic](https://github.com/milanvidakovic) volt olyan jó fej, hogy megosztotta az általa írt [Java-s kernel küldő](https://github.com/milanvidakovic/Raspbootin64Client)t.
Hogy az új kernelt ugyanoda tölthessük be, el kell mozdítanunk a kódunkat az útból. Ezt chain loading-nak hívják, amikor
az első kód ugyanarra a címre tölti be a második kódot, ezért az utóbbi azt hiszi, a firmware töltötte be.
Hogy ezt megvalósítsuk, egy alacsonyabb címre linkeljük a kódot, és mivel a GPU ettől függetlenül a 0x80000-ra tölt be,
nekünk kell a módosított címre másolnunk magunkat. Amikor végeztünk, a 0x80000-as címen lévő memóriának használaton
kívülinek kell lennie. Ezt a következő paranccsal ellenőrizheted:
```sh
$ aarch64-elf-readelf -s kernel8.elf | grep __bss_end
27: 000000000007ffe0 0 NOTYPE GLOBAL DEFAULT 4 __bss_end
```
Ajánlott a kódunkat minimalizálni, mivel úgyis figyelmen kívül hagyja az újonnan betöltendő kód. Ezért kivettem az
`uart_puts()` és még néhány eljárást, így sikerült a teljes méretet 1024 bájt alá csökkenteni.
Start
-----
Először is elmentjük a regiszter paramétereket. Hozzáadtam egy ciklust, ami átmásolja a kódunkat arra a címre, ahová
vártuk, hogy betöltődjön. Végül meghívjuk a relokált C kódot. Mivel a gcc RIP-relatív ugrást generál, nekünk kell a
relokálást hozzáadni a címhez.
Linker
------
Másik címre linkelünk ebben a példában. Hasonlóan a bss méret kiszámításához, ugyanúgy meghatározzuk a kód
méretét is, amit másolnunk kell.
Main
----
Kiírjuk, hogy 'RBIN64', majd beolvassuk az új kernelt a soros vonalról, pontosan oda, ahová a start.elf is töltötte volna.
Majd visszaállítjuk a regiszterparamétereket és átadjuk a vezérlést az új kernelre abszolút címzést használva.

@ -1,45 +0,0 @@
Tutorial 14 - Raspbootin64
==========================
Because changing SD card is boring and also to avoid potential SD card damage, we create a kernel8.img that will
load the real kernel8.img over serial.
This tutorial is a rewrite of the well known serial boot loader, [raspbootin](https://github.com/mrvn/raspbootin) in 64 bit.
I only provide one part of the loader, the kernel receiver, which runs on the RPi. For the other
part, the sender, which runs on your PC see the original [raspbootcom](https://github.com/mrvn/raspbootin/blob/master/raspbootcom/raspbootcom.cc) utility.
If you want to send kernels from a Windows machine, I suggest to take a look at John Cronin's rewrite,
[raspbootin-server](https://github.com/jncronin/rpi-boot/blob/master/raspbootin-server.c) which can be compiled for the Win32 API.
Even more, [@milanvidakovic](https://github.com/milanvidakovic) was kind to share a [Java version](https://github.com/milanvidakovic/Raspbootin64Client) of the kernel sender with you.
In order to load the new kernel to the same address, we have to move ourself out of the way. It's called chain
loading: one code loads the next code to the same position in memory, therefore the latter thinks it was loaded
by the firmware. To implement that we use a different linking address this time, and since GPU loads us to 0x80000
regardless, we have to copy our code to that link address. When we're done, the memory at 0x80000 must be free to use.
You can check that with:
```sh
$ aarch64-elf-readelf -s kernel8.elf | grep __bss_end
27: 000000000007ffe0 0 NOTYPE GLOBAL DEFAULT 4 __bss_end
```
We also should minimize the size of the loader, since it will be regarded by the newly loaded code anyway.
By removing `uart_puts()` and other functions, I've managed to shrink the loader's size below 1024 bytes.
Start
-----
We have to save the arguments in registers passed by the firmware. Added a loop to relocate our code to the
address it should have been loaded to. And last, since gcc generates RIP-relative jumps, we must adjust the
branch instruction to jump to the relocated C code.
Linker
------
We use a different linking address this time. Similarly to bss size calculation, we calculate our code's size to
know how many bytes we have to copy.
Main
----
We print 'RBIN64', receive the new kernel over serial and save it at the memory address where the start.elf would
have been loaded it. When finished, we restore the arguments and jump to the new kernel using an absolute address.

@ -1,45 +0,0 @@
/*
* Copyright (C) 2018 bzt (bztsrc@github)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
#define MMIO_BASE 0x3F000000
#define GPFSEL0 ((volatile unsigned int*)(MMIO_BASE+0x00200000))
#define GPFSEL1 ((volatile unsigned int*)(MMIO_BASE+0x00200004))
#define GPFSEL2 ((volatile unsigned int*)(MMIO_BASE+0x00200008))
#define GPFSEL3 ((volatile unsigned int*)(MMIO_BASE+0x0020000C))
#define GPFSEL4 ((volatile unsigned int*)(MMIO_BASE+0x00200010))
#define GPFSEL5 ((volatile unsigned int*)(MMIO_BASE+0x00200014))
#define GPSET0 ((volatile unsigned int*)(MMIO_BASE+0x0020001C))
#define GPSET1 ((volatile unsigned int*)(MMIO_BASE+0x00200020))
#define GPCLR0 ((volatile unsigned int*)(MMIO_BASE+0x00200028))
#define GPLEV0 ((volatile unsigned int*)(MMIO_BASE+0x00200034))
#define GPLEV1 ((volatile unsigned int*)(MMIO_BASE+0x00200038))
#define GPEDS0 ((volatile unsigned int*)(MMIO_BASE+0x00200040))
#define GPEDS1 ((volatile unsigned int*)(MMIO_BASE+0x00200044))
#define GPHEN0 ((volatile unsigned int*)(MMIO_BASE+0x00200064))
#define GPHEN1 ((volatile unsigned int*)(MMIO_BASE+0x00200068))
#define GPPUD ((volatile unsigned int*)(MMIO_BASE+0x00200094))
#define GPPUDCLK0 ((volatile unsigned int*)(MMIO_BASE+0x00200098))
#define GPPUDCLK1 ((volatile unsigned int*)(MMIO_BASE+0x0020009C))

Binary file not shown.

@ -1,79 +0,0 @@
/*
* Copyright (C) 2018 bzt (bztsrc@github)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
#include "uart.h"
void main()
{
int size=0;
char *kernel=(char*)0x80000;
// set up serial console
uart_init();
// say hello. To reduce loader size I removed uart_puts()
again:
uart_send('R');
uart_send('B');
uart_send('I');
uart_send('N');
uart_send('6');
uart_send('4');
uart_send('\r');
uart_send('\n');
// notify raspbootcom to send the kernel
uart_send(3);
uart_send(3);
uart_send(3);
// read the kernel's size
size=uart_getc();
size|=uart_getc()<<8;
size|=uart_getc()<<16;
size|=uart_getc()<<24;
// send negative or positive acknowledge
if(size<64 || size>1024*1024) {
// size error
uart_send('S');
uart_send('E');
goto again;
}
uart_send('O');
uart_send('K');
// read the kernel
while(size--) *kernel++ = uart_getc();
// restore arguments and jump to the new kernel.
asm volatile (
"mov x0, x10;"
"mov x1, x11;"
"mov x2, x12;"
"mov x3, x13;"
// we must force an absolute address to branch to
"mov x30, 0x80000; ret"
);
}

@ -1,63 +0,0 @@
/*
* Copyright (C) 2018 bzt (bztsrc@github)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
#include "gpio.h"
/* mailbox message buffer */
volatile unsigned int __attribute__((aligned(16))) mbox[8];
#define VIDEOCORE_MBOX (MMIO_BASE+0x0000B880)
#define MBOX_READ ((volatile unsigned int*)(VIDEOCORE_MBOX+0x0))
#define MBOX_POLL ((volatile unsigned int*)(VIDEOCORE_MBOX+0x10))
#define MBOX_SENDER ((volatile unsigned int*)(VIDEOCORE_MBOX+0x14))
#define MBOX_STATUS ((volatile unsigned int*)(VIDEOCORE_MBOX+0x18))
#define MBOX_CONFIG ((volatile unsigned int*)(VIDEOCORE_MBOX+0x1C))
#define MBOX_WRITE ((volatile unsigned int*)(VIDEOCORE_MBOX+0x20))
#define MBOX_RESPONSE 0x80000000
#define MBOX_FULL 0x80000000
#define MBOX_EMPTY 0x40000000
/**
* Make a mailbox call. Returns 0 on failure, non-zero on success
*/
int mbox_call(unsigned char ch)
{
unsigned int r;
/* wait until we can write to the mailbox */
do{asm volatile("nop");}while(*MBOX_STATUS & MBOX_FULL);
/* write the address of our message to the mailbox with channel identifier */
*MBOX_WRITE = (((unsigned int)((unsigned long)&mbox)&~0xF) | (ch&0xF));
/* now wait for the response */
while(1) {
/* is there a response? */
do{asm volatile("nop");}while(*MBOX_STATUS & MBOX_EMPTY);
r=*MBOX_READ;
/* is it a response to our message? */
if((unsigned char)(r&0xF)==ch && (r&~0xF)==(unsigned int)((unsigned long)&mbox))
/* is it a valid successful response? */
return mbox[1]==MBOX_RESPONSE;
}
return 0;
}

@ -1,47 +0,0 @@
/*
* Copyright (C) 2018 bzt (bztsrc@github)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
/* a properly aligned buffer */
extern volatile unsigned int mbox[36];
#define MBOX_REQUEST 0
/* channels */
#define MBOX_CH_POWER 0
#define MBOX_CH_FB 1
#define MBOX_CH_VUART 2
#define MBOX_CH_VCHIQ 3
#define MBOX_CH_LEDS 4
#define MBOX_CH_BTNS 5
#define MBOX_CH_TOUCH 6
#define MBOX_CH_COUNT 7
#define MBOX_CH_PROP 8
/* tags */
#define MBOX_TAG_GETSERIAL 0x10004
#define MBOX_TAG_SETCLKRATE 0x38002
#define MBOX_TAG_LAST 0
int mbox_call(unsigned char ch);

@ -1,96 +0,0 @@
/*
* Copyright (C) 2018 bzt (bztsrc@github)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
#include "gpio.h"
#include "mbox.h"
/* PL011 UART registers */
#define UART0_DR ((volatile unsigned int*)(MMIO_BASE+0x00201000))
#define UART0_FR ((volatile unsigned int*)(MMIO_BASE+0x00201018))
#define UART0_IBRD ((volatile unsigned int*)(MMIO_BASE+0x00201024))
#define UART0_FBRD ((volatile unsigned int*)(MMIO_BASE+0x00201028))
#define UART0_LCRH ((volatile unsigned int*)(MMIO_BASE+0x0020102C))
#define UART0_CR ((volatile unsigned int*)(MMIO_BASE+0x00201030))
#define UART0_IMSC ((volatile unsigned int*)(MMIO_BASE+0x00201038))
#define UART0_ICR ((volatile unsigned int*)(MMIO_BASE+0x00201044))
/**
* Set baud rate and characteristics (115200 8N1) and map to GPIO
*/
void uart_init()
{
register unsigned int r;
/* initialize UART */
*UART0_CR = 0; // turn off UART0
/* set up clock for consistent divisor values */
mbox[0] = 8*4;
mbox[1] = MBOX_REQUEST;
mbox[2] = MBOX_TAG_SETCLKRATE; // set clock rate
mbox[3] = 12;
mbox[4] = 8;
mbox[5] = 2; // UART clock
mbox[6] = 4000000; // 4Mhz
mbox[7] = MBOX_TAG_LAST;
mbox_call(MBOX_CH_PROP);
/* map UART0 to GPIO pins */
r=*GPFSEL1;
r&=~((7<<12)|(7<<15)); // gpio14, gpio15
r|=(4<<12)|(4<<15); // alt0
*GPFSEL1 = r;
*GPPUD = 0; // enable pins 14 and 15
r=150; while(r--) { asm volatile("nop"); }
*GPPUDCLK0 = (1<<14)|(1<<15);
r=150; while(r--) { asm volatile("nop"); }
*GPPUDCLK0 = 0; // flush GPIO setup
*UART0_ICR = 0x7FF; // clear interrupts
*UART0_IBRD = 2; // 115200 baud
*UART0_FBRD = 0xB;
*UART0_LCRH = 0b11<<5; // 8n1
*UART0_CR = 0x301; // enable Tx, Rx, FIFO
}
/**
* Send a character
*/
void uart_send(unsigned int c) {
/* wait until we can send */
do{asm volatile("nop");}while(*UART0_FR&0x20);
/* write the character to the buffer */
*UART0_DR=c;
}
/**
* Receive a character
*/
char uart_getc() {
/* wait until something is in the buffer */
do{asm volatile("nop");}while(*UART0_FR&0x10);
/* read it and return */
return (char)(*UART0_DR);
}

@ -1,30 +0,0 @@
/*
* Copyright (C) 2018 bzt (bztsrc@github)
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*/
void uart_init();
void uart_send(unsigned int c);
char uart_getc();
void uart_puts(char *s);
void uart_hex(unsigned int d);

@ -27,7 +27,7 @@ TARGET = aarch64-raspi3-none-elf
CROSS_CONTAINER = ./dockcross-linux-aarch64
CROSS_CONTAINER_OBJCOPY = aarch64-linux-gnu-objcopy
QEMU_CONTAINER = andrerichter/raspi3-qemu
UTILS_CONTAINER = andrerichter/raspi3-utils
DOCKER_CMD = docker run -it --rm -v $(shell pwd):/work -w /work
QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img
@ -57,7 +57,7 @@ kernel8.img: kernel8
$(CROSS_CONTAINER) $(CROSS_CONTAINER_OBJCOPY) -O binary -S $< kernel8.img
qemu:
$(DOCKER_CMD) $(QEMU_CONTAINER) $(QEMU_CMD) -d in_asm
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -d in_asm
clean:
cargo clean

@ -27,7 +27,7 @@ TARGET = aarch64-raspi3-none-elf
CROSS_CONTAINER = ./dockcross-linux-aarch64
CROSS_CONTAINER_OBJCOPY = aarch64-linux-gnu-objcopy
QEMU_CONTAINER = andrerichter/raspi3-qemu
UTILS_CONTAINER = andrerichter/raspi3-utils
DOCKER_CMD = docker run -it --rm -v $(shell pwd):/work -w /work
QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img
@ -57,7 +57,7 @@ kernel8.img: kernel8
$(CROSS_CONTAINER) $(CROSS_CONTAINER_OBJCOPY) -O binary -S $< kernel8.img
qemu:
$(DOCKER_CMD) $(QEMU_CONTAINER) $(QEMU_CMD) -d in_asm
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -d in_asm
clippy:
RUSTFLAGS="-C panic=abort" xargo clippy --target=$(TARGET)

@ -27,7 +27,7 @@ TARGET = aarch64-raspi3-none-elf
CROSS_CONTAINER = ./dockcross-linux-aarch64
CROSS_CONTAINER_OBJCOPY = aarch64-linux-gnu-objcopy
QEMU_CONTAINER = andrerichter/raspi3-qemu
UTILS_CONTAINER = andrerichter/raspi3-utils
DOCKER_CMD = docker run -it --rm -v $(shell pwd):/work -w /work
QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img
@ -57,7 +57,7 @@ kernel8.img: kernel8
$(CROSS_CONTAINER) $(CROSS_CONTAINER_OBJCOPY) -O binary -S $< kernel8.img
qemu:
$(DOCKER_CMD) $(QEMU_CONTAINER) $(QEMU_CMD) -d in_asm
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio
clippy:
RUSTFLAGS="-C panic=abort" xargo clippy --target=$(TARGET)

@ -27,7 +27,7 @@ TARGET = aarch64-raspi3-none-elf
CROSS_CONTAINER = ./dockcross-linux-aarch64
CROSS_CONTAINER_OBJCOPY = aarch64-linux-gnu-objcopy
QEMU_CONTAINER = andrerichter/raspi3-qemu
UTILS_CONTAINER = andrerichter/raspi3-utils
DOCKER_CMD = docker run -it --rm -v $(shell pwd):/work -w /work
QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img
@ -57,7 +57,7 @@ kernel8.img: kernel8
$(CROSS_CONTAINER) $(CROSS_CONTAINER_OBJCOPY) -O binary -S $< kernel8.img
qemu:
$(DOCKER_CMD) $(QEMU_CONTAINER) $(QEMU_CMD) -serial stdio
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio
clippy:
RUSTFLAGS="-C panic=abort" xargo clippy --target=$(TARGET)

@ -27,7 +27,7 @@ TARGET = aarch64-raspi3-none-elf
CROSS_CONTAINER = ./dockcross-linux-aarch64
CROSS_CONTAINER_OBJCOPY = aarch64-linux-gnu-objcopy
QEMU_CONTAINER = andrerichter/raspi3-qemu
UTILS_CONTAINER = andrerichter/raspi3-utils
DOCKER_CMD = docker run -it --rm -v $(shell pwd):/work -w /work
QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img
@ -57,7 +57,7 @@ kernel8.img: kernel8
$(CROSS_CONTAINER) $(CROSS_CONTAINER_OBJCOPY) -O binary -S $< kernel8.img
qemu:
$(DOCKER_CMD) $(QEMU_CONTAINER) $(QEMU_CMD) -serial stdio
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio
clippy:
RUSTFLAGS="-C panic=abort" xargo clippy --target=$(TARGET)

@ -27,7 +27,7 @@ TARGET = aarch64-raspi3-none-elf
CROSS_CONTAINER = ./dockcross-linux-aarch64
CROSS_CONTAINER_OBJCOPY = aarch64-linux-gnu-objcopy
QEMU_CONTAINER = andrerichter/raspi3-qemu
UTILS_CONTAINER = andrerichter/raspi3-utils
DOCKER_CMD = docker run -it --rm -v $(shell pwd):/work -w /work
QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img
@ -57,7 +57,7 @@ kernel8.img: kernel8
$(CROSS_CONTAINER) $(CROSS_CONTAINER_OBJCOPY) -O binary -S $< kernel8.img
qemu:
$(DOCKER_CMD) $(QEMU_CONTAINER) $(QEMU_CMD) -serial stdio
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio
clippy:
RUSTFLAGS="-C panic=abort" xargo clippy --target=$(TARGET)

@ -0,0 +1,86 @@
# Tutorial 06 - Raspbootin64
We are now at a point where we have a running serial connection, but for each
new feature we want to try, we still have to write and exchange the SD card
every time.
As this tends to get very annoying and also to avoid potential SD card damage,
we create a kernel8.img that will load the real kernel8.img over serial.
This tutorial is a rewrite of the well known serial boot loader
[raspbootin][bootin] in 64-bit. We only provide one part of the loader, the
kernel receiver, which runs on the RPi. For the other part, the sender, which
runs on your PC, we will rely on the original [raspbootcom][bootcom] utility.
[bootin]:(https://github.com/mrvn/raspbootin)
[bootcom]:(https://github.com/mrvn/raspbootin/blob/master/raspbootcom/raspbootcom.cc)
For convenience, it is already packaged in our `raspi3-utils` docker
container. So if you are running a Linux host, it will be as easy as calling
another Makefile target. It will be included starting with the next tutorial,
`07_random`. You can invoke it with
```bash
make raspboot
```
If you want to use it with earlier versions of this tutorial, here is a bash
command to invoke it:
```bash
docker run -it --rm \
--privileged -v /dev/:/dev/ \
-v $PWD:/work -w /work \
raspi3-utils \
raspbootcom /dev/ttyUSB0 kernel8.img
```
In any case, if your USB device is enumerated differently, adapt accordingly.
If you want to send kernels from a Windows machine, I suggest to take a look at
John Cronin's rewrite, [raspbootin-server][w32] which can be compiled for the
Win32 API. Even more, [@milanvidakovic](https://github.com/milanvidakovic) was
so kind to share a [Java version][java] of the kernel sender with you.
[w32]:(https://github.com/jncronin/rpi-boot/blob/master/raspbootin-server.c)
[java]:(https://github.com/milanvidakovic/Raspbootin64Client)
## Chain Loading
In order to load the new kernel to the same address, we have to move ourself out
of the way. It's called `chain loading`: One code loads the next code to the
same position in memory, therefore the latter thinks it was loaded by the
firmware. To implement that, we use a different linking address this time, and
since the GPU loads us to `0x80_000` regardless, we have to copy our code to
that link address. When we're done, the memory at `0x80_000` is free to use. You
can check that with:
```sh
$ ./dockcross-linux-aarch64 aarch64-linux-gnu-nm kernel8 | grep reset
000000000007ffc0 T reset
```
We also should minimize the size of the loader, since it will be overwritten by
the newly loaded code anyway. By removing `Uart::puts()` and other functions,
we've managed to shrink the loader's size to 1024 bytes.
## boot_cores.S
First, we have to save the arguments in registers passed by the
firmware. Second, we added a loop to relocate our code to the address it should
have been loaded to. And last, since rustc generates RIP-relative jumps, we must
adjust the branch instruction to jump to the relocated Rust code.
## Linker and Glue Code
We use a different linking address this time. We calculate our code's size to
know how many bytes we have to copy.
Additionally, we can remove the `bss section` entirely, since our loader does
not use any static variables.
## main.rs
We print 'RBIN64', receive the new kernel over serial and save it at the memory
address where the start.elf would have been loaded it. When finished, we restore
the arguments and jump to the new kernel using an absolute address.

Binary file not shown.

@ -1,5 +1,6 @@
/*
* Copyright (C) 2018 bzt (bztsrc@github)
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
@ -25,22 +26,13 @@
SECTIONS
{
. = 0x80000 - 1024;
PROVIDE(_code = .);
. = 0x80000 - 2048;
_code = .;
.text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) }
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) }
PROVIDE(_data = .);
.data : { *(.data .data.* .gnu.linkonce.d*) }
.bss (NOLOAD) : {
. = ALIGN(16);
__bss_start = .;
*(.bss .bss.*)
*(COMMON)
__bss_end = .;
}
_end = .;
/DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) }
}
__bss_size = (__bss_end - __bss_start)>>3;
__loader_size = (_end - _code)>>3;

@ -1,5 +1,6 @@
/*
* Copyright (C) 2018 bzt (bztsrc@github)
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
@ -25,16 +26,17 @@
.section ".text.boot"
.global _start
.global _boot_cores
_start:
// save arguments in registers (we will need them later for the new kernel)
// I choosed x10-x13 because instructions generated from C by gcc does not
// touch them. You can check that with "aarch64-elf-objdump -d kernel8.elf"
mov x10, x0
mov x11, x1
mov x12, x2
mov x13, x3
_boot_cores:
// Save arguments in registers (we will need them later for the new kernel)
// We choose x25-x28 because instructions generated from Rust by rustc do not
// touch them. You can check that with
// "./dockcross-linux-aarch64 aarch64-linux-gnu-objdump -d kernel8"
mov x25, x0
mov x26, x1
mov x27, x2
mov x28, x3
// read cpu id, stop slave cores
mrs x1, mpidr_el1
@ -47,26 +49,18 @@ _start:
// relocate our code from load address to link address
ldr x1, =0x80000
ldr x2, =_start
ldr x2, =_boot_cores
ldr w3, =__loader_size
1: ldr x4, [x1], #8
3: ldr x4, [x1], #8
str x4, [x2], #8
sub w3, w3, #1
cbnz w3, 1b
cbnz w3, 3b
// set stack before our code
ldr x1, =_start
ldr x1, =_boot_cores
mov sp, x1
// clear bss
ldr x1, =__bss_start
ldr w2, =__bss_size
3: cbz w2, 4f
str xzr, [x1], #8
sub w2, w2, #1
cbnz w2, 3b
// jump to relocated C code, should not return
4: bl main-1024
// jump to relocated Rust code, should not return
bl reset-2048
// for failsafe, halt this core too
b 1b

@ -0,0 +1,73 @@
/*
* MIT License
*
* Copyright (c) 2018 Jorge Aparicio
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#![feature(lang_items)]
#![no_std]
#![feature(global_asm)]
use core::ptr;
#[lang = "panic_fmt"]
unsafe extern "C" fn panic_fmt(
_args: core::fmt::Arguments,
_file: &'static str,
_line: u32,
_col: u32,
) -> ! {
loop {}
}
#[lang = "start"]
extern "C" fn start<T>(user_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize
where
T: Termination,
{
user_main().report() as isize
}
#[lang = "termination"]
trait Termination {
fn report(self) -> i32;
}
impl Termination for () {
fn report(self) -> i32 {
0
}
}
#[no_mangle]
pub unsafe extern "C" fn reset() -> ! {
extern "C" {
fn main(argc: isize, argv: *const *const u8) -> isize;
}
main(0, ptr::null());
loop {}
}
// Disable all cores except core 0, and then jump to reset()
global_asm!(include_str!("boot_cores.S"));

@ -0,0 +1,83 @@
/*
* MIT License
*
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#![no_std]
#![feature(asm)]
extern crate raspi3_glue;
extern crate volatile_register;
const MMIO_BASE: u32 = 0x3F00_0000;
mod gpio;
mod mbox;
mod uart;
fn main() {
let mut mbox = mbox::Mbox::new();
let uart = uart::Uart::new();
// set up serial console
if uart.init(&mut mbox).is_err() {
return; // If UART fails, abort early
}
let kernel: *mut u8 = 0x80_000 as *mut u8;
// Say hello
for c in "RBIN64\r\n".chars() {
uart.send(c);
}
// Notify raspbootcom to send the kernel
uart.send(3 as char);
uart.send(3 as char);
uart.send(3 as char);
// Read the kernel's size
let mut size: u32 = uart.getc() as u32;
size |= (uart.getc() as u32) << 8;
size |= (uart.getc() as u32) << 16;
size |= (uart.getc() as u32) << 24;
// For now, blindly trust it's not too big
uart.send('O');
uart.send('K');
unsafe {
// Read the kernel byte by byte
for i in 0..size {
*kernel.offset(i as isize) = uart.getc();
}
// Restore arguments and jump to loaded kernel
asm!("mov x0, x25 \n\t\
mov x1, x26 \n\t\
mov x2, x27 \n\t\
mov x3, x28 \n\t\
mov x30, 0x80000 \n\t\
ret"
:::: "volatile");
}
}

@ -0,0 +1,139 @@
/*
* MIT License
*
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use super::MMIO_BASE;
use volatile_register::{RO, WO};
const VIDEOCORE_MBOX: u32 = MMIO_BASE + 0xB880;
#[allow(non_snake_case)]
#[repr(C)]
struct Registers {
READ: RO<u32>, // 0x00
__reserved_0: [u32; 3], // 0x04
POLL: RO<u32>, // 0x10
SENDER: RO<u32>, // 0x14
STATUS: RO<u32>, // 0x18
CONFIG: RO<u32>, // 0x1C
WRITE: WO<u32>, // 0x20
}
// Custom errors
pub enum MboxError {
ResponseError,
UnknownError,
}
pub type Result<T> = ::core::result::Result<T, MboxError>;
// Channels
pub mod channel {
pub const PROP: u32 = 8;
}
// Tags
pub mod tag {
pub const SETCLKRATE: u32 = 0x38002;
pub const LAST: u32 = 0;
}
// Clocks
pub mod clock {
pub const UART: u32 = 0x0_0000_0002;
}
// Responses
mod response {
pub const SUCCESS: u32 = 0x8000_0000;
pub const ERROR: u32 = 0x8000_0001; // error parsing request buffer (partial response)
}
pub const REQUEST: u32 = 0;
const FULL: u32 = 0x8000_0000;
const EMPTY: u32 = 0x4000_0000;
// Public interface to the mailbox
#[repr(C)]
pub struct Mbox {
// The address for buffer needs to be 16-byte aligned so that the
// Videcore can handle it properly. We don't take precautions here
// to achieve that, but for now it just works. Since alignment of
// data structures in Rust is a bit of a hassle right now, we just
// close our eyes and roll with it.
pub buffer: [u32; 10],
registers: *const Registers,
}
impl Mbox {
pub fn new() -> Mbox {
Mbox {
buffer: [0; 10],
registers: VIDEOCORE_MBOX as *const Registers,
}
}
/// Make a mailbox call. Returns Err(MboxError) on failure, Ok(()) success
pub fn call(&mut self, channel: u32) -> Result<()> {
// wait until we can write to the mailbox
loop {
unsafe {
if ((*self.registers).STATUS.read() & FULL) != FULL {
break;
}
asm!("nop" :::: "volatile");
}
}
// write the address of our message to the mailbox with channel identifier
unsafe {
(*self.registers)
.WRITE
.write(((self.buffer.as_mut_ptr() as u32) & !0xF) | (channel & 0xF));
}
// now wait for the response
loop {
// is there a response?
loop {
unsafe {
if ((*self.registers).STATUS.read() & EMPTY) != EMPTY {
break;
}
asm!("nop" :::: "volatile");
}
}
let resp: u32 = unsafe { (*self.registers).READ.read() };
// is it a response to our message?
if ((resp & 0xF) == channel) && ((resp & !0xF) == (self.buffer.as_mut_ptr() as u32)) {
// is it a valid successful response?
return match self.buffer[1] {
response::SUCCESS => Ok(()),
response::ERROR => Err(MboxError::ResponseError),
_ => Err(MboxError::UnknownError),
};
}
}
}
}

@ -0,0 +1,153 @@
/*
* MIT License
*
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use super::MMIO_BASE;
use core::sync::atomic::{compiler_fence, Ordering};
use gpio;
use mbox;
use volatile_register::*;
const UART_BASE: u32 = MMIO_BASE + 0x20_1000;
// PL011 UART registers
#[allow(non_snake_case)]
#[repr(C)]
struct Registers {
DR: RW<u32>, // 0x00
__reserved_0: [u32; 5], // 0x04
FR: RO<u32>, // 0x18
__reserved_1: [u32; 2], // 0x1c
IBRD: WO<u32>, // 0x24
FBRD: WO<u32>, // 0x28
LCRH: WO<u32>, // 0x2C
CR: WO<u32>, // 0x30
__reserved_2: [u32; 4], // 0x34
ICR: WO<u32>, // 0x44
}
pub enum UartError {
MailboxError,
}
pub type Result<T> = ::core::result::Result<T, UartError>;
pub struct Uart {
registers: *const Registers,
}
impl Uart {
pub fn new() -> Uart {
Uart {
registers: UART_BASE as *const Registers,
}
}
///Set baud rate and characteristics (115200 8N1) and map to GPIO
pub fn init(&self, mbox: &mut mbox::Mbox) -> Result<()> {
// turn off UART0
unsafe { (*self.registers).CR.write(0) };
// set up clock for consistent divisor values
mbox.buffer[0] = 9 * 4;
mbox.buffer[1] = mbox::REQUEST;
mbox.buffer[2] = mbox::tag::SETCLKRATE;
mbox.buffer[3] = 12;
mbox.buffer[4] = 8;
mbox.buffer[5] = mbox::clock::UART; // UART clock
mbox.buffer[6] = 4_000_000; // 4Mhz
mbox.buffer[7] = 0; // skip turbo setting
mbox.buffer[8] = mbox::tag::LAST;
// Insert a compiler fence that ensures that all stores to the
// mbox buffer are finished before the GPU is signaled (which
// is done by a store operation as well).
compiler_fence(Ordering::Release);
if mbox.call(mbox::channel::PROP).is_err() {
return Err(UartError::MailboxError); // Abort if UART clocks couldn't be set
};
// map UART0 to GPIO pins
unsafe {
(*gpio::GPFSEL1).modify(|x| {
// Modify with a closure
let mut ret = x;
ret &= !((7 << 12) | (7 << 15)); // gpio14, gpio15
ret |= (4 << 12) | (4 << 15); // alt0
ret
});
(*gpio::GPPUD).write(0); // enable pins 14 and 15
for _ in 0..150 {
asm!("nop" :::: "volatile");
}
(*gpio::GPPUDCLK0).write((1 << 14) | (1 << 15));
for _ in 0..150 {
asm!("nop" :::: "volatile");
}
(*gpio::GPPUDCLK0).write(0);
(*self.registers).ICR.write(0x7FF); // clear interrupts
(*self.registers).IBRD.write(2); // 115200 baud
(*self.registers).FBRD.write(0xB);
(*self.registers).LCRH.write(0b11 << 5); // 8n1
(*self.registers).CR.write(0x301); // enable Tx, Rx, FIFO
}
Ok(())
}
/// Send a character
pub fn send(&self, c: char) {
unsafe {
// wait until we can send
loop {
if ((*self.registers).FR.read() & 0x20) != 0x20 {
break;
}
asm!("nop" :::: "volatile");
}
// write the character to the buffer
(*self.registers).DR.write(c as u32);
}
}
/// Receive a character
pub fn getc(&self) -> u8 {
unsafe {
// wait until something is in the buffer
loop {
if ((*self.registers).FR.read() & 0x10) != 0x10 {
break;
}
asm!("nop" :::: "volatile");
}
// read it and return
(*self.registers).DR.read() as u8
}
}
}

28
07_random/Cargo.lock generated

@ -0,0 +1,28 @@
[[package]]
name = "kernel8"
version = "0.1.0"
dependencies = [
"raspi3_glue 0.1.0",
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "raspi3_glue"
version = "0.1.0"
[[package]]
name = "vcell"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "volatile-register"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata]
"checksum vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45c297f0afb6928cd08ab1ff9d95e99392595ea25ae1b5ecf822ff8764e57a0d"
"checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286"

@ -0,0 +1,8 @@
[package]
name = "kernel8"
version = "0.1.0"
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
[dependencies]
raspi3_glue = { path = "raspi3_glue" }
volatile-register = "0.2.0"

@ -0,0 +1,72 @@
#
# MIT License
#
# Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
TARGET = aarch64-raspi3-none-elf
CROSS_CONTAINER = ./dockcross-linux-aarch64
CROSS_CONTAINER_OBJCOPY = aarch64-linux-gnu-objcopy
UTILS_CONTAINER = andrerichter/raspi3-utils
DOCKER_CMD = docker run -it --rm -v $(shell pwd):/work -w /work
DOCKER_TTY = --privileged -v /dev:/dev
QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img
RASPBOOT_CMD = raspbootcom /dev/ttyUSB0 kernel8.img
all: clean cross_cont_download kernel8.img
cross_cont_download:
ifeq (,$(wildcard $(CROSS_CONTAINER)))
docker run --rm dockcross/linux-arm64 > $(CROSS_CONTAINER)
chmod +x $(CROSS_CONTAINER)
endif
target/$(TARGET)/debug/kernel8: src/main.rs
RUST_TARGET_PATH=$(shell pwd) xargo build --target=$(TARGET)
cp $@ .
target/$(TARGET)/release/kernel8: src/main.rs
RUST_TARGET_PATH=$(shell pwd) xargo build --target=$(TARGET) --release
cp $@ .
ifeq ($(DEBUG),1)
kernel8: target/$(TARGET)/debug/kernel8
else
kernel8: target/$(TARGET)/release/kernel8
endif
kernel8.img: kernel8
$(CROSS_CONTAINER) $(CROSS_CONTAINER_OBJCOPY) -O binary -S $< kernel8.img
qemu:
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio
raspboot:
$(DOCKER_CMD) $(DOCKER_TTY) $(UTILS_CONTAINER) $(RASPBOOT_CMD)
clippy:
RUSTFLAGS="-C panic=abort" xargo clippy --target=$(TARGET)
clean:
cargo clean
rm -f kernel8

@ -1,4 +1,4 @@
# Tutorial 06 - Hardware Random Number Generator
# Tutorial 07 - Hardware Random Number Generator
This going to be an easy tutorial. We query a number from the (undocumented)
hardware random number generator. You can use this to implement a simple, but

@ -0,0 +1,6 @@
[dependencies.core]
stage = 0
[dependencies.compiler_builtins]
features = ["mem"] # for memset() et al.
stage = 1

@ -0,0 +1,24 @@
{
"arch": "aarch64",
"data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
"executables": true,
"linker-flavor": "ld.lld",
"linker-is-gnu": true,
"pre-link-args": {
"ld.lld": [
"--script=link.ld"
]
},
"llvm-target": "aarch64-unknown-none",
"no-compiler-rt": true,
"features": "+a53,+strict-align",
"max-atomic-width": 128,
"os": "none",
"panic": "abort",
"panic-strategy": "abort",
"relocation-model": "pic",
"target-c-int-width": "32",
"target-endian": "little",
"target-pointer-width": "64",
"disable-redzone": true
}

@ -0,0 +1,6 @@
[package]
name = "raspi3_glue"
version = "0.1.0"
authors = ["Andre Richter <andre.o.richter@gmail.com>"]
[dependencies]

@ -0,0 +1,30 @@
/*
* MIT License
*
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use volatile_register::RW;
use super::MMIO_BASE;
pub const GPFSEL1: *const RW<u32> = (MMIO_BASE + 0x0020_0004) as *const RW<u32>;
pub const GPPUD: *const RW<u32> = (MMIO_BASE + 0x0020_0094) as *const RW<u32>;
pub const GPPUDCLK0: *const RW<u32> = (MMIO_BASE + 0x0020_0098) as *const RW<u32>;
Loading…
Cancel
Save