satp

Supervisor Address Translation and Protection

Controls the translation mode in (H)S-mode and U-mode, and holds the current ASID and page table base pointer.

Attributes

Defining Extension

  • S, version >= 0

CSR Address

0x180

Length

32-bit

64-bit

Privilege Mode

S

Format

This CSR format changes dynamically.

svg
svg

Field Summary

Name Location Type Reset Value

MODE

31

63:60

RW-R

UNDEFINED_LEGAL

ASID

30:22

59:44

RW-R

UNDEFINED_LEGAL

PPN

21:0

43:0

RW-R

UNDEFINED_LEGAL

Fields

MODE

Location
  • 31 when CSR[mstatus].SXL == 0

  • 63:60 when CSR[mstatus].SXL == 1

Description

Translation Mode

Controls the current translation mode according to the table below.

[separator="!",%autowidth]
!===
! Value ! Name ! Description

! 0 ! Bare a! No translation → virtual address == physical address
!===

Any other value shall be ignored on a write.

Type

RW-R

Reset value

UNDEFINED_LEGAL

ASID

Location
  • 30:22 when CSR[mstatus].SXL == 0

  • 59:44 when CSR[mstatus].SXL == 1

Description

Address Space ID

Type

RW-R

Reset value

UNDEFINED_LEGAL

PPN

Location
  • 21:0 when CSR[mstatus].SXL == 0

  • 43:0 when CSR[mstatus].SXL == 1

Description

Physical Page Number

The physical address of the active root page table is PPN << 12.

Can only hold values that correspond to a valid page table base, which
will be implementation-dependent.

Type

RW-R

Reset value

UNDEFINED_LEGAL

Software write

This CSR may store a value that is different from what software attempts to write.

When a software write occurs (e.g., through csrrw), the following determines the written value:

MODE = if (SATP_MODE_BARE) {
  if (csr_value.MODE == 0) {
    # In Bare, ASID and PPN must be zero, else the entire write is ignored
    if (csr_value.ASID == 0 && csr_value.PPN == 0) {
      if (CSR[satp].MODE != 0) {
        # changes *to* Bare mode take effect immediately without needing sfence.vma
        # thus, an implicit sfence.vma occurs now
        VmaOrderType order_type;
        order_type.global = true;
        order_pgtbl_writes_before_vmafence(order_type);

        invalidate_translations(order_type);

        order_pgtbl_reads_after_vmafence(order_type);
      }
      return csr_value.MODE;
    } else {
      return UNDEFINED_LEGAL_DETERMINISTIC;
    }
  }
}
else if (implemented?(ExtensionName::Sv39) && csr_value.MODE == 8) {
  if (CSR[satp].MODE == 0) {
    # changes *from* Bare mode take effect immediately without needing sfence.vma
    # thus, an implicit sfence.vma occurs now
    VmaOrderType order_type;
    order_type.global = true;
    order_pgtbl_writes_before_vmafence(order_type);

    invalidate_translations(order_type);

    order_pgtbl_reads_after_vmafence(order_type);
  }
  return csr_value.MODE;
}
else if (implemented?(ExtensionName::Sv48) && csr_value.MODE == 9) {
  if (CSR[satp].MODE == 0) {
    # changes *from* Bare mode take effect immediately without needing sfence.vma
    # thus, an implicit sfence.vma occurs now
    VmaOrderType order_type;
    order_type.global = true;
    order_pgtbl_writes_before_vmafence(order_type);

    invalidate_translations(order_type);

    order_pgtbl_reads_after_vmafence(order_type);
  }

  return csr_value.MODE;
}
else if (implemented?(ExtensionName::Sv57) && csr_value.MODE == 10) {
  if (CSR[satp].MODE == 0) {
    # changes *from* Bare mode take effect immediately without needing sfence.vma
    # thus, an implicit sfence.vma occurs now
    VmaOrderType order_type;
    order_type.global = true;
    order_pgtbl_writes_before_vmafence(order_type);

    invalidate_translations(order_type);

    order_pgtbl_reads_after_vmafence(order_type);
  }

  return csr_value.MODE;
}
else {
  return UNDEFINED_LEGAL_DETERMINISTIC;
}

ASID = if (csr_value.MODE == 0) {
  # when MODE == Bare, PPN and ASID must be zero
  if (csr_value.ASID == 0 && csr_value.PPN == 0) {
    return csr_value.ASID;
  } else {
    return UNDEFINED_LEGAL_DETERMINISTIC;
  }
} else {
  XReg shamt = (XLEN == 32 || (CSR[mstatus].SXL == $bits(XRegWidth::XLEN32))) ? 9 : 16;
  XReg all_ones = ((1 << shamt) - 1);
  XReg largest_allowed_asid = (1 << shamt) - 1;

  if (csr_value.ASID == all_ones) {
    # the specification states that if all 1's are written to the ASID field, then
    # you must return the largest asid
    return largest_allowed_asid;
  } else if (csr_value.ASID > largest_allowed_asid) {
    # ... but is silent on what happens on any other illegal value
    return UNDEFINED_LEGAL_DETERMINISTIC;
  } else {
    # unrestricted
    return csr_value.ASID;
  }
}

PPN = if (csr_value.MODE == 0) {
  # when MODE == Bare, PPN and ASID must be zero
  if (csr_value.ASID == 0 && csr_value.PPN == 0) {
    return csr_value.PPN;
  } else {
    return UNDEFINED_LEGAL_DETERMINISTIC;
  }
} else {
  # unrestricted
  return csr_value.PPN;
}