]> mj.ucw.cz Git - pciutils.git/blobdiff - lib/mmio-ports.c
libpci: hwdb: Remove ID_SUBSYSTEM and ID_GEN_SUBSYSTEM usage from pci_id_hwdb_lookup()
[pciutils.git] / lib / mmio-ports.c
index b9e3926bf262bd652b0e9e388bbaa1b3e8f83b65..00a595afee38990470f8df48fcc24d5991f26a03 100644 (file)
@@ -3,67 +3,63 @@
  *
  *      Copyright (c) 2022 Pali Rohár <pali@kernel.org>
  *
- *      Can be freely distributed and used under the terms of the GNU GPL.
- */
-
-/*
- * Tell 32-bit platforms that we are interested in 64-bit variant of off_t type
- * as 32-bit variant of off_t type is signed and so it cannot represent all
- * possible 32-bit offsets. It is required because off_t type is used by mmap().
+ *     Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ *     SPDX-License-Identifier: GPL-2.0-or-later
  */
-#define _FILE_OFFSET_BITS 64
 
 #include "internal.h"
+#include "physmem.h"
+#include "physmem-access.h"
 
 #include <ctype.h>
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
-#include <limits.h>
-
-#include <sys/mman.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <unistd.h>
 
-#ifndef OFF_MAX
-#define OFF_MAX (off_t)((1ULL << (sizeof(off_t) * CHAR_BIT - 1)) - 1)
-#endif
-
-struct mmio_cache
-{
-  off_t addr_page;
-  off_t data_page;
+struct mmio_cache {
+  u64 addr_page;
+  u64 data_page;
   void *addr_map;
   void *data_map;
 };
 
-static long pagesize;
+struct mmio_access {
+  struct mmio_cache *cache;
+  struct physmem *physmem;
+  long pagesize;
+};
 
 static void
 munmap_regs(struct pci_access *a)
 {
-  struct mmio_cache *cache = a->aux;
+  struct mmio_access *macc = a->backend_data;
+  struct mmio_cache *cache = macc->cache;
+  struct physmem *physmem = macc->physmem;
+  long pagesize = macc->pagesize;
 
   if (!cache)
     return;
 
-  munmap(cache->addr_map, pagesize);
+  physmem_unmap(physmem, cache->addr_map, pagesize);
   if (cache->addr_page != cache->data_page)
-    munmap(cache->data_map, pagesize);
+    physmem_unmap(physmem, cache->data_map, pagesize);
 
-  pci_mfree(a->aux);
-  a->aux = NULL;
+  pci_mfree(macc->cache);
+  macc->cache = NULL;
 }
 
 static int
-mmap_regs(struct pci_access *a, off_t addr_reg, off_t data_reg, int data_off, volatile void **addr, volatile void **data)
+mmap_regs(struct pci_access *a, u64 addr_reg, u64 data_reg, int data_off, volatile void **addr, volatile void **data)
 {
-  struct mmio_cache *cache = a->aux;
-  off_t addr_page = addr_reg & ~(pagesize-1);
-  off_t data_page = data_reg & ~(pagesize-1);
-  void *addr_map = MAP_FAILED;
-  void *data_map = MAP_FAILED;
+  struct mmio_access *macc = a->backend_data;
+  struct mmio_cache *cache = macc->cache;
+  struct physmem *physmem = macc->physmem;
+  long pagesize = macc->pagesize;
+  u64 addr_page = addr_reg & ~(pagesize-1);
+  u64 data_page = data_reg & ~(pagesize-1);
+  void *addr_map = (void *)-1;
+  void *data_map = (void *)-1;
 
   if (cache && cache->addr_page == addr_page)
     addr_map = cache->addr_map;
@@ -71,35 +67,35 @@ mmap_regs(struct pci_access *a, off_t addr_reg, off_t data_reg, int data_off, vo
   if (cache && cache->data_page == data_page)
     data_map = cache->data_map;
 
-  if (addr_map == MAP_FAILED)
-    addr_map = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, a->fd, addr_page);
+  if (addr_map == (void *)-1)
+    addr_map = physmem_map(physmem, addr_page, pagesize, 1);
 
-  if (addr_map == MAP_FAILED)
+  if (addr_map == (void *)-1)
     return 0;
 
-  if (data_map == MAP_FAILED)
+  if (data_map == (void *)-1)
     {
       if (data_page == addr_page)
         data_map = addr_map;
       else
-        data_map = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_SHARED, a->fd, data_page);
+        data_map = physmem_map(physmem, data_page, pagesize, 1);
     }
 
-  if (data_map == MAP_FAILED)
+  if (data_map == (void *)-1)
     {
       if (!cache || cache->addr_map != addr_map)
-        munmap(addr_map, pagesize);
+        physmem_unmap(physmem, addr_map, pagesize);
       return 0;
     }
 
   if (cache && cache->addr_page != addr_page)
-    munmap(cache->addr_map, pagesize);
+    physmem_unmap(physmem, cache->addr_map, pagesize);
 
   if (cache && cache->data_page != data_page && cache->data_page != cache->addr_page)
-    munmap(cache->data_map, pagesize);
+    physmem_unmap(physmem, cache->data_map, pagesize);
 
   if (!cache)
-    cache = a->aux = pci_malloc(a, sizeof(*cache));
+    cache = macc->cache = pci_malloc(a, sizeof(*cache));
 
   cache->addr_page = addr_page;
   cache->data_page = data_page;
@@ -111,47 +107,11 @@ mmap_regs(struct pci_access *a, off_t addr_reg, off_t data_reg, int data_off, vo
   return 1;
 }
 
-static void
-writeb(unsigned char value, volatile void *addr)
-{
-  *(volatile unsigned char *)addr = value;
-}
-
-static void
-writew(unsigned short value, volatile void *addr)
-{
-  *(volatile unsigned short *)addr = value;
-}
-
-static void
-writel(unsigned long value, volatile void *addr)
-{
-  *(volatile unsigned long *)addr = value;
-}
-
-static unsigned char
-readb(volatile void *addr)
-{
-  return *(volatile unsigned char *)addr;
-}
-
-static unsigned short
-readw(volatile void *addr)
-{
-  return *(volatile unsigned short *)addr;
-}
-
-static unsigned long
-readl(volatile void *addr)
-{
-  return *(volatile unsigned long *)addr;
-}
-
 static int
 validate_addrs(const char *addrs)
 {
   const char *sep, *next;
-  unsigned long long num;
+  u64 num;
   char *endptr;
 
   if (!*addrs)
@@ -172,12 +132,12 @@ validate_addrs(const char *addrs)
 
       errno = 0;
       num = strtoull(addrs, &endptr, 16);
-      if (errno || endptr != sep || (num & 3) || num > OFF_MAX)
+      if (errno || endptr != sep || (num & 3))
         return 0;
 
       errno = 0;
       num = strtoull(sep+1, &endptr, 16);
-      if (errno || endptr != next || (num & 3) || num > OFF_MAX)
+      if (errno || endptr != next || (num & 3))
         return 0;
 
       if (!*next)
@@ -200,7 +160,7 @@ get_domain_count(const char *addrs)
 }
 
 static int
-get_domain_addr(const char *addrs, int domain, off_t *addr_reg, off_t *data_reg)
+get_domain_addr(const char *addrs, int domain, u64 *addr_reg, u64 *data_reg)
 {
   char *endptr;
 
@@ -221,74 +181,110 @@ get_domain_addr(const char *addrs, int domain, off_t *addr_reg, off_t *data_reg)
 static void
 conf1_config(struct pci_access *a)
 {
-  pci_define_param(a, "devmem.path", PCI_PATH_DEVMEM_DEVICE, "Path to the /dev/mem device");
+  physmem_init_config(a);
   pci_define_param(a, "mmio-conf1.addrs", "", "Physical addresses of memory mapped Intel conf1 interface"); /* format: 0xaddr1/0xdata1,0xaddr2/0xdata2,... */
 }
 
+static void
+conf1_ext_config(struct pci_access *a)
+{
+  physmem_init_config(a);
+  pci_define_param(a, "mmio-conf1-ext.addrs", "", "Physical addresses of memory mapped Intel conf1 extended interface"); /* format: 0xaddr1/0xdata1,0xaddr2/0xdata2,... */
+}
+
 static int
-conf1_detect(struct pci_access *a)
+detect(struct pci_access *a, char *addrs_param_name)
 {
-  char *addrs = pci_get_param(a, "mmio-conf1.addrs");
-  char *devmem = pci_get_param(a, "devmem.path");
+  char *addrs = pci_get_param(a, addrs_param_name);
 
   if (!*addrs)
     {
-      a->debug("mmio-conf1.addrs was not specified");
+      a->debug("%s was not specified", addrs_param_name);
       return 0;
     }
 
   if (!validate_addrs(addrs))
     {
-      a->debug("mmio-conf1.addrs has invalid address format %s", addrs);
+      a->debug("%s has invalid address format %s", addrs_param_name, addrs);
       return 0;
     }
 
-  if (access(devmem, R_OK))
+  if (physmem_access(a, 1))
     {
-      a->debug("cannot access %s", devmem);
+      a->debug("cannot access physical memory: %s", strerror(errno));
       return 0;
     }
 
-  a->debug("using %s with %s", devmem, addrs);
+  a->debug("using with %s", addrs);
   return 1;
 }
 
+static int
+conf1_detect(struct pci_access *a)
+{
+  return detect(a, "mmio-conf1.addrs");
+}
+
+static int
+conf1_ext_detect(struct pci_access *a)
+{
+  return detect(a, "mmio-conf1-ext.addrs");
+}
+
+static char*
+get_addrs_param_name(struct pci_access *a)
+{
+  if (a->methods->config == conf1_ext_config)
+    return "mmio-conf1-ext.addrs";
+  else
+    return "mmio-conf1.addrs";
+}
+
 static void
 conf1_init(struct pci_access *a)
 {
-  char *addrs = pci_get_param(a, "mmio-conf1.addrs");
-  char *devmem = pci_get_param(a, "devmem.path");
-
-  pagesize = sysconf(_SC_PAGESIZE);
-  if (pagesize < 0)
-    a->error("Cannot get page size: %s", strerror(errno));
+  char *addrs_param_name = get_addrs_param_name(a);
+  char *addrs = pci_get_param(a, addrs_param_name);
+  struct mmio_access *macc;
+  struct physmem *physmem;
+  long pagesize;
 
   if (!*addrs)
-    a->error("Option mmio-conf1.addrs was not specified.");
+    a->error("Option %s was not specified.", addrs_param_name);
 
   if (!validate_addrs(addrs))
-    a->error("Option mmio-conf1.addrs has invalid address format \"%s\".", addrs);
+    a->error("Option %s has invalid address format \"%s\".", addrs_param_name, addrs);
+
+  physmem = physmem_open(a, 1);
+  if (!physmem)
+    a->error("Cannot open physcal memory: %s.", strerror(errno));
+
+  pagesize = physmem_get_pagesize(physmem);
+  if (pagesize <= 0)
+    a->error("Cannot get page size: %s.", strerror(errno));
 
-  a->fd = open(devmem, O_RDWR);
-  if (a->fd < 0)
-    a->error("Cannot open %s: %s.", devmem, strerror(errno));
+  macc = pci_malloc(a, sizeof(*macc));
+  macc->cache = NULL;
+  macc->physmem = physmem;
+  macc->pagesize = pagesize;
+  a->backend_data = macc;
 }
 
 static void
 conf1_cleanup(struct pci_access *a)
 {
-  if (a->fd < 0)
-    return;
+  struct mmio_access *macc = a->backend_data;
 
   munmap_regs(a);
-  close(a->fd);
-  a->fd = -1;
+  physmem_close(macc->physmem);
+  pci_mfree(macc);
 }
 
 static void
 conf1_scan(struct pci_access *a)
 {
-  char *addrs = pci_get_param(a, "mmio-conf1.addrs");
+  char *addrs_param_name = get_addrs_param_name(a);
+  char *addrs = pci_get_param(a, addrs_param_name);
   int domain_count = get_domain_count(addrs);
   int domain;
 
@@ -297,13 +293,14 @@ conf1_scan(struct pci_access *a)
 }
 
 static int
-conf1_read(struct pci_dev *d, int pos, byte *buf, int len)
+conf1_ext_read(struct pci_dev *d, int pos, byte *buf, int len)
 {
-  char *addrs = pci_get_param(d->access, "mmio-conf1.addrs");
+  char *addrs_param_name = get_addrs_param_name(d->access);
+  char *addrs = pci_get_param(d->access, addrs_param_name);
   volatile void *addr, *data;
-  off_t addr_reg, data_reg;
+  u64 addr_reg, data_reg;
 
-  if (pos >= 256)
+  if (pos >= 4096)
     return 0;
 
   if (len != 1 && len != 2 && len != 4)
@@ -315,18 +312,19 @@ conf1_read(struct pci_dev *d, int pos, byte *buf, int len)
   if (!mmap_regs(d->access, addr_reg, data_reg, pos&3, &addr, &data))
     return 0;
 
-  writel(0x80000000 | ((d->bus & 0xff) << 16) | (PCI_DEVFN(d->dev, d->func) << 8) | (pos & 0xfc), addr);
+  physmem_writel(0x80000000 | ((pos & 0xf00) << 16) | ((d->bus & 0xff) << 16) | (PCI_DEVFN(d->dev, d->func) << 8) | (pos & 0xfc), addr);
+  physmem_readl(addr); /* write barrier for address */
 
   switch (len)
     {
     case 1:
-      buf[0] = readb(data);
+      buf[0] = physmem_readb(data);
       break;
     case 2:
-      ((u16 *) buf)[0] = readw(data);
+      ((u16 *) buf)[0] = physmem_readw(data);
       break;
     case 4:
-      ((u32 *) buf)[0] = readl(data);
+      ((u32 *) buf)[0] = physmem_readl(data);
       break;
     }
 
@@ -334,13 +332,23 @@ conf1_read(struct pci_dev *d, int pos, byte *buf, int len)
 }
 
 static int
-conf1_write(struct pci_dev *d, int pos, byte *buf, int len)
+conf1_read(struct pci_dev *d, int pos, byte *buf, int len)
+{
+  if (pos >= 256)
+    return 0;
+
+  return conf1_ext_read(d, pos, buf, len);
+}
+
+static int
+conf1_ext_write(struct pci_dev *d, int pos, byte *buf, int len)
 {
-  char *addrs = pci_get_param(d->access, "mmio-conf1.addrs");
+  char *addrs_param_name = get_addrs_param_name(d->access);
+  char *addrs = pci_get_param(d->access, addrs_param_name);
   volatile void *addr, *data;
-  off_t addr_reg, data_reg;
+  u64 addr_reg, data_reg;
 
-  if (pos >= 256)
+  if (pos >= 4096)
     return 0;
 
   if (len != 1 && len != 2 && len != 4)
@@ -352,36 +360,67 @@ conf1_write(struct pci_dev *d, int pos, byte *buf, int len)
   if (!mmap_regs(d->access, addr_reg, data_reg, pos&3, &addr, &data))
     return 0;
 
-  writel(0x80000000 | ((d->bus & 0xff) << 16) | (PCI_DEVFN(d->dev, d->func) << 8) | (pos & 0xfc), addr);
+  physmem_writel(0x80000000 | ((pos & 0xf00) << 16) | ((d->bus & 0xff) << 16) | (PCI_DEVFN(d->dev, d->func) << 8) | (pos & 0xfc), addr);
+  physmem_readl(addr); /* write barrier for address */
 
   switch (len)
     {
     case 1:
-      writeb(buf[0], data);
+      physmem_writeb(buf[0], data);
       break;
     case 2:
-      writew(((u16 *) buf)[0], data);
+      physmem_writew(((u16 *) buf)[0], data);
       break;
     case 4:
-      writel(((u32 *) buf)[0], data);
+      physmem_writel(((u32 *) buf)[0], data);
       break;
     }
 
+  /*
+   * write barrier for data
+   * Note that we cannot read from data port because it may have side effect.
+   * Instead we read from address port (which should not have side effect) to
+   * create a barrier between two conf1_write() calls. But this does not have
+   * to be 100% correct as it does not ensure barrier on data port itself.
+   * Correct way is to issue CPU instruction for full hw sync barrier but gcc
+   * does not provide any (builtin) function yet.
+   */
+  physmem_readl(addr);
+
   return 1;
 }
 
+static int
+conf1_write(struct pci_dev *d, int pos, byte *buf, int len)
+{
+  if (pos >= 256)
+    return 0;
+
+  return conf1_ext_write(d, pos, buf, len);
+}
+
 struct pci_methods pm_mmio_conf1 = {
-  "mmio-conf1",
-  "Raw memory mapped I/O port access using Intel conf1 interface",
-  conf1_config,
-  conf1_detect,
-  conf1_init,
-  conf1_cleanup,
-  conf1_scan,
-  pci_generic_fill_info,
-  conf1_read,
-  conf1_write,
-  NULL,                                        /* read_vpd */
-  NULL,                                        /* init_dev */
-  NULL                                 /* cleanup_dev */
+  .name = "mmio-conf1",
+  .help = "Raw memory mapped I/O port access using Intel conf1 interface",
+  .config = conf1_config,
+  .detect = conf1_detect,
+  .init = conf1_init,
+  .cleanup = conf1_cleanup,
+  .scan = conf1_scan,
+  .fill_info = pci_generic_fill_info,
+  .read = conf1_read,
+  .write = conf1_write,
+};
+
+struct pci_methods pm_mmio_conf1_ext = {
+  .name = "mmio-conf1-ext",
+  .help = "Raw memory mapped I/O port access using Intel conf1 extended interface",
+  .config = conf1_ext_config,
+  .detect = conf1_ext_detect,
+  .init = conf1_init,
+  .cleanup = conf1_cleanup,
+  .scan = conf1_scan,
+  .fill_info = pci_generic_fill_info,
+  .read = conf1_ext_read,
+  .write = conf1_ext_write,
 };