diff --git a/.14_raspbootin64/Makefile b/.14_raspbootin64/Makefile deleted file mode 100644 index f6ba7559..00000000 --- a/.14_raspbootin64/Makefile +++ /dev/null @@ -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 diff --git a/.14_raspbootin64/OLVASSEL.md b/.14_raspbootin64/OLVASSEL.md deleted file mode 100644 index b25a615f..00000000 --- a/.14_raspbootin64/OLVASSEL.md +++ /dev/null @@ -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. diff --git a/.14_raspbootin64/README.md b/.14_raspbootin64/README.md deleted file mode 100644 index 4ede6ebb..00000000 --- a/.14_raspbootin64/README.md +++ /dev/null @@ -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. diff --git a/.14_raspbootin64/gpio.h b/.14_raspbootin64/gpio.h deleted file mode 100644 index 52fa671d..00000000 --- a/.14_raspbootin64/gpio.h +++ /dev/null @@ -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)) diff --git a/.14_raspbootin64/kernel8.img b/.14_raspbootin64/kernel8.img deleted file mode 100755 index 50d3215b..00000000 Binary files a/.14_raspbootin64/kernel8.img and /dev/null differ diff --git a/.14_raspbootin64/main.c b/.14_raspbootin64/main.c deleted file mode 100644 index 3619f37b..00000000 --- a/.14_raspbootin64/main.c +++ /dev/null @@ -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" - ); -} diff --git a/.14_raspbootin64/mbox.c b/.14_raspbootin64/mbox.c deleted file mode 100644 index a7220834..00000000 --- a/.14_raspbootin64/mbox.c +++ /dev/null @@ -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; -} diff --git a/.14_raspbootin64/mbox.h b/.14_raspbootin64/mbox.h deleted file mode 100644 index 09844dea..00000000 --- a/.14_raspbootin64/mbox.h +++ /dev/null @@ -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); diff --git a/.14_raspbootin64/uart.c b/.14_raspbootin64/uart.c deleted file mode 100644 index 406f9dad..00000000 --- a/.14_raspbootin64/uart.c +++ /dev/null @@ -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); -} diff --git a/.14_raspbootin64/uart.h b/.14_raspbootin64/uart.h deleted file mode 100644 index 99b2be48..00000000 --- a/.14_raspbootin64/uart.h +++ /dev/null @@ -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); diff --git a/01_bareminimum/Makefile b/01_bareminimum/Makefile index f0e75cd2..dc7722b5 100644 --- a/01_bareminimum/Makefile +++ b/01_bareminimum/Makefile @@ -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 diff --git a/02_multicore_rust/Makefile b/02_multicore_rust/Makefile index 94bb0ad3..1691dc78 100644 --- a/02_multicore_rust/Makefile +++ b/02_multicore_rust/Makefile @@ -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) diff --git a/03_uart1/Makefile b/03_uart1/Makefile index 94bb0ad3..9f09c6a8 100644 --- a/03_uart1/Makefile +++ b/03_uart1/Makefile @@ -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) diff --git a/04_mailboxes/Makefile b/04_mailboxes/Makefile index a136e894..9f09c6a8 100644 --- a/04_mailboxes/Makefile +++ b/04_mailboxes/Makefile @@ -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) diff --git a/05_uart0/Makefile b/05_uart0/Makefile index a136e894..9f09c6a8 100644 --- a/05_uart0/Makefile +++ b/05_uart0/Makefile @@ -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) diff --git a/06_random/Cargo.lock b/06_raspbootin64/Cargo.lock similarity index 100% rename from 06_random/Cargo.lock rename to 06_raspbootin64/Cargo.lock diff --git a/06_random/Cargo.toml b/06_raspbootin64/Cargo.toml similarity index 100% rename from 06_random/Cargo.toml rename to 06_raspbootin64/Cargo.toml diff --git a/06_random/Makefile b/06_raspbootin64/Makefile similarity index 95% rename from 06_random/Makefile rename to 06_raspbootin64/Makefile index a136e894..9f09c6a8 100644 --- a/06_random/Makefile +++ b/06_raspbootin64/Makefile @@ -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) diff --git a/06_raspbootin64/README.md b/06_raspbootin64/README.md new file mode 100644 index 00000000..07473779 --- /dev/null +++ b/06_raspbootin64/README.md @@ -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. diff --git a/06_random/Xargo.toml b/06_raspbootin64/Xargo.toml similarity index 100% rename from 06_random/Xargo.toml rename to 06_raspbootin64/Xargo.toml diff --git a/06_random/aarch64-raspi3-none-elf.json b/06_raspbootin64/aarch64-raspi3-none-elf.json similarity index 100% rename from 06_random/aarch64-raspi3-none-elf.json rename to 06_raspbootin64/aarch64-raspi3-none-elf.json diff --git a/06_raspbootin64/kernel8.img b/06_raspbootin64/kernel8.img new file mode 100755 index 00000000..0411835c Binary files /dev/null and b/06_raspbootin64/kernel8.img differ diff --git a/.14_raspbootin64/link.ld b/06_raspbootin64/link.ld similarity index 84% rename from .14_raspbootin64/link.ld rename to 06_raspbootin64/link.ld index 1259029c..531b98a2 100644 --- a/.14_raspbootin64/link.ld +++ b/06_raspbootin64/link.ld @@ -1,5 +1,6 @@ /* * Copyright (C) 2018 bzt (bztsrc@github) + * Copyright (c) 2018 Andre Richter * * 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; diff --git a/06_random/raspi3_glue/Cargo.toml b/06_raspbootin64/raspi3_glue/Cargo.toml similarity index 100% rename from 06_random/raspi3_glue/Cargo.toml rename to 06_raspbootin64/raspi3_glue/Cargo.toml diff --git a/.14_raspbootin64/start.S b/06_raspbootin64/raspi3_glue/src/boot_cores.S similarity index 71% rename from .14_raspbootin64/start.S rename to 06_raspbootin64/raspi3_glue/src/boot_cores.S index dc88415d..d766bc52 100644 --- a/.14_raspbootin64/start.S +++ b/06_raspbootin64/raspi3_glue/src/boot_cores.S @@ -1,5 +1,6 @@ /* * Copyright (C) 2018 bzt (bztsrc@github) + * Copyright (c) 2018 Andre Richter * * 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 diff --git a/06_raspbootin64/raspi3_glue/src/lib.rs b/06_raspbootin64/raspi3_glue/src/lib.rs new file mode 100644 index 00000000..abf32610 --- /dev/null +++ b/06_raspbootin64/raspi3_glue/src/lib.rs @@ -0,0 +1,73 @@ +/* + * MIT License + * + * Copyright (c) 2018 Jorge Aparicio + * Copyright (c) 2018 Andre Richter + * + * 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(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")); diff --git a/06_random/src/gpio.rs b/06_raspbootin64/src/gpio.rs similarity index 100% rename from 06_random/src/gpio.rs rename to 06_raspbootin64/src/gpio.rs diff --git a/06_raspbootin64/src/main.rs b/06_raspbootin64/src/main.rs new file mode 100644 index 00000000..9c3adfe2 --- /dev/null +++ b/06_raspbootin64/src/main.rs @@ -0,0 +1,83 @@ +/* + * MIT License + * + * Copyright (c) 2018 Andre Richter + * + * 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"); + } +} diff --git a/06_raspbootin64/src/mbox.rs b/06_raspbootin64/src/mbox.rs new file mode 100644 index 00000000..407c1072 --- /dev/null +++ b/06_raspbootin64/src/mbox.rs @@ -0,0 +1,139 @@ +/* + * MIT License + * + * Copyright (c) 2018 Andre Richter + * + * 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, // 0x00 + __reserved_0: [u32; 3], // 0x04 + POLL: RO, // 0x10 + SENDER: RO, // 0x14 + STATUS: RO, // 0x18 + CONFIG: RO, // 0x1C + WRITE: WO, // 0x20 +} + +// Custom errors +pub enum MboxError { + ResponseError, + UnknownError, +} +pub type Result = ::core::result::Result; + +// 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), + }; + } + } + } +} diff --git a/06_raspbootin64/src/uart.rs b/06_raspbootin64/src/uart.rs new file mode 100644 index 00000000..25b7872f --- /dev/null +++ b/06_raspbootin64/src/uart.rs @@ -0,0 +1,153 @@ +/* + * MIT License + * + * Copyright (c) 2018 Andre Richter + * + * 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, // 0x00 + __reserved_0: [u32; 5], // 0x04 + FR: RO, // 0x18 + __reserved_1: [u32; 2], // 0x1c + IBRD: WO, // 0x24 + FBRD: WO, // 0x28 + LCRH: WO, // 0x2C + CR: WO, // 0x30 + __reserved_2: [u32; 4], // 0x34 + ICR: WO, // 0x44 +} + +pub enum UartError { + MailboxError, +} +pub type Result = ::core::result::Result; + +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 + } + } +} diff --git a/07_random/Cargo.lock b/07_random/Cargo.lock new file mode 100644 index 00000000..3352bcb2 --- /dev/null +++ b/07_random/Cargo.lock @@ -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" diff --git a/07_random/Cargo.toml b/07_random/Cargo.toml new file mode 100644 index 00000000..df9e43a9 --- /dev/null +++ b/07_random/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "kernel8" +version = "0.1.0" +authors = ["Andre Richter "] + +[dependencies] +raspi3_glue = { path = "raspi3_glue" } +volatile-register = "0.2.0" diff --git a/07_random/Makefile b/07_random/Makefile new file mode 100644 index 00000000..473b2814 --- /dev/null +++ b/07_random/Makefile @@ -0,0 +1,72 @@ +# +# MIT License +# +# Copyright (c) 2018 Andre Richter +# +# 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 diff --git a/06_random/README.md b/07_random/README.md similarity index 91% rename from 06_random/README.md rename to 07_random/README.md index 3489c004..4677ac70 100644 --- a/06_random/README.md +++ b/07_random/README.md @@ -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 diff --git a/07_random/Xargo.toml b/07_random/Xargo.toml new file mode 100644 index 00000000..163024ec --- /dev/null +++ b/07_random/Xargo.toml @@ -0,0 +1,6 @@ +[dependencies.core] +stage = 0 + +[dependencies.compiler_builtins] +features = ["mem"] # for memset() et al. +stage = 1 diff --git a/07_random/aarch64-raspi3-none-elf.json b/07_random/aarch64-raspi3-none-elf.json new file mode 100644 index 00000000..407f21b8 --- /dev/null +++ b/07_random/aarch64-raspi3-none-elf.json @@ -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 +} diff --git a/06_random/kernel8.img b/07_random/kernel8.img similarity index 100% rename from 06_random/kernel8.img rename to 07_random/kernel8.img diff --git a/06_random/link.ld b/07_random/link.ld similarity index 100% rename from 06_random/link.ld rename to 07_random/link.ld diff --git a/07_random/raspi3_glue/Cargo.toml b/07_random/raspi3_glue/Cargo.toml new file mode 100644 index 00000000..99c8e683 --- /dev/null +++ b/07_random/raspi3_glue/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "raspi3_glue" +version = "0.1.0" +authors = ["Andre Richter "] + +[dependencies] diff --git a/06_random/raspi3_glue/src/boot_cores.S b/07_random/raspi3_glue/src/boot_cores.S similarity index 100% rename from 06_random/raspi3_glue/src/boot_cores.S rename to 07_random/raspi3_glue/src/boot_cores.S diff --git a/06_random/raspi3_glue/src/lib.rs b/07_random/raspi3_glue/src/lib.rs similarity index 100% rename from 06_random/raspi3_glue/src/lib.rs rename to 07_random/raspi3_glue/src/lib.rs diff --git a/07_random/src/gpio.rs b/07_random/src/gpio.rs new file mode 100644 index 00000000..9db608e8 --- /dev/null +++ b/07_random/src/gpio.rs @@ -0,0 +1,30 @@ +/* + * MIT License + * + * Copyright (c) 2018 Andre Richter + * + * 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 = (MMIO_BASE + 0x0020_0004) as *const RW; +pub const GPPUD: *const RW = (MMIO_BASE + 0x0020_0094) as *const RW; +pub const GPPUDCLK0: *const RW = (MMIO_BASE + 0x0020_0098) as *const RW; diff --git a/06_random/src/main.rs b/07_random/src/main.rs similarity index 100% rename from 06_random/src/main.rs rename to 07_random/src/main.rs diff --git a/06_random/src/mbox.rs b/07_random/src/mbox.rs similarity index 100% rename from 06_random/src/mbox.rs rename to 07_random/src/mbox.rs diff --git a/06_random/src/rand.rs b/07_random/src/rand.rs similarity index 100% rename from 06_random/src/rand.rs rename to 07_random/src/rand.rs diff --git a/06_random/src/uart.rs b/07_random/src/uart.rs similarity index 100% rename from 06_random/src/uart.rs rename to 07_random/src/uart.rs