Merge "msm: kgsl: Make secure memory depend on QCOM_SECURE_BUFFER"

This commit is contained in:
qctecmdr 2019-12-05 19:00:04 -08:00 • committed by Gerrit - the friendly Code Review server
commit d4d0ed4fbc
9 changed files with 157 additions and 213 deletions

View file

@ -1427,8 +1427,10 @@ static int adreno_probe(struct platform_device *pdev)
* check the GPU capabilities here and modify mmu->secured accordingly
*/
#if IS_ENABLED(CONFIG_QCOM_SECURE_BUFFER)
if (!ADRENO_FEATURE(adreno_dev, ADRENO_CONTENT_PROTECTION))
device->mmu.secured = false;
#endif
if (ADRENO_FEATURE(adreno_dev, ADRENO_IOCOHERENT))
device->mmu.features |= KGSL_MMU_IO_COHERENT;

View file

@ -312,16 +312,14 @@ static int ctx_print(struct seq_file *s, void *unused)
static int ctx_open(struct inode *inode, struct file *file)
{
int ret;
unsigned int id = (unsigned int)(unsigned long)inode->i_private;
struct kgsl_context *context;
struct adreno_context *ctx = inode->i_private;
context = kgsl_context_get(kgsl_get_device(KGSL_DEVICE_3D0), id);
if (context == NULL)
if (!_kgsl_context_get(&ctx->base))
return -ENODEV;
ret = single_open(file, ctx_print, context);
ret = single_open(file, ctx_print, &ctx->base);
if (ret)
kgsl_context_put(context);
kgsl_context_put(&ctx->base);
return ret;
}
@ -350,11 +348,16 @@ adreno_context_debugfs_init(struct adreno_device *adreno_dev,
{
unsigned char name[16];
/*
* Get the context here to make sure it still exists for the life of the
* file
*/
_kgsl_context_get(&ctx->base);
snprintf(name, sizeof(name), "%d", ctx->base.id);
ctx->debug_root = debugfs_create_file(name, 0444,
adreno_dev->ctx_d_debugfs,
(void *)(unsigned long)ctx->base.id, &ctx_fops);
adreno_dev->ctx_d_debugfs, ctx, &ctx_fops);
}
void adreno_debugfs_init(struct adreno_device *adreno_dev)

View file

@ -478,6 +478,8 @@ void adreno_drawctxt_detach(struct kgsl_context *context)
}
debugfs_remove_recursive(drawctxt->debug_root);
/* The debugfs file has a reference, release it */
kgsl_context_put(context);
/*
* internal_timestamp is set in adreno_ringbuffer_addcmds,

View file

@ -4551,169 +4551,94 @@ static unsigned long _gpu_set_svm_region(struct kgsl_process_private *private,
return addr;
}
static unsigned long _gpu_find_svm(struct kgsl_process_private *private,
unsigned long start, unsigned long end, unsigned long len,
unsigned int align)
static unsigned long get_align(struct kgsl_mem_entry *entry)
{
uint64_t addr = kgsl_mmu_find_svm_region(private->pagetable,
(uint64_t) start, (uint64_t)end, (uint64_t) len, align);
int bit = kgsl_memdesc_get_align(&entry->memdesc);
WARN(!IS_ERR_VALUE((unsigned long)addr) && (addr > ULONG_MAX),
"Couldn't find range\n");
if (bit >= ilog2(SZ_2M))
return SZ_2M;
else if (bit >= ilog2(SZ_1M))
return SZ_1M;
else if (bit >= ilog2(SZ_64K))
return SZ_64K;
return (unsigned long) addr;
return SZ_4K;
}
/* Search top down in the CPU VM region for a free address */
static unsigned long _cpu_get_unmapped_area(unsigned long bottom,
unsigned long top, unsigned long len, unsigned long align)
{
struct vm_unmapped_area_info info;
unsigned long addr, err;
info.flags = VM_UNMAPPED_AREA_TOPDOWN;
info.low_limit = bottom;
info.high_limit = top;
info.length = len;
info.align_offset = 0;
info.align_mask = align - 1;
addr = vm_unmapped_area(&info);
if (IS_ERR_VALUE(addr))
return addr;
err = security_mmap_addr(addr);
return err ? err : addr;
}
static unsigned long _search_range(struct kgsl_process_private *private,
static unsigned long set_svm_area(struct file *file,
struct kgsl_mem_entry *entry,
unsigned long start, unsigned long end,
unsigned long len, uint64_t align)
unsigned long addr, unsigned long len,
unsigned long flags)
{
unsigned long cpu, gpu = end, result = -ENOMEM;
struct kgsl_device_private *dev_priv = file->private_data;
struct kgsl_process_private *private = dev_priv->process_priv;
unsigned long ret;
while (gpu > start) {
/* find a new empty spot on the CPU below the last one */
cpu = _cpu_get_unmapped_area(start, gpu, len,
(unsigned long) align);
if (IS_ERR_VALUE(cpu)) {
result = cpu;
break;
}
/* try to map it on the GPU */
result = _gpu_set_svm_region(private, entry, cpu, len);
if (!IS_ERR_VALUE(result))
break;
trace_kgsl_mem_unmapped_area_collision(entry, cpu, len);
if (cpu <= start) {
result = -ENOMEM;
break;
}
/* move downward to the next empty spot on the GPU */
gpu = _gpu_find_svm(private, start, cpu, len, align);
if (IS_ERR_VALUE(gpu)) {
result = gpu;
break;
}
/* Check that_gpu_find_svm doesn't put us in a loop */
if (gpu >= cpu) {
result = -ENOMEM;
break;
}
/* Break if the recommended GPU address is out of range */
if (gpu < start) {
result = -ENOMEM;
break;
}
/*
* Add the length of the chunk to the GPU address to yield the
* upper bound for the CPU search
*/
gpu += len;
}
return result;
}
static unsigned long _get_svm_area(struct kgsl_process_private *private,
struct kgsl_mem_entry *entry, unsigned long hint,
unsigned long len, unsigned long flags)
{
uint64_t start, end;
int align_shift = kgsl_memdesc_get_align(&entry->memdesc);
uint64_t align;
unsigned long result;
unsigned long addr;
if (align_shift >= ilog2(SZ_2M))
align = SZ_2M;
else if (align_shift >= ilog2(SZ_1M))
align = SZ_1M;
else if (align_shift >= ilog2(SZ_64K))
align = SZ_64K;
else
align = SZ_4K;
align = max_t(uint64_t, align, PAGE_SIZE);
/* get the GPU pagetable's SVM range */
if (kgsl_mmu_svm_range(private->pagetable, &start, &end,
entry->memdesc.flags))
return -ERANGE;
/* now clamp the range based on the CPU's requirements */
start = max_t(uint64_t, start, mmap_min_addr);
end = min_t(uint64_t, end, current->mm->mmap_base);
if (start >= end)
return -ERANGE;
if (flags & MAP_FIXED) {
/* We must honor alignment requirements */
if (!IS_ALIGNED(hint, align))
return -EINVAL;
/* we must use addr 'hint' or fail */
return _gpu_set_svm_region(private, entry, hint, len);
} else if (hint != 0) {
struct vm_area_struct *vma;
/*
* See if the hint is usable, if not we will use
* it as the start point for searching.
*/
addr = clamp_t(unsigned long, hint & ~(align - 1),
start, (end - len) & ~(align - 1));
vma = find_vma(current->mm, addr);
if (vma == NULL || ((addr + len) <= vma->vm_start)) {
result = _gpu_set_svm_region(private, entry, addr, len);
/* On failure drop down to keep searching */
if (!IS_ERR_VALUE(result))
return result;
}
} else {
/* no hint, start search at the top and work down */
addr = end & ~(align - 1);
}
/* Make sure there isn't a vma conflict in the chosen range */
if (find_vma_intersection(current->mm, addr, addr + len - 1))
return -ENOMEM;
/*
* Search downwards from the hint first. If that fails we
* must try to search above it.
* Do additoinal constraints checking on the address. Passing MAP_FIXED
* ensures that the address we want gets checked
*/
result = _search_range(private, entry, start, addr, len, align);
if (IS_ERR_VALUE(result) && hint != 0)
result = _search_range(private, entry, addr, end, len, align);
ret = current->mm->get_unmapped_area(file, addr, len, 0,
flags & MAP_FIXED);
return result;
/* If it passes, attempt to set the region in the SVM */
if (!IS_ERR_VALUE(ret))
return _gpu_set_svm_region(private, entry, addr, len);
return ret;
}
static unsigned long get_svm_unmapped_area(struct file *file,
struct kgsl_mem_entry *entry,
unsigned long addr, unsigned long len,
unsigned long flags)
{
struct kgsl_device_private *dev_priv = file->private_data;
struct kgsl_process_private *private = dev_priv->process_priv;
unsigned long align = get_align(entry);
unsigned long ret, iova;
u64 start = 0, end = 0;
if (flags & MAP_FIXED) {
/* Even fixed addresses need to obey alignment */
if (!IS_ALIGNED(addr, align))
return -EINVAL;
return set_svm_area(file, entry, addr, len, flags);
}
/* If a hint was provided, try to use that first */
if (addr) {
if (IS_ALIGNED(addr, align)) {
ret = set_svm_area(file, entry, addr, len, flags);
if (!IS_ERR_VALUE(ret))
return ret;
}
}
/* Get the SVM range for the current process */
if (kgsl_mmu_svm_range(private->pagetable, &start, &end,
entry->memdesc.flags))
return -ERANGE;
/* Find the first gap in the iova map */
iova = kgsl_mmu_find_svm_region(private->pagetable, start, end,
len, align);
while (!IS_ERR_VALUE(iova)) {
ret = set_svm_area(file, entry, iova, len, flags);
if (!IS_ERR_VALUE(ret))
return ret;
iova = kgsl_mmu_find_svm_region(private->pagetable,
start, iova - 1, len, align);
}
return -ENOMEM;
}
static unsigned long
@ -4737,28 +4662,26 @@ kgsl_get_unmapped_area(struct file *file, unsigned long addr,
/* Do not allow CPU mappings for secure buffers */
if (kgsl_memdesc_is_secured(&entry->memdesc)) {
val = -EPERM;
goto put;
kgsl_mem_entry_put(entry);
return (unsigned long) -EPERM;
}
if (!kgsl_memdesc_use_cpu_map(&entry->memdesc)) {
val = get_unmapped_area(NULL, addr, len, 0, flags);
val = current->mm->get_unmapped_area(file, addr, len, 0, flags);
if (IS_ERR_VALUE(val))
dev_err_ratelimited(device->dev,
"get_unmapped_area: pid %d addr %lx pgoff %lx len %ld failed error %d\n",
private->pid, addr, pgoff, len,
(int) val);
} else {
val = _get_svm_area(private, entry, addr, len, flags);
val = get_svm_unmapped_area(file, entry, addr, len, flags);
if (IS_ERR_VALUE(val))
dev_err_ratelimited(device->dev,
"_get_svm_area: pid %d mmap_base %lx addr %lx pgoff %lx len %ld failed error %d\n",
private->pid,
current->mm->mmap_base, addr,
pgoff, len, (int) val);
"_get_svm_area: pid %d addr %lx pgoff %lx len %ld failed error %d\n",
private->pid, addr, pgoff, len,
(int) val);
}
put:
kgsl_mem_entry_put(entry);
return val;
}
@ -5078,7 +5001,7 @@ void kgsl_device_platform_remove(struct kgsl_device *device)
kgsl_exit_page_pools();
kgsl_pwrctrl_uninit_sysfs(device);
kobject_put(device->gpu_sysfs_kobj);
idr_destroy(&device->context_idr);

View file

@ -579,6 +579,7 @@ static inline int kgsl_copy_from_user(void *dest, void __user *src,
return copy_from_user(dest, src, copy) ? -EFAULT : 0;
}
#ifndef MODULE
static inline void kgsl_gpu_sysfs_add_link(struct kobject *dst,
struct kobject *src, const char *src_name,
const char *dst_name)
@ -594,6 +595,13 @@ static inline void kgsl_gpu_sysfs_add_link(struct kobject *dst,
kernfs_create_link(dst->sd, dst_name, old);
}
#else
static inline void kgsl_gpu_sysfs_add_link(struct kobject *dst,
struct kobject *src, const char *src_name,
const char *dst_name)
{
}
#endif
static inline bool kgsl_is_compat_task(void)
{

View file

@ -1003,6 +1003,7 @@ done:
return ret;
}
#if IS_ENABLED(CONFIG_QCOM_SECURE_BUFFER)
static int _init_secure_pt(struct kgsl_mmu *mmu, struct kgsl_pagetable *pt)
{
struct kgsl_device *device = KGSL_MMU_DEVICE(mmu);
@ -1011,7 +1012,6 @@ static int _init_secure_pt(struct kgsl_mmu *mmu, struct kgsl_pagetable *pt)
struct kgsl_iommu *iommu = _IOMMU_PRIV(mmu);
struct kgsl_iommu_context *ctx = &iommu->ctx[KGSL_IOMMU_CONTEXT_SECURE];
int secure_vmid = VMID_CP_PIXEL;
unsigned int cb_num;
if (!mmu->secured)
return -EPERM;
@ -1036,23 +1036,17 @@ static int _init_secure_pt(struct kgsl_mmu *mmu, struct kgsl_pagetable *pt)
iommu_set_fault_handler(iommu_pt->domain,
kgsl_iommu_fault_handler, pt);
ret = iommu_domain_get_attr(iommu_pt->domain,
DOMAIN_ATTR_CONTEXT_BANK, &cb_num);
if (ret) {
dev_err(device->dev, "get DOMAIN_ATTR_PROCID failed: %d\n",
ret);
goto done;
}
ctx->cb_num = cb_num;
ctx->regbase = iommu->regbase + KGSL_IOMMU_CB0_OFFSET
+ (cb_num << KGSL_IOMMU_CB_SHIFT);
done:
if (ret)
_free_pt(ctx, pt);
return ret;
}
#else
static int _init_secure_pt(struct kgsl_mmu *mmu, struct kgsl_pagetable *pt)
{
return -EPERM;
}
#endif
static int _init_per_process_pt(struct kgsl_mmu *mmu, struct kgsl_pagetable *pt)
{
@ -1335,7 +1329,6 @@ static int _setup_secure_context(struct kgsl_mmu *mmu)
int ret;
struct kgsl_iommu *iommu = _IOMMU_PRIV(mmu);
struct kgsl_iommu_context *ctx = &iommu->ctx[KGSL_IOMMU_CONTEXT_SECURE];
unsigned int cb_num;
struct kgsl_iommu_pt *iommu_pt;
@ -1348,22 +1341,12 @@ static int _setup_secure_context(struct kgsl_mmu *mmu)
iommu_pt = mmu->securepagetable->priv;
ret = _attach_pt(iommu_pt, ctx);
if (ret)
goto done;
ctx->default_pt = mmu->securepagetable;
ret = iommu_domain_get_attr(iommu_pt->domain, DOMAIN_ATTR_CONTEXT_BANK,
&cb_num);
if (ret) {
dev_err(KGSL_MMU_DEVICE(mmu)->dev,
"get CONTEXT_BANK attr, err %d\n", ret);
goto done;
if (!ret) {
ctx->default_pt = mmu->securepagetable;
return 0;
}
ctx->cb_num = cb_num;
done:
if (ret)
_detach_context(ctx);
_detach_context(ctx);
return ret;
}

View file

@ -1191,45 +1191,41 @@ static const struct attribute *pwrctrl_attr_list[] = {
NULL,
};
struct sysfs_link {
const char *src;
const char *dst;
};
static DEVICE_ATTR(gpu_busy, 0200, gpu_busy_percentage_show, NULL);
static DEVICE_ATTR(gpu_min_clock, 0644, min_clock_mhz_show,
min_clock_mhz_store);
static DEVICE_ATTR(gpu_max_clock, 0644, max_clock_mhz_show,
max_clock_mhz_store);
static DEVICE_ATTR(gpu_clock, 0200, clock_mhz_show, NULL);
static DEVICE_ATTR(gpu_freq_table, 0200, freq_table_mhz_show, NULL);
static DEVICE_ATTR(gpu_tmu, 0200, temp_show, NULL);
static struct sysfs_link link_names[] = {
{ "gpu_model", "gpu_model",},
{ "gpu_busy_percentage", "gpu_busy",},
{ "min_clock_mhz", "gpu_min_clock",},
{ "max_clock_mhz", "gpu_max_clock",},
{ "clock_mhz", "gpu_clock",},
{ "freq_table_mhz", "gpu_freq_table",},
{ "temp", "gpu_tmu",},
static const struct attribute *pwrctrl_gpu_attr_list[] = {
&dev_attr_gpu_model.attr,
&dev_attr_gpu_busy.attr,
&dev_attr_gpu_min_clock.attr,
&dev_attr_gpu_max_clock.attr,
&dev_attr_gpu_clock.attr,
&dev_attr_gpu_freq_table.attr,
&dev_attr_gpu_tmu.attr,
NULL,
};
int kgsl_pwrctrl_init_sysfs(struct kgsl_device *device)
{
int i, ret;
int ret;
ret = sysfs_create_files(&device->dev->kobj, pwrctrl_attr_list);
if (ret)
return ret;
device->gpu_sysfs_kobj = kobject_create_and_add("gpu", kernel_kobj);
if (IS_ERR_OR_NULL(device->gpu_sysfs_kobj))
return (device->gpu_sysfs_kobj == NULL) ?
-ENOMEM : PTR_ERR(device->gpu_sysfs_kobj);
for (i = 0; i < ARRAY_SIZE(link_names); i++)
kgsl_gpu_sysfs_add_link(device->gpu_sysfs_kobj,
&device->dev->kobj, link_names[i].src,
link_names[i].dst);
if (!device->gpu_sysfs_kobj)
return 0;
return 0;
}
void kgsl_pwrctrl_uninit_sysfs(struct kgsl_device *device)
{
sysfs_remove_files(&device->dev->kobj, pwrctrl_attr_list);
return sysfs_create_files(device->gpu_sysfs_kobj,
pwrctrl_gpu_attr_list);
}
/*

View file

@ -165,7 +165,6 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device,
void kgsl_pwrctrl_buslevel_update(struct kgsl_device *device,
bool on);
int kgsl_pwrctrl_init_sysfs(struct kgsl_device *device);
void kgsl_pwrctrl_uninit_sysfs(struct kgsl_device *device);
int kgsl_pwrctrl_change_state(struct kgsl_device *device, int state);
int kgsl_clk_set_rate(struct kgsl_device *device,
unsigned int pwrlevel);

View file

@ -8,7 +8,6 @@
#include <linux/highmem.h>
#include <linux/slab.h>
#include <linux/random.h>
#include <soc/qcom/secure_buffer.h>
#include "kgsl_device.h"
#include "kgsl_sharedmem.h"
@ -404,6 +403,10 @@ done:
mutex_unlock(&kernel_map_global_lock);
}
#if IS_ENABLED(CONFIG_QCOM_SECURE_BUFFER)
#include <soc/qcom/secure_buffer.h>
static int lock_sgt(struct sg_table *sgt, u64 size)
{
struct scatterlist *sg;
@ -460,6 +463,7 @@ static int unlock_sgt(struct sg_table *sgt)
ClearPagePrivate(sg_page_iter_page(&sg_iter));
return 0;
}
#endif
static int kgsl_paged_map_kernel(struct kgsl_memdesc *memdesc)
{
@ -712,6 +716,7 @@ void kgsl_sharedmem_free(struct kgsl_memdesc *memdesc)
memdesc->ops->free(memdesc);
}
#if IS_ENABLED(CONFIG_QCOM_SECURE_BUFFER)
void kgsl_free_secure_page(struct page *page)
{
struct sg_table sgt;
@ -758,6 +763,16 @@ struct page *kgsl_alloc_secure_page(void)
}
return page;
}
#else
void kgsl_free_secure_page(struct page *page)
{
}
struct page *kgsl_alloc_secure_page(void)
{
return NULL;
}
#endif
int
kgsl_sharedmem_readl(const struct kgsl_memdesc *memdesc,
@ -1029,6 +1044,7 @@ static void kgsl_contiguous_free(struct kgsl_memdesc *memdesc)
_kgsl_contiguous_free(memdesc);
}
#if IS_ENABLED(CONFIG_QCOM_SECURE_BUFFER)
static void kgsl_free_secure_pool_pages(struct kgsl_memdesc *memdesc)
{
int ret = unlock_sgt(memdesc->sgt);
@ -1054,6 +1070,7 @@ static void kgsl_free_secure_pool_pages(struct kgsl_memdesc *memdesc)
memdesc->sgt = NULL;
}
#endif
static void kgsl_free_pool_pages(struct kgsl_memdesc *memdesc)
{
@ -1076,11 +1093,12 @@ static struct kgsl_memdesc_ops kgsl_contiguous_ops = {
.vmfault = kgsl_contiguous_vmfault,
};
#if IS_ENABLED(CONFIG_QCOM_SECURE_BUFFER)
static struct kgsl_memdesc_ops kgsl_secure_pool_ops = {
.free = kgsl_free_secure_pool_pages,
/* FIXME: Make sure vmflags / vmfault does the right thing here */
};
#endif
static struct kgsl_memdesc_ops kgsl_pool_ops = {
.free = kgsl_free_pool_pages,
@ -1090,6 +1108,7 @@ static struct kgsl_memdesc_ops kgsl_pool_ops = {
.unmap_kernel = kgsl_paged_unmap_kernel,
};
#if IS_ENABLED(CONFIG_QCOM_SECURE_BUFFER)
static int kgsl_alloc_secure_pages(struct kgsl_device *device,
struct kgsl_memdesc *memdesc, u64 size, u64 flags, u32 priv)
{
@ -1145,6 +1164,7 @@ static int kgsl_alloc_secure_pages(struct kgsl_device *device,
return 0;
}
#endif
static int kgsl_alloc_pages(struct kgsl_device *device,
struct kgsl_memdesc *memdesc, u64 size, u64 flags, u32 priv)
@ -1229,11 +1249,19 @@ static int kgsl_alloc_contiguous(struct kgsl_device *device,
return ret;
}
#if IS_ENABLED(CONFIG_QCOM_SECURE_BUFFER)
static int kgsl_allocate_secure(struct kgsl_device *device,
struct kgsl_memdesc *memdesc, u64 size, u64 flags, u32 priv)
{
return kgsl_alloc_secure_pages(device, memdesc, size, flags, priv);
}
#else
static int kgsl_allocate_secure(struct kgsl_device *device,
struct kgsl_memdesc *memdesc, u64 size, u64 flags, u32 priv)
{
return -ENODEV;
}
#endif
int kgsl_allocate_user(struct kgsl_device *device, struct kgsl_memdesc *memdesc,
u64 size, u64 flags, u32 priv)