Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Hypervisor extension #41

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft

Implement Hypervisor extension #41

wants to merge 4 commits into from

Conversation

alexmikhalevich
Copy link
Contributor

Hypervisor extension implementation.

@alexmikhalevich alexmikhalevich self-assigned this May 24, 2023
@edubart edubart added enhancement New feature or request epic labels May 24, 2023
@edubart edubart linked an issue May 24, 2023 that may be closed by this pull request
src/riscv-constants.h Outdated Show resolved Hide resolved
src/interpret.cpp Outdated Show resolved Hide resolved
src/interpret.cpp Outdated Show resolved Hide resolved
src/interpret.cpp Outdated Show resolved Hide resolved
src/interpret.cpp Outdated Show resolved Hide resolved
src/riscv-constants.h Outdated Show resolved Hide resolved
src/translate-virtual-address.h Show resolved Hide resolved
src/translate-virtual-address.h Show resolved Hide resolved
src/translate-virtual-address.h Outdated Show resolved Hide resolved
src/translate-virtual-address.h Show resolved Hide resolved
src/interpret.cpp Show resolved Hide resolved
src/interpret.cpp Outdated Show resolved Hide resolved
src/interpret.cpp Outdated Show resolved Hide resolved
enabled_ints = ~a.read_mideleg();
}
break;
auto mstatus = a.read_mstatus();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I miss the comments explaining what was going on that you added and were lost here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I'll do this after we settle on the final decision regarding this discussion

