*
* 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;
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;
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)
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)
}
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;
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));
- a->fd = open(devmem, O_RDWR | O_DSYNC); /* O_DSYNC bypass CPU cache for mmap() on Linux */
- if (a->fd < 0)
- a->error("Cannot open %s: %s.", devmem, strerror(errno));
+ pagesize = physmem_get_pagesize(physmem);
+ if (pagesize <= 0)
+ a->error("Cannot get page size: %s.", 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;
}
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)
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);
- readl(addr); /* write barrier for address */
+ 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;
}
}
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)
{
- char *addrs = pci_get_param(d->access, "mmio-conf1.addrs");
+ 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_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)
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);
- readl(addr); /* write barrier for address */
+ 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;
}
* Correct way is to issue CPU instruction for full hw sync barrier but gcc
* does not provide any (builtin) function yet.
*/
- readl(addr);
+ 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,
};