120 lines
2.6 KiB
C
120 lines
2.6 KiB
C
/*
|
|
* Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
/*
|
|
* @file linux/shmem.c
|
|
* @brief Linux libmetal shared memory handling.
|
|
*/
|
|
|
|
#include <metal/shmem.h>
|
|
#include <metal/sys.h>
|
|
#include <metal/utilities.h>
|
|
|
|
struct metal_shmem {
|
|
struct metal_io_region io;
|
|
metal_phys_addr_t *phys;
|
|
};
|
|
|
|
static void metal_shmem_io_close(struct metal_io_region *io)
|
|
{
|
|
metal_unmap(io->virt, io->size);
|
|
free((void *)io->physmap);
|
|
}
|
|
|
|
static const struct metal_io_ops metal_shmem_io_ops = {
|
|
NULL, NULL, NULL, NULL, NULL, metal_shmem_io_close
|
|
};
|
|
|
|
static int metal_shmem_try_map(struct metal_page_size *ps, int fd, size_t size,
|
|
struct metal_io_region **result)
|
|
{
|
|
size_t pages, page, phys_size;
|
|
struct metal_io_region *io;
|
|
metal_phys_addr_t *phys;
|
|
uint8_t *virt;
|
|
void *mem;
|
|
int error;
|
|
|
|
size = metal_align_up(size, ps->page_size);
|
|
pages = size / ps->page_size;
|
|
|
|
error = metal_map(fd, 0, size, 1, ps->mmap_flags, &mem);
|
|
if (error) {
|
|
metal_log(METAL_LOG_ERROR, "failed to mmap shmem - %s\n",
|
|
strerror(-error));
|
|
return error;
|
|
}
|
|
|
|
error = metal_mlock(mem, size);
|
|
if (error) {
|
|
metal_log(METAL_LOG_WARNING, "failed to mlock shmem - %s\n",
|
|
strerror(-error));
|
|
}
|
|
|
|
phys_size = sizeof(*phys) * pages;
|
|
phys = malloc(phys_size);
|
|
if (!phys) {
|
|
metal_unmap(mem, size);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
io = malloc(sizeof(*io));
|
|
if (!io) {
|
|
free(phys);
|
|
metal_unmap(mem, size);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (_metal.pagemap_fd < 0) {
|
|
phys[0] = 0;
|
|
metal_log(METAL_LOG_WARNING,
|
|
"shmem - failed to get va2pa mapping. use offset as pa.\n");
|
|
metal_io_init(io, mem, phys, size, -1, 0, &metal_shmem_io_ops);
|
|
} else {
|
|
for (virt = mem, page = 0; page < pages; page++) {
|
|
size_t offset = page * ps->page_size;
|
|
error = metal_virt2phys(virt + offset, &phys[page]);
|
|
if (error < 0)
|
|
phys[page] = METAL_BAD_OFFSET;
|
|
}
|
|
metal_io_init(io, mem, phys, size, ps->page_shift, 0,
|
|
&metal_shmem_io_ops);
|
|
}
|
|
*result = io;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int metal_shmem_open(const char *name, size_t size,
|
|
struct metal_io_region **result)
|
|
{
|
|
struct metal_page_size *ps;
|
|
int fd, error;
|
|
|
|
error = metal_shmem_open_generic(name, size, result);
|
|
if (!error)
|
|
return error;
|
|
|
|
error = metal_open(name, 1);
|
|
if (error < 0) {
|
|
metal_log(METAL_LOG_ERROR, "Failed to open shmem file :%s\n", name);
|
|
return error;
|
|
}
|
|
fd = error;
|
|
|
|
/* Iterate through page sizes in decreasing order. */
|
|
metal_for_each_page_size_down(ps) {
|
|
if (ps->page_size > 2 * size)
|
|
continue;
|
|
error = metal_shmem_try_map(ps, fd, size, result);
|
|
if (!error)
|
|
break;
|
|
}
|
|
|
|
close(fd);
|
|
return error;
|
|
}
|