1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003 |
- #include <linux/module.h>
- #include <linux/kallsyms.h>
- #include <linux/version.h>
- #include <linux/init.h>
- #include <linux/fs.h>
- #include <linux/mm_types.h>
- #include <linux/mm.h>
- #include <linux/mmu_notifier.h>
- #include <linux/slab.h>
- #include <linux/swap.h>
- #include <linux/swapops.h>
- #include <linux/miscdevice.h>
- #include <linux/sched.h>
- #include <linux/pagemap.h>
- #include <linux/bitmap.h>
- #include <asm/mman.h>
- #include <asm/tlb.h>
- #include "graphene-ipc.h"
- #include "ksyms.h"
- MODULE_LICENSE("Dual BSD/GPL");
- #define FILE_POISON LIST_POISON1
- struct kmem_cache *gipc_queue_cachep;
- struct kmem_cache *gipc_send_buffer_cachep;
- #define GIPC_DEBUG 0
- #if defined(GIPC_DEBUG) && GIPC_DEBUG == 1
- # define DEBUG(...) printk(KERN_INFO __VA_ARGS__)
- # define GIPC_BUG_ON(cond) BUG_ON(cond)
- #else
- # define DEBUG(...)
- # define GIPC_BUG_ON(cond)
- #endif
- #if defined(CONFIG_GRAPHENE_BULK_IPC) || LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
- # if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
- # define DO_MMAP_PGOFF(file, addr, len, prot, flags, pgoff) \
- ({ \
- unsigned long populate; \
- unsigned long rv = do_mmap_pgoff((file), (addr), (len), \
- (prot), (flags), \
- (pgoff), &populate); \
- rv; })
- # else
- # define DO_MMAP_PGOFF(file, addr, len, prot, flags, pgoff) \
- do_mmap_pgoff((file), (addr), (len), (prot), (flags), (pgoff))
- # endif /* kernel_version < 3.9.0 */
- #else
- # if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)
- # define MY_DO_MMAP
- # define DO_MMAP_PGOFF(file, addr, len, prot, flags, pgoff) \
- ({ \
- unsigned long populate; \
- unsigned long rv; \
- rv = KSYM(do_mmap)((file), (addr), (len), \
- (prot), (flags), 0, (pgoff), \
- &populate); \
- rv; })
- # elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
- # define MY_DO_MMAP_PGOFF
- # define DO_MMAP_PGOFF(file, addr, len, prot, flags, pgoff) \
- ({ \
- unsigned long populate; \
- unsigned long rv; \
- rv = KSYM(do_mmap_pgoff)((file), (addr), (len), \
- (prot), (flags), (pgoff), \
- &populate); \
- rv; })
- # else
- # define MY_DO_MMAP_PGOFF
- # define DO_MMAP_PGOFF(file, addr, len, prot, flags, pgoff) \
- KSYM(do_mmap_pgoff)((file), (addr), (len), (prot), (flags), (pgoff))
- # endif /* kernel version < 3.9 */
- #endif /* !CONFIG_GRAPHENE_BULK_IPC && kernel version > 3.4.0 */
- #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0)
- # ifdef CONFIG_GRAPHENE_BULK_IPC
- # define FLUSH_TLB_MM_RANGE flush_tlb_mm_range
- # else
- # define MY_FLUSH_TLB_MM_RANGE
- # define FLUSH_TLB_MM_RANGE KSYM(flush_tlb_mm_range)
- # endif
- #else /* LINUX_VERSION_CODE < 3.7.0 */
- # if defined(CONFIG_GRAPHENE_BULK_IPC) || LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)
- # define FLUSH_TLB_PAGE flush_tlb_page
- # else
- # define MY_FLUSH_TLB_PAGE
- # define FLUSH_TLB_PAGE KSYM(flush_tlb_page)
- # endif
- #endif
- #ifdef MY_DO_MMAP
- IMPORT_KSYM(do_mmap);
- #endif
- #ifdef MY_DO_MMAP_PGOFF
- IMPORT_KSYM(do_mmap_pgoff);
- #endif
- #ifdef MY_FLUSH_TLB_MM_RANGE
- IMPORT_KSYM(flush_tlb_mm_range);
- #endif
- #ifdef MY_FLUSH_TLB_PAGE
- IMPORT_KSYM(flush_tlb_page);
- #endif
- #ifndef gipc_get_session
- u64 (*my_gipc_get_session) (struct task_struct *) = NULL;
- #endif
- struct gipc_queue {
- struct list_head list;
- s64 token;
- u64 owner;
- atomic_t count;
- struct mutex send_lock, recv_lock;
- wait_queue_head_t send, recv;
- volatile int next, last;
- struct {
- struct page *page;
- struct file *file;
- u64 pgoff;
- } pages[PAGE_QUEUE];
- };
- struct gipc_send_buffer {
- unsigned long page_bit_map[PAGE_BITS];
- struct page *pages[PAGE_QUEUE];
- struct vm_area_struct *vmas[PAGE_QUEUE];
- struct file *files[PAGE_QUEUE];
- unsigned long pgoffs[PAGE_QUEUE];
- };
- struct {
- spinlock_t lock;
- /*
- * For now, just make them monotonically increasing. XXX: At
- * some point, do something smarter for security.
- */
- u64 max_token;
- struct list_head channels; // gipc_queue structs
- } gdev;
- #ifdef gipc_get_session
- #define GIPC_OWNER gipc_get_session(current)
- #else
- #define GIPC_OWNER (my_gipc_get_session ? my_gipc_get_session(current) : 0)
- #endif
- static inline struct gipc_queue * create_gipc_queue(struct file *creator)
- {
- struct gipc_queue *gq = kmem_cache_alloc(gipc_queue_cachep, GFP_KERNEL);
- if (!gq)
- return gq;
- memset(gq, 0, sizeof(*gq));
- INIT_LIST_HEAD(&gq->list);
- mutex_init(&gq->send_lock);
- mutex_init(&gq->recv_lock);
- init_waitqueue_head(&gq->send);
- init_waitqueue_head(&gq->recv);
- gq->owner = GIPC_OWNER;
- creator->private_data = gq;
- atomic_set(&gq->count, 1);
- spin_lock(&gdev.lock);
- list_add(&gq->list, &gdev.channels);
- gq->token = gdev.max_token++;
- spin_unlock(&gdev.lock);
- return gq;
- }
- static inline void release_gipc_queue(struct gipc_queue *gq, bool locked)
- {
- int idx;
- if (!atomic_dec_and_test(&gq->count))
- return;
- if (!locked)
- spin_lock(&gdev.lock);
- while (gq->next != gq->last) {
- idx = gq->next;
- if (gq->pages[idx].page) {
- #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)
- put_page(gq->pages[idx].page);
- #else
- page_cache_release(gq->pages[idx].page);
- #endif
- gq->pages[idx].page = NULL;
- }
- if (gq->pages[idx].file) {
- fput_atomic(gq->pages[idx].file);
- gq->pages[idx].file = NULL;
- gq->pages[idx].pgoff = 0;
- }
- gq->next++;
- gq->next &= (PAGE_QUEUE - 1);
- }
- list_del(&gq->list);
- if (!locked)
- spin_unlock(&gdev.lock);
- kmem_cache_free(gipc_queue_cachep, gq);
- }
- #if defined(SPLIT_RSS_COUNTING)
- static void add_mm_counter_fast(struct mm_struct *mm, int member, int val)
- {
- struct task_struct *task = current;
- if (likely(task->mm == mm))
- task->rss_stat.count[member] += val;
- else
- add_mm_counter(mm, member, val);
- }
- #else
- #define add_mm_counter_fast(mm, member, val) add_mm_counter(mm, member, val)
- #endif
- #define inc_mm_counter_fast(mm, member) add_mm_counter_fast(mm, member, 1)
- #define dec_mm_counter_fast(mm, member) add_mm_counter_fast(mm, member, -1)
- inline int make_page_cow(struct mm_struct *mm, struct vm_area_struct *vma,
- unsigned long addr)
- {
- pgd_t *pgd;
- pud_t *pud;
- pmd_t *pmd;
- pte_t *pte;
- spinlock_t *ptl;
- pgd = pgd_offset(mm, addr);
- if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
- goto no_page;
- pud = pud_offset(pgd, addr);
- if (pud_none(*pud) || unlikely(pud_bad(*pud)))
- goto no_page;
- pmd = pmd_offset(pud, addr);
- if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd)))
- goto no_page;
- BUG_ON(pmd_trans_huge(*pmd));
- pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
- if (!pte_present(*pte)) {
- spin_unlock(ptl);
- goto no_page;
- }
- ptep_set_wrprotect(mm, addr, pte);
- spin_unlock(ptl);
- DEBUG("make page COW at %lx\n", addr);
- return 0;
- no_page:
- return -EFAULT;
- }
- static void fill_page_bit_map(struct mm_struct *mm,
- unsigned long addr, unsigned long nr_pages,
- unsigned long page_bit_map[PAGE_BITS])
- {
- int i = 0;
- DEBUG("GIPC_SEND fill_page_bit_map %lx - %lx\n",
- addr, addr + (nr_pages << PAGE_SHIFT));
- do {
- struct vm_area_struct *vma;
- pgd_t *pgd;
- pud_t *pud;
- pmd_t *pmd;
- pte_t *pte;
- spinlock_t *ptl;
- bool has_page = false;
- vma = find_vma(mm, addr);
- if (!vma)
- goto next;
- BUG_ON(vma->vm_flags & VM_HUGETLB);
- pgd = pgd_offset(mm, addr);
- if (pgd_none(*pgd) || pgd_bad(*pgd))
- goto next;
- pud = pud_offset(pgd, addr);
- if (pud_none(*pud) || pud_bad(*pud))
- goto next;
- pmd = pmd_offset(pud, addr);
- if (pmd_none(*pmd))
- goto next;
- if (unlikely(pmd_trans_huge(*pmd))) {
- has_page = true;
- goto next;
- }
- if (pmd_bad(*pmd))
- goto next;
- pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
- if (pte_none(*pte))
- goto next_locked;
- /*
- if (unlikely(!pte_present(*pte)) && pte_file(*pte))
- goto next_locked;
- */
- has_page = true;
- next_locked:
- spin_unlock(ptl);
- next:
- if (has_page) {
- DEBUG("found a page at %lx\n", addr);
- set_bit(i, page_bit_map);
- } else {
- clear_bit(i, page_bit_map);
- }
- } while (i++, addr += PAGE_SIZE, i < nr_pages);
- }
- static int get_pages (struct task_struct *task, unsigned long start,
- unsigned long nr_pages,
- unsigned long page_bit_map[PAGE_BITS],
- struct page *pages[PAGE_QUEUE],
- struct vm_area_struct *vmas[PAGE_QUEUE])
- {
- struct mm_struct *mm = task->mm;
- struct vm_area_struct *vma = NULL;
- unsigned long addr = start, nr;
- int i = 0, j, rv;
- while (i < nr_pages) {
- unsigned long flushed, vmflags;
- int last = i;
- if (test_bit(last, page_bit_map)) {
- i = find_next_zero_bit(page_bit_map, PAGE_QUEUE,
- last + 1);
- if (i > nr_pages)
- i = nr_pages;
- nr = i - last;
- DEBUG("GIPC_SEND get_user_pages %ld pages at %lx\n",
- addr, nr);
- #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
- rv = get_user_pages(addr, nr,
- FOLL_GET|FOLL_FORCE|FOLL_SPLIT,
- pages + last, vmas + last);
- #else
- rv = __get_user_pages(task, mm, addr, nr,
- FOLL_GET|FOLL_FORCE|FOLL_SPLIT,
- pages + last, vmas + last, NULL);
- #endif
- if (rv <= 0) {
- printk(KERN_ERR "Graphene error: "
- "get_user_pages at 0x%016lx-0x%016lx\n",
- addr, addr + (nr << PAGE_SHIFT));
- return rv;
- }
- if (rv != nr) {
- printk(KERN_ERR "Graphene error: "
- "get_user_pages at 0x%016lx\n",
- addr + (rv << PAGE_SHIFT));
- return -EACCES;
- }
- flushed = addr;
- vmflags = 0;
- for (j = 0; j < nr; j++) {
- unsigned long target = addr + (j << PAGE_SHIFT);
- /* Mark source COW */
- rv = make_page_cow(mm, vmas[last + j],
- target);
- if (rv)
- return rv;
- if (PageAnon(pages[last + j])) {
- /* Fix up the counters */
- inc_mm_counter_fast(mm, MM_FILEPAGES);
- dec_mm_counter_fast(mm, MM_ANONPAGES);
- pages[last + j]->mapping = NULL;
- }
- #ifdef FLUSH_TLB_MM_RANGE
- if (vmflags == vmas[last + j]->vm_flags)
- continue;
- if (flushed < target)
- FLUSH_TLB_MM_RANGE(mm, flushed, target,
- vmflags);
- flushed = target;
- vmflags = vmas[last + j]->vm_flags;
- #else
- FLUSH_TLB_PAGE(vmas[last + j], target);
- #endif
- }
- #ifdef FLUSH_TLB_MM_RANGE
- if (flushed < addr + (nr << PAGE_SHIFT))
- FLUSH_TLB_MM_RANGE(mm, flushed,
- addr + (nr << PAGE_SHIFT),
- vmflags);
- #endif
- vma = vmas[i - 1];
- addr += nr << PAGE_SHIFT;
- } else {
- /* This is the case where a page (or pages) are not
- * currently mapped.
- * Handle the hole appropriately. */
- i = find_next_bit(page_bit_map, PAGE_QUEUE, last + 1);
- if (i > nr_pages)
- i = nr_pages;
- nr = i - last;
- DEBUG("GIPC_SEND skip %ld pages at %lx\n", addr, nr);
- for (j = 0; j < nr; j++) {
- if (!vma) {
- vma = find_vma(mm, addr);
- } else {
- /* DEP 6/17/13 - these addresses should
- * be monotonically increasing. */
- for (; vma && addr >= vma->vm_end;
- vma = vma->vm_next);
- /* Leverage monotonic increasing vmas
- * to more quickly detect holes in the
- * address space. */
- if (vma && addr < vma->vm_start)
- vma = NULL;
- }
- pages[last + j] = NULL;
- vmas[last + j] = vma;
- addr += PAGE_SIZE;
- }
- }
- }
- return i;
- }
- static int do_gipc_send(struct task_struct *task, struct gipc_queue *gq,
- struct gipc_send_buffer *gbuf,
- unsigned long __user *uaddr, unsigned long __user *ulen,
- unsigned long *copied_pages)
- {
- struct mm_struct *mm = task->mm;
- unsigned long addr, len, nr_pages;
- int rv, i;
- DEBUG("GIPC_SEND uaddr = %p, ulen = %p\n", uaddr, ulen);
- rv = copy_from_user(&addr, uaddr, sizeof(unsigned long));
- if (rv) {
- printk(KERN_ALERT "Graphene SEND: bad buffer %p\n", uaddr);
- return -EFAULT;
- }
- rv = copy_from_user(&len, ulen, sizeof(unsigned long));
- if (rv) {
- printk(KERN_ALERT "Graphene SEND: bad buffer %p\n", ulen);
- return -EFAULT;
- }
- if (addr > addr + len) {
- printk(KERN_ALERT "Graphene SEND: attempt to send %p - %p "
- " by thread %d FAIL: bad argument\n",
- (void *) addr, (void *) (addr + len), task->pid);
- return -EINVAL;
- }
- DEBUG("GIPC_SEND addr = %lx, len = %ld\n", addr, len);
- nr_pages = len >> PAGE_SHIFT;
- if (!access_ok(VERIFY_READ, addr, len)) {
- printk(KERN_ALERT "Graphene SEND:"
- " attempt to send %p - %p (%ld pages) "
- " by thread %d FAIL: bad permission\n",
- (void *) addr, (void *) (addr + len), nr_pages,
- task->pid);
- return -EFAULT;
- }
- DEBUG(" %p - %p (%ld pages) sent by thread %d\n",
- (void *) addr, (void *) (addr + len), nr_pages, task->pid);
- while (nr_pages) {
- unsigned long nr =
- (nr_pages <= PAGE_QUEUE) ? nr_pages : PAGE_QUEUE;
- /* for each of these addresses - check if
- * demand faulting will be triggered
- * if vma is present, but there is no page
- * present(pmd/pud not present or PTE_PRESENT
- * is off) then get_user_pages will trigger
- * the creation of those */
- down_write(&mm->mmap_sem);
- fill_page_bit_map(mm, addr, nr, gbuf->page_bit_map);
- rv = get_pages(task, addr, nr,
- gbuf->page_bit_map,
- gbuf->pages,
- gbuf->vmas);
- if (rv < 0) {
- up_write(&mm->mmap_sem);
- break;
- }
- for (i = 0; i < nr; i++) {
- BUG_ON((!gbuf->vmas[i]) && (!!gbuf->pages[i]));
- if (gbuf->vmas[i] && gbuf->vmas[i]->vm_file) {
- gbuf->files[i] = get_file(gbuf->vmas[i]->vm_file);
- gbuf->pgoffs[i] =
- ((addr - gbuf->vmas[i]->vm_start) >> PAGE_SHIFT)
- + gbuf->vmas[i]->vm_pgoff;
- } else {
- gbuf->files[i] = NULL;
- gbuf->pgoffs[i] = 0;
- }
- addr += PAGE_SIZE;
- }
- up_write(&mm->mmap_sem);
- for (i = 0; i < nr ; i++) {
- /* Put in the pending buffer*/
- if (((gq->last + 1) & (PAGE_QUEUE - 1)) == gq->next) {
- /* The blocking condition for send
- * and recv can't both be true! */
- wake_up_all(&gq->recv);
- wait_event_interruptible(gq->send,
- ((gq->last + 1) & (PAGE_QUEUE - 1)) != gq->next);
- if (signal_pending(task)) {
- rv = -ERESTARTSYS;
- goto out;
- }
- }
- gq->pages[gq->last].page = gbuf->pages[i];
- gq->pages[gq->last].file = gbuf->files[i];
- gq->pages[gq->last].pgoff = gbuf->pgoffs[i];
- gq->last++;
- gq->last &= PAGE_QUEUE - 1;
- (*copied_pages)++;
- }
- wake_up_all(&gq->recv);
- nr_pages -= nr;
- }
- out:
- return rv;
- }
- static inline
- int recv_next (struct task_struct *task, struct gipc_queue *gq)
- {
- if (gq->next == gq->last) {
- /* The blocking condition for send & recv can't both be true */
- wake_up_all(&gq->send);
- wait_event_interruptible(gq->recv, gq->next != gq->last);
- if (signal_pending(task))
- return -ERESTARTSYS;
- }
-
- return gq->next;
- }
- static int do_gipc_recv(struct task_struct *task, struct gipc_queue *gq,
- unsigned long __user *uaddr, unsigned long __user *ulen,
- unsigned long __user *uprot,
- unsigned long *copied_pages)
- {
- struct mm_struct *mm = task->mm;
- struct vm_area_struct *vma = NULL;
- unsigned long start, addr, len, nr_pages, prot, pgoff;
- struct page *page = NULL;
- struct file *file = NULL;
- int i = 0, rv;
- rv = copy_from_user(&addr, uaddr, sizeof(unsigned long));
- if (rv) {
- printk(KERN_ALERT "Graphene RECV: bad buffer %p\n", uaddr);
- return -EFAULT;
- }
- rv = copy_from_user(&len, ulen, sizeof(unsigned long));
- if (rv) {
- printk(KERN_ALERT "Graphene RECV: bad buffer %p\n", ulen);
- return -EFAULT;
- }
- rv = copy_from_user(&prot, uprot, sizeof(unsigned long));
- if (rv) {
- printk(KERN_ALERT "Graphene RECV: bad buffer %p\n", uprot);
- return -EFAULT;
- }
- nr_pages = len >> PAGE_SHIFT;
- start = addr;
- down_write(&mm->mmap_sem);
- while (i < nr_pages) {
- int found = recv_next(task, gq);
- int need_map = 1;
- if (found < 0) {
- rv = found;
- goto finish;
- }
- page = gq->pages[found].page;
- file = gq->pages[found].file;
- pgoff = gq->pages[found].pgoff;
- gq->next++;
- gq->next &= PAGE_QUEUE - 1;
- wake_up_all(&gq->send);
- if (vma) {
- need_map = 0;
- if (vma->vm_file != file)
- need_map = 1;
- if (file && vma->vm_start +
- ((pgoff - vma->vm_pgoff) << PAGE_SHIFT)
- != addr)
- need_map = 1;
- if (prot != (vma->vm_flags & (VM_READ|VM_WRITE|VM_EXEC)))
- need_map = 1;
- }
- if (need_map) {
- unsigned long flags = MAP_PRIVATE;
- if (addr)
- flags |= MAP_FIXED;
- if (file)
- flags |= MAP_FILE;
- else
- flags |= MAP_ANONYMOUS;
- addr = DO_MMAP_PGOFF(file, addr,
- (nr_pages - i) << PAGE_SHIFT,
- prot, flags, pgoff);
- if (IS_ERR_VALUE(addr)) {
- rv = PTR_ERR((void *) addr);
- printk(KERN_ERR
- "Graphene error: failed to mmap (%d)\n",
- -rv);
- goto finish;
- }
- if (file)
- DEBUG("map %08lx-%08lx file %p\n", addr,
- addr + ((nr_pages - i) << PAGE_SHIFT),
- file);
- else
- DEBUG("map %08lx-%08lx\n", addr,
- addr + ((nr_pages - i) << PAGE_SHIFT));
- if (!start)
- start = addr;
- vma = find_vma(mm, addr);
- if (!vma) {
- printk(KERN_ERR
- "Graphene error: can't find vma at %p\n",
- (void *) addr);
- rv = -ENOENT;
- goto finish;
- }
- } else {
- BUG_ON(!vma);
- }
- if (page) {
- rv = vm_insert_page(vma, addr, page);
- if (rv) {
- printk(KERN_ERR "Graphene error: "
- "fail to insert page %d\n", rv);
- goto finish;
- }
- rv = make_page_cow(mm, vma, addr);
- if (rv) {
- printk(KERN_ERR "Graphene error: "
- "can't make vma copy-on-write at %p\n",
- (void *) addr);
- goto finish;
- }
- }
- finish:
- /* Drop the kernel's reference to this page */
- if (page)
- #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)
- put_page(page);
- #else
- page_cache_release(page);
- #endif
- if (file)
- fput_atomic(file);
- if (rv)
- break;
- i++;
- addr += PAGE_SIZE;
- (*copied_pages)++;
- }
- up_write(&mm->mmap_sem);
- if (i)
- DEBUG(" %p - %p (%d pages) received by thread %d\n",
- (void *) start, (void *) start + (i << PAGE_SHIFT), i,
- task->pid);
- if (start) {
- rv = copy_to_user(uaddr, &start, sizeof(unsigned long));
- if (rv) {
- printk(KERN_ERR "Graphene error: bad buffer %p\n",
- uaddr);
- return -EFAULT;
- }
- }
- return rv;
- }
- static long gipc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
- {
- struct task_struct *task = current;
- struct gipc_queue *gq = NULL;
- long rv = 0;
- switch (cmd) {
- case GIPC_SEND: {
- struct gipc_send gs;
- struct gipc_send_buffer *gbuf;
- int i;
- unsigned long nr_pages = 0;
- rv = copy_from_user(&gs, (void *) arg, sizeof(gs));
- if (rv) {
- printk(KERN_ALERT "Graphene SEND: bad buffer %p\n",
- (void *) arg);
- return -EFAULT;
- }
- /* Find/allocate the gipc_pages struct for our recipient */
- gq = (struct gipc_queue *) file->private_data;
- if (!gq)
- return -EFAULT;
- gbuf = kmem_cache_alloc(gipc_send_buffer_cachep, GFP_KERNEL);
- if (!gbuf)
- return -ENOMEM;
- DEBUG("GIPC_SEND %ld entries to token %lld by thread %d\n",
- gs.entries, gq->token, task->pid);
- mutex_lock(&gq->send_lock);
- for (i = 0; i < gs.entries; i++) {
- rv = do_gipc_send(task, gq, gbuf, gs.addr + i,
- gs.len + i, &nr_pages);
- if (rv < 0)
- break;
- }
- mutex_unlock(&gq->send_lock);
- DEBUG("GIPC_SEND return to thread %d, %ld pages are sent\n",
- task->pid, nr_pages);
- kmem_cache_free(gipc_send_buffer_cachep, gbuf);
- rv = nr_pages ? : rv;
- break;
- }
- case GIPC_RECV: {
- struct gipc_recv gr;
- int i;
- unsigned long nr_pages = 0;
- rv = copy_from_user(&gr, (void *) arg, sizeof(gr));
- if (rv) {
- printk(KERN_ERR "Graphene error: bad buffer %p\n",
- (void *) arg);
- return -EFAULT;
- }
- gq = (struct gipc_queue *) file->private_data;
- if (!gq)
- return -EBADF;
- DEBUG("GIPC_RECV %ld entries to token %lld by thread %d\n",
- gr.entries, gq->token, task->pid);
- mutex_lock(&gq->recv_lock);
- for (i = 0; i < gr.entries; i++) {
- rv = do_gipc_recv(task, gq, gr.addr + i, gr.len + i,
- gr.prot + i, &nr_pages);
- if (rv < 0)
- break;
- }
- mutex_unlock(&gq->recv_lock);
- DEBUG("GIPC_RECV return to thread %d, %ld pages are received\n",
- task->pid, nr_pages);
- rv = nr_pages ? : rv;
- break;
- }
- case GIPC_CREATE: {
- gq = create_gipc_queue(file);
- if (!gq) {
- rv = -ENOMEM;
- break;
- }
- DEBUG("GIPC_CREATE token %lld by thread %d\n", gq->token,
- task->pid);
- rv = gq->token;
- break;
- }
- case GIPC_JOIN: {
- struct gipc_queue *q;
- u64 token = arg;
- u64 session = GIPC_OWNER;
- if (file->private_data != NULL)
- return -EBUSY;
- /* Search for this token */
- spin_lock(&gdev.lock);
- list_for_each_entry(q, &gdev.channels, list) {
- if (q->token == token) {
- gq = q;
- break;
- }
- }
- /* Fail if we didn't find it */
- if (!gq) {
- spin_unlock(&gdev.lock);
- return -ENOENT;
- }
- if (gq->owner != session) {
- spin_unlock(&gdev.lock);
- return -EPERM;
- }
- atomic_inc(&gq->count);
- file->private_data = gq;
- /* Hold the lock until we allocate so only one process
- * gets the queue */
- spin_unlock(&gdev.lock);
- DEBUG("GIPC_JOIN token %lld by thread %d\n", token, task->pid);
- rv = 0;
- break;
- }
- default:
- printk(KERN_ALERT "Graphene unknown ioctl %u %lu\n", cmd, arg);
- rv = -ENOSYS;
- break;
- }
- return rv;
- }
- static int gipc_release(struct inode *inode, struct file *file)
- {
- struct gipc_queue *gq = (struct gipc_queue *) file->private_data;
- if (!gq)
- return 0;
- file->private_data = NULL;
- release_gipc_queue(gq, false);
- return 0;
- }
- static int gipc_open(struct inode *inode, struct file *file)
- {
- file->private_data = NULL;
- return 0;
- }
- static struct file_operations gipc_fops = {
- .owner = THIS_MODULE,
- .release = gipc_release,
- .open = gipc_open,
- .unlocked_ioctl = gipc_ioctl,
- .compat_ioctl = gipc_ioctl,
- .llseek = noop_llseek,
- };
- static struct miscdevice gipc_dev = {
- .minor = GIPC_MINOR,
- .name = "gipc",
- .fops = &gipc_fops,
- .mode = 0666,
- };
- static int __init gipc_init(void)
- {
- int rv = 0;
- #ifdef MY_DO_MMAP
- LOOKUP_KSYM(do_mmap);
- #endif
- #ifdef MY_DO_MMAP_PGOFF
- LOOKUP_KSYM(do_mmap_pgoff);
- #endif
- #ifdef MY_FLUSH_TLB_MM_RANGE
- LOOKUP_KSYM(flush_tlb_mm_range);
- #endif
- #ifdef MY_FLUSH_TLB_PAGE
- LOOKUP_KSYM(flush_tlb_page);
- #endif
- #ifndef gipc_get_session
- my_gipc_get_session = (void *) kallsyms_lookup_name("gipc_get_session");
- #endif
- /* Register the kmem cache */
- gipc_queue_cachep = kmem_cache_create("gipc queue",
- sizeof(struct gipc_queue),
- 0,
- SLAB_HWCACHE_ALIGN|
- SLAB_DESTROY_BY_RCU,
- NULL);
- if (!gipc_queue_cachep) {
- printk(KERN_ERR "Graphene error: "
- "failed to create a gipc queues cache\n");
- return -ENOMEM;
- }
- gipc_send_buffer_cachep = kmem_cache_create("gipc send buffer",
- sizeof(struct gipc_send_buffer),
- 0,
- SLAB_HWCACHE_ALIGN|
- SLAB_DESTROY_BY_RCU,
- NULL);
- if (!gipc_send_buffer_cachep) {
- printk(KERN_ERR "Graphene error: "
- "failed to create a gipc buffers cache\n");
- return -ENOMEM;
- }
- INIT_LIST_HEAD(&gdev.channels);
- spin_lock_init(&gdev.lock);
- gdev.max_token = 1;
- rv = misc_register(&gipc_dev);
- if (rv) {
- printk(KERN_ERR "Graphene error: "
- "failed to add a char device (rv=%d)\n", rv);
- return rv;
- }
- printk(KERN_ALERT "Graphene IPC: Hello, world\n");
- return 0;
- }
- static void __exit gipc_exit(void)
- {
- struct gipc_queue *gq, *n;
- spin_lock(&gdev.lock);
- list_for_each_entry_safe(gq, n, &gdev.channels, list)
- release_gipc_queue(gq, true);
- spin_unlock(&gdev.lock);
- misc_deregister(&gipc_dev);
- kmem_cache_destroy(gipc_queue_cachep);
- printk(KERN_ALERT "Graphene IPC: Goodbye, cruel world\n");
- }
- module_init(gipc_init);
- module_exit(gipc_exit);
|