2 * Copyright (c) 2020 ARM Limited
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
14 * Copyright 2019 Google, Inc.
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions are
18 * met: redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer;
20 * redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in the
22 * documentation and/or other materials provided with the distribution;
23 * neither the name of the copyright holders nor the names of its
24 * contributors may be used to endorse or promote products derived from
25 * this software without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 #include "arch/arm/fastmodel/iris/thread_context.hh"
44 #include "arch/arm/system.hh"
45 #include "arch/arm/utility.hh"
46 #include "iris/detail/IrisCppAdapter.h"
47 #include "iris/detail/IrisObjects.h"
48 #include "mem/se_translating_port_proxy.hh"
49 #include "mem/translating_port_proxy.hh"
50 #include "sim/pseudo_inst.hh"
56 ThreadContext::initFromIrisInstance(const ResourceMap
&resources
)
59 call().perInstanceExecution_getState(_instId
, enabled
);
60 _status
= enabled
? Active
: Suspended
;
64 call().memory_getMemorySpaces(_instId
, memorySpaces
);
65 call().memory_getUsefulAddressTranslations(_instId
, translations
);
67 typedef ThreadContext Self
;
68 iris::EventSourceInfo evSrcInfo
;
70 client
.registerEventCallback
<Self
, &Self::breakpointHit
>(
71 this, "ec_IRIS_BREAKPOINT_HIT",
72 "Handle hitting a breakpoint", "Iris::ThreadContext");
73 call().event_getEventSource(_instId
, evSrcInfo
, "IRIS_BREAKPOINT_HIT");
74 call().eventStream_create(_instId
, breakpointEventStreamId
,
75 evSrcInfo
.evSrcId
, client
.getInstId());
77 for (auto it
= bps
.begin(); it
!= bps
.end(); it
++)
80 client
.registerEventCallback
<Self
, &Self::semihostingEvent
>(
81 this, "ec_IRIS_SEMIHOSTING_CALL_EXTENSION",
82 "Handle a semihosting call", "Iris::ThreadContext");
83 call().event_getEventSource(_instId
, evSrcInfo
,
84 "IRIS_SEMIHOSTING_CALL_EXTENSION");
85 call().eventStream_create(_instId
, semihostingEventStreamId
,
86 evSrcInfo
.evSrcId
, client
.getInstId(),
87 // Set all arguments to their defaults, except syncEc which is
89 nullptr, "", false, 0, nullptr, false, false, true);
93 ThreadContext::extractResourceId(
94 const ResourceMap
&resources
, const std::string
&name
)
96 return resources
.at(name
).rscId
;
100 ThreadContext::extractResourceMap(
101 ResourceIds
&ids
, const ResourceMap
&resources
,
102 const IdxNameMap
&idx_names
)
104 for (const auto &idx_name
: idx_names
) {
105 int idx
= idx_name
.first
;
106 const std::string
&name
= idx_name
.second
;
108 if (idx
>= ids
.size())
109 ids
.resize(idx
+ 1, iris::IRIS_UINT64_MAX
);
111 ids
[idx
] = extractResourceId(resources
, name
);
116 ThreadContext::maintainStepping()
121 if (comInstEventQueue
.empty()) {
122 // Set to 0 to deactivate stepping.
123 call().step_setup(_instId
, 0, "instruction");
127 Tick next
= comInstEventQueue
.nextTick();
129 now
= getCurrentInstCount();
132 comInstEventQueue
.serviceEvents(now
);
133 // Start over now that comInstEventQueue has likely changed.
137 // Set to the number of instructions still to step through.
138 Tick remaining
= next
- now
;
139 call().step_setup(_instId
, remaining
, "instruction");
144 ThreadContext::BpInfoIt
145 ThreadContext::getOrAllocBp(Addr pc
)
147 auto pc_it
= bps
.find(pc
);
149 if (pc_it
!= bps
.end())
152 auto res
= bps
.emplace(std::make_pair(pc
, new BpInfo(pc
)));
153 panic_if(!res
.second
, "Inserting breakpoint failed.");
158 ThreadContext::installBp(BpInfoIt it
)
160 Addr pc
= it
->second
->pc
;
161 const auto &space_ids
= getBpSpaceIds();
162 for (auto sid
: space_ids
) {
164 call().breakpoint_set_code(_instId
, id
, pc
, sid
, 0, true);
165 it
->second
->ids
.push_back(id
);
170 ThreadContext::uninstallBp(BpInfoIt it
)
172 for (auto id
: it
->second
->ids
)
173 call().breakpoint_delete(_instId
, id
);
174 it
->second
->clearIds();
178 ThreadContext::delBp(BpInfoIt it
)
180 panic_if(!it
->second
->empty(),
181 "BP info still had events associated with it.");
183 if (it
->second
->validIds())
190 ThreadContext::instanceRegistryChanged(
191 uint64_t esId
, const iris::IrisValueMap
&fields
, uint64_t time
,
192 uint64_t sInstId
, bool syncEc
, std::string
&error_message_out
)
194 const std::string
&event
= fields
.at("EVENT").getString();
195 const iris::InstanceId id
= fields
.at("INST_ID").getU64();
196 const std::string
&name
= fields
.at("INST_NAME").getString();
198 if (name
!= "component." + _irisPath
)
201 if (event
== "added")
203 else if (event
== "removed")
204 _instId
= iris::IRIS_UINT64_MAX
;
206 panic("Unrecognized event type %s", event
);
212 ThreadContext::phaseInitLeave(
213 uint64_t esId
, const iris::IrisValueMap
&fields
, uint64_t time
,
214 uint64_t sInstId
, bool syncEc
, std::string
&error_message_out
)
216 std::vector
<iris::ResourceInfo
> resources
;
217 call().resource_getList(_instId
, resources
);
219 std::map
<iris::ResourceId
, const iris::ResourceInfo
*>
221 for (const auto &resource
: resources
) {
222 idToResource
[resource
.rscId
] = &resource
;
224 ResourceMap resourceMap
;
225 for (const auto &resource
: resources
) {
226 std::string name
= resource
.name
;
227 iris::ResourceId parentId
= resource
.parentRscId
;
228 while (parentId
!= iris::IRIS_UINT64_MAX
) {
229 const auto *parent
= idToResource
[parentId
];
230 name
= parent
->name
+ "." + name
;
231 parentId
= parent
->parentRscId
;
233 resourceMap
[name
] = resource
;
236 initFromIrisInstance(resourceMap
);
242 ThreadContext::simulationTimeEvent(
243 uint64_t esId
, const iris::IrisValueMap
&fields
, uint64_t time
,
244 uint64_t sInstId
, bool syncEc
, std::string
&error_message_out
)
246 if (fields
.at("RUNNING").getAsBool()) {
247 // If this is just simulation time starting up, don't do anything.
251 // If simulation time has stopped for any reason, IRIS helpfully clears
252 // all stepping counters and we need to set them back. We might also need
253 // to service events based on the current number of executed instructions.
256 // Restart simulation time to make sure things progress once we give
258 call().simulationTime_run(iris::IrisInstIdSimulationEngine
);
264 ThreadContext::breakpointHit(
265 uint64_t esId
, const iris::IrisValueMap
&fields
, uint64_t time
,
266 uint64_t sInstId
, bool syncEc
, std::string
&error_message_out
)
268 Addr pc
= fields
.at("PC").getU64();
270 auto it
= getOrAllocBp(pc
);
272 std::shared_ptr
<BpInfo::EventList
> events
= it
->second
->events
;
273 auto e_it
= events
->begin();
274 while (e_it
!= events
->end()) {
276 // Advance e_it here since e might remove itself from the list.
285 ThreadContext::semihostingEvent(
286 uint64_t esId
, const iris::IrisValueMap
&fields
, uint64_t time
,
287 uint64_t sInstId
, bool syncEc
, std::string
&error_message_out
)
289 if (ArmSystem::callSemihosting(this, true)) {
290 // Stop execution in case an exit of the sim loop was scheduled. We
291 // don't want to keep executing instructions in the mean time.
292 call().perInstanceExecution_setState(_instId
, false);
294 // Schedule an event to resume execution right after any exit has
295 // had a chance to happen.
296 if (!enableAfterPseudoEvent
->scheduled())
297 getCpuPtr()->schedule(enableAfterPseudoEvent
, curTick());
299 call().semihosting_return(_instId
, readIntReg(0));
301 call().semihosting_notImplemented(_instId
);
306 ThreadContext::ThreadContext(
307 BaseCPU
*cpu
, int id
, System
*system
, ::BaseTLB
*dtb
, ::BaseTLB
*itb
,
308 iris::IrisConnectionInterface
*iris_if
, const std::string
&iris_path
) :
309 _cpu(cpu
), _threadId(id
), _system(system
), _dtb(dtb
), _itb(itb
),
310 _irisPath(iris_path
), vecRegs(ArmISA::NumVecRegs
),
311 vecPredRegs(ArmISA::NumVecPredRegs
),
312 comInstEventQueue("instruction-based event queue"),
313 client(iris_if
, "client." + iris_path
)
315 iris::InstanceInfo info
;
316 auto ret_code
= noThrow().instanceRegistry_getInstanceInfoByName(
317 info
, "component." + iris_path
);
318 if (ret_code
== iris::E_ok
) {
319 // The iris instance registry already new about this path.
320 _instId
= info
.instId
;
322 // This path doesn't (yet) exist. Set the ID to something invalid.
323 _instId
= iris::IRIS_UINT64_MAX
;
326 typedef ThreadContext Self
;
327 iris::EventSourceInfo evSrcInfo
;
329 client
.registerEventCallback
<Self
, &Self::instanceRegistryChanged
>(
330 this, "ec_IRIS_INSTANCE_REGISTRY_CHANGED",
331 "Install the iris instance ID", "Iris::ThreadContext");
332 call().event_getEventSource(iris::IrisInstIdGlobalInstance
, evSrcInfo
,
333 "IRIS_INSTANCE_REGISTRY_CHANGED");
334 regEventStreamId
= iris::IRIS_UINT64_MAX
;
335 static const std::vector
<std::string
> fields
=
336 { "EVENT", "INST_ID", "INST_NAME" };
337 call().eventStream_create(iris::IrisInstIdGlobalInstance
, regEventStreamId
,
338 evSrcInfo
.evSrcId
, client
.getInstId(), &fields
);
340 client
.registerEventCallback
<Self
, &Self::phaseInitLeave
>(
341 this, "ec_IRIS_SIM_PHASE_INIT_LEAVE",
342 "Initialize register contexts", "Iris::ThreadContext");
343 call().event_getEventSource(iris::IrisInstIdSimulationEngine
, evSrcInfo
,
344 "IRIS_SIM_PHASE_INIT_LEAVE");
345 initEventStreamId
= iris::IRIS_UINT64_MAX
;
346 call().eventStream_create(
347 iris::IrisInstIdSimulationEngine
, initEventStreamId
,
348 evSrcInfo
.evSrcId
, client
.getInstId());
350 client
.registerEventCallback
<Self
, &Self::simulationTimeEvent
>(
351 this, "ec_IRIS_SIMULATION_TIME_EVENT",
352 "Handle simulation time stopping for breakpoints or stepping",
353 "Iris::ThreadContext");
354 call().event_getEventSource(iris::IrisInstIdSimulationEngine
, evSrcInfo
,
355 "IRIS_SIMULATION_TIME_EVENT");
356 timeEventStreamId
= iris::IRIS_UINT64_MAX
;
357 call().eventStream_create(
358 iris::IrisInstIdSimulationEngine
, timeEventStreamId
,
359 evSrcInfo
.evSrcId
, client
.getInstId());
361 breakpointEventStreamId
= iris::IRIS_UINT64_MAX
;
362 semihostingEventStreamId
= iris::IRIS_UINT64_MAX
;
364 auto enable_lambda
= [this]{
365 call().perInstanceExecution_setState(_instId
, true);
367 enableAfterPseudoEvent
= new EventFunctionWrapper(
368 enable_lambda
, "resume after pseudo inst",
369 false, Event::Sim_Exit_Pri
+ 1);
372 ThreadContext::~ThreadContext()
374 call().eventStream_destroy(
375 iris::IrisInstIdSimulationEngine
, initEventStreamId
);
376 initEventStreamId
= iris::IRIS_UINT64_MAX
;
377 client
.unregisterEventCallback("ec_IRIS_SIM_PHASE_INIT_LEAVE");
379 call().eventStream_destroy(
380 iris::IrisInstIdGlobalInstance
, regEventStreamId
);
381 regEventStreamId
= iris::IRIS_UINT64_MAX
;
382 client
.unregisterEventCallback("ec_IRIS_INSTANCE_REGISTRY_CHANGED");
384 call().eventStream_destroy(
385 iris::IrisInstIdGlobalInstance
, timeEventStreamId
);
386 timeEventStreamId
= iris::IRIS_UINT64_MAX
;
387 client
.unregisterEventCallback("ec_IRIS_SIMULATION_TIME_EVENT");
389 if (enableAfterPseudoEvent
->scheduled())
390 getCpuPtr()->deschedule(enableAfterPseudoEvent
);
391 delete enableAfterPseudoEvent
;
395 ThreadContext::schedule(PCEvent
*e
)
397 auto it
= getOrAllocBp(e
->pc());
398 it
->second
->events
->push_back(e
);
400 if (_instId
!= iris::IRIS_UINT64_MAX
&& !it
->second
->validIds())
407 ThreadContext::remove(PCEvent
*e
)
409 auto it
= getOrAllocBp(e
->pc());
410 it
->second
->events
->remove(e
);
412 if (it
->second
->empty())
419 ThreadContext::translateAddress(Addr
&paddr
, iris::MemorySpaceId p_space
,
420 Addr vaddr
, iris::MemorySpaceId v_space
)
422 iris::MemoryAddressTranslationResult result
;
423 auto ret
= noThrow().memory_translateAddress(
424 _instId
, result
, v_space
, vaddr
, p_space
);
426 if (ret
!= iris::E_ok
) {
427 // Check if there was a legal translation between these two spaces.
428 // If so, something else went wrong.
429 for (auto &trans
: translations
)
430 if (trans
.inSpaceId
== v_space
&& trans
.outSpaceId
== p_space
)
433 panic("No legal translation IRIS address translation found.");
436 if (result
.address
.empty())
439 if (result
.address
.size() > 1) {
440 warn("Multiple mappings for address %#x.", vaddr
);
444 paddr
= result
.address
[0];
449 ThreadContext::scheduleInstCountEvent(Event
*event
, Tick count
)
451 Tick now
= getCurrentInstCount();
452 comInstEventQueue
.schedule(event
, count
);
454 call().simulationTime_stop(iris::IrisInstIdSimulationEngine
);
460 ThreadContext::descheduleInstCountEvent(Event
*event
)
462 comInstEventQueue
.deschedule(event
);
467 ThreadContext::getCurrentInstCount()
470 auto ret
= call().step_getStepCounterValue(_instId
, count
, "instruction");
471 panic_if(ret
!= iris::E_ok
, "Failed to get instruction count.");
476 ThreadContext::initMemProxies(::ThreadContext
*tc
)
479 assert(!physProxy
&& !virtProxy
);
480 physProxy
.reset(new PortProxy(_cpu
->getSendFunctional(),
481 _cpu
->cacheLineSize()));
482 virtProxy
.reset(new TranslatingPortProxy(tc
));
485 virtProxy
.reset(new SETranslatingPortProxy(this,
486 SETranslatingPortProxy::NextPage
));
490 ThreadContext::Status
491 ThreadContext::status() const
497 ThreadContext::setStatus(Status new_status
)
499 if (enableAfterPseudoEvent
->scheduled())
500 getCpuPtr()->deschedule(enableAfterPseudoEvent
);
501 if (new_status
== Active
) {
502 if (_status
!= Active
)
503 call().perInstanceExecution_setState(_instId
, true);
505 if (_status
== Active
)
506 call().perInstanceExecution_setState(_instId
, false);
508 _status
= new_status
;
512 ThreadContext::pcState() const
514 ArmISA::CPSR cpsr
= readMiscRegNoEffect(ArmISA::MISCREG_CPSR
);
518 pc
.nextThumb(pc
.thumb());
520 pc
.nextJazelle(cpsr
.j
);
521 pc
.aarch64(!cpsr
.width
);
522 pc
.nextAArch64(!cpsr
.width
);
523 pc
.illegalExec(false);
524 pc
.itstate(ArmISA::itState(cpsr
));
527 iris::ResourceReadResult result
;
528 call().resource_read(_instId
, result
, pcRscId
);
529 Addr addr
= result
.data
.at(0);
530 if (cpsr
.width
&& cpsr
.t
)
537 ThreadContext::pcState(const ArmISA::PCState
&val
)
541 ArmISA::CPSR cpsr
= readMiscRegNoEffect(ArmISA::MISCREG_CPSR
);
542 if (cpsr
.width
&& cpsr
.t
)
545 iris::ResourceWriteResult result
;
546 call().resource_write(_instId
, result
, pcRscId
, pc
);
550 ThreadContext::instAddr() const
552 return pcState().instAddr();
556 ThreadContext::nextInstAddr() const
558 return pcState().nextInstAddr();
562 ThreadContext::readMiscRegNoEffect(RegIndex misc_reg
) const
564 iris::ResourceReadResult result
;
565 call().resource_read(_instId
, result
, miscRegIds
.at(misc_reg
));
566 return result
.data
.at(0);
570 ThreadContext::setMiscRegNoEffect(RegIndex misc_reg
, const RegVal val
)
572 iris::ResourceWriteResult result
;
573 call().resource_write(_instId
, result
, miscRegIds
.at(misc_reg
), val
);
577 ThreadContext::readIntReg(RegIndex reg_idx
) const
579 ArmISA::CPSR cpsr
= readMiscRegNoEffect(ArmISA::MISCREG_CPSR
);
581 iris::ResourceReadResult result
;
583 call().resource_read(_instId
, result
, intReg32Ids
.at(reg_idx
));
585 call().resource_read(_instId
, result
, intReg64Ids
.at(reg_idx
));
586 return result
.data
.at(0);
590 ThreadContext::setIntReg(RegIndex reg_idx
, RegVal val
)
592 ArmISA::CPSR cpsr
= readMiscRegNoEffect(ArmISA::MISCREG_CPSR
);
594 iris::ResourceWriteResult result
;
596 call().resource_write(_instId
, result
, intReg32Ids
.at(reg_idx
), val
);
598 call().resource_write(_instId
, result
, intReg64Ids
.at(reg_idx
), val
);
602 * The 64 bit version of registers gives us a pre-flattened view of the reg
603 * file, no matter what mode we're in or if we're currently 32 or 64 bit.
606 ThreadContext::readIntRegFlat(RegIndex idx
) const
608 if (idx
>= flattenedIntIds
.size())
610 iris::ResourceId res_id
= flattenedIntIds
.at(idx
);
611 if (res_id
== iris::IRIS_UINT64_MAX
)
613 iris::ResourceReadResult result
;
614 call().resource_read(_instId
, result
, res_id
);
615 return result
.data
.at(0);
619 ThreadContext::setIntRegFlat(RegIndex idx
, uint64_t val
)
621 iris::ResourceId res_id
=
622 (idx
>= flattenedIntIds
.size()) ? iris::IRIS_UINT64_MAX
:
623 flattenedIntIds
.at(idx
);
624 panic_if(res_id
== iris::IRIS_UINT64_MAX
,
625 "Int reg %d is not supported by fast model.", idx
);
626 iris::ResourceWriteResult result
;
627 call().resource_write(_instId
, result
, flattenedIntIds
.at(idx
), val
);
631 ThreadContext::readCCRegFlat(RegIndex idx
) const
633 if (idx
>= ccRegIds
.size())
635 iris::ResourceReadResult result
;
636 call().resource_read(_instId
, result
, ccRegIds
.at(idx
));
637 return result
.data
.at(0);
641 ThreadContext::setCCRegFlat(RegIndex idx
, RegVal val
)
643 panic_if(idx
>= ccRegIds
.size(),
644 "CC reg %d is not supported by fast model.", idx
);
645 iris::ResourceWriteResult result
;
646 call().resource_write(_instId
, result
, ccRegIds
.at(idx
), val
);
649 const ArmISA::VecRegContainer
&
650 ThreadContext::readVecReg(const RegId
®_id
) const
652 const RegIndex idx
= reg_id
.index();
653 ArmISA::VecRegContainer
®
= vecRegs
.at(idx
);
656 // Ignore accesses to registers which aren't architected. gem5 defines a
657 // few extra registers which it uses internally in the implementation of
658 // some instructions.
659 if (idx
>= vecRegIds
.size())
662 iris::ResourceReadResult result
;
663 call().resource_read(_instId
, result
, vecRegIds
.at(idx
));
664 size_t data_size
= result
.data
.size() * (sizeof(*result
.data
.data()));
665 size_t size
= std::min(data_size
, reg
.size());
666 memcpy(reg
.raw_ptr
<void>(), (void *)result
.data
.data(), size
);
671 const ArmISA::VecRegContainer
&
672 ThreadContext::readVecRegFlat(RegIndex idx
) const
674 return readVecReg(RegId(VecRegClass
, idx
));
677 const ArmISA::VecPredRegContainer
&
678 ThreadContext::readVecPredReg(const RegId
®_id
) const
680 RegIndex idx
= reg_id
.index();
682 ArmISA::VecPredRegContainer
®
= vecPredRegs
.at(idx
);
685 if (idx
>= vecPredRegIds
.size())
688 iris::ResourceReadResult result
;
689 call().resource_read(_instId
, result
, vecPredRegIds
.at(idx
));
692 size_t num_bits
= reg
.NUM_BITS
;
693 uint8_t *bytes
= (uint8_t *)result
.data
.data();
694 while (num_bits
> 8) {
695 reg
.set_bits(offset
, 8, *bytes
);
701 reg
.set_bits(offset
, num_bits
, *bytes
);
706 const ArmISA::VecPredRegContainer
&
707 ThreadContext::readVecPredRegFlat(RegIndex idx
) const
709 return readVecPredReg(RegId(VecPredRegClass
, idx
));