libocxl
mmio.c
Go to the documentation of this file.
1 /*
2  * Copyright 2017 International Business Machines
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "libocxl_internal.h"
18 #include "sys/mman.h"
19 #include "errno.h"
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <fcntl.h>
23 #include <unistd.h>
24 #include <string.h>
25 #include <stdlib.h>
26 
52 static ocxl_err register_mmio(ocxl_afu *afu, void *addr, size_t size, ocxl_mmio_type type, ocxl_mmio_h *handle)
53 {
54  int available_mmio = -1;
55 
56  // Look for an available MMIO region that has been unmapped
57  for (uint16_t mmio = 0; mmio < afu->mmio_count; mmio++) {
58  if (!afu->mmios[mmio].start) {
59  available_mmio = mmio;
60  break;
61  }
62  }
63 
64  if (available_mmio == -1) {
65  if (afu->mmio_count == afu->mmio_max_count) {
66  ocxl_err rc = grow_buffer(afu, (void **)&afu->mmios, &afu->mmio_max_count, sizeof(ocxl_mmio_area), INITIAL_MMIO_COUNT);
67  if (rc != OCXL_OK) {
68  errmsg(afu, rc, "Could not grow MMIO buffer");
69  return rc;
70  }
71  }
72 
73  available_mmio = afu->mmio_count++;
74  }
75 
76  afu->mmios[available_mmio].start = addr;
77  afu->mmios[available_mmio].length = size;
78  afu->mmios[available_mmio].type = type;
79  afu->mmios[available_mmio].afu = afu;
80 
81  *handle = (ocxl_mmio_h)&afu->mmios[available_mmio];
82 
83  return OCXL_OK;
84 }
85 
93 ocxl_err global_mmio_open(ocxl_afu *afu)
94 {
95  char path[PATH_MAX + 1];
96  int length = snprintf(path, sizeof(path), "%s/global_mmio_area", afu->sysfs_path);
97  if (length >= (int)sizeof(path)) {
98  ocxl_err rc = OCXL_NO_DEV;
99  errmsg(afu, rc, "global MMIO path truncated");
100  return rc;
101  }
102 
103  int fd = open(path, O_RDWR | O_CLOEXEC);
104  if (fd < 0) {
105  ocxl_err rc = OCXL_NO_DEV;
106  errmsg(afu, rc, "Could not open global MMIO '%s': Error %d: %s", path, errno, strerror(errno));
107  return rc;
108  }
109 
110  afu->global_mmio_fd = fd;
111 
112  return OCXL_OK;
113 }
114 
138 static ocxl_err global_mmio_map(ocxl_afu *afu, size_t size, int prot, uint64_t flags, off_t offset,
139  ocxl_mmio_h *region) // static function extraction hack
140 {
141  if (afu->global_mmio.length == 0) {
142  ocxl_err rc = OCXL_NO_MEM;
143  errmsg(afu, rc, "Cannot map Global MMIO as there is 0 bytes allocated by the AFU");
144  return rc;
145  }
146 
147  if (flags) {
149  errmsg(afu, rc, "MMIO flags of 0x%llx is not supported by this version of libocxl", flags);
150  return rc;
151  }
152 
153  if (size == 0) {
154  size = afu->global_mmio.length;
155  }
156 
157  void *addr = mmap(NULL, size, prot, MAP_SHARED, afu->global_mmio_fd, offset);
158  if (addr == MAP_FAILED) {
159  ocxl_err rc = OCXL_NO_MEM;
160  errmsg(afu, rc, "Could not map global MMIO, %d: %s", errno, strerror(errno));
161  return rc;
162  }
163 
164  ocxl_mmio_h mmio_region;
165  ocxl_err rc = register_mmio(afu, addr, size, OCXL_GLOBAL_MMIO, &mmio_region);
166  if (rc != OCXL_OK) {
167  errmsg(afu, rc, "Could not register global MMIO region");
168  return rc;
169  }
170 
171  *region = mmio_region;
172 
173  return OCXL_OK;
174 }
175 
199 static ocxl_err mmio_map(ocxl_afu *afu, size_t size, int prot, uint64_t flags, off_t offset, ocxl_mmio_h *region)
200 {
201  if (flags) {
203  errmsg(afu, rc, "MMIO flags of 0x%llx is not supported by this version of libocxl", flags);
204  return rc;
205  }
206 
207  if (afu->fd < 0) {
209  errmsg(afu, rc, "Could not map per-PASID MMIO on AFU '%s' as it has not been opened",
210  afu->identifier.afu_name);
211  return rc;
212  }
213 
214  void *addr = mmap(NULL, afu->per_pasid_mmio.length, prot, MAP_SHARED, afu->fd, offset);
215  if (addr == MAP_FAILED) {
216  ocxl_err rc = OCXL_NO_MEM;
217  errmsg(afu, rc, "Could not map per-PASID MMIO: %d: %s", errno, strerror(errno));
218  return rc;
219  }
220 
221  ocxl_mmio_h mmio_region;
222  ocxl_err rc = register_mmio(afu, addr, size, OCXL_PER_PASID_MMIO, &mmio_region);
223  if (rc != OCXL_OK) {
224  errmsg(afu, rc, "Could not register global MMIO region", afu->identifier.afu_name);
225  return rc;
226  }
227 
228  *region = mmio_region;
229 
230  return OCXL_OK;
231 }
232 
254 ocxl_err ocxl_mmio_map_advanced(ocxl_afu_h afu, ocxl_mmio_type type, size_t size, int prot, uint64_t flags,
255  off_t offset, ocxl_mmio_h *region)
256 {
257  ocxl_afu *my_afu = (ocxl_afu *) afu;
259 
260  switch (type) {
261  case OCXL_GLOBAL_MMIO:
262  return global_mmio_map(my_afu, size, prot, flags, offset, region);
263  break;
264 
265  case OCXL_PER_PASID_MMIO:
266  return mmio_map(my_afu, size, prot, flags, offset, region);
267  break;
268 
269  default:
270  errmsg(my_afu, rc, "Unknown MMIO type %d", type);
271  return rc;
272  }
273 }
274 
292 {
293  return ocxl_mmio_map_advanced(afu, type, 0, PROT_READ | PROT_WRITE, 0, 0, region);
294 }
295 
304 {
305  ocxl_mmio_area *mmio = (ocxl_mmio_area *)region;
306 
307  if (!mmio->start) {
308  return;
309  }
310 
311  munmap(mmio->start, mmio->length);
312  mmio->start = NULL;
313 }
314 
330 {
331  ocxl_afu *my_afu = (ocxl_afu *) afu;
332 
333  switch (type) {
334  case OCXL_GLOBAL_MMIO:
335  return my_afu->global_mmio_fd;
336  break;
337 
338  case OCXL_PER_PASID_MMIO:
339  return my_afu->fd;
340  break;
341 
342  default:
343  errmsg(my_afu, OCXL_INVALID_ARGS, "Unknown MMIO type %d", type);
344  return -1;
345  }
346 }
347 
357 {
358  ocxl_afu *my_afu = (ocxl_afu *) afu;
359 
360  switch(type) {
361  case OCXL_GLOBAL_MMIO:
362  return my_afu->global_mmio.length;
363 
364  case OCXL_PER_PASID_MMIO:
365  return my_afu->per_pasid_mmio.length;
366 
367  default:
368  errmsg(my_afu, OCXL_INVALID_ARGS, "Invalid MMIO area requested '%d'", type);
369  return 0;
370  }
371 }
372 
373 
386 ocxl_err ocxl_mmio_get_info(ocxl_mmio_h region, void **address, size_t *size)
387 {
388  ocxl_mmio_area *mmio = (ocxl_mmio_area *)region;
389 
390  if (!mmio->start) {
392  errmsg(mmio->afu, rc, "MMIO region has already been unmapped");
393  return rc;
394  }
395 
396  *address = mmio->start;
397  *size = mmio->length;
398 
399  return OCXL_OK;
400 }
401 
402 
414 inline static ocxl_err mmio_check(ocxl_mmio_h region, off_t offset, size_t size)
415 {
416  if (!region) {
418  errmsg(NULL, rc, "MMIO region is invalid");
419  return rc;
420  }
421 
422  ocxl_mmio_area *mmio = (ocxl_mmio_area *)region;
423 
424  if (!mmio->start) {
426  errmsg(mmio->afu, rc, "MMIO region has already been unmapped");
427  return rc;
428  }
429 
430  if (offset >= (off_t)(mmio->length - (size - 1))) {
432  errmsg(mmio->afu, rc, "%s MMIO access of 0x%016lx exceeds limit of 0x%016lx",
433  mmio->type == OCXL_GLOBAL_MMIO ? "Global" : "Per-PASID",
434  offset, mmio->length);
435  return rc;
436  }
437 
438  return OCXL_OK;
439 }
440 
459 inline static ocxl_err mmio_read32_native(ocxl_mmio_h region, off_t offset, uint32_t *out)
460 {
461  ocxl_err ret = mmio_check(region, offset, 4);
462  if (ret != OCXL_OK) {
463  return ret;
464  }
465 
466  ocxl_mmio_area *mmio = (ocxl_mmio_area *)region;
467 
468  __sync_synchronize();
469  *out = *(volatile uint32_t *)(mmio->start + offset);
470  __sync_synchronize();
471 
472  TRACE(mmio->afu, "%s MMIO Read32@0x%04lx=0x%08x",
473  mmio->type == OCXL_GLOBAL_MMIO ? "Global" : "Per-PASID",
474  offset, *out);
475 
476  return OCXL_OK;
477 }
478 
497 inline static ocxl_err mmio_read64_native(ocxl_mmio_h region, off_t offset, uint64_t *out)
498 {
499  ocxl_err ret = mmio_check(region, offset, 8);
500  if (ret != OCXL_OK) {
501  return ret;
502  }
503 
504  ocxl_mmio_area *mmio = (ocxl_mmio_area *)region;
505 
506  __sync_synchronize();
507  *out = *(volatile uint64_t *)(mmio->start + offset);
508  __sync_synchronize();
509 
510  TRACE(mmio->afu, "%s MMIO Read64@0x%04lx=0x%016lx",
511  mmio->type == OCXL_GLOBAL_MMIO ? "Global" : "Per-PASID",
512  offset, *out);
513 
514  return OCXL_OK;
515 }
516 
537 inline static ocxl_err mmio_write32_native(ocxl_mmio_h region, off_t offset, uint32_t value)
538 {
539  ocxl_err ret = mmio_check(region, offset, 4);
540  if (ret != OCXL_OK) {
541  return ret;
542  }
543 
544  ocxl_mmio_area *mmio = (ocxl_mmio_area *)region;
545 
546  TRACE(mmio->afu, "%s MMIO Write32@0x%04lx=0x%08x",
547  mmio->type == OCXL_GLOBAL_MMIO ? "Global" : "Per-PASID",
548  offset, value);
549 
550  volatile uint32_t * addr = (uint32_t *)(mmio->start + offset);
551 
552  __sync_synchronize();
553  *addr = value;
554  __sync_synchronize();
555 
556  return OCXL_OK;
557 }
558 
577 inline static ocxl_err mmio_write64_native(ocxl_mmio_h region, off_t offset, uint64_t value)
578 {
579  ocxl_err ret = mmio_check(region, offset, 8);
580  if (ret != OCXL_OK) {
581  return ret;
582  }
583 
584  ocxl_mmio_area *mmio = (ocxl_mmio_area *)region;
585 
586  TRACE(mmio->afu, "%s MMIO Write64@0x%04lx=0x%016lx",
587  mmio->type == OCXL_GLOBAL_MMIO ? "Global" : "Per-PASID",
588  offset, value);
589 
590  volatile uint64_t * addr = (uint64_t *)(mmio->start + offset);
591 
592  __sync_synchronize();
593  *addr = value;
594  __sync_synchronize();
595 
596  return OCXL_OK;
597 }
598 
617 ocxl_err ocxl_mmio_read32(ocxl_mmio_h mmio, off_t offset, ocxl_endian endian, uint32_t *out)
618 {
619  uint32_t val;
620  ocxl_err ret = mmio_read32_native(mmio, offset, &val);
621 
622  switch (endian) {
624  *out = be32toh(val);
625  break;
626 
628  *out = le32toh(val);
629  break;
630  default:
631  *out = val;
632  break;
633  }
634 
635  return ret;
636 }
637 
657 ocxl_err ocxl_mmio_read64(ocxl_mmio_h mmio, off_t offset, ocxl_endian endian, uint64_t *out)
658 {
659  uint64_t val;
660  ocxl_err ret = mmio_read64_native(mmio, offset, &val);
661 
662  switch (endian) {
664  *out = be64toh(val);
665  break;
666 
668  *out = le64toh(val);
669  break;
670  default:
671  *out = val;
672  break;
673  }
674 
675  return ret;
676 }
677 
697 ocxl_err ocxl_mmio_write32(ocxl_mmio_h mmio, off_t offset, ocxl_endian endian, uint32_t value)
698 {
699  switch (endian) {
701  value = htobe32(value);
702  break;
703 
705  value = htole32(value);
706  break;
707  default:
708  break;
709  }
710 
711  return mmio_write32_native(mmio, offset, value);
712 }
713 
732 ocxl_err ocxl_mmio_write64(ocxl_mmio_h mmio, off_t offset, ocxl_endian endian, uint64_t value)
733 {
734  switch (endian) {
736  value = htobe64(value);
737  break;
738 
740  value = htole64(value);
741  break;
742  default:
743  break;
744  }
745 
746  return mmio_write64_native(mmio, offset, value);
747 }
748 
749 
AFU data is little-endian.
Definition: libocxl.h:41
ocxl_err ocxl_mmio_map_advanced(ocxl_afu_h afu, ocxl_mmio_type type, size_t size, int prot, uint64_t flags, off_t offset, ocxl_mmio_h *region)
Map an MMIO area of an AFU.
Definition: mmio.c:254
ocxl_err ocxl_mmio_read32(ocxl_mmio_h mmio, off_t offset, ocxl_endian endian, uint32_t *out)
Read a 32-bit value from an AFU&#39;s MMIO region & convert endianess.
Definition: mmio.c:617
ocxl_err ocxl_mmio_get_info(ocxl_mmio_h region, void **address, size_t *size)
Get the address & size of a mapped MMIO region.
Definition: mmio.c:386
ocxl_err
Potential return values from ocxl_* functions.
Definition: libocxl.h:84
size_t ocxl_mmio_size(ocxl_afu_h afu, ocxl_mmio_type type)
Get the size of an MMIO region for an AFU.
Definition: mmio.c:356
The action requested falls outside the permitted area.
Definition: libocxl.h:92
The call requires an open context on the AFU.
Definition: libocxl.h:88
ocxl_err ocxl_mmio_read64(ocxl_mmio_h mmio, off_t offset, ocxl_endian endian, uint64_t *out)
Read a 64-bit value from an AFU&#39;s MMIO region & convert endianess.
Definition: mmio.c:657
void ocxl_mmio_unmap(ocxl_mmio_h region)
Unmap an MMIO region from an AFU.
Definition: mmio.c:303
ocxl_err ocxl_mmio_map(ocxl_afu_h afu, ocxl_mmio_type type, ocxl_mmio_h *region)
Map an MMIO area of an AFU.
Definition: mmio.c:291
void * ocxl_mmio_h
A handle for an MMIO region on an AFU.
Definition: libocxl.h:78
One or more arguments are invalid.
Definition: libocxl.h:94
ocxl_endian
Defines the endianess of an AFU MMIO area.
Definition: libocxl.h:39
The OpenCAPI device is not available.
Definition: libocxl.h:87
ocxl_mmio_type
Defines the type of an MMIO area.
Definition: libocxl.h:48
ocxl_err ocxl_mmio_write32(ocxl_mmio_h mmio, off_t offset, ocxl_endian endian, uint32_t value)
Convert endianess and write a 32-bit value to an AFU&#39;s MMIO region.
Definition: mmio.c:697
AFU data is big-endian.
Definition: libocxl.h:40
ocxl_err global_mmio_open(ocxl_afu *afu)
Open the global MMIO descriptor on an AFU.
Definition: mmio.c:93
void * ocxl_afu_h
A handle for an AFU.
Definition: libocxl.h:66
An out of memory error occurred.
Definition: libocxl.h:86
ocxl_err ocxl_mmio_write64(ocxl_mmio_h mmio, off_t offset, ocxl_endian endian, uint64_t value)
Convert endianess and write a 64-bit value to an AFU&#39;s MMIO region.
Definition: mmio.c:732
int ocxl_mmio_get_fd(ocxl_afu_h afu, ocxl_mmio_type type)
Get a file descriptor for an MMIO area of an AFU.
Definition: mmio.c:329
The call succeeded.
Definition: libocxl.h:85