// SPDX-License-Identifier: GPL-2.0
/*
* linux/drivers/char/mem.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*
* Added devfs support.
* Jan-11-1998, C. Scott Ananian <cananian@alumni.princeton.edu>
* Shared /dev/zero mmapping support, Feb 2000, Kanoj Sarcar <kanoj@sgi.com>
*/
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mman.h>
#include <linux/random.h>
#include <linux/init.h>
#include <linux/raw.h>
#include <linux/tty.h>
#include <linux/capability.h>
#include <linux/ptrace.h>
#include <linux/device.h>
#include <linux/highmem.h>
#include <linux/backing-dev.h>
#include <linux/shmem_fs.h>
#include <linux/splice.h>
#include <linux/pfn.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/uio.h>
#include <linux/uaccess.h>
#include <linux/security.h>
#include <linux/pseudo_fs.h>
#include <uapi/linux/magic.h>
#include <linux/mount.h>
#ifdef CONFIG_IA64
# include <linux/efi.h>
#endif
#define DEVMEM_MINOR 1
#define DEVPORT_MINOR 4
static inline unsigned long size_inside_page(unsigned long start,
unsigned long size)
{
unsigned long sz;
sz = PAGE_SIZE - (start & (PAGE_SIZE - 1));
return min(sz, size);
}
#ifndef ARCH_HAS_VALID_PHYS_ADDR_RANGE
static inline int valid_phys_addr_range(phys_addr_t addr, size_t count)
{
return addr + count <= __pa(high_memory);
}
static inline int valid_mmap_phys_addr_range(unsigned long pfn, size_t size)
{
return 1;
}
#endif
#ifdef CONFIG_STRICT_DEVMEM
static inline int page_is_allowed(unsigned long pfn)
{
return devmem_is_allowed(pfn);
}
static inline int range_is_allowed(unsigned long pfn, unsigned long size)
{
u64 from = ((u64)pfn) << PAGE_SHIFT;
u64 to = from + size;
u64 cursor = from;
while (cursor < to) {
if (!devmem_is_allowed(pfn))
return 0;
cursor += PAGE_SIZE;
pfn++;
}
return 1;
}
#else
static inline int page_is_allowed(unsigned long pfn)
{
return 1;
}
static inline int range_is_allowed(unsigned long pfn, unsigned long size)
{
return 1;
}
#endif
#ifndef unxlate_dev_mem_ptr
#define unxlate_dev_mem_ptr unxlate_dev_mem_ptr
void __weak unxlate_dev_mem_ptr(phys_addr_t phys, void *addr)
{
}
#endif
static inline bool should_stop_iteration(void)
{
if (need_resched())
cond_resched();
return fatal_signal_pending(current);
}
/*
* This funcion reads the *physical* memory. The f_pos points directly to the
* memory location.
*/
static ssize_t read_mem(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
phys_addr_t p = *ppos;
ssize_t read, sz;
void *ptr;
char *bounce;
int err;
if (p != *ppos)
return 0;
if (!valid_phys_addr_range(p, count))
return -EFAULT;
read = 0;
#ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED
/* we don't have page 0 mapped on sparc and m68k.. */
if (p < PAGE_SIZE) {
sz = size_inside_page(p, count);
if (sz > 0) {
if (clear_user(buf, sz))
return -EFAULT;
buf += sz;
p += sz;
count -= sz;
read += sz;
}
}
#endif
bounce = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!bounce)
return -ENOMEM;
while (count > 0) {
unsigned long remaining;
int allowed, probe;
sz = size_inside_page(p, count);
err = -EPERM;
allowed = page_is_allowed(p >> PAGE_SHIFT);
if (!allowed)
goto failed;
err = -EFAULT;
if (allowed == 2) {
/* Show zeros for restricted memory. */
remaining = clear_user(buf, sz);
} else {
/*
* On ia64 if a page has been mapped somewhere as
* uncached, then it must also be accessed uncached
* by the kernel or data corruption may occur.
*/
ptr = xlate_dev_mem_ptr(p);
if (!ptr)
goto failed;
probe = copy_from_kernel_nofault(bounce, ptr, sz);
unxlate_dev_mem_ptr(p, ptr);
if (probe)
goto failed;
remaining = copy_to_user(buf, bounce, sz);
}
if (remaining)
goto failed;
buf += sz;
p += sz;
count -= sz;
read += sz;
if (should_stop_iteration())
break;
}
kfree(bounce);
*ppos += read;
return read;
failed:
kfree(bounce);
return err;
}
static ssize_t write_mem(struct file *file, const char __user *