Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright 2018-2023 NXP
3 : : */
4 : :
5 : : #include <eal_export.h>
6 : : #include <rte_memory.h>
7 : :
8 : : #include "dpaax_iova_table.h"
9 : : #include "dpaax_logs.h"
10 : :
11 : : /* Global table reference */
12 : : RTE_EXPORT_INTERNAL_SYMBOL(dpaax_iova_table_p)
13 : : struct dpaax_iova_table *dpaax_iova_table_p;
14 : :
15 : : static int dpaax_handle_memevents(void);
16 : :
17 : : /* A structure representing the device-tree node available in /proc/device-tree.
18 : : */
19 : : struct reg_node {
20 : : phys_addr_t addr;
21 : : size_t len;
22 : : };
23 : :
24 : : /* A ntohll equivalent routine
25 : : * XXX: This is only applicable for 64 bit environment.
26 : : */
27 : : static void
28 : : rotate_8(unsigned char *arr)
29 : : {
30 : : uint32_t temp;
31 : : uint32_t *first_half;
32 : : uint32_t *second_half;
33 : :
34 : : first_half = (uint32_t *)(arr);
35 : : second_half = (uint32_t *)(arr + 4);
36 : :
37 : 0 : temp = *first_half;
38 : 0 : *first_half = *second_half;
39 : : *second_half = temp;
40 : :
41 : 0 : *first_half = ntohl(*first_half);
42 : 0 : *second_half = ntohl(*second_half);
43 : : }
44 : :
45 : : /* read_memory_nodes
46 : : * Memory layout for DPAAx platforms (LS1043, LS1046, LS1088, LS2088, LX2160)
47 : : * are populated by Uboot and available in device tree:
48 : : * /proc/device-tree/memory@<address>/reg <= register.
49 : : * Entries are of the form:
50 : : * (<8 byte start addr><8 byte length>)(..more similar blocks of start,len>)..
51 : : *
52 : : * @param count
53 : : * OUT populate number of entries found in memory node
54 : : * @return
55 : : * Pointer to array of reg_node elements, count size
56 : : */
57 : : static struct reg_node *
58 : 0 : read_memory_node(unsigned int *count)
59 : : {
60 : : int fd, ret, i;
61 : : unsigned int j;
62 : 0 : glob_t result = {0};
63 : 0 : struct stat statbuf = {0};
64 : : char file_data[MEM_NODE_FILE_LEN];
65 : : struct reg_node *nodes = NULL;
66 : :
67 : 0 : *count = 0;
68 : :
69 : 0 : ret = glob(MEM_NODE_PATH_GLOB, 0, NULL, &result);
70 [ # # ]: 0 : if (ret != 0)
71 : 0 : ret = glob(MEM_NODE_PATH_GLOB_VM, 0, NULL, &result);
72 : :
73 [ # # ]: 0 : if (ret != 0) {
74 : 0 : DPAAX_DEBUG("Unable to glob device-tree memory node (err: %d)",
75 : : ret);
76 : 0 : goto out;
77 : : }
78 : :
79 [ # # ]: 0 : if (result.gl_pathc != 1) {
80 : : /* Either more than one memory@<addr> node found, or none.
81 : : * In either case, cannot work ahead.
82 : : */
83 : 0 : DPAAX_DEBUG("Found (%zu) entries in device-tree. Not supported!",
84 : : result.gl_pathc);
85 : 0 : goto out;
86 : : }
87 : :
88 : 0 : DPAAX_DEBUG("Opening and parsing device-tree node: (%s)",
89 : : result.gl_pathv[0]);
90 : 0 : fd = open(result.gl_pathv[0], O_RDONLY);
91 [ # # ]: 0 : if (fd < 0) {
92 : 0 : DPAAX_DEBUG("Unable to open the device-tree node: (%s)(fd=%d)",
93 : : MEM_NODE_PATH_GLOB, fd);
94 : 0 : goto cleanup;
95 : : }
96 : :
97 : : /* Stat to get the file size */
98 : 0 : ret = fstat(fd, &statbuf);
99 [ # # ]: 0 : if (ret != 0) {
100 : 0 : DPAAX_DEBUG("Unable to get device-tree memory node size.");
101 : 0 : goto cleanup;
102 : : }
103 : :
104 : 0 : DPAAX_DEBUG("Size of device-tree mem node: %" PRIu64, statbuf.st_size);
105 [ # # ]: 0 : if (statbuf.st_size > MEM_NODE_FILE_LEN) {
106 : 0 : DPAAX_DEBUG("More memory nodes available than assumed.");
107 : 0 : DPAAX_DEBUG("System may not work properly!");
108 : : }
109 : :
110 : 0 : ret = read(fd, file_data, statbuf.st_size > MEM_NODE_FILE_LEN ?
111 [ # # ]: 0 : MEM_NODE_FILE_LEN : statbuf.st_size);
112 [ # # ]: 0 : if (ret <= 0) {
113 : 0 : DPAAX_DEBUG("Unable to read device-tree memory node: (%d)",
114 : : ret);
115 : 0 : goto cleanup;
116 : : }
117 : :
118 : : /* The reg node should be multiple of 16 bytes, 8 bytes each for addr
119 : : * and len.
120 : : */
121 : 0 : *count = (statbuf.st_size / 16);
122 [ # # # # ]: 0 : if ((*count) <= 0 || (statbuf.st_size % 16 != 0)) {
123 : 0 : DPAAX_DEBUG("Invalid memory node values or count. (size=%" PRIu64 ")",
124 : : statbuf.st_size);
125 : 0 : goto cleanup;
126 : : }
127 : :
128 : : /* each entry is of 16 bytes, and size/16 is total count of entries */
129 : 0 : nodes = malloc(sizeof(struct reg_node) * (*count));
130 [ # # ]: 0 : if (!nodes) {
131 : 0 : DPAAX_DEBUG("Failure in allocating working memory.");
132 : 0 : goto cleanup;
133 : : }
134 : : memset(nodes, 0, sizeof(struct reg_node) * (*count));
135 : :
136 [ # # # # ]: 0 : for (i = 0, j = 0; i < (statbuf.st_size) && j < (*count); i += 16, j++) {
137 : 0 : memcpy(&nodes[j], file_data + i, 16);
138 : : /* Rotate (ntohl) each 8 byte entry */
139 : : rotate_8((unsigned char *)(&(nodes[j].addr)));
140 : : rotate_8((unsigned char *)(&(nodes[j].len)));
141 : : }
142 : :
143 : 0 : DPAAX_DEBUG("Device-tree memory node data:");
144 : :
145 [ # # ]: 0 : while (j > 0) {
146 : 0 : --j;
147 : 0 : DPAAX_DEBUG(" %08" PRIx64 " %08zu",
148 : : nodes[j].addr, nodes[j].len);
149 : : }
150 : :
151 : 0 : cleanup:
152 : 0 : close(fd);
153 : 0 : globfree(&result);
154 : 0 : out:
155 : 0 : return nodes;
156 : : }
157 : :
158 : : RTE_EXPORT_INTERNAL_SYMBOL(dpaax_iova_table_populate)
159 : : int
160 : 0 : dpaax_iova_table_populate(void)
161 : : {
162 : : int ret;
163 : : unsigned int i, node_count;
164 : : size_t tot_memory_size, total_table_size;
165 : : struct reg_node *nodes;
166 : : struct dpaax_iovat_element *entry;
167 : :
168 : : /* dpaax_iova_table_p is a singleton - only one instance should be
169 : : * created.
170 : : */
171 [ # # ]: 0 : if (dpaax_iova_table_p) {
172 : 0 : DPAAX_DEBUG("Multiple allocation attempt for IOVA Table (%p)",
173 : : dpaax_iova_table_p);
174 : : /* This can be an error case as well - some path not cleaning
175 : : * up table - but, for now, it is assumed that if IOVA Table
176 : : * pointer is valid, table is allocated.
177 : : */
178 : 0 : return 0;
179 : : }
180 : :
181 : 0 : nodes = read_memory_node(&node_count);
182 [ # # ]: 0 : if (nodes == NULL) {
183 : 0 : DPAAX_WARN("PA->VA translation not available;");
184 : 0 : DPAAX_WARN("Expect performance impact.");
185 : 0 : return -1;
186 : : }
187 : :
188 : : tot_memory_size = 0;
189 [ # # ]: 0 : for (i = 0; i < node_count; i++)
190 : 0 : tot_memory_size += nodes[i].len;
191 : :
192 : 0 : DPAAX_DEBUG("Total available PA memory size: %zu", tot_memory_size);
193 : :
194 : : /* Total table size = meta data + tot_memory_size/8 */
195 : 0 : total_table_size = sizeof(struct dpaax_iova_table) +
196 : 0 : (sizeof(struct dpaax_iovat_element) * node_count) +
197 : 0 : ((tot_memory_size / DPAAX_MEM_SPLIT) * sizeof(uint64_t));
198 : :
199 : : /* TODO: This memory doesn't need to shared but needs to be always
200 : : * pinned to RAM (no swap out) - using hugepage rather than malloc
201 : : */
202 : 0 : dpaax_iova_table_p = rte_zmalloc(NULL, total_table_size, 0);
203 [ # # ]: 0 : if (dpaax_iova_table_p == NULL) {
204 : 0 : DPAAX_WARN("Unable to allocate memory for PA->VA Table;");
205 : 0 : DPAAX_WARN("PA->VA translation not available;");
206 : 0 : DPAAX_WARN("Expect performance impact.");
207 : 0 : free(nodes);
208 : 0 : return -1;
209 : : }
210 : :
211 : : /* Initialize table */
212 : 0 : dpaax_iova_table_p->count = node_count;
213 : 0 : entry = dpaax_iova_table_p->entries;
214 : :
215 : 0 : DPAAX_DEBUG("IOVA Table entries: (entry start = %p)", (void *)entry);
216 : 0 : DPAAX_DEBUG("\t(entry),(start),(len),(next)");
217 : :
218 [ # # ]: 0 : for (i = 0; i < node_count; i++) {
219 : : /* dpaax_iova_table_p
220 : : * | dpaax_iova_table_p->entries
221 : : * | |
222 : : * | |
223 : : * V V
224 : : * +------+------+-------+---+----------+---------+---
225 : : * |iova_ |entry | entry | | pages | pages |
226 : : * |table | 1 | 2 |...| entry 1 | entry2 |
227 : : * +-----'+.-----+-------+---+;---------+;--------+---
228 : : * \ \ / /
229 : : * `~~~~~~|~~~~~>pages /
230 : : * \ /
231 : : * `~~~~~~~~~~~>pages
232 : : */
233 : 0 : entry[i].start = nodes[i].addr;
234 : 0 : entry[i].len = nodes[i].len;
235 [ # # ]: 0 : if (i > 0)
236 : 0 : entry[i].pages = entry[i-1].pages +
237 : 0 : ((entry[i-1].len/DPAAX_MEM_SPLIT));
238 : : else
239 : 0 : entry[i].pages = (uint64_t *)((unsigned char *)entry +
240 : 0 : (sizeof(struct dpaax_iovat_element) *
241 : : node_count));
242 : :
243 : 0 : DPAAX_DEBUG("\t(%u),(%8"PRIx64"),(%8zu),(%8p)",
244 : : i, entry[i].start, entry[i].len, entry[i].pages);
245 : : }
246 : :
247 : : /* Release memory associated with nodes array - not required now */
248 : 0 : free(nodes);
249 : :
250 : 0 : DPAAX_DEBUG("Adding mem-event handler");
251 : 0 : ret = dpaax_handle_memevents();
252 [ # # ]: 0 : if (ret) {
253 : 0 : DPAAX_ERR("Unable to add mem-event handler");
254 : 0 : DPAAX_WARN("Cases with non-buffer pool mem won't work!");
255 : : }
256 : :
257 : : return 0;
258 : : }
259 : :
260 : : RTE_EXPORT_INTERNAL_SYMBOL(dpaax_iova_table_depopulate)
261 : : void
262 : 0 : dpaax_iova_table_depopulate(void)
263 : : {
264 : 0 : rte_free(dpaax_iova_table_p);
265 : 0 : dpaax_iova_table_p = NULL;
266 : :
267 : 0 : DPAAX_DEBUG("IOVA Table cleaned");
268 : 0 : }
269 : :
270 : : RTE_EXPORT_INTERNAL_SYMBOL(dpaax_iova_table_update)
271 : : int
272 : 0 : dpaax_iova_table_update(phys_addr_t paddr, void *vaddr, size_t length)
273 : : {
274 : : int found = 0;
275 : : unsigned int i;
276 : : size_t req_length = length, e_offset;
277 : : struct dpaax_iovat_element *entry;
278 : : uintptr_t align_vaddr;
279 : : phys_addr_t align_paddr;
280 : :
281 [ # # ]: 0 : if (unlikely(dpaax_iova_table_p == NULL))
282 : : return -1;
283 : :
284 : 0 : align_paddr = paddr & DPAAX_MEM_SPLIT_MASK;
285 : 0 : align_vaddr = ((uintptr_t)vaddr & DPAAX_MEM_SPLIT_MASK);
286 : :
287 : : /* Check if paddr is available in table */
288 : 0 : entry = dpaax_iova_table_p->entries;
289 [ # # ]: 0 : for (i = 0; i < dpaax_iova_table_p->count; i++) {
290 [ # # ]: 0 : if (align_paddr < entry[i].start) {
291 : : /* Address lower than start, but not found in previous
292 : : * iteration shouldn't exist.
293 : : */
294 : 0 : DPAAX_ERR("Add: Incorrect entry for PA->VA Table"
295 : : "(%"PRIu64")", paddr);
296 : 0 : DPAAX_ERR("Add: Lowest address: %"PRIu64"",
297 : : entry[i].start);
298 : 0 : return -1;
299 : : }
300 : :
301 [ # # ]: 0 : if (align_paddr > (entry[i].start + entry[i].len))
302 : : continue;
303 : :
304 : : /* align_paddr >= start && align_paddr < (start + len) */
305 : : found = 1;
306 : :
307 : : do {
308 : 0 : e_offset = ((align_paddr - entry[i].start) / DPAAX_MEM_SPLIT);
309 : : /* TODO: Whatif something already exists at this
310 : : * location - is that an error? For now, ignoring the
311 : : * case.
312 : : */
313 : 0 : entry[i].pages[e_offset] = align_vaddr;
314 : : #ifdef RTE_COMMON_DPAAX_DEBUG
315 : : DPAAX_DEBUG("Added: vaddr=%zu for Phy:%"PRIu64" at %zu"
316 : : " remaining len %zu", align_vaddr,
317 : : align_paddr, e_offset, req_length);
318 : : #endif
319 : : /* Incoming request can be larger than the
320 : : * DPAAX_MEM_SPLIT size - in which case, multiple
321 : : * entries in entry->pages[] are filled up.
322 : : */
323 [ # # ]: 0 : if (req_length <= DPAAX_MEM_SPLIT)
324 : : break;
325 : 0 : align_paddr += DPAAX_MEM_SPLIT;
326 : 0 : align_vaddr += DPAAX_MEM_SPLIT;
327 : 0 : req_length -= DPAAX_MEM_SPLIT;
328 : : } while (1);
329 : :
330 : : break;
331 : : }
332 : :
333 [ # # ]: 0 : if (!found) {
334 : : /* There might be case where the incoming physical address is
335 : : * beyond the address discovered in the memory node of
336 : : * device-tree. Specially if some malloc'd area is used by EAL
337 : : * and the memevent handlers passes that across. But, this is
338 : : * not necessarily an error.
339 : : */
340 : 0 : DPAAX_DEBUG("Add: Unable to find slot for vaddr:(%p),"
341 : : " phy(%"PRIu64")",
342 : : vaddr, paddr);
343 : 0 : return -1;
344 : : }
345 : : #ifdef RTE_COMMON_DPAAX_DEBUG
346 : : DPAAX_DEBUG("Add: Found slot at (%"PRIu64")[(%zu)] for vaddr:(%p),"
347 : : " phy(%"PRIu64"), len(%zu)", entry[i].start, e_offset,
348 : : vaddr, paddr, length);
349 : : #endif
350 : : return 0;
351 : : }
352 : :
353 : : /* dpaax_iova_table_dump
354 : : * Dump the table, with its entries, on screen. Only works in Debug Mode
355 : : * Not for weak hearted - the tables can get quite large
356 : : */
357 : : RTE_EXPORT_INTERNAL_SYMBOL(dpaax_iova_table_dump)
358 : : void
359 : 0 : dpaax_iova_table_dump(void)
360 : : {
361 : : unsigned int i, j;
362 : : struct dpaax_iovat_element *entry;
363 : :
364 : : /* In case DEBUG is not enabled, some 'if' conditions might misbehave
365 : : * as they have nothing else in them except a DPAAX_DEBUG() which if
366 : : * tuned out would leave 'if' naked.
367 : : */
368 [ # # ]: 0 : if (rte_log_get_global_level() < RTE_LOG_DEBUG) {
369 : 0 : DPAAX_ERR("Set log level to Debug for PA->Table dump!");
370 : 0 : return;
371 : : }
372 : :
373 : 0 : DPAAX_DEBUG(" === Start of PA->VA Translation Table ===");
374 [ # # ]: 0 : if (dpaax_iova_table_p == NULL) {
375 : 0 : DPAAX_DEBUG("\tNULL");
376 : 0 : return;
377 : : }
378 : :
379 : 0 : entry = dpaax_iova_table_p->entries;
380 [ # # ]: 0 : for (i = 0; i < dpaax_iova_table_p->count; i++) {
381 : 0 : DPAAX_DEBUG("\t(%16i),(%16"PRIu64"),(%16zu),(%16p)",
382 : : i, entry[i].start, entry[i].len, entry[i].pages);
383 : 0 : DPAAX_DEBUG("\t\t (PA), (VA)");
384 [ # # ]: 0 : for (j = 0; j < (entry->len/DPAAX_MEM_SPLIT); j++) {
385 [ # # ]: 0 : if (entry[i].pages[j] == 0)
386 : 0 : continue;
387 : 0 : DPAAX_DEBUG("\t\t(%16"PRIx64"),(%16"PRIx64")",
388 : : (entry[i].start + (j * sizeof(uint64_t))),
389 : : entry[i].pages[j]);
390 : : }
391 : : }
392 : 0 : DPAAX_DEBUG(" === End of PA->VA Translation Table ===");
393 : : }
394 : :
395 : : static void
396 : 0 : dpaax_memevent_cb(enum rte_mem_event type, const void *addr, size_t len,
397 : : void *arg __rte_unused)
398 : : {
399 : : struct rte_memseg_list *msl;
400 : : struct rte_memseg *ms;
401 : : size_t cur_len = 0, map_len = 0;
402 : : phys_addr_t phys_addr;
403 : : void *virt_addr;
404 : : int ret;
405 : :
406 : 0 : DPAAX_DEBUG("Called with addr=%p, len=%zu", addr, len);
407 : :
408 : 0 : msl = rte_mem_virt2memseg_list(addr);
409 : :
410 [ # # ]: 0 : while (cur_len < len) {
411 : 0 : const void *va = RTE_PTR_ADD(addr, cur_len);
412 : :
413 : 0 : ms = rte_mem_virt2memseg(va, msl);
414 : 0 : phys_addr = rte_mem_virt2phy(ms->addr);
415 : 0 : virt_addr = ms->addr;
416 : 0 : map_len = ms->len;
417 : : #ifdef RTE_COMMON_DPAAX_DEBUG
418 : : DPAAX_DEBUG("Request for %s, va=%p, virt_addr=%p,"
419 : : "iova=%"PRIu64", map_len=%zu",
420 : : type == RTE_MEM_EVENT_ALLOC ?
421 : : "alloc" : "dealloc",
422 : : va, virt_addr, phys_addr, map_len);
423 : : #endif
424 [ # # ]: 0 : if (type == RTE_MEM_EVENT_ALLOC)
425 : 0 : ret = dpaax_iova_table_update(phys_addr, virt_addr,
426 : : map_len);
427 : : else
428 : : /* In case of mem_events for MEM_EVENT_FREE, complete
429 : : * hugepage is released and its PA entry is set to 0.
430 : : */
431 : 0 : ret = dpaax_iova_table_update(phys_addr, 0, map_len);
432 : :
433 [ # # ]: 0 : if (ret != 0) {
434 : 0 : DPAAX_DEBUG("PA-Table entry update failed. "
435 : : "Map=%d, addr=%p, len=%zu, err:(%d)",
436 : : type, va, map_len, ret);
437 : 0 : return;
438 : : }
439 : :
440 : 0 : cur_len += map_len;
441 : : }
442 : : }
443 : :
444 : : static int
445 : 0 : dpaax_memevent_walk_memsegs(const struct rte_memseg_list *msl __rte_unused,
446 : : const struct rte_memseg *ms, size_t len,
447 : : void *arg __rte_unused)
448 : : {
449 : 0 : DPAAX_DEBUG("Walking for %p (pa=%"PRIu64") and len %zu",
450 : : ms->addr, ms->iova, len);
451 : 0 : dpaax_iova_table_update(rte_mem_virt2phy(ms->addr), ms->addr, len);
452 : 0 : return 0;
453 : : }
454 : :
455 : : static int
456 : 0 : dpaax_handle_memevents(void)
457 : : {
458 : : /* First, walk through all memsegs and pin them, before installing
459 : : * handler. This assures that all memseg which have already been
460 : : * identified/allocated by EAL, are already part of PA->VA Table. This
461 : : * is especially for cases where application allocates memory before
462 : : * the EAL or this is an externally allocated memory passed to EAL.
463 : : */
464 : 0 : rte_memseg_contig_walk_thread_unsafe(dpaax_memevent_walk_memsegs, NULL);
465 : :
466 : 0 : return rte_mem_event_callback_register("dpaax_memevents_cb",
467 : : dpaax_memevent_cb, NULL);
468 : : }
469 : :
470 : : RTE_EXPORT_INTERNAL_SYMBOL(dpaax_logger)
471 [ - + ]: 252 : RTE_LOG_REGISTER_DEFAULT(dpaax_logger, ERR);
|