123 lines
3.0 KiB
C
123 lines
3.0 KiB
C
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 and
|
|
* only version 2 as published by the Free Software Foundation.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <soc/qcom/scm.h>
|
|
#include <soc/qcom/scm-mpu.h>
|
|
#include <linux/mm.h>
|
|
|
|
#include <asm/sections.h>
|
|
|
|
#define TZ_PROTECT_MEMORY 0x1
|
|
|
|
/*
|
|
* prevent memory from being zeroed out on unlock on nonsecure
|
|
* devices only.
|
|
*/
|
|
#define FLAG_DONT_CLEAR_ON_UNLOCK 0x1
|
|
|
|
/* filesystem parameters */
|
|
#define MPU_MAGIC_LOCK 0x11
|
|
#define MPU_MAGIC_UNLOCK 0x10
|
|
|
|
static ulong mpu_start;
|
|
static ulong mpu_size;
|
|
static u32 mpu_enable;
|
|
/* Some drivers write to the kernel text area by design */
|
|
static bool kernel_text_protected;
|
|
|
|
module_param(mpu_start, ulong, 0644);
|
|
module_param(mpu_size, ulong, 0644);
|
|
|
|
static int set_enabled(const char *val, const struct kernel_param *kp)
|
|
{
|
|
int ret = 0;
|
|
ret = param_set_int(val, kp);
|
|
|
|
if (mpu_enable == MPU_MAGIC_LOCK)
|
|
mem_prot_region(mpu_start, mpu_size, true);
|
|
else if (mpu_enable == MPU_MAGIC_UNLOCK)
|
|
mem_prot_region(mpu_start, mpu_size, false);
|
|
return ret;
|
|
}
|
|
|
|
static struct kernel_param_ops mpu_ops = {
|
|
.set = set_enabled,
|
|
.get = param_get_int,
|
|
};
|
|
module_param_cb(mpu_enable, &mpu_ops, &mpu_enable, 0644);
|
|
|
|
int mem_prot_region(u64 start, u64 size, bool lock)
|
|
{
|
|
int ret;
|
|
struct scm_desc desc = {0};
|
|
|
|
desc.arginfo = SCM_ARGS(5);
|
|
desc.args[0] = PAGE_ALIGN(start);
|
|
desc.args[1] = PAGE_ALIGN(size);
|
|
/*
|
|
* Permissions: Write: Read
|
|
* 0x1 TZ Anyone
|
|
* 0x2 TZ Tz, APPS
|
|
* 0x3 TZ Tz
|
|
*/
|
|
desc.args[2] = 0x1;
|
|
desc.args[3] = lock;
|
|
desc.args[4] = lock ? 0 : FLAG_DONT_CLEAR_ON_UNLOCK;
|
|
|
|
if (!is_scm_armv8())
|
|
ret = -EINVAL;
|
|
else
|
|
ret = scm_call2(SCM_SIP_FNID(SCM_SVC_MP, TZ_PROTECT_MEMORY),
|
|
&desc);
|
|
if (ret != 0)
|
|
pr_err("Failed to %s region %llx - %llx\n",
|
|
lock ? "protect" : "unlock", start, start + size);
|
|
return ret;
|
|
}
|
|
|
|
void scm_mpu_unlock_kernel_text(void)
|
|
{
|
|
phys_addr_t phys = virt_to_phys(_stext);
|
|
if (!kernel_text_protected)
|
|
return;
|
|
kernel_text_protected = false;
|
|
mem_prot_region((u64)phys, (u64)(_etext - _stext), false);
|
|
}
|
|
|
|
void scm_mpu_lock_kernel_text(void)
|
|
{
|
|
int ret;
|
|
phys_addr_t phys = virt_to_phys(_stext);
|
|
ret = mem_prot_region((u64)phys, (u64)(_etext - _stext), true);
|
|
kernel_text_protected = !ret;
|
|
}
|
|
|
|
#ifdef CONFIG_KERNEL_TEXT_MPU_PROT
|
|
static int __init mem_prot_init(void)
|
|
{
|
|
scm_mpu_lock_kernel_text();
|
|
return 0;
|
|
}
|
|
late_initcall(mem_prot_init);
|
|
#else
|
|
static int __init mem_prot_init(void)
|
|
{
|
|
if (mpu_start && mpu_size)
|
|
mem_prot_region(mpu_start, mpu_size, true);
|
|
return 0;
|
|
}
|
|
late_initcall(mem_prot_init);
|
|
#endif
|