| /* rt_header is defined by the general linker script (libtock_layout.ld). It has |
| * the following layout: |
| * |
| * Field | Offset |
| * ------------------------------------ |
| * Address of the start symbol | 0 |
| * Initial process break | 4 |
| * Top of the stack | 8 |
| * Size of .data | 12 |
| * Start of .data in flash | 16 |
| * Start of .data in ram | 20 |
| * Size of .bss | 24 |
| * Start of .bss in ram | 28 |
| */ |
| |
| /* start is the entry point -- the first code executed by the kernel. The kernel |
| * passes arguments through 4 registers: |
| * |
| * a0 Pointer to beginning of the process binary's code. The linker script |
| * locates rt_header at this address. |
| * |
| * a1 Address of the beginning of the process's usable memory region. |
| * a2 Size of the process' allocated memory region (including grant region) |
| * a3 Process break provided by the kernel. |
| * |
| * We currently only use the value in a0. It is copied into a5 early on because |
| * a0-a4 are needed to invoke system calls. |
| */ |
| .section .start, "ax" |
| .globl start |
| start: |
| /* First, verify the process binary was loaded at the correct address. The |
| * check is performed by comparing the program counter at the start to the |
| * address of `start`, which is stored in rt_header. */ |
| auipc s0, 0 /* s0 = pc */ |
| mv a5, a0 /* Save rt_header so syscalls don't overwrite it */ |
| lw s1, 0(a5) /* s1 = rt_header.start */ |
| beq s0, s1, .Lset_brk /* Skip error handling code if pc is correct */ |
| /* If the beq on the previous line did not jump, then the binary is not at |
| * the correct location. Report the error via LowLevelDebug then exit. */ |
| li a0, 8 /* LowLevelDebug driver number */ |
| li a1, 1 /* Command: Print alert code */ |
| li a2, 2 /* Alert code 2 (incorrect location) */ |
| li a4, 2 /* `command` class */ |
| ecall |
| li a0, 0 /* exit-terminate */ |
| /* TODO: Set a completion code, once completion codes are decided */ |
| li a4, 6 /* `exit` class */ |
| ecall |
| |
| .Lset_brk: |
| /* memop(): set brk to rt_header's initial break value */ |
| li a0, 0 /* operation: set break */ |
| lw a1, 4(a5) /* rt_header's initial process break */ |
| li a4, 5 /* `memop` class */ |
| ecall |
| |
| /* Set the stack pointer */ |
| lw sp, 8(a5) /* sp = rt_header._stack_top */ |
| |
| /* Copy .data into place. */ |
| lw a0, 12(a5) /* remaining = rt_header.data_size */ |
| beqz a0, .Lzero_bss /* Jump to zero_bss if remaining is zero */ |
| lw a1, 16(a5) /* src = rt_header.data_flash_start */ |
| lw a2, 20(a5) /* dest = rt_header.data_ram_start */ |
| .Ldata_loop_body: |
| lw a3, 0(a1) /* a3 = *src */ |
| sw a3, 0(a2) /* *dest = a3 */ |
| addi a0, a0, -4 /* remaining -= 4 */ |
| addi a1, a1, 4 /* src += 4 */ |
| addi a2, a2, 4 /* dest += 4 */ |
| bnez a0, .Ldata_loop_body /* Iterate again if remaining != 0 */ |
| |
| .Lzero_bss: |
| lw a0, 24(a5) /* remaining = rt_header.bss_size */ |
| beqz a0, .Lcall_rust_start /* Jump to call_Main if remaining is zero */ |
| lw a1, 28(a5) /* dest = rt_header.bss_start */ |
| .Lbss_loop_body: |
| sb zero, 0(a1) /* *dest = zero */ |
| addi a0, a0, -1 /* remaining -= 1 */ |
| addi a1, a1, 1 /* dest += 1 */ |
| bnez a0, .Lbss_loop_body /* Iterate again if remaining != 0 */ |
| |
| .Lcall_rust_start: |
| /* Note: rust_start must be a diverging function (i.e. return `!`) */ |
| jal rust_start |