Fixed Issue #11

pull/4/head
bzt 6 years ago
parent 837e67c97a
commit c09567541b

@ -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.

@ -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.

Binary file not shown.

@ -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"
);
}

@ -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

Loading…
Cancel
Save