qubes-linux-utils/kernel-modules/u2mfn/u2mfn.c
Marek Marczykowski-Górecki e7c90c705f
Declare u2mfn module version, skip build for qubes kernels
This will allow dkms to skip u2mfn module install if one is already
shipped with the kernel package - which is the case for kernels
delivered through dom0.
Make the version high enough to be considered newer than dkms package.

Sadly, it does not prevent module build. And the build fails becaue of
mismatching compiler version (kernel headers include gcc plugins).
Skip the build by setting BUILD_EXCLUSIVE_KERNEL in dkms.conf. Ideally,
we'd set some value indicating "don't build on kernel *qubes*", but
this variable does not support negation. So, set this variable to a
dummy value after manually checking $kernelver variable.

Fixes QubesOS/qubes-issues#4963
2019-06-05 18:21:49 +02:00

174 lines
4.1 KiB
C

/*
* The Qubes OS Project, http://www.qubes-os.org
*
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include <linux/module.h>
#include <linux/version.h>
#include <linux/proc_fs.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/sched.h>
#ifndef FOREIGN_FRAME_BIT
#include <xen/page.h>
#endif
#include <linux/highmem.h>
/* copy of /usr/include/u2mfn-kernel.h, to reduce requirements */
#include <linux/ioctl.h>
#define U2MFN_MAGIC 0xf5
#define U2MFN_GET_MFN_FOR_PAGE _IOW(U2MFN_MAGIC, 1, int)
#define U2MFN_GET_LAST_MFN _IO(U2MFN_MAGIC, 2)
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)
static inline unsigned long virt_to_phys(volatile void *address)
{
return __pa((unsigned long) address);
}
#endif
#ifdef virt_to_mfn
#define VIRT_TO_MFN virt_to_mfn
#else
extern unsigned long *phys_to_machine_mapping;
static inline unsigned long VIRT_TO_MFN(void *addr)
{
return phys_to_machine_mapping[virt_to_phys(addr) >> PAGE_SHIFT] & ~FOREIGN_FRAME_BIT;
}
#endif
static int u2mfn_get_mfn(pte_t *pte, pgtable_t token, unsigned long addr, void *data) {
*((unsigned long *) data) = pfn_to_mfn(pte_pfn(*pte));
return 0;
}
/// User virtual address to mfn translator
/**
\param cmd ignored
\param data the user-specified address
\return mfn corresponding to "data" argument, or -1 on error
*/
static long u2mfn_ioctl(struct file *f, unsigned int cmd,
unsigned long data)
{
long ret;
unsigned long mfn;
if (_IOC_TYPE(cmd) != U2MFN_MAGIC) {
printk("Qubes u2mfn: wrong IOCTL magic");
return -ENOTTY;
}
switch (cmd) {
case U2MFN_GET_MFN_FOR_PAGE:
ret = apply_to_page_range(current->mm, data, PAGE_SIZE, u2mfn_get_mfn, &mfn);
if (ret < 0 || mfn == INVALID_P2M_ENTRY) {
printk("U2MFN_GET_MFN_FOR_PAGE: failed to get mfn, "
"addr=0x%lx ret=0x%lx\n", data, ret);
return -1;
}
ret = mfn;
break;
case U2MFN_GET_LAST_MFN:
if (f->private_data)
ret = VIRT_TO_MFN(f->private_data);
else
ret = 0;
break;
default:
printk("Qubes u2mfn: wrong ioctl passed!\n");
return -ENOTTY;
}
return ret;
}
static int u2mfn_mmap(struct file *f, struct vm_area_struct *vma)
{
int ret;
char *kbuf;
long length = vma->vm_end - vma->vm_start;
printk("u2mfn_mmap: entering, private=%p\n", f->private_data);
if (f->private_data)
return -EBUSY;
if (length != PAGE_SIZE)
return -EINVAL;
kbuf = (char *) __get_free_page(GFP_KERNEL);
if (!kbuf)
return -ENOMEM;
f->private_data = kbuf;
ret = remap_pfn_range(vma, vma->vm_start,
virt_to_phys(kbuf) >> PAGE_SHIFT,
length, vma->vm_page_prot);
printk("u2mfn_mmap: calling remap return %d\n", ret);
if (ret)
return ret;
return 0;
}
static int u2mfn_release(struct inode *i, struct file *f)
{
printk("u2mfn_release, priv=%p\n", f->private_data);
if (f->private_data)
__free_page(f->private_data);
f->private_data = NULL;
return 0;
}
static struct file_operations u2mfn_fops = {
.unlocked_ioctl = u2mfn_ioctl,
.mmap = u2mfn_mmap,
.release = u2mfn_release
};
/// u2mfn module registration
/**
tries to register "/proc/u2mfn" pseudofile
*/
static int u2mfn_init(void)
{
struct proc_dir_entry *u2mfn_node =
proc_create_data("u2mfn", 0666, NULL,
&u2mfn_fops, 0);
if (!u2mfn_node)
return -1;
return 0;
}
static void u2mfn_exit(void)
{
remove_proc_entry("u2mfn", 0);
}
module_init(u2mfn_init);
module_exit(u2mfn_exit);
MODULE_LICENSE("GPL");
MODULE_VERSION("5.0.0");