diff options
| author | Miquel Sabaté Solà <mikisabate@gmail.com> | 2024-09-03 11:53:15 +0200 |
|---|---|---|
| committer | Miquel Sabaté Solà <mikisabate@gmail.com> | 2024-12-05 16:02:49 +0100 |
| commit | 4f69fb67ee229c7b59ced5db64142b527d74ce7b (patch) | |
| tree | d9aab519f0caa6086368c297d9849bbf7e4589b0 /arch/riscv/machine/machine.S | |
| parent | e94cc18273810ce9cc44d80608042c16eae4e6e4 (diff) | |
| download | farga-4f69fb67ee229c7b59ced5db64142b527d74ce7b.tar.gz farga-4f69fb67ee229c7b59ced5db64142b527d74ce7b.zip | |
Provide an improved example on early boot stage
This also implied a renaming arch/riscv/bios -> arch/riscv/machine.
Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
Diffstat (limited to 'arch/riscv/machine/machine.S')
| -rw-r--r-- | arch/riscv/machine/machine.S | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/arch/riscv/machine/machine.S b/arch/riscv/machine/machine.S new file mode 100644 index 0000000..8a7ce8d --- /dev/null +++ b/arch/riscv/machine/machine.S @@ -0,0 +1,125 @@ +.section .text + +# Returns the physical address that was identified to be for the serial port. If +# any error was found then it just returns 0. This function accepts one argument +# which is a pointer to the FDT structure as given by the firmware. You can +# consider this function to have this equivalent C signature: +# +# uint64_t get_serial_address(void *fdt); +# +.type get_serial_address, @function +get_serial_address: + # Leave early if the caller passed a NULL pointer. + beqz a0, .parse_error + + # First loop. This one simply iterates over the FDT structure in search for + # 'serial@', which marks the beginning of the address definition. +.search_serial_loop_reset: + la t1, .serial +.search_serial_loop: + lbu t2, (t1) + beqz t2, .parse_hex + + lbu t3, (a0) + addi a0, a0, 1 + bne t2, t3, .search_serial_loop_reset + addi t1, t1, 1 + j .search_serial_loop + + # Second loop. On each digit shift left one nibble and add the character + # converted to an integer. If the character cannot be converted, then we go + # to `parse_error` which sets the return value to 0. +.parse_hex: + mv t1, zero + li t2, 10 + li t3, 0x30 # Character '0' + +.parse_hex_loop: + # The FDT specification guarantees that strings are NULL-terminated. Thus, + # whenever we find the NULL character, then we are done parsing. + lbu t0, (a0) + beqz t0, .get_serial_address_end + + # Trying to parse a numeric character. Note that this parser is grossly + # uncapable of handling Aa-Ff hexadecimal values, and only knows numeric + # digits. This can be improved but I did not have an example to work + # on this case. + sub t0, t0, t3 + bltz t0, .parse_error + blt t2, t0, .parse_error + + # Shift one nibble on the accumulator and add the computed value to it. + sll t1, t1, 4 + add t1, t1, t0 + + # Next loop. + addi a0, a0, 1 + j .parse_hex_loop + +.parse_error: + mv t1, zero +.get_serial_address_end: + mv a0, t1 + ret + +# Send the `message` to the given `address` so it's printed there. You can +# consider this function to have this equivalent C signature: +# +# void printm(uint64_t address, char *message); +# +.type printm, @function +printm: + mv t1, a1 + +.printm_loop: + lbu t0, (t1) + beqz t0, .printm_end + + sb t0, (a0) + addi t1, t1, 1 + j .printm_loop + +.printm_end: + ret + +.section .text.bios + +# The entry point: the firmware will blindly jump here. We expect the firmware +# to pass up two arguments, the first one being the hart ID, and the other being +# a pointer to the FDT describing this machine. You can consider this function +# to have this equivalent C signature: +# +# void _start(uint64_t hart_id, void *fdt) __attribute__((noreturn)); +# +.global _start +.type _start, @function +_start: + # We expect the configuration to be given as a devicetree blob pointed by + # the second argument (a1). Hence, the configuration pointer from RISC-V + # should be set to NULL indicating that. If that's not the case, then it's + # not a supported scenario and we jump to the infinite loop gracefully. + csrr t0, mconfigptr + bnez t0, .end + + # We don't care about the hard ID, but we need the FDT pointer as an + # argument for the `get_serial_address` function. + mv a0, a1 + call get_serial_address + + # If the return value of `get_serial_address` is a NULL value, then + # something went wrong there and we just have to end it here. + beqz a0, .end + + # And now we can print our message. + la a1, .msg1 + call printm + + # Infinite loop so not to crash :) +.end: + j .end + +.section .rodata +.msg1: + .string "Hello, world!\n" +.serial: + .string "serial@" |
