class SystemCallSequence(Sequence): def __init__(self, aGenThread): super().__init__(aGenThread) self._mAssemblyHelper = AssemblyHelperRISCV(self) self._mDataBlockAddrRegIndex = None self._mActionCodeRegIndex = None self._mAppRegSize = 64 def generate(self, **kwargs): (handlers_set_name, valid) = self.getOption("handlers_set") if valid and (handlers_set_name == "Fast"): self.error( "Fast exception handlers are enabled. SystemCallSequence only " "supports using comprehensive exception handlers." ) self._mAppRegSize = self.getGlobalState("AppRegisterWidth") function = kwargs.setdefault("Function", "SwitchPrivilegeLevel") if function == "SwitchPrivilegeLevel": ret_code = self._switchPrivilegeLevel(kwargs) else: self.error("SystemCallSequence does not support the %s function" % function) self._processReturnCode(ret_code, function) # Execute a privilege level switch. # # @params aParams A dictionary of optional parameters. # PrivilegeLevel: The target privilege level. Permissible values are # 'U', 'S', 'M', 0, 1, 3 or 'Random'; defaults to 'Random'. # TargetAddr: The address at which to resume execution after # completing the privilege level switch. Permissible values are # any 64-bit integer; defaults to a randomly generated valid # instruction address. # SkipAddrValidation: Specifies whether to skip validating a # specified TargetAddr value. Permissible values are 0 or 1; # defaults to 0. If SkipAddrValidation is 0, the TargetAddr value # is checked to determine whether it is a valid instruction # address. If it isn't, an alternative address is randomly # generated. If SkipAddrValidation is 1, the target address value # is always used as is. # AddrChoicesModID: Specifies ID of choices modification set to be # applied when generating the target address in the event no # TargetAddr value is provided or the value provided is invalid. # Permissible values are any valid choices modification set ID. # InterruptMask: Value to set the xstatus.xIE field to for the target # privilege level. Permissible values are 0, 1, 'Same', 'Flip' or # 'Random'; defaults to 'Random'. # SUM: *** Not currently supported! *** Value to set the xstatus.SUM # field to for the target privilege level. Permissible values are # 0, 1, 'Same', 'Flip' or 'Random'; defaults to 'Random'. # MXR: *** Not currently supported! *** Value to set the xstatus.MXR # field to for the target privilege level. Permissible values are # 0, 1, 'Same', 'Flip' or 'Random'; defaults to 'Random'. # MPRV: *** Not currently supported! *** Value to set the # mstatus.MPRV field to for the target privilege level. # Permissible values are 0, 1, 'Same', 'Flip' or 'Random'; # defaults to 'Random'. As this field is only relevant to M mode, # it is ignored if the target privilege level is not M. def _switchPrivilegeLevel(self, aParams): except_request_results = self.exceptionRequest("SystemCall", aParams) ret_code = except_request_results["RetCode"] if ret_code != 0: return ret_code self._assignRegisters( except_request_results["DataBlockAddrRegIndex"], except_request_results["ActionCodeRegIndex"], ) load_gpr64_seq = LoadGPR64(self.genThread) load_gpr64_seq.load( self._mDataBlockAddrRegIndex, except_request_results["DataBlockAddr"], ) self._genPrivilegeLevelSwitchInstructions( except_request_results["InstrSeqCode"], except_request_results["PrivilegeLevel"], except_request_results["TargetAddr"], except_request_results["IntermediateRetAddr"], ) self._unassignRegisters() self.genSequence("UpdatePeState", {"RecordId": except_request_results["RecordId"]}) self.updateVm() return ret_code # Output appropriate messages based on the return code. Fail if the return # code is not 0 and NoSkip is specified. # # @param aRetCode The return code from the system call operation. # @param aFunction The operation performed by the system call. def _processReturnCode(self, aRetCode, aFunction): if aRetCode == 0: self.notice("SystemCallSequence request %s processed succesfully" % aFunction) else: self.notice( "SystemCallSequence request %s could not be processed " "successfully; return code %d" % (aFunction, aRetCode) ) (no_skip, valid) = self.genThread.getOption("NoSkip") if valid and (no_skip == 1) and (aRetCode != 0): self.error( "Unable to process SystemCallSequence request and NoSkip option was specified" ) # Generate the instructions required to execute the privilege level switch. # # @param aInstrSeqCode A value indicating the instructions that need to be # generated. # @param aTargetPrivLevel The target privilege level. # @param aTargetAddr The address at which to resume execution after # completing the privilege level switch. # @param aIntermediateRetAddr The return address for the first ECALL of a # 2 ECALL sequence def _genPrivilegeLevelSwitchInstructions( self, aInstrSeqCode, aTargetPrivLevel, aTargetAddr, aIntermediateRetAddr, ): if aInstrSeqCode == 0: # Do nothing pass elif aInstrSeqCode == 1: # Generate 1 ECALL instruction action_code = 2 # Load From Data Block self._mAssemblyHelper.genMoveImmediate(self._mActionCodeRegIndex, action_code) self._genEcall() elif aInstrSeqCode == 2: # Generate xRET instruction self._genXRet(aTargetPrivLevel, aTargetAddr) elif aInstrSeqCode == 3: # Generate 2 ECALL instructions action_code = 1 # Return to S Mode self._mAssemblyHelper.genMoveImmediate(self._mActionCodeRegIndex, action_code) self._genEcall() self.setPEstate("PrivilegeLevel", 1) self.setPEstate("PC", aIntermediateRetAddr) # For a 2 ECALL sequence, the data block contains a return address # for the first ECALL as the first entry; after the first ECALL, we # increment the data block address pointer to allow loading the # data block as usual in the second ECALL self._genIncrementDataBlockPointer() action_code = 2 # Load From Data Block self._mAssemblyHelper.genMoveImmediate(self._mActionCodeRegIndex, action_code) self._genEcall() else: self.error("Unexpected InstrSeqCode value %d" % aInstrSeqCode) # Assign registers to be used for executing the privilege level switch so # that they can be referenced later. # # @param aDataBlockRegIndex The index of the register used for storing the # data block address. # @param aActionCodeRegIndex The index of the register used for storing # the action code. def _assignRegisters(self, aDataBlockRegIndex, aActionCodeRegIndex): self._mDataBlockAddrRegIndex = aDataBlockRegIndex self._mActionCodeRegIndex = aActionCodeRegIndex # Clear register assignments. def _unassignRegisters(self): self._mActionCodeRegIndex = None self._mDataBlockAddrRegIndex = None # Generate an ECALL instruction. def _genEcall(self): self.genInstruction("ECALL##RISCV") # Generate an xRET instruction. # # @param aTargetPrivLevel The target privilege level. # @param aTargetAddr The address at which to resume execution after # completing the privilege level switch. def _genXRet(self, aTargetPrivLevel, aTargetAddr): priv_level = self.getPEstate("PrivilegeLevel") if priv_level == 1: priv_level_prefix = "S" elif priv_level == 3: priv_level_prefix = "M" else: self.error( "Unexpected request to execute xRET instruction to transition " "from privilege level %s to privilege level %s" % (priv_level, aTargetPrivLevel) ) # The action code register is used as a scratch register here to reduce # the number of required registers self._genLoadGPR(self._mActionCodeRegIndex, self._mDataBlockAddrRegIndex, 0) self._mAssemblyHelper.genWriteSystemRegister( ("%sstatus" % priv_level_prefix.lower()), self._mActionCodeRegIndex ) self._genLoadGPR(self._mActionCodeRegIndex, self._mDataBlockAddrRegIndex, 1) self._mAssemblyHelper.genWriteSystemRegister( ("%sepc" % priv_level_prefix.lower()), self._mActionCodeRegIndex ) self._genLoadGPR(self._mActionCodeRegIndex, self._mDataBlockAddrRegIndex, 2) self._mAssemblyHelper.genWriteSystemRegister("satp", self._mActionCodeRegIndex) self.genInstruction(("%sRET##RISCV" % priv_level_prefix), {"NoRestriction": 1}) # Generate an instruction to increment the data block address pointer. def _genIncrementDataBlockPointer(self): if self._mAppRegSize == 32: self._mAssemblyHelper.genAddImmediate(self._mDataBlockAddrRegIndex, 4) else: self._mAssemblyHelper.genAddImmediate(self._mDataBlockAddrRegIndex, 8) # Generate instruction to load a single GPR # # @param aDestReg - index of destination GPR # @param aAddrReg - index of GPR containing base address # @param aOffset - offset from base address def _genLoadGPR(self, aDestReg, aAddrReg, aOffset=0): if self._mAppRegSize == 32: self.genInstruction( "LW##RISCV", { "rd": aDestReg, "rs1": aAddrReg, "simm12": aOffset * 4, "NoRestriction": 1, }, ) else: self.genInstruction( "LD##RISCV", { "rd": aDestReg, "rs1": aAddrReg, "simm12": aOffset * 8, "NoRestriction": 1, }, )
def generate(self, **kargs): assembly_helper = AssemblyHelperRISCV(self) # TODO(Noah): Test branch instruction generation methods when there is time to do so. (dest_reg_index, src_reg_index, src_reg_index_2) = self.getRandomGPRs(3, exclude='0') load_gpr64_seq = LoadGPR64(self.genThread) src_reg_val = RandomUtils.random64() load_gpr64_seq.load(src_reg_index, src_reg_val) src_reg_val_2 = RandomUtils.random64() load_gpr64_seq.load(src_reg_index_2, src_reg_val_2) MAX_GPR_VAL = 0xFFFFFFFFFFFFFFFF imm_val = RandomUtils.random32(0, 2047) assembly_helper.genMoveImmediate(dest_reg_index, imm_val) dest_reg_val = imm_val exception_handlers_test_utils.assertGprHasValue( self, dest_reg_index, dest_reg_val) shift_amount = RandomUtils.random32(0, 63) assembly_helper.genShiftLeftImmediate(dest_reg_index, shift_amount) dest_reg_val = (dest_reg_val << shift_amount) & MAX_GPR_VAL exception_handlers_test_utils.assertGprHasValue( self, dest_reg_index, dest_reg_val) shift_amount = RandomUtils.random32(0, 63) assembly_helper.genShiftRightImmediate(dest_reg_index, shift_amount) dest_reg_val = (dest_reg_val >> shift_amount) & MAX_GPR_VAL exception_handlers_test_utils.assertGprHasValue( self, dest_reg_index, dest_reg_val) shift_amount = RandomUtils.random32(0, 63) assembly_helper.genShiftLeftImmediate(dest_reg_index, shift_amount, aSrcRegIndex=src_reg_index) dest_reg_val = (src_reg_val << shift_amount) & MAX_GPR_VAL exception_handlers_test_utils.assertGprHasValue( self, dest_reg_index, dest_reg_val) shift_amount = RandomUtils.random32(0, 63) assembly_helper.genShiftRightImmediate(dest_reg_index, shift_amount, aSrcRegIndex=src_reg_index) dest_reg_val = (src_reg_val >> shift_amount) & MAX_GPR_VAL exception_handlers_test_utils.assertGprHasValue( self, dest_reg_index, dest_reg_val) imm_val = RandomUtils.random32(0, 2047) assembly_helper.genAndImmediate(dest_reg_index, imm_val) dest_reg_val &= imm_val exception_handlers_test_utils.assertGprHasValue( self, dest_reg_index, dest_reg_val) imm_val = RandomUtils.random32(0, 2047) assembly_helper.genAndImmediate(dest_reg_index, imm_val, aSrcRegIndex=src_reg_index) dest_reg_val = src_reg_val & imm_val exception_handlers_test_utils.assertGprHasValue( self, dest_reg_index, dest_reg_val) imm_val = RandomUtils.random32(0, 2047) assembly_helper.genOrImmediate(dest_reg_index, imm_val) dest_reg_val |= imm_val exception_handlers_test_utils.assertGprHasValue( self, dest_reg_index, dest_reg_val) imm_val = RandomUtils.random32(0, 2047) assembly_helper.genOrImmediate(dest_reg_index, imm_val, aSrcRegIndex=src_reg_index) dest_reg_val = src_reg_val | imm_val exception_handlers_test_utils.assertGprHasValue( self, dest_reg_index, dest_reg_val) imm_val = RandomUtils.random32(0, 2047) assembly_helper.genXorImmediate(dest_reg_index, imm_val) dest_reg_val ^= imm_val exception_handlers_test_utils.assertGprHasValue( self, dest_reg_index, dest_reg_val) imm_val = RandomUtils.random32(0, 2047) assembly_helper.genXorImmediate(dest_reg_index, imm_val, aSrcRegIndex=src_reg_index) dest_reg_val = src_reg_val ^ imm_val exception_handlers_test_utils.assertGprHasValue( self, dest_reg_index, dest_reg_val) imm_val = RandomUtils.random32(0, 2047) assembly_helper.genAddImmediate(dest_reg_index, imm_val) dest_reg_val = (dest_reg_val + imm_val) & MAX_GPR_VAL exception_handlers_test_utils.assertGprHasValue( self, dest_reg_index, dest_reg_val) imm_val = RandomUtils.random32(0, 2047) assembly_helper.genAddImmediate(dest_reg_index, imm_val, aSrcRegIndex=src_reg_index) dest_reg_val = (src_reg_val + imm_val) & MAX_GPR_VAL exception_handlers_test_utils.assertGprHasValue( self, dest_reg_index, dest_reg_val) assembly_helper.genMoveRegister(dest_reg_index, src_reg_index) dest_reg_val = src_reg_val exception_handlers_test_utils.assertGprHasValue( self, dest_reg_index, dest_reg_val) # Reset destination register value after copying directly from source; otherwise, the tests # that follow will be less interesting dest_reg_val = RandomUtils.random64() load_gpr64_seq.load(dest_reg_index, dest_reg_val) assembly_helper.genAndRegister(dest_reg_index, src_reg_index) dest_reg_val &= src_reg_val exception_handlers_test_utils.assertGprHasValue( self, dest_reg_index, dest_reg_val) assembly_helper.genAndRegister(dest_reg_index, src_reg_index, aSrcRegIndex2=src_reg_index_2) dest_reg_val = src_reg_val_2 & src_reg_val exception_handlers_test_utils.assertGprHasValue( self, dest_reg_index, dest_reg_val) assembly_helper.genOrRegister(dest_reg_index, src_reg_index) dest_reg_val |= src_reg_val exception_handlers_test_utils.assertGprHasValue( self, dest_reg_index, dest_reg_val) assembly_helper.genOrRegister(dest_reg_index, src_reg_index, aSrcRegIndex2=src_reg_index_2) dest_reg_val = src_reg_val_2 | src_reg_val exception_handlers_test_utils.assertGprHasValue( self, dest_reg_index, dest_reg_val) assembly_helper.genNotRegister(dest_reg_index) dest_reg_val = ~dest_reg_val & MAX_GPR_VAL exception_handlers_test_utils.assertGprHasValue( self, dest_reg_index, dest_reg_val) assembly_helper.genNotRegister(dest_reg_index, aSrcRegIndex=src_reg_index) dest_reg_val = ~src_reg_val & MAX_GPR_VAL exception_handlers_test_utils.assertGprHasValue( self, dest_reg_index, dest_reg_val) assembly_helper.genAddRegister(dest_reg_index, src_reg_index) dest_reg_val = (dest_reg_val + src_reg_val) & MAX_GPR_VAL exception_handlers_test_utils.assertGprHasValue( self, dest_reg_index, dest_reg_val) assembly_helper.genAddRegister(dest_reg_index, src_reg_index, aSrcRegIndex2=src_reg_index_2) dest_reg_val = (src_reg_val_2 + src_reg_val) & MAX_GPR_VAL exception_handlers_test_utils.assertGprHasValue( self, dest_reg_index, dest_reg_val) assembly_helper.genSubRegister(dest_reg_index, src_reg_index) dest_reg_val = (dest_reg_val - src_reg_val) & MAX_GPR_VAL exception_handlers_test_utils.assertGprHasValue( self, dest_reg_index, dest_reg_val) assembly_helper.genSubRegister(dest_reg_index, src_reg_index, aMinuendRegIndex=src_reg_index_2) dest_reg_val = (src_reg_val_2 - src_reg_val) & MAX_GPR_VAL exception_handlers_test_utils.assertGprHasValue( self, dest_reg_index, dest_reg_val) assembly_helper.genReadSystemRegister(dest_reg_index, 'mscratch') (dest_reg_val, valid) = self.readRegister('mscratch') if not valid: self.error('Value for register mscratch is invalid') exception_handlers_test_utils.assertGprHasValue( self, dest_reg_index, dest_reg_val) assembly_helper.genWriteSystemRegister('mscratch', src_reg_index) (mscratch_val, valid) = self.readRegister('mscratch') if not valid: self.error('Value for register mscratch is invalid') if mscratch_val != src_reg_val: self.error( 'Value of register mscratch did not match the expected value. Expected=0x%x, Actual=0x%x' % (src_reg_val, mscratch_val))
def generate(self, **kargs): assembly_helper = AssemblyHelperRISCV(self) appRegSize = self.getGlobalState("AppRegisterWidth") if appRegSize == 32: self.notice("FORCE/RISCV configured for 32-bits...") else: self.notice("FORCE/RISCV configured for 64-bits...") (dest_reg_index, src_reg_index, src_reg_index_2) = self.getRandomGPRs(3, exclude="0") load_gpr64_seq = LoadGPR64(self.genThread) src_reg_val = RandomUtils.random32( ) if appRegSize == 32 else RandomUtils.random64() load_gpr64_seq.load(src_reg_index, src_reg_val) src_reg_val_2 = RandomUtils.random32( ) if appRegSize == 32 else RandomUtils.random64() load_gpr64_seq.load(src_reg_index_2, src_reg_val_2) MAX_GPR_VAL = 0xFFFFFFFF if appRegSize == 32 else 0xFFFFFFFFFFFFFFFF imm_val = RandomUtils.random32(0, 2047) assembly_helper.genMoveImmediate(dest_reg_index, imm_val) dest_reg_val = imm_val exception_handlers_test_utils.assert_gpr_has_value( self, dest_reg_index, dest_reg_val) max_shift_val = 31 if appRegSize == 32 else 63 shift_amount = RandomUtils.random32(0, max_shift_val) assembly_helper.genShiftLeftImmediate(dest_reg_index, shift_amount) dest_reg_val = (dest_reg_val << shift_amount) & MAX_GPR_VAL exception_handlers_test_utils.assert_gpr_has_value( self, dest_reg_index, dest_reg_val) shift_amount = RandomUtils.random32(0, max_shift_val) assembly_helper.genShiftRightImmediate(dest_reg_index, shift_amount) dest_reg_val = (dest_reg_val >> shift_amount) & MAX_GPR_VAL exception_handlers_test_utils.assert_gpr_has_value( self, dest_reg_index, dest_reg_val) shift_amount = RandomUtils.random32(0, max_shift_val) assembly_helper.genShiftLeftImmediate(dest_reg_index, shift_amount, aSrcRegIndex=src_reg_index) dest_reg_val = (src_reg_val << shift_amount) & MAX_GPR_VAL exception_handlers_test_utils.assert_gpr_has_value( self, dest_reg_index, dest_reg_val) shift_amount = RandomUtils.random32(0, max_shift_val) assembly_helper.genShiftRightImmediate(dest_reg_index, shift_amount, aSrcRegIndex=src_reg_index) dest_reg_val = (src_reg_val >> shift_amount) & MAX_GPR_VAL exception_handlers_test_utils.assert_gpr_has_value( self, dest_reg_index, dest_reg_val) imm_val = RandomUtils.random32(0, 2047) assembly_helper.genAndImmediate(dest_reg_index, imm_val) dest_reg_val &= imm_val exception_handlers_test_utils.assert_gpr_has_value( self, dest_reg_index, dest_reg_val) imm_val = RandomUtils.random32(0, 2047) assembly_helper.genAndImmediate(dest_reg_index, imm_val, aSrcRegIndex=src_reg_index) dest_reg_val = src_reg_val & imm_val exception_handlers_test_utils.assert_gpr_has_value( self, dest_reg_index, dest_reg_val) imm_val = RandomUtils.random32(0, 2047) assembly_helper.genOrImmediate(dest_reg_index, imm_val) dest_reg_val |= imm_val exception_handlers_test_utils.assert_gpr_has_value( self, dest_reg_index, dest_reg_val) imm_val = RandomUtils.random32(0, 2047) assembly_helper.genOrImmediate(dest_reg_index, imm_val, aSrcRegIndex=src_reg_index) dest_reg_val = src_reg_val | imm_val exception_handlers_test_utils.assert_gpr_has_value( self, dest_reg_index, dest_reg_val) imm_val = RandomUtils.random32(0, 2047) assembly_helper.genXorImmediate(dest_reg_index, imm_val) dest_reg_val ^= imm_val exception_handlers_test_utils.assert_gpr_has_value( self, dest_reg_index, dest_reg_val) imm_val = RandomUtils.random32(0, 2047) assembly_helper.genXorImmediate(dest_reg_index, imm_val, aSrcRegIndex=src_reg_index) dest_reg_val = src_reg_val ^ imm_val exception_handlers_test_utils.assert_gpr_has_value( self, dest_reg_index, dest_reg_val) imm_val = RandomUtils.random32(0, 2047) assembly_helper.genAddImmediate(dest_reg_index, imm_val) dest_reg_val = (dest_reg_val + imm_val) & MAX_GPR_VAL exception_handlers_test_utils.assert_gpr_has_value( self, dest_reg_index, dest_reg_val) imm_val = RandomUtils.random32(0, 2047) assembly_helper.genAddImmediate(dest_reg_index, imm_val, aSrcRegIndex=src_reg_index) dest_reg_val = (src_reg_val + imm_val) & MAX_GPR_VAL exception_handlers_test_utils.assert_gpr_has_value( self, dest_reg_index, dest_reg_val) assembly_helper.genMoveRegister(dest_reg_index, src_reg_index) dest_reg_val = src_reg_val exception_handlers_test_utils.assert_gpr_has_value( self, dest_reg_index, dest_reg_val) # Reset destination register value after copying directly from source; # otherwise, the tests that follow will be less interesting dest_reg_val = RandomUtils.random32( ) if appRegSize == 32 else RandomUtils.random64() load_gpr64_seq.load(dest_reg_index, dest_reg_val) assembly_helper.genAndRegister(dest_reg_index, src_reg_index) dest_reg_val &= src_reg_val exception_handlers_test_utils.assert_gpr_has_value( self, dest_reg_index, dest_reg_val) assembly_helper.genAndRegister(dest_reg_index, src_reg_index, aSrcRegIndex2=src_reg_index_2) dest_reg_val = src_reg_val_2 & src_reg_val exception_handlers_test_utils.assert_gpr_has_value( self, dest_reg_index, dest_reg_val) assembly_helper.genOrRegister(dest_reg_index, src_reg_index) dest_reg_val |= src_reg_val exception_handlers_test_utils.assert_gpr_has_value( self, dest_reg_index, dest_reg_val) assembly_helper.genOrRegister(dest_reg_index, src_reg_index, aSrcRegIndex2=src_reg_index_2) dest_reg_val = src_reg_val_2 | src_reg_val exception_handlers_test_utils.assert_gpr_has_value( self, dest_reg_index, dest_reg_val) assembly_helper.genNotRegister(dest_reg_index) dest_reg_val = ~dest_reg_val & MAX_GPR_VAL exception_handlers_test_utils.assert_gpr_has_value( self, dest_reg_index, dest_reg_val) assembly_helper.genNotRegister(dest_reg_index, aSrcRegIndex=src_reg_index) dest_reg_val = ~src_reg_val & MAX_GPR_VAL exception_handlers_test_utils.assert_gpr_has_value( self, dest_reg_index, dest_reg_val) assembly_helper.genAddRegister(dest_reg_index, src_reg_index) dest_reg_val = (dest_reg_val + src_reg_val) & MAX_GPR_VAL exception_handlers_test_utils.assert_gpr_has_value( self, dest_reg_index, dest_reg_val) assembly_helper.genAddRegister(dest_reg_index, src_reg_index, aSrcRegIndex2=src_reg_index_2) dest_reg_val = (src_reg_val_2 + src_reg_val) & MAX_GPR_VAL exception_handlers_test_utils.assert_gpr_has_value( self, dest_reg_index, dest_reg_val) assembly_helper.genSubRegister(dest_reg_index, src_reg_index) dest_reg_val = (dest_reg_val - src_reg_val) & MAX_GPR_VAL exception_handlers_test_utils.assert_gpr_has_value( self, dest_reg_index, dest_reg_val) assembly_helper.genSubRegister(dest_reg_index, src_reg_index, aMinuendRegIndex=src_reg_index_2) dest_reg_val = (src_reg_val_2 - src_reg_val) & MAX_GPR_VAL exception_handlers_test_utils.assert_gpr_has_value( self, dest_reg_index, dest_reg_val) assembly_helper.genReadSystemRegister(dest_reg_index, "mscratch") (dest_reg_val, valid) = self.readRegister("mscratch") if not valid: self.error("Value for register mscratch is invalid") exception_handlers_test_utils.assert_gpr_has_value( self, dest_reg_index, dest_reg_val) assembly_helper.genWriteSystemRegister("mscratch", src_reg_index) (mscratch_val, valid) = self.readRegister("mscratch") if not valid: self.error("Value for register mscratch is invalid") if mscratch_val != src_reg_val: self.error("Value of register mscratch did not match the expected " "value. Expected=0x%x, Actual=0x%x" % (src_reg_val, mscratch_val))
class SystemCallSequence(Sequence): def __init__(self, aGenThread): super().__init__(aGenThread) self._mAssemblyHelper = AssemblyHelperRISCV(self) self._mDataBlockAddrRegIndex = None self._mActionCodeRegIndex = None def generate(self, **kwargs): (handlers_set_name, valid) = self.getOption('handlers_set') if valid and (handlers_set_name == 'Fast'): self.error( 'Fast exception handlers are enabled. SystemCallSequence only supports using comprehensive exception handlers.' ) function = kwargs.setdefault('Function', 'SwitchPrivilegeLevel') if function == 'SwitchPrivilegeLevel': ret_code = self._switchPrivilegeLevel(kwargs) else: self.error('SystemCallSequence does not support the %s function' % function) self._processReturnCode(ret_code, function) ## Execute a privilege level switch. # # @params aParams A dictionary of optional parameters. # PrivilegeLevel: The target privilege level. Permissible values are 'U', 'S', 'M', 0, 1, # 3 or 'Random'; defaults to 'Random'. # TargetAddr: The address at which to resume execution after completing the privilege # level switch. Permissible values are any 64-bit integer; defaults to a randomly # generated valid instruction address. # SkipAddrValidation: Specifies whether to skip validating a specified TargetAddr value. # Permissible values are 0 or 1; defaults to 0. If SkipAddrValidation is 0, the # TargetAddr value is checked to determine whether it is a valid instruction address. # If it isn't, an alternative address is randomly generated. If SkipAddrValidation is # 1, the target address value is always used as is. # AddrChoicesModID: Specifies ID of choices modification set to be applied when generating # the target address in the event no TargetAddr value is provided or the value # provided is invalid. Permissible values are any valid choices modification set ID. # InterruptMask: Value to set the xstatus.xIE field to for the target privilege level. # Permissible values are 0, 1, 'Same', 'Flip' or 'Random'; defaults to 'Random'. # SUM: *** Not currently supported! *** Value to set the xstatus.SUM field to for the # target privilege level. Permissible values are 0, 1, 'Same', 'Flip' or 'Random'; # defaults to 'Random'. # MXR: *** Not currently supported! *** Value to set the xstatus.MXR field to for the # target privilege level. Permissible values are 0, 1, 'Same', 'Flip' or 'Random'; # defaults to 'Random'. # MPRV: *** Not currently supported! *** Value to set the mstatus.MPRV field to for the # target privilege level. Permissible values are 0, 1, 'Same', 'Flip' or 'Random'; # defaults to 'Random'. As this field is only relevant to M mode, it is ignored if the # target privilege level is not M. def _switchPrivilegeLevel(self, aParams): except_request_results = self.exceptionRequest('SystemCall', aParams) ret_code = except_request_results['RetCode'] if ret_code != 0: return ret_code self._assignRegisters(except_request_results['DataBlockAddrRegIndex'], except_request_results['ActionCodeRegIndex']) load_gpr64_seq = LoadGPR64(self.genThread) load_gpr64_seq.load(self._mDataBlockAddrRegIndex, except_request_results['DataBlockAddr']) self._genPrivilegeLevelSwitchInstructions( except_request_results['InstrSeqCode'], except_request_results['PrivilegeLevel'], except_request_results['TargetAddr'], except_request_results['IntermediateRetAddr']) self._unassignRegisters() self.genSequence('UpdatePeState', {'RecordId': except_request_results['RecordId']}) self.updateVm() return ret_code ## Output appropriate messages based on the return code. Fail if the return code is not 0 and # NoSkip is specified. # # @param aRetCode The return code from the system call operation. # @param aFunction The operation performed by the system call. def _processReturnCode(self, aRetCode, aFunction): if aRetCode == 0: self.notice('SystemCallSequence request %s processed succesfully' % aFunction) else: self.notice( 'SystemCallSequence request %s could not be processed successfully; return code %d' % (aFunction, aRetCode)) (no_skip, valid) = self.genThread.getOption('NoSkip') if valid and (no_skip == 1) and (aRetCode != 0): self.error( 'Unable to process SystemCallSequence request and NoSkip option was specified' ) ## Generate the instructions required to execute the privilege level switch. # # @param aInstrSeqCode A value indicating the instructions that need to be generated. # @param aTargetPrivLevel The target privilege level. # @param aTargetAddr The address at which to resume execution after completing the privilege # level switch. # @param aIntermediateRetAddr The return address for the first ECALL of a 2 ECALL sequence def _genPrivilegeLevelSwitchInstructions(self, aInstrSeqCode, aTargetPrivLevel, aTargetAddr, aIntermediateRetAddr): if aInstrSeqCode == 0: # Do nothing pass elif aInstrSeqCode == 1: # Generate 1 ECALL instruction action_code = 2 # Load From Data Block self._mAssemblyHelper.genMoveImmediate(self._mActionCodeRegIndex, action_code) self._genEcall() elif aInstrSeqCode == 2: # Generate xRET instruction self._genXRet(aTargetPrivLevel, aTargetAddr) elif aInstrSeqCode == 3: # Generate 2 ECALL instructions action_code = 1 # Return to S Mode self._mAssemblyHelper.genMoveImmediate(self._mActionCodeRegIndex, action_code) self._genEcall() self.setPEstate('PrivilegeLevel', 1) self.setPEstate('PC', aIntermediateRetAddr) # For a 2 ECALL sequence, the data block contains a return address for the first ECALL # as the first entry; after the first ECALL, we increment the data block address pointer # to allow loading the data block as usual in the second ECALL self._mAssemblyHelper.genAddImmediate(self._mDataBlockAddrRegIndex, 8) action_code = 2 # Load From Data Block self._mAssemblyHelper.genMoveImmediate(self._mActionCodeRegIndex, action_code) self._genEcall() else: self.error('Unexpected InstrSeqCode value %d' % aInstrSeqCode) ## Assign registers to be used for executing the privilege level switch so that they can be # referenced later. # # @param aDataBlockRegIndex The index of the register used for storing the data block address. # @param aActionCodeRegIndex The index of the register used for storing the action code. def _assignRegisters(self, aDataBlockRegIndex, aActionCodeRegIndex): self._mDataBlockAddrRegIndex = aDataBlockRegIndex self._mActionCodeRegIndex = aActionCodeRegIndex ## Clear register assignments. def _unassignRegisters(self): self._mActionCodeRegIndex = None self._mDataBlockAddrRegIndex = None ## Generate an ECALL instruction. def _genEcall(self): self.genInstruction('ECALL##RISCV') ## Generate an xRET instruction. # # @param aTargetPrivLevel The target privilege level. # @param aTargetAddr The address at which to resume execution after completing the privilege # level switch. def _genXRet(self, aTargetPrivLevel, aTargetAddr): priv_level = self.getPEstate('PrivilegeLevel') if priv_level == 1: priv_level_prefix = 'S' elif priv_level == 3: priv_level_prefix = 'M' else: self.error( 'Unexpected request to execute xRET instruction to transition from privilege level %s to privilege level %s' % (priv_level, aTargetPrivLevel)) # The action code register is used as a scratch register here to reduce the number of # required registers self.genInstruction( 'LD##RISCV', { 'rd': self._mActionCodeRegIndex, 'rs1': self._mDataBlockAddrRegIndex, 'simm12': 0, 'NoRestriction': 1 }) self._mAssemblyHelper.genWriteSystemRegister( ('%sstatus' % priv_level_prefix.lower()), self._mActionCodeRegIndex) self.genInstruction( 'LD##RISCV', { 'rd': self._mActionCodeRegIndex, 'rs1': self._mDataBlockAddrRegIndex, 'simm12': 8, 'NoRestriction': 1 }) self._mAssemblyHelper.genWriteSystemRegister( ('%sepc' % priv_level_prefix.lower()), self._mActionCodeRegIndex) self.genInstruction( 'LD##RISCV', { 'rd': self._mActionCodeRegIndex, 'rs1': self._mDataBlockAddrRegIndex, 'simm12': 16, 'NoRestriction': 1 }) self._mAssemblyHelper.genWriteSystemRegister('satp', self._mActionCodeRegIndex) self.genInstruction( 'LD##RISCV', { 'rd': self._mActionCodeRegIndex, 'rs1': self._mDataBlockAddrRegIndex, 'simm12': 24, 'NoRestriction': 1 }) self.genInstruction( 'LD##RISCV', { 'rd': self._mDataBlockAddrRegIndex, 'rs1': self._mDataBlockAddrRegIndex, 'simm12': 32, 'NoRestriction': 1 }) self.genInstruction(('%sRET##RISCV' % priv_level_prefix), {'NoRestriction': 1})