static inline uint32_t get_enabled_irq_mask_HS(STATE_ACCESS &a, uint64_t sie, uint8_t priv) {
uint32_t deleg = a.read_mideleg() & ~a.read_hideleg();
uint32_t enabled_HS = is_virtual_mode(a) || priv < PRV_HS || (priv == PRV_HS && sie);
return (deleg & -enabled_HS);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't you do an & sie in the final return? Checking just if sie has any bit set feels odd for me, actually it should check if the corresponding deleg and sie is bits are enabled. Same apply for other get_enabled_irq_mask_* functions. I think these functions may need some rewriting considering this and the previous comments.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is done at the end of the get_pending_irq_mask method here. maybe the naming of those methods is misleading, do you have any ideas what would be a better naming here?

src/interpret.cpp Outdated Show resolved Hide resolved
src/interpret.cpp Outdated Show resolved Hide resolved
src/interpret.cpp Outdated Show resolved Hide resolved
src/interpret.cpp Outdated Show resolved Hide resolved
PRV_HS = 2, ///< Hypervisor-extended supervisor mode
PRV_M = 3 ///< Machine mode
PRV_U = 0, ///< User mode
PRV_HS = 1, ///< Supervisor mode
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After reading the spec and the code, I feel that PRV_HS should be renamed back to PRV_S. Because when VRT is 1, we are in VS mode, and the checks have HS in them, so it is misleading.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Copy link
Contributor

@edubart edubart left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I commented on things that may have to be improved after reading the spec. Despite having some issues this is already working well, most issues commented are edge cases or minor details. Great work @alexmikhalevich !


template <typename STATE_ACCESS>
static execute_status write_csr_vsstatus(STATE_ACCESS &a, uint64_t val) {
a.write_vsstatus(val);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A write filter is missing here, we should not allow to writes to WPRI fields of vsstatus to preserve backward compability.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FS and SD fields are not being handled here, they should be handled is similar way to mstatus. While it was handled in write_csr_sstatus, I think you should move the vsstatus related logic from write_csr_sstatus to here and call this function.

When doing this, FS field should always mark state as dirty, like is done in write_csr_mstatus and according to the spec for both mstatus and vsstatus:

Modifying the floating-point state when V=1 causes both fields to be set to 3 (Dirty).

Meaning if vsstatus.FS is set as dirty, then mstatus.FS will also be set as dirty, and both vsstatus.SD and mstatus.SD must also be set.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

if (is_virtual_mode(a)) {
uint64_t vsstatus = a.read_vsstatus() & VSSTATUS_R_MASK;
vsstatus = (vsstatus & ~VSSTATUS_W_MASK) | (val & VSSTATUS_W_MASK);
if ((vsstatus & MSTATUS_FS_MASK) == MSTATUS_FS_MASK) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Despite MSTATUS_FS_MASK being set, it's not ever read. You will find multiple of the following checks in the code:

    // If FS is OFF, attempts to read or write the float state will cause an illegal instruction exception.
    if (unlikely((mstatus & MSTATUS_FS_MASK) == MSTATUS_FS_OFF)) {
        return execute_status::failure;
    }

They all should be extended taking this in consideration:

When V=1, both vsstatus.FS and the HS-level sstatus.FS are in effect. Attempts to execute a
floating-point instruction when either field is 0 (Off) raise an illegal-instruction exception.

This mean a exception should be raised if either mstatus.FS is OFF, or vsstatus.FS is Off while in virtual mode.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

src/interpret.cpp Outdated Show resolved Hide resolved
src/interpret.cpp Outdated Show resolved Hide resolved
return execute_HSV_H(a, pc, mcycle, insn);
case insn_privileged_funct7::HSV_W:
return execute_HSV_W(a, pc, mcycle, insn);
// case insn_privileged_funct7::HLV_WU: // TODO
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be implemented.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The case still commented.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right, that's because this insn is processed in another switch. I removed this comment.

// hgatp can only be changed when virtual mode is off, and when virtual mode is off
// the address translation function will not use it. Enabling virtual mode will trigger
// a TLB shootdown.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is missing:

When mstatus.TVM=1, attempts to read or write hgatp while executing
in HS-mode will raise an illegal instruction exception.

Setting TVM=1 prevents HS-mode from accessing hgatp or executing HFENCE.GVMA or HIN-
VAL.GVMA

The above is also missing in read_csr_hgatp.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

// a TLB shootdown.

auto mode = val >> 60;
if (mode == 0 || (mode >= 8 && mode <= 10)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use the enums SATP_MODE_BARE, SATP_MODE_SV39 and SATP_MODE_SV57 instead of these constants here.
And SATP_MODE_SHIFT for the 60 above.

I also think a switch makes the code more clear, like in write_csr_satp. Same applies to write_csr_vsatp, it could have used a switch.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@@ -1975,7 +2687,10 @@ static NO_INLINE execute_status write_csr_satp(STATE_ACCESS &a, uint64_t val) {
}

uint64_t old_satp = a.read_satp();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an extra unnecessary read of satp here when read_iflags_VRT is true, you could move this read in the else of the bellow if.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@@ -1975,7 +2687,10 @@ static NO_INLINE execute_status write_csr_satp(STATE_ACCESS &a, uint64_t val) {
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TSR and TVM fields of mstatus affect execution only in HS-mode, not in VS-mode. The TW
field affects execution in all modes except M-mode.

This is not respected here, TVM is affecting VS-mode.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

auto mode = current_mode(a);
auto priv = (mode & ACCESS_MODE_PRV_MASK) >> ACCESS_MODE_PRV_SHIFT;
uint64_t mstatus = a.read_mstatus();
if (unlikely(priv == PRV_U || (priv == PRV_S && (mstatus & MSTATUS_TVM_MASK)))) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TSR and TVM fields of mstatus affect execution only in HS-mode, not in VS-mode.

setting TVM=1 does not cause traps for accesses to vsatp or instructions
HFENCE.VVMA or HINVAL.VVMA, or for any actions taken in VS-mode

This is not respected here, TVM is affecting VS-mode.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

uint64_t old_vsstatus = a.read_vsstatus() & VSSTATUS_R_MASK;
uint64_t vsstatus = (old_vsstatus & ~VSSTATUS_W_MASK) | (val & VSSTATUS_W_MASK);
auto mstatus = a.read_mstatus();
if ((vsstatus & MSTATUS_FS_MASK) == MSTATUS_FS_MASK) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this if should have been (vsstatus & MSTATUS_FS_MASK) != MSTATUS_FS_OFF.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

vsstatus |= MSTATUS_FS_DIRTY;
vsstatus |= MSTATUS_SD_MASK;
} else {
mstatus &= ~MSTATUS_SD_MASK;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MSTATUS_FS_MASK could still be dirty, and SD cannot be cleared in this case. So I think this line should be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@@ -2198,6 +2924,12 @@ static inline execute_status write_csr_fflags(STATE_ACCESS &a, uint64_t val) {
if (unlikely((mstatus & MSTATUS_FS_MASK) == MSTATUS_FS_OFF)) {
return execute_status::failure;
}
if (a.read_iflags_VRT()) {
auto vsstatus = a.read_vsstatus();
if (unlikely((vsstatus & MSTATUS_FS_MASK) == MSTATUS_FS_OFF)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is correct, however there are other places missing the same check, such as:

  • read_csr_fflags
  • read_csr_frm
  • read_csr_fcsr
  • the two switches in execute_insn

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@@ -5407,17 +6608,22 @@ template <typename STATE_ACCESS>
static FORCE_INLINE fetch_status fetch_translate_pc_slow(STATE_ACCESS &a, uint64_t &pc, uint64_t vaddr,
unsigned char **phptr) {
uint64_t paddr{};
MODE_constants access_mode = get_current_memory_access_mode(a);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When MPRV=1, load
and store memory addresses are translated and protected, and endianness is applied, as though
the current privilege mode were set to MPP. Instruction address-translation and protection are
unaffected by the setting of MPRV.

get_current_memory_access_mode will always consider MRPV, but for fetch MRPV should be ignored.

Our old code respected this, with this (in translate_virtual_address):

    // When MPRV is set, data loads and stores use privilege in MPP
    // instead of the current privilege level (code access is unaffected)
    if (xwr_shift != PTE_XWR_X_SHIFT && (mstatus & MSTATUS_MPRV_MASK)) {
        priv = (mstatus & MSTATUS_MPP_MASK) >> MSTATUS_MPP_SHIFT;
    }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

}

// when checking the U bit for G translation, the current privilege mode is always taken to be U-mode
if constexpr (TRANSLATION_MODE == TRANSLATION_G) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After making all G translations to use priv=PRV_U, this if could just be removed to simplify this code. The else branch already has the same logic.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@alexmikhalevich alexmikhalevich force-pushed the hypervisor branch 2 times, most recently from 2f9548f to aeef15b Compare October 15, 2023 00:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request epic
Projects
Status: In Progress
Development

Successfully merging this pull request may close these issues.

RISC-V Hypervisor extension support
4 participants