| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -77,15 +77,6 @@ SECTIONS | |
| . += CONFIG_LP_STACK_SIZE; | ||
| . = ALIGN(16); | ||
| _stack = .; | ||
| } | ||
|
|
||
| _end = .; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,8 +25,3 @@ void arch_prog_run(struct prog *prog) | |
| doit = prog_entry(prog); | ||
| doit(prog_entry_arg(prog)); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,8 +14,6 @@ | |
| #ifndef ARCH_HLT_H | ||
| #define ARCH_HLT_H | ||
|
|
||
| static __always_inline void hlt(void) | ||
| { | ||
| for (;;) ; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,8 +14,6 @@ | |
| #ifndef ARCH_HLT_H | ||
| #define ARCH_HLT_H | ||
|
|
||
| static __always_inline void hlt(void) | ||
| { | ||
| for (;;) ; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,8 +23,3 @@ void arch_prog_run(struct prog *prog) | |
|
|
||
| doit(cb_tables); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,7 +30,7 @@ struct cpu_driver { | |
| struct thread; | ||
|
|
||
| struct cpu_info { | ||
| struct device *cpu; | ||
| unsigned long index; | ||
| }; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,8 +21,3 @@ void arch_prog_run(struct prog *prog) | |
|
|
||
| doit(prog_entry_arg(prog)); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,8 +11,6 @@ | |
| * GNU General Public License for more details. | ||
| */ | ||
|
|
||
| static __always_inline void hlt(void) | ||
| { | ||
| while (1) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| /* | ||
| * This file is part of the coreboot project. | ||
| * | ||
| * Copyright (C) 2018 HardenedLinux | ||
| * | ||
| * 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; version 2 of the License. | ||
| * | ||
| * 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. | ||
| */ | ||
|
|
||
| #ifndef __RISCV_PMP_H__ | ||
| #define __RISCV_PMP_H__ | ||
|
|
||
| #include <stdint.h> | ||
|
|
||
| /* | ||
| * this function needs to be implemented by a specific SoC. | ||
| * return number of PMP entries for current hart | ||
| */ | ||
| extern int pmp_entries_num(void); | ||
|
|
||
| /* reset PMP setting */ | ||
| void reset_pmp(void); | ||
|
|
||
| /* set up PMP record */ | ||
| void setup_pmp(uintptr_t base, uintptr_t size, uintptr_t flags); | ||
|
|
||
| #endif /* __RISCV_PMP_H__ */ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| /* | ||
| * This file is part of the coreboot project. | ||
| * | ||
| * Copyright (C) 2018 HardenedLinux. | ||
| * | ||
| * 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; version 2 of the License. | ||
| * | ||
| * 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. | ||
| */ | ||
|
|
||
| #ifndef _RISCV_SMP_H | ||
| #define _RISCV_SMP_H | ||
|
|
||
| /* | ||
| * This function is used to pause smp. Only the hart with hartid equal | ||
| * to working_hartid can be returned from smp_pause, other harts will | ||
| * enter halt and wait for wakeup | ||
| */ | ||
| void smp_pause(int working_hartid); | ||
|
|
||
| /* | ||
| * This function is used to wake up the harts that are halted by the | ||
| * smp_pause function. And this function will not return, all hart will | ||
| * jump to fn execution, and arg is the argument of the function fn. | ||
| */ | ||
| void smp_resume(void (*fn)(void *), void *arg); | ||
|
|
||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| /* | ||
| * This file is part of the coreboot project. | ||
| * | ||
| * Copyright (C) 2018 HardenedLinux | ||
| * | ||
| * 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; version 2 of the License. | ||
| * | ||
| * 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. | ||
| */ | ||
|
|
||
| #ifndef RISCV_SBI_H | ||
| #define RISCV_SBI_H | ||
|
|
||
| #define SBI_SET_TIMER 0 | ||
| #define SBI_CONSOLE_PUTCHAR 1 | ||
| #define SBI_CONSOLE_GETCHAR 2 | ||
| #define SBI_CLEAR_IPI 3 | ||
| #define SBI_SEND_IPI 4 | ||
| #define SBI_REMOTE_FENCE_I 5 | ||
| #define SBI_REMOTE_SFENCE_VMA 6 | ||
| #define SBI_REMOTE_SFENCE_VMA_ASID 7 | ||
| #define SBI_SHUTDOWN 8 | ||
|
|
||
| #define IPI_SOFT 1 | ||
| #define IPI_FENCE_I 2 | ||
| #define IPI_SFENCE_VMA 4 | ||
| #define IPI_SFENCE_VMA_ASID 8 | ||
| #define IPI_SHUTDOWN 16 | ||
|
|
||
| void handle_sbi(trapframe *tf); | ||
|
|
||
| #endif /* RISCV_SBI_H */ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,321 @@ | ||
| /* | ||
| * This file is part of the coreboot project. | ||
| * | ||
| * Copyright (C) 2018 HardenedLinux | ||
| * | ||
| * 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; version 2 of the License. | ||
| * | ||
| * 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. | ||
| */ | ||
|
|
||
| #include <arch/encoding.h> | ||
| #include <stdint.h> | ||
| #include <arch/pmp.h> | ||
| #include <console/console.h> | ||
| #include <commonlib/helpers.h> | ||
|
|
||
| #define GRANULE (1 << PMP_SHIFT) | ||
|
|
||
| /* | ||
| * This structure is used to temporarily record PMP | ||
| * configuration information. | ||
| */ | ||
| typedef struct { | ||
| /* used to record the value of pmpcfg[i] */ | ||
| uintptr_t cfg; | ||
| /* | ||
| * When generating a TOR type configuration, | ||
| * the previous entry needs to record the starting address. | ||
| * used to record the value of pmpaddr[i - 1] | ||
| */ | ||
| uintptr_t previous_address; | ||
| /* used to record the value of pmpaddr[i] */ | ||
| uintptr_t address; | ||
| } pmpcfg_t; | ||
|
|
||
| /* This variable is used to record which entries have been used. */ | ||
| static uintptr_t pmp_entry_used_mask; | ||
|
|
||
| /* helper function used to read pmpcfg[idx] */ | ||
| static uintptr_t read_pmpcfg(int idx) | ||
| { | ||
| #if __riscv_xlen == 32 | ||
| int shift = 8 * (idx & 3); | ||
| switch (idx >> 2) { | ||
| case 0: | ||
| return (read_csr(pmpcfg0) >> shift) & 0xff; | ||
| case 1: | ||
| return (read_csr(pmpcfg1) >> shift) & 0xff; | ||
| case 2: | ||
| return (read_csr(pmpcfg2) >> shift) & 0xff; | ||
| case 3: | ||
| return (read_csr(pmpcfg3) >> shift) & 0xff; | ||
| } | ||
| #elif __riscv_xlen == 64 | ||
| int shift = 8 * (idx & 7); | ||
| switch (idx >> 3) { | ||
| case 0: | ||
| return (read_csr(pmpcfg0) >> shift) & 0xff; | ||
| case 1: | ||
| return (read_csr(pmpcfg2) >> shift) & 0xff; | ||
| } | ||
| #endif | ||
| return -1; | ||
| } | ||
|
|
||
| /* helper function used to write pmpcfg[idx] */ | ||
| static void write_pmpcfg(int idx, uintptr_t cfg) | ||
| { | ||
| uintptr_t old; | ||
| uintptr_t new; | ||
| #if __riscv_xlen == 32 | ||
| int shift = 8 * (idx & 3); | ||
| switch (idx >> 2) { | ||
| case 0: | ||
| old = read_csr(pmpcfg0); | ||
| new = (old & ~((uintptr_t)0xff << shift)) | ||
| | ((cfg & 0xff) << shift); | ||
| write_csr(pmpcfg0, new); | ||
| break; | ||
| case 1: | ||
| old = read_csr(pmpcfg1); | ||
| new = (old & ~((uintptr_t)0xff << shift)) | ||
| | ((cfg & 0xff) << shift); | ||
| write_csr(pmpcfg1, new); | ||
| break; | ||
| case 2: | ||
| old = read_csr(pmpcfg2); | ||
| new = (old & ~((uintptr_t)0xff << shift)) | ||
| | ((cfg & 0xff) << shift); | ||
| write_csr(pmpcfg2, new); | ||
| break; | ||
| case 3: | ||
| old = read_csr(pmpcfg3); | ||
| new = (old & ~((uintptr_t)0xff << shift)) | ||
| | ((cfg & 0xff) << shift); | ||
| write_csr(pmpcfg3, new); | ||
| break; | ||
| } | ||
| #elif __riscv_xlen == 64 | ||
| int shift = 8 * (idx & 7); | ||
| switch (idx >> 3) { | ||
| case 0: | ||
| old = read_csr(pmpcfg0); | ||
| new = (old & ~((uintptr_t)0xff << shift)) | ||
| | ((cfg & 0xff) << shift); | ||
| write_csr(pmpcfg0, new); | ||
| break; | ||
| case 1: | ||
| old = read_csr(pmpcfg2); | ||
| new = (old & ~((uintptr_t)0xff << shift)) | ||
| | ((cfg & 0xff) << shift); | ||
| write_csr(pmpcfg2, new); | ||
| break; | ||
| } | ||
| #endif | ||
| if (read_pmpcfg(idx) != cfg) | ||
| die("write pmpcfg failure!"); | ||
| } | ||
|
|
||
| /* helper function used to read pmpaddr[idx] */ | ||
| static uintptr_t read_pmpaddr(int idx) | ||
| { | ||
| switch (idx) { | ||
| case 0: | ||
| return read_csr(pmpaddr0); | ||
| case 1: | ||
| return read_csr(pmpaddr1); | ||
| case 2: | ||
| return read_csr(pmpaddr2); | ||
| case 3: | ||
| return read_csr(pmpaddr3); | ||
| case 4: | ||
| return read_csr(pmpaddr4); | ||
| case 5: | ||
| return read_csr(pmpaddr5); | ||
| case 6: | ||
| return read_csr(pmpaddr6); | ||
| case 7: | ||
| return read_csr(pmpaddr7); | ||
| case 8: | ||
| return read_csr(pmpaddr8); | ||
| case 9: | ||
| return read_csr(pmpaddr9); | ||
| case 10: | ||
| return read_csr(pmpaddr10); | ||
| case 11: | ||
| return read_csr(pmpaddr11); | ||
| case 12: | ||
| return read_csr(pmpaddr12); | ||
| case 13: | ||
| return read_csr(pmpaddr13); | ||
| case 14: | ||
| return read_csr(pmpaddr14); | ||
| case 15: | ||
| return read_csr(pmpaddr15); | ||
| } | ||
| return -1; | ||
| } | ||
|
|
||
| /* helper function used to write pmpaddr[idx] */ | ||
| static void write_pmpaddr(int idx, uintptr_t val) | ||
| { | ||
| switch (idx) { | ||
| case 0: | ||
| write_csr(pmpaddr0, val); | ||
| break; | ||
| case 1: | ||
| write_csr(pmpaddr1, val); | ||
| break; | ||
| case 2: | ||
| write_csr(pmpaddr2, val); | ||
| break; | ||
| case 3: | ||
| write_csr(pmpaddr3, val); | ||
| break; | ||
| case 4: | ||
| write_csr(pmpaddr4, val); | ||
| break; | ||
| case 5: | ||
| write_csr(pmpaddr5, val); | ||
| break; | ||
| case 6: | ||
| write_csr(pmpaddr6, val); | ||
| break; | ||
| case 7: | ||
| write_csr(pmpaddr7, val); | ||
| break; | ||
| case 8: | ||
| write_csr(pmpaddr8, val); | ||
| break; | ||
| case 9: | ||
| write_csr(pmpaddr9, val); | ||
| break; | ||
| case 10: | ||
| write_csr(pmpaddr10, val); | ||
| break; | ||
| case 11: | ||
| write_csr(pmpaddr11, val); | ||
| break; | ||
| case 12: | ||
| write_csr(pmpaddr12, val); | ||
| break; | ||
| case 13: | ||
| write_csr(pmpaddr13, val); | ||
| break; | ||
| case 14: | ||
| write_csr(pmpaddr14, val); | ||
| break; | ||
| case 15: | ||
| write_csr(pmpaddr15, val); | ||
| break; | ||
| } | ||
| if (read_pmpaddr(idx) != val) | ||
| die("write pmpaddr failure"); | ||
| } | ||
|
|
||
| /* Generate a PMP configuration of type NA4/NAPOT */ | ||
| static pmpcfg_t generate_pmp_napot( | ||
| uintptr_t base, uintptr_t size, uintptr_t flags) | ||
| { | ||
| pmpcfg_t p; | ||
| flags = flags & (PMP_R | PMP_W | PMP_X | PMP_L); | ||
| p.cfg = flags | (size > GRANULE ? PMP_NAPOT : PMP_NA4); | ||
| p.previous_address = 0; | ||
| p.address = (base + (size / 2 - 1)) >> PMP_SHIFT; | ||
| return p; | ||
| } | ||
|
|
||
| /* Generate a PMP configuration of type TOR */ | ||
| static pmpcfg_t generate_pmp_range( | ||
| uintptr_t base, uintptr_t size, uintptr_t flags) | ||
| { | ||
| pmpcfg_t p; | ||
| flags = flags & (PMP_R | PMP_W | PMP_X | PMP_L); | ||
| p.cfg = flags | PMP_TOR; | ||
| p.previous_address = base >> PMP_SHIFT; | ||
| p.address = (base + size) >> PMP_SHIFT; | ||
| return p; | ||
| } | ||
|
|
||
| /* Generate a PMP configuration */ | ||
| static pmpcfg_t generate_pmp(uintptr_t base, uintptr_t size, uintptr_t flags) | ||
| { | ||
| if (IS_POWER_OF_2(size) && (size >= 4) && ((base & (size - 1)) == 0)) | ||
| return generate_pmp_napot(base, size, flags); | ||
| else | ||
| return generate_pmp_range(base, size, flags); | ||
| } | ||
|
|
||
| /* | ||
| * find empty PMP entry by type | ||
| * TOR type configuration requires two consecutive PMP entries, | ||
| * others requires one. | ||
| */ | ||
| static int find_empty_pmp_entry(int is_range) | ||
| { | ||
| int free_entries = 0; | ||
| for (int i = 0; i < pmp_entries_num(); i++) { | ||
| if (pmp_entry_used_mask & (1 << i)) | ||
| free_entries = 0; | ||
| else | ||
| free_entries++; | ||
| if (is_range && (free_entries == 2)) | ||
| return i; | ||
| if (!is_range && (free_entries == 1)) | ||
| return i; | ||
| } | ||
| die("Too many PMP configurations, no free entries can be used!"); | ||
| return -1; | ||
| } | ||
|
|
||
| /* | ||
| * mark PMP entry has be used | ||
| * this function need be used with find_entry_pmp_entry | ||
| * | ||
| * n = find_empty_pmp_entry(is_range) | ||
| * ... // PMP set operate | ||
| * mask_pmp_entry_used(n); | ||
| */ | ||
| static void mask_pmp_entry_used(int idx) | ||
| { | ||
| pmp_entry_used_mask |= 1 << idx; | ||
| } | ||
|
|
||
| /* reset PMP setting */ | ||
| void reset_pmp(void) | ||
| { | ||
| for (int i = 0; i < pmp_entries_num(); i++) { | ||
| if (read_pmpcfg(i) & PMP_L) | ||
| die("Some PMP configurations are locked " | ||
| "and cannot be reset!"); | ||
| write_pmpcfg(i, 0); | ||
| write_pmpaddr(i, 0); | ||
| } | ||
| } | ||
|
|
||
| /* set up PMP record */ | ||
| void setup_pmp(uintptr_t base, uintptr_t size, uintptr_t flags) | ||
| { | ||
| pmpcfg_t p; | ||
| int is_range, n; | ||
|
|
||
| p = generate_pmp(base, size, flags); | ||
| is_range = ((p.cfg & PMP_A) == PMP_TOR); | ||
|
|
||
| n = find_empty_pmp_entry(is_range); | ||
|
|
||
| write_pmpaddr(n, p.address); | ||
| if (is_range) | ||
| write_pmpaddr(n - 1, p.previous_address); | ||
| write_pmpcfg(n, p.cfg); | ||
|
|
||
| mask_pmp_entry_used(n); | ||
| if (is_range) | ||
| mask_pmp_entry_used(n - 1); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,121 @@ | ||
| /* | ||
| * This file is part of the coreboot project. | ||
| * | ||
| * Copyright (C) 2018 HardenedLinux | ||
| * | ||
| * 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; version 2 of the License. | ||
| * | ||
| * 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. | ||
| */ | ||
|
|
||
| #include <mcall.h> | ||
| #include <stdint.h> | ||
| #include <commonlib/compiler.h> | ||
| #include <arch/exception.h> | ||
| #include <sbi.h> | ||
| #include <vm.h> | ||
| #include <console/uart.h> | ||
| #include <console/console.h> | ||
| #include <commonlib/helpers.h> | ||
|
|
||
| static uintptr_t send_ipi(uintptr_t *pmask, intptr_t type) | ||
| { | ||
| uintptr_t mask = mprv_read_uintptr_t(pmask); | ||
| for (int i = 0; mask; i++) { | ||
| if (mask & 1) { | ||
| OTHER_HLS(i)->ipi_pending |= type; | ||
| /* send soft interrupt to target hart */ | ||
| set_msip(i, 1); | ||
| } | ||
| mask = mask >> 1; | ||
| } | ||
| return 0; | ||
| } | ||
|
|
||
| static uintptr_t sbi_set_timer(uint64_t when) | ||
| { | ||
| clear_csr(mip, MIP_STIP); | ||
| set_csr(mie, MIP_MTIP); | ||
| *(HLS()->timecmp) = when; | ||
| return 0; | ||
| } | ||
|
|
||
| #if IS_ENABLED(CONFIG_CONSOLE_SERIAL) | ||
| static uintptr_t sbi_console_putchar(uint8_t ch) | ||
| { | ||
| uart_tx_byte(CONFIG_UART_FOR_CONSOLE, ch); | ||
| return 0; | ||
| } | ||
|
|
||
| static uintptr_t sbi_console_getchar(void) | ||
| { | ||
| return uart_rx_byte(CONFIG_UART_FOR_CONSOLE); | ||
| } | ||
| #endif | ||
|
|
||
| static uintptr_t sbi_clear_ipi(void) | ||
| { | ||
| clear_csr(mip, MIP_SSIP); | ||
| return 0; | ||
| } | ||
|
|
||
|
|
||
| /* | ||
| * sbi is triggered by the s-mode ecall | ||
| * parameter : register a0 a1 a2 | ||
| * function : register a7 | ||
| * return : register a0 | ||
| */ | ||
| void handle_sbi(trapframe *tf) | ||
| { | ||
| uintptr_t ret = 0; | ||
| uintptr_t arg0 = tf->gpr[10]; | ||
| __unused uintptr_t arg1 = tf->gpr[11]; | ||
| uintptr_t which = tf->gpr[17]; | ||
|
|
||
| switch (which) { | ||
| case SBI_SET_TIMER: | ||
| #if __riscv_xlen == 32 | ||
| ret = sbi_set_timer(arg0 + ((uint64_t)arg1 << 32)); | ||
| #else | ||
| ret = sbi_set_timer(arg0); | ||
| #endif | ||
| break; | ||
| #if IS_ENABLED(CONFIG_CONSOLE_SERIAL) | ||
| case SBI_CONSOLE_PUTCHAR: | ||
| ret = sbi_console_putchar(arg0); | ||
| break; | ||
| case SBI_CONSOLE_GETCHAR: | ||
| ret = sbi_console_getchar(); | ||
| break; | ||
| #endif | ||
| case SBI_CLEAR_IPI: | ||
| ret = sbi_clear_ipi(); | ||
| break; | ||
| case SBI_SEND_IPI: | ||
| ret = send_ipi((uintptr_t *)arg0, IPI_SOFT); | ||
| break; | ||
| case SBI_REMOTE_FENCE_I: | ||
| ret = send_ipi((uintptr_t *)arg0, IPI_FENCE_I); | ||
| break; | ||
| case SBI_REMOTE_SFENCE_VMA: | ||
| ret = send_ipi((uintptr_t *)arg0, IPI_SFENCE_VMA); | ||
| break; | ||
| case SBI_REMOTE_SFENCE_VMA_ASID: | ||
| ret = send_ipi((uintptr_t *)arg0, IPI_SFENCE_VMA_ASID); | ||
| break; | ||
| case SBI_SHUTDOWN: | ||
| ret = send_ipi((uintptr_t *)arg0, IPI_SHUTDOWN); | ||
| break; | ||
| default: | ||
| ret = -38; | ||
| break; | ||
| } | ||
| tf->gpr[10] = ret; | ||
| write_csr(mepc, read_csr(mepc) + 4); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| /* | ||
| * This file is part of the coreboot project. | ||
| * | ||
| * Copyright (C) 2018 HardenedLinux. | ||
| * | ||
| * 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; version 2 of the License. | ||
| * | ||
| * 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. | ||
| */ | ||
|
|
||
| #include <stddef.h> | ||
| #include <arch/encoding.h> | ||
| #include <arch/smp/smp.h> | ||
| #include <arch/smp/spinlock.h> | ||
| #include <mcall.h> | ||
| #include <commonlib/compiler.h> | ||
| #include <console/console.h> | ||
|
|
||
| void smp_pause(int working_hartid) | ||
| { | ||
| #define SYNCA (OTHER_HLS(working_hartid)->entry.sync_a) | ||
| #define SYNCB (OTHER_HLS(working_hartid)->entry.sync_b) | ||
|
|
||
| int hartid = read_csr(mhartid); | ||
|
|
||
| if (hartid != working_hartid) { | ||
| /* waiting for work hart */ | ||
| do { | ||
| barrier(); | ||
| } while (SYNCA != 0x01234567); | ||
|
|
||
| clear_csr(mstatus, MSTATUS_MIE); | ||
| write_csr(mie, MIP_MSIP); | ||
|
|
||
| /* count how many cores enter the halt */ | ||
| __sync_fetch_and_add(&SYNCB, 1); | ||
|
|
||
| do { | ||
| barrier(); | ||
| __asm__ volatile ("wfi"); | ||
| } while ((read_csr(mip) & MIP_MSIP) == 0); | ||
| set_msip(hartid, 0); | ||
| HLS()->entry.fn(HLS()->entry.arg); | ||
| } else { | ||
| /* Initialize the counter and | ||
| * mark the work hart into smp_pause */ | ||
| SYNCB = 0; | ||
| SYNCA = 0x01234567; | ||
|
|
||
| /* waiting for other Hart to enter the halt */ | ||
| do { | ||
| barrier(); | ||
| } while (SYNCB + 1 < CONFIG_RISCV_HART_NUM); | ||
|
|
||
| /* initialize for the next call */ | ||
| SYNCA = 0; | ||
| SYNCB = 0; | ||
| } | ||
| #undef SYNCA | ||
| #undef SYNCB | ||
| } | ||
|
|
||
| void smp_resume(void (*fn)(void *), void *arg) | ||
| { | ||
| int hartid = read_csr(mhartid); | ||
|
|
||
| if (fn == NULL) | ||
| die("must pass a non-null function pointer\n"); | ||
|
|
||
| for (int i = 0; i < CONFIG_RISCV_HART_NUM; i++) { | ||
| OTHER_HLS(i)->entry.fn = fn; | ||
| OTHER_HLS(i)->entry.arg = arg; | ||
| } | ||
|
|
||
| for (int i = 0; i < CONFIG_RISCV_HART_NUM; i++) | ||
| if (i != hartid) | ||
| set_msip(i, 1); | ||
|
|
||
| HLS()->entry.fn(HLS()->entry.arg); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| /* | ||
| * This file is part of the coreboot project. | ||
| * | ||
| * Copyright 2017 Google, Inc. | ||
| * | ||
| * 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; version 2 of the License. | ||
| * | ||
| * 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. | ||
| */ | ||
|
|
||
| #include <arch/io.h> | ||
| #include <arch/cache.h> | ||
| #include <cf9_reset.h> | ||
| #include <console/console.h> | ||
| #include <halt.h> | ||
| #include <reset.h> | ||
|
|
||
| /* | ||
| * A system reset in terms of the CF9 register asserts the INIT# | ||
| * signal to reset the CPU along the PLTRST# signal to reset other | ||
| * board components. It is usually the hardest reset type that | ||
| * does not power cycle the board. Thus, it could be called a | ||
| * "warm reset". | ||
| */ | ||
| void do_system_reset(void) | ||
| { | ||
| dcache_clean_all(); | ||
| outb(SYS_RST, RST_CNT); | ||
| outb(RST_CPU | SYS_RST, RST_CNT); | ||
| } | ||
|
|
||
| /* | ||
| * A full reset in terms of the CF9 register triggers a power cycle | ||
| * (i.e. S0 -> S5 -> S0 transition). Thus, it could be called a | ||
| * "cold reset". | ||
| * Note: Not all x86 implementations comply with this defitinion, | ||
| * some may require additional configuration to power cycle. | ||
| */ | ||
| void do_full_reset(void) | ||
| { | ||
| dcache_clean_all(); | ||
| outb(FULL_RST | SYS_RST, RST_CNT); | ||
| outb(FULL_RST | RST_CPU | SYS_RST, RST_CNT); | ||
| } | ||
|
|
||
| void system_reset(void) | ||
| { | ||
| printk(BIOS_INFO, "%s() called!\n", __func__); | ||
| cf9_reset_prepare(); | ||
| do_system_reset(); | ||
| halt(); | ||
| } | ||
|
|
||
| void full_reset(void) | ||
| { | ||
| printk(BIOS_INFO, "%s() called!\n", __func__); | ||
| cf9_reset_prepare(); | ||
| do_full_reset(); | ||
| halt(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,8 +20,6 @@ static void hlt(void) | |
| __builtin_hlt(); | ||
| } | ||
| #else | ||
| static __always_inline void hlt(void) | ||
| { | ||
| asm("hlt"); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,7 +14,6 @@ | |
| #ifndef _ASM_IO_H | ||
| #define _ASM_IO_H | ||
|
|
||
| #include <endian.h> | ||
| #include <stdint.h> | ||
| #include <rules.h> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,7 +14,6 @@ | |
| #ifndef _PCI_IO_CFG_H | ||
| #define _PCI_IO_CFG_H | ||
|
|
||
| #include <arch/io.h> | ||
|
|
||
| static __always_inline | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,7 +17,6 @@ | |
| #define _PCI_MMIO_CFG_H | ||
|
|
||
| #include <arch/io.h> | ||
|
|
||
| #define DEFAULT_PCIEXBAR CONFIG_MMCONF_BASE_ADDRESS | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| /* | ||
| * This file is part of the coreboot project. | ||
| * | ||
| * Copyright (C) 2017 Intel Corp. | ||
| * | ||
| * 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; version 2 of the License. | ||
| * | ||
| * 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. | ||
| */ | ||
|
|
||
| #ifndef X86_CF9_RESET_H | ||
| #define X86_CF9_RESET_H | ||
|
|
||
| /* Reset control port */ | ||
| #define RST_CNT 0xcf9 | ||
| #define FULL_RST (1 << 3) | ||
| #define RST_CPU (1 << 2) | ||
| #define SYS_RST (1 << 1) | ||
|
|
||
| /* Implement the bare reset, i.e. write to cf9. */ | ||
| void do_system_reset(void); | ||
| void do_full_reset(void); | ||
|
|
||
| /* Called by functions below before reset. */ | ||
| #if IS_ENABLED(CONFIG_HAVE_CF9_RESET_PREPARE) | ||
| void cf9_reset_prepare(void); | ||
| #else | ||
| static inline void cf9_reset_prepare(void) {} | ||
| #endif | ||
|
|
||
| /* Prepare for reset, run do_*_reset(), halt. */ | ||
| __noreturn void system_reset(void); | ||
| __noreturn void full_reset(void); | ||
|
|
||
| #endif /* X86_CF9_RESET_H */ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,7 +13,6 @@ | |
| * GNU General Public License for more details. | ||
| */ | ||
|
|
||
| #include <cpu/x86/tsc.h> | ||
| #include <timestamp.h> | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,7 +17,6 @@ | |
|
|
||
| #include <stdint.h> | ||
| #include <stddef.h> | ||
|
|
||
| #define RMODULE_MAGIC 0xf8fe | ||
| #define RMODULE_VERSION_1 1 | ||
|
|
||