diff --git a/14_raspbootin64/OLVASSEL.md b/14_raspbootin64/OLVASSEL.md index 78c63fd4..090c7d2c 100644 --- a/14_raspbootin64/OLVASSEL.md +++ b/14_raspbootin64/OLVASSEL.md @@ -9,25 +9,28 @@ A betöltőprogram egyik felét adja csak, a kernel fogadót, ami az RPi-n fut. 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 volt olyan jó fej, hogy megosztotta az általa írt [Java-s verziót](https://github.com/bztsrc/raspi3-tutorial/files/1850345/Raspbootin64Client.zip). 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. Fontos, hogy ezalatt csak relatív címzést használhatunk. 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: +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 - 21: 000000000007ffc0 0 NOTYPE GLOBAL DEFAULT 4 __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()` eljárást, így a teljes méret 1024 bájt alá csökkent. +`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 ----- -Hozzáadtam egy ciklust, ami átmásolja a kódunkat arra a címre, ahová vártuk, hogy betöltődjön. +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 ------ @@ -38,4 +41,5 @@ méretét is, amit másolnunk kell. Main ---- -Kiírjuk, hogy 'RBIN64', majd beolvassuk az új kernelt a soros vonalról, és átadjuk rá a vezérlést. +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 index 3dc427d0..d23bd25c 100644 --- a/14_raspbootin64/README.md +++ b/14_raspbootin64/README.md @@ -9,26 +9,28 @@ I only provide one part of the loader, the kernel receiver, which runs on the RP 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 was kind to share a [Java version](https://github.com/bztsrc/raspi3-tutorial/files/1850345/Raspbootin64Client.zip) 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. What's important, that we can only use relative addresses -while doing so. When we're done, the memory at 0x80000 must be free to use. You can checked that with: +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 - 21: 000000000007ffc0 0 NOTYPE GLOBAL DEFAULT 4 __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()` I've managed to shrink the -size below 1024 bytes. +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 ----- -Added a loop to relocate our code to the address it should have been loaded to. +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 ------ @@ -39,4 +41,5 @@ know how many bytes we have to copy. Main ---- -We print 'RBIN64', receive the new kernel over serial, and jump to it. +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/kernel8.img b/14_raspbootin64/kernel8.img index d60f7938..50d3215b 100755 Binary files a/14_raspbootin64/kernel8.img and b/14_raspbootin64/kernel8.img differ diff --git a/14_raspbootin64/main.c b/14_raspbootin64/main.c index cc6302b3..3619f37b 100644 --- a/14_raspbootin64/main.c +++ b/14_raspbootin64/main.c @@ -67,6 +67,13 @@ again: // read the kernel while(size--) *kernel++ = uart_getc(); - // jump to the new kernel - asm volatile ("b 0x80000"); + // 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/start.S b/14_raspbootin64/start.S index aa5bb323..dc88415d 100644 --- a/14_raspbootin64/start.S +++ b/14_raspbootin64/start.S @@ -28,6 +28,14 @@ .global _start _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 + // read cpu id, stop slave cores mrs x1, mpidr_el1 and x1, x1, #3