rogue
Loading...
Searching...
No Matches
SrpV3Emulation.cpp
Go to the documentation of this file.
1
17#include "rogue/Directives.h"
18
20
21#include <inttypes.h>
22#include <stdint.h>
23
24#include <cstring>
25#include <memory>
26#include <random>
27#include <utility>
28#include <vector>
29
30#include "rogue/GilRelease.h"
31#include "rogue/Logging.h"
37
38namespace rps = rogue::protocols::srp;
40
41#ifndef NO_PYTHON
42 #include <boost/python.hpp>
43namespace bp = boost::python;
44#endif
45
47rps::SrpV3EmulationPtr rps::SrpV3Emulation::create() {
48 rps::SrpV3EmulationPtr p = std::make_shared<rps::SrpV3Emulation>();
49 return (p);
50}
51
53void rps::SrpV3Emulation::setup_python() {
54#ifndef NO_PYTHON
55 bp::class_<rps::SrpV3Emulation, rps::SrpV3EmulationPtr, bp::bases<ris::Master, ris::Slave>, boost::noncopyable>(
56 "SrpV3Emulation",
57 bp::init<>());
58 bp::implicitly_convertible<rps::SrpV3EmulationPtr, ris::MasterPtr>();
59 bp::implicitly_convertible<rps::SrpV3EmulationPtr, ris::SlavePtr>();
60#endif
61}
62
64rps::SrpV3Emulation::SrpV3Emulation() : ris::Master(), ris::Slave() {
65 log_ = rogue::Logging::create("SrpV3Emulation");
66 totAlloc_ = 0;
67 threadEn_ = true;
68 thread_ = std::thread(&SrpV3Emulation::runThread, this);
69}
70
72rps::SrpV3Emulation::~SrpV3Emulation() {
73 stop();
74
75 MemoryMap::iterator it = memMap_.begin();
76 while (it != memMap_.end()) {
77 free(it->second);
78 ++it;
79 }
80}
81
83void rps::SrpV3Emulation::stop() {
84 {
85 std::lock_guard<std::mutex> lock(queMtx_);
86 threadEn_ = false;
87 }
88 queCond_.notify_all();
89
90 if (thread_.joinable()) thread_.join();
91
92 ris::Master::stop();
93}
94
96void rps::SrpV3Emulation::runThread() {
97 ris::FramePtr frame;
98 log_->debug("Worker thread started");
99
100 while (threadEn_) {
101 {
102 std::unique_lock<std::mutex> lock(queMtx_);
103 queCond_.wait(lock, [this] { return !queue_.empty() || !threadEn_; });
104
105 if (!threadEn_) break;
106
107 frame = queue_.front();
108 queue_.pop();
109 }
110
111 processFrame(frame);
112 }
113
114 log_->debug("Worker thread stopped");
115}
116
118uint8_t* rps::SrpV3Emulation::allocatePage(uint64_t addr4k) {
119 uint8_t* page = reinterpret_cast<uint8_t*>(malloc(0x1000));
120 if (page == nullptr) {
121 log_->error("Failed to allocate page at 0x%" PRIx64, addr4k);
122 return nullptr;
123 }
124
125 // Fill with random data to emulate uninitialized hardware memory
126 std::mt19937 gen(std::random_device {}());
127 uint32_t* p32 = reinterpret_cast<uint32_t*>(page);
128 for (size_t i = 0; i < 0x1000 / 4; i++) p32[i] = gen();
129
130 memMap_.insert(std::make_pair(addr4k, page));
131 totAlloc_++;
132 log_->debug("Allocating page at 0x%" PRIx64 ". Total pages %" PRIu32, addr4k, totAlloc_);
133 return page;
134}
135
137void rps::SrpV3Emulation::readMemory(uint64_t address, uint8_t* data, uint32_t size) {
138 uint64_t addr4k;
139 uint64_t off4k;
140 uint64_t size4k;
141
142 while (size > 0) {
143 addr4k = (address / 0x1000) * 0x1000;
144 off4k = address % 0x1000;
145 size4k = (addr4k + 0x1000) - (addr4k + off4k);
146
147 if (size4k > size) size4k = size;
148
149 auto it = memMap_.find(addr4k);
150 if (it == memMap_.end()) {
151 // Allocate page with random data on first read (like uninitialized SRAM)
152 uint8_t* page = allocatePage(addr4k);
153 if (page == nullptr) return;
154 memcpy(data, page + off4k, size4k);
155 } else {
156 memcpy(data, it->second + off4k, size4k);
157 }
158
159 size -= size4k;
160 address += size4k;
161 data += size4k;
162 }
163}
164
166void rps::SrpV3Emulation::writeMemory(uint64_t address, const uint8_t* data, uint32_t size) {
167 uint64_t addr4k;
168 uint64_t off4k;
169 uint64_t size4k;
170
171 while (size > 0) {
172 addr4k = (address / 0x1000) * 0x1000;
173 off4k = address % 0x1000;
174 size4k = (addr4k + 0x1000) - (addr4k + off4k);
175
176 if (size4k > size) size4k = size;
177
178 auto it = memMap_.find(addr4k);
179 if (it == memMap_.end()) {
180 uint8_t* page = allocatePage(addr4k);
181 if (page == nullptr) return;
182 it = memMap_.find(addr4k);
183 }
184
185 memcpy(it->second + off4k, data, size4k);
186
187 size -= size4k;
188 address += size4k;
189 data += size4k;
190 }
191}
192
194void rps::SrpV3Emulation::acceptFrame(ris::FramePtr frame) {
195 {
196 std::lock_guard<std::mutex> lock(queMtx_);
197 queue_.push(frame);
198 }
199 queCond_.notify_one();
200}
201
203void rps::SrpV3Emulation::processFrame(ris::FramePtr frame) {
204 uint32_t header[HeadLen / 4];
205 uint32_t tail[TailLen / 4];
206 uint32_t fSize;
207 uint32_t opCode;
208 uint32_t id;
209 uint64_t address;
210 uint32_t reqSize;
211 bool doWrite;
212 bool isPost;
213
214 rogue::GilRelease noGil;
215 ris::FrameLockPtr frLock = frame->lock();
216
217 if (frame->getError()) {
218 log_->warning("Got errored frame = 0x%" PRIx8, frame->getError());
219 return;
220 }
221
222 // Check minimum frame size
223 fSize = frame->getPayload();
224 if (fSize < HeadLen) {
225 log_->warning("Got undersized frame size = %" PRIu32, fSize);
226 return;
227 }
228
229 // Read header
230 ris::FrameIterator fIter = frame->begin();
231 ris::fromFrame(fIter, HeadLen, header);
232
233 // Verify SRPv3 version
234 if ((header[0] & 0xFF) != 0x03) {
235 log_->warning("Got non-SRPv3 frame, version = 0x%" PRIx32, header[0] & 0xFF);
236 return;
237 }
238
239 // Extract fields
240 opCode = (header[0] >> 8) & 0x3;
241 id = header[1];
242 address = (static_cast<uint64_t>(header[3]) << 32) | header[2];
243 reqSize = header[4] + 1;
244
245 // Validate opCode: 0=read, 1=write, 2=posted write
246 if (opCode == 0x3) {
247 log_->warning("Got invalid SRPv3 opcode 0x3 for id=%" PRIu32, id);
248 return;
249 }
250
251 // Match the public SrpV3 client contract: request size is encoded as N-1
252 // and only valid for aligned sizes in the inclusive range [4, 4096].
253 if (reqSize < 4 || reqSize > 4096 || (reqSize & 0x3) != 0) {
254 log_->warning("Got invalid SRPv3 request size=%" PRIu32 " for id=%" PRIu32, reqSize, id);
255 return;
256 }
257
258 doWrite = (opCode == 0x1); // Write
259 isPost = (opCode == 0x2); // Posted write
260
261 log_->debug("Got request id=%" PRIu32 ", opCode=%" PRIu32 ", addr=0x%" PRIx64 ", size=%" PRIu32,
262 id,
263 opCode,
264 address,
265 reqSize);
266
267 // Release the incoming frame lock before building/sending response
268 frLock.reset();
269
270 {
271 std::lock_guard<std::mutex> lock(memMtx_);
272
273 if (doWrite || isPost) {
274 // Verify frame contains write data
275 if (fSize < HeadLen + reqSize) {
276 log_->warning("Write frame too small: got %" PRIu32 ", need %" PRIu32, fSize, HeadLen + reqSize);
277 return;
278 }
279
280 // Re-lock and read write data from frame
281 frLock = frame->lock();
282 fIter = frame->begin() + HeadLen;
283 std::vector<uint8_t> wrData(reqSize);
284 ris::fromFrame(fIter, reqSize, wrData.data());
285 frLock.reset();
286
287 writeMemory(address, wrData.data(), reqSize);
288
289 log_->debug("Write complete id=%" PRIu32 ", addr=0x%" PRIx64 ", size=%" PRIu32, id, address, reqSize);
290 }
291
292 // Posted writes do not get a response
293 if (isPost) return;
294
295 // Build response frame
296 uint32_t respLen = HeadLen + reqSize + TailLen;
297 ris::FramePtr respFrame = reqFrame(respLen, true);
298 respFrame->setPayload(respLen);
299 ris::FrameIterator rIter = respFrame->begin();
300
301 // Write header (echo back the request header)
302 ris::toFrame(rIter, HeadLen, header);
303
304 // Write data payload (read from internal memory)
305 std::vector<uint8_t> rdData(reqSize);
306 readMemory(address, rdData.data(), reqSize);
307 ris::toFrame(rIter, reqSize, rdData.data());
308
309 // Write tail (status = 0 = success)
310 tail[0] = 0;
311 ris::toFrame(rIter, TailLen, tail);
312
313 log_->debug("Sending response id=%" PRIu32 ", size=%" PRIu32, id, respLen);
314
315 sendFrame(respFrame);
316 }
317}
RAII helper that releases the Python GIL for a scope.
Definition GilRelease.h:36
static std::shared_ptr< rogue::Logging > create(const std::string &name, bool quiet=false)
Creates a logger instance.
Definition Logging.cpp:95
Random-access byte iterator across a Frame payload.
static void fromFrame(rogue::interfaces::stream::FrameIterator &iter, uint32_t size, void *dst)
Copies bytes from a frame iterator to a destination pointer.
std::shared_ptr< rogue::interfaces::stream::Frame > FramePtr
Shared pointer alias for Frame.
Definition Frame.h:549
static void toFrame(rogue::interfaces::stream::FrameIterator &iter, uint32_t size, void *src)
Copies bytes from a source pointer into a frame iterator.
std::shared_ptr< rogue::interfaces::stream::FrameLock > FrameLockPtr
Shared pointer alias for FrameLock.
Definition FrameLock.h:110
std::shared_ptr< rogue::protocols::srp::SrpV3Emulation > SrpV3EmulationPtr