blob: e15e82e439afe8997b5e2095bd112cecb2ec0c59 [file] [log] [blame]
/* 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