| /* |
| * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation; either version 2 of the |
| * License, or any later version. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| * 02110-1301, USA. |
| * |
| * You can also choose to distribute this program under the terms of |
| * the Unmodified Binary Distribution Licence (as given in the file |
| * COPYING.UBDL), provided that you have satisfied its requirements. |
| */ |
| |
| FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) |
| |
| /** @file |
| * |
| * RISC-V prefix library |
| * |
| */ |
| |
| #include <config/serial.h> |
| #include <config/fault.h> |
| |
| .section ".note.GNU-stack", "", @progbits |
| .text |
| |
| /* Link-time base address of _prefix |
| * |
| * This will be not be updated if runtime relocations are applied. |
| */ |
| .section ".rodata.prefix_link", "a", @progbits |
| .balign ( __riscv_xlen / 8 ) |
| prefix_link: |
| .dword _base |
| .size prefix_link, . - prefix_link |
| |
| /* Virtual address of _prefix |
| * |
| * This will be updated if runtime relocations are applied. |
| */ |
| .section ".rodata.prefix_virt", "a", @progbits |
| .balign ( __riscv_xlen / 8 ) |
| prefix_virt: |
| .dword _prefix |
| .size prefix_virt, . - prefix_virt |
| |
| /***************************************************************************** |
| * |
| * Print character via debug console extension |
| * |
| ***************************************************************************** |
| * |
| * Print a single character via the SBI DBCN extension. |
| * |
| * Parameters: |
| * |
| * a0 - Character to print |
| * |
| * Returns: |
| * |
| * a0 - Zero if character printed successfully |
| * a1 - Overwritten |
| * a6 - Overwritten |
| * a7 - Overwritten |
| * |
| */ |
| |
| /* SBI debug console extension */ |
| #define SBI_DBCN ( ( 'D' << 24 ) | ( 'B' << 16 ) | ( 'C' << 8 ) | 'N' ) |
| #define SBI_DBCN_WRITE_BYTE 0x02 |
| |
| .macro print_char_dbcn |
| li a7, SBI_DBCN |
| li a6, SBI_DBCN_WRITE_BYTE |
| ecall |
| .endm |
| |
| /***************************************************************************** |
| * |
| * Print character via legacy extension |
| * |
| ***************************************************************************** |
| * |
| * Print a single character via the SBI putchar legacy extension. |
| * |
| * Parameters: |
| * |
| * a0 - Character to print |
| * |
| * Returns: |
| * |
| * a0 - Overwritten |
| * a7 - Overwritten |
| * |
| */ |
| |
| /* SBI legacy console putchar */ |
| #define SBI_LEGACY_PUTCHAR 0x01 |
| |
| .macro print_char_legacy |
| li a7, SBI_LEGACY_PUTCHAR |
| ecall |
| .endm |
| |
| /***************************************************************************** |
| * |
| * Print character via early UART |
| * |
| ***************************************************************************** |
| * |
| * Print a single character via a UART. |
| * |
| * For devices without a functional SBI console, a UART at a hardcoded |
| * address can be used as a last resort mechanism for obtaining debug |
| * output from the prefix. |
| * |
| * Parameters: |
| * |
| * a0 - Character to print |
| * |
| * Returns: |
| * |
| * a0 - Preserved |
| * a1 - May be overwritten |
| * a6 - May be overwritten |
| * a7 - May be overwritten |
| * |
| */ |
| |
| /* Default to no UART, if not specified */ |
| #ifndef EARLY_UART_MODEL |
| #define EARLY_UART_MODEL none |
| #endif |
| |
| /* Default to a register shift of zero, if not specified */ |
| #ifndef EARLY_UART_REG_SHIFT |
| #define EARLY_UART_REG_SHIFT 0 |
| #endif |
| |
| #define print_char_uart _C2 ( print_char_uart_, EARLY_UART_MODEL ) |
| |
| #define early_uart_reg_base _C2 ( early_uart_reg_base_, __riscv_xlen ) |
| |
| /* Print character via nonexistent UART */ |
| .macro print_char_uart_none |
| .endm |
| |
| /* |
| * Get UART base address (64-bit addressing) |
| */ |
| .macro early_uart_reg_base_64 reg |
| csrr \reg, satp |
| beqz \reg, early_uart_reg_base_64_nonpaged_\@ |
| LOADN \reg, early_uart_reg_base_64_virt |
| j early_uart_reg_base_64_done_\@ |
| early_uart_reg_base_64_nonpaged_\@: |
| li \reg, EARLY_UART_REG_BASE |
| early_uart_reg_base_64_done_\@: |
| .endm |
| |
| /* |
| * Get UART base address (32-bit addressing) |
| */ |
| .macro early_uart_reg_base_32 reg |
| li \reg, EARLY_UART_REG_BASE |
| sub \reg, \reg, tp |
| .endm |
| |
| /***************************************************************************** |
| * |
| * Print character via 8250-compatible early UART |
| * |
| ***************************************************************************** |
| * |
| * Print a single character via an 8250- or 16550-compatible UART. |
| * |
| * Parameters: |
| * |
| * a0 - Character to print |
| * |
| * Returns: |
| * |
| * a0 - Preserved |
| * a1 - Overwritten |
| * a7 - Overwritten |
| * |
| */ |
| |
| /* 8250-compatible UART transmit registers */ |
| #define EARLY_UART_8250_TX ( 0 << EARLY_UART_REG_SHIFT ) |
| #define EARLY_UART_8250_LSR ( 5 << EARLY_UART_REG_SHIFT ) |
| #define EARLY_UART_8250_LSR_THRE 0x20 |
| |
| .macro print_char_uart_8250 |
| early_uart_reg_base a7 |
| sb a0, EARLY_UART_8250_TX(a7) |
| fence |
| early_uart_8250_wait_\@: |
| lbu a1, EARLY_UART_8250_LSR(a7) |
| andi a1, a1, EARLY_UART_8250_LSR_THRE |
| beqz a1, early_uart_8250_wait_\@ |
| .endm |
| |
| /***************************************************************************** |
| * |
| * Print character via SiFive-compatible early UART |
| * |
| ***************************************************************************** |
| * |
| * Print a single character via a SiFive-compatible UART. |
| * |
| * Parameters: |
| * |
| * a0 - Character to print |
| * |
| * Returns: |
| * |
| * a0 - Preserved |
| * a1 - Overwritten |
| * a7 - Overwritten |
| * |
| */ |
| |
| /* SiFive-compatible UART transmit registers */ |
| #define EARLY_UART_SIFIVE_TXFIFO ( 0 << EARLY_UART_REG_SHIFT ) |
| |
| .macro print_char_uart_sifive |
| early_uart_reg_base a7 |
| sw a0, EARLY_UART_SIFIVE_TXFIFO(a7) |
| fence |
| early_uart_sifive_wait_\@: |
| lw a1, EARLY_UART_SIFIVE_TXFIFO(a7) |
| bltz a1, early_uart_sifive_wait_\@ |
| .endm |
| |
| /***************************************************************************** |
| * |
| * Print single character to early UART (from C code) |
| * |
| ***************************************************************************** |
| * |
| * This function is called by the SBI console driver to output a |
| * character to the early UART (if enabled). |
| * |
| * The standard C ABI applies to this function. |
| * |
| * Parameters: |
| * |
| * a0 - Character to print |
| * |
| * Returns: none |
| * |
| */ |
| |
| .section ".prefix.early_uart_putchar", "ax", @progbits |
| .globl early_uart_putchar |
| early_uart_putchar: |
| print_char_uart |
| ret |
| .size early_uart_putchar, . - early_uart_putchar |
| |
| /***************************************************************************** |
| * |
| * Print message to debug console |
| * |
| ***************************************************************************** |
| * |
| * Print a NUL-terminated string to the debug console. |
| * |
| * This function prints one character at a time via the "write byte" |
| * call (rather than using "write string"), since this avoids any need |
| * to know the current virtual-physical address translation. It does |
| * not require a valid stack. |
| * |
| * Note that the parameter is passed in register t1 (rather than a0) |
| * and all non-temporary registers are preserved. |
| * |
| * Parameters: |
| * |
| * t1 - Pointer to string |
| * |
| * Returns: none |
| * |
| */ |
| |
| .section ".prefix.print_message", "ax", @progbits |
| .globl print_message |
| print_message: |
| /* Handle alternate link register */ |
| mv t0, ra |
| print_message_alt: |
| /* Register usage: |
| * |
| * a0 - current character |
| * t0 - alternate link register |
| * t1 - character pointer |
| * t2 - preserved a0 |
| * t3 - preserved a1 |
| * t4 - preserved a6 |
| * t5 - preserved a7 |
| */ |
| mv t2, a0 |
| mv t3, a1 |
| mv t4, a6 |
| mv t5, a7 |
| |
| 1: /* Print each character in turn */ |
| lbu a0, (t1) |
| addi t1, t1, 1 |
| beqz a0, 2f |
| print_char_uart |
| print_char_dbcn |
| beqz a0, 1b |
| lbu a0, -1(t1) |
| print_char_legacy |
| j 1b |
| 2: |
| /* Restore registers and return (via alternate link register) */ |
| mv a7, t5 |
| mv a6, t4 |
| mv a1, t3 |
| mv a0, t2 |
| jr t0 |
| .size print_message, . - print_message |
| |
| /* |
| * Display progress message (if debugging is enabled) |
| */ |
| .macro progress message |
| #ifndef NDEBUG |
| .section ".rodata.progress_\@", "a", @progbits |
| progress_\@: |
| .asciz "\message" |
| .size progress_\@, . - progress_\@ |
| .previous |
| la t1, progress_\@ |
| jal t0, print_message_alt |
| #endif |
| .endm |
| |
| /***************************************************************************** |
| * |
| * Print hexadecimal value to debug console |
| * |
| ***************************************************************************** |
| * |
| * Print a register value in hexadecimal to the debug console. |
| * |
| * This function does not require a valid stack. |
| * |
| * Note that the parameters are passed in registers t1 and t2 (rather |
| * than a0) and all non-temporary registers are preserved. |
| * |
| * Parameters: |
| * |
| * t1 - Value to print |
| * t2 - Number of bits to print (must be a multiple of 4) |
| * |
| * Returns: none |
| * |
| */ |
| |
| /* |
| * Convert a single nibble to an ASCII character |
| */ |
| .macro nibble_to_ascii reg |
| addi \reg, \reg, -10 |
| bltz \reg, dec_\@ |
| addi \reg, \reg, ( 'a' - ( '0' + 10 ) ) |
| dec_\@: addi \reg, \reg, ( '0' + 10 ) |
| .endm |
| |
| .section ".prefix.print_hex_value", "ax", @progbits |
| .globl print_hex_value |
| print_hex_value: |
| /* Handle alternate link register */ |
| mv t0, ra |
| print_hex_value_alt: |
| /* Register usage: |
| * |
| * a0 - current digit / general temporary |
| * t0 - alternate link register |
| * t1 - current value |
| * t2 - digit counter |
| * t3 - preserved a0 |
| * t4 - preserved a1 |
| * t5 - preserved a6 |
| * t6 - preserved a7 |
| */ |
| mv t3, a0 |
| mv t4, a1 |
| mv t5, a6 |
| mv t6, a7 |
| |
| /* Skip any unprinted digits */ |
| li a0, __riscv_xlen |
| sub a0, a0, t2 |
| sll t1, t1, a0 |
| |
| 1: /* Print each digit in turn */ |
| srli a0, t1, ( __riscv_xlen - 4 ) |
| nibble_to_ascii a0 |
| print_char_uart |
| print_char_dbcn |
| beqz a0, 2f |
| srli a0, t1, ( __riscv_xlen - 4 ) |
| nibble_to_ascii a0 |
| print_char_legacy |
| 2: slli t1, t1, 4 |
| addi t2, t2, -4 |
| bgtz t2, 1b |
| |
| /* Restore registers and return (via alternate link register) */ |
| mv a7, t6 |
| mv a6, t5 |
| mv a1, t4 |
| mv a0, t3 |
| jr t0 |
| .size print_hex_value, . - print_hex_value |
| |
| /* |
| * Display hexadecimal register value (if debugging is enabled) |
| */ |
| .macro print_hex_reg reg, bits=__riscv_xlen |
| #ifndef NDEBUG |
| mv t1, \reg |
| li t2, \bits |
| jal t0, print_hex_value_alt |
| #endif |
| .endm |
| |
| /* |
| * Display hexadecimal symbol address (if debugging is enabled) |
| */ |
| .macro print_hex_addr sym |
| #ifndef NDEBUG |
| la t1, \sym |
| li t2, __riscv_xlen |
| jal t0, print_hex_value_alt |
| #endif |
| .endm |
| |
| /* |
| * Display hexadecimal data value (if debugging is enabled) |
| */ |
| .macro print_hex_data sym |
| #ifndef NDEBUG |
| LOADN t1, \sym |
| li t2, __riscv_xlen |
| jal t0, print_hex_value_alt |
| #endif |
| .endm |
| |
| /***************************************************************************** |
| * |
| * Apply compressed relocation records |
| * |
| ***************************************************************************** |
| * |
| * Apply compressed relocation records to fix up iPXE to run at its |
| * current virtual address. |
| * |
| * This function must run before .bss is zeroed (since the relocation |
| * records are overlaid with .bss). It does not require a valid stack |
| * pointer. |
| * |
| * Parameters: none |
| * |
| * a0 - Relocation records |
| * |
| * Returns: none |
| * |
| */ |
| |
| /** Number of bits in a skip value */ |
| #define ZREL_SKIP_BITS 19 |
| |
| .section ".prefix.apply_relocs", "ax", @progbits |
| .globl apply_relocs |
| apply_relocs: |
| /* Register usage: |
| * |
| * a0 - current relocation record pointer |
| * a1 - current relocation target address |
| * a2 - relocation addend |
| * a3 - current relocation record value |
| * a4 - number of bits remaining in current relocation record |
| */ |
| la a1, _prefix |
| |
| /* Calculate relocation addend */ |
| LOADN a2, prefix_virt |
| sub a2, a1, a2 |
| |
| /* Skip applying relocations if addend is zero */ |
| beqz a2, apply_relocs_done |
| progress " reloc" |
| |
| /* Test writability |
| * |
| * We do this to avoid accidentally sending an undefined |
| * sequence of commands to a flash device, if we are started |
| * from read-only memory with no paging support. |
| * |
| * We attempt to write an all-ones pattern, on the basis that |
| * this pattern will harmlessly cause any flash device |
| * conforming to the CFI01 specification to enter the default |
| * "read array" state. |
| */ |
| la t0, apply_relocs_test |
| li t1, -1 |
| STOREN t1, (t0) |
| LOADN t2, (t0) |
| bne t1, t2, apply_relocs_failed |
| |
| apply_relocs_loop: |
| /* Read new relocation record */ |
| LOADN a3, (a0) |
| addi a0, a0, ( __riscv_xlen / 8 ) |
| li a4, ( __riscv_xlen - 1 ) |
| |
| /* Consume and apply skip, if present (i.e. if MSB=0) */ |
| bltz a3, 1f |
| addi a4, a4, -ZREL_SKIP_BITS |
| srli t0, a3, ( __riscv_xlen - ( ZREL_SKIP_BITS + 1 ) ) |
| slli t0, t0, ( ( __riscv_xlen / 32 ) + 1 ) |
| add a1, a1, t0 |
| 1: |
| /* Apply relocations corresponding to set bits in record */ |
| 1: andi t0, a3, 1 |
| beqz t0, 2f |
| LOADN t1, (a1) |
| add t1, t1, a2 |
| STOREN t1, (a1) |
| 2: addi a1, a1, ( __riscv_xlen / 8 ) |
| srli a3, a3, 1 |
| addi a4, a4, -1 |
| bnez a4, 1b |
| |
| /* Loop until we have reached a terminator record (MSB=0, offset=0) */ |
| bnez a3, apply_relocs_loop |
| |
| /* Check that relocations were applied successfully */ |
| la t0, _prefix |
| LOADN t1, prefix_virt |
| bne t0, t1, apply_relocs_failed |
| |
| apply_relocs_done: |
| /* Return to caller */ |
| progress " ok\r\n" |
| ret |
| |
| apply_relocs_failed: |
| /* Failure to apply relocations (if relocations were needed) |
| * is a fatal error. |
| */ |
| progress " failed\r\n" |
| j reset_system |
| .size apply_relocs, . - apply_relocs |
| |
| /* Writability test |
| * |
| * Placed within .data rather than .bss, since we need this to |
| * be within the range of the stored iPXE image. |
| */ |
| .section ".data.apply_relocs_test", "aw", @progbits |
| .balign ( __riscv_xlen / 8 ) |
| apply_relocs_test: |
| .space ( __riscv_xlen / 8 ) |
| .size apply_relocs_test, . - apply_relocs_test |
| |
| /***************************************************************************** |
| * |
| * Enable paging |
| * |
| ***************************************************************************** |
| * |
| * This function must be called with flat physical addressing. It |
| * does not require a valid stack pointer. |
| * |
| * Parameters: |
| * |
| * a0 - Page table to fill in (4kB, must be aligned to a 4kB boundary) |
| * |
| * Returns: |
| * |
| * a0 - Size of accessible physical address space (or zero for no limit) |
| * tp - Virtual address offset |
| * pc - Updated to a virtual address if paging enabled |
| * |
| */ |
| |
| /** Number of bits in a page offset */ |
| #define PAGE_SHIFT 12 |
| |
| /** Page size */ |
| #define PAGE_SIZE ( 1 << PAGE_SHIFT ) |
| |
| /** Size of a page table entry (log2) */ |
| #define PTE_SIZE_LOG2 ( ( __riscv_xlen / 32 ) + 1 ) |
| |
| /** Size of a page table entry */ |
| #define PTE_SIZE ( 1 << PTE_SIZE_LOG2 ) |
| |
| /** Number of page table entries (log2) */ |
| #define PTE_COUNT_LOG2 ( PAGE_SHIFT - PTE_SIZE_LOG2 ) |
| |
| /** Number of page table entries */ |
| #define PTE_COUNT ( 1 << PTE_COUNT_LOG2 ) |
| |
| /** Number of bits in a virtual or physical page number */ |
| #define VPPN_SHIFT PTE_COUNT_LOG2 |
| |
| /* Page table entry flags */ |
| #define PTE_V 0x00000001 /**< Page table entry is valid */ |
| #define PTE_R 0x00000002 /**< Page is readable */ |
| #define PTE_W 0x00000004 /**< Page is writable */ |
| #define PTE_X 0x00000008 /**< Page is executable */ |
| #define PTE_A 0x00000040 /**< Page has been accessed */ |
| #define PTE_D 0x00000080 /**< Page is dirty */ |
| |
| /* Page table entry flags for our leaf pages */ |
| #define PTE_LEAF ( PTE_D | PTE_A | PTE_X | PTE_W | PTE_R | PTE_V ) |
| |
| /** Physical page number LSB in PTE */ |
| #define PTE_PPN_LSB(x) ( 10 + (x) * VPPN_SHIFT ) |
| #define PTE_PPN4_LSB PTE_PPN_LSB(4) /**< PPN[4] LSB (Sv57) */ |
| #define PTE_PPN3_LSB PTE_PPN_LSB(3) /**< PPN[3] LSB (Sv57 & Sv48) */ |
| #define PTE_PPN2_LSB PTE_PPN_LSB(2) /**< PPN[2] LSB (Sv57, Sv48, & Sv39) */ |
| #define PTE_PPN1_LSB PTE_PPN_LSB(1) /**< PPN[1] LSB (all levels) */ |
| #define PTE_PPN0_LSB PTE_PPN_LSB(0) /**< PPN[0] LSB (all levels) */ |
| |
| /** Page table entry physical page address shift */ |
| #define PTE_PPN_SHIFT ( PAGE_SHIFT - PTE_PPN0_LSB ) |
| |
| /** Virtual page number LSB */ |
| #define VPN_LSB(x) ( PAGE_SHIFT + (x) * VPPN_SHIFT ) |
| #define VPN4_LSB VPN_LSB(4) /**< VPN[4] LSB (Sv57) */ |
| #define VPN3_LSB VPN_LSB(3) /**< VPN[3] LSB (Sv57 & Sv48) */ |
| #define VPN2_LSB VPN_LSB(2) /**< VPN[2] LSB (Sv57, Sv48, & Sv39) */ |
| #define VPN1_LSB VPN_LSB(1) /**< VPN[1] LSB (all levels) */ |
| #define VPN0_LSB VPN_LSB(0) /**< VPN[0] LSB (all levels) */ |
| |
| /* Paging modes */ |
| #define SATP_MODE_SV57 10 /**< Five-level paging (Sv57) */ |
| #define SATP_MODE_SV48 9 /**< Four-level paging (Sv48) */ |
| #define SATP_MODE_SV39 8 /**< Three-level paging (Sv39) */ |
| #define SATP_MODE_SV32 1 /**< Two-level paging (Sv32) */ |
| |
| /** Paging mode shift */ |
| #if __riscv_xlen == 64 |
| #define SATP_MODE_SHIFT 60 |
| #else |
| #define SATP_MODE_SHIFT 31 |
| #endif |
| |
| .globl enable_paging |
| .equ enable_paging, _C2 ( enable_paging_, __riscv_xlen ) |
| |
| /* Paging mode names (for debug messages) */ |
| .section ".rodata.paging_mode_names", "a", @progbits |
| paging_mode_names: |
| .asciz "none" |
| .org ( paging_mode_names + 5 * SATP_MODE_SV32 ) |
| .asciz "Sv32" |
| .org ( paging_mode_names + 5 * SATP_MODE_SV39 ) |
| .asciz "Sv39" |
| .org ( paging_mode_names + 5 * SATP_MODE_SV48 ) |
| .asciz "Sv48" |
| .org ( paging_mode_names + 5 * SATP_MODE_SV57 ) |
| .asciz "Sv57" |
| .size paging_mode_names, . - paging_mode_names |
| |
| /* |
| * Display paging mode name (if debugging is enabled) |
| */ |
| .macro paging_mode_name reg |
| #ifndef NDEBUG |
| slli t0, \reg, 2 |
| add t0, t0, \reg |
| la t1, paging_mode_names |
| add t1, t1, t0 |
| jal t0, print_message_alt |
| #endif |
| .endm |
| |
| /* Maximum physical alignment |
| * |
| * We align to a "megapage" boundary to simplify the task of |
| * setting up page table mappings. |
| */ |
| .globl _max_align |
| .equ _max_align, ( 1 << VPN1_LSB ) |
| |
| /* Space for page table |
| * |
| * This can be used only once .bss is known to be writable. |
| */ |
| .section ".bss.page_table", "a", @nobits |
| .globl page_table |
| .balign PAGE_SIZE |
| page_table: |
| .space PAGE_SIZE |
| .size page_table, . - page_table |
| |
| /* Convert physical address to virtual address */ |
| .macro phys_to_virt rd, rs:vararg |
| _C2 ( phys_to_virt_, __riscv_xlen ) \rd, \rs |
| .endm |
| |
| /***************************************************************************** |
| * |
| * Disable paging |
| * |
| ***************************************************************************** |
| * |
| * This function may be called with either virtual or flat physical |
| * addressing. It does not require a valid stack pointer. |
| * |
| * Parameters: |
| * |
| * tp - Virtual address offset |
| * |
| * Returns: |
| * |
| * tp - Virtual address offset (zeroed) |
| * pc - Updated to a physical address |
| * |
| */ |
| |
| .globl disable_paging |
| .equ disable_paging, _C2 ( disable_paging_, __riscv_xlen ) |
| |
| /***************************************************************************** |
| * |
| * Enable 64-bit paging |
| * |
| ***************************************************************************** |
| * |
| * Construct a 64-bit page table to identity-map the whole of the |
| * mappable physical address space, and to map iPXE itself at its |
| * link-time address (which must be 2MB-aligned and be within the |
| * upper half of the kernel address space). |
| * |
| * This function must be called with flat physical addressing. It |
| * does not require a valid stack pointer. |
| * |
| * Parameters: |
| * |
| * a0 - Page table to fill in (4kB, must be aligned to a 4kB boundary) |
| * |
| * Returns: |
| * |
| * a0 - Size of accessible physical address space (or zero for no limit) |
| * tp - Virtual address offset |
| * pc - Updated to a virtual address if paging enabled |
| * |
| * A 4kB 64-bit page table contains 512 8-byte PTEs. We choose to use |
| * these as: |
| * |
| * - PTE[0-255] : Identity map for the physical address space. |
| * |
| * This conveniently requires exactly 256 PTEs, regardless of the |
| * paging level. Higher paging levels are able to identity-map a |
| * larger physical address space: |
| * |
| * Sv57 : 256 x 256TB "petapages" (55-bit physical address space) |
| * Sv48 : 256 x 512GB "terapages" (46-bit physical address space) |
| * Sv39 : 256 x 1GB "gigapages" (37-bit physical address space) |
| * |
| * Note that Sv48 and Sv39 cannot identity-map the whole of the |
| * available physical address space, since the virtual address |
| * space is not large enough (and is halved by the constraint |
| * that virtual addresses with bit 47/38 set must also have all |
| * higher bits set, and so cannot identity-map to a 55-bit |
| * physical address). |
| * |
| * - PTE[x-y] : Virtual address map for iPXE |
| * |
| * These are 2MB "megapages" used to map the link-time virtual |
| * address range used by iPXE itself. We can use any 2MB-aligned |
| * range within 0xffffffffe0800000-0xffffffffffc00000, which |
| * breaks down as: |
| * |
| * VPN[4] = 511 (in Sv57, must be all-ones in Sv48 and Sv39) |
| * VPN[3] = 511 (in Sv57 and Sv48, must be all-ones in Sv39) |
| * VPN[2] = 511 (in all paging levels) |
| * VPN[1] = 260-510 (in all paging levels) |
| * VPN[0] = 0 (in all paging levels) |
| * |
| * In most builds, only a single 2MB "megapage" will be needed. |
| * We choose a link-time starting address of 0xffffffffeb000000 |
| * within the permitted range, since the "eb" pattern is fairly |
| * distinctive and so makes it easy to visually identify any |
| * addresses originating from within iPXE's virtual address |
| * space. |
| * |
| * - PTE[511] : Recursive next level page table pointer |
| * |
| * This is a non-leaf PTE that points back to the page table |
| * itself. It acts as the next level page table pointer for: |
| * |
| * VPN[4] = 511 (in Sv57) |
| * VPN[3] = 511 (in Sv57 and Sv48) |
| * VPN[2] = 511 (in Sv57, Sv48, and Sv39) |
| * |
| * This recursive usage creates some duplicate mappings within |
| * unused portions of the virtual address space, but allows us to |
| * use only a single physical 4kB page table. |
| */ |
| |
| /** SBI base extension */ |
| #define SBI_BASE 0x10 |
| #define SBI_BASE_MVENDORID 0x04 |
| |
| /** Non-standard T-Head page table entry additional flags |
| * |
| * T-Head processors such as the C910 use the high bits of the PTE in |
| * a very non-standard way that is incompatible with the RISC-V |
| * specification. |
| * |
| * As per the "Memory Attribute Extension (XTheadMae)", bits 62 and 61 |
| * represent cacheability and "bufferability" (i.e. write-back |
| * cacheability) respectively. If we do not enable these bits, then |
| * the processor gets incredibly confused at the point that paging is |
| * enabled. The symptom is that cache lines will occasionally fail to |
| * fill, and so reads from any address may return unrelated data from |
| * a previously read cache line for a different address. |
| */ |
| #define THEAD_PTE_MAEE ( 0x60 << ( __riscv_xlen - 8 ) ) |
| |
| /** T-Head vendor ID */ |
| #define THEAD_MVENDORID 0x5b7 |
| |
| /** T-Head "sxstatus" CSR */ |
| #define THEAD_CSR_SXSTATUS 0x5c0 |
| #define THEAD_CSR_SXSTATUS_MAEE 0x00200000 /**< XTheadMae enabled */ |
| |
| .section ".prefix.enable_paging_64", "ax", @progbits |
| enable_paging_64: |
| /* Register usage: |
| * |
| * tp - return value (virtual address offset) |
| * a0 - page table base address |
| * a1 - currently attempted paging level |
| * a2 - enabled paging level |
| * a3 - PTE pointer |
| * a4 - PTE stride |
| * a5 - size of accessible physical address space |
| */ |
| progress " paging:" |
| |
| /* Calculate virtual address offset */ |
| LOADN t0, prefix_link |
| la t1, _prefix |
| sub tp, t1, t0 |
| |
| /* Zero PTE[0-511] */ |
| li t0, PTE_COUNT |
| mv a3, a0 |
| 1: STOREN zero, (a3) |
| addi a3, a3, PTE_SIZE |
| addi t0, t0, -1 |
| bgtz t0, 1b |
| |
| /* Construct PTE[511] as next level page table pointer */ |
| srli t0, a0, PTE_PPN_SHIFT |
| ori t0, t0, PTE_V |
| STOREN t0, -PTE_SIZE(a3) |
| |
| /* Construct base page table entry for address zero */ |
| li t0, PTE_LEAF |
| STOREN t0, (a0) |
| |
| /* Check for broken T-Head paging extensions */ |
| mv a3, a0 |
| li a7, SBI_BASE |
| li a6, SBI_BASE_MVENDORID |
| ecall |
| bnez a0, 1f |
| li t0, THEAD_MVENDORID |
| bne a1, t0, 1f |
| progress "thead-" |
| csrr t0, THEAD_CSR_SXSTATUS |
| li t1, THEAD_CSR_SXSTATUS_MAEE |
| and t0, t0, t1 |
| beqz t0, 1f |
| progress "mae-" |
| LOADN t0, (a3) |
| li t1, THEAD_PTE_MAEE |
| or t0, t0, t1 |
| STOREN t0, (a3) |
| 1: mv a0, a3 |
| |
| /* Calculate PTE[x] address for iPXE virtual address map */ |
| LOADN t0, prefix_link |
| srli t0, t0, VPN1_LSB |
| andi t0, t0, ( PTE_COUNT - 1 ) |
| slli t0, t0, PTE_SIZE_LOG2 |
| add a3, a0, t0 |
| |
| /* Calculate PTE stride for iPXE virtual address map |
| * |
| * PPN[1] LSB is PTE bit 19 in all paging modes, and so the |
| * stride is always ( 1 << 19 ) |
| */ |
| li a4, 1 |
| slli a4, a4, PTE_PPN1_LSB |
| |
| /* Construct PTE[x-1] for early UART, if applicable */ |
| #ifdef EARLY_UART_REG_BASE |
| li t0, ( EARLY_UART_REG_BASE & ~( ( 1 << VPN1_LSB ) - 1 ) ) |
| srli t0, t0, PTE_PPN_SHIFT |
| ori t0, t0, ( PTE_LEAF & ~PTE_X ) |
| STOREN t0, -PTE_SIZE(a3) |
| #endif |
| |
| /* Construct PTE[x-y] for iPXE virtual address map */ |
| la t0, _prefix |
| srli t0, t0, PTE_PPN_SHIFT |
| LOADN t1, (a0) |
| or t0, t0, t1 |
| la t2, _ebss |
| srli t2, t2, PTE_PPN_SHIFT |
| 1: STOREN t0, (a3) |
| addi a3, a3, PTE_SIZE |
| add t0, t0, a4 |
| ble t0, t2, 1b |
| |
| /* Find highest supported paging level */ |
| li a1, SATP_MODE_SV57 |
| enable_paging_64_loop: |
| |
| /* Calculate PTE stride for identity map at this paging level |
| * |
| * a1 == 10 == Sv57: PPN[4] LSB is PTE bit 46 => stride := 1 << 46 |
| * a1 == 9 == Sv48: PPN[3] LSB is PTE bit 37 => stride := 1 << 37 |
| * a1 == 8 == Sv39: PPN[2] LSB is PTE bit 28 => stride := 1 << 28 |
| * |
| * and so we calculate stride a4 := ( 1 << ( 9 * a1 - 44 ) ) |
| */ |
| slli a4, a1, 3 |
| add a4, a4, a1 |
| addi a4, a4, -44 |
| li t0, 1 |
| sll a4, t0, a4 |
| |
| /* Calculate size of accessible physical address space |
| * |
| * The identity map comprises only the lower half of the PTEs, |
| * since virtual addresses for the higher half must have all |
| * high bits set, and so cannot form part of an identity map. |
| */ |
| slli a5, a4, ( PTE_PPN_SHIFT + ( PTE_COUNT_LOG2 - 1 ) ) |
| |
| /* Construct PTE[0-255] for identity map at this paging level */ |
| mv a3, a0 |
| li t0, ( PTE_COUNT / 2 ) |
| LOADN t1, (a0) |
| 1: STOREN t1, (a3) |
| addi a3, a3, PTE_SIZE |
| add t1, t1, a4 |
| addi t0, t0, -1 |
| bgtz t0, 1b |
| |
| /* Attempt to enable paging, and read back active paging level */ |
| slli t0, a1, SATP_MODE_SHIFT |
| srli t1, a0, PAGE_SHIFT |
| or t0, t0, t1 |
| csrw satp, t0 |
| sfence.vma |
| csrr a2, satp |
| srli a2, a2, SATP_MODE_SHIFT |
| |
| /* Loop until we successfully enable paging, or run out of levels */ |
| beq a2, a1, 1f |
| csrw satp, zero |
| addi a1, a1, -1 |
| li t0, SATP_MODE_SV39 |
| bge a1, t0, enable_paging_64_loop |
| mv tp, zero |
| mv a5, zero |
| 1: |
| /* Adjust return address to a virtual address */ |
| sub ra, ra, tp |
| |
| /* Return, with or without paging enabled */ |
| paging_mode_name a2 |
| mv a0, a5 |
| ret |
| .size enable_paging_64, . - enable_paging_64 |
| |
| /* Convert 64-bit physical address to virtual address */ |
| .macro phys_to_virt_64 rd, rs:vararg |
| .ifnb \rs |
| mv \rd, \rs |
| .endif |
| .endm |
| |
| /* Early UART base address when 64-bit paging is enabled |
| * |
| * When an early UART is in use, we choose to use the 2MB |
| * "megapage" immediately below iPXE itself to map the UART. |
| */ |
| #ifdef EARLY_UART_REG_BASE |
| .section ".rodata.early_uart_reg_base_64_virt", "a", @progbits |
| .balign 8 |
| early_uart_reg_base_64_virt: |
| .dword ( _base - ( 1 << VPN1_LSB ) + \ |
| ( EARLY_UART_REG_BASE & ( ( 1 << VPN1_LSB ) - 1 ) ) ) |
| .size early_uart_reg_base_64_virt, . - early_uart_reg_base_64_virt |
| #endif |
| |
| /***************************************************************************** |
| * |
| * Disable 64-bit paging |
| * |
| ***************************************************************************** |
| * |
| * This function may be called with either virtual or flat physical |
| * addressing. It does not require a valid stack pointer. |
| * |
| * Parameters: |
| * |
| * tp - Virtual address offset |
| * |
| * Returns: |
| * |
| * tp - Virtual address offset (zeroed) |
| * pc - Updated to a physical address |
| * |
| */ |
| |
| .section ".prefix.disable_paging_64", "ax", @progbits |
| disable_paging_64: |
| /* Register usage: |
| * |
| * tp - virtual address offset |
| */ |
| |
| /* Jump to physical address */ |
| la t0, 1f |
| bgez t0, 1f |
| add t0, t0, tp |
| jr t0 |
| 1: |
| /* Disable paging */ |
| csrw satp, zero |
| sfence.vma |
| |
| /* Update return address to a physical address */ |
| bgez ra, 1f |
| add ra, ra, tp |
| 1: |
| /* Return with paging disabled and virtual offset zeroed */ |
| mv tp, zero |
| ret |
| .size disable_paging_64, . - disable_paging_64 |
| |
| /***************************************************************************** |
| * |
| * Enable 32-bit paging |
| * |
| ***************************************************************************** |
| * |
| * Construct a 32-bit page table to map the whole of the 32-bit |
| * address space with a fixed offset selected to map iPXE itself at |
| * its link-time address (which must be 4MB-aligned). |
| * |
| * This function must be called with flat physical addressing. It |
| * does not require a valid stack pointer. |
| * |
| * Parameters: |
| * |
| * a0 - Page table to fill in (4kB, must be aligned to a 4kB boundary) |
| * |
| * Returns: |
| * |
| * a0 - Size of accessible physical address space (or zero for no limit) |
| * tp - Virtual address offset |
| * pc - Updated to a virtual address if paging enabled |
| * |
| * A 4kB 32-bit page table contains 1024 4-byte PTEs. We choose to |
| * use these to produce a circular map of the 32-bit address space |
| * using 4MB "megapages", with a fixed offset to align the virtual and |
| * link-time addresses. |
| * |
| * To handle the transition from physical to virtual addresses, we |
| * temporarily adjust the PTE covering the current program counter to |
| * be a direct physical map (so that the program counter remains valid |
| * at the moment when paging is enabled), then jump to a virtual |
| * address, then restore the temporarily modified PTE. |
| */ |
| |
| .equ enable_paging_32_xalign, 32 |
| |
| .section ".prefix.enable_paging_32", "ax", @progbits |
| enable_paging_32: |
| /* Register usage: |
| * |
| * tp - return value (virtual address offset) |
| * a0 - page table base address |
| * a1 - enabled paging level |
| * a2 - PTE pointer |
| * a3 - saved content of temporarily modified PTE |
| */ |
| progress " paging:" |
| |
| /* Calculate virtual address offset */ |
| LOADN t0, prefix_link |
| la t1, _prefix |
| sub tp, t1, t0 |
| |
| /* Construct PTEs for circular map */ |
| mv a2, a0 |
| li t0, PTE_COUNT |
| mv t1, tp |
| ori t1, t1, ( PTE_LEAF << PTE_PPN_SHIFT ) |
| li t2, ( 1 << ( PTE_PPN1_LSB + PTE_PPN_SHIFT ) ) |
| 1: srli t3, t1, PTE_PPN_SHIFT |
| STOREN t3, (a2) |
| addi a2, a2, PTE_SIZE |
| add t1, t1, t2 |
| addi t0, t0, -1 |
| bgtz t0, 1b |
| |
| /* Temporarily modify PTE for transition code to be an identity map */ |
| la t0, enable_paging_32_xstart |
| srli t0, t0, VPN1_LSB |
| slli t1, t0, PTE_SIZE_LOG2 |
| add a2, a0, t1 |
| LOADN a3, (a2) |
| slli t0, t0, PTE_PPN1_LSB |
| ori t0, t0, PTE_LEAF |
| STOREN t0, (a2) |
| |
| /* Adjust PTE pointer to a virtual address */ |
| sub a2, a2, tp |
| |
| /* Attempt to enable paging, and read back active paging level */ |
| la t0, 1f |
| sub t0, t0, tp |
| li t1, ( SATP_MODE_SV32 << SATP_MODE_SHIFT ) |
| srli t2, a0, PAGE_SHIFT |
| or t1, t1, t2 |
| .balign enable_paging_32_xalign |
| /* Start of transition code */ |
| enable_paging_32_xstart: |
| csrw satp, t1 |
| sfence.vma |
| csrr a1, satp |
| beqz a1, 2f |
| jr t0 |
| 1: /* Restore temporarily modified PTE */ |
| STOREN a3, (a2) |
| sfence.vma |
| /* End of transition code */ |
| .equ enable_paging_32_xlen, . - enable_paging_32_xstart |
| 2: srli a1, a1, SATP_MODE_SHIFT |
| |
| /* Zero SATP and virtual address offset if paging is not enabled */ |
| bnez a1, 1f |
| csrw satp, zero |
| mv tp, zero |
| 1: |
| /* Adjust return address to a virtual address */ |
| sub ra, ra, tp |
| |
| /* Return, with or without paging enabled */ |
| paging_mode_name a1 |
| mv a0, zero |
| ret |
| .size enable_paging_32, . - enable_paging_32 |
| |
| /* Ensure that transition code did not cross an alignment boundary */ |
| .section ".bss.enable_paging_32_xcheck", "aw", @nobits |
| .org . + enable_paging_32_xalign - enable_paging_32_xlen |
| |
| /* Convert 32-bit physical address to virtual address */ |
| .macro phys_to_virt_32 rd, rs:vararg |
| .ifnb \rs |
| sub \rd, \rs, tp |
| .else |
| sub \rd, \rd, tp |
| .endif |
| .endm |
| |
| /***************************************************************************** |
| * |
| * Disable 32-bit paging |
| * |
| ***************************************************************************** |
| * |
| * This function may be called with either virtual or flat physical |
| * addressing. It does not require a valid stack pointer. |
| * |
| * Parameters: |
| * |
| * tp - Virtual address offset |
| * |
| * Returns: |
| * |
| * tp - Virtual address offset (zeroed) |
| * pc - Updated to a physical address |
| * |
| */ |
| |
| .equ disable_paging_32_xalign, 16 |
| |
| .section ".prefix.disable_paging_32", "ax", @progbits |
| disable_paging_32: |
| /* Register usage: |
| * |
| * tp - virtual address offset |
| * a0 - page table address |
| * a1 - transition PTE pointer |
| * a2 - transition PTE content |
| */ |
| |
| /* Get page table address, and exit if paging is already disabled */ |
| csrr a0, satp |
| beqz a0, 99f |
| slli a0, a0, PAGE_SHIFT |
| sub a0, a0, tp |
| |
| /* Prepare for modifying transition PTE */ |
| la t0, disable_paging_32_xstart |
| add t0, t0, tp |
| srli t0, t0, VPN1_LSB |
| slli a1, t0, PTE_SIZE_LOG2 |
| add a1, a1, a0 |
| slli a2, t0, PTE_PPN1_LSB |
| ori a2, a2, PTE_LEAF |
| |
| /* Jump to physical address in transition PTE, and disable paging */ |
| la t0, 1f |
| add t0, t0, tp |
| .balign disable_paging_32_xalign |
| /* Start of transition code */ |
| disable_paging_32_xstart: |
| STOREN a2, (a1) |
| sfence.vma |
| jr t0 |
| 1: csrw satp, zero |
| sfence.vma |
| /* End of transition code */ |
| .equ disable_paging_32_xlen, . - disable_paging_32_xstart |
| |
| /* Update return address to a physical address */ |
| add ra, ra, tp |
| |
| 99: /* Return with paging disabled and virtual offset zeroed */ |
| mv tp, zero |
| ret |
| .size disable_paging_32, . - disable_paging_32 |
| |
| /* Ensure that transition code did not cross an alignment boundary */ |
| .section ".bss.disable_paging_32_xcheck", "aw", @nobits |
| .org . + disable_paging_32_xalign - disable_paging_32_xlen |
| |
| /***************************************************************************** |
| * |
| * Poison .bss section |
| * |
| ***************************************************************************** |
| * |
| * Fill the .bss section with an invalid non-zero value to expose bugs |
| * in early initialisation code that erroneously relies upon variables |
| * in .bss before the section has been zeroed. |
| * |
| * We use the value 0xeb55eb55eb55eb55 ("EBSS") since this is |
| * immediately recognisable as a value in a crash dump, and will |
| * trigger a page fault if dereferenced since the address is in a |
| * non-canonical form. |
| * |
| * Poisoning the .bss will overwrite the relocation records, and so |
| * can be done only as a debugging step on a system where relocation |
| * is known to be unnecessary (e.g. because paging is supported). |
| * |
| * This function does not require a valid stack pointer, but will |
| * destroy any existing stack contents if the stack happens to be |
| * placed within the original .bss section. |
| * |
| * Parameters: none |
| * |
| * Returns: none |
| * |
| */ |
| |
| .equ poison_bss_value_32, 0xeb55eb55 |
| .equ poison_bss_value_64, 0xeb55eb55eb55eb55 |
| .equ poison_bss_value, _C2 ( poison_bss_value_, __riscv_xlen ) |
| |
| .section ".prefix.poison_bss", "ax", @progbits |
| poison_bss: |
| /* Fill .bss section */ |
| la t0, _bss |
| la t1, _ebss |
| li t2, poison_bss_value |
| 1: STOREN t2, (t0) |
| addi t0, t0, ( __riscv_xlen / 8 ) |
| blt t0, t1, 1b |
| ret |
| .size poison_bss, . - poison_bss |
| |
| /***************************************************************************** |
| * |
| * Install iPXE to a suitable runtime address |
| * |
| ***************************************************************************** |
| * |
| * Identify a suitable runtime address for iPXE, relocate there, and |
| * set up for running normal C code. |
| * |
| * A valid temporary stack pointer is required. A 4kB space for a |
| * temporary page table may be provided, and must be provided if the |
| * iPXE image is running from read-only memory. |
| * |
| * Note that this function does not preserve the callee-save registers. |
| * |
| * Parameters: |
| * |
| * a0 - Boot hart ID |
| * a1 - Device tree physical address |
| * a2 - Optional temporary page table space (4kB, aligned to a 4kB boundary) |
| * sp - Valid temporary stack pointer |
| * |
| * Returns: |
| * |
| * pc - Updated to be within the relocated iPXE |
| * sp - Top of internal stack |
| * tp - Virtual address offset |
| * |
| */ |
| |
| .section ".prefix.install", "ax", @progbits |
| .globl install |
| install: |
| /* Register usage: |
| * |
| * s0 - boot hart ID |
| * s1 - device tree physical address |
| * s2 - saved return address |
| * s3 - relocation records physical address |
| * s4 - maximum accessible physical address |
| * s5 - relocation physical address |
| * s6 - relocation offset |
| * tp - virtual address offset |
| */ |
| mv tp, zero |
| progress "\r\nSBI->iPXE hart:" |
| print_hex_reg a0 |
| progress " temp:" |
| print_hex_reg a2 |
| progress " fdt:" |
| print_hex_reg a1 |
| progress "\r\nSBI->iPXE phys:" |
| print_hex_addr _prefix |
| progress " virt:" |
| print_hex_data prefix_virt |
| mv s0, a0 |
| mv s1, a1 |
| mv s2, ra |
| la s3, _edata |
| |
| /* Poison .bss if configured to do so */ |
| #if POISON_BSS |
| call poison_bss |
| #endif |
| |
| /* Attempt to enable paging, if we have temporary page table space */ |
| mv a0, a2 |
| beqz a2, 1f |
| call enable_paging |
| 1: addi s4, a0, -1 |
| |
| /* Apply relocations, if still needed after enabling paging */ |
| mv a0, s3 |
| call apply_relocs |
| |
| /* Find a suitable address for relocation (using temporary stack) */ |
| phys_to_virt a0, s1 |
| mv a1, s4 |
| phys_to_virt sp |
| call fdtmem_relocate |
| mv s5, a0 |
| progress "SBI->iPXE dest:" |
| print_hex_reg a0 |
| |
| /* Disable paging */ |
| call disable_paging |
| |
| /* Determine relocation offset */ |
| la s6, _prefix |
| sub s6, s5, s6 |
| |
| /* Copy iPXE image to new location and zero .bss */ |
| mv t0, s5 |
| la t1, _prefix |
| la t2, _edata |
| 1: LOADN t3, (t1) |
| STOREN t3, (t0) |
| addi t0, t0, ( __riscv_xlen / 8 ) |
| addi t1, t1, ( __riscv_xlen / 8 ) |
| blt t1, t2, 1b |
| la t1, _ebss |
| add t1, t1, s6 |
| 2: STOREN zero, (t0) |
| addi t0, t0, ( __riscv_xlen / 8 ) |
| blt t0, t1, 2b |
| |
| /* Jump to relocated copy */ |
| la t0, 1f |
| add t0, t0, s6 |
| jr t0 |
| 1: |
| /* Attempt to re-enable paging */ |
| la a0, page_table |
| call enable_paging |
| |
| /* Reapply relocations, if still needed after enabling paging */ |
| phys_to_virt a0, s3 |
| call apply_relocs |
| |
| /* Load stack pointer */ |
| la sp, _estack |
| |
| /* Store boot hart */ |
| STOREN s0, boot_hart, t0 |
| |
| /* Copy and register system device tree */ |
| phys_to_virt a0, s1 |
| mv a1, s4 |
| call fdtmem_register |
| |
| /* Return to a virtual address in the relocated copy */ |
| add ra, s2, s6 |
| sub ra, ra, tp |
| progress "\r\n" |
| ret |
| .size install, . - install |
| |
| /***************************************************************************** |
| * |
| * Reset (or lock up) system |
| * |
| ***************************************************************************** |
| * |
| * Reset via system via SBI, as a means of exiting from a prefix that |
| * has no other defined exit path. If the reset fails, lock up the |
| * system since there is nothing else that can sensibly be done. |
| * |
| * This function does not require a valid stack pointer. |
| * |
| * Parameters: none |
| * |
| * Returns: n/a (does not return) |
| * |
| */ |
| |
| /* SBI system reset extension */ |
| #define SBI_SRST ( ( 'S' << 24 ) | ( 'R' << 16 ) | ( 'S' << 8 ) | 'T' ) |
| #define SBI_SRST_SYSTEM_RESET 0x00 |
| #define SBI_RESET_COLD 0x00000001 |
| |
| /* SBI legacy shutdown */ |
| #define SBI_LEGACY_SHUTDOWN 0x08 |
| |
| .section ".prefix.reset_system", "ax", @progbits |
| .globl reset_system |
| reset_system: |
| /* Register usage: irrelevant (does not return) */ |
| progress "\r\niPXE->SBI reset\r\n" |
| |
| /* Attempt reset */ |
| li a7, SBI_SRST |
| li a6, SBI_SRST_SYSTEM_RESET |
| li a0, SBI_RESET_COLD |
| mv a1, zero |
| ecall |
| progress "(reset failed)\r\n" |
| |
| /* Attempt legacy shutdown */ |
| li a7, SBI_LEGACY_SHUTDOWN |
| ecall |
| progress "(legacy shutdown failed)\r\n" |
| |
| /* If reset failed, lock the system */ |
| 1: wfi |
| j 1b |
| .size reset_system, . - reset_system |
| |
| /***************************************************************************** |
| * |
| * File split information for the compressor |
| * |
| ***************************************************************************** |
| */ |
| |
| /* ELF machine type */ |
| #define EM_RISCV 243 |
| |
| .section ".zinfo", "a", @progbits |
| .org 0 |
| /* Copy initialised-data portion of image */ |
| .ascii "COPY" |
| .word 0 |
| .word _filesz |
| .word 1 |
| /* Notify compressor of link-time base address */ |
| .ascii "BASE" |
| .word 0 |
| .dword _base |
| /* Construct compressed relocation records */ |
| .ascii "ZREL" |
| .word _reloc_offset |
| .word _reloc_filesz |
| .word EM_RISCV |