/* * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2024 Rivos Inc. * * Authors: * Clément Léger */ #include #include #include #include #include #include #include #include #include #include #include /** Offset of pointer to FWFT HART state in scratch space */ static unsigned long fwft_ptr_offset; #define fwft_get_hart_state_ptr(__scratch) \ sbi_scratch_read_type((__scratch), void *, fwft_ptr_offset) #define fwft_thishart_state_ptr() \ fwft_get_hart_state_ptr(sbi_scratch_thishart_ptr()) #define fwft_set_hart_state_ptr(__scratch, __phs) \ sbi_scratch_write_type((__scratch), void *, fwft_ptr_offset, (__phs)) #define MIS_DELEG (1UL << CAUSE_MISALIGNED_LOAD | 1UL << CAUSE_MISALIGNED_STORE) struct fwft_config; struct fwft_feature { enum sbi_fwft_feature_t id; int (*supported)(struct fwft_config *conf); int (*set)(struct fwft_config *conf, unsigned long value); int (*get)(struct fwft_config *conf, unsigned long *value); }; struct fwft_config { const struct fwft_feature *feature; unsigned long flags; }; struct fwft_hart_state { unsigned int config_count; struct fwft_config configs[]; }; static const unsigned long fwft_defined_features[] = { SBI_FWFT_MISALIGNED_EXC_DELEG, SBI_FWFT_LANDING_PAD, SBI_FWFT_SHADOW_STACK, SBI_FWFT_DOUBLE_TRAP, SBI_FWFT_PTE_AD_HW_UPDATING, }; static bool fwft_is_defined_feature(enum sbi_fwft_feature_t feature) { int i; for (i = 0; i < array_size(fwft_defined_features); i++) { if (fwft_defined_features[i] == feature) return true; } return false; } static int fwft_misaligned_delegation_supported(struct fwft_config *conf) { if (!misa_extension('S')) return SBI_ENOTSUPP; return SBI_OK; } static int fwft_set_misaligned_delegation(struct fwft_config *conf, unsigned long value) { if (value == 1) csr_set(CSR_MEDELEG, MIS_DELEG); else if (value == 0) csr_clear(CSR_MEDELEG, MIS_DELEG); else return SBI_EINVAL; return SBI_OK; } static int fwft_get_misaligned_delegation(struct fwft_config *conf, unsigned long *value) { *value = (csr_read(CSR_MEDELEG) & MIS_DELEG) != 0; return SBI_OK; } static int fwft_adue_supported(struct fwft_config *conf) { if (!sbi_hart_has_extension(sbi_scratch_thishart_ptr(), SBI_HART_EXT_SVADU)) return SBI_ENOTSUPP; return SBI_OK; } static int fwft_set_adue(struct fwft_config *conf, unsigned long value) { if (value == 1) #if __riscv_xlen == 32 csr_set(CSR_MENVCFGH, ENVCFG_ADUE >> 32); #else csr_set(CSR_MENVCFG, ENVCFG_ADUE); #endif else if (value == 0) #if __riscv_xlen == 32 csr_clear(CSR_MENVCFGH, ENVCFG_ADUE >> 32); #else csr_clear(CSR_MENVCFG, ENVCFG_ADUE); #endif else return SBI_EINVAL; return SBI_OK; } static int fwft_get_adue(struct fwft_config *conf, unsigned long *value) { unsigned long cfg; #if __riscv_xlen == 32 cfg = csr_read(CSR_MENVCFGH) & (ENVCFG_ADUE >> 32); #else cfg = csr_read(CSR_MENVCFG) & ENVCFG_ADUE; #endif *value = cfg != 0; return SBI_OK; } static struct fwft_config* get_feature_config(enum sbi_fwft_feature_t feature) { int i; struct fwft_hart_state *fhs = fwft_thishart_state_ptr(); if (feature & SBI_FWFT_GLOBAL_FEATURE_BIT) return NULL; for (i = 0; i < fhs->config_count; i++){ if (feature == fhs->configs[i].feature->id) return &fhs->configs[i]; } return NULL; } static int fwft_get_feature(enum sbi_fwft_feature_t feature, struct fwft_config **conf) { int ret; struct fwft_config *tconf; tconf = get_feature_config(feature); if (!tconf) { if (fwft_is_defined_feature(feature)) return SBI_ENOTSUPP; return SBI_EDENIED; } if (tconf->feature->supported) { ret = tconf->feature->supported(tconf); if (ret) return ret; } *conf = tconf; return SBI_SUCCESS; } int sbi_fwft_set(enum sbi_fwft_feature_t feature, unsigned long value, unsigned long flags) { int ret; struct fwft_config *conf; ret = fwft_get_feature(feature, &conf); if (ret) return ret; if ((flags & ~SBI_FWFT_SET_FLAG_LOCK) != 0) return SBI_EINVAL; if (conf->flags & SBI_FWFT_SET_FLAG_LOCK) return SBI_EDENIED; ret = conf->feature->set(conf, value); if (ret) return ret; conf->flags = flags; return SBI_OK; } int sbi_fwft_get(enum sbi_fwft_feature_t feature, unsigned long *out_val) { int ret; struct fwft_config *conf; ret = fwft_get_feature(feature, &conf); if (ret) return ret; return conf->feature->get(conf, out_val); } static const struct fwft_feature features[] = { { .id = SBI_FWFT_MISALIGNED_EXC_DELEG, .supported = fwft_misaligned_delegation_supported, .set = fwft_set_misaligned_delegation, .get = fwft_get_misaligned_delegation, }, { .id = SBI_FWFT_PTE_AD_HW_UPDATING, .supported = fwft_adue_supported, .set = fwft_set_adue, .get = fwft_get_adue, }, }; int sbi_fwft_init(struct sbi_scratch *scratch, bool cold_boot) { int i; struct fwft_hart_state *fhs; if (cold_boot) { fwft_ptr_offset = sbi_scratch_alloc_type_offset(void *); if (!fwft_ptr_offset) return SBI_ENOMEM; } fhs = fwft_get_hart_state_ptr(scratch); if (!fhs) { fhs = sbi_zalloc(sizeof(fhs) + array_size(features) * sizeof(struct fwft_config)); if (!fhs) return SBI_ENOMEM; fhs->config_count = array_size(features); for (i = 0; i < array_size(features); i++) fhs->configs[i].feature = &features[i]; fwft_set_hart_state_ptr(scratch, fhs); } return 0; }