+/* Init hash table csr_extra_hash to handle CSR. */
+static void
+riscv_init_csr_hash (const char *name,
+ unsigned address,
+ enum riscv_csr_class class,
+ enum riscv_priv_spec_class define_version,
+ enum riscv_priv_spec_class abort_version)
+{
+ struct riscv_csr_extra *entry, *pre_entry;
+ bfd_boolean need_enrty = TRUE;
+
+ pre_entry = NULL;
+ entry = (struct riscv_csr_extra *) str_hash_find (csr_extra_hash, name);
+ while (need_enrty && entry != NULL)
+ {
+ if (entry->csr_class == class
+ && entry->address == address
+ && entry->define_version == define_version
+ && entry->abort_version == abort_version)
+ need_enrty = FALSE;
+ pre_entry = entry;
+ entry = entry->next;
+ }
+
+ /* Duplicate setting for the CSR, just return and do nothing. */
+ if (!need_enrty)
+ return;
+
+ entry = XNEW (struct riscv_csr_extra);
+ entry->csr_class = class;
+ entry->address = address;
+ entry->define_version = define_version;
+ entry->abort_version = abort_version;
+ entry->next = NULL;
+
+ /* If the CSR hasn't been inserted in the hash table, then insert it.
+ Otherwise, attach the extra information to the entry which is already
+ in the hash table. */
+ if (pre_entry == NULL)
+ str_hash_insert (csr_extra_hash, name, entry, 0);
+ else
+ pre_entry->next = entry;
+}
+
+/* Return the suitable CSR address after checking the ISA dependency and
+ priv spec versions. */
+
+static unsigned int
+riscv_csr_address (const char *csr_name,
+ struct riscv_csr_extra *entry)
+{
+ struct riscv_csr_extra *saved_entry = entry;
+ enum riscv_csr_class csr_class = entry->csr_class;
+ bfd_boolean need_check_version = TRUE;
+ bfd_boolean result = TRUE;
+
+ switch (csr_class)
+ {
+ case CSR_CLASS_I:
+ result = riscv_subset_supports ("i");
+ break;
+ case CSR_CLASS_I_32:
+ result = (xlen == 32 && riscv_subset_supports ("i"));
+ break;
+ case CSR_CLASS_F:
+ result = riscv_subset_supports ("f");
+ need_check_version = FALSE;
+ break;
+ case CSR_CLASS_DEBUG:
+ need_check_version = FALSE;
+ break;
+ default:
+ as_bad (_("internal: bad RISC-V CSR class (0x%x)"), csr_class);
+ }
+
+ /* Don't report the ISA conflict when -mcsr-check isn't set. */
+ if (riscv_opts.csr_check && !result)
+ as_warn (_("Invalid CSR `%s' for the current ISA"), csr_name);
+
+ while (entry != NULL)
+ {
+ if (!need_check_version
+ || (default_priv_spec >= entry->define_version
+ && default_priv_spec < entry->abort_version))
+ {
+ /* Find the suitable CSR according to the specific version. */
+ return entry->address;
+ }
+ entry = entry->next;
+ }
+
+ /* We can not find the suitable CSR address according to the privilege
+ version. Therefore, we use the last defined value. Report the warning
+ only when the -mcsr-check is set. Enable the -mcsr-check is recommended,
+ otherwise, you may get the unexpected CSR address. */
+ if (riscv_opts.csr_check)
+ {
+ const char *priv_name = riscv_get_priv_spec_name (default_priv_spec);
+
+ if (priv_name != NULL)
+ as_warn (_("Invalid CSR `%s' for the privilege spec `%s'"),
+ csr_name, priv_name);
+ }
+
+ return saved_entry->address;
+}
+
+/* Once the CSR is defined, including the old privilege spec, then we call
+ riscv_csr_class_check and riscv_csr_version_check to do the further checking
+ and get the corresponding address. Return -1 if the CSR is never been
+ defined. Otherwise, return the address. */
+
+static unsigned int
+reg_csr_lookup_internal (const char *s)
+{
+ struct riscv_csr_extra *r =
+ (struct riscv_csr_extra *) str_hash_find (csr_extra_hash, s);
+
+ if (r == NULL)
+ return -1U;
+
+ /* We just report the warning when the CSR is invalid. "Invalid CSR" means
+ the CSR was defined, but isn't allowed for the current ISA setting or
+ the privilege spec. If the CSR is never been defined, then assembler
+ will regard it as a "Unknown CSR" and report error. If user use number
+ to set the CSR, but over the range (> 0xfff), then assembler will report
+ "Improper CSR" error for it. */
+ return riscv_csr_address (s, r);
+}
+