603 lines
20 KiB
C
603 lines
20 KiB
C
/**
|
|
* Copyright (c) 2021, Systems Group, ETH Zurich
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modification,
|
|
* are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "fpga_fops.h"
|
|
|
|
/*
|
|
███████╗ ██████╗ ██████╗ ███████╗
|
|
██╔════╝██╔═══██╗██╔══██╗██╔════╝
|
|
█████╗ ██║ ██║██████╔╝███████╗
|
|
██╔══╝ ██║ ██║██╔═══╝ ╚════██║
|
|
██║ ╚██████╔╝██║ ███████║
|
|
╚═╝ ╚═════╝ ╚═╝ ╚══════╝
|
|
*/
|
|
|
|
|
|
/* Hash tables */
|
|
struct hlist_head hpid_cpid_map[MAX_N_REGIONS][1 << (PID_HASH_TABLE_ORDER)];
|
|
|
|
#ifdef HMM_KERNEL
|
|
|
|
static struct mmu_interval_notifier_ops cyt_not_ops = {
|
|
.invalidate = cyt_interval_invalidate};
|
|
|
|
#endif
|
|
|
|
|
|
/**
|
|
* @brief Acquire a region
|
|
*
|
|
*/
|
|
int fpga_open(struct inode *inode, struct file *file)
|
|
{
|
|
int minor = iminor(inode);
|
|
|
|
struct fpga_dev *d = container_of(inode->i_cdev, struct fpga_dev, cdev);
|
|
BUG_ON(!d);
|
|
|
|
pr_info("fpga device %d opened, spid %d, ref cnt %d\n", minor, current->pid, d->ref_cnt);
|
|
|
|
// set private data
|
|
file->private_data = (void *)d;
|
|
|
|
// ref cnt
|
|
d->ref_cnt++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Release a region
|
|
*
|
|
*/
|
|
int fpga_release(struct inode *inode, struct file *file)
|
|
{
|
|
int bkt;
|
|
struct hpid_cpid_pages *tmp_h_entry;
|
|
struct list_head *l_p, *l_n;
|
|
struct cpid_entry *l_entry;
|
|
int minor = iminor(inode);
|
|
|
|
struct fpga_dev *d = container_of(inode->i_cdev, struct fpga_dev, cdev);
|
|
BUG_ON(!d);
|
|
|
|
// ref count
|
|
if(--d->ref_cnt == 0) {
|
|
// clear
|
|
hash_for_each(hpid_cpid_map[d->id], bkt, tmp_h_entry, entry) {
|
|
|
|
// traverse all cpid
|
|
list_for_each_safe(l_p, l_n, &tmp_h_entry->cpid_list) {
|
|
// entry
|
|
l_entry = list_entry(l_p, struct cpid_entry, list);
|
|
|
|
#ifdef HMM_KERNEL
|
|
// unamp all leftover user pages
|
|
if(en_hmm)
|
|
free_card_mem(d, l_entry->cpid);
|
|
else
|
|
#endif
|
|
tlb_put_user_pages_cpid(d, l_entry->cpid, tmp_h_entry->hpid, 1);
|
|
|
|
// unregister (if registered)
|
|
d->pid_chunks[l_entry->cpid].next = d->pid_alloc;
|
|
d->pid_alloc = &d->pid_chunks[l_entry->cpid];
|
|
|
|
// delete entry
|
|
list_del(&l_entry->list);
|
|
|
|
// remove if all cpid are gone
|
|
if(list_empty(&tmp_h_entry->cpid_list)) {
|
|
#ifdef HMM_KERNEL
|
|
// remove notifier
|
|
if(en_hmm) {
|
|
dbg_info("releasing notifier for hpid %d\n", tmp_h_entry->hpid);
|
|
mmu_interval_notifier_remove(&tmp_h_entry->mmu_not);
|
|
}
|
|
#endif
|
|
|
|
// free from hpid hash
|
|
hash_del(&tmp_h_entry->entry);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pr_info("fpga device %d released, spid %d, ref cnt %d\n", minor, current->pid, d->ref_cnt);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief ioctl, control and status
|
|
*
|
|
*/
|
|
long fpga_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
{
|
|
int ret_val, i;
|
|
uint64_t tmp[MAX_USER_WORDS];
|
|
int32_t cpid;
|
|
pid_t hpid;
|
|
pid_t spid;
|
|
struct hpid_cpid_pages *tmp_h_entry, *new_h_entry;
|
|
struct list_head *l_p, *l_n;
|
|
struct cpid_entry *l_entry;
|
|
bool k = false;
|
|
#ifdef HMM_KERNEL
|
|
struct task_struct *task;
|
|
struct mm_struct *mm;
|
|
#endif
|
|
|
|
struct fpga_dev *d = (struct fpga_dev *)file->private_data;
|
|
struct bus_drvdata *pd;
|
|
|
|
BUG_ON(!d);
|
|
pd = d->pd;
|
|
BUG_ON(!pd);
|
|
|
|
switch (cmd) {
|
|
|
|
// register a new cpid
|
|
case IOCTL_REGISTER_CPID:
|
|
// read cpid
|
|
ret_val = copy_from_user(&tmp, (unsigned long *)arg, sizeof(unsigned long));
|
|
if (ret_val != 0) {
|
|
pr_info("user data could not be coppied, return %d\n", ret_val);
|
|
} else {
|
|
|
|
// lock
|
|
spin_lock(&d->pid_lock);
|
|
|
|
spid = current->pid;
|
|
hpid = (pid_t)tmp[0];
|
|
|
|
// map
|
|
if(d->num_free_pid_chunks == 0) {
|
|
dbg_info("registration failed hpid %d, spid %d\n", hpid, spid);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
cpid = (int32_t)d->pid_alloc->id;
|
|
d->pid_array[d->pid_alloc->id] = hpid;
|
|
d->pid_alloc = d->pid_alloc->next;
|
|
|
|
// add to hpid map
|
|
k = false;
|
|
hash_for_each_possible(hpid_cpid_map[d->id], tmp_h_entry, entry, hpid) {
|
|
if(tmp_h_entry->hpid == hpid) {
|
|
k = true;
|
|
}
|
|
}
|
|
|
|
if(!k) {
|
|
new_h_entry = kzalloc(sizeof(struct hpid_cpid_pages), GFP_KERNEL);
|
|
BUG_ON(!new_h_entry);
|
|
new_h_entry->hpid = hpid;
|
|
INIT_LIST_HEAD(&new_h_entry->cpid_list);
|
|
#ifdef HMM_KERNEL
|
|
if(en_hmm) {
|
|
task = pid_task(find_vpid(hpid), PIDTYPE_PID);
|
|
mm = task->mm;
|
|
|
|
ret_val = mmu_interval_notifier_insert(&new_h_entry->mmu_not, mm, 0, ULONG_MAX & PAGE_MASK, &cyt_not_ops);
|
|
if (ret_val) {
|
|
dbg_info("mmu notifier registration failed, vFPGA %d\n", d->id);
|
|
kfree(new_h_entry);
|
|
return ret_val;
|
|
}
|
|
}
|
|
#endif
|
|
hash_add(hpid_cpid_map[d->id], &new_h_entry->entry, hpid);
|
|
}
|
|
|
|
// add cpid
|
|
hash_for_each_possible(hpid_cpid_map[d->id], tmp_h_entry, entry, hpid) {
|
|
if(tmp_h_entry->hpid == hpid) {
|
|
// add to cpid list
|
|
l_entry = kmalloc(sizeof(struct cpid_entry), GFP_KERNEL);
|
|
BUG_ON(!l_entry);
|
|
l_entry->cpid = cpid;
|
|
|
|
list_add_tail(&l_entry->list, &tmp_h_entry->cpid_list);
|
|
}
|
|
}
|
|
|
|
#ifdef HMM_KERNEL
|
|
INIT_LIST_HEAD(&migrated_pages[d->id][cpid]);
|
|
#endif
|
|
|
|
pr_info("registration succeeded, cpid %d, hpid %d, spid %d\n", cpid, hpid, spid);
|
|
|
|
// return cpid
|
|
tmp[1] = (int64_t) cpid;
|
|
ret_val = copy_to_user((unsigned long *)arg, &tmp, 2 * sizeof(unsigned long));
|
|
|
|
// unlock
|
|
spin_unlock(&d->pid_lock);
|
|
}
|
|
break;
|
|
|
|
// unregister cpid
|
|
case IOCTL_UNREGISTER_CPID:
|
|
// read cpid
|
|
ret_val = copy_from_user(&tmp, (unsigned long *)arg, sizeof(unsigned long));
|
|
if (ret_val != 0) {
|
|
pr_info("user data could not be coppied, return %d\n", ret_val);
|
|
} else {
|
|
spin_lock(&d->pid_lock);
|
|
|
|
cpid = (int32_t)tmp[0];
|
|
|
|
// map
|
|
hpid = d->pid_array[cpid];
|
|
spid = current->pid;
|
|
|
|
// hash
|
|
hash_for_each_possible(hpid_cpid_map[d->id], tmp_h_entry, entry, hpid) {
|
|
if(tmp_h_entry->hpid == hpid) {
|
|
// traverse all cpid
|
|
list_for_each_safe(l_p, l_n, &tmp_h_entry->cpid_list) {
|
|
// entry
|
|
l_entry = list_entry(l_p, struct cpid_entry, list);
|
|
|
|
if(l_entry->cpid == cpid) {
|
|
#ifdef HMM_KERNEL
|
|
// unamp all leftover user pages
|
|
if(en_hmm)
|
|
free_card_mem(d, cpid);
|
|
else
|
|
#endif
|
|
tlb_put_user_pages_cpid(d, cpid, hpid, 1);
|
|
|
|
// unregister (if registered)
|
|
d->pid_chunks[l_entry->cpid].next = d->pid_alloc;
|
|
d->pid_alloc = &d->pid_chunks[l_entry->cpid];
|
|
|
|
// delete entry
|
|
list_del(&l_entry->list);
|
|
}
|
|
}
|
|
|
|
// remove if all cpid are gone
|
|
if(list_empty(&tmp_h_entry->cpid_list)) {
|
|
#ifdef HMM_KERNEL
|
|
// remove notifier
|
|
if(en_hmm) {
|
|
dbg_info("releasing notifier for hpid %d\n", hpid);
|
|
mmu_interval_notifier_remove(&tmp_h_entry->mmu_not);
|
|
}
|
|
#endif
|
|
|
|
// free from hpid hash
|
|
hash_del(&tmp_h_entry->entry);
|
|
}
|
|
}
|
|
}
|
|
|
|
pr_info("unregistration succeeded, cpid %d, hpid %d, spid %d\n", cpid, hpid, spid);
|
|
|
|
spin_unlock(&d->pid_lock);
|
|
|
|
}
|
|
break;
|
|
|
|
// notify registration
|
|
case IOCTL_REGISTER_EVENTFD:
|
|
// cpid + eventfd descriptor
|
|
ret_val = copy_from_user(&tmp, (unsigned long *)arg, 2 * sizeof(uint64_t));
|
|
if (ret_val) {
|
|
pr_info("user data could not be coppied, ret_val: %d\n", ret_val);
|
|
} else {
|
|
ret_val = fpga_register_eventfd(d, (int32_t)tmp[0], tmp[1]);
|
|
if (ret_val) {
|
|
pr_info("eventfd could not be registered, ret_val: %d\n", ret_val);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IOCTL_UNREGISTER_EVENTFD:
|
|
// cpid
|
|
ret_val = copy_from_user(&tmp, (unsigned long *)arg, 2 * sizeof(uint64_t));
|
|
if (ret_val) {
|
|
pr_info("user data could not be coppied, ret_val: %d\n", ret_val);
|
|
} else {
|
|
fpga_unregister_eventfd(d, (int32_t)tmp[0]);
|
|
}
|
|
break;
|
|
|
|
// explicit mapping
|
|
case IOCTL_MAP_USER:
|
|
// read vaddr + len + cpid
|
|
ret_val = copy_from_user(&tmp, (unsigned long *)arg, 3 * sizeof(unsigned long));
|
|
if (ret_val != 0) {
|
|
pr_info("user data could not be coppied, return %d\n", ret_val);
|
|
} else {
|
|
cpid = (int32_t)tmp[2];
|
|
hpid = d->pid_array[cpid];
|
|
|
|
// lock
|
|
mutex_lock(&d->mmu_lock);
|
|
fpga_change_lock_tlb(d);
|
|
|
|
#ifdef HMM_KERNEL
|
|
if(en_hmm)
|
|
ret_val = mmu_handler_hmm(d, tmp[0], tmp[1], cpid, true, hpid);
|
|
else
|
|
#endif
|
|
ret_val = mmu_handler_gup(d, tmp[0], tmp[1], cpid, true, hpid);
|
|
|
|
if(ret_val) {
|
|
pr_info("buffer could not be mapped, ret_val: %d\n", ret_val);
|
|
}
|
|
|
|
// unlock
|
|
fpga_change_lock_tlb(d);
|
|
mutex_unlock(&d->mmu_lock);
|
|
|
|
dbg_info("user mapping vFPGA %d handled\n", d->id);
|
|
}
|
|
break;
|
|
|
|
// explicit unmapping
|
|
case IOCTL_UNMAP_USER:
|
|
// read vaddr + cpid
|
|
ret_val = copy_from_user(&tmp, (unsigned long *)arg, 2 * sizeof(unsigned long));
|
|
if (ret_val != 0) {
|
|
pr_info("user data could not be coppied, return %d\n", ret_val);
|
|
} else {
|
|
if(!en_hmm) {
|
|
cpid = (int32_t)tmp[1];
|
|
hpid = d->pid_array[cpid];
|
|
|
|
// lock
|
|
mutex_lock(&d->mmu_lock);
|
|
fpga_change_lock_tlb(d);
|
|
|
|
tlb_put_user_pages(d, tmp[0], cpid, hpid, 1);
|
|
|
|
// unlock
|
|
fpga_change_lock_tlb(d);
|
|
mutex_unlock(&d->mmu_lock);
|
|
|
|
dbg_info("user unmapping vFPGA %d handled\n", d->id);
|
|
}
|
|
}
|
|
break;
|
|
|
|
// dmabuf mapping
|
|
case IOCTL_MAP_DMABUF:
|
|
// read buf_fd + vaddr + cpid
|
|
ret_val = copy_from_user(&tmp, (unsigned long *)arg, 3 * sizeof(unsigned long));
|
|
if (ret_val != 0) {
|
|
pr_info("user data could not be coppied, return %d\n", ret_val);
|
|
} else {
|
|
// TODO: Not open-sourced yet
|
|
dbg_info("Dmabuf mapping");
|
|
}
|
|
break;
|
|
|
|
// dmabuf unmapping
|
|
case IOCTL_UNMAP_DMABUF:
|
|
// read fd + cpid
|
|
ret_val = copy_from_user(&tmp, (unsigned long *)arg, 2 * sizeof(unsigned long));
|
|
if (ret_val != 0) {
|
|
pr_info("user data could not be coppied, return %d\n", ret_val);
|
|
} else {
|
|
// TODO: Not open-sourced yet
|
|
dbg_info("Dmabuf unmapping");
|
|
}
|
|
break;
|
|
|
|
// Offload
|
|
case IOCTL_OFFLOAD_REQ:
|
|
// read vaddr + cpid
|
|
ret_val = copy_from_user(&tmp, (unsigned long *)arg, 2 * sizeof(unsigned long));
|
|
if (ret_val != 0) {
|
|
pr_info("user data could not be coppied, return %d\n", ret_val);
|
|
} else {
|
|
if(!en_hmm) {
|
|
ret_val = offload_user_gup(d, tmp[0], (int32_t)tmp[1]);
|
|
if(ret_val) {
|
|
pr_info("buffer could not be offloaded, ret_val: %d\n", ret_val);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
// Sync
|
|
case IOCTL_SYNC_REQ:
|
|
// read vaddr + cpid
|
|
ret_val = copy_from_user(&tmp, (unsigned long *)arg, 2 * sizeof(unsigned long));
|
|
if (ret_val != 0) {
|
|
pr_info("user data could not be coppied, return %d\n", ret_val);
|
|
} else {
|
|
if(!en_hmm) {
|
|
ret_val = sync_user_gup(d, tmp[0], (int32_t)tmp[1]);
|
|
if(ret_val) {
|
|
pr_info("buffer could not be synced, ret_val: %d\n", ret_val);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
// set ip address
|
|
case IOCTL_SET_IP_ADDRESS:
|
|
if (pd->en_net) {
|
|
ret_val = copy_from_user(&tmp, (unsigned long*) arg, sizeof(unsigned long));
|
|
if (ret_val != 0) {
|
|
pr_info("user data could not be coppied, return %d\n", ret_val);
|
|
}
|
|
else {
|
|
spin_lock(&pd->stat_lock);
|
|
|
|
// ip change
|
|
pd->fpga_shell_cnfg->net_ip = tmp[0];
|
|
dbg_info("ip address changed to %08llx\n", tmp[0]);
|
|
pd->net_ip_addr = tmp[0];
|
|
|
|
spin_unlock(&pd->stat_lock);
|
|
}
|
|
} else {
|
|
pr_info("network not enabled\n");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
// set board number
|
|
case IOCTL_SET_MAC_ADDRESS:
|
|
if (pd->en_net) {
|
|
ret_val = copy_from_user(&tmp, (unsigned long*) arg, sizeof(unsigned long));
|
|
if (ret_val != 0) {
|
|
pr_info("user data could not be coppied, return %d\n", ret_val);
|
|
} else {
|
|
spin_lock(&pd->stat_lock);
|
|
|
|
pd->fpga_shell_cnfg->net_mac = tmp[0];
|
|
dbg_info("mac address changed to %llx\n", tmp[0]);
|
|
pd->net_mac_addr = tmp[0];
|
|
|
|
spin_unlock(&pd->stat_lock);
|
|
}
|
|
} else {
|
|
pr_info("network not enabled\n");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
// get ip address
|
|
case IOCTL_GET_IP_ADDRESS:
|
|
tmp[0] = pd->net_ip_addr;
|
|
ret_val = copy_to_user((unsigned long *)arg, &tmp, sizeof(unsigned long));
|
|
break;
|
|
|
|
// get mac address
|
|
case IOCTL_GET_MAC_ADDRESS:
|
|
tmp[0] = pd->net_mac_addr;
|
|
ret_val = copy_to_user((unsigned long *)arg, &tmp, sizeof(unsigned long));
|
|
break;
|
|
|
|
// read config
|
|
case IOCTL_READ_CNFG:
|
|
tmp[0] = ((uint64_t)pd->n_fpga_chan << 32) | ((uint64_t)pd->n_fpga_reg << 48) |
|
|
((uint64_t)pd->en_avx) | ((uint64_t)pd->en_wb << 1) |
|
|
((uint64_t)pd->en_strm << 2) | ((uint64_t)pd->en_mem << 3) | ((uint64_t)pd->en_pr << 4) |
|
|
((uint64_t)pd->en_rdma << 16) | ((uint64_t)pd->en_tcp << 17);
|
|
dbg_info("reading config 0x%llx\n", tmp[0]);
|
|
ret_val = copy_to_user((unsigned long *)arg, &tmp, sizeof(unsigned long));
|
|
break;
|
|
|
|
// xdma status
|
|
case IOCTL_SHELL_XDMA_STATS:
|
|
dbg_info("retreiving xdma status");
|
|
for(i = 0; i < pd->n_fpga_chan * N_XDMA_STAT_CH_REGS; i++) {
|
|
tmp[i] = pd->fpga_shell_cnfg->xdma_debug[i];
|
|
}
|
|
ret_val = copy_to_user((unsigned long *)arg, &tmp, pd->n_fpga_chan * N_XDMA_STAT_CH_REGS * sizeof(unsigned long));
|
|
break;
|
|
|
|
// network status
|
|
case IOCTL_SHELL_NET_STATS:
|
|
if (pd->en_net) {
|
|
dbg_info("retreiving network status for port %llx", tmp[0]);
|
|
for (i = 0; i < N_NET_STAT_REGS; i++) {
|
|
tmp[i] = pd->fpga_shell_cnfg->net_debug[i];
|
|
}
|
|
ret_val = copy_to_user((unsigned long *)arg, &tmp, N_NET_STAT_REGS * sizeof(unsigned long));
|
|
}
|
|
else {
|
|
dbg_info("network not enabled\n");
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Mmap control and mem
|
|
*
|
|
*/
|
|
int fpga_mmap(struct file *file, struct vm_area_struct *vma)
|
|
{
|
|
struct fpga_dev *d;
|
|
|
|
d = (struct fpga_dev *)file->private_data;
|
|
BUG_ON(!d);
|
|
|
|
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
|
|
|
// map user ctrl region
|
|
if (vma->vm_pgoff == MMAP_CTRL) {
|
|
dbg_info("fpga dev. %d, memory mapping user ctrl region at %llx of size %x\n",
|
|
d->id, d->fpga_phys_addr_ctrl + FPGA_CTRL_USER_OFFS, FPGA_CTRL_USER_SIZE);
|
|
if (remap_pfn_range(vma, vma->vm_start, (d->fpga_phys_addr_ctrl + FPGA_CTRL_USER_OFFS) >> PAGE_SHIFT,
|
|
FPGA_CTRL_USER_SIZE, vma->vm_page_prot)) {
|
|
return -EIO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// map cnfg region
|
|
if (vma->vm_pgoff == MMAP_CNFG) {
|
|
dbg_info("fpga dev. %d, memory mapping config region at %llx of size %x\n",
|
|
d->id, d->fpga_phys_addr_ctrl + FPGA_CTRL_CNFG_OFFS, FPGA_CTRL_CNFG_SIZE);
|
|
if (remap_pfn_range(vma, vma->vm_start, (d->fpga_phys_addr_ctrl + FPGA_CTRL_CNFG_OFFS) >> PAGE_SHIFT,
|
|
FPGA_CTRL_CNFG_SIZE, vma->vm_page_prot)) {
|
|
return -EIO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// map cnfg AVX region
|
|
if (vma->vm_pgoff == MMAP_CNFG_AVX) {
|
|
dbg_info("fpga dev. %d, memory mapping config AVX region at %llx of size %x\n",
|
|
d->id, d->fpga_phys_addr_ctrl_avx, FPGA_CTRL_CNFG_AVX_SIZE);
|
|
if (remap_pfn_range(vma, vma->vm_start, d->fpga_phys_addr_ctrl_avx >> PAGE_SHIFT,
|
|
FPGA_CTRL_CNFG_AVX_SIZE, vma->vm_page_prot)) {
|
|
return -EIO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// map writeback
|
|
if (vma->vm_pgoff == MMAP_WB) {
|
|
set_memory_uc((uint64_t)d->wb_addr_virt, N_WB_PAGES);
|
|
dbg_info("fpga dev. %d, memory mapping writeback regions at %llx of size %lx\n",
|
|
d->id, d->wb_phys_addr, WB_SIZE);
|
|
if (remap_pfn_range(vma, vma->vm_start, (d->wb_phys_addr) >> PAGE_SHIFT,
|
|
WB_SIZE, vma->vm_page_prot)) {
|
|
return -EIO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
return -EINVAL;
|
|
} |