SystemC Library API Reference Manual
Reference documentation for the Simics SystemC Library.
 
Loading...
Searching...
No Matches
pcie_mapping_interconnect.h
Go to the documentation of this file.
1/*
2
3 © 2024 Intel Corporation
4
5 This software and the related documents are Intel copyrighted materials, and
6 your use of them is governed by the express license under which they were
7 provided to you ("License"). Unless the License provides otherwise, you may
8 not use, modify, copy, publish, distribute, disclose or transmit this software
9 or the related documents without Intel's prior written permission.
10
11 This software and the related documents are provided as is, with no express or
12 implied warranties, other than those that are expressly stated in the License.
13*/
14
15#ifndef SIMICS_SYSTEMC_COMPOSITE_PCIE_MAPPING_INTERCONNECT_H
16#define SIMICS_SYSTEMC_COMPOSITE_PCIE_MAPPING_INTERCONNECT_H
17
18#include <systemc>
19#include <tlm>
20#include <tlm_utils/multi_socket_bases.h>
21#include <tlm_utils/simple_initiator_socket.h>
22#include <tlm_utils/simple_target_socket.h>
23
24#include <simics/cc-api.h>
31
32#include <algorithm>
33#include <cstring>
34#include <sstream>
35#include <map>
36#include <vector>
37#include <stdexcept>
38#include <string>
39#include <utility> // pair, ignore
40
41namespace { // NOLINT(build/namespaces_headers)
42const char *const PCIE_MAPPING_INTERCONNECT_TAG = \
43 "intel/PcieMappingInterconnect";
44
46const char *pcieTypeName(simics::types::pcie_type_t t) {
47 switch (t) {
49 return "Not Set";
51 return "Memory";
53 return "I/O";
55 return "Config";
57 return "Message";
59 return "Other";
60 default:
61 return "Unknown";
62 }
63}
64
65std::string pcieDeviceIdStr(uint16_t device_id) {
66 std::ostringstream os;
67 os << "device id 0x" << std::hex << device_id
68 << " (" << std::dec << (device_id >> 8)
69 << ":" << ((device_id >> 3) & 0x1f)
70 << "." << (device_id & 7) << ")";
71 return os.str();
72}
73} // namespace
74
75namespace simics {
76namespace systemc {
77namespace composite {
78
81template<typename TSocket>
83 : public iface::ExtensionSender<TSocket> {
84 public:
86
87 // iface::ExtensionSender
91 }
95 }
96
98};
99
106template <unsigned int BUSWIDTH, typename TYPES>
107class PcieMappingInterconnect : public sc_core::sc_module {
108 // Socket types used by the interconnect
109 typedef tlm_utils::simple_target_socket<
110 PcieMappingInterconnect, BUSWIDTH, TYPES> target_socket_t;
111 typedef tlm_utils::simple_initiator_socket<
112 PcieMappingInterconnect, BUSWIDTH, TYPES> initiator_socket_t;
113 typedef struct {
114 tlm::tlm_target_socket<BUSWIDTH, TYPES> *tlm;
115 tlm_utils::multi_target_base<BUSWIDTH, TYPES> *multi;
116 } supported_target_socket_t;
117
118 public:
119#ifndef SYSTEMC_3_0_0
121#endif
123 sc_core::sc_module_name = "PcieMappingInterconnect")
124 : transaction_target_socket("transaction_target_socket"),
125 pcie_device_target_socket("dummy_pcie_device_target_socket"),
126 pcie_map_initiator_socket("pcie_map_initiator_socket"),
127 initiator_sockets_("initiator_socket"),
128 config_target_socket_(),
129 msg_target_socket_(),
130 pcie_map_target_socket_("pcie_map_target_socket") {
131 transaction_target_socket.register_b_transport(
132 this, &PcieMappingInterconnect::transaction_b_transport);
133 transaction_target_socket.register_transport_dbg(
134 this, &PcieMappingInterconnect::transaction_transport_dbg);
135 pcie_map_initiator_socket.register_invalidate_direct_mem_ptr(
136 this, &PcieMappingInterconnect::pcie_map_invalidate_direct_mem_ptr);
137 pcie_map_target_socket_.register_b_transport(
138 this, &PcieMappingInterconnect::pcie_map_b_transport);
139 pcie_map_target_socket_.register_transport_dbg(
140 this, &PcieMappingInterconnect::pcie_map_transport_dbg);
141 pcie_map_target_socket_.register_get_direct_mem_ptr(
142 this, &PcieMappingInterconnect::pcie_map_get_direct_mem_ptr);
143
145 pcie_map_extension_.init(&sender_);
146
147 // f0 is required
148 function_data_[0];
149
150 SC_METHOD(warmReset);
151 sensitive << warm_reset_pin.pos();
152 dont_initialize();
153
154 config_target_socket_.tlm = nullptr;
155 config_target_socket_.multi = nullptr;
156 msg_target_socket_.tlm = nullptr;
157 msg_target_socket_.multi = nullptr;
158 }
159
160 // sc_core::sc_module
162
163 // Called by (outer) composite class from PcieDevice::connected. The
164 // device_id parameter is a PCIe Requester ID; its Function Number field
165 // is discarded before this interconnect combines it with each function.
166 void connected(uint16_t device_id);
167
168 // Called by (outer) composite class from PcieDevice::disconnected
169 void disconnected(uint16_t device_id);
170
171 // Called by (outer) composite class from PcieDevice::hot_reset
172 void hotReset();
173
190 ConfObjectRef o,
191 iface::PcieSriovQueryInterface *sriov = nullptr);
192
193 // Create map helpers which are mapped on the cfg space. It contains
194 // PCI information like PCIe type and function id which is used to
195 // route the transaction to the right TLM2 socket
197
198 // Return the current map from address range to socket ID (Memory)
199 std::map<std::pair<size_t, size_t>, size_t> addressIdMemMap() const;
200
201 // Return the current map from address range to socket ID (IO)
202 std::map<std::pair<size_t, size_t>, size_t> addressIdIoMap() const;
203
204 // Sockets bound by external logic (see also private scope):
205 // Simics -> IC, intercepts the incoming PCIe transactions
207 // Dummy socket as PcieDevice is handled by connected/disconnected/hotReset
210 initiator_socket_t pcie_map_initiator_socket;
211 // Simics -> IC, receives warm reset signal
212 sc_core::sc_in<bool> warm_reset_pin;
213 // For MEM/IO, this parameter controls if the base address of the
214 // memory range is subtracted before sending the GP payload to endpoint
216
217 private:
218 // Called when warm_reset_pin is positive
219 void warmReset();
220
221 // Help methods
222 // Adds the BAR mappings of type with map info
223 void addMap(types::map_info_t info, types::pcie_type_t type);
224
225 // Removes the BAR mappings of type for socket ID
226 void delMap(size_t socket_id, types::pcie_type_t type);
227
228 // The supplied device_id passed to addFunction & delFunction should
229 // contain the full function ID and not just the function number.
230 // Adds the function's configuration header in the upstream target
231 void addFunction(uint16_t device_id);
232 // Removes the current mapping (if any) of this function's configuration
233 // header in the upstream target
234 void delFunction(uint16_t device_id);
235
236 // Cast sc_object to a supported target socket
237 void castToTarget(sc_core::sc_object *object,
238 supported_target_socket_t *target);
239
240 // Update the PCIe mapping
241 // @param function_id the config id used to index into function_data_,
242 // -1 means checks all config ids
243 // @param bar_id the bar id used to index into base_address,
244 // -1 means checks all bar ids
245 void updateMappings(int function_id = -1, int bar_id = -1);
246
247 /*
248 * When command register is written to, the mappings need to be re-evaluated
249 * and possibly updated.
250 *
251 * There is no error handling, so if the mapping fails this will only be
252 * logged but the access will still pass (i.e. transaction forwarded to
253 * target device where registers are updated without side-effects)
254 */
255 void updateCommand(uint16_t function_id, uint16_t old_command);
256
257 // Get up-to-date BAR/CMD information whenever we need it. It cannot be
258 // cached in the interconnect module as it might change over time by the
259 // model, reverse execution, checkpoint restore, ...
260 // @param id is the accessing initiator socket ID
261 // @param bar_id is the BAR number currently access, or -1 for config
262 // register access in which case all BARs must be retrieved
263 void retrieveFunctionData(size_t socket_id, int bar_id);
264
265 // Intercept Config space header write access and update the mapping table
266 void interceptCfgHeaderAccess(size_t socket_id,
267 tlm::tlm_generic_payload &trans); // NOLINT
268
269 // Find the id of the initiator_sockets_ list to route the transaction to
270 // based on the PCIe transaction type, function number and offset
271 size_t findSocketId(types::pcie_type_t type, uint16_t function_id,
272 tlm::tlm_generic_payload &trans) const; // NOLINT
273
274 // Return the created cfg map helper by device_id
275 conf_object_t *getCfgMapHelper(uint16_t device_id);
276
277 // Validate BAR info entries in bar_info_ right after getBarInfo().
278 // Reports configuration errors via SIM_LOG_ERROR_STR.
279 // Returns true if all entries are valid, false if any error was found.
280 // Override to skip or relax validation for special use cases.
281 virtual bool validateBarInfo(ConfObjectRef o);
282
283 // Validate and update TLM2 response status
284 // If it passes validation it returns the socket-id as well
285 size_t validateTransaction(tlm::tlm_generic_payload &trans); // NOLINT
286 /*
287 * The single entry for all incoming PCIe transactions from Simics
288 * Depends on the PCIe type (CFG/MEM/IO/MSG), the transaction is
289 * routed to the corresponding initiator socket towards the endpoint device
290 * For CFG space header write, updating the mapping table as well
291 */
292 void transaction_b_transport(tlm::tlm_generic_payload &trans, // NOLINT
293 sc_core::sc_time &t); // NOLINT
294 unsigned int transaction_transport_dbg(
295 tlm::tlm_generic_payload &trans); // NOLINT
296
297 void pcie_map_invalidate_direct_mem_ptr(sc_dt::uint64 start_range,
298 sc_dt::uint64 end_range);
299 void pcie_map_b_transport(
300 tlm::tlm_generic_payload &trans, // NOLINT: SystemC API
301 sc_core::sc_time &t); // NOLINT: SystemC API
302 unsigned int pcie_map_transport_dbg(
303 tlm::tlm_generic_payload &trans); // NOLINT: SystemC API
304 bool pcie_map_get_direct_mem_ptr(
305 tlm::tlm_generic_payload &trans, // NOLINT: SystemC API
306 tlm::tlm_dmi &dmi_data); // NOLINT: SystemC API
307
308 // Sockets created and bound by internal logic (see also public scope):
309 // IC -> Device, forwards the incoming PCIe transactions
310 sc_core::sc_vector<initiator_socket_t> initiator_sockets_;
311 supported_target_socket_t config_target_socket_;
312 supported_target_socket_t msg_target_socket_;
313 // Device -> IC, intercept upstream pcie-map transactions
314 target_socket_t pcie_map_target_socket_;
315
316 struct cfg_header_t {
317 uint16_t command; // command register
318 uint32_t base_address[7]; // 6 BARs + expansion ROM BAR
319 };
320 // Cache each function's cfg header, f0 is required
321 std::map<uint16_t, cfg_header_t> function_data_;
322 std::vector<iface::PcieBaseAddressRegisterQueryInterface::PCIeBar>
323 bar_info_;
324
325 // Extension and sender for talking to the Simics pcie upstream target
326 PcieMappingInterconnectExtensionSender<initiator_socket_t> sender_;
327 iface::PcieMapExtension pcie_map_extension_;
328
329 // Trigger hot reset on the device
330 iface::PcieResetInterface *reset_ {nullptr};
331
332 // A map to store MEM address ranges and corresponding socket IDs
333 std::map<std::pair<size_t, size_t>, size_t> address_id_mem_map_;
334 // A map to store IO address ranges and corresponding socket IDs
335 std::map<std::pair<size_t, size_t>, size_t> address_id_io_map_;
336 // A map from function_number/bar_offset to socket IDs
337 std::map<std::pair<size_t, size_t>, size_t> fn_bar_id_map_;
338
339 ConfObjectRef simulation_obj_ {nullptr};
340 // For PCIe gasket object, the pcie_device and transaction interfaces
341 // are implemented on the gasket_obj_. For other cases, it is the same
342 // object as simulation_obj_
343 ConfObjectRef gasket_obj_ {nullptr};
344
345 // In some case (for example ARI), function number has 8 bits
346 // This boolean variable is set when the function number from getBarInfo()
347 // returns a value greater than 7
348 bool function_number_has_8bits_ {false};
349
350 // PCIe Requester ID reported by connected(), with its Function Number
351 // field cleared. OR:ed with a function number for pcie_map operations.
352 uint16_t device_id_ {0};
353
354 // Optional static SR-IOV configuration provided by the device.
355 // When set, connected() uses it instead of transport_dbg.
356 iface::PcieSriovQueryInterface *sriov_query_ {nullptr};
357
358 // SR-IOV support ---------------------------------------------------------
359 // Populated by connected() from sriov_query_ (if set).
360 // One entry per PF that exposes VFs (multi-PF devices have multiple
361 // entries).
362 struct SriovState {
363 uint16_t cap_offset = 0; // Extended Cap byte offset in PF cfg
364 int pf_function = 0; // PF function number (SR-IOV cap owner)
365 int total_vfs = 0; // TotalVFs: hardware maximum (RO)
366 int first_vf_function = 0; // Function number of VF0
367 int vf_stride = 1; // VF Stride: step between VF functions
368
369 // Cached VF BAR base addresses, written by the OS into the SR-IOV
370 // Extended Capability BAR registers (+0x24..+0x38, 6 × 4-byte slots).
371 // Index i = (cap_rel_offset - 0x24) / 4, which equals
372 // (vf_cfg_bar_offset - 0x10) / 4 — the same index used by
373 // cfg_header_t::base_address[] for the VF config-header BAR:
374 // vf_bar[0] ↔ cap+0x24 ↔ VF cfg BAR offset 0x10
375 // vf_bar[1] ↔ cap+0x28 ↔ VF cfg BAR offset 0x14
376 // vf_bar[2] ↔ cap+0x2C ↔ VF cfg BAR offset 0x18
377 // vf_bar[3] ↔ cap+0x30 ↔ VF cfg BAR offset 0x1C
378 // vf_bar[4] ↔ cap+0x34 ↔ VF cfg BAR offset 0x20
379 // vf_bar[5] ↔ cap+0x38 ↔ VF cfg BAR offset 0x24
380 // The SR-IOV spec §9.3.3.14 guarantees every VF shares
381 // the same BAR layout.
382 uint32_t vf_bar[6] = {};
383
384 // Cached register values updated by interceptCfgHeaderAccess()
385 uint16_t control = 0;
386 uint16_t num_vfs = 0; // NumVFs: OS-programmed active VF count
387 };
388 std::vector<SriovState> sriov_states_;
389
390 // Populate sriov_states_ from sriov_query_. Called from connect() so
391 // that SR-IOV config-space snooping works even when
392 // pcie_device.connected() is never invoked (e.g. direct upstream_target
393 // assignment in tests). Also called from connected() to reset runtime
394 // state on reconnect.
395 void setupSriovStates();
396
397 // Map/unmap VF config space in the Simics pcie_map
398 // (enable_function / disable_function).
399 // Triggered when VF Enable (SR-IOV Control bit 0) transitions.
400 void updateVfCfgMappings(SriovState &state, bool enable);
401
402 // Map/unmap VF BAR memory regions (add_map / del_map).
403 // Triggered when VF MSE (SR-IOV Control bit 3) transitions while
404 // VF Enable is set, or when VF Enable transitions while VF MSE is set.
405 void updateVfBarMappings(SriovState &state, bool enable);
406};
407
408template <unsigned int BUSWIDTH, typename TYPES>
410 // Because of the way SystemC has been designed, we cannot create new
411 // sockets dynamically ouside of the CTOR except for in the
412 // before_end_of_elaboration() method.
413
414 // Create initiator sockets equal to the number of Config, Mem/IO
415 // target sockets and Msg target socket
416 initiator_sockets_.init(function_data_.size() + bar_info_.size() + 1);
417
418 size_t socket_id = 0;
419 // Allocate and initialize function data based on device configuration
420 for (; socket_id < function_data_.size(); ++socket_id) {
421 if (config_target_socket_.tlm) {
422 initiator_sockets_[socket_id].bind(*config_target_socket_.tlm);
423 } else {
424 initiator_sockets_[socket_id].bind(*config_target_socket_.multi);
425 }
426 }
427
428 for (const auto &bar : bar_info_) {
429 supported_target_socket_t target = {};
430 castToTarget(bar.target_socket, &target);
431 if (target.tlm) {
432 initiator_sockets_[socket_id].bind(*target.tlm);
433 } else if (target.multi) {
434 initiator_sockets_[socket_id].bind(*target.multi);
435 } else {
436 SC_REPORT_ERROR(
437 PCIE_MAPPING_INTERCONNECT_TAG,
438 "BAR target_socket type is not supported");
439 }
440 fn_bar_id_map_[std::make_pair(bar.function, bar.offset)] = socket_id;
441 ++socket_id;
442 }
443
444 if (msg_target_socket_.tlm) {
445 initiator_sockets_[socket_id].bind(*msg_target_socket_.tlm);
446 } else {
447 initiator_sockets_[socket_id].bind(*msg_target_socket_.multi);
448 }
449}
450
451template <unsigned int BUSWIDTH, typename TYPES>
453 // Use the side effect of it to set device object on pcie map gasket.
454 // The device object is used internally for get the mapping helper port
455 // object
456 pcie_map_extension_.get_device_id(gasket_obj_);
457
458 const uint16_t function_mask =
459 function_number_has_8bits_ ? 0xffu : 0x7u;
460 device_id_ = device_id & static_cast<uint16_t>(~function_mask);
461
462 {
463 std::ostringstream os;
464 os << "connected(): raw device_id=0x" << std::hex << device_id
465 << ", ARI=" << std::boolalpha << function_number_has_8bits_
466 << ", masked device_id_=0x" << std::hex << device_id_;
467 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
468 }
469
470 // The PCIe Modeling Library requires endpoints to add their functions
471 // and map resources like Memory and I/O BARs. However, it is unlikely
472 // that these resources are configured before the connection. Therefore,
473 // we deviate from the PCIe DML Modeling Library by not performing the
474 // mapping at this stage.
475 for (const auto &fd : function_data_) {
476 delFunction(device_id_ | fd.first);
477 addFunction(device_id_ | fd.first);
478 }
479
480 // Populate SR-IOV state from the static PcieSriovQueryInterface.
481 setupSriovStates();
482
483 // PCIe SR-IOV spec §9.3.3.3: VF config space must remain hidden
484 // until VF Enable (SR-IOV Control bit 0) is set. All VF functions
485 // were registered above via add_function(); immediately suppress them
486 // with disable_function() so they start in the correct hidden state.
487 for (const auto &state : sriov_states_) {
488 for (int i = 0; i < state.total_vfs; ++i) {
489 const uint16_t fn = static_cast<uint16_t>(
490 state.first_vf_function
491 + i * state.vf_stride);
492 pcie_map_extension_.disable_function(device_id_ | fn);
493 }
494 }
495}
496
497template <unsigned int BUSWIDTH, typename TYPES>
499disconnected(uint16_t device_id) {
500 const uint16_t function_mask =
501 function_number_has_8bits_ ? 0xffu : 0x7u;
502 const uint16_t masked_device_id =
503 device_id & static_cast<uint16_t>(~function_mask);
504 if (masked_device_id != device_id_) {
505 SIM_LOG_ERROR(
506 simulation_obj_, Log_Configuration,
507 "disconnected() called with device_id 0x%x, but currently"
508 " connected to 0x%x", masked_device_id, device_id_);
509 }
510
511 for (auto &fd : function_data_) {
512 delFunction(device_id_ | fd.first);
513 // disabled both MEM and IO
514 fd.second.command = 0;
515 updateMappings(fd.first);
516 }
517}
518
519template <unsigned int BUSWIDTH, typename TYPES>
521 if (reset_) {
522 reset_->hotReset();
523 for (auto &fd : function_data_) {
524 // disabled both MEM and IO
525 fd.second.command = 0;
526 updateMappings(fd.first);
527 }
528 // Undo VF config space routing (governed by VF Enable, bit 0).
529 // BAR map cleanup was already performed by the updateMappings() loop
530 // above (fd.command → 0 for all VF functions including active VFs).
531 // Guard disable_function on VF Enable being set: if VFs were never
532 // enabled (or the device was already disconnected), pcie_map_extension_
533 // has no upstream provider and calling it would abort.
534 for (auto &state : sriov_states_) {
535 if (state.control & 0x1u) { // VF Enable was set
536 updateVfCfgMappings(state, false);
537 }
538 state.control = 0;
539 state.num_vfs = 0;
540 std::fill(std::begin(state.vf_bar), std::end(state.vf_bar), 0u);
541 }
542 }
543}
544
545template <unsigned int BUSWIDTH, typename TYPES>
547 if (reset_) {
548 reset_->warmReset();
549 for (auto &fd : function_data_) {
550 // disabled both MEM and IO
551 fd.second.command = 0;
552 updateMappings(fd.first);
553 }
554 }
555}
556
557template <unsigned int BUSWIDTH, typename TYPES>
558void PcieMappingInterconnect<BUSWIDTH, TYPES>::
559addFunction(uint16_t device_id) {
560 std::ostringstream os;
561 os << "Adding function for device_id: "
562 << device_id;
563 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
564 auto *map_helper = getCfgMapHelper(device_id);
565 if (map_helper == nullptr) {
566 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG,
567 "Failure to get map helper");
568 return;
569 }
570 pcie_map_extension_.add_function(map_helper, device_id);
571 if (sender_.failed_transaction_.payload()) {
572 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG,
573 "Failure to add function");
574 return;
575 }
576}
577
578template <unsigned int BUSWIDTH, typename TYPES>
579void PcieMappingInterconnect<BUSWIDTH, TYPES>::
580delFunction(uint16_t device_id) {
581 std::ostringstream os;
582 os << "Deleting function for device_id: "
583 << device_id;
584 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
585 auto *map_helper = getCfgMapHelper(device_id);
586 if (map_helper == nullptr) {
587 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG,
588 "Failure to get map helper");
589 return;
590 }
591 pcie_map_extension_.del_function(map_helper, device_id);
592 if (sender_.failed_transaction_.payload()) {
593 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG,
594 "Failure to delete function");
595 return;
596 }
597}
598
599template <unsigned int BUSWIDTH, typename TYPES>
600bool PcieMappingInterconnect<BUSWIDTH, TYPES>::
601validateBarInfo(ConfObjectRef o) {
602 // Track (function, offset) pairs to detect duplicates.
603 std::map<std::pair<int, int>, bool> seen;
604 bool valid = true;
605
606 for (const auto &bar : bar_info_) {
607 // Helper lambda: prepend a consistent BAR context prefix.
608 auto err = [&](const std::string &msg) {
609 std::ostringstream os;
610 os << "BAR (function=" << bar.function
611 << ", offset=0x" << std::hex << bar.offset
612 << "): " << msg;
613 SIM_LOG_ERROR_STR(o, Log_Configuration, os.str());
614 valid = false;
615 };
616
617 if (bar.function < 0) {
618 err("function number must be >= 0");
619 }
620
621 // size_bits >= 64 would cause UB in '1ULL << size_bits'
622 if (bar.size_bits <= 0 || bar.size_bits >= 64) {
623 std::ostringstream os;
624 os << "size_bits=" << bar.size_bits
625 << " is out of valid range [1, 63]";
626 err(os.str());
627 }
628
629 // PCIe Base Spec 6.0, Sec 7.5.1.2.1: I/O Space BARs are
630 // always 32-bit; there is no 64-bit I/O BAR support.
631 if (!bar.is_memory && bar.is_64bit) {
632 err("I/O BARs cannot be 64-bit");
633 }
634
635 // BAR offsets must be one of the six 4-byte-aligned standard BAR
636 // slots (0x10..0x24) or the Expansion ROM slot (0x30). Any other
637 // value would produce an out-of-bounds index in retrieveFunctionData
638 // via '(offset - 0x10) / 4'.
639 static constexpr int kValidOffsets[] = {
640 0x10, 0x14, 0x18, 0x1C, 0x20, 0x24, 0x30};
641 if (std::none_of(std::begin(kValidOffsets),
642 std::end(kValidOffsets),
643 [&](int vo) { return bar.offset == vo; })) {
644 std::ostringstream os;
645 os << "offset=0x" << std::hex << bar.offset
646 << " is not a valid BAR offset;"
647 << " expected one of 0x10, 0x14, 0x18, 0x1C, 0x20, 0x24, or"
648 << " 0x30 (Expansion ROM)";
649 err(os.str());
650 }
651
652 // PCIe Base Spec 6.0, Sec 7.5.1.2.4: Expansion ROM BAR
653 // (offset 0x30) must be a memory BAR and cannot be 64-bit.
654 if (bar.offset == 0x30) {
655 if (!bar.is_memory) {
656 err("Expansion ROM BAR must have is_memory=true");
657 }
658 if (bar.is_64bit) {
659 err("Expansion ROM BAR cannot be 64-bit");
660 }
661 }
662
663 // Valid 64-bit BAR offsets: 0x10, 0x18, 0x20 (naturally
664 // aligned to 8 bytes)
665 if (bar.is_64bit && (bar.offset & 7) != 0) {
666 err("64-bit BAR must be naturally aligned to 8-byte boundary; "
667 "offset must be 0x10, 0x18, or 0x20");
668 }
669
670 // For non-64-bit BARs the register is 32-bit wide; a size
671 // exceeding 4 GiB is not addressable (PCIe Base Spec 6.0,
672 // Sec 7.5.1.2.1).
673 if (!bar.is_64bit && bar.size_bits > 32) {
674 std::ostringstream os;
675 os << "32-bit BAR size_bits=" << bar.size_bits
676 << " exceeds maximum of 32";
677 err(os.str());
678 }
679
680 if (bar.target_socket == nullptr) {
681 err("target_socket must not be null");
682 }
683
684 // Duplicate (function, offset) pairs corrupt the internal
685 // socket-ID map built in before_end_of_elaboration.
686 auto key = std::make_pair(bar.function, bar.offset);
687 if (seen.count(key)) {
688 err("duplicate BAR entry; each (function, offset)"
689 " pair must be unique");
690 } else {
691 seen[key] = true;
692 }
693 }
694
695 // Second pass: detect 64-bit BAR slot overlap.
696 // A 64-bit BAR at offset N implicitly consumes the upper-half slot at
697 // N+4. If a separate entry also uses N+4 for the same function,
698 // retrieveFunctionData() would read/write the same base_address slot
699 // inconsistently and before_end_of_elaboration() would build invalid maps.
700 // Collect the set of (function, offset) pairs claimed by 64-bit upper
701 // halves, then flag any explicit entry that collides with them.
702 std::map<std::pair<int, int>, int> upper_half_of;
703 for (const auto &bar : bar_info_) {
704 if (bar.is_64bit && bar.offset != 0x30) {
705 upper_half_of[std::make_pair(bar.function,
706 bar.offset + 4)] = bar.offset;
707 }
708 }
709 for (const auto &bar : bar_info_) {
710 auto it = upper_half_of.find(
711 std::make_pair(bar.function, bar.offset));
712 if (it != upper_half_of.end()) {
713 std::ostringstream os;
714 os << "BAR (function=" << bar.function
715 << ", offset=0x" << std::hex << bar.offset
716 << "): offset is already occupied as the upper 32-bit half"
717 << " of the 64-bit BAR at offset 0x" << it->second;
718 SIM_LOG_ERROR_STR(o, Log_Configuration, os.str());
719 valid = false;
720 }
721 }
722 return valid;
723}
724
725template <unsigned int BUSWIDTH, typename TYPES>
729 iface::PcieResetInterface *reset, ConfObjectRef o,
731 if (pci == nullptr) {
732 SIM_LOG_ERROR(
734 "PcieDeviceQueryInterface not implemented on the device");
735 return;
736 }
737
738 if (bar == nullptr) {
739 SIM_LOG_ERROR(
741 "PcieBaseAddressRegisterQueryInterface not implemented on"
742 " the device");
743 return;
744 }
745
746 if (reset == nullptr) {
747 SIM_LOG_ERROR(o, Log_Configuration,
748 "PcieResetInterface not implemented on the device");
749 return;
750 }
751
752 // Check the BAR info
753 bar_info_ = bar->getBarInfo();
754 if (!validateBarInfo(o)) {
755 bar_info_.clear();
756 return;
757 }
758
759 // Get the config target_socket from the device
760 castToTarget(pci->getConfigTargetSocket(), &config_target_socket_);
761
762 // Get the message target_socket from the device
763 castToTarget(pci->getMsgTargetSocket(), &msg_target_socket_);
764
765 // Set the reset interface
766 reset_ = reset;
767
768 // Calculate how many functions are there (f0 is required)
769 for (const auto &bar : bar_info_) {
770 if (function_data_.find(bar.function) == function_data_.end()) {
771 function_data_[bar.function];
772 if (bar.function > 7) {
773 function_number_has_8bits_ = true;
774 }
775 }
776 }
777
778 // Bind pcie_map_target_socket_ so device can call pcie_map interface
779 auto *initiator = dynamic_cast<
780 tlm::tlm_initiator_socket<BUSWIDTH, TYPES> *>(
782 if (initiator) {
783 pcie_map_target_socket_.bind(*initiator);
784 } else {
785 SIM_LOG_ERROR(o, Log_Configuration,
786 "object returned by getPcieMapInitiatorSocket was not"
787 " a tlm::tlm_initiator_socket.");
788 }
789
790 simulation_obj_ = o;
791 sriov_query_ = sriov;
792 setupSriovStates();
793 createCfgMapHelper();
794}
795
796template <unsigned int BUSWIDTH, typename TYPES>
797std::map<std::pair<size_t, size_t>, size_t>
799 return address_id_mem_map_;
800}
801
802template <unsigned int BUSWIDTH, typename TYPES>
803std::map<std::pair<size_t, size_t>, size_t>
805 return address_id_io_map_;
806}
807
808template <unsigned int BUSWIDTH, typename TYPES>
811 sriov_states_.clear();
812 if (sriov_query_ != nullptr) {
813 for (const auto &info : sriov_query_->getSriovInfo()) {
814 SriovState state = {};
815 state.cap_offset = info.cap_offset;
816 state.pf_function = info.pf_function;
817 state.total_vfs = info.total_vfs;
818 state.first_vf_function = info.first_vf_function;
819 state.vf_stride = (info.vf_stride <= 0) ? 1 : info.vf_stride;
820
821 // SR-IOV spec §9.3.3.10: VF BARs must be Memory Space only.
822 for (const auto &bi : bar_info_) {
823 const int fn = bi.function;
824 const bool is_vf =
825 (fn >= state.first_vf_function)
826 && ((fn - state.first_vf_function) % state.vf_stride == 0)
827 && ((fn - state.first_vf_function) / state.vf_stride
828 < state.total_vfs);
829 if (is_vf && !bi.is_memory) {
830 std::ostringstream oss;
831 oss << "SR-IOV: VF BAR (function=" << fn
832 << " offset=0x" << std::hex << bi.offset
833 << ") has is_memory=false; VF BARs must be"
834 " Memory Space only per PCIe SR-IOV spec";
835 SIM_LOG_ERROR(simulation_obj_, Log_Configuration,
836 "%s", oss.str().c_str());
837 }
838 }
839
840 sriov_states_.push_back(state);
841 }
842 }
843}
844
845// Map or unmap VF config space entries in the Simics pcie_map.
846// Called when VF Enable (SR-IOV Control bit 0) transitions.
847template <unsigned int BUSWIDTH, typename TYPES>
848void PcieMappingInterconnect<BUSWIDTH, TYPES>::
849updateVfCfgMappings(SriovState &state, bool enable) {
850 const int num_vfs = static_cast<int>(state.num_vfs);
851 {
852 std::ostringstream os;
853 os << "SR-IOV PF" << state.pf_function << ": "
854 << (enable ? "enabling " : "disabling ") << num_vfs
855 << " VF config space(s)";
856 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
857 }
858 for (int i = 0; i < num_vfs; ++i) {
859 const uint16_t fn = static_cast<uint16_t>(
860 state.first_vf_function + i * state.vf_stride);
861 if (enable) {
862 pcie_map_extension_.enable_function(device_id_ | fn);
863 } else {
864 pcie_map_extension_.disable_function(device_id_ | fn);
865 }
866 }
867}
868
869// Map or unmap VF BAR memory regions in the Simics pcie_map.
870// Called when VF MSE (SR-IOV Control bit 3) transitions while VF Enable is
871// set, or when VF Enable transitions while VF MSE is already set.
872template <unsigned int BUSWIDTH, typename TYPES>
873void PcieMappingInterconnect<BUSWIDTH, TYPES>::
874updateVfBarMappings(SriovState &state, bool enable) {
875 const int num_vfs = static_cast<int>(state.num_vfs);
876
877 // NumVFs is OS-programmed at runtime; validate it does not exceed the
878 // hardware maximum TotalVFs declared by PcieSriovQueryInterface.
879 if (num_vfs > state.total_vfs) {
880 std::ostringstream os;
881 os << "SR-IOV: NumVFs (" << num_vfs
882 << ") exceeds TotalVFs (" << state.total_vfs
883 << ") for PF" << state.pf_function
884 << " — refusing to update VF BAR mappings";
885 SC_REPORT_WARNING(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
886 return;
887 }
888
889 // Iterate over VF0's BAR entries to discover the layout (size_bits,
890 // is_64bit etc.). The SR-IOV spec §9.3.3.14 guarantees every VF shares
891 // the same BAR layout. vf_bar[i] == (cap+0x24 + i*4) == cfg BAR offset
892 // (0x10+i*4), so the index maps 1:1 to cfg_header_t::base_address[].
893 for (const auto &bi : bar_info_) {
894 if (bi.function != state.first_vf_function || !bi.is_memory)
895 continue;
896
897 const int ba_idx = (bi.offset - 0x10) / 4;
898 if (ba_idx < 0 || ba_idx >= 6)
899 continue;
900
901 const int sz = bi.size_bits;
902 if (sz >= 64) {
903 // Silently skip bad configurations. This should be validated by
904 // the initial query and not here on each map/unmap call.
905 continue;
906 }
907 const uint32_t lo = state.vf_bar[ba_idx];
908 const uint32_t hi = (bi.is_64bit && ba_idx + 1 < 6)
909 ? state.vf_bar[ba_idx + 1] : 0u;
910 const uint64_t vf_bar_raw = (static_cast<uint64_t>(hi) << 32) | lo;
911 const uint64_t align_mask = ~((1ULL << sz) - 1ULL);
912 const uint64_t vf_base = vf_bar_raw & align_mask;
913
914 {
915 std::ostringstream os;
916 os << "SR-IOV PF" << state.pf_function
917 << " VF BAR (cfg 0x" << std::hex << bi.offset << "): "
918 << (enable ? "enabling " : "disabling ")
919 << std::dec << num_vfs << " VF BAR(s)";
920 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
921 }
922
923 // Loop only over the NumVFs VFs that are active (or were active).
924 for (int i = 0; i < num_vfs; ++i) {
925 const uint16_t fn = static_cast<uint16_t>(
926 state.first_vf_function + i * state.vf_stride);
927 auto it = function_data_.find(fn);
928 if (it == function_data_.end()) {
929 std::ostringstream os;
930 os << "SR-IOV PF" << state.pf_function
931 << ": VF function " << fn
932 << " not found in function_data_ — inconsistent SR-IOV"
933 " configuration (getSriovInfo() and getBarInfo()"
934 " disagree)";
935 SC_REPORT_ERROR(PCIE_MAPPING_INTERCONNECT_TAG,
936 os.str().c_str());
937 return;
938 }
939 auto &fd = it->second;
940 // VF Command register has MSE hardwired to 0 (PCIe spec
941 // §7.5.1.1.3) — do not use fd.command to gate VF BAR
942 // mappings. Drive add_map / del_map directly so that the
943 // VF's command register is never forged.
944 const size_t socket_id = fn_bar_id_map_.at(
945 std::make_pair(static_cast<size_t>(fn),
946 static_cast<size_t>(bi.offset)));
947 delMap(socket_id, types::PCIE_Type_Mem);
948 if (enable) {
949 const uint64_t vf_addr =
950 vf_base + static_cast<uint64_t>(i) * (1ULL << sz);
951 fd.base_address[ba_idx] = static_cast<uint32_t>(vf_addr);
952 if (bi.is_64bit && ba_idx + 1 < 7)
953 fd.base_address[ba_idx + 1] =
954 static_cast<uint32_t>(vf_addr >> 32);
955 const types::map_info_t info(
956 vf_addr, vf_addr, 1ULL << sz,
957 static_cast<int>(fn));
958 address_id_mem_map_[std::make_pair(
959 vf_addr, vf_addr + (1ULL << sz))] = socket_id;
960 addMap(info, types::PCIE_Type_Mem);
961 } else {
962 fd.base_address[ba_idx] = 0;
963 if (bi.is_64bit && ba_idx + 1 < 7)
964 fd.base_address[ba_idx + 1] = 0;
965 }
966 }
967 }
968}
969
970template <unsigned int BUSWIDTH, typename TYPES>
971void PcieMappingInterconnect<BUSWIDTH, TYPES>::
972retrieveFunctionData(size_t socket_id, int bar_id) {
973 if (initiator_sockets_.size() <= socket_id) {
974 std::ostringstream os;
975 os << "Invalid socket_id: " << socket_id;
976 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
977 return;
978 }
979
980 tlm::tlm_generic_payload trans;
981 // The data pointer, length and stream width attribute are reused
982 // since they are not modifiable by the target according to the spec
983 uint32_t value = 0;
984 trans.set_data_ptr(reinterpret_cast<unsigned char *>(&value));
985 trans.set_data_length(4);
986 trans.set_streaming_width(4);
987
988 trans.set_command(tlm::TLM_READ_COMMAND);
989 trans.set_address(0x4);
990
991 auto ret = initiator_sockets_[socket_id]->transport_dbg(trans);
992 if (ret == 0) {
993 // The target is not able to perform the transport_dbg.
994 // The PCIe interconnector is unable to get the up-to-date
995 // BAR/CMD information which may cause unexpected behavior
996 return;
997 }
998
999 auto fd = function_data_.begin();
1000 std::advance(fd, socket_id);
1001 fd->second.command = value;
1002
1003 // NOTE: the IO, Mem32, Mem64 bits are ignored by updateMapping,
1004 // no need to filter them out here
1005 for (const auto &bar : bar_info_) {
1006 // BarInfo holds _all_ BARs for _all_ functions. This can probably
1007 // be done in a much more clever way that avoids iterating over the
1008 // entire BarInfo for each function in the outer loop.
1009 if (bar.function != fd->first) {
1010 continue;
1011 }
1012
1013 int expected_bar_id = bar.offset == 0x30 ? 6 : (bar.offset - 0x10) / 4;
1014 // bar_id == -1 => read all bars
1015 if (bar_id != -1 && bar_id != expected_bar_id) {
1016 continue;
1017 }
1018
1019 trans.set_address(bar.offset);
1020 initiator_sockets_[socket_id]->transport_dbg(trans);
1021 fd->second.base_address[expected_bar_id] = value;
1022 if (bar.is_64bit) {
1023 if (expected_bar_id == 6) {
1024 SC_REPORT_ERROR(
1025 PCIE_MAPPING_INTERCONNECT_TAG,
1026 "Expansion ROM BAR can't support 64-bit address");
1027 } else {
1028 // Read higher 32 bits in next BAR
1029 trans.set_address(bar.offset + 4);
1030 initiator_sockets_[socket_id]->transport_dbg(trans);
1031 fd->second.base_address[expected_bar_id + 1] = value;
1032 }
1033 }
1034 }
1035}
1036
1037template <unsigned int BUSWIDTH, typename TYPES>
1038void PcieMappingInterconnect<BUSWIDTH, TYPES>::
1039addMap(types::map_info_t info, types::pcie_type_t type) {
1040 std::ostringstream os;
1041 os << "Adding mapping of type: "
1042 << pcieTypeName(type);
1043 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
1044 pcie_map_extension_.add_map(info, type);
1045 if (sender_.failed_transaction_.payload()) {
1046 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, "Failure to add map");
1047 return;
1048 }
1049}
1050
1051template <unsigned int BUSWIDTH, typename TYPES>
1052void PcieMappingInterconnect<BUSWIDTH, TYPES>::
1053delMap(size_t socket_id, types::pcie_type_t type) {
1054 types::map_info_t::physical_address_t base = 0xffffffffffffffff;
1055 if (type == types::PCIE_Type_Mem) {
1056 auto it = std::find_if(
1057 address_id_mem_map_.begin(),
1058 address_id_mem_map_.end(),
1059 [socket_id](
1060 const std::pair<std::pair<size_t, size_t>, size_t> &entry) {
1061 return entry.second == socket_id;
1062 });
1063
1064 if (it != address_id_mem_map_.end()) {
1065 base = it->first.first;
1066 address_id_mem_map_.erase(it);
1067 }
1068 } else if (type == types::PCIE_Type_IO) {
1069 auto it = std::find_if(
1070 address_id_io_map_.begin(),
1071 address_id_io_map_.end(),
1072 [socket_id](
1073 const std::pair<std::pair<size_t, size_t>, size_t> &entry) {
1074 return entry.second == socket_id;
1075 });
1076
1077 if (it != address_id_io_map_.end()) {
1078 base = it->first.first;
1079 address_id_io_map_.erase(it);
1080 }
1081 }
1082
1083 if (base != 0xffffffffffffffff) {
1084 std::ostringstream os;
1085 os << "Deleting mapping of type: "
1086 << pcieTypeName(type) << ", base: 0x" << std::hex << base;
1087 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
1088
1089 pcie_map_extension_.del_map(base, type);
1090 if (sender_.failed_transaction_.payload()) {
1091 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, "Failure to del map");
1092 return;
1093 }
1094 }
1095}
1096
1097template <unsigned int BUSWIDTH, typename TYPES>
1098void PcieMappingInterconnect<BUSWIDTH, TYPES>::
1099updateMappings(int function_id, int bar_id) {
1100 for (const auto &bi : bar_info_) {
1101 if (function_id != -1 && bi.function != function_id) {
1102 continue;
1103 }
1104
1105 auto header = function_data_.at(bi.function);
1106
1107 int expected_bar_id = bi.offset == 0x30 ? 6 : (bi.offset - 0x10) / 4;
1108 if (bar_id != -1 && bar_id != expected_bar_id) {
1109 continue;
1110 }
1111
1112 uint64_t base_address = header.base_address[expected_bar_id];
1113 if (bi.is_64bit) {
1114 if (expected_bar_id == 6) {
1115 SC_REPORT_ERROR(
1116 PCIE_MAPPING_INTERCONNECT_TAG,
1117 "Expansion ROM BAR can't support 64-bit address");
1118 } else {
1119 base_address |= static_cast<uint64_t>(
1120 header.base_address[expected_bar_id + 1]) << 32;
1121 }
1122 }
1123
1124 bool enable = header.command & 1;
1126 if (bi.is_memory) {
1127 type = types::PCIE_Type_Mem;
1128 if (bi.offset == 0x30) {
1129 enable = base_address & 1;
1130 } else {
1131 enable = (header.command >> 1) & 1;
1132 }
1133 }
1134 base_address &= ~((1ULL << bi.size_bits) - 1);
1135 size_t socket_id = fn_bar_id_map_.at(
1136 std::make_pair(bi.function, bi.offset));
1137 delMap(socket_id, type);
1138 if (enable) {
1139 // The `start` field in map_info_t is set to base,
1140 // so later when receiving the transaction, the address
1141 // can be used to route the transaction to the right socket
1142 types::map_info_t info(base_address, base_address,
1143 1ULL << bi.size_bits, function_id);
1144 auto address_range = std::make_pair(info.base,
1145 info.base + info.length);
1146
1147 if (bi.is_memory) {
1148 address_id_mem_map_[address_range] = socket_id;
1149 std::ostringstream os;
1150 os << "Memory address for function " << bi.function
1151 << " and bar ID " << expected_bar_id
1152 << " in range of [0x" << std::hex << info.base
1153 << "-0x" << info.base + info.length << "] maps to socket ID "
1154 << socket_id;
1155 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
1156 } else {
1157 address_id_io_map_[address_range] = socket_id;
1158 std::ostringstream os;
1159 os << "IO address for function " << bi.function
1160 << " and bar ID " << expected_bar_id
1161 << " in range of [0x" << std::hex << info.base
1162 << "-0x" << info.base + info.length << "] maps to socket ID "
1163 << socket_id;
1164 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
1165 }
1166 addMap(info, type);
1167 }
1168 }
1169}
1170
1171template <unsigned int BUSWIDTH, typename TYPES>
1172void PcieMappingInterconnect<BUSWIDTH, TYPES>::
1173updateCommand(uint16_t function_id, uint16_t old_command) {
1174 if (function_data_[function_id].command != old_command) {
1175 updateMappings(function_id);
1176 }
1177}
1178
1179template <unsigned int BUSWIDTH, typename TYPES>
1180void PcieMappingInterconnect<BUSWIDTH, TYPES>::
1181interceptCfgHeaderAccess(size_t socket_id,
1182 tlm::tlm_generic_payload &trans) { // NOLINT
1183 unsigned int size = trans.get_data_length();
1184 // Check assumptions (but leave it up to target device to report errors)
1185 if (trans.get_byte_enable_ptr() || trans.get_streaming_width() != size) {
1186 return;
1187 }
1188
1189 // We are only interested in write access to BARn or Command regs
1190 if (!trans.is_write()) {
1191 return;
1192 }
1193
1194 // Maximum size is 8
1195 uint64_t *data = reinterpret_cast<uint64_t *>(trans.get_data_ptr());
1196
1197 auto it = function_data_.begin();
1198 std::advance(it, socket_id);
1199 uint16_t function_id = it->first;
1200 auto &header = it->second;
1201 sc_dt::uint64 offset = trans.get_address();
1202 switch (offset) {
1203 case 0x04: { // command & status
1204 uint16_t old_command = header.command;
1205 retrieveFunctionData(socket_id, -1);
1206 header.command = (*data) & 0xffff; // ignore status
1207 updateCommand(function_id, old_command);
1208 break;
1209 }
1210 case 0x10: // BAR0-BAR5
1211 case 0x14:
1212 case 0x18:
1213 case 0x1c:
1214 case 0x20:
1215 case 0x24: {
1216 int bar_id = (offset - 0x10) / 4;
1217 retrieveFunctionData(socket_id, bar_id);
1218 // NOTE: the IO, Mem32, Mem64 bits are ignored by
1219 // updateMapping, no need to filter them out here. In
1220 // addition, any bits less than the size of the BAR as defined
1221 // by the BarInfo will also be ignored/truncated by
1222 // updateMapping; making sure we always send a naturally
1223 // aligned address to Simics
1224 header.base_address[bar_id] = *data & 0xffffffff;
1225 if (size == 8 && !(offset % 8)) {
1226 header.base_address[bar_id + 1] = *data >> 32;
1227 }
1228 updateMappings(function_id, bar_id);
1229 break;
1230 }
1231 case 0x30: // Expansion ROM BAR
1232 int bar_id = 6;
1233 retrieveFunctionData(socket_id, bar_id);
1234 header.base_address[bar_id] = *data & 0xffffffff;
1235 updateMappings(function_id, bar_id);
1236 break;
1237 }
1238
1239 // SR-IOV Extended Capability snooping — iterate all PF SR-IOV groups.
1240 for (auto &state : sriov_states_) {
1241 if (static_cast<int>(function_id) != state.pf_function)
1242 continue;
1243 const sc_dt::uint64 cap = state.cap_offset;
1244 const uint32_t written = static_cast<uint32_t>(*data);
1245 if (offset == cap + 0x08) {
1246 const uint16_t old_ctrl = state.control;
1247 state.control = static_cast<uint16_t>(written & 0xFFFFu);
1248 // Per PCIe SR-IOV spec §9.3.3.3, the two bits have separate roles:
1249 // VF Enable (bit 0): VFs become enumerable; their config space
1250 // is accessible → enable_function / disable_function.
1251 // VF MSE (bit 3): VF memory space (BARs) responds to memory
1252 // transactions; requires VF Enable to be set first
1253 // → add_map / del_map.
1254 // The two transitions are handled independently so that software
1255 // can set VF Enable alone to probe VF config space before
1256 // committing BAR resources.
1257 const bool vf_enable_was = old_ctrl & 0x1u;
1258 const bool vf_enable_now = state.control & 0x1u;
1259 const bool vf_mse_was = (old_ctrl >> 3) & 0x1u;
1260 const bool vf_mse_now = (state.control >> 3) & 0x1u;
1261 const bool bars_were_live = vf_enable_was && vf_mse_was;
1262 const bool bars_are_live = vf_enable_now && vf_mse_now;
1263 if (vf_enable_was != vf_enable_now) {
1264 updateVfCfgMappings(state, vf_enable_now);
1265 }
1266 if (bars_were_live != bars_are_live) {
1267 updateVfBarMappings(state, bars_are_live);
1268 }
1269 } else if (offset == cap + 0x10) { // NumVFs (OS-programmed)
1270 state.num_vfs = static_cast<uint16_t>(written & 0xFFFFu);
1271 } else if (offset >= cap + 0x24 && offset < cap + 0x24 + 6 * 4) {
1272 // VF BAR registers +0x24..+0x38 (6 × 4-byte slots, §9.3.3.14).
1273 // Index = (cap_rel - 0x24)/4 == (vf_cfg_offset - 0x10)/4,
1274 // matching vf_bar[] and cfg_header_t::base_address[] directly.
1275 const int idx = static_cast<int>((offset - (cap + 0x24)) / 4);
1276 state.vf_bar[idx] = written;
1277 // §9.3.3.14: a VF BAR write changes the base address for ALL
1278 // VFs (BARb_VFv = VF_BARb + v×aperture). If BARs are live
1279 // (VF Enable && VF MSE), del_map old ranges and add_map new
1280 // ranges for every active VF.
1281 const bool vf_en = (state.control & 0x1u) != 0;
1282 const bool vf_mse = ((state.control >> 3) & 0x1u) != 0;
1283 if (vf_en && vf_mse)
1284 updateVfBarMappings(state, true);
1285 }
1286 break; // each function_id matches at most one PF group
1287 }
1288}
1289
1290template <unsigned int BUSWIDTH, typename TYPES>
1291size_t PcieMappingInterconnect<BUSWIDTH, TYPES>::
1292findSocketId(types::pcie_type_t type, uint16_t function_id,
1293 tlm::tlm_generic_payload &trans) const { // NOLINT
1294 if (type == types::PCIE_Type_Cfg) {
1295 auto found = function_data_.find(function_id);
1296 if (found == function_data_.end()) {
1297 std::ostringstream os;
1298 os << "Function id(" << function_id
1299 << ") not found for this device";
1300 throw std::invalid_argument {
1301 os.str()
1302 };
1303 }
1304 return std::distance(function_data_.begin(), found);
1305 } else if (type == types::PCIE_Type_Mem) {
1306 size_t address = trans.get_address();
1307 for (const auto &entry : address_id_mem_map_) {
1308 auto startAddress = entry.first.first;
1309 auto endAddress = entry.first.second;
1310 auto id = entry.second;
1311
1312 if (address >= startAddress && address < endAddress) {
1313 if (enable_base_address_subtraction) {
1314 trans.set_address(address - startAddress);
1315 }
1316 return id;
1317 }
1318 }
1319 throw std::invalid_argument {
1320 "Unable to find the initiator_socket to forward the MEM transaction"
1321 };
1322 } else if (type == types::PCIE_Type_IO) {
1323 size_t address = trans.get_address();
1324 for (const auto &entry : address_id_io_map_) {
1325 auto startAddress = entry.first.first;
1326 auto endAddress = entry.first.second;
1327 auto id = entry.second;
1328
1329 if (address >= startAddress && address < endAddress) {
1330 if (enable_base_address_subtraction) {
1331 trans.set_address(address - startAddress);
1332 }
1333 return id;
1334 }
1335 }
1336 throw std::invalid_argument {
1337 "Unable to find the initiator_socket to forward the IO transaction"
1338 };
1339 } else if (type == types::PCIE_Type_Msg) {
1340 return function_data_.size() + bar_info_.size();
1341 }
1342 throw std::invalid_argument {
1343 "Unsupported PCIe type"
1344 };
1345}
1346
1347template <unsigned int BUSWIDTH, typename TYPES>
1350 auto *map_helper = SIM_get_class("pcie_map_helper_cpp");
1351 if (map_helper == nullptr) {
1352 SIM_LOG_ERROR(
1353 simulation_obj_, Log_Configuration,
1354 "Simics class pcie_map_helper_cpp is required."
1355 "Try load the module pcie-map-helper-c++ first.");
1356 return;
1357 }
1358
1359 if (SIM_c_get_interface(simulation_obj_, "pcie_device") == nullptr) {
1360 // Make sure simulation_obj_ is ready to be used by other objects
1361 if (!SIM_object_is_configured(simulation_obj_)) {
1362 SIM_LOG_ERROR(
1363 simulation_obj_, Log_Configuration,
1364 "Simulation object is not configured yet, cannot get"
1365 " its gasket_list attribute");
1366 return;
1367 }
1368
1369 // For PCIe gasket object, the interface is not implemented
1370 // on the simulation object, but rather the gasket object
1371 auto gasket_list = SIM_get_attribute(simulation_obj_, "gasket_list");
1372 int gasket_count = SIM_attr_list_size(gasket_list);
1373 for (int i = 0; i < gasket_count; ++i) {
1374 auto obj = SIM_attr_object(SIM_attr_list_item(gasket_list, i));
1375 if (SIM_c_get_interface(obj, "pcie_device")) {
1376 gasket_obj_ = obj;
1377 break;
1378 }
1379 }
1380 if (gasket_obj_.object() == nullptr) {
1381 SIM_LOG_ERROR(
1382 simulation_obj_, Log_Configuration,
1383 "No gasket object implemented the required"
1384 " pcie_device interface");
1385 return;
1386 }
1387 } else {
1388 gasket_obj_ = simulation_obj_.object();
1389 }
1390
1391 for (const auto &fd : function_data_) {
1392 auto pcie_type = std_to_attr<>(
1393 std::pair<std::string, int>("pcie_type", types::PCIE_Type_Cfg));
1394 auto forward_target = std_to_attr<>(
1395 std::pair<std::string, ConfObjectRef>("forward_target",
1396 gasket_obj_));
1397 auto function_id = std_to_attr<>(
1398 std::pair<std::string, int>("function_id", fd.first));
1399 attr_value_t attr = SIM_make_attr_list(3, pcie_type, forward_target,
1400 function_id);
1401
1402 auto obj_name = gasket_obj_.name() + ".port.cfg" + \
1403 std::to_string(fd.first);
1404 auto *o = SIM_get_object(obj_name.c_str());
1405 if (o == nullptr) {
1406 std::ignore = SIM_clear_exception();
1407 o = SIM_create_object(map_helper, obj_name.c_str(), attr);
1408 }
1409 assert(o);
1410 SIM_attr_free(&attr);
1411 }
1412}
1413
1414template <unsigned int BUSWIDTH, typename TYPES>
1416getCfgMapHelper(uint16_t device_id) {
1417 std::string name = "port.cfg";
1418 if (function_number_has_8bits_) {
1419 name += std::to_string(device_id & 0xff);
1420 } else {
1421 name += std::to_string(device_id & 7);
1422 }
1423 auto map_helper = SIM_object_descendant(gasket_obj_, name.c_str());
1424 if (!map_helper) {
1425 SIM_LOG_CRITICAL(simulation_obj_, Log_Configuration,
1426 "Could not find map helper object with name '%s.%s'",
1427 gasket_obj_.name().c_str(), name.c_str());
1428 }
1429 return map_helper;
1430}
1431
1432template <unsigned int BUSWIDTH, typename TYPES>
1433size_t PcieMappingInterconnect<BUSWIDTH, TYPES>::
1434validateTransaction(tlm::tlm_generic_payload &trans) { // NOLINT
1435 PcieTlmExtension *pcie_ext = nullptr;
1436 trans.get_extension<PcieTlmExtension>(pcie_ext);
1437
1438 if (pcie_ext == nullptr) {
1439 trans.set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE);
1440 SC_REPORT_ERROR(PCIE_MAPPING_INTERCONNECT_TAG,
1441 "PcieTlmExtension is required but not found");
1442 return 0;
1443 }
1444
1445 std::ostringstream os;
1446 os << "Received a PCIe transaction with type "
1447 << pcieTypeName(pcie_ext->type);
1448 if (pcie_ext->device_id_set) {
1449 os << ", " << pcieDeviceIdStr(pcie_ext->device_id);
1450 }
1451 os << ", address 0x" << std::hex << trans.get_address() << std::endl;
1452 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
1453
1454 if (pcie_ext->type == types::PCIE_Type_Not_Set
1455 || pcie_ext->type == types::PCIE_Type_Other) {
1456 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG,
1457 "Only support following PCIe types: MEM/IO/CFG/MSG");
1458 trans.set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE);
1459 return 0;
1460 }
1461
1462 if (pcie_ext->type == types::PCIE_Type_Cfg
1463 && !pcie_ext->device_id_set) {
1464 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG,
1465 "PCIe device ID ATOM is required");
1466 trans.set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE);
1467 return 0;
1468 }
1469
1470 size_t socket_id = 0;
1471 try {
1472 // For ARI (function numbers > 7) use 8-bit function extraction,
1473 // otherwise mask to the standard 3-bit function field.
1474 const uint16_t fn = function_number_has_8bits_
1475 ? (pcie_ext->device_id & 0xFF)
1476 : (pcie_ext->device_id & 7);
1477 socket_id = findSocketId(pcie_ext->type, fn, trans);
1478 } catch (const std::exception &e) {
1479 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, e.what());
1480 trans.set_response_status(tlm::TLM_ADDRESS_ERROR_RESPONSE);
1481 return 0;
1482 }
1483
1484 if (socket_id >= initiator_sockets_.size()) {
1485 os.clear();
1486 os << "Initiator socket ID " << socket_id << " not created yet";
1487 SC_REPORT_INFO(PCIE_MAPPING_INTERCONNECT_TAG, os.str().c_str());
1488 trans.set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE);
1489 return 0;
1490 }
1491
1492 if (pcie_ext->type == types::PCIE_Type_Cfg) {
1493 interceptCfgHeaderAccess(socket_id, trans);
1494 }
1495
1496 return socket_id;
1497}
1498
1499template <unsigned int BUSWIDTH, typename TYPES>
1500void PcieMappingInterconnect<BUSWIDTH, TYPES>::
1501transaction_b_transport(tlm::tlm_generic_payload &trans, // NOLINT: SystemC API
1502 sc_core::sc_time &t) { // NOLINT: SystemC API
1503 trans.set_response_status(tlm::TLM_OK_RESPONSE);
1504 size_t socket_id = validateTransaction(trans);
1505 if (trans.get_response_status() == tlm::TLM_OK_RESPONSE) {
1506 initiator_sockets_.at(socket_id)->b_transport(trans, t);
1507 }
1508}
1509
1510template <unsigned int BUSWIDTH, typename TYPES>
1511unsigned int PcieMappingInterconnect<BUSWIDTH, TYPES>::
1512transaction_transport_dbg(tlm::tlm_generic_payload &trans) { // NOLINT
1513 trans.set_response_status(tlm::TLM_OK_RESPONSE);
1514 size_t socket_id = validateTransaction(trans);
1515 if (trans.get_response_status() == tlm::TLM_OK_RESPONSE) {
1516 return initiator_sockets_.at(socket_id)->transport_dbg(trans);
1517 } else {
1518 return 0;
1519 }
1520}
1521
1522/*
1523 * For pcie-map, just relay the incoming BW transaction to target socket
1524 */
1525template <unsigned int BUSWIDTH, typename TYPES>
1526void PcieMappingInterconnect<BUSWIDTH, TYPES>::
1527pcie_map_invalidate_direct_mem_ptr(sc_dt::uint64 start_range,
1528 sc_dt::uint64 end_range) {
1529 pcie_map_target_socket_->invalidate_direct_mem_ptr(start_range,
1530 end_range);
1531}
1532
1533/*
1534 * For pcie-map, just relay the incoming transaction to outgoing socket
1535 */
1536template <unsigned int BUSWIDTH, typename TYPES>
1537void PcieMappingInterconnect<BUSWIDTH, TYPES>::pcie_map_b_transport(
1538 tlm::tlm_generic_payload &trans, // NOLINT
1539 sc_core::sc_time &t) { // NOLINT
1540 pcie_map_initiator_socket->b_transport(trans, t);
1541}
1542
1543/*
1544 * For pcie-map, just relay the incoming transaction to outgoing socket
1545 */
1546template <unsigned int BUSWIDTH, typename TYPES>
1547unsigned int PcieMappingInterconnect<BUSWIDTH, TYPES>::pcie_map_transport_dbg(
1548 tlm::tlm_generic_payload &trans) { // NOLINT
1549 return pcie_map_initiator_socket->transport_dbg(trans);
1550}
1551
1552/*
1553 * For pcie-map, just relay the incoming transaction to outgoing socket
1554 */
1555template <unsigned int BUSWIDTH, typename TYPES>
1556bool PcieMappingInterconnect<BUSWIDTH, TYPES>::pcie_map_get_direct_mem_ptr(
1557 tlm::tlm_generic_payload &trans, // NOLINT
1558 tlm::tlm_dmi &dmi_data) { // NOLINT
1559 return pcie_map_initiator_socket->get_direct_mem_ptr(trans, dmi_data);
1560}
1561
1562template <unsigned int BUSWIDTH, typename TYPES>
1563void PcieMappingInterconnect<BUSWIDTH, TYPES>::
1564castToTarget(sc_core::sc_object *object, supported_target_socket_t *target) {
1565 if (object == nullptr) {
1566 SC_REPORT_ERROR(PCIE_MAPPING_INTERCONNECT_TAG,
1567 "Unable to castToTarget from a nullptr");
1568 return;
1569 }
1570
1571 auto *tlm_target = dynamic_cast<
1572 tlm::tlm_target_socket<BUSWIDTH, TYPES> *>(object);
1573 if (tlm_target) {
1574 target->tlm = tlm_target;
1575 return;
1576 }
1577
1578 auto *multi_target = dynamic_cast<
1579 tlm_utils::multi_target_base<BUSWIDTH, TYPES> *>(object);
1580 if (multi_target) {
1581 target->multi = multi_target;
1582 return;
1583 }
1584
1585 SC_REPORT_ERROR(PCIE_MAPPING_INTERCONNECT_TAG,
1586 "Unable to dynamic-cast PCIe target socket");
1587}
1588
1589} // namespace composite
1590} // namespace systemc
1591} // namespace simics
1592
1593#endif
void send_extension(iface::Transaction *transaction) override
Called by extension after the extension is set on the payload.
Definition: pcie_mapping_interconnect.h:88
virtual ~PcieMappingInterconnectExtensionSender()
Definition: pcie_mapping_interconnect.h:85
iface::Transaction failed_transaction_
Definition: pcie_mapping_interconnect.h:97
void send_failed(iface::Transaction *transaction) override
Called by extension if method_call invocation was missing.
Definition: pcie_mapping_interconnect.h:92
Definition: pcie_mapping_interconnect.h:107
PcieMappingInterconnect(sc_core::sc_module_name="PcieMappingInterconnect")
Definition: pcie_mapping_interconnect.h:122
void connected(uint16_t device_id)
Definition: pcie_mapping_interconnect.h:452
void before_end_of_elaboration() override
Definition: pcie_mapping_interconnect.h:409
bool enable_base_address_subtraction
Definition: pcie_mapping_interconnect.h:215
initiator_socket_t pcie_map_initiator_socket
IC -> Simics, forwards the upstream pcie-map transactions.
Definition: pcie_mapping_interconnect.h:210
void connect(iface::PcieDeviceQueryInterface *pci, iface::PcieBaseAddressRegisterQueryInterface *bar, iface::PcieResetInterface *reset, ConfObjectRef o, iface::PcieSriovQueryInterface *sriov=nullptr)
Called by (outer) composite class to retrieve the required information to connect the IC with the PCI...
Definition: pcie_mapping_interconnect.h:727
void hotReset()
Definition: pcie_mapping_interconnect.h:520
void createCfgMapHelper()
Definition: pcie_mapping_interconnect.h:1349
target_socket_t pcie_device_target_socket
Definition: pcie_mapping_interconnect.h:208
std::map< std::pair< size_t, size_t >, size_t > addressIdMemMap() const
Definition: pcie_mapping_interconnect.h:798
target_socket_t transaction_target_socket
Definition: pcie_mapping_interconnect.h:206
void disconnected(uint16_t device_id)
Definition: pcie_mapping_interconnect.h:499
sc_core::sc_in< bool > warm_reset_pin
Definition: pcie_mapping_interconnect.h:212
std::map< std::pair< size_t, size_t >, size_t > addressIdIoMap() const
Definition: pcie_mapping_interconnect.h:804
Generic extension sender initialized with a TLM2 initiator socket of TSocket type.
Definition: extension_sender.h:33
void send_extension(Transaction *transaction) override
Called by extension after the extension is set on the payload.
Definition: extension_sender.h:53
void init(TSocket *socket)
Definition: extension_sender.h:38
Transaction transaction() override
Called by extension to get a new Transaction.
Definition: extension_sender.h:47
void send_failed(Transaction *transaction) override
Called by extension if method_call invocation was missing.
Definition: extension_sender.h:56
void init(ExtensionSenderInterface *sender, tlm::tlm_generic_payload *payload)
Deprecated, use the init(ExtensionSenderInterface *sender) instead.
Definition: extension.h:43
Interface that allows the Simics glue to perform snooping and automatic connection of the device's ta...
Definition: pcie_device_query_interface.h:51
virtual std::vector< PCIeBar > getBarInfo()=0
BAR register information.
Interface required from a SystemC PCIe device in order to connect to Simics.
Definition: pcie_device_query_interface.h:30
virtual sc_core::sc_object * getMsgTargetSocket()=0
virtual sc_core::sc_object * getConfigTargetSocket()=0
virtual sc_core::sc_object * getPcieMapInitiatorSocket()=0
Extension for Simics pcie_map interface.
Definition: pcie_map_extension.h:26
Definition: pcie_device_query_interface.h:93
Interface providing static SR-IOV configuration to the PcieMappingInterconnect.
Definition: pcie_device_query_interface.h:124
Class that encapsulates a generic_payload and returns it to the TransactionPool when the Transaction ...
Definition: transaction.h:29
conf_object_t * SIM_create_object(conf_class_t *NOTNULL cls, const char *name, attr_value_t attrs)
conf_object_t * SIM_get_object(const char *NOTNULL name)
attr_value_t SIM_get_attribute(conf_object_t *NOTNULL obj, const char *name)
conf_class_t * SIM_get_class(const char *NOTNULL name)
@ Log_Configuration
Definition: adapter_log_groups.h:23
pcie_type_t
Definition: pcie_type.h:20
@ PCIE_Type_Cfg
Definition: pcie_type.h:24
@ PCIE_Type_Mem
Definition: pcie_type.h:22
@ PCIE_Type_Msg
Definition: pcie_type.h:25
@ PCIE_Type_Not_Set
Definition: pcie_type.h:21
@ PCIE_Type_Other
Definition: pcie_type.h:26
@ PCIE_Type_IO
Definition: pcie_type.h:23
Definition: adapter.h:79
Reduced, stand-alone, version of the Simics map_info_t struct.
Definition: map_info.h:23
uint64_t physical_address_t
Definition: map_info.h:24