diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/sbi/Kconfig | 9 | ||||
| -rw-r--r-- | lib/sbi/sbi_dbtr.c | 166 | ||||
| -rw-r--r-- | lib/sbi/sbi_domain_context.c | 3 | ||||
| -rw-r--r-- | lib/sbi/sbi_ecall.c | 2 | ||||
| -rw-r--r-- | lib/sbi/sbi_hart_pmp.c | 13 | ||||
| -rw-r--r-- | lib/sbi/sbi_hart_protection.c | 153 | ||||
| -rw-r--r-- | lib/sbi/sbi_init.c | 18 | ||||
| -rw-r--r-- | lib/sbi/sbi_mpxy.c | 53 | ||||
| -rw-r--r-- | lib/sbi/sbi_sse.c | 13 | ||||
| -rw-r--r-- | lib/sbi/sbi_timer.c | 12 | ||||
| -rw-r--r-- | lib/sbi/sbi_tlb.c | 46 | ||||
| -rw-r--r-- | lib/sbi/tests/sbi_ecall_test.c | 59 | ||||
| -rw-r--r-- | lib/utils/cache/fdt_andes_llcache.c | 23 | ||||
| -rw-r--r-- | lib/utils/cppc/fdt_cppc_rpmi.c | 44 | ||||
| -rw-r--r-- | lib/utils/hsm/fdt_hsm_andes_atcsmu.c | 64 | ||||
| -rw-r--r-- | lib/utils/mpxy/fdt_mpxy_rpmi_logging.c | 2 | ||||
| -rw-r--r-- | lib/utils/mpxy/fdt_mpxy_rpmi_mbox.c | 2 | ||||
| -rw-r--r-- | lib/utils/mpxy/fdt_mpxy_rpmi_mm.c | 9 | ||||
| -rw-r--r-- | lib/utils/reset/Kconfig | 4 | ||||
| -rw-r--r-- | lib/utils/reset/fdt_reset_metanoia.c | 72 | ||||
| -rw-r--r-- | lib/utils/reset/objects.mk | 3 | ||||
| -rw-r--r-- | lib/utils/suspend/fdt_suspend_andes_atcsmu.c | 70 |
22 files changed, 695 insertions, 145 deletions
diff --git a/lib/sbi/Kconfig b/lib/sbi/Kconfig index e76aecca..24132eaa 100644 --- a/lib/sbi/Kconfig +++ b/lib/sbi/Kconfig @@ -2,6 +2,15 @@ menu "Generic SBI Support" +config HARTMASK_MAX_BITS + int "Maximum number of hart mask bits (32-512)" + range 32 512 + default 256 + help + The hartmask is indexed using physical hart IDs so this setting + also represents the maximum number of hart IDs generic OpenSBI + can handle. + config DEFAULT_HART_STACK_SIZE int "Default per-HART stack size (bytes)" range 8192 1048576 diff --git a/lib/sbi/sbi_dbtr.c b/lib/sbi/sbi_dbtr.c index 01047969..4fd7fb5e 100644 --- a/lib/sbi/sbi_dbtr.c +++ b/lib/sbi/sbi_dbtr.c @@ -24,6 +24,22 @@ /** Offset of pointer to HART's debug triggers info in scratch space */ static unsigned long hart_state_ptr_offset; +/** Device specific debug trigger operations */ +static const struct sbi_dbtr_device *dbtr_dev = NULL; + +const struct sbi_dbtr_device *sbi_dbtr_get_device(void) +{ + return dbtr_dev; +} + +void sbi_dbtr_set_device(const struct sbi_dbtr_device *dev) +{ + if (!dev || dbtr_dev) + return; + + dbtr_dev = dev; +} + #define dbtr_get_hart_state_ptr(__scratch) \ sbi_scratch_read_type((__scratch), void *, hart_state_ptr_offset) @@ -105,10 +121,75 @@ static void sbi_trigger_init(struct sbi_dbtr_trigger *trig, trig->index = idx; } -static inline struct sbi_dbtr_trigger *sbi_alloc_trigger(void) +static bool dbtr_trigger_hw_supported(unsigned long idx, unsigned long tdata1, + unsigned long tdata2, + unsigned long tdata3) +{ + if (dbtr_dev && dbtr_dev->trigger_supported) + return dbtr_dev->trigger_supported(idx, tdata1, tdata2, + tdata3); + + return true; +} + +static bool dbtr_trigger_any_hw_supported( + struct sbi_dbtr_hart_triggers_state *hs, + unsigned long tdata1, unsigned long tdata2, + unsigned long tdata3) +{ + unsigned long type = TDATA1_GET_TYPE(tdata1); + struct sbi_dbtr_trigger *trig; + int i; + + for (i = 0; i < hs->total_trigs; i++) { + trig = INDEX_TO_TRIGGER(i); + if (__test_bit(type, &trig->type_mask) && + dbtr_trigger_hw_supported(trig->index, tdata1, tdata2, + tdata3)) + return true; + } + + return false; +} + +/* + * Find the first free hardware trigger slot supporting the configuration. + * Slots set in claimed_mask are treated as taken, which allows the caller + * to track slot availability. A 32-bit mask covers RV_MAX_TRIGGERS (32); + * for a larger number of triggers, this function needs to be updated. + */ +static int dbtr_find_free_slot(struct sbi_dbtr_hart_triggers_state *hs, + u32 claimed_mask, + unsigned long tdata1, unsigned long tdata2, + unsigned long tdata3) +{ + unsigned long type = TDATA1_GET_TYPE(tdata1); + struct sbi_dbtr_trigger *trig; + int i; + + for (i = 0; i < hs->total_trigs; i++) { + trig = INDEX_TO_TRIGGER(i); + if (trig->state & RV_DBTR_BIT_MASK(TS, MAPPED)) + continue; + if (claimed_mask & BIT(i)) + continue; + if (!__test_bit(type, &trig->type_mask)) + continue; + if (!dbtr_trigger_hw_supported(trig->index, tdata1, + tdata2, tdata3)) + continue; + return i; + } + + return SBI_ENOENT; +} + +static inline struct sbi_dbtr_trigger *sbi_alloc_trigger(unsigned long tdata1, + unsigned long tdata2, + unsigned long tdata3) { int i; - struct sbi_dbtr_trigger *f_trig = NULL; + struct sbi_dbtr_trigger *f_trig; struct sbi_dbtr_hart_triggers_state *hart_state; hart_state = dbtr_thishart_state_ptr(); @@ -118,17 +199,12 @@ static inline struct sbi_dbtr_trigger *sbi_alloc_trigger(void) if (hart_state->available_trigs <= 0) return NULL; - for (i = 0; i < hart_state->total_trigs; i++) { - f_trig = INDEX_TO_TRIGGER(i); - if (f_trig->state & RV_DBTR_BIT_MASK(TS, MAPPED)) - continue; - hart_state->available_trigs--; - break; - } - - if (i == hart_state->total_trigs) + i = dbtr_find_free_slot(hart_state, 0, tdata1, tdata2, tdata3); + if (i < 0) return NULL; + f_trig = INDEX_TO_TRIGGER(i); + hart_state->available_trigs--; __set_bit(RV_DBTR_BIT(TS, MAPPED), &f_trig->state); return f_trig; @@ -547,7 +623,8 @@ int sbi_dbtr_num_trig(unsigned long data, unsigned long *out) for (i = 0; i < hs->total_trigs; i++) { trig = INDEX_TO_TRIGGER(i); - if (__test_bit(type, &trig->type_mask)) + if (__test_bit(type, &trig->type_mask) && + dbtr_trigger_hw_supported(trig->index, data, 0, 0)) total++; } @@ -573,7 +650,7 @@ int sbi_dbtr_read_trig(unsigned long smode, if (trig_idx_base >= hs->total_trigs || trig_idx_base + trig_count >= hs->total_trigs) - return SBI_ERR_INVALID_PARAM; + return SBI_ERR_BAD_RANGE; if (sbi_dbtr_shmem_disabled(hs)) return SBI_ERR_NO_SHMEM; @@ -608,6 +685,8 @@ int sbi_dbtr_install_trig(unsigned long smode, struct sbi_dbtr_data_msg *recv; struct sbi_dbtr_id_msg *xmit; unsigned long ctrl; + u32 claimed = 0; + int slot; struct sbi_dbtr_trigger *trig; struct sbi_dbtr_hart_triggers_state *hs = NULL; bool tdata2_impl, tdata3_impl; @@ -626,8 +705,10 @@ int sbi_dbtr_install_trig(unsigned long smode, /* * SBI v3.0 sec 19.4 requires SBI_ERR_NOT_SUPPORTED when a trigger * programs a non-zero value into an unimplemented optional CSR. Only - * the "whole CSR unimplemented" case is caught; WARL bits tied off - * inside an otherwise-implemented CSR are not. + * the "whole CSR unimplemented" case is caught here; WARL bits tied + * off inside an otherwise-implemented CSR are delegated to the + * device-specific trigger_supported() callback via + * dbtr_trigger_any_hw_supported(). */ tdata2_impl = tdata_implemented(CSR_TDATA2); tdata3_impl = tdata_implemented(CSR_TDATA3); @@ -648,7 +729,7 @@ int sbi_dbtr_install_trig(unsigned long smode, *out = _idx; sbi_hart_protection_unmap_range((unsigned long)shmem_base, trig_count * sizeof(*entry)); - return SBI_ERR_FAILED; + return SBI_ERR_INVALID_PARAM; } if ((recv->tdata2 && !tdata2_impl) || @@ -658,6 +739,16 @@ int sbi_dbtr_install_trig(unsigned long smode, trig_count * sizeof(*entry)); return SBI_ERR_NOT_SUPPORTED; } + + if (!dbtr_trigger_any_hw_supported(hs, + lle_to_cpu(recv->tdata1), + lle_to_cpu(recv->tdata2), + lle_to_cpu(recv->tdata3))) { + *out = _idx; + sbi_hart_protection_unmap_range((unsigned long)shmem_base, + trig_count * sizeof(*entry)); + return SBI_ERR_NOT_SUPPORTED; + } } if (hs->available_trigs < trig_count) { @@ -667,17 +758,40 @@ int sbi_dbtr_install_trig(unsigned long smode, return SBI_ERR_FAILED; } - /* Install triggers */ + /* + * Dry-run the allocation of the whole batch so that no trigger + * is installed if any of the requested configurations cannot be + * matched to a free hardware trigger slot. + */ for_each_trig_entry(shmem_base, trig_count, typeof(*entry), entry) { - /* - * Since we have already checked if enough triggers are - * available, trigger allocation must succeed. - */ - trig = sbi_alloc_trigger(); + recv = (struct sbi_dbtr_data_msg *)(&entry->data); + slot = dbtr_find_free_slot(hs, claimed, + lle_to_cpu(recv->tdata1), + lle_to_cpu(recv->tdata2), + lle_to_cpu(recv->tdata3)); + if (slot < 0) { + *out = _idx; + sbi_hart_protection_unmap_range((unsigned long)shmem_base, + trig_count * sizeof(*entry)); + return SBI_ERR_FAILED; + } + claimed |= BIT(slot); + } + /* Install triggers */ + for_each_trig_entry(shmem_base, trig_count, typeof(*entry), entry) { recv = (struct sbi_dbtr_data_msg *)(&entry->data); xmit = (struct sbi_dbtr_id_msg *)(&entry->id); + /* + * The dry-run above matched every requested configuration + * to a free hardware trigger slot, so allocation must + * succeed. + */ + trig = sbi_alloc_trigger(lle_to_cpu(recv->tdata1), + lle_to_cpu(recv->tdata2), + lle_to_cpu(recv->tdata3)); + dbtr_trigger_setup(trig, recv); dbtr_trigger_enable(trig); xmit->idx = cpu_to_lle(trig->index); @@ -789,6 +903,14 @@ int sbi_dbtr_update_trig(unsigned long smode, return SBI_ERR_NOT_SUPPORTED; } + if (!dbtr_trigger_hw_supported(trig->index, + lle_to_cpu(entry->data.tdata1), + lle_to_cpu(entry->data.tdata2), + lle_to_cpu(entry->data.tdata3))) { + sbi_hart_protection_unmap_range((unsigned long)entry, sizeof(*entry)); + return SBI_ERR_NOT_SUPPORTED; + } + dbtr_trigger_setup(trig, &entry->data); sbi_hart_protection_unmap_range((unsigned long)entry, sizeof(*entry)); dbtr_trigger_enable(trig); diff --git a/lib/sbi/sbi_domain_context.c b/lib/sbi/sbi_domain_context.c index cc4cc04d..0861d541 100644 --- a/lib/sbi/sbi_domain_context.c +++ b/lib/sbi/sbi_domain_context.c @@ -171,8 +171,7 @@ static int switch_to_next_domain_context(struct hart_context *ctx, * is also required for some of the above CSR updates (such * as satp CSR). */ - sbi_hart_protection_unconfigure(scratch); - sbi_hart_protection_configure(scratch); + sbi_hart_protection_reconfigure(scratch, current_dom, target_dom); /* Mark current context structure initialized because context saved */ ctx->initialized = true; diff --git a/lib/sbi/sbi_ecall.c b/lib/sbi/sbi_ecall.c index 745fa313..65c5a55a 100644 --- a/lib/sbi/sbi_ecall.c +++ b/lib/sbi/sbi_ecall.c @@ -66,6 +66,8 @@ void sbi_ecall_get_extensions_str(char *exts_str, int exts_str_size, bool experi sbi_list_for_each_entry(t, &ecall_exts_list, head) { if (experimental != t->experimental) continue; + if (offset + sbi_strlen(t->name) + 1 > exts_str_size) + break; sbi_snprintf(exts_str + offset, exts_str_size - offset, "%s,", t->name); offset = offset + sbi_strlen(t->name) + 1; diff --git a/lib/sbi/sbi_hart_pmp.c b/lib/sbi/sbi_hart_pmp.c index 4528258d..c0a4ce1b 100644 --- a/lib/sbi/sbi_hart_pmp.c +++ b/lib/sbi/sbi_hart_pmp.c @@ -228,10 +228,10 @@ static bool is_valid_pmp_idx(unsigned int pmp_count, unsigned int pmp_idx) return false; } -static int sbi_hart_smepmp_configure(struct sbi_scratch *scratch) +static int sbi_hart_smepmp_configure(struct sbi_scratch *scratch, + struct sbi_domain *dom) { struct sbi_domain_memregion *reg; - struct sbi_domain *dom = sbi_domain_thishart_ptr(); unsigned int pmp_log2gran, pmp_bits; unsigned int pmp_idx, pmp_count; unsigned long pmp_addr_max; @@ -366,10 +366,10 @@ static int sbi_hart_smepmp_unmap_range(struct sbi_scratch *scratch, return sbi_hart_pmp_disable(SBI_SMEPMP_RESV_ENTRY); } -static int sbi_hart_oldpmp_configure(struct sbi_scratch *scratch) +static int sbi_hart_oldpmp_configure(struct sbi_scratch *scratch, + struct sbi_domain *dom) { struct sbi_domain_memregion *reg; - struct sbi_domain *dom = sbi_domain_thishart_ptr(); unsigned long pmp_addr, pmp_addr_max; unsigned int pmp_log2gran, pmp_bits; unsigned int pmp_idx, pmp_count; @@ -407,7 +407,8 @@ static int sbi_hart_oldpmp_configure(struct sbi_scratch *scratch) return 0; } -static void sbi_hart_pmp_unconfigure(struct sbi_scratch *scratch) +static void sbi_hart_pmp_unconfigure(struct sbi_scratch *scratch, + struct sbi_domain *dom) { int i, pmp_count = sbi_hart_pmp_count(scratch); @@ -424,6 +425,7 @@ static void sbi_hart_pmp_unconfigure(struct sbi_scratch *scratch) static struct sbi_hart_protection pmp_protection = { .name = "pmp", .rating = 100, + .type = SBI_HART_PROTECTION_TYPE_MEMORY, .configure = sbi_hart_oldpmp_configure, .unconfigure = sbi_hart_pmp_unconfigure, }; @@ -431,6 +433,7 @@ static struct sbi_hart_protection pmp_protection = { static struct sbi_hart_protection epmp_protection = { .name = "epmp", .rating = 200, + .type = SBI_HART_PROTECTION_TYPE_MEMORY, .configure = sbi_hart_smepmp_configure, .unconfigure = sbi_hart_pmp_unconfigure, .map_range = sbi_hart_smepmp_map_range, diff --git a/lib/sbi/sbi_hart_protection.c b/lib/sbi/sbi_hart_protection.c index fbebfd1a..c4c149c8 100644 --- a/lib/sbi/sbi_hart_protection.c +++ b/lib/sbi/sbi_hart_protection.c @@ -4,18 +4,50 @@ * Copyright (c) 2025 Ventana Micro Systems Inc. */ +#include <sbi/sbi_console.h> #include <sbi/sbi_error.h> #include <sbi/sbi_hart_protection.h> #include <sbi/sbi_scratch.h> +#include <sbi/sbi_string.h> static SBI_LIST_HEAD(hart_protection_list); -struct sbi_hart_protection *sbi_hart_protection_best(void) +static struct sbi_hart_protection *__hart_memory_protection_best(void) { - if (sbi_list_empty(&hart_protection_list)) - return NULL; + struct sbi_hart_protection *pos; - return sbi_list_first_entry(&hart_protection_list, struct sbi_hart_protection, head); + sbi_list_for_each_entry(pos, &hart_protection_list, head) { + if (pos->type == SBI_HART_PROTECTION_TYPE_MEMORY) + return pos; + } + + return NULL; +} + +void sbi_hart_protection_get_str(char *out_str, int out_str_size) +{ + bool memory_protect_done = false; + struct sbi_hart_protection *pos; + int offset = 0; + + if (!out_str || out_str_size <= 0) + return; + sbi_memset(out_str, 0, out_str_size); + + sbi_list_for_each_entry(pos, &hart_protection_list, head) { + if (pos->type == SBI_HART_PROTECTION_TYPE_MEMORY) { + if (memory_protect_done) + continue; + memory_protect_done = true; + } + sbi_snprintf(out_str + offset, out_str_size - offset, "%s,", pos->name); + offset = offset + sbi_strlen(pos->name) + 1; + } + + if (offset) + out_str[offset - 1] = '\0'; + else + sbi_strncpy(out_str, "none", out_str_size); } int sbi_hart_protection_register(struct sbi_hart_protection *hprot) @@ -25,6 +57,8 @@ int sbi_hart_protection_register(struct sbi_hart_protection *hprot) if (!hprot) return SBI_EINVAL; + if (hprot->type >= SBI_HART_PROTECTION_TYPE_MAX) + return SBI_EINVAL; sbi_list_for_each_entry(pos, &hart_protection_list, head) { if (hprot->rating > pos->rating) { @@ -49,31 +83,122 @@ void sbi_hart_protection_unregister(struct sbi_hart_protection *hprot) sbi_list_del(&hprot->head); } -int sbi_hart_protection_configure(struct sbi_scratch *scratch) +static int __hart_protection_configure(struct sbi_scratch *scratch, + struct sbi_hart_protection *hprot, + struct sbi_domain *dom) { - struct sbi_hart_protection *hprot = sbi_hart_protection_best(); - if (!hprot) return 0; if (!hprot->configure) return SBI_ENOSYS; - return hprot->configure(scratch); + return hprot->configure(scratch, dom); } -void sbi_hart_protection_unconfigure(struct sbi_scratch *scratch) +static void __hart_protection_unconfigure(struct sbi_scratch *scratch, + struct sbi_hart_protection *hprot, + struct sbi_domain *dom) { - struct sbi_hart_protection *hprot = sbi_hart_protection_best(); - if (!hprot || !hprot->unconfigure) return; - hprot->unconfigure(scratch); + hprot->unconfigure(scratch, dom); +} + +int sbi_hart_protection_configure(struct sbi_scratch *scratch, + struct sbi_domain *dom) +{ + bool do_configure, memory_protect_done = false; + struct sbi_hart_protection *hprot; + int ret; + + sbi_list_for_each_entry(hprot, &hart_protection_list, head) { + do_configure = false; + switch (hprot->type) { + case SBI_HART_PROTECTION_TYPE_MEMORY: + do_configure = !memory_protect_done; + memory_protect_done = true; + break; + case SBI_HART_PROTECTION_TYPE_ID: + do_configure = true; + break; + default: + break; + } + if (!do_configure) + continue; + + ret = __hart_protection_configure(scratch, hprot, dom); + if (ret) + return ret; + } + + return 0; +} + +void sbi_hart_protection_unconfigure(struct sbi_scratch *scratch, + struct sbi_domain *dom) +{ + + bool do_unconfigure, memory_protect_done = false; + struct sbi_hart_protection *hprot; + + sbi_list_for_each_entry(hprot, &hart_protection_list, head) { + do_unconfigure = false; + switch (hprot->type) { + case SBI_HART_PROTECTION_TYPE_MEMORY: + do_unconfigure = !memory_protect_done; + memory_protect_done = true; + break; + case SBI_HART_PROTECTION_TYPE_ID: + do_unconfigure = true; + break; + default: + break; + } + if (!do_unconfigure) + continue; + + __hart_protection_unconfigure(scratch, hprot, dom); + } +} + +int sbi_hart_protection_reconfigure(struct sbi_scratch *scratch, + struct sbi_domain *current_dom, + struct sbi_domain *next_dom) +{ + bool do_reconfigure, memory_protect_done = false; + struct sbi_hart_protection *hprot; + int ret; + + sbi_list_for_each_entry(hprot, &hart_protection_list, head) { + do_reconfigure = false; + switch (hprot->type) { + case SBI_HART_PROTECTION_TYPE_MEMORY: + do_reconfigure = !memory_protect_done; + memory_protect_done = true; + break; + case SBI_HART_PROTECTION_TYPE_ID: + do_reconfigure = true; + break; + default: + break; + } + if (!do_reconfigure) + continue; + + __hart_protection_unconfigure(scratch, hprot, current_dom); + ret = __hart_protection_configure(scratch, hprot, next_dom); + if (ret) + return ret; + } + + return 0; } int sbi_hart_protection_map_range(unsigned long base, unsigned long size) { - struct sbi_hart_protection *hprot = sbi_hart_protection_best(); + struct sbi_hart_protection *hprot = __hart_memory_protection_best(); if (!hprot || !hprot->map_range) return 0; @@ -83,7 +208,7 @@ int sbi_hart_protection_map_range(unsigned long base, unsigned long size) int sbi_hart_protection_unmap_range(unsigned long base, unsigned long size) { - struct sbi_hart_protection *hprot = sbi_hart_protection_best(); + struct sbi_hart_protection *hprot = __hart_memory_protection_best(); if (!hprot || !hprot->unmap_range) return 0; diff --git a/lib/sbi/sbi_init.c b/lib/sbi/sbi_init.c index 9bb1a37e..acd2f8b6 100644 --- a/lib/sbi/sbi_init.c +++ b/lib/sbi/sbi_init.c @@ -76,7 +76,6 @@ static void sbi_boot_print_general(struct sbi_scratch *scratch) const struct sbi_hsm_device *hdev; const struct sbi_ipi_device *idev; const struct sbi_timer_device *tdev; - const struct sbi_hart_protection *hprot; const struct sbi_console_device *cdev; const struct sbi_system_reset_device *srdev; const struct sbi_system_suspend_device *susp_dev; @@ -93,9 +92,8 @@ static void sbi_boot_print_general(struct sbi_scratch *scratch) sbi_printf("Platform Features : %s\n", str); sbi_printf("Platform HART Count : %u\n", sbi_platform_hart_count(plat)); - hprot = sbi_hart_protection_best(); - sbi_printf("Platform HART Protection : %s\n", - (hprot) ? hprot->name : "---"); + sbi_hart_protection_get_str(str, sizeof(str)); + sbi_printf("Platform HART Protection : %s\n", str); idev = sbi_ipi_get_device(); sbi_printf("Platform IPI Device : %s\n", (idev) ? idev->name : "---"); @@ -432,12 +430,12 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid) } /* - * Configure hart isolation at last because if SMEPMP is, + * Configure hart protection at last because if SMEPMP is * detected, M-mode access to the S/U space will be rescinded. */ - rc = sbi_hart_protection_configure(scratch); + rc = sbi_hart_protection_configure(scratch, sbi_domain_thishart_ptr()); if (rc) { - sbi_printf("%s: hart isolation configure failed (error %d)\n", + sbi_printf("%s: hart protection configure failed (error %d)\n", __func__, rc); sbi_hart_hang(); } @@ -515,10 +513,10 @@ static void __noreturn init_warm_startup(struct sbi_scratch *scratch, sbi_hart_hang(); /* - * Configure hart isolation at last because if SMEPMP is, + * Configure hart protection at last because if SMEPMP is * detected, M-mode access to the S/U space will be rescinded. */ - rc = sbi_hart_protection_configure(scratch); + rc = sbi_hart_protection_configure(scratch, sbi_domain_thishart_ptr()); if (rc) sbi_hart_hang(); @@ -539,7 +537,7 @@ static void __noreturn init_warm_resume(struct sbi_scratch *scratch, if (rc) sbi_hart_hang(); - rc = sbi_hart_protection_configure(scratch); + rc = sbi_hart_protection_configure(scratch, sbi_domain_thishart_ptr()); if (rc) sbi_hart_hang(); diff --git a/lib/sbi/sbi_mpxy.c b/lib/sbi/sbi_mpxy.c index ff5c6ab6..19f59f3a 100644 --- a/lib/sbi/sbi_mpxy.c +++ b/lib/sbi/sbi_mpxy.c @@ -145,14 +145,36 @@ static inline bool mpxy_is_std_attr(u32 attr_id) return (attr_id >> 31) ? false : true; } +static inline bool mpxy_channel_visible(struct sbi_mpxy_channel *channel, + struct sbi_domain *dom) +{ + return channel->owner_domain == dom; +} + +/** Find channel_id in registered channels list for this domain */ +static struct sbi_mpxy_channel *mpxy_find_channel(u32 channel_id, + struct sbi_domain *dom) +{ + struct sbi_mpxy_channel *channel; + + sbi_list_for_each_entry(channel, &mpxy_channel_list, head) { + if (channel->channel_id == channel_id && + mpxy_channel_visible(channel, dom)) + return channel; + } + + return NULL; +} + /** Find channel_id in registered channels list */ -static struct sbi_mpxy_channel *mpxy_find_channel(u32 channel_id) +struct sbi_mpxy_channel *sbi_mpxy_find_channel_any(u32 channel_id) { struct sbi_mpxy_channel *channel; - sbi_list_for_each_entry(channel, &mpxy_channel_list, head) + sbi_list_for_each_entry(channel, &mpxy_channel_list, head) { if (channel->channel_id == channel_id) return channel; + } return NULL; } @@ -224,7 +246,10 @@ int sbi_mpxy_register_channel(struct sbi_mpxy_channel *channel) if (!channel) return SBI_EINVAL; - if (mpxy_find_channel(channel->channel_id)) + if (!channel->owner_domain) + return SBI_EINVAL; + + if (sbi_mpxy_find_channel_any(channel->channel_id)) return SBI_EALREADY; /* Initialize channel specific attributes */ @@ -397,12 +422,15 @@ int sbi_mpxy_get_channel_ids(u32 start_index) struct sbi_mpxy_channel *channel; u32 channels_count = 0; u32 *shmem_base; + struct sbi_domain *dom = sbi_domain_thishart_ptr(); if (!mpxy_shmem_enabled(ms)) return SBI_ERR_NO_SHMEM; - sbi_list_for_each_entry(channel, &mpxy_channel_list, head) - channels_count += 1; + sbi_list_for_each_entry(channel, &mpxy_channel_list, head) { + if (mpxy_channel_visible(channel, dom)) + channels_count += 1; + } if (start_index > channels_count) return SBI_ERR_INVALID_PARAM; @@ -420,6 +448,9 @@ int sbi_mpxy_get_channel_ids(u32 start_index) // Iterate over the list of channels to get the channel ids. sbi_list_for_each_entry(channel, &mpxy_channel_list, head) { + if (!mpxy_channel_visible(channel, sbi_domain_thishart_ptr())) + continue; + if (node_index >= start_index && node_index < (start_index + returned)) { shmem_base[2 + node_ret] = cpu_to_le32(channel->channel_id); @@ -446,11 +477,12 @@ int sbi_mpxy_read_attrs(u32 channel_id, u32 base_attr_id, u32 attr_count) int ret = SBI_SUCCESS; u32 *attr_ptr, end_id; void *shmem_base; + struct sbi_domain *dom = sbi_domain_thishart_ptr(); if (!mpxy_shmem_enabled(ms)) return SBI_ERR_NO_SHMEM; - struct sbi_mpxy_channel *channel = mpxy_find_channel(channel_id); + struct sbi_mpxy_channel *channel = mpxy_find_channel(channel_id, dom); if (!channel) return SBI_ERR_NOT_SUPPORTED; @@ -597,11 +629,12 @@ int sbi_mpxy_write_attrs(u32 channel_id, u32 base_attr_id, u32 attr_count) struct sbi_mpxy_channel *channel; int ret, mem_idx; void *shmem_base; + struct sbi_domain *dom = sbi_domain_thishart_ptr(); if (!mpxy_shmem_enabled(ms)) return SBI_ERR_NO_SHMEM; - channel = mpxy_find_channel(channel_id); + channel = mpxy_find_channel(channel_id, dom); if (!channel) return SBI_ERR_NOT_SUPPORTED; @@ -686,12 +719,13 @@ int sbi_mpxy_send_message(u32 channel_id, u8 msg_id, struct sbi_mpxy_channel *channel; void *shmem_base, *resp_buf; u32 resp_bufsize; + struct sbi_domain *dom = sbi_domain_thishart_ptr(); int ret; if (!mpxy_shmem_enabled(ms)) return SBI_ERR_NO_SHMEM; - channel = mpxy_find_channel(channel_id); + channel = mpxy_find_channel(channel_id, dom); if (!channel) return SBI_ERR_NOT_SUPPORTED; @@ -743,12 +777,13 @@ int sbi_mpxy_get_notification_events(u32 channel_id, unsigned long *events_len) struct mpxy_state *ms = sbi_domain_mpxy_state_thishart_ptr(); struct sbi_mpxy_channel *channel; void *eventsbuf, *shmem_base; + struct sbi_domain *dom = sbi_domain_thishart_ptr(); int ret; if (!mpxy_shmem_enabled(ms)) return SBI_ERR_NO_SHMEM; - channel = mpxy_find_channel(channel_id); + channel = mpxy_find_channel(channel_id, dom); if (!channel || !channel->get_notification_events) return SBI_ERR_NOT_SUPPORTED; diff --git a/lib/sbi/sbi_sse.c b/lib/sbi/sbi_sse.c index 818afb87..13d78e7c 100644 --- a/lib/sbi/sbi_sse.c +++ b/lib/sbi/sbi_sse.c @@ -563,19 +563,14 @@ static void sse_event_inject(struct sbi_sse_event *e, if (misa_extension('H')) { unsigned long hstatus = csr_read(CSR_HSTATUS); - -#if __riscv_xlen == 64 - if (regs->mstatus & MSTATUS_MPV) -#elif __riscv_xlen == 32 - if (regs->mstatusH & MSTATUSH_MPV) -#else -#error "Unexpected __riscv_xlen" -#endif + if (sbi_regs_from_virt(regs)) hstatus |= HSTATUS_SPV; + else + hstatus &= ~HSTATUS_SPV; hstatus &= ~HSTATUS_SPVP; if (hstatus & HSTATUS_SPV && regs->mstatus & SSTATUS_SPP) - hstatus |= HSTATUS_SPVP; + hstatus |= HSTATUS_SPVP; csr_write(CSR_HSTATUS, hstatus); } diff --git a/lib/sbi/sbi_timer.c b/lib/sbi/sbi_timer.c index 4d737d22..8abb5e7b 100644 --- a/lib/sbi/sbi_timer.c +++ b/lib/sbi/sbi_timer.c @@ -104,6 +104,18 @@ bool sbi_timer_waitms_until(bool (*predicate)(void *), void *arg, return true; } +unsigned long sbi_timer_frequency(void) +{ + const struct sbi_timer_device *timer_dev = sbi_timer_get_device(); + + if (!timer_dev) { + sbi_printf("%s: called without timer device\n", __func__); + return 0; + } + + return timer_dev->timer_freq; +} + u64 sbi_timer_value(void) { if (get_time_val) diff --git a/lib/sbi/sbi_tlb.c b/lib/sbi/sbi_tlb.c index ada60c32..ab1e3a99 100644 --- a/lib/sbi/sbi_tlb.c +++ b/lib/sbi/sbi_tlb.c @@ -29,6 +29,38 @@ static unsigned long tlb_fifo_off; static unsigned long tlb_fifo_mem_off; static unsigned long tlb_range_flush_limit; +static void sbi_tlb_local_fence_i(struct sbi_tlb_info *tinfo); +static void sbi_tlb_local_sfence_vma(struct sbi_tlb_info *tinfo); +static void sbi_tlb_local_sfence_vma_asid(struct sbi_tlb_info *tinfo); +static void sbi_tlb_local_hfence_gvma_vmid(struct sbi_tlb_info *tinfo); +static void sbi_tlb_local_hfence_gvma(struct sbi_tlb_info *tinfo); +static void sbi_tlb_local_hfence_vvma_asid(struct sbi_tlb_info *tinfo); +static void sbi_tlb_local_hfence_vvma(struct sbi_tlb_info *tinfo); + +static struct sbi_tlb_local_operations default_tlb_local_ops = { + .local_fence_i = sbi_tlb_local_fence_i, + .local_sfence_vma = sbi_tlb_local_sfence_vma, + .local_sfence_vma_asid = sbi_tlb_local_sfence_vma_asid, + .local_hfence_gvma_vmid = sbi_tlb_local_hfence_gvma_vmid, + .local_hfence_gvma = sbi_tlb_local_hfence_gvma, + .local_hfence_vvma_asid = sbi_tlb_local_hfence_vvma_asid, + .local_hfence_vvma = sbi_tlb_local_hfence_vvma +}; + +static const struct sbi_tlb_local_operations *tlb_local_ops = + &default_tlb_local_ops; + +const struct sbi_tlb_local_operations *sbi_tlb_get_local_operations(void) +{ + return tlb_local_ops; +} + +void sbi_tlb_set_local_operations( + const struct sbi_tlb_local_operations *ops) +{ + tlb_local_ops = ops; +} + void __sbi_sfence_vma_all(void) { __asm__ __volatile("sfence.vma"); @@ -183,25 +215,25 @@ static void tlb_entry_local_process(struct sbi_tlb_info *data) switch (data->type) { case SBI_TLB_FENCE_I: - sbi_tlb_local_fence_i(data); + tlb_local_ops->local_fence_i(data); break; case SBI_TLB_SFENCE_VMA: - sbi_tlb_local_sfence_vma(data); + tlb_local_ops->local_sfence_vma(data); break; case SBI_TLB_SFENCE_VMA_ASID: - sbi_tlb_local_sfence_vma_asid(data); + tlb_local_ops->local_sfence_vma_asid(data); break; case SBI_TLB_HFENCE_GVMA_VMID: - sbi_tlb_local_hfence_gvma_vmid(data); + tlb_local_ops->local_hfence_gvma_vmid(data); break; case SBI_TLB_HFENCE_GVMA: - sbi_tlb_local_hfence_gvma(data); + tlb_local_ops->local_hfence_gvma(data); break; case SBI_TLB_HFENCE_VVMA_ASID: - sbi_tlb_local_hfence_vvma_asid(data); + tlb_local_ops->local_hfence_vvma_asid(data); break; case SBI_TLB_HFENCE_VVMA: - sbi_tlb_local_hfence_vvma(data); + tlb_local_ops->local_hfence_vvma(data); break; default: break; diff --git a/lib/sbi/tests/sbi_ecall_test.c b/lib/sbi/tests/sbi_ecall_test.c index 5b6ce37c..f5c553e9 100644 --- a/lib/sbi/tests/sbi_ecall_test.c +++ b/lib/sbi/tests/sbi_ecall_test.c @@ -40,10 +40,69 @@ static void test_sbi_ecall_register_find_extension(struct sbiunit_test_case *tes SBIUNIT_EXPECT_EQ(test, sbi_ecall_find_extension(SBI_EXT_EXPERIMENTAL_START), NULL); } +static void test_sbi_ecall_get_extensions_str_bounds(struct sbiunit_test_case *test) +{ + struct sbi_ecall_extension e1 = { + .extid_start = SBI_EXT_EXPERIMENTAL_START, + .extid_end = SBI_EXT_EXPERIMENTAL_START, + .name = "Alpha", + .handle = dummy_handler, + .experimental = false, + }; + struct sbi_ecall_extension e2 = { + .extid_start = SBI_EXT_EXPERIMENTAL_START + 1, + .extid_end = SBI_EXT_EXPERIMENTAL_START + 1, + .name = "Bravo", + .handle = dummy_handler, + .experimental = false, + }; + struct sbi_ecall_extension e3 = { + .extid_start = SBI_EXT_EXPERIMENTAL_START + 2, + .extid_end = SBI_EXT_EXPERIMENTAL_START + 2, + .name = "Charli", + .handle = dummy_handler, + .experimental = false, + }; + char storage[16 + 16]; + char *buf = storage; + char big[128]; + int i; + int found_alpha = 0; + + SBIUNIT_EXPECT_EQ(test, sbi_ecall_register_extension(&e1), 0); + SBIUNIT_EXPECT_EQ(test, sbi_ecall_register_extension(&e2), 0); + SBIUNIT_EXPECT_EQ(test, sbi_ecall_register_extension(&e3), 0); + + for (i = 16; i < 32; i++) + storage[i] = (char)0xA5; + + /* Undersized buffer must not write past the caller-provided size. */ + sbi_ecall_get_extensions_str(buf, 16, false); + SBIUNIT_EXPECT_EQ(test, buf[15], '\0'); + for (i = 16; i < 32; i++) + SBIUNIT_EXPECT_EQ(test, (unsigned char)storage[i], 0xA5); + + /* Negative control: room for the full list, including registered names. */ + sbi_ecall_get_extensions_str(big, sizeof(big), false); + SBIUNIT_EXPECT_NE(test, sbi_strlen(big), 0); + for (i = 0; big[i] != '\0'; i++) { + if (sbi_strncmp(&big[i], "Alpha", 5) == 0) { + found_alpha = 1; + break; + } + } + SBIUNIT_EXPECT_EQ(test, found_alpha, 1); + + sbi_ecall_unregister_extension(&e1); + sbi_ecall_unregister_extension(&e2); + sbi_ecall_unregister_extension(&e3); +} + static struct sbiunit_test_case ecall_tests[] = { SBIUNIT_TEST_CASE(test_sbi_ecall_version), SBIUNIT_TEST_CASE(test_sbi_ecall_impid), SBIUNIT_TEST_CASE(test_sbi_ecall_register_find_extension), + SBIUNIT_TEST_CASE(test_sbi_ecall_get_extensions_str_bounds), SBIUNIT_END_CASE, }; diff --git a/lib/utils/cache/fdt_andes_llcache.c b/lib/utils/cache/fdt_andes_llcache.c index 490503ee..7a61b6ab 100644 --- a/lib/utils/cache/fdt_andes_llcache.c +++ b/lib/utils/cache/fdt_andes_llcache.c @@ -5,8 +5,11 @@ */ #include <sbi/riscv_io.h> +#include <sbi/sbi_domain.h> #include <sbi/sbi_error.h> +#include <sbi/sbi_hart.h> #include <sbi/sbi_heap.h> +#include <sbi/sbi_scratch.h> #include <sbi_utils/cache/fdt_cache.h> #include <sbi_utils/fdt/fdt_driver.h> #include <sbi_utils/hsm/fdt_hsm_andes_atcsmu.h> @@ -102,7 +105,10 @@ static int andes_llcache_enable(struct cache_device *dev, bool enable) } llcache_ctrl = readl(ctrl_addr); - return enable == EXTRACT_FIELD(llcache_ctrl, LLCACHE_REG_CTRL_EN_MASK); + if (enable != EXTRACT_FIELD(llcache_ctrl, LLCACHE_REG_CTRL_EN_MASK)) + return SBI_EFAIL; + + return SBI_OK; } static struct cache_ops andes_llcache_ops = { @@ -113,13 +119,13 @@ static struct cache_ops andes_llcache_ops = { static int andes_llcache_probe(const void *fdt, int nodeoff, const struct fdt_match *match) { int rc; - u64 llcache_base = 0; + u64 llcache_base = 0, size; struct andes_llcache *llcache; struct cache_device *dev; uint32_t llcache_cfg; - rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &llcache_base, NULL); - if (rc < 0 || !llcache_base) + rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &llcache_base, &size); + if (rc < 0 || !llcache_base || !size) return SBI_ENODEV; llcache = sbi_zalloc(sizeof(*llcache)); @@ -148,6 +154,15 @@ static int andes_llcache_probe(const void *fdt, int nodeoff, const struct fdt_ma llcache->status_core_stride = 4; } + if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), SBI_HART_EXT_SMEPMP)) { + rc = sbi_domain_root_add_memrange( + (unsigned long)llcache->base, (unsigned long)size, PAGE_SIZE, + SBI_DOMAIN_MEMREGION_MMIO | + SBI_DOMAIN_MEMREGION_SHARED_SURW_MRW); + if (rc) + return rc; + } + /* Wait for the hardware initialization done */ while (!andes_llcache_init_done(llcache)) ; diff --git a/lib/utils/cppc/fdt_cppc_rpmi.c b/lib/utils/cppc/fdt_cppc_rpmi.c index 7bd3b0f9..15c89351 100644 --- a/lib/utils/cppc/fdt_cppc_rpmi.c +++ b/lib/utils/cppc/fdt_cppc_rpmi.c @@ -12,6 +12,7 @@ #include <sbi/sbi_cppc.h> #include <sbi/sbi_ecall_interface.h> #include <sbi/sbi_scratch.h> +#include <sbi/sbi_timer.h> #include <sbi_utils/fdt/fdt_driver.h> #include <sbi_utils/fdt/fdt_helper.h> #include <sbi_utils/mailbox/fdt_mailbox.h> @@ -39,6 +40,8 @@ struct rpmi_cppc { ulong fc_db_addr; u64 fc_db_setmask; u64 fc_db_preservemask; + u64 prev_time_csr; + u64 prev_delivered_counter; }; static unsigned long rpmi_cppc_offset; @@ -107,6 +110,7 @@ static void rpmi_cppc_fc_db_trigger(struct rpmi_cppc *cppc) static int rpmi_cppc_read(unsigned long reg, u64 *val) { + u64 curr_freq, curr_time_csr, time_csr_delta, freq; int rc = SBI_SUCCESS; struct rpmi_cppc_read_reg_req req; struct rpmi_cppc_read_reg_resp resp; @@ -116,12 +120,38 @@ static int rpmi_cppc_read(unsigned long reg, u64 *val) req.reg_id = reg; cppc = rpmi_cppc_get_pointer(req.hart_id); - rc = rpmi_normal_request_with_status( - cppc->chan, RPMI_CPPC_SRV_READ_REG, - &req, rpmi_u32_count(req), rpmi_u32_count(req), - &resp, rpmi_u32_count(resp), rpmi_u32_count(resp)); - if (rc) - return rc; + if ((reg == SBI_CPPC_DELIVERED_CTR) && cppc->fc_supported) { +#if __riscv_xlen != 32 + curr_freq = readq((void *)cppc->fc_perf_feedback_addr); +#else + curr_freq = readl((void *)cppc->fc_perf_feedback_addr + 4); + curr_freq <<= 32; + curr_freq |= readl((void *)cppc->fc_perf_feedback_addr); +#endif + curr_time_csr = sbi_timer_value(); + time_csr_delta = curr_time_csr - cppc->prev_time_csr; + cppc->prev_time_csr = curr_time_csr; + + freq = sbi_timer_frequency(); + if (freq) + cppc->prev_delivered_counter += + curr_freq * (time_csr_delta / freq) + + (curr_freq * (time_csr_delta % freq)) / freq; + + resp.data_lo = cpu_to_le32(cppc->prev_delivered_counter & UINT32_MAX); + resp.data_hi = cpu_to_le32(cppc->prev_delivered_counter >> 32); + } else if (reg == SBI_CPPC_REFERENCE_CTR) { + curr_time_csr = sbi_timer_value(); + resp.data_lo = cpu_to_le32(curr_time_csr & UINT32_MAX); + resp.data_hi = cpu_to_le32(curr_time_csr >> 32); + } else { + rc = rpmi_normal_request_with_status( + cppc->chan, RPMI_CPPC_SRV_READ_REG, + &req, rpmi_u32_count(req), rpmi_u32_count(req), + &resp, rpmi_u32_count(resp), rpmi_u32_count(resp)); + if (rc) + return rc; + } #if __riscv_xlen == 32 *val = resp.data_lo; @@ -276,6 +306,8 @@ static int rpmi_cppc_update_hart_scratch(struct mbox_chan *chan) if (!cppc) return SBI_ENOSYS; + cppc->prev_time_csr = 0; + cppc->prev_delivered_counter = 0; cppc->chan = chan; cppc->mode = cppc_mode; cppc->fc_supported = fc_supported; diff --git a/lib/utils/hsm/fdt_hsm_andes_atcsmu.c b/lib/utils/hsm/fdt_hsm_andes_atcsmu.c index 115916d5..2885a983 100644 --- a/lib/utils/hsm/fdt_hsm_andes_atcsmu.c +++ b/lib/utils/hsm/fdt_hsm_andes_atcsmu.c @@ -56,7 +56,7 @@ bool atcsmu_support_sleep_mode(u32 sleep_type, u32 hartid) void atcsmu_set_command(u32 pcs_ctl, u32 hartid) { - writel_relaxed(pcs_ctl, (char *)atcsmu_base + PCSm_CTL_OFFSET(hartid)); + writel(pcs_ctl, (char *)atcsmu_base + PCSm_CTL_OFFSET(hartid)); } int atcsmu_set_reset_vector(u64 wakeup_addr, u32 hartid) @@ -94,42 +94,24 @@ u32 atcsmu_read_scratch(void) return readl_relaxed((char *)atcsmu_base + SCRATCH_PAD_OFFSET); } -bool atcsmu_pcs_is_sleep(u32 hartid, bool deep_sleep) +bool atcsmu_hart_is_sleep(void *opaque) { - u32 pcs_status = readl_relaxed((char *)atcsmu_base + PCSm_STATUS_OFFSET(hartid)); - u32 pd_status = deep_sleep ? PD_STATUS_DEEP_SLEEP : PD_STATUS_LIGHT_SLEEP; + struct atcsmu_sleep_arg *arg = opaque; - if (EXTRACT_FIELD(pcs_status, PD_TYPE_MASK) != PD_TYPE_SLEEP) { - sbi_printf("ATCSMU: hart%d (PCS%d): failed to sleep\n", hartid, hartid + 3); - return false; - } - - if (EXTRACT_FIELD(pcs_status, PD_STATUS_MASK) != pd_status) { - sbi_printf("ATCSMU: hart%d (PCS%d): failed to enter %s sleep\n", - hartid, hartid + 3, deep_sleep ? "deep" : "light"); - return false; - } + u32 pcs_status = readl_relaxed((char *)atcsmu_base + + PCSm_STATUS_OFFSET(arg->hartid)); + u32 pd_status = arg->deep_sleep ? PD_STATUS_DEEP_SLEEP : + PD_STATUS_LIGHT_SLEEP; - return true; + return EXTRACT_FIELD(pcs_status, PD_TYPE_MASK) == PD_TYPE_SLEEP && + EXTRACT_FIELD(pcs_status, PD_STATUS_MASK) == pd_status; } static int ae350_hart_start(u32 hartid, ulong saddr) { u32 hartindex = sbi_hartid_to_hartindex(hartid); - u32 sleep_type = atcsmu_get_sleep_type(hartid); - - /* - * Don't send wakeup command when: - * 1) boot time - * 2) the target hart is non-sleepable 25-series hart0 - * 3) light sleep - */ - if (!sbi_init_count(hartindex) || (is_andes(25) && hartid == 0) || - sleep_type == SBI_SUSP_AE350_LIGHT_SLEEP) - return sbi_ipi_raw_send(hartindex, false); - atcsmu_set_command(WAKEUP_CMD, hartid); - return 0; + return sbi_ipi_raw_send(hartindex, false); } static int ae350_hart_stop(void) @@ -152,18 +134,19 @@ static int ae350_hart_stop(void) /* Prevent the core leaving the WFI mode unexpectedly */ csr_write(CSR_MIE, 0); + atcsmu_set_wakeup_events(PCS_WAKEUP_MSIP_MASK | PCS_WAKEUP_MEIP_MASK, hartid); if (sleep_type == SBI_SUSP_AE350_LIGHT_SLEEP) { - csr_write(CSR_MIE, MIP_MSIP); - atcsmu_set_wakeup_events(PCS_WAKEUP_MSIP_MASK, hartid); + /* Clock-gated only: needs MSI or MEI set to resume past the WFI */ + csr_set(CSR_MIE, MIP_MSIP | MIP_MEIP); atcsmu_set_command(LIGHT_SLEEP_CMD, hartid); } else if (sleep_type == SBI_SUSP_SLEEP_TYPE_SUSPEND) { - atcsmu_set_wakeup_events(0x0, hartid); - atcsmu_set_command(DEEP_SLEEP_CMD, hartid); + /* Power-gated: SMU wakes it via cold reset, interrupts not needed */ rc = atcsmu_set_reset_vector((ulong)ae350_enable_coherency_warmboot, hartid); if (rc) return SBI_EFAIL; ae350_non_ret_save(sbi_scratch_thishart_ptr()); + atcsmu_set_command(DEEP_SLEEP_CMD, hartid); } ae350_disable_coherency(); @@ -184,18 +167,27 @@ static const struct sbi_hsm_device hsm_andes_atcsmu = { static int hsm_andes_atcsmu_probe(const void *fdt, int nodeoff, const struct fdt_match *match) { int poff, rc; - u64 addr; + u64 addr, size; /* Need to find the parent for the address property */ poff = fdt_parent_offset(fdt, nodeoff); if (poff < 0) return SBI_EINVAL; - rc = fdt_get_node_addr_size(fdt, poff, 0, &addr, NULL); - if (rc < 0 || !addr) + rc = fdt_get_node_addr_size(fdt, poff, 0, &addr, &size); + if (rc < 0 || !addr || !size) return SBI_ENODEV; - atcsmu_base = addr; + if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), SBI_HART_EXT_SMEPMP)) { + rc = sbi_domain_root_add_memrange( + (unsigned long)addr, (unsigned long)size, PAGE_SIZE, + SBI_DOMAIN_MEMREGION_MMIO | + SBI_DOMAIN_MEMREGION_SHARED_SURW_MRW); + if (rc) + return rc; + } + + atcsmu_base = addr; sbi_hsm_set_device(&hsm_andes_atcsmu); return 0; } diff --git a/lib/utils/mpxy/fdt_mpxy_rpmi_logging.c b/lib/utils/mpxy/fdt_mpxy_rpmi_logging.c index e8f085eb..1e92053d 100644 --- a/lib/utils/mpxy/fdt_mpxy_rpmi_logging.c +++ b/lib/utils/mpxy/fdt_mpxy_rpmi_logging.c @@ -11,7 +11,7 @@ static struct mpxy_rpmi_service_data logging_services[] = { [0] = { - .id = RPMI_MM_SRV_ENABLE_NOTIFICATION, + .id = RPMI_LOGGING_SRV_ENABLE_NOTIFICATION, .min_tx_len = sizeof(struct rpmi_enable_notification_req), .max_tx_len = sizeof(struct rpmi_enable_notification_req), .min_rx_len = sizeof(struct rpmi_enable_notification_resp), diff --git a/lib/utils/mpxy/fdt_mpxy_rpmi_mbox.c b/lib/utils/mpxy/fdt_mpxy_rpmi_mbox.c index 84b7a67d..825ac9b3 100644 --- a/lib/utils/mpxy/fdt_mpxy_rpmi_mbox.c +++ b/lib/utils/mpxy/fdt_mpxy_rpmi_mbox.c @@ -299,6 +299,8 @@ int mpxy_rpmi_mbox_init(const void *fdt, int nodeoff, const struct fdt_match *ma /* Setup MPXY mbox client */ /* Channel ID*/ rmb->channel.channel_id = channel_id; + /* Set the owner domain */ + rmb->channel.owner_domain = &root; /* Callback for read RPMI attributes */ rmb->channel.read_attributes = mpxy_mbox_read_attributes; /* Callback for write RPMI attributes */ diff --git a/lib/utils/mpxy/fdt_mpxy_rpmi_mm.c b/lib/utils/mpxy/fdt_mpxy_rpmi_mm.c index d7176aca..7185f3f3 100644 --- a/lib/utils/mpxy/fdt_mpxy_rpmi_mm.c +++ b/lib/utils/mpxy/fdt_mpxy_rpmi_mm.c @@ -15,13 +15,20 @@ static struct rpmi_mm_get_attributes_rsp rsp; static struct mpxy_rpmi_service_data mm_srvcdata[] = { [0] = { + .id = RPMI_MM_SRV_ENABLE_NOTIFICATION, + .min_tx_len = sizeof(struct rpmi_enable_notification_req), + .max_tx_len = sizeof(struct rpmi_enable_notification_req), + .min_rx_len = sizeof(struct rpmi_enable_notification_resp), + .max_rx_len = sizeof(struct rpmi_enable_notification_resp), + }, + [1] = { .id = RPMI_MM_SRV_GET_ATTRIBUTES, .min_tx_len = 0, .max_tx_len = 0, .min_rx_len = sizeof(struct rpmi_mm_get_attributes_rsp), .max_rx_len = sizeof(struct rpmi_mm_get_attributes_rsp), }, - [1] = { + [2] = { .id = RPMI_MM_SRV_COMMUNICATE, .min_tx_len = sizeof(struct rpmi_mm_communicate_req), .max_tx_len = sizeof(struct rpmi_mm_communicate_req), diff --git a/lib/utils/reset/Kconfig b/lib/utils/reset/Kconfig index b2ac120e..8988548c 100644 --- a/lib/utils/reset/Kconfig +++ b/lib/utils/reset/Kconfig @@ -29,6 +29,10 @@ config FDT_RESET_LITEX depends on FDT_GPIO default n +config FDT_RESET_METANOIA + bool "Metanoia FDT reset driver" + default n + config FDT_RESET_RPMI bool "RPMI FDT reset driver" depends on FDT_MAILBOX && RPMI_MAILBOX diff --git a/lib/utils/reset/fdt_reset_metanoia.c b/lib/utils/reset/fdt_reset_metanoia.c new file mode 100644 index 00000000..6f75a893 --- /dev/null +++ b/lib/utils/reset/fdt_reset_metanoia.c @@ -0,0 +1,72 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2026 Metanoia Communications Inc. + * + */ + +#include <sbi/riscv_io.h> +#include <sbi/sbi_platform.h> +#include <sbi/sbi_system.h> +#include <sbi_utils/fdt/fdt_helper.h> +#include <sbi_utils/fdt/fdt_driver.h> + +#define SWRSTREQ_CTRL_REG_OFFSET 0x00 +#define SWRSTREQ_REG_OFFSET 0x04 + +struct reset_metanoia_data { + u8 *reg_base; +}; + +static struct reset_metanoia_data reset_data; + +static int metanoia_system_reset_check(u32 type, u32 reason) +{ + switch (type) { + case SBI_SRST_RESET_TYPE_WARM_REBOOT: + case SBI_SRST_RESET_TYPE_COLD_REBOOT: + return 1; + case SBI_SRST_RESET_TYPE_SHUTDOWN: + default: + return 0; + } +} + +static void metanoia_system_reset(u32 type, u32 reason) +{ + writew(0x1, reset_data.reg_base + SWRSTREQ_CTRL_REG_OFFSET); + writew(0x1, reset_data.reg_base + SWRSTREQ_REG_OFFSET); +} + +static struct sbi_system_reset_device metanoia_reset = { + .name = "metanoia-mt2824-reboot", + .system_reset_check = metanoia_system_reset_check, + .system_reset = metanoia_system_reset, +}; + +static int metanoia_reset_init(const void *fdt, int nodeoff, + const struct fdt_match *match) +{ + u64 reg_addr; + int rc; + + rc = fdt_get_node_addr_size(fdt, nodeoff, 0, ®_addr, NULL); + if (rc < 0 || !reg_addr) + return SBI_ENODEV; + + reset_data.reg_base = (u8 *)(ulong)reg_addr; + + sbi_system_reset_add_device(&metanoia_reset); + + return 0; +} + +static const struct fdt_match metanoia_reset_match[] = { + { .compatible = "metanoia,mt2824-reboot" }, + { /* sentinel */ } +}; + +const struct fdt_driver fdt_reset_metanoia = { + .match_table = metanoia_reset_match, + .init = metanoia_reset_init, +}; diff --git a/lib/utils/reset/objects.mk b/lib/utils/reset/objects.mk index 38b4e306..a9b6edad 100644 --- a/lib/utils/reset/objects.mk +++ b/lib/utils/reset/objects.mk @@ -20,6 +20,9 @@ libsbiutils-objs-$(CONFIG_FDT_RESET_HTIF) += reset/fdt_reset_htif.o carray-fdt_early_drivers-$(CONFIG_FDT_RESET_LITEX) += fdt_reset_litex libsbiutils-objs-$(CONFIG_FDT_RESET_LITEX) += reset/fdt_reset_litex.o +carray-fdt_early_drivers-$(CONFIG_FDT_RESET_METANOIA) += fdt_reset_metanoia +libsbiutils-objs-$(CONFIG_FDT_RESET_METANOIA) += reset/fdt_reset_metanoia.o + carray-fdt_early_drivers-$(CONFIG_FDT_RESET_SG2042_HWMON_MCU) += fdt_reset_sg2042_mcu libsbiutils-objs-$(CONFIG_FDT_RESET_SG2042_HWMON_MCU) += reset/fdt_reset_sg2042_hwmon_mcu.o diff --git a/lib/utils/suspend/fdt_suspend_andes_atcsmu.c b/lib/utils/suspend/fdt_suspend_andes_atcsmu.c index 072accc0..86cdf955 100644 --- a/lib/utils/suspend/fdt_suspend_andes_atcsmu.c +++ b/lib/utils/suspend/fdt_suspend_andes_atcsmu.c @@ -12,22 +12,37 @@ #include <sbi/sbi_ecall_interface.h> #include <sbi/sbi_hart.h> #include <sbi/sbi_system.h> +#include <sbi/sbi_timer.h> #include <sbi_utils/cache/fdt_cmo_helper.h> #include <sbi_utils/fdt/fdt_driver.h> #include <sbi_utils/fdt/fdt_helper.h> #include <sbi_utils/hsm/fdt_hsm_andes_atcsmu.h> -static int check_secondary_harts_sleep(u32 hartid, bool deep_sleep) +#define HART_SLEEP_TIMEOUT_MS 1000 + +static int wait_secondary_harts_sleep(u32 hartid, bool deep_sleep) { const struct sbi_domain *dom = &root; unsigned long i; u32 target; + struct atcsmu_sleep_arg arg; + + arg.deep_sleep = deep_sleep; - /* Ensure the secondary harts entering the corresponding sleep state */ + /* Wait for the secondary harts entering the corresponding sleep state */ sbi_hartmask_for_each_hartindex(i, dom->possible_harts) { target = sbi_hartindex_to_hartid(i); - if (target != hartid && !atcsmu_pcs_is_sleep(target, deep_sleep)) - return SBI_EFAIL; + if (target == hartid) + continue; + + arg.hartid = target; + if (!sbi_timer_waitms_until(atcsmu_hart_is_sleep, &arg, + HART_SLEEP_TIMEOUT_MS)) { + sbi_printf("ATCSMU: hart%u (PCS%u): timed out waiting for %s sleep\n", + target, target + 3, + deep_sleep ? "deep" : "light"); + return SBI_ETIMEOUT; + } } return SBI_OK; @@ -42,39 +57,45 @@ static int ae350_system_suspend_check(u32 sleep_type) static int ae350_system_suspend(u32 sleep_type, unsigned long addr) { u32 hartid = current_hartid(); + unsigned long saved_mie; int rc; /* Prevent the core leaving the WFI mode unexpectedly */ + saved_mie = csr_read(CSR_MIE); csr_write(CSR_MIE, 0); - /* - * Only allow the S-mode external interrupts (UART2 and RTC alarm) to - * wake up the primary hart - */ - csr_set(CSR_SIE, MIP_SEIP); + /* SMU wakes the primary hart on RTC alarm / UART2 */ atcsmu_set_wakeup_events(PCS_WAKEUP_RTC_ALARM_MASK | PCS_WAKEUP_UART2_MASK, hartid); if (sleep_type == SBI_SUSP_AE350_LIGHT_SLEEP) { - rc = check_secondary_harts_sleep(hartid, false); + rc = wait_secondary_harts_sleep(hartid, false); if (rc) - return rc; + goto err_restore_mie; + /* Clock-gated only: enable SEI to resume past the WFI */ + csr_set(CSR_MIE, MIP_SEIP); atcsmu_set_command(LIGHT_SLEEP_CMD, hartid); } else if (sleep_type == SBI_SUSP_SLEEP_TYPE_SUSPEND) { - rc = check_secondary_harts_sleep(hartid, true); + rc = wait_secondary_harts_sleep(hartid, true); if (rc) - return rc; + goto err_restore_mie; - atcsmu_set_command(DEEP_SLEEP_CMD, hartid); rc = atcsmu_set_reset_vector((ulong)ae350_enable_coherency_warmboot, hartid); if (rc) - return rc; + goto err_restore_mie; ae350_non_ret_save(sbi_scratch_thishart_ptr()); - fdt_cmo_llc_enable(false); + + /* No LLC is fine; only fail on real errors */ + rc = fdt_cmo_llc_enable(false); + if (rc && rc != SBI_ENODEV) + goto err_discard_save; + rc = fdt_cmo_llc_flush_all(); - if (rc) - return rc; + if (rc && rc != SBI_ENODEV) + goto err_enable_llc; + + atcsmu_set_command(DEEP_SLEEP_CMD, hartid); } ae350_disable_coherency(); @@ -84,6 +105,15 @@ static int ae350_system_suspend(u32 sleep_type, unsigned long addr) ae350_enable_coherency(); return SBI_OK; + +err_enable_llc: + fdt_cmo_llc_enable(true); +err_discard_save: + ae350_non_ret_discard(sbi_scratch_thishart_ptr()); +err_restore_mie: + csr_write(CSR_MIE, saved_mie); + + return rc; } static void ae350_system_resume(void) @@ -91,8 +121,10 @@ static void ae350_system_resume(void) u32 hartid = current_hartid(); u32 sleep_type = atcsmu_get_sleep_type(hartid); - if (sleep_type == SBI_SUSP_SLEEP_TYPE_SUSPEND) + if (sleep_type == SBI_SUSP_SLEEP_TYPE_SUSPEND) { fdt_cmo_llc_enable(true); + ae350_non_ret_restore(sbi_scratch_thishart_ptr()); + } } static struct sbi_system_suspend_device suspend_andes_atcsmu = { |
