xref: /openbmc/bios-bmc-smm-error-logger/src/pci_handler.cpp (revision 454b30407b9d759103da57b41dd364eaf9114c33)
1 #include "pci_handler.hpp"
2 
3 #include <fcntl.h>
4 
5 #include <stdplus/fd/managed.hpp>
6 #include <stdplus/fd/mmap.hpp>
7 #include <stdplus/print.hpp>
8 
9 #include <cstdint>
10 #include <cstring>
11 #include <format>
12 #include <memory>
13 #include <span>
14 #include <vector>
15 
16 namespace bios_bmc_smm_error_logger
17 {
18 
PciDataHandler(uint32_t regionAddress,size_t regionSize,std::unique_ptr<stdplus::fd::Fd> fd)19 PciDataHandler::PciDataHandler(uint32_t regionAddress, size_t regionSize,
20                                std::unique_ptr<stdplus::fd::Fd> fd) :
21     regionSize(regionSize), fd(std::move(fd)),
22     mmap(stdplus::fd::MMap(
23         *this->fd, regionSize, stdplus::fd::ProtFlags{PROT_READ | PROT_WRITE},
24         stdplus::fd::MMapFlags{stdplus::fd::MMapAccess::Shared}, regionAddress))
25 {}
26 
read(const uint32_t offset,const uint32_t length)27 std::vector<uint8_t> PciDataHandler::read(const uint32_t offset,
28                                           const uint32_t length)
29 {
30     if (offset > regionSize || length == 0)
31     {
32         stdplus::print(stderr,
33                        "[read] Offset [{}] was bigger than regionSize [{}] "
34                        "OR length [{}] was equal to 0\n",
35                        offset, regionSize, length);
36         return {};
37     }
38 
39     // Read up to regionSize in case the offset + length overflowed
40     uint32_t finalLength =
41         (offset + length < regionSize) ? length : regionSize - offset;
42     std::vector<uint8_t> results(finalLength);
43 
44     // Use a volatile pointer to ensure every access reads directly from the
45     // memory-mapped region, preventing compiler optimizations like caching.
46     const volatile uint8_t* src =
47         reinterpret_cast<volatile const uint8_t*>(mmap.get().data() + offset);
48 
49     // Perform a byte-by-byte copy to avoid undefined behavior with memcpy on
50     // volatile memory.
51     for (uint32_t i = 0; i < finalLength; ++i)
52     {
53         results[i] = src[i];
54     }
55     return results;
56 }
57 
write(const uint32_t offset,const std::span<const uint8_t> bytes)58 uint32_t PciDataHandler::write(const uint32_t offset,
59                                const std::span<const uint8_t> bytes)
60 {
61     const size_t length = bytes.size();
62     if (offset > regionSize || length == 0)
63     {
64         stdplus::print(stderr,
65                        "[write] Offset [{}] was bigger than regionSize [{}] "
66                        "OR length [{}] was equal to 0\n",
67                        offset, regionSize, length);
68         return 0;
69     }
70 
71     // Write up to regionSize in case the offset + length overflowed
72     uint16_t finalLength =
73         (offset + length < regionSize) ? length : regionSize - offset;
74     // Use a volatile pointer to ensure every access writes directly to the
75     // memory-mapped region.
76     volatile uint8_t* dest =
77         reinterpret_cast<volatile uint8_t*>(mmap.get().data() + offset);
78 
79     // Perform a byte-by-byte copy to ensure volatile semantics.
80     for (uint16_t i = 0; i < finalLength; ++i)
81     {
82         dest[i] = bytes[i];
83     }
84     return finalLength;
85 }
86 
getMemoryRegionSize()87 uint32_t PciDataHandler::getMemoryRegionSize()
88 {
89     return regionSize;
90 }
91 
92 } // namespace bios_bmc_smm_error_logger
93