GCC Code Coverage Report


Directory: ./
File: util/BBBPruUtils.cpp
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 339 390 86.9%
Functions: 28 30 93.3%
Branches: 232 420 55.2%

Line Branch Exec Source
1 /*
2 * This file is part of the Falcon Player (FPP) and is Copyright (C)
3 * 2013-2022 by the Falcon Player Developers.
4 *
5 * The Falcon Player (FPP) is free software, and is covered under
6 * multiple Open Source licenses. Please see the included 'LICENSES'
7 * file for descriptions of what files are covered by each license.
8 *
9 * This source file is covered under the LGPL v2.1 as described in the
10 * included LICENSE.LGPL file.
11 */
12
13 #include "fpp-pch.h"
14
15 #include <sys/mman.h>
16 #include <sys/stat.h>
17 #include <sys/wait.h>
18 #include <algorithm>
19 #include <cerrno>
20 #include <cstring>
21 #include <elf.h>
22 #include <fcntl.h>
23 #include <filesystem>
24 #include <inttypes.h>
25 #include <map>
26 #include <mutex>
27 #include <stdexcept>
28 #include <stdint.h>
29 #include <thread>
30 #include <unistd.h>
31 #include <vector>
32
33 #include "../common.h"
34 #include "../log.h"
35
36 #include "BBBPruUtils.h"
37
38 class PRUCoreInfo {
39 public:
40 int pru_num = 0;
41 uint8_t* dataRam = nullptr;
42 uintptr_t dataRamPhyLoc = 0;
43 int dataRamSize = 0;
44 uint8_t* sharedRam = nullptr;
45 uintptr_t sharedRamPhyLoc = 0;
46 int sharedRamSize = 0;
47
48 uint8_t* sram = nullptr;
49 uint32_t sram_addr = 0;
50 size_t sram_size = 0;
51
52 uint8_t* m4ram = nullptr;
53 uint32_t m4ram_addr = 0;
54 size_t m4ram_size = 0;
55
56 206 std::string getRemoteProcStateFile() {
57 #ifdef PLATFORM_BBB
58
2/4
✓ Branch 0 taken 159 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 159 times.
✗ Branch 3 not taken.
159 return "/sys/class/remoteproc/remoteproc" + std::to_string(pru_num) + "/state";
59 #elif defined(PLATFORM_BB64)
60 47 std::string base;
61
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 41 times.
47 if (pru_num == 0) {
62
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 base = "/sys/devices/platform/bus@f0000/30040000.pruss/30074000.pru/remoteproc/remoteproc";
63
1/2
✓ Branch 0 taken 41 times.
✗ Branch 1 not taken.
41 } else if (pru_num == 1) {
64
1/2
✓ Branch 0 taken 41 times.
✗ Branch 1 not taken.
41 base = "/sys/devices/platform/bus@f0000/30040000.pruss/30078000.pru/remoteproc/remoteproc";
65 } else {
66 // M4 core, not PRU, but startable via remoteproc
67 ✗ base = "/sys/devices/platform/bus@f0000/bus@f0000:bus@4000000/5000000.m4fss/remoteproc/remoteproc";
68 }
69
1/2
✓ Branch 0 taken 88 times.
✗ Branch 1 not taken.
88 for (int x = 0; x < 10; x++) {
70
5/8
✓ Branch 0 taken 88 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 88 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 88 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 47 times.
✓ Branch 7 taken 41 times.
88 if (FileExists(base + std::to_string(x) + "/state")) {
71
2/4
✓ Branch 0 taken 47 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 47 times.
✗ Branch 3 not taken.
47 return base + std::to_string(x) + "/state";
72 }
73 }
74 ✗ return "/sys/class/remoteproc/remoteproc" + std::to_string(pru_num) + "/state";
75 #endif
76 47 }
77 137 void disable() {
78
1/2
✓ Branch 0 taken 137 times.
✗ Branch 1 not taken.
137 std::string filename = getRemoteProcStateFile();
79
2/4
✓ Branch 0 taken 137 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 137 times.
✗ Branch 3 not taken.
137 if (FileExists(filename)) {
80
1/2
✓ Branch 0 taken 137 times.
✗ Branch 1 not taken.
137 FILE* rp = fopen(filename.c_str(), "w");
81
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 136 times.
137 if (rp == nullptr) {
82
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 LogWarn(VB_CHANNELOUT, "BBBPru::Pru::disable() - could not open %s: %s\n", filename.c_str(), strerror(errno));
83 1 return;
84 }
85
1/2
✓ Branch 0 taken 136 times.
✗ Branch 1 not taken.
136 fprintf(rp, "stop");
86
1/2
✓ Branch 0 taken 136 times.
✗ Branch 1 not taken.
136 fclose(rp);
87
1/2
✓ Branch 0 taken 136 times.
✗ Branch 1 not taken.
136 std::this_thread::sleep_for(std::chrono::milliseconds(1));
88
1/2
✓ Branch 0 taken 136 times.
✗ Branch 1 not taken.
136 std::string f = GetFileContents(filename);
89
1/2
✓ Branch 0 taken 136 times.
✗ Branch 1 not taken.
136 TrimWhiteSpace(f);
90 136 int cnt = 0;
91
3/8
✓ Branch 0 taken 136 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 136 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 136 times.
136 while (f != "offline" && cnt < 500) {
92 ✗ std::this_thread::sleep_for(std::chrono::milliseconds(1));
93 ✗ f = GetFileContents(filename);
94 ✗ TrimWhiteSpace(f);
95 ✗ cnt++;
96 }
97
2/4
✓ Branch 0 taken 136 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 136 times.
136 if (f != "offline") {
98 ✗ LogWarn(VB_CHANNELOUT, "BBBPru::Pru::disable() - could not stop PRU core %d State: %s\n", pru_num, f.c_str());
99 }
100 136 }
101
2/2
✓ Branch 0 taken 136 times.
✓ Branch 1 taken 1 times.
137 }
102 69 bool enable() {
103
1/2
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
69 std::string filename = getRemoteProcStateFile();
104 69 int cnt = 0;
105
3/8
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 69 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 69 times.
69 while (!FileExists(filename) && cnt < 10000) {
106 ✗ cnt++;
107 ✗ std::this_thread::sleep_for(std::chrono::milliseconds(1));
108 }
109
4/8
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 69 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 69 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 69 times.
✗ Branch 7 not taken.
69 if (FileExists(filename) && cnt < 10000) {
110
1/2
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
69 if (pru_num <= 1) {
111 // explicitly select the firmware image; the sysfs setting is
112 // sticky so anything else that loaded firmware on this core
113 // (tests, other tools) would otherwise be re-booted instead
114
2/4
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 69 times.
✗ Branch 3 not taken.
69 std::string fwFile = filename.substr(0, filename.size() - 5) + "firmware";
115
1/2
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
69 FILE* fw = fopen(fwFile.c_str(), "w");
116
2/2
✓ Branch 0 taken 68 times.
✓ Branch 1 taken 1 times.
69 if (fw) {
117 #if defined(PLATFORM_BBB)
118
1/2
✓ Branch 0 taken 53 times.
✗ Branch 1 not taken.
53 fprintf(fw, "am335x-pru%d-fw", pru_num);
119 #else
120
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 fprintf(fw, "am62x-pru%d-fw", pru_num);
121 #endif
122
1/2
✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
68 fclose(fw);
123 }
124 69 }
125
1/2
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
69 FILE* rp = fopen(filename.c_str(), "w");
126
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 68 times.
69 if (rp == nullptr) {
127
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 LogWarn(VB_CHANNELOUT, "BBBPru::Pru::enable() - could not open %s: %s\n", filename.c_str(), strerror(errno));
128 1 return false;
129 }
130
1/2
✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
68 fprintf(rp, "start");
131
1/2
✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
68 fclose(rp);
132 68 cnt = 0;
133
1/2
✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
68 std::this_thread::sleep_for(std::chrono::milliseconds(1));
134
1/2
✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
68 std::string f = GetFileContents(filename);
135
1/2
✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
68 TrimWhiteSpace(f);
136
3/8
✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 68 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 68 times.
68 while (f != "running" && cnt < 500) {
137 ✗ std::this_thread::sleep_for(std::chrono::milliseconds(1));
138 ✗ f = GetFileContents(filename);
139 ✗ TrimWhiteSpace(f);
140 ✗ cnt++;
141 }
142
2/4
✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 68 times.
68 if (f != "running") {
143 ✗ LogWarn(VB_CHANNELOUT, "BBBPru::Pru::enable() - could not start PRU core %d State: %s\n", pru_num, f.c_str());
144 ✗ return false;
145 }
146
1/2
✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
68 } else {
147 ✗ LogWarn(VB_CHANNELOUT, "BBBPru::Pru::enable() - could not start PRU core %d\n", pru_num);
148 ✗ return false;
149 }
150 68 return true;
151 69 }
152 };
153
154 static int prussUseCount = 0;
155
156 static uint8_t* ddr_mem_loc = nullptr;
157 static uint32_t ddr_phy_mem_loc = 0;
158 static size_t ddr_filelen = 0;
159 static uint8_t* base_memory_location = nullptr;
160 static PRUCoreInfo prus[2];
161
162 #if defined(PLATFORM_BBB)
163
164 constexpr uintptr_t PRUSS_MMAP_BASE = 0x4a300000;
165 constexpr size_t PRUSS_MMAP_SIZE = 0x40000;
166
167 constexpr uintptr_t PRUSS_DATARAM0_PHYS_BASE = 0x4a300000;
168 constexpr uintptr_t PRUSS_DATARAM1_PHYS_BASE = 0x4a302000;
169 constexpr size_t PRUSS_DATARAM_SIZE = 8 * 1024; // 8K
170
171 constexpr uintptr_t PRUSS_SHAREDRAM_BASE = 0x4a310000;
172 constexpr size_t PRUSS_SHAREDRAM_SIZE = 12 * 1024; // 12K
173
174 constexpr uint32_t DDR_ADDR = 0x8f000000;
175 constexpr size_t DDR_SIZE = 0x00400000;
176
177 constexpr std::string FIRMWARE_PREFIX = "am335x";
178 constexpr bool FAKE_PRU = false;
179
180 static uint8_t* PRUSS_SRAM_BASE = 0x00000000;
181 constexpr uint32_t PRUSS_SRAM_GLOBAL = 0x00000000;
182 constexpr size_t PRUSS_SRAM_SIZE = 0;
183
184 static uint8_t* PRUSS_M4RAM_BASE = 0x00000000;
185 constexpr uint32_t PRUSS_M4RAM_GLOBAL = 0x00000000;
186 constexpr size_t PRUSS_M4RAM_SIZE = 0;
187
188 #elif defined(PLATFORM_BB64)
189 constexpr uintptr_t PRUSS_MMAP_BASE = 0x30040000;
190 constexpr size_t PRUSS_MMAP_SIZE = 0x80000;
191
192 constexpr size_t PRUSS_DATARAM_SIZE = 8 * 1024; // 8K
193 constexpr uintptr_t PRUSS_DATARAM0_PHYS_BASE = 0x30040000;
194 constexpr uintptr_t PRUSS_DATARAM1_PHYS_BASE = 0x30042000;
195
196 constexpr uintptr_t PRUSS_SHAREDRAM_BASE = 0x30050000;
197 constexpr size_t PRUSS_SHAREDRAM_SIZE = 32 * 1024; // 32K
198
199 constexpr uint32_t DDR_ADDR = 0x8f000000;
200 constexpr size_t DDR_SIZE = 0x00400000;
201
202 // LDI32 r8, 0x70000000 // OCSRAM
203 // LDI32 r8, 0x0005040000 // M4 DRAM
204
205 static uint8_t* PRUSS_SRAM_BASE = nullptr;
206 constexpr uint32_t PRUSS_SRAM_GLOBAL = 0x70000000;
207 constexpr size_t PRUSS_SRAM_SIZE = 64 * 1024; // 64K
208
209 static uint8_t* PRUSS_M4RAM_BASE = nullptr;
210 constexpr uint32_t PRUSS_M4RAM_GLOBAL = 0x05040000;
211 constexpr size_t PRUSS_M4RAM_SIZE = 256 * 1024; // 256K;
212
213 constexpr std::string FIRMWARE_PREFIX = "am62x";
214
215 static bool FAKE_PRU = !FileExists("/sys/class/remoteproc/remoteproc0/state");
216 #endif
217
218 89 static void releasePrus() {
219
1/2
✓ Branch 0 taken 89 times.
✗ Branch 1 not taken.
89 if (ddr_mem_loc) {
220 89 munmap(ddr_mem_loc, ddr_filelen);
221 89 ddr_mem_loc = nullptr;
222 }
223
2/2
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 1 times.
89 if (base_memory_location) {
224 88 munmap(base_memory_location, PRUSS_MMAP_SIZE);
225 88 base_memory_location = nullptr;
226 }
227
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 74 times.
89 if (PRUSS_SRAM_BASE) {
228 15 munmap(PRUSS_SRAM_BASE, PRUSS_SRAM_SIZE);
229 15 PRUSS_SRAM_BASE = nullptr;
230 }
231
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 74 times.
89 if (PRUSS_M4RAM_BASE) {
232 15 munmap(PRUSS_M4RAM_BASE, PRUSS_M4RAM_SIZE);
233 15 PRUSS_M4RAM_BASE = nullptr;
234 }
235 89 }
236
237 // mmap() reports failure as MAP_FAILED, not nullptr; normalise so the rest of
238 // this file can keep testing the pointers for null, and keep the first errno.
239 208 static uint8_t* mapOrNull(size_t len, int flags, int fd, off_t off, int& err) {
240 208 void* p = mmap(0, len, PROT_WRITE | PROT_READ, flags, fd, off);
241
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 207 times.
208 if (p == MAP_FAILED) {
242
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (!err) {
243 1 err = errno;
244 }
245 1 return nullptr;
246 }
247 207 return (uint8_t*)p;
248 }
249
250 // Returns 0, or the errno of the first thing that failed. On failure nothing
251 // is left mapped.
252 89 static int initPrus() {
253 89 int err = 0;
254 89 int mem_fd = -1;
255
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 38 times.
53 if (!FAKE_PRU) {
256
1/2
✓ Branch 0 taken 51 times.
✗ Branch 1 not taken.
51 mem_fd = open("/dev/mem", O_RDWR | O_CLOEXEC);
257
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 51 times.
51 if (mem_fd < 0) {
258 ✗ return errno;
259 }
260 51 base_memory_location = mapOrNull(PRUSS_MMAP_SIZE, MAP_SHARED, mem_fd, PRUSS_MMAP_BASE, err);
261 if (PRUSS_SRAM_SIZE > 0) {
262 15 PRUSS_SRAM_BASE = mapOrNull(PRUSS_SRAM_SIZE, MAP_SHARED, mem_fd, PRUSS_SRAM_GLOBAL, err);
263 }
264 if (PRUSS_M4RAM_SIZE > 0) {
265 15 PRUSS_M4RAM_BASE = mapOrNull(PRUSS_M4RAM_SIZE, MAP_SHARED, mem_fd, PRUSS_M4RAM_GLOBAL, err);
266 }
267 } else {
268 38 base_memory_location = mapOrNull(PRUSS_MMAP_SIZE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0, err);
269 }
270
271 89 uint32_t ddr_addr = DDR_ADDR;
272 89 size_t ddr_sizeb = DDR_SIZE;
273
274
3/4
✓ Branch 0 taken 89 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
✓ Branch 3 taken 38 times.
89 if (ddr_mem_loc == nullptr && !FAKE_PRU) {
275 51 ddr_phy_mem_loc = ddr_addr;
276 51 ddr_filelen = ddr_sizeb;
277 51 ddr_mem_loc = mapOrNull(ddr_filelen, MAP_SHARED, mem_fd, ddr_addr, err);
278
1/2
✓ Branch 0 taken 51 times.
✗ Branch 1 not taken.
51 if (ddr_mem_loc) {
279 51 madvise(ddr_mem_loc, ddr_filelen, MADV_HUGEPAGE);
280 }
281
2/4
✓ Branch 0 taken 38 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 38 times.
✗ Branch 3 not taken.
38 } else if (ddr_addr == 0 || FAKE_PRU) {
282 // just malloc some memory so we don't crash
283 38 ddr_phy_mem_loc = ddr_addr;
284 38 ddr_filelen = ddr_sizeb;
285 38 ddr_mem_loc = mapOrNull(ddr_filelen, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0, err);
286 }
287
2/2
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 38 times.
89 if (mem_fd >= 0) {
288
1/2
✓ Branch 0 taken 51 times.
✗ Branch 1 not taken.
51 close(mem_fd);
289 }
290
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 88 times.
89 if (err) {
291 1 releasePrus();
292 1 return err;
293 }
294
295 88 prus[0].pru_num = 0;
296 88 prus[1].pru_num = 1;
297 88 prus[0].dataRam = base_memory_location + (PRUSS_DATARAM0_PHYS_BASE - PRUSS_MMAP_BASE);
298 88 prus[0].dataRamSize = PRUSS_DATARAM_SIZE;
299 88 prus[0].dataRamPhyLoc = PRUSS_DATARAM0_PHYS_BASE;
300 88 prus[1].dataRam = base_memory_location + (PRUSS_DATARAM1_PHYS_BASE - PRUSS_MMAP_BASE);
301 88 prus[1].dataRamSize = PRUSS_DATARAM_SIZE;
302 88 prus[1].dataRamPhyLoc = PRUSS_DATARAM1_PHYS_BASE;
303 88 prus[0].sharedRam = base_memory_location + (PRUSS_SHAREDRAM_BASE - PRUSS_MMAP_BASE);
304 88 prus[0].sharedRamSize = PRUSS_SHAREDRAM_SIZE;
305 88 prus[0].sharedRamPhyLoc = PRUSS_SHAREDRAM_BASE;
306 88 prus[1].sharedRam = base_memory_location + (PRUSS_SHAREDRAM_BASE - PRUSS_MMAP_BASE);
307 88 prus[1].sharedRamSize = PRUSS_SHAREDRAM_SIZE;
308 88 prus[1].sharedRamPhyLoc = PRUSS_SHAREDRAM_BASE;
309
310
2/2
✓ Branch 0 taken 176 times.
✓ Branch 1 taken 88 times.
264 for (int x = 0; x < 2; x++) {
311 176 prus[x].sram = PRUSS_SRAM_BASE;
312 176 prus[x].sram_addr = PRUSS_SRAM_GLOBAL;
313 176 prus[x].sram_size = PRUSS_SRAM_SIZE;
314
315 176 prus[x].m4ram = PRUSS_M4RAM_BASE;
316 176 prus[x].m4ram_addr = PRUSS_M4RAM_GLOBAL;
317 176 prus[x].m4ram_size = PRUSS_M4RAM_SIZE;
318 }
319
320
1/2
✓ Branch 0 taken 88 times.
✗ Branch 1 not taken.
88 if (ddr_sizeb) {
321 88 memset(ddr_mem_loc, 0, ddr_sizeb);
322 }
323 88 __asm__ __volatile__("" ::
324 : "memory");
325 88 return 0;
326 }
327
328 122 BBBPru::BBBPru(int pru, bool mapShared, bool mapOther) :
329 122 pru_num(pru) {
330
2/2
✓ Branch 0 taken 89 times.
✓ Branch 1 taken 33 times.
122 if (prussUseCount == 0) {
331
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 88 times.
89 if (int err = initPrus()) {
332 // Nothing is mapped and prussUseCount is untouched, so no
333 // destructor work is owed. Output Init() runs inside the channel
334 // output loader's try/catch, which logs this, raises its own
335 // (reload-cleared) warning and drops the output -- instead of
336 // memset()ing through MAP_FAILED as this used to.
337
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 std::string msg = std::string("could not map PRU memory: ") + FPPstrerror(err);
338
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 LogErr(VB_CHANNELOUT, "BBBPru: %s\n", msg.c_str());
339
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 throw std::runtime_error(msg);
340 1 }
341 }
342 121 prussUseCount++;
343
344 121 this->data_ram = prus[pru].dataRam;
345 121 this->data_ram_size = prus[pru].dataRamSize;
346
347 121 this->ddr = ddr_mem_loc;
348 121 this->ddr_size = ddr_filelen;
349 121 this->ddr_addr = ddr_phy_mem_loc;
350
351
2/2
✓ Branch 0 taken 93 times.
✓ Branch 1 taken 28 times.
121 if (mapShared) {
352 93 this->shared_ram_size = prus[pru].sharedRamSize;
353 93 this->shared_ram = prus[pru].sharedRam;
354 } else {
355 28 this->shared_ram_size = 0;
356 28 this->shared_ram = 0;
357 }
358
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 92 times.
121 if (mapOther) {
359 29 int p = pru ? 0 : 1;
360 29 this->other_data_ram = prus[p].dataRam;
361 29 this->other_data_ram_size = prus[p].dataRamSize;
362 } else {
363 92 this->other_data_ram_size = 0;
364 92 this->other_data_ram = 0;
365 }
366
367 121 this->sram = prus[pru].sram;
368 121 this->sram_addr = prus[pru].sram_addr;
369 121 this->sram_size = prus[pru].sram_size;
370
371 121 this->m4ram = prus[pru].m4ram;
372 121 this->m4ram_addr = prus[pru].m4ram_addr;
373 121 this->m4ram_size = prus[pru].m4ram_size;
374 121 }
375
376 121 BBBPru::~BBBPru() {
377 121 prussUseCount--;
378
2/2
✓ Branch 0 taken 88 times.
✓ Branch 1 taken 33 times.
121 if (prussUseCount == 0) {
379 88 releasePrus();
380 }
381 121 }
382
383 // The core control register sits 0x22000 into the PRUSS mapping, one 0x2000
384 // block per core, on both the AM335x and the AM62x. Bit 1 enables the core.
385 static constexpr uintptr_t PRU_CTRL_OFFSET = 0x22000;
386 static constexpr uintptr_t PRU_CTRL_STRIDE = 0x2000;
387 static constexpr uint32_t PRU_CTRL_ENABLE = 0x2;
388
389 137 static volatile uint32_t* pruControlReg(unsigned pru_num) {
390 // only the two PRU cores: one block further on is the PRUSS CFG space
391
2/4
✓ Branch 0 taken 137 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 137 times.
137 if (!base_memory_location || pru_num > 1) {
392 ✗ return nullptr;
393 }
394 137 return (volatile uint32_t*)(base_memory_location + PRU_CTRL_OFFSET + (pru_num * PRU_CTRL_STRIDE));
395 }
396
397 // Clear the core's enable bit and wait until it reads back clear with the
398 // core no longer running. A remoteproc stop alone does not guarantee this:
399 // the firmware's RESET_PRU_CLOCK read-modify-writes this same register to
400 // restart the cycle counter, and every FPP program runs it constantly while
401 // idle. A stop that clears EN between the firmware's read and its write is
402 // undone, and the core keeps running its old program while remoteproc
403 // reports it offline - acting on any command word it sees, and executing
404 // whatever lands in its instruction RAM when the next program is loaded.
405 // Our own write can be undone the same way, so it is repeated until it
406 // sticks; once the core is halted nothing can set the bit again.
407 137 static bool haltCore(unsigned pru_num) {
408 137 volatile uint32_t* ctrl = pruControlReg(pru_num);
409
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 137 times.
137 if (!ctrl) {
410 ✗ return true;
411 }
412 137 constexpr uint32_t PRU_CTRL_RUNSTATE = 0x8000;
413
1/2
✓ Branch 0 taken 243 times.
✗ Branch 1 not taken.
243 for (int x = 0; x < 100; x++) {
414 243 uint32_t v = *ctrl;
415
4/4
✓ Branch 0 taken 143 times.
✓ Branch 1 taken 100 times.
✓ Branch 2 taken 137 times.
✓ Branch 3 taken 6 times.
243 if (!(v & PRU_CTRL_ENABLE) && !(v & PRU_CTRL_RUNSTATE)) {
416 137 return true;
417 }
418 106 *ctrl = v & ~PRU_CTRL_ENABLE;
419 106 __sync_synchronize();
420
1/2
✓ Branch 0 taken 106 times.
✗ Branch 1 not taken.
106 std::this_thread::sleep_for(std::chrono::microseconds(10));
421 }
422 ✗ LogWarn(VB_CHANNELOUT, "BBBPru: PRU core %d did not halt (control 0x%X)\n", pru_num, *ctrl);
423 ✗ return false;
424 }
425
426 363 static std::string pruFirmwareName(int pru_num) {
427
3/6
✓ Branch 0 taken 363 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 363 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 363 times.
✗ Branch 5 not taken.
363 return FIRMWARE_PREFIX + "-pru" + std::to_string(pru_num) + "-fw";
428 }
429
430 // /lib/firmware/<prefix>-pruN-fw ships as a symlink to the "-fw.sleep"
431 // parking firmware that the image loads at boot. Copying the program
432 // through that name would overwrite the sleep firmware itself, so install
433 // the program under its own name and atomically repoint the link at it.
434 // Returns false if the program could not be copied in, leaving the
435 // previously installed program in place.
436 121 static bool installPRUFirmware(int pru_num, const std::string& program) {
437
2/4
✓ Branch 0 taken 121 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 121 times.
✗ Branch 3 not taken.
121 std::string fw = "/lib/firmware/" + pruFirmwareName(pru_num);
438
3/6
✓ Branch 0 taken 121 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 121 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 121 times.
121 if (!CopyFileContents(program, fw + ".fpp")) {
439 ✗ return false;
440 }
441
1/2
✓ Branch 0 taken 121 times.
✗ Branch 1 not taken.
121 std::string tmpLink = fw + ".new";
442 121 unlink(tmpLink.c_str());
443
3/6
✓ Branch 0 taken 121 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 121 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 121 times.
✗ Branch 5 not taken.
121 if (symlink((pruFirmwareName(pru_num) + ".fpp").c_str(), tmpLink.c_str()) == 0) {
444 121 rename(tmpLink.c_str(), fw.c_str());
445 }
446 121 return true;
447 121 }
448
449 // Point the firmware name back at the sleep firmware so the next boot parks
450 // the PRU instead of re-running the last output program. FalconSerial in
451 // particular free-runs frames without waiting for a first command, so leaving
452 // it installed as the boot firmware blasts garbage on any muxed pins.
453 68 static void restorePRUSleepFirmware(int pru_num) {
454
2/4
✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 68 times.
✗ Branch 3 not taken.
68 std::string sleepName = pruFirmwareName(pru_num) + ".sleep";
455
4/6
✓ Branch 0 taken 68 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 68 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 15 times.
✓ Branch 5 taken 53 times.
68 if (!FileExists("/lib/firmware/" + sleepName)) {
456 15 return;
457 }
458
2/4
✓ Branch 0 taken 53 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 53 times.
✗ Branch 3 not taken.
53 std::string fw = "/lib/firmware/" + pruFirmwareName(pru_num);
459
1/2
✓ Branch 0 taken 53 times.
✗ Branch 1 not taken.
53 std::string tmpLink = fw + ".new";
460 53 unlink(tmpLink.c_str());
461
1/2
✓ Branch 0 taken 53 times.
✗ Branch 1 not taken.
53 if (symlink(sleepName.c_str(), tmpLink.c_str()) == 0) {
462 53 rename(tmpLink.c_str(), fw.c_str());
463 }
464
2/2
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 15 times.
68 }
465
466 121 bool BBBPru::run(const std::string& program, bool clearSharedMems, bool clearMems) {
467
1/2
✓ Branch 0 taken 121 times.
✗ Branch 1 not taken.
121 LogDebug(VB_CHANNELOUT, "BBBPru[%d]::run(%s)\n", pru_num, program.c_str());
468
469 189 auto clearPRUMems = [&]() {
470
2/2
✓ Branch 0 taken 55 times.
✓ Branch 1 taken 134 times.
189 if (!clearMems) {
471 55 return;
472 }
473 134 clearPRUMem(data_ram, data_ram_size);
474
2/2
✓ Branch 0 taken 117 times.
✓ Branch 1 taken 17 times.
134 if (clearSharedMems) {
475
2/2
✓ Branch 0 taken 61 times.
✓ Branch 1 taken 56 times.
117 if (shared_ram) {
476 61 clearPRUMem(shared_ram, shared_ram_size);
477 }
478
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 117 times.
117 if (other_data_ram) {
479 ✗ clearPRUMem(other_data_ram, other_data_ram_size);
480 }
481 }
482 121 };
483 // A program that is missing - a runtime firmware build that failed - must
484 // not start the core: the copy below would fail and the core would boot
485 // whatever an earlier run left installed, possibly another output's
486 // firmware, while this reported success.
487
2/4
✓ Branch 0 taken 121 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 121 times.
121 if (!FileExists(program)) {
488 ✗ LogErr(VB_CHANNELOUT, "BBBPru[%d]: PRU program %s does not exist\n", pru_num, program.c_str());
489 ✗ return false;
490 }
491 121 bool enabled = true;
492
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 52 times.
68 if (!FAKE_PRU) {
493 // halted before the program is installed: remoteproc loads it into
494 // the instruction RAM of a core it believes is stopped
495
1/2
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
69 haltCore(pru_num);
496
1/2
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
69 prus[pru_num].disable();
497
2/4
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 69 times.
69 if (!installPRUFirmware(pru_num, program)) {
498 ✗ LogErr(VB_CHANNELOUT, "BBBPru[%d]: could not install PRU program %s\n", pru_num, program.c_str());
499 ✗ return false;
500 }
501 // Clear before the core starts as well as after. Firmware loading
502 // does not clear data RAM, so on a restart the previous run's
503 // handshake words (the SMEM ring config in particular) are still
504 // there: the new firmware passes its wait on them at once and reads
505 // config the ARM has not written yet. The clear after enable() stays
506 // for a PRUSS that was powered down, where these writes are dropped.
507
1/2
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
69 clearPRUMems();
508
1/2
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
69 enabled = prus[pru_num].enable();
509
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 68 times.
69 if (!enabled) {
510 1 return false;
511 }
512
2/4
✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 52 times.
52 } else if (!installPRUFirmware(pru_num, program)) {
513 ✗ LogErr(VB_CHANNELOUT, "BBBPru[%d]: could not install PRU program %s\n", pru_num, program.c_str());
514 ✗ return false;
515 }
516
1/2
✓ Branch 0 taken 120 times.
✗ Branch 1 not taken.
120 clearPRUMems();
517 /*
518 printf("DL: %p\n", data_ram);
519 printf("OL: %p\n", other_data_ram);
520 printf("SH: %p\n", shared_ram);
521 printf("DDR: %p\n", ddr);
522 */
523 120 return true;
524 }
525
526 #ifdef PLATFORM_BBB
527 54 void BBBPru::clearPRUMem(uint8_t* ptr, size_t sz) {
528 54 memset(ptr, 0, sz);
529 54 }
530 99 void BBBPru::memcpyToPRU(uint8_t* dst, uint8_t* src, size_t sz) {
531 99 memcpy(dst, src, sz);
532 99 }
533 #elif defined(PLATFORM_BB64)
534 #pragma GCC push_options
535 #pragma GCC optimize("O0")
536 // The optimized memset and memcpy on Arm64 will segfault
537 // when doing certain sized operations to un-cacheable
538 // ram segments. Need to use ldnp/stnp instructions
539 //
540 // The block loop moves whole 64 byte units and needs 16 byte aligned operands
541 // (the PRU memories map uncached, where an unaligned vector access faults), so
542 // the head/tail and anything that does not qualify go through the byte loop
543 // instead - slower, but correct for every size and alignment. Exactly sztotal
544 // bytes are written and no more.
545 132 void memcpy_ldnp(volatile unsigned char* dst, volatile unsigned char* src, int sztotal) {
546 // NB: "sztotal - sztotal & 64" parses as "(sztotal - sztotal) & 64", i.e.
547 // always zero, which left the block loop below writing one fixed 64 byte
548 // store - past the end for anything shorter - and everything else crawling
549 // through the byte loop. Mask off the low bits instead.
550 132 int sz = 0;
551
4/4
✓ Branch 0 taken 68 times.
✓ Branch 1 taken 64 times.
✓ Branch 2 taken 66 times.
✓ Branch 3 taken 2 times.
132 if (sztotal >= 64 && ((((uintptr_t)dst | (uintptr_t)src) & 15) == 0)) {
552 66 sz = sztotal & ~63;
553 }
554
2/2
✓ Branch 0 taken 4992 times.
✓ Branch 1 taken 132 times.
5124 for (int x = sz; x < sztotal; x++) {
555 4992 dst[x] = src[x];
556 }
557
2/2
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 66 times.
132 if (sz == 0) {
558 66 return;
559 }
560 66 asm volatile(
561 "NEONCopyPLD: \n"
562 "sub %[dst], %[dst], #64 \n"
563 "1: \n"
564 "ldnp q0, q1, [%[src]] \n"
565 "ldnp q2, q3, [%[src], #32] \n"
566 "add %[dst], %[dst], #64 \n"
567 "subs %[sz], %[sz], #64 \n"
568 "add %[src], %[src], #64 \n"
569 "stnp q0, q1, [%[dst]] \n"
570 "stnp q2, q3, [%[dst], #32] \n"
571 "b.gt 1b \n"
572 : [dst] "+r"(dst), [src] "+r"(src), [sz] "+r"(sz) : : "q0", "q1", "q2", "q3", "cc", "memory");
573 }
574
575 141 void BBBPru::clearPRUMem(uint8_t* ptr, size_t sz) {
576 141 __uint128_t z = 0;
577 141 int c = sz / sizeof(z);
578 141 __uint128_t* p = (__uint128_t*)ptr;
579
2/2
✓ Branch 0 taken 165888 times.
✓ Branch 1 taken 141 times.
166029 for (int x = 0; x < c; ++x) {
580 165888 *p = z;
581 165888 p++;
582 }
583
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 141 times.
141 for (int x = c * sizeof(z); x < sz; ++x) {
584 ✗ ptr[x] = 0;
585 }
586 141 }
587 132 void BBBPru::memcpyToPRU(uint8_t* dst, uint8_t* src, size_t sz) {
588 132 memcpy_ldnp(dst, src, sz);
589 132 }
590 #pragma GCC pop_options
591 #endif
592
593 static std::mutex ddrAllocLock;
594 // owner -> (offset, size) within the reserved region
595 static std::map<std::string, std::pair<size_t, size_t>> ddrAllocs;
596
597 45 uint8_t* BBBPru::ddrAlloc(const std::string& owner, size_t size, uint32_t& physAddr, size_t minOffset) {
598
1/2
✓ Branch 0 taken 45 times.
✗ Branch 1 not taken.
45 std::unique_lock<std::mutex> lock(ddrAllocLock);
599
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 45 times.
45 if (ddr_mem_loc == nullptr) {
600 ✗ LogErr(VB_CHANNELOUT, "BBBPru::ddrAlloc(%s): DDR region not mapped yet\n", owner.c_str());
601 ✗ return nullptr;
602 }
603 45 size = (size + 4095) & ~(size_t)4095;
604 45 size_t pos = (minOffset + 4095) & ~(size_t)4095;
605
1/2
✓ Branch 0 taken 45 times.
✗ Branch 1 not taken.
45 ddrAllocs.erase(owner);
606 45 std::vector<std::pair<size_t, size_t>> used;
607
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 45 times.
49 for (auto& a : ddrAllocs) {
608
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 used.push_back(a.second);
609 }
610
1/2
✓ Branch 0 taken 45 times.
✗ Branch 1 not taken.
45 std::sort(used.begin(), used.end());
611
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 44 times.
48 for (auto& u : used) {
612
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 if (pos + size <= u.first) {
613 1 break;
614 }
615
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (u.first + u.second > pos) {
616 3 pos = u.first + u.second;
617 }
618 }
619
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 43 times.
45 if (pos + size > ddr_filelen) {
620
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 LogErr(VB_CHANNELOUT, "BBBPru::ddrAlloc(%s): %zuKB does not fit in the PRU DDR region (%zuKB of %zuKB already in use)\n",
621 owner.c_str(), size / 1024, pos / 1024, ddr_filelen / 1024);
622 2 return nullptr;
623 }
624
1/2
✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
43 ddrAllocs[owner] = { pos, size };
625 43 physAddr = ddr_phy_mem_loc + pos;
626
1/2
✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
43 LogInfo(VB_CHANNELOUT, "BBBPru: DDR region: '%s' at 0x%zX-0x%zX (%zuKB)\n",
627 owner.c_str(), pos, pos + size, size / 1024);
628 43 return ddr_mem_loc + pos;
629 45 }
630
631 93 void BBBPru::ddrRelease(const std::string& owner) {
632
1/2
✓ Branch 0 taken 93 times.
✗ Branch 1 not taken.
93 std::unique_lock<std::mutex> lock(ddrAllocLock);
633
1/2
✓ Branch 0 taken 93 times.
✗ Branch 1 not taken.
93 ddrAllocs.erase(owner);
634 93 }
635
636 119 void BBBPru::stop() {
637
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 51 times.
66 if (!FAKE_PRU) {
638 68 haltCore(pru_num);
639 68 prus[pru_num].disable();
640 68 restorePRUSleepFirmware(pru_num);
641 }
642 119 }
643
644 ✗ void BBBPru::pause() {
645 ✗ if (FAKE_PRU) {
646 ✗ return;
647 }
648 ✗ volatile uint32_t* ctrl = pruControlReg(pru_num);
649 ✗ if (ctrl) {
650 ✗ *ctrl = *ctrl & ~PRU_CTRL_ENABLE;
651 ✗ __sync_synchronize();
652 }
653 }
654
655 ✗ void BBBPru::resume() {
656 ✗ if (FAKE_PRU) {
657 ✗ return;
658 }
659 ✗ volatile uint32_t* ctrl = pruControlReg(pru_num);
660 ✗ if (ctrl) {
661 ✗ *ctrl = *ctrl | PRU_CTRL_ENABLE;
662 ✗ __sync_synchronize();
663 }
664 }
665
666 #include "../pru/SMEMRing.hp"
667
668 47 void BBBPruSMEMRing::attach(BBBPru* pru, uint32_t pruBaseAddr, uint32_t ringSize, bool usePointerMode) {
669 47 basePru = pruBaseAddr;
670 47 size = ringSize;
671 47 pointerMode = usePointerMode;
672 47 writeOff = 0;
673 47 produced = 0;
674 47 uint8_t* arm = pru->shared_ram + (pruBaseAddr - SMEM_RING_SMEM_PRU_ADDR);
675 47 ring = (volatile uint64_t*)arm;
676 47 ctrl = (volatile uint32_t*)(arm + ringSize);
677
1/2
✓ Branch 0 taken 47 times.
✗ Branch 1 not taken.
47 ctrl[0] = pointerMode ? basePru : 0;
678
1/2
✓ Branch 0 taken 47 times.
✗ Branch 1 not taken.
47 ctrl[1] = pointerMode ? basePru : 0;
679 // the firmware polls for the config; base first, then the nonzero size
680 47 volatile uint32_t* rc = (volatile uint32_t*)(pru->data_ram + SMEM_RING_CONFIG_OFFSET);
681 47 rc[0] = basePru;
682 47 rc[1] = ringSize;
683 47 }
684
685 2935 uint32_t BBBPruSMEMRing::write(const uint8_t* src, uint32_t len) {
686 // Counters are free running, so their difference is the exact number of
687 // bytes in flight across the 2^32 wrap. Reducing a raw count modulo the
688 // ring size is not: 2^32 is not a multiple of the size, so each wrap moved
689 // the computed read position (by 4096 bytes on a 30720 byte ring) and,
690 // once that error was large enough, let the producer overwrite data the
691 // consumer had not read yet.
692
1/2
✓ Branch 0 taken 2935 times.
✗ Branch 1 not taken.
2935 uint32_t used = pointerMode ? (writeOff - (ctrl[1] - basePru) + size) % size
693 ✗ : (produced - ctrl[1]);
694 2935 uint32_t space = size - 64 - used;
695
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2935 times.
2935 if (len > space) {
696 ✗ len = space;
697 }
698 2935 uint32_t total = 0;
699
2/2
✓ Branch 0 taken 3856 times.
✓ Branch 1 taken 2935 times.
6791 while (total < len) {
700 3856 uint32_t n = len - total;
701
2/2
✓ Branch 0 taken 921 times.
✓ Branch 1 taken 2935 times.
3856 if (n > (size - writeOff)) {
702 921 n = size - writeOff;
703 }
704 3856 volatile uint64_t* d = ring + writeOff / 8;
705 3856 const uint64_t* s = (const uint64_t*)(src + total);
706
2/2
✓ Branch 0 taken 2473672 times.
✓ Branch 1 taken 3856 times.
2477528 for (uint32_t i = 0; i < n / 8; i++) {
707 2473672 d[i] = s[i];
708 }
709 3856 writeOff += n;
710
2/2
✓ Branch 0 taken 1042 times.
✓ Branch 1 taken 2814 times.
3856 if (writeOff == size) {
711 1042 writeOff = 0;
712 }
713 3856 produced += n;
714 3856 total += n;
715 }
716
1/2
✓ Branch 0 taken 2935 times.
✗ Branch 1 not taken.
2935 if (total) {
717 // the data must be observable before the position update
718 2935 __sync_synchronize();
719
1/2
✓ Branch 0 taken 2935 times.
✗ Branch 1 not taken.
2935 ctrl[0] = pointerMode ? (basePru + writeOff) : produced;
720 }
721 2935 return total;
722 }
723
724 48 void CompilePRUProgram(const std::string& script, const std::vector<std::string>& args) {
725 // argv is built before the fork: the child of a threaded process should
726 // do nothing but exec
727 48 std::vector<char*> argv;
728
1/2
✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
48 argv.push_back((char*)"/bin/bash");
729
1/2
✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
48 argv.push_back((char*)script.c_str());
730
1/2
✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
48 std::string log = script;
731
2/2
✓ Branch 0 taken 506 times.
✓ Branch 1 taken 48 times.
554 for (auto& a : args) {
732
1/2
✓ Branch 0 taken 506 times.
✗ Branch 1 not taken.
506 argv.push_back((char*)a.c_str());
733
2/4
✓ Branch 0 taken 506 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 506 times.
✗ Branch 3 not taken.
506 log += " " + a;
734 }
735
1/2
✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
48 argv.push_back(nullptr);
736
1/2
✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
48 LogDebug(VB_CHANNELOUT, "Compiling PRU program: %s\n", log.c_str());
737
738 48 pid_t pid = fork();
739
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 96 times.
96 if (pid < 0) {
740 ✗ LogErr(VB_CHANNELOUT, "Could not fork to compile PRU program %s: %s\n", script.c_str(), strerror(errno));
741 ✗ return;
742 }
743
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 48 times.
96 if (pid == 0) {
744 48 execv("/bin/bash", argv.data());
745 // never return into the caller: that would be a second fppd
746 48 _exit(127);
747 }
748 // returns when this child exits (with ECHILD, as it is reaped for us)
749
6/8
✓ Branch 0 taken 51 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 51 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 48 times.
✓ Branch 6 taken 3 times.
✓ Branch 7 taken 48 times.
51 while (waitpid(pid, nullptr, 0) < 0 && errno == EINTR) {
750 }
751
2/4
✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 48 times.
✗ Branch 3 not taken.
48 }
752