Merge android-4.4.117 (4ec3656
) into msm-4.4
* refs/heads/tmp-4ec3656 Linux 4.4.117 media: r820t: fix r820t_write_reg for KASAN ARM: dts: s5pv210: add interrupt-parent for ohci ARM: pxa/tosa-bt: add MODULE_LICENSE tag vfs: don't do RCU lookup of empty pathnames x86: fix build warnign with 32-bit PAE dm: correctly handle chained bios in dec_pending() mvpp2: fix multicast address filter ALSA: seq: Fix racy pool initializations ALSA: usb-audio: add implicit fb quirk for Behringer UFX1204 ALSA: hda/realtek: PCI quirk for Fujitsu U7x7 ALSA: usb-audio: Fix UAC2 get_ctl request with a RANGE attribute ALSA: hda - Fix headset mic detection problem for two Dell machines Btrfs: fix unexpected -EEXIST when creating new inode Btrfs: fix crash due to not cleaning up tree log block's dirty bits Btrfs: fix deadlock in run_delalloc_nocow rtlwifi: rtl8821ae: Fix connection lost problem correctly console/dummy: leave .con_font_get set to NULL video: fbdev: atmel_lcdfb: fix display-timings lookup mm: hide a #warning for COMPILE_TEST ext4: correct documentation for grpid mount option ext4: save error to disk in __ext4_grp_locked_error() rtc-opal: Fix handling of firmware error codes, prevent busy loops drm/radeon: adjust tested variable x86/cpu: Change type of x86_cache_size variable to unsigned int KVM/x86: Reduce retpoline performance impact in slot_handle_level_range(), by always inlining iterator helper methods ALSA: seq: Fix regression by incorrect ioctl_mutex usages arm: spear13xx: Fix spics gpio controller's warning arm: spear13xx: Fix dmas cells arm: spear600: Add missing interrupt-parent of rtc ARM: dts: STi: Add gpio polarity for "hdmi,hpd-gpio" property s390: fix handling of -1 in set{,fs}[gu]id16 syscalls PM / devfreq: Propagate error from devfreq_add_device() IB/mlx4: Fix incorrectly releasing steerable UD QPs when have only ETH ports BACKPORT: tee: shm: Potential NULL dereference calling tee_shm_register() BACKPORT: tee: shm: don't put_page on null shm->pages BACKPORT: tee: shm: make function __tee_shm_alloc static BACKPORT: tee: optee: check type of registered shared memory BACKPORT: tee: add start argument to shm_register callback BACKPORT: tee: optee: fix header dependencies BACKPORT: tee: shm: inline tee_shm_get_id() BACKPORT: tee: use reference counting for tee_context BACKPORT: tee: optee: enable dynamic SHM support BACKPORT: tee: optee: add optee-specific shared pool implementation BACKPORT: tee: optee: store OP-TEE capabilities in private data BACKPORT: tee: optee: add registered buffers handling into RPC calls BACKPORT: tee: optee: add registered shared parameters handling BACKPORT: tee: optee: add shared buffer registration functions BACKPORT: tee: optee: add page list manipulation functions BACKPORT: tee: optee: Update protocol definitions BACKPORT: tee: shm: add page accessor functions BACKPORT: tee: shm: add accessors for buffer size and page offset BACKPORT: tee: add register user memory BACKPORT: tee: flexible shared memory pool creation BACKPORT: optee: support asynchronous supplicant requests BACKPORT: tee: add TEE_IOCTL_PARAM_ATTR_META BACKPORT: tee: add tee_param_is_memref() for driver use f2fs: fix build error with multiply defined inode_nohighmem() Change-Id: I6b4b025e4b03fe9433de0bf2ff292a8f24e3c3cb Signed-off-by: Srinivasarao P <spathi@codeaurora.org>
This commit is contained in:
commit
ac86a6d648
56 changed files with 1630 additions and 489 deletions
|
@ -58,6 +58,6 @@ Example:
|
|||
interrupts = <0 35 0x4>;
|
||||
status = "disabled";
|
||||
dmas = <&dmahost 12 0 1>,
|
||||
<&dmahost 13 0 1 0>;
|
||||
<&dmahost 13 1 0>;
|
||||
dma-names = "rx", "rx";
|
||||
};
|
||||
|
|
|
@ -233,7 +233,7 @@ data_err=ignore(*) Just print an error message if an error occurs
|
|||
data_err=abort Abort the journal if an error occurs in a file
|
||||
data buffer in ordered mode.
|
||||
|
||||
grpid Give objects the same group ID as their creator.
|
||||
grpid New objects have the group ID of their parent.
|
||||
bsdgroups
|
||||
|
||||
nogrpid (*) New objects have the group ID of their creator.
|
||||
|
|
2
Makefile
2
Makefile
|
@ -1,6 +1,6 @@
|
|||
VERSION = 4
|
||||
PATCHLEVEL = 4
|
||||
SUBLEVEL = 116
|
||||
SUBLEVEL = 117
|
||||
EXTRAVERSION =
|
||||
NAME = Blurry Fish Butt
|
||||
|
||||
|
|
|
@ -461,6 +461,7 @@
|
|||
compatible = "samsung,exynos4210-ohci";
|
||||
reg = <0xec300000 0x100>;
|
||||
interrupts = <23>;
|
||||
interrupt-parent = <&vic1>;
|
||||
clocks = <&clocks CLK_USB_HOST>;
|
||||
clock-names = "usbhost";
|
||||
#address-cells = <1>;
|
||||
|
|
|
@ -349,7 +349,7 @@
|
|||
spi0: spi@e0100000 {
|
||||
status = "okay";
|
||||
num-cs = <3>;
|
||||
cs-gpios = <&gpio1 7 0>, <&spics 0>, <&spics 1>;
|
||||
cs-gpios = <&gpio1 7 0>, <&spics 0 0>, <&spics 1 0>;
|
||||
|
||||
stmpe610@0 {
|
||||
compatible = "st,stmpe610";
|
||||
|
|
|
@ -141,8 +141,8 @@
|
|||
reg = <0xb4100000 0x1000>;
|
||||
interrupts = <0 105 0x4>;
|
||||
status = "disabled";
|
||||
dmas = <&dwdma0 0x600 0 0 1>, /* 0xC << 11 */
|
||||
<&dwdma0 0x680 0 1 0>; /* 0xD << 7 */
|
||||
dmas = <&dwdma0 12 0 1>,
|
||||
<&dwdma0 13 1 0>;
|
||||
dma-names = "tx", "rx";
|
||||
};
|
||||
|
||||
|
|
|
@ -100,7 +100,7 @@
|
|||
reg = <0xb2800000 0x1000>;
|
||||
interrupts = <0 29 0x4>;
|
||||
status = "disabled";
|
||||
dmas = <&dwdma0 0 0 0 0>;
|
||||
dmas = <&dwdma0 0 0 0>;
|
||||
dma-names = "data";
|
||||
};
|
||||
|
||||
|
@ -288,8 +288,8 @@
|
|||
#size-cells = <0>;
|
||||
interrupts = <0 31 0x4>;
|
||||
status = "disabled";
|
||||
dmas = <&dwdma0 0x2000 0 0 0>, /* 0x4 << 11 */
|
||||
<&dwdma0 0x0280 0 0 0>; /* 0x5 << 7 */
|
||||
dmas = <&dwdma0 4 0 0>,
|
||||
<&dwdma0 5 0 0>;
|
||||
dma-names = "tx", "rx";
|
||||
};
|
||||
|
||||
|
|
|
@ -194,6 +194,7 @@
|
|||
rtc@fc900000 {
|
||||
compatible = "st,spear600-rtc";
|
||||
reg = <0xfc900000 0x1000>;
|
||||
interrupt-parent = <&vic0>;
|
||||
interrupts = <10>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
*/
|
||||
#include "stih407-clock.dtsi"
|
||||
#include "stih407-family.dtsi"
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
/ {
|
||||
soc {
|
||||
sti-display-subsystem {
|
||||
|
@ -112,7 +113,7 @@
|
|||
<&clk_s_d2_quadfs 0>,
|
||||
<&clk_s_d2_quadfs 1>;
|
||||
|
||||
hdmi,hpd-gpio = <&pio5 3>;
|
||||
hdmi,hpd-gpio = <&pio5 3 GPIO_ACTIVE_LOW>;
|
||||
reset-names = "hdmi";
|
||||
resets = <&softreset STIH407_HDMI_TX_PHY_SOFTRESET>;
|
||||
ddc = <&hdmiddc>;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "stih410-clock.dtsi"
|
||||
#include "stih407-family.dtsi"
|
||||
#include "stih410-pinctrl.dtsi"
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
/ {
|
||||
aliases {
|
||||
bdisp0 = &bdisp0;
|
||||
|
@ -203,7 +204,7 @@
|
|||
<&clk_s_d2_quadfs 0>,
|
||||
<&clk_s_d2_quadfs 1>;
|
||||
|
||||
hdmi,hpd-gpio = <&pio5 3>;
|
||||
hdmi,hpd-gpio = <&pio5 3 GPIO_ACTIVE_LOW>;
|
||||
reset-names = "hdmi";
|
||||
resets = <&softreset STIH407_HDMI_TX_PHY_SOFTRESET>;
|
||||
ddc = <&hdmiddc>;
|
||||
|
|
|
@ -132,3 +132,7 @@ static struct platform_driver tosa_bt_driver = {
|
|||
},
|
||||
};
|
||||
module_platform_driver(tosa_bt_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Dmitry Baryshkov");
|
||||
MODULE_DESCRIPTION("Bluetooth built-in chip control");
|
||||
|
|
|
@ -110,7 +110,7 @@ COMPAT_SYSCALL_DEFINE2(s390_setregid16, u16, rgid, u16, egid)
|
|||
|
||||
COMPAT_SYSCALL_DEFINE1(s390_setgid16, u16, gid)
|
||||
{
|
||||
return sys_setgid((gid_t)gid);
|
||||
return sys_setgid(low2highgid(gid));
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE2(s390_setreuid16, u16, ruid, u16, euid)
|
||||
|
@ -120,7 +120,7 @@ COMPAT_SYSCALL_DEFINE2(s390_setreuid16, u16, ruid, u16, euid)
|
|||
|
||||
COMPAT_SYSCALL_DEFINE1(s390_setuid16, u16, uid)
|
||||
{
|
||||
return sys_setuid((uid_t)uid);
|
||||
return sys_setuid(low2highuid(uid));
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE3(s390_setresuid16, u16, ruid, u16, euid, u16, suid)
|
||||
|
@ -173,12 +173,12 @@ COMPAT_SYSCALL_DEFINE3(s390_getresgid16, u16 __user *, rgidp,
|
|||
|
||||
COMPAT_SYSCALL_DEFINE1(s390_setfsuid16, u16, uid)
|
||||
{
|
||||
return sys_setfsuid((uid_t)uid);
|
||||
return sys_setfsuid(low2highuid(uid));
|
||||
}
|
||||
|
||||
COMPAT_SYSCALL_DEFINE1(s390_setfsgid16, u16, gid)
|
||||
{
|
||||
return sys_setfsgid((gid_t)gid);
|
||||
return sys_setfsgid(low2highgid(gid));
|
||||
}
|
||||
|
||||
static int groups16_to_user(u16 __user *grouplist, struct group_info *group_info)
|
||||
|
|
|
@ -113,7 +113,7 @@ struct cpuinfo_x86 {
|
|||
char x86_vendor_id[16];
|
||||
char x86_model_id[64];
|
||||
/* in KB - valid for CPUS which support this call: */
|
||||
int x86_cache_size;
|
||||
unsigned int x86_cache_size;
|
||||
int x86_cache_alignment; /* In bytes */
|
||||
/* Cache QoS architectural values: */
|
||||
int x86_cache_max_rmid; /* max index */
|
||||
|
|
|
@ -955,7 +955,7 @@ static void identify_cpu(struct cpuinfo_x86 *c)
|
|||
int i;
|
||||
|
||||
c->loops_per_jiffy = loops_per_jiffy;
|
||||
c->x86_cache_size = -1;
|
||||
c->x86_cache_size = 0;
|
||||
c->x86_vendor = X86_VENDOR_UNKNOWN;
|
||||
c->x86_model = c->x86_mask = 0; /* So far unknown... */
|
||||
c->x86_vendor_id[0] = '\0'; /* Unset */
|
||||
|
|
|
@ -1075,7 +1075,7 @@ static struct microcode_ops microcode_intel_ops = {
|
|||
|
||||
static int __init calc_llc_size_per_core(struct cpuinfo_x86 *c)
|
||||
{
|
||||
u64 llc_size = c->x86_cache_size * 1024;
|
||||
u64 llc_size = c->x86_cache_size * 1024ULL;
|
||||
|
||||
do_div(llc_size, c->x86_max_cores);
|
||||
|
||||
|
|
|
@ -87,8 +87,8 @@ static int show_cpuinfo(struct seq_file *m, void *v)
|
|||
}
|
||||
|
||||
/* Cache size */
|
||||
if (c->x86_cache_size >= 0)
|
||||
seq_printf(m, "cache size\t: %d KB\n", c->x86_cache_size);
|
||||
if (c->x86_cache_size)
|
||||
seq_printf(m, "cache size\t: %u KB\n", c->x86_cache_size);
|
||||
|
||||
show_cpuinfo_core(m, c, cpu);
|
||||
show_cpuinfo_misc(m, c);
|
||||
|
|
|
@ -4503,7 +4503,7 @@ void kvm_mmu_setup(struct kvm_vcpu *vcpu)
|
|||
typedef bool (*slot_level_handler) (struct kvm *kvm, unsigned long *rmap);
|
||||
|
||||
/* The caller should hold mmu-lock before calling this function. */
|
||||
static bool
|
||||
static __always_inline bool
|
||||
slot_handle_level_range(struct kvm *kvm, struct kvm_memory_slot *memslot,
|
||||
slot_level_handler fn, int start_level, int end_level,
|
||||
gfn_t start_gfn, gfn_t end_gfn, bool lock_flush_tlb)
|
||||
|
@ -4533,7 +4533,7 @@ slot_handle_level_range(struct kvm *kvm, struct kvm_memory_slot *memslot,
|
|||
return flush;
|
||||
}
|
||||
|
||||
static bool
|
||||
static __always_inline bool
|
||||
slot_handle_level(struct kvm *kvm, struct kvm_memory_slot *memslot,
|
||||
slot_level_handler fn, int start_level, int end_level,
|
||||
bool lock_flush_tlb)
|
||||
|
@ -4544,7 +4544,7 @@ slot_handle_level(struct kvm *kvm, struct kvm_memory_slot *memslot,
|
|||
lock_flush_tlb);
|
||||
}
|
||||
|
||||
static bool
|
||||
static __always_inline bool
|
||||
slot_handle_all_level(struct kvm *kvm, struct kvm_memory_slot *memslot,
|
||||
slot_level_handler fn, bool lock_flush_tlb)
|
||||
{
|
||||
|
@ -4552,7 +4552,7 @@ slot_handle_all_level(struct kvm *kvm, struct kvm_memory_slot *memslot,
|
|||
PT_MAX_HUGEPAGE_LEVEL, lock_flush_tlb);
|
||||
}
|
||||
|
||||
static bool
|
||||
static __always_inline bool
|
||||
slot_handle_large_level(struct kvm *kvm, struct kvm_memory_slot *memslot,
|
||||
slot_level_handler fn, bool lock_flush_tlb)
|
||||
{
|
||||
|
@ -4560,7 +4560,7 @@ slot_handle_large_level(struct kvm *kvm, struct kvm_memory_slot *memslot,
|
|||
PT_MAX_HUGEPAGE_LEVEL, lock_flush_tlb);
|
||||
}
|
||||
|
||||
static bool
|
||||
static __always_inline bool
|
||||
slot_handle_leaf(struct kvm *kvm, struct kvm_memory_slot *memslot,
|
||||
slot_level_handler fn, bool lock_flush_tlb)
|
||||
{
|
||||
|
|
|
@ -607,7 +607,7 @@ struct devfreq *devm_devfreq_add_device(struct device *dev,
|
|||
devfreq = devfreq_add_device(dev, profile, governor_name, data);
|
||||
if (IS_ERR(devfreq)) {
|
||||
devres_free(ptr);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
return devfreq;
|
||||
}
|
||||
|
||||
*ptr = devfreq;
|
||||
|
|
|
@ -946,7 +946,7 @@ int radeon_uvd_calc_upll_dividers(struct radeon_device *rdev,
|
|||
/* calc dclk divider with current vco freq */
|
||||
dclk_div = radeon_uvd_calc_upll_post_div(vco_freq, dclk,
|
||||
pd_min, pd_even);
|
||||
if (vclk_div > pd_max)
|
||||
if (dclk_div > pd_max)
|
||||
break; /* vco is too big, it has to stop */
|
||||
|
||||
/* calc score with current vco freq */
|
||||
|
|
|
@ -2483,9 +2483,8 @@ err_steer_free_bitmap:
|
|||
kfree(ibdev->ib_uc_qpns_bitmap);
|
||||
|
||||
err_steer_qp_release:
|
||||
if (ibdev->steering_support == MLX4_STEERING_MODE_DEVICE_MANAGED)
|
||||
mlx4_qp_release_range(dev, ibdev->steer_qpn_base,
|
||||
ibdev->steer_qpn_count);
|
||||
mlx4_qp_release_range(dev, ibdev->steer_qpn_base,
|
||||
ibdev->steer_qpn_count);
|
||||
err_counter:
|
||||
for (i = 0; i < ibdev->num_ports; ++i)
|
||||
mlx4_ib_delete_counters_table(ibdev, &ibdev->counters_table[i]);
|
||||
|
@ -2586,11 +2585,9 @@ static void mlx4_ib_remove(struct mlx4_dev *dev, void *ibdev_ptr)
|
|||
ibdev->iboe.nb.notifier_call = NULL;
|
||||
}
|
||||
|
||||
if (ibdev->steering_support == MLX4_STEERING_MODE_DEVICE_MANAGED) {
|
||||
mlx4_qp_release_range(dev, ibdev->steer_qpn_base,
|
||||
ibdev->steer_qpn_count);
|
||||
kfree(ibdev->ib_uc_qpns_bitmap);
|
||||
}
|
||||
mlx4_qp_release_range(dev, ibdev->steer_qpn_base,
|
||||
ibdev->steer_qpn_count);
|
||||
kfree(ibdev->ib_uc_qpns_bitmap);
|
||||
|
||||
iounmap(ibdev->uar_map);
|
||||
for (p = 0; p < ibdev->num_ports; ++p)
|
||||
|
|
|
@ -974,7 +974,8 @@ static void dec_pending(struct dm_io *io, int error)
|
|||
} else {
|
||||
/* done with normal IO or empty flush */
|
||||
trace_block_bio_complete(md->queue, bio, io_error);
|
||||
bio->bi_error = io_error;
|
||||
if (io_error)
|
||||
bio->bi_error = io_error;
|
||||
bio_endio(bio);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -410,9 +410,11 @@ static int r820t_write(struct r820t_priv *priv, u8 reg, const u8 *val,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int r820t_write_reg(struct r820t_priv *priv, u8 reg, u8 val)
|
||||
static inline int r820t_write_reg(struct r820t_priv *priv, u8 reg, u8 val)
|
||||
{
|
||||
return r820t_write(priv, reg, &val, 1);
|
||||
u8 tmp = val; /* work around GCC PR81715 with asan-stack=1 */
|
||||
|
||||
return r820t_write(priv, reg, &tmp, 1);
|
||||
}
|
||||
|
||||
static int r820t_read_cache_reg(struct r820t_priv *priv, int reg)
|
||||
|
@ -425,17 +427,18 @@ static int r820t_read_cache_reg(struct r820t_priv *priv, int reg)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int r820t_write_reg_mask(struct r820t_priv *priv, u8 reg, u8 val,
|
||||
static inline int r820t_write_reg_mask(struct r820t_priv *priv, u8 reg, u8 val,
|
||||
u8 bit_mask)
|
||||
{
|
||||
u8 tmp = val;
|
||||
int rc = r820t_read_cache_reg(priv, reg);
|
||||
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
val = (rc & ~bit_mask) | (val & bit_mask);
|
||||
tmp = (rc & ~bit_mask) | (tmp & bit_mask);
|
||||
|
||||
return r820t_write(priv, reg, &val, 1);
|
||||
return r820t_write(priv, reg, &tmp, 1);
|
||||
}
|
||||
|
||||
static int r820t_read(struct r820t_priv *priv, u8 reg, u8 *val, int len)
|
||||
|
|
|
@ -5666,6 +5666,7 @@ static void mvpp2_set_rx_mode(struct net_device *dev)
|
|||
int id = port->id;
|
||||
bool allmulti = dev->flags & IFF_ALLMULTI;
|
||||
|
||||
retry:
|
||||
mvpp2_prs_mac_promisc_set(priv, id, dev->flags & IFF_PROMISC);
|
||||
mvpp2_prs_mac_multi_set(priv, id, MVPP2_PE_MAC_MC_ALL, allmulti);
|
||||
mvpp2_prs_mac_multi_set(priv, id, MVPP2_PE_MAC_MC_IP6, allmulti);
|
||||
|
@ -5673,9 +5674,13 @@ static void mvpp2_set_rx_mode(struct net_device *dev)
|
|||
/* Remove all port->id's mcast enries */
|
||||
mvpp2_prs_mcast_del_all(priv, id);
|
||||
|
||||
if (allmulti && !netdev_mc_empty(dev)) {
|
||||
netdev_for_each_mc_addr(ha, dev)
|
||||
mvpp2_prs_mac_da_accept(priv, id, ha->addr, true);
|
||||
if (!allmulti) {
|
||||
netdev_for_each_mc_addr(ha, dev) {
|
||||
if (mvpp2_prs_mac_da_accept(priv, id, ha->addr, true)) {
|
||||
allmulti = true;
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -280,6 +280,9 @@ void mlx4_qp_release_range(struct mlx4_dev *dev, int base_qpn, int cnt)
|
|||
u64 in_param = 0;
|
||||
int err;
|
||||
|
||||
if (!cnt)
|
||||
return;
|
||||
|
||||
if (mlx4_is_mfunc(dev)) {
|
||||
set_param_l(&in_param, base_qpn);
|
||||
set_param_h(&in_param, cnt);
|
||||
|
|
|
@ -1127,7 +1127,7 @@ static u8 _rtl8821ae_dbi_read(struct rtl_priv *rtlpriv, u16 addr)
|
|||
}
|
||||
if (0 == tmp) {
|
||||
read_addr = REG_DBI_RDATA + addr % 4;
|
||||
ret = rtl_read_word(rtlpriv, read_addr);
|
||||
ret = rtl_read_byte(rtlpriv, read_addr);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -1169,7 +1169,8 @@ static void _rtl8821ae_enable_aspm_back_door(struct ieee80211_hw *hw)
|
|||
}
|
||||
|
||||
tmp = _rtl8821ae_dbi_read(rtlpriv, 0x70f);
|
||||
_rtl8821ae_dbi_write(rtlpriv, 0x70f, tmp | BIT(7));
|
||||
_rtl8821ae_dbi_write(rtlpriv, 0x70f, tmp | BIT(7) |
|
||||
ASPM_L1_LATENCY << 3);
|
||||
|
||||
tmp = _rtl8821ae_dbi_read(rtlpriv, 0x719);
|
||||
_rtl8821ae_dbi_write(rtlpriv, 0x719, tmp | BIT(3) | BIT(4));
|
||||
|
|
|
@ -99,6 +99,7 @@
|
|||
#define RTL_USB_MAX_RX_COUNT 100
|
||||
#define QBSS_LOAD_SIZE 5
|
||||
#define MAX_WMMELE_LENGTH 64
|
||||
#define ASPM_L1_LATENCY 7
|
||||
|
||||
#define TOTAL_CAM_ENTRY 32
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ static void tm_to_opal(struct rtc_time *tm, u32 *y_m_d, u64 *h_m_s_ms)
|
|||
static int opal_get_rtc_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
long rc = OPAL_BUSY;
|
||||
int retries = 10;
|
||||
u32 y_m_d;
|
||||
u64 h_m_s_ms;
|
||||
__be32 __y_m_d;
|
||||
|
@ -67,8 +68,11 @@ static int opal_get_rtc_time(struct device *dev, struct rtc_time *tm)
|
|||
rc = opal_rtc_read(&__y_m_d, &__h_m_s_ms);
|
||||
if (rc == OPAL_BUSY_EVENT)
|
||||
opal_poll_events(NULL);
|
||||
else
|
||||
else if (retries-- && (rc == OPAL_HARDWARE
|
||||
|| rc == OPAL_INTERNAL_ERROR))
|
||||
msleep(10);
|
||||
else if (rc != OPAL_BUSY && rc != OPAL_BUSY_EVENT)
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc != OPAL_SUCCESS)
|
||||
|
@ -84,6 +88,7 @@ static int opal_get_rtc_time(struct device *dev, struct rtc_time *tm)
|
|||
static int opal_set_rtc_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
long rc = OPAL_BUSY;
|
||||
int retries = 10;
|
||||
u32 y_m_d = 0;
|
||||
u64 h_m_s_ms = 0;
|
||||
|
||||
|
@ -92,8 +97,11 @@ static int opal_set_rtc_time(struct device *dev, struct rtc_time *tm)
|
|||
rc = opal_rtc_write(y_m_d, h_m_s_ms);
|
||||
if (rc == OPAL_BUSY_EVENT)
|
||||
opal_poll_events(NULL);
|
||||
else
|
||||
else if (retries-- && (rc == OPAL_HARDWARE
|
||||
|| rc == OPAL_INTERNAL_ERROR))
|
||||
msleep(10);
|
||||
else if (rc != OPAL_BUSY && rc != OPAL_BUSY_EVENT)
|
||||
break;
|
||||
}
|
||||
|
||||
return rc == OPAL_SUCCESS ? 0 : -EIO;
|
||||
|
|
|
@ -3,3 +3,4 @@ optee-objs += core.o
|
|||
optee-objs += call.o
|
||||
optee-objs += rpc.o
|
||||
optee-objs += supp.o
|
||||
optee-objs += shm_pool.o
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/tee_drv.h>
|
||||
#include <linux/types.h>
|
||||
|
@ -135,6 +136,7 @@ u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg)
|
|||
struct optee *optee = tee_get_drvdata(ctx->teedev);
|
||||
struct optee_call_waiter w;
|
||||
struct optee_rpc_param param = { };
|
||||
struct optee_call_ctx call_ctx = { };
|
||||
u32 ret;
|
||||
|
||||
param.a0 = OPTEE_SMC_CALL_WITH_ARG;
|
||||
|
@ -159,13 +161,14 @@ u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg)
|
|||
param.a1 = res.a1;
|
||||
param.a2 = res.a2;
|
||||
param.a3 = res.a3;
|
||||
optee_handle_rpc(ctx, ¶m);
|
||||
optee_handle_rpc(ctx, ¶m, &call_ctx);
|
||||
} else {
|
||||
ret = res.a0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
optee_rpc_finalize_call(&call_ctx);
|
||||
/*
|
||||
* We're done with our thread in secure world, if there's any
|
||||
* thread waiters wake up one.
|
||||
|
@ -442,3 +445,218 @@ void optee_disable_shm_cache(struct optee *optee)
|
|||
}
|
||||
optee_cq_wait_final(&optee->call_queue, &w);
|
||||
}
|
||||
|
||||
#define PAGELIST_ENTRIES_PER_PAGE \
|
||||
((OPTEE_MSG_NONCONTIG_PAGE_SIZE / sizeof(u64)) - 1)
|
||||
|
||||
/**
|
||||
* optee_fill_pages_list() - write list of user pages to given shared
|
||||
* buffer.
|
||||
*
|
||||
* @dst: page-aligned buffer where list of pages will be stored
|
||||
* @pages: array of pages that represents shared buffer
|
||||
* @num_pages: number of entries in @pages
|
||||
* @page_offset: offset of user buffer from page start
|
||||
*
|
||||
* @dst should be big enough to hold list of user page addresses and
|
||||
* links to the next pages of buffer
|
||||
*/
|
||||
void optee_fill_pages_list(u64 *dst, struct page **pages, int num_pages,
|
||||
size_t page_offset)
|
||||
{
|
||||
int n = 0;
|
||||
phys_addr_t optee_page;
|
||||
/*
|
||||
* Refer to OPTEE_MSG_ATTR_NONCONTIG description in optee_msg.h
|
||||
* for details.
|
||||
*/
|
||||
struct {
|
||||
u64 pages_list[PAGELIST_ENTRIES_PER_PAGE];
|
||||
u64 next_page_data;
|
||||
} *pages_data;
|
||||
|
||||
/*
|
||||
* Currently OP-TEE uses 4k page size and it does not looks
|
||||
* like this will change in the future. On other hand, there are
|
||||
* no know ARM architectures with page size < 4k.
|
||||
* Thus the next built assert looks redundant. But the following
|
||||
* code heavily relies on this assumption, so it is better be
|
||||
* safe than sorry.
|
||||
*/
|
||||
BUILD_BUG_ON(PAGE_SIZE < OPTEE_MSG_NONCONTIG_PAGE_SIZE);
|
||||
|
||||
pages_data = (void *)dst;
|
||||
/*
|
||||
* If linux page is bigger than 4k, and user buffer offset is
|
||||
* larger than 4k/8k/12k/etc this will skip first 4k pages,
|
||||
* because they bear no value data for OP-TEE.
|
||||
*/
|
||||
optee_page = page_to_phys(*pages) +
|
||||
round_down(page_offset, OPTEE_MSG_NONCONTIG_PAGE_SIZE);
|
||||
|
||||
while (true) {
|
||||
pages_data->pages_list[n++] = optee_page;
|
||||
|
||||
if (n == PAGELIST_ENTRIES_PER_PAGE) {
|
||||
pages_data->next_page_data =
|
||||
virt_to_phys(pages_data + 1);
|
||||
pages_data++;
|
||||
n = 0;
|
||||
}
|
||||
|
||||
optee_page += OPTEE_MSG_NONCONTIG_PAGE_SIZE;
|
||||
if (!(optee_page & ~PAGE_MASK)) {
|
||||
if (!--num_pages)
|
||||
break;
|
||||
pages++;
|
||||
optee_page = page_to_phys(*pages);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The final entry in each pagelist page is a pointer to the next
|
||||
* pagelist page.
|
||||
*/
|
||||
static size_t get_pages_list_size(size_t num_entries)
|
||||
{
|
||||
int pages = DIV_ROUND_UP(num_entries, PAGELIST_ENTRIES_PER_PAGE);
|
||||
|
||||
return pages * OPTEE_MSG_NONCONTIG_PAGE_SIZE;
|
||||
}
|
||||
|
||||
u64 *optee_allocate_pages_list(size_t num_entries)
|
||||
{
|
||||
return alloc_pages_exact(get_pages_list_size(num_entries), GFP_KERNEL);
|
||||
}
|
||||
|
||||
void optee_free_pages_list(void *list, size_t num_entries)
|
||||
{
|
||||
free_pages_exact(list, get_pages_list_size(num_entries));
|
||||
}
|
||||
|
||||
static bool is_normal_memory(pgprot_t p)
|
||||
{
|
||||
#if defined(CONFIG_ARM)
|
||||
return (pgprot_val(p) & L_PTE_MT_MASK) == L_PTE_MT_WRITEALLOC;
|
||||
#elif defined(CONFIG_ARM64)
|
||||
return (pgprot_val(p) & PTE_ATTRINDX_MASK) == PTE_ATTRINDX(MT_NORMAL);
|
||||
#else
|
||||
#error "Unuspported architecture"
|
||||
#endif
|
||||
}
|
||||
|
||||
static int __check_mem_type(struct vm_area_struct *vma, unsigned long end)
|
||||
{
|
||||
while (vma && is_normal_memory(vma->vm_page_prot)) {
|
||||
if (vma->vm_end >= end)
|
||||
return 0;
|
||||
vma = vma->vm_next;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int check_mem_type(unsigned long start, size_t num_pages)
|
||||
{
|
||||
struct mm_struct *mm = current->mm;
|
||||
int rc;
|
||||
|
||||
down_read(&mm->mmap_sem);
|
||||
rc = __check_mem_type(find_vma(mm, start),
|
||||
start + num_pages * PAGE_SIZE);
|
||||
up_read(&mm->mmap_sem);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm,
|
||||
struct page **pages, size_t num_pages,
|
||||
unsigned long start)
|
||||
{
|
||||
struct tee_shm *shm_arg = NULL;
|
||||
struct optee_msg_arg *msg_arg;
|
||||
u64 *pages_list;
|
||||
phys_addr_t msg_parg;
|
||||
int rc;
|
||||
|
||||
if (!num_pages)
|
||||
return -EINVAL;
|
||||
|
||||
rc = check_mem_type(start, num_pages);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
pages_list = optee_allocate_pages_list(num_pages);
|
||||
if (!pages_list)
|
||||
return -ENOMEM;
|
||||
|
||||
shm_arg = get_msg_arg(ctx, 1, &msg_arg, &msg_parg);
|
||||
if (IS_ERR(shm_arg)) {
|
||||
rc = PTR_ERR(shm_arg);
|
||||
goto out;
|
||||
}
|
||||
|
||||
optee_fill_pages_list(pages_list, pages, num_pages,
|
||||
tee_shm_get_page_offset(shm));
|
||||
|
||||
msg_arg->cmd = OPTEE_MSG_CMD_REGISTER_SHM;
|
||||
msg_arg->params->attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT |
|
||||
OPTEE_MSG_ATTR_NONCONTIG;
|
||||
msg_arg->params->u.tmem.shm_ref = (unsigned long)shm;
|
||||
msg_arg->params->u.tmem.size = tee_shm_get_size(shm);
|
||||
/*
|
||||
* In the least bits of msg_arg->params->u.tmem.buf_ptr we
|
||||
* store buffer offset from 4k page, as described in OP-TEE ABI.
|
||||
*/
|
||||
msg_arg->params->u.tmem.buf_ptr = virt_to_phys(pages_list) |
|
||||
(tee_shm_get_page_offset(shm) & (OPTEE_MSG_NONCONTIG_PAGE_SIZE - 1));
|
||||
|
||||
if (optee_do_call_with_arg(ctx, msg_parg) ||
|
||||
msg_arg->ret != TEEC_SUCCESS)
|
||||
rc = -EINVAL;
|
||||
|
||||
tee_shm_free(shm_arg);
|
||||
out:
|
||||
optee_free_pages_list(pages_list, num_pages);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm)
|
||||
{
|
||||
struct tee_shm *shm_arg;
|
||||
struct optee_msg_arg *msg_arg;
|
||||
phys_addr_t msg_parg;
|
||||
int rc = 0;
|
||||
|
||||
shm_arg = get_msg_arg(ctx, 1, &msg_arg, &msg_parg);
|
||||
if (IS_ERR(shm_arg))
|
||||
return PTR_ERR(shm_arg);
|
||||
|
||||
msg_arg->cmd = OPTEE_MSG_CMD_UNREGISTER_SHM;
|
||||
|
||||
msg_arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT;
|
||||
msg_arg->params[0].u.rmem.shm_ref = (unsigned long)shm;
|
||||
|
||||
if (optee_do_call_with_arg(ctx, msg_parg) ||
|
||||
msg_arg->ret != TEEC_SUCCESS)
|
||||
rc = -EINVAL;
|
||||
tee_shm_free(shm_arg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int optee_shm_register_supp(struct tee_context *ctx, struct tee_shm *shm,
|
||||
struct page **pages, size_t num_pages,
|
||||
unsigned long start)
|
||||
{
|
||||
/*
|
||||
* We don't want to register supplicant memory in OP-TEE.
|
||||
* Instead information about it will be passed in RPC code.
|
||||
*/
|
||||
return check_mem_type(start, num_pages);
|
||||
}
|
||||
|
||||
int optee_shm_unregister_supp(struct tee_context *ctx, struct tee_shm *shm)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <linux/uaccess.h>
|
||||
#include "optee_private.h"
|
||||
#include "optee_smc.h"
|
||||
#include "shm_pool.h"
|
||||
|
||||
#define DRIVER_NAME "optee"
|
||||
|
||||
|
@ -97,6 +98,25 @@ int optee_from_msg_param(struct tee_param *params, size_t num_params,
|
|||
return rc;
|
||||
}
|
||||
break;
|
||||
case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT:
|
||||
case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT:
|
||||
case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT:
|
||||
p->attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT +
|
||||
attr - OPTEE_MSG_ATTR_TYPE_RMEM_INPUT;
|
||||
p->u.memref.size = mp->u.rmem.size;
|
||||
shm = (struct tee_shm *)(unsigned long)
|
||||
mp->u.rmem.shm_ref;
|
||||
|
||||
if (!shm) {
|
||||
p->u.memref.shm_offs = 0;
|
||||
p->u.memref.shm = NULL;
|
||||
break;
|
||||
}
|
||||
p->u.memref.shm_offs = mp->u.rmem.offs;
|
||||
p->u.memref.shm = shm;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -104,6 +124,46 @@ int optee_from_msg_param(struct tee_param *params, size_t num_params,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int to_msg_param_tmp_mem(struct optee_msg_param *mp,
|
||||
const struct tee_param *p)
|
||||
{
|
||||
int rc;
|
||||
phys_addr_t pa;
|
||||
|
||||
mp->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT + p->attr -
|
||||
TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
|
||||
|
||||
mp->u.tmem.shm_ref = (unsigned long)p->u.memref.shm;
|
||||
mp->u.tmem.size = p->u.memref.size;
|
||||
|
||||
if (!p->u.memref.shm) {
|
||||
mp->u.tmem.buf_ptr = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = tee_shm_get_pa(p->u.memref.shm, p->u.memref.shm_offs, &pa);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
mp->u.tmem.buf_ptr = pa;
|
||||
mp->attr |= OPTEE_MSG_ATTR_CACHE_PREDEFINED <<
|
||||
OPTEE_MSG_ATTR_CACHE_SHIFT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int to_msg_param_reg_mem(struct optee_msg_param *mp,
|
||||
const struct tee_param *p)
|
||||
{
|
||||
mp->attr = OPTEE_MSG_ATTR_TYPE_RMEM_INPUT + p->attr -
|
||||
TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
|
||||
|
||||
mp->u.rmem.shm_ref = (unsigned long)p->u.memref.shm;
|
||||
mp->u.rmem.size = p->u.memref.size;
|
||||
mp->u.rmem.offs = p->u.memref.shm_offs;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* optee_to_msg_param() - convert from struct tee_params to OPTEE_MSG parameters
|
||||
* @msg_params: OPTEE_MSG parameters
|
||||
|
@ -116,7 +176,6 @@ int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params,
|
|||
{
|
||||
int rc;
|
||||
size_t n;
|
||||
phys_addr_t pa;
|
||||
|
||||
for (n = 0; n < num_params; n++) {
|
||||
const struct tee_param *p = params + n;
|
||||
|
@ -139,22 +198,12 @@ int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params,
|
|||
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
|
||||
mp->attr = OPTEE_MSG_ATTR_TYPE_TMEM_INPUT +
|
||||
p->attr -
|
||||
TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
|
||||
mp->u.tmem.shm_ref = (unsigned long)p->u.memref.shm;
|
||||
mp->u.tmem.size = p->u.memref.size;
|
||||
if (!p->u.memref.shm) {
|
||||
mp->u.tmem.buf_ptr = 0;
|
||||
break;
|
||||
}
|
||||
rc = tee_shm_get_pa(p->u.memref.shm,
|
||||
p->u.memref.shm_offs, &pa);
|
||||
if (tee_shm_is_registered(p->u.memref.shm))
|
||||
rc = to_msg_param_reg_mem(mp, p);
|
||||
else
|
||||
rc = to_msg_param_tmp_mem(mp, p);
|
||||
if (rc)
|
||||
return rc;
|
||||
mp->u.tmem.buf_ptr = pa;
|
||||
mp->attr |= OPTEE_MSG_ATTR_CACHE_PREDEFINED <<
|
||||
OPTEE_MSG_ATTR_CACHE_SHIFT;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
@ -171,6 +220,10 @@ static void optee_get_version(struct tee_device *teedev,
|
|||
.impl_caps = TEE_OPTEE_CAP_TZ,
|
||||
.gen_caps = TEE_GEN_CAP_GP,
|
||||
};
|
||||
struct optee *optee = tee_get_drvdata(teedev);
|
||||
|
||||
if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
|
||||
v.gen_caps |= TEE_GEN_CAP_REG_MEM;
|
||||
*vers = v;
|
||||
}
|
||||
|
||||
|
@ -187,12 +240,12 @@ static int optee_open(struct tee_context *ctx)
|
|||
if (teedev == optee->supp_teedev) {
|
||||
bool busy = true;
|
||||
|
||||
mutex_lock(&optee->supp.ctx_mutex);
|
||||
mutex_lock(&optee->supp.mutex);
|
||||
if (!optee->supp.ctx) {
|
||||
busy = false;
|
||||
optee->supp.ctx = ctx;
|
||||
}
|
||||
mutex_unlock(&optee->supp.ctx_mutex);
|
||||
mutex_unlock(&optee->supp.mutex);
|
||||
if (busy) {
|
||||
kfree(ctxdata);
|
||||
return -EBUSY;
|
||||
|
@ -252,11 +305,8 @@ static void optee_release(struct tee_context *ctx)
|
|||
|
||||
ctx->data = NULL;
|
||||
|
||||
if (teedev == optee->supp_teedev) {
|
||||
mutex_lock(&optee->supp.ctx_mutex);
|
||||
optee->supp.ctx = NULL;
|
||||
mutex_unlock(&optee->supp.ctx_mutex);
|
||||
}
|
||||
if (teedev == optee->supp_teedev)
|
||||
optee_supp_release(&optee->supp);
|
||||
}
|
||||
|
||||
static const struct tee_driver_ops optee_ops = {
|
||||
|
@ -267,6 +317,8 @@ static const struct tee_driver_ops optee_ops = {
|
|||
.close_session = optee_close_session,
|
||||
.invoke_func = optee_invoke_func,
|
||||
.cancel_req = optee_cancel_req,
|
||||
.shm_register = optee_shm_register,
|
||||
.shm_unregister = optee_shm_unregister,
|
||||
};
|
||||
|
||||
static const struct tee_desc optee_desc = {
|
||||
|
@ -281,6 +333,8 @@ static const struct tee_driver_ops optee_supp_ops = {
|
|||
.release = optee_release,
|
||||
.supp_recv = optee_supp_recv,
|
||||
.supp_send = optee_supp_send,
|
||||
.shm_register = optee_shm_register_supp,
|
||||
.shm_unregister = optee_shm_unregister_supp,
|
||||
};
|
||||
|
||||
static const struct tee_desc optee_supp_desc = {
|
||||
|
@ -345,21 +399,22 @@ static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn,
|
|||
}
|
||||
|
||||
static struct tee_shm_pool *
|
||||
optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm)
|
||||
optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm,
|
||||
u32 sec_caps)
|
||||
{
|
||||
union {
|
||||
struct arm_smccc_res smccc;
|
||||
struct optee_smc_get_shm_config_result result;
|
||||
} res;
|
||||
struct tee_shm_pool *pool;
|
||||
unsigned long vaddr;
|
||||
phys_addr_t paddr;
|
||||
size_t size;
|
||||
phys_addr_t begin;
|
||||
phys_addr_t end;
|
||||
void *va;
|
||||
struct tee_shm_pool_mem_info priv_info;
|
||||
struct tee_shm_pool_mem_info dmabuf_info;
|
||||
struct tee_shm_pool_mgr *priv_mgr;
|
||||
struct tee_shm_pool_mgr *dmabuf_mgr;
|
||||
void *rc;
|
||||
|
||||
invoke_fn(OPTEE_SMC_GET_SHM_CONFIG, 0, 0, 0, 0, 0, 0, 0, &res.smccc);
|
||||
if (res.result.status != OPTEE_SMC_RETURN_OK) {
|
||||
|
@ -389,22 +444,49 @@ optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm)
|
|||
}
|
||||
vaddr = (unsigned long)va;
|
||||
|
||||
priv_info.vaddr = vaddr;
|
||||
priv_info.paddr = paddr;
|
||||
priv_info.size = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
|
||||
dmabuf_info.vaddr = vaddr + OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
|
||||
dmabuf_info.paddr = paddr + OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
|
||||
dmabuf_info.size = size - OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
|
||||
/*
|
||||
* If OP-TEE can work with unregistered SHM, we will use own pool
|
||||
* for private shm
|
||||
*/
|
||||
if (sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) {
|
||||
rc = optee_shm_pool_alloc_pages();
|
||||
if (IS_ERR(rc))
|
||||
goto err_memunmap;
|
||||
priv_mgr = rc;
|
||||
} else {
|
||||
const size_t sz = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
|
||||
|
||||
pool = tee_shm_pool_alloc_res_mem(&priv_info, &dmabuf_info);
|
||||
if (IS_ERR(pool)) {
|
||||
memunmap(va);
|
||||
goto out;
|
||||
rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, sz,
|
||||
3 /* 8 bytes aligned */);
|
||||
if (IS_ERR(rc))
|
||||
goto err_memunmap;
|
||||
priv_mgr = rc;
|
||||
|
||||
vaddr += sz;
|
||||
paddr += sz;
|
||||
size -= sz;
|
||||
}
|
||||
|
||||
rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, size, PAGE_SHIFT);
|
||||
if (IS_ERR(rc))
|
||||
goto err_free_priv_mgr;
|
||||
dmabuf_mgr = rc;
|
||||
|
||||
rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr);
|
||||
if (IS_ERR(rc))
|
||||
goto err_free_dmabuf_mgr;
|
||||
|
||||
*memremaped_shm = va;
|
||||
out:
|
||||
return pool;
|
||||
|
||||
return rc;
|
||||
|
||||
err_free_dmabuf_mgr:
|
||||
tee_shm_pool_mgr_destroy(dmabuf_mgr);
|
||||
err_free_priv_mgr:
|
||||
tee_shm_pool_mgr_destroy(priv_mgr);
|
||||
err_memunmap:
|
||||
memunmap(va);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Simple wrapper functions to be able to use a function pointer */
|
||||
|
@ -482,7 +564,7 @@ static struct optee *optee_probe(struct device_node *np)
|
|||
if (!(sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm);
|
||||
pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm, sec_caps);
|
||||
if (IS_ERR(pool))
|
||||
return (void *)pool;
|
||||
|
||||
|
@ -493,6 +575,7 @@ static struct optee *optee_probe(struct device_node *np)
|
|||
}
|
||||
|
||||
optee->invoke_fn = invoke_fn;
|
||||
optee->sec_caps = sec_caps;
|
||||
|
||||
teedev = tee_device_alloc(&optee_desc, NULL, pool, optee);
|
||||
if (IS_ERR(teedev)) {
|
||||
|
|
|
@ -67,11 +67,32 @@
|
|||
#define OPTEE_MSG_ATTR_META BIT(8)
|
||||
|
||||
/*
|
||||
* The temporary shared memory object is not physically contigous and this
|
||||
* temp memref is followed by another fragment until the last temp memref
|
||||
* that doesn't have this bit set.
|
||||
* Pointer to a list of pages used to register user-defined SHM buffer.
|
||||
* Used with OPTEE_MSG_ATTR_TYPE_TMEM_*.
|
||||
* buf_ptr should point to the beginning of the buffer. Buffer will contain
|
||||
* list of page addresses. OP-TEE core can reconstruct contiguous buffer from
|
||||
* that page addresses list. Page addresses are stored as 64 bit values.
|
||||
* Last entry on a page should point to the next page of buffer.
|
||||
* Every entry in buffer should point to a 4k page beginning (12 least
|
||||
* significant bits must be equal to zero).
|
||||
*
|
||||
* 12 least significant bints of optee_msg_param.u.tmem.buf_ptr should hold page
|
||||
* offset of the user buffer.
|
||||
*
|
||||
* So, entries should be placed like members of this structure:
|
||||
*
|
||||
* struct page_data {
|
||||
* uint64_t pages_array[OPTEE_MSG_NONCONTIG_PAGE_SIZE/sizeof(uint64_t) - 1];
|
||||
* uint64_t next_page_data;
|
||||
* };
|
||||
*
|
||||
* Structure is designed to exactly fit into the page size
|
||||
* OPTEE_MSG_NONCONTIG_PAGE_SIZE which is a standard 4KB page.
|
||||
*
|
||||
* The size of 4KB is chosen because this is the smallest page size for ARM
|
||||
* architectures. If REE uses larger pages, it should divide them to 4KB ones.
|
||||
*/
|
||||
#define OPTEE_MSG_ATTR_FRAGMENT BIT(9)
|
||||
#define OPTEE_MSG_ATTR_NONCONTIG BIT(9)
|
||||
|
||||
/*
|
||||
* Memory attributes for caching passed with temp memrefs. The actual value
|
||||
|
@ -94,6 +115,11 @@
|
|||
#define OPTEE_MSG_LOGIN_APPLICATION_USER 0x00000005
|
||||
#define OPTEE_MSG_LOGIN_APPLICATION_GROUP 0x00000006
|
||||
|
||||
/*
|
||||
* Page size used in non-contiguous buffer entries
|
||||
*/
|
||||
#define OPTEE_MSG_NONCONTIG_PAGE_SIZE 4096
|
||||
|
||||
/**
|
||||
* struct optee_msg_param_tmem - temporary memory reference parameter
|
||||
* @buf_ptr: Address of the buffer
|
||||
|
@ -145,8 +171,8 @@ struct optee_msg_param_value {
|
|||
*
|
||||
* @attr & OPTEE_MSG_ATTR_TYPE_MASK indicates if tmem, rmem or value is used in
|
||||
* the union. OPTEE_MSG_ATTR_TYPE_VALUE_* indicates value,
|
||||
* OPTEE_MSG_ATTR_TYPE_TMEM_* indicates tmem and
|
||||
* OPTEE_MSG_ATTR_TYPE_RMEM_* indicates rmem.
|
||||
* OPTEE_MSG_ATTR_TYPE_TMEM_* indicates @tmem and
|
||||
* OPTEE_MSG_ATTR_TYPE_RMEM_* indicates @rmem,
|
||||
* OPTEE_MSG_ATTR_TYPE_NONE indicates that none of the members are used.
|
||||
*/
|
||||
struct optee_msg_param {
|
||||
|
|
|
@ -53,36 +53,24 @@ struct optee_wait_queue {
|
|||
* @ctx the context of current connected supplicant.
|
||||
* if !NULL the supplicant device is available for use,
|
||||
* else busy
|
||||
* @ctx_mutex: held while accessing @ctx
|
||||
* @func: supplicant function id to call
|
||||
* @ret: call return value
|
||||
* @num_params: number of elements in @param
|
||||
* @param: parameters for @func
|
||||
* @req_posted: if true, a request has been posted to the supplicant
|
||||
* @supp_next_send: if true, next step is for supplicant to send response
|
||||
* @thrd_mutex: held by the thread doing a request to supplicant
|
||||
* @supp_mutex: held by supplicant while operating on this struct
|
||||
* @data_to_supp: supplicant is waiting on this for next request
|
||||
* @data_from_supp: requesting thread is waiting on this to get the result
|
||||
* @mutex: held while accessing content of this struct
|
||||
* @req_id: current request id if supplicant is doing synchronous
|
||||
* communication, else -1
|
||||
* @reqs: queued request not yet retrieved by supplicant
|
||||
* @idr: IDR holding all requests currently being processed
|
||||
* by supplicant
|
||||
* @reqs_c: completion used by supplicant when waiting for a
|
||||
* request to be queued.
|
||||
*/
|
||||
struct optee_supp {
|
||||
/* Serializes access to this struct */
|
||||
struct mutex mutex;
|
||||
struct tee_context *ctx;
|
||||
/* Serializes access of ctx */
|
||||
struct mutex ctx_mutex;
|
||||
|
||||
u32 func;
|
||||
u32 ret;
|
||||
size_t num_params;
|
||||
struct tee_param *param;
|
||||
|
||||
bool req_posted;
|
||||
bool supp_next_send;
|
||||
/* Serializes access to this struct for requesting thread */
|
||||
struct mutex thrd_mutex;
|
||||
/* Serializes access to this struct for supplicant threads */
|
||||
struct mutex supp_mutex;
|
||||
struct completion data_to_supp;
|
||||
struct completion data_from_supp;
|
||||
int req_id;
|
||||
struct list_head reqs;
|
||||
struct idr idr;
|
||||
struct completion reqs_c;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -96,6 +84,8 @@ struct optee_supp {
|
|||
* @supp: supplicant synchronization struct for RPC to supplicant
|
||||
* @pool: shared memory pool
|
||||
* @memremaped_shm virtual address of memory in shared memory pool
|
||||
* @sec_caps: secure world capabilities defined by
|
||||
* OPTEE_SMC_SEC_CAP_* in optee_smc.h
|
||||
*/
|
||||
struct optee {
|
||||
struct tee_device *supp_teedev;
|
||||
|
@ -106,6 +96,7 @@ struct optee {
|
|||
struct optee_supp supp;
|
||||
struct tee_shm_pool *pool;
|
||||
void *memremaped_shm;
|
||||
u32 sec_caps;
|
||||
};
|
||||
|
||||
struct optee_session {
|
||||
|
@ -130,7 +121,16 @@ struct optee_rpc_param {
|
|||
u32 a7;
|
||||
};
|
||||
|
||||
void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param);
|
||||
/* Holds context that is preserved during one STD call */
|
||||
struct optee_call_ctx {
|
||||
/* information about pages list used in last allocation */
|
||||
void *pages_list;
|
||||
size_t num_entries;
|
||||
};
|
||||
|
||||
void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param,
|
||||
struct optee_call_ctx *call_ctx);
|
||||
void optee_rpc_finalize_call(struct optee_call_ctx *call_ctx);
|
||||
|
||||
void optee_wait_queue_init(struct optee_wait_queue *wq);
|
||||
void optee_wait_queue_exit(struct optee_wait_queue *wq);
|
||||
|
@ -142,6 +142,7 @@ int optee_supp_read(struct tee_context *ctx, void __user *buf, size_t len);
|
|||
int optee_supp_write(struct tee_context *ctx, void __user *buf, size_t len);
|
||||
void optee_supp_init(struct optee_supp *supp);
|
||||
void optee_supp_uninit(struct optee_supp *supp);
|
||||
void optee_supp_release(struct optee_supp *supp);
|
||||
|
||||
int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params,
|
||||
struct tee_param *param);
|
||||
|
@ -160,11 +161,26 @@ int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session);
|
|||
void optee_enable_shm_cache(struct optee *optee);
|
||||
void optee_disable_shm_cache(struct optee *optee);
|
||||
|
||||
int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm,
|
||||
struct page **pages, size_t num_pages,
|
||||
unsigned long start);
|
||||
int optee_shm_unregister(struct tee_context *ctx, struct tee_shm *shm);
|
||||
|
||||
int optee_shm_register_supp(struct tee_context *ctx, struct tee_shm *shm,
|
||||
struct page **pages, size_t num_pages,
|
||||
unsigned long start);
|
||||
int optee_shm_unregister_supp(struct tee_context *ctx, struct tee_shm *shm);
|
||||
|
||||
int optee_from_msg_param(struct tee_param *params, size_t num_params,
|
||||
const struct optee_msg_param *msg_params);
|
||||
int optee_to_msg_param(struct optee_msg_param *msg_params, size_t num_params,
|
||||
const struct tee_param *params);
|
||||
|
||||
u64 *optee_allocate_pages_list(size_t num_entries);
|
||||
void optee_free_pages_list(void *array, size_t num_entries);
|
||||
void optee_fill_pages_list(u64 *dst, struct page **pages, int num_pages,
|
||||
size_t page_offset);
|
||||
|
||||
/*
|
||||
* Small helpers
|
||||
*/
|
||||
|
|
|
@ -222,6 +222,13 @@ struct optee_smc_get_shm_config_result {
|
|||
#define OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM BIT(0)
|
||||
/* Secure world can communicate via previously unregistered shared memory */
|
||||
#define OPTEE_SMC_SEC_CAP_UNREGISTERED_SHM BIT(1)
|
||||
|
||||
/*
|
||||
* Secure world supports commands "register/unregister shared memory",
|
||||
* secure world accepts command buffers located in any parts of non-secure RAM
|
||||
*/
|
||||
#define OPTEE_SMC_SEC_CAP_DYNAMIC_SHM BIT(2)
|
||||
|
||||
#define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES 9
|
||||
#define OPTEE_SMC_EXCHANGE_CAPABILITIES \
|
||||
OPTEE_SMC_FAST_CALL_VAL(OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES)
|
||||
|
|
|
@ -192,15 +192,16 @@ static struct tee_shm *cmd_alloc_suppl(struct tee_context *ctx, size_t sz)
|
|||
if (ret)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mutex_lock(&optee->supp.ctx_mutex);
|
||||
mutex_lock(&optee->supp.mutex);
|
||||
/* Increases count as secure world doesn't have a reference */
|
||||
shm = tee_shm_get_from_id(optee->supp.ctx, param.u.value.c);
|
||||
mutex_unlock(&optee->supp.ctx_mutex);
|
||||
mutex_unlock(&optee->supp.mutex);
|
||||
return shm;
|
||||
}
|
||||
|
||||
static void handle_rpc_func_cmd_shm_alloc(struct tee_context *ctx,
|
||||
struct optee_msg_arg *arg)
|
||||
struct optee_msg_arg *arg,
|
||||
struct optee_call_ctx *call_ctx)
|
||||
{
|
||||
phys_addr_t pa;
|
||||
struct tee_shm *shm;
|
||||
|
@ -245,10 +246,49 @@ static void handle_rpc_func_cmd_shm_alloc(struct tee_context *ctx,
|
|||
goto bad;
|
||||
}
|
||||
|
||||
arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT;
|
||||
arg->params[0].u.tmem.buf_ptr = pa;
|
||||
arg->params[0].u.tmem.size = sz;
|
||||
arg->params[0].u.tmem.shm_ref = (unsigned long)shm;
|
||||
sz = tee_shm_get_size(shm);
|
||||
|
||||
if (tee_shm_is_registered(shm)) {
|
||||
struct page **pages;
|
||||
u64 *pages_list;
|
||||
size_t page_num;
|
||||
|
||||
pages = tee_shm_get_pages(shm, &page_num);
|
||||
if (!pages || !page_num) {
|
||||
arg->ret = TEEC_ERROR_OUT_OF_MEMORY;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
pages_list = optee_allocate_pages_list(page_num);
|
||||
if (!pages_list) {
|
||||
arg->ret = TEEC_ERROR_OUT_OF_MEMORY;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
call_ctx->pages_list = pages_list;
|
||||
call_ctx->num_entries = page_num;
|
||||
|
||||
arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT |
|
||||
OPTEE_MSG_ATTR_NONCONTIG;
|
||||
/*
|
||||
* In the least bits of u.tmem.buf_ptr we store buffer offset
|
||||
* from 4k page, as described in OP-TEE ABI.
|
||||
*/
|
||||
arg->params[0].u.tmem.buf_ptr = virt_to_phys(pages_list) |
|
||||
(tee_shm_get_page_offset(shm) &
|
||||
(OPTEE_MSG_NONCONTIG_PAGE_SIZE - 1));
|
||||
arg->params[0].u.tmem.size = tee_shm_get_size(shm);
|
||||
arg->params[0].u.tmem.shm_ref = (unsigned long)shm;
|
||||
|
||||
optee_fill_pages_list(pages_list, pages, page_num,
|
||||
tee_shm_get_page_offset(shm));
|
||||
} else {
|
||||
arg->params[0].attr = OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT;
|
||||
arg->params[0].u.tmem.buf_ptr = pa;
|
||||
arg->params[0].u.tmem.size = sz;
|
||||
arg->params[0].u.tmem.shm_ref = (unsigned long)shm;
|
||||
}
|
||||
|
||||
arg->ret = TEEC_SUCCESS;
|
||||
return;
|
||||
bad:
|
||||
|
@ -307,8 +347,24 @@ static void handle_rpc_func_cmd_shm_free(struct tee_context *ctx,
|
|||
arg->ret = TEEC_SUCCESS;
|
||||
}
|
||||
|
||||
static void free_pages_list(struct optee_call_ctx *call_ctx)
|
||||
{
|
||||
if (call_ctx->pages_list) {
|
||||
optee_free_pages_list(call_ctx->pages_list,
|
||||
call_ctx->num_entries);
|
||||
call_ctx->pages_list = NULL;
|
||||
call_ctx->num_entries = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void optee_rpc_finalize_call(struct optee_call_ctx *call_ctx)
|
||||
{
|
||||
free_pages_list(call_ctx);
|
||||
}
|
||||
|
||||
static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee,
|
||||
struct tee_shm *shm)
|
||||
struct tee_shm *shm,
|
||||
struct optee_call_ctx *call_ctx)
|
||||
{
|
||||
struct optee_msg_arg *arg;
|
||||
|
||||
|
@ -329,7 +385,8 @@ static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee,
|
|||
handle_rpc_func_cmd_wait(arg);
|
||||
break;
|
||||
case OPTEE_MSG_RPC_CMD_SHM_ALLOC:
|
||||
handle_rpc_func_cmd_shm_alloc(ctx, arg);
|
||||
free_pages_list(call_ctx);
|
||||
handle_rpc_func_cmd_shm_alloc(ctx, arg, call_ctx);
|
||||
break;
|
||||
case OPTEE_MSG_RPC_CMD_SHM_FREE:
|
||||
handle_rpc_func_cmd_shm_free(ctx, arg);
|
||||
|
@ -343,10 +400,12 @@ static void handle_rpc_func_cmd(struct tee_context *ctx, struct optee *optee,
|
|||
* optee_handle_rpc() - handle RPC from secure world
|
||||
* @ctx: context doing the RPC
|
||||
* @param: value of registers for the RPC
|
||||
* @call_ctx: call context. Preserved during one OP-TEE invocation
|
||||
*
|
||||
* Result of RPC is written back into @param.
|
||||
*/
|
||||
void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param)
|
||||
void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param,
|
||||
struct optee_call_ctx *call_ctx)
|
||||
{
|
||||
struct tee_device *teedev = ctx->teedev;
|
||||
struct optee *optee = tee_get_drvdata(teedev);
|
||||
|
@ -381,7 +440,7 @@ void optee_handle_rpc(struct tee_context *ctx, struct optee_rpc_param *param)
|
|||
break;
|
||||
case OPTEE_SMC_RPC_FUNC_CMD:
|
||||
shm = reg_pair_to_ptr(param->a1, param->a2);
|
||||
handle_rpc_func_cmd(ctx, optee, shm);
|
||||
handle_rpc_func_cmd(ctx, optee, shm, call_ctx);
|
||||
break;
|
||||
default:
|
||||
pr_warn("Unknown RPC func 0x%x\n",
|
||||
|
|
75
drivers/tee/optee/shm_pool.c
Normal file
75
drivers/tee/optee/shm_pool.c
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (c) 2015, Linaro Limited
|
||||
* Copyright (c) 2017, EPAM Systems
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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/device.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/genalloc.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/tee_drv.h>
|
||||
#include "optee_private.h"
|
||||
#include "optee_smc.h"
|
||||
#include "shm_pool.h"
|
||||
|
||||
static int pool_op_alloc(struct tee_shm_pool_mgr *poolm,
|
||||
struct tee_shm *shm, size_t size)
|
||||
{
|
||||
unsigned int order = get_order(size);
|
||||
struct page *page;
|
||||
|
||||
page = alloc_pages(GFP_KERNEL | __GFP_ZERO, order);
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
|
||||
shm->kaddr = page_address(page);
|
||||
shm->paddr = page_to_phys(page);
|
||||
shm->size = PAGE_SIZE << order;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pool_op_free(struct tee_shm_pool_mgr *poolm,
|
||||
struct tee_shm *shm)
|
||||
{
|
||||
free_pages((unsigned long)shm->kaddr, get_order(shm->size));
|
||||
shm->kaddr = NULL;
|
||||
}
|
||||
|
||||
static void pool_op_destroy_poolmgr(struct tee_shm_pool_mgr *poolm)
|
||||
{
|
||||
kfree(poolm);
|
||||
}
|
||||
|
||||
static const struct tee_shm_pool_mgr_ops pool_ops = {
|
||||
.alloc = pool_op_alloc,
|
||||
.free = pool_op_free,
|
||||
.destroy_poolmgr = pool_op_destroy_poolmgr,
|
||||
};
|
||||
|
||||
/**
|
||||
* optee_shm_pool_alloc_pages() - create page-based allocator pool
|
||||
*
|
||||
* This pool is used when OP-TEE supports dymanic SHM. In this case
|
||||
* command buffers and such are allocated from kernel's own memory.
|
||||
*/
|
||||
struct tee_shm_pool_mgr *optee_shm_pool_alloc_pages(void)
|
||||
{
|
||||
struct tee_shm_pool_mgr *mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
|
||||
|
||||
if (!mgr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mgr->ops = &pool_ops;
|
||||
|
||||
return mgr;
|
||||
}
|
23
drivers/tee/optee/shm_pool.h
Normal file
23
drivers/tee/optee/shm_pool.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (c) 2015, Linaro Limited
|
||||
* Copyright (c) 2016, EPAM Systems
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHM_POOL_H
|
||||
#define SHM_POOL_H
|
||||
|
||||
#include <linux/tee_drv.h>
|
||||
|
||||
struct tee_shm_pool_mgr *optee_shm_pool_alloc_pages(void);
|
||||
|
||||
#endif
|
|
@ -16,21 +16,61 @@
|
|||
#include <linux/uaccess.h>
|
||||
#include "optee_private.h"
|
||||
|
||||
struct optee_supp_req {
|
||||
struct list_head link;
|
||||
|
||||
bool busy;
|
||||
u32 func;
|
||||
u32 ret;
|
||||
size_t num_params;
|
||||
struct tee_param *param;
|
||||
|
||||
struct completion c;
|
||||
};
|
||||
|
||||
void optee_supp_init(struct optee_supp *supp)
|
||||
{
|
||||
memset(supp, 0, sizeof(*supp));
|
||||
mutex_init(&supp->ctx_mutex);
|
||||
mutex_init(&supp->thrd_mutex);
|
||||
mutex_init(&supp->supp_mutex);
|
||||
init_completion(&supp->data_to_supp);
|
||||
init_completion(&supp->data_from_supp);
|
||||
mutex_init(&supp->mutex);
|
||||
init_completion(&supp->reqs_c);
|
||||
idr_init(&supp->idr);
|
||||
INIT_LIST_HEAD(&supp->reqs);
|
||||
supp->req_id = -1;
|
||||
}
|
||||
|
||||
void optee_supp_uninit(struct optee_supp *supp)
|
||||
{
|
||||
mutex_destroy(&supp->ctx_mutex);
|
||||
mutex_destroy(&supp->thrd_mutex);
|
||||
mutex_destroy(&supp->supp_mutex);
|
||||
mutex_destroy(&supp->mutex);
|
||||
idr_destroy(&supp->idr);
|
||||
}
|
||||
|
||||
void optee_supp_release(struct optee_supp *supp)
|
||||
{
|
||||
int id;
|
||||
struct optee_supp_req *req;
|
||||
struct optee_supp_req *req_tmp;
|
||||
|
||||
mutex_lock(&supp->mutex);
|
||||
|
||||
/* Abort all request retrieved by supplicant */
|
||||
idr_for_each_entry(&supp->idr, req, id) {
|
||||
req->busy = false;
|
||||
idr_remove(&supp->idr, id);
|
||||
req->ret = TEEC_ERROR_COMMUNICATION;
|
||||
complete(&req->c);
|
||||
}
|
||||
|
||||
/* Abort all queued requests */
|
||||
list_for_each_entry_safe(req, req_tmp, &supp->reqs, link) {
|
||||
list_del(&req->link);
|
||||
req->ret = TEEC_ERROR_COMMUNICATION;
|
||||
complete(&req->c);
|
||||
}
|
||||
|
||||
supp->ctx = NULL;
|
||||
supp->req_id = -1;
|
||||
|
||||
mutex_unlock(&supp->mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -44,53 +84,42 @@ void optee_supp_uninit(struct optee_supp *supp)
|
|||
*/
|
||||
u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params,
|
||||
struct tee_param *param)
|
||||
|
||||
{
|
||||
bool interruptable;
|
||||
struct optee *optee = tee_get_drvdata(ctx->teedev);
|
||||
struct optee_supp *supp = &optee->supp;
|
||||
struct optee_supp_req *req = kzalloc(sizeof(*req), GFP_KERNEL);
|
||||
bool interruptable;
|
||||
u32 ret;
|
||||
|
||||
/*
|
||||
* Other threads blocks here until we've copied our answer from
|
||||
* supplicant.
|
||||
*/
|
||||
while (mutex_lock_interruptible(&supp->thrd_mutex)) {
|
||||
/* See comment below on when the RPC can be interrupted. */
|
||||
mutex_lock(&supp->ctx_mutex);
|
||||
interruptable = !supp->ctx;
|
||||
mutex_unlock(&supp->ctx_mutex);
|
||||
if (interruptable)
|
||||
return TEEC_ERROR_COMMUNICATION;
|
||||
}
|
||||
if (!req)
|
||||
return TEEC_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
/*
|
||||
* We have exclusive access now since the supplicant at this
|
||||
* point is either doing a
|
||||
* wait_for_completion_interruptible(&supp->data_to_supp) or is in
|
||||
* userspace still about to do the ioctl() to enter
|
||||
* optee_supp_recv() below.
|
||||
*/
|
||||
init_completion(&req->c);
|
||||
req->func = func;
|
||||
req->num_params = num_params;
|
||||
req->param = param;
|
||||
|
||||
supp->func = func;
|
||||
supp->num_params = num_params;
|
||||
supp->param = param;
|
||||
supp->req_posted = true;
|
||||
/* Insert the request in the request list */
|
||||
mutex_lock(&supp->mutex);
|
||||
list_add_tail(&req->link, &supp->reqs);
|
||||
mutex_unlock(&supp->mutex);
|
||||
|
||||
/* Let supplicant get the data */
|
||||
complete(&supp->data_to_supp);
|
||||
/* Tell an eventual waiter there's a new request */
|
||||
complete(&supp->reqs_c);
|
||||
|
||||
/*
|
||||
* Wait for supplicant to process and return result, once we've
|
||||
* returned from wait_for_completion(data_from_supp) we have
|
||||
* returned from wait_for_completion(&req->c) successfully we have
|
||||
* exclusive access again.
|
||||
*/
|
||||
while (wait_for_completion_interruptible(&supp->data_from_supp)) {
|
||||
mutex_lock(&supp->ctx_mutex);
|
||||
while (wait_for_completion_interruptible(&req->c)) {
|
||||
mutex_lock(&supp->mutex);
|
||||
interruptable = !supp->ctx;
|
||||
if (interruptable) {
|
||||
/*
|
||||
* There's no supplicant available and since the
|
||||
* supp->ctx_mutex currently is held none can
|
||||
* supp->mutex currently is held none can
|
||||
* become available until the mutex released
|
||||
* again.
|
||||
*
|
||||
|
@ -101,24 +130,91 @@ u32 optee_supp_thrd_req(struct tee_context *ctx, u32 func, size_t num_params,
|
|||
* will serve all requests in a timely manner and
|
||||
* interrupting then wouldn't make sense.
|
||||
*/
|
||||
supp->ret = TEEC_ERROR_COMMUNICATION;
|
||||
init_completion(&supp->data_to_supp);
|
||||
interruptable = !req->busy;
|
||||
if (!req->busy)
|
||||
list_del(&req->link);
|
||||
}
|
||||
mutex_unlock(&supp->ctx_mutex);
|
||||
if (interruptable)
|
||||
mutex_unlock(&supp->mutex);
|
||||
|
||||
if (interruptable) {
|
||||
req->ret = TEEC_ERROR_COMMUNICATION;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = supp->ret;
|
||||
supp->param = NULL;
|
||||
supp->req_posted = false;
|
||||
|
||||
/* We're done, let someone else talk to the supplicant now. */
|
||||
mutex_unlock(&supp->thrd_mutex);
|
||||
ret = req->ret;
|
||||
kfree(req);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct optee_supp_req *supp_pop_entry(struct optee_supp *supp,
|
||||
int num_params, int *id)
|
||||
{
|
||||
struct optee_supp_req *req;
|
||||
|
||||
if (supp->req_id != -1) {
|
||||
/*
|
||||
* Supplicant should not mix synchronous and asnynchronous
|
||||
* requests.
|
||||
*/
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (list_empty(&supp->reqs))
|
||||
return NULL;
|
||||
|
||||
req = list_first_entry(&supp->reqs, struct optee_supp_req, link);
|
||||
|
||||
if (num_params < req->num_params) {
|
||||
/* Not enough room for parameters */
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
*id = idr_alloc(&supp->idr, req, 1, 0, GFP_KERNEL);
|
||||
if (*id < 0)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
list_del(&req->link);
|
||||
req->busy = true;
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
static int supp_check_recv_params(size_t num_params, struct tee_param *params,
|
||||
size_t *num_meta)
|
||||
{
|
||||
size_t n;
|
||||
|
||||
if (!num_params)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* If there's memrefs we need to decrease those as they where
|
||||
* increased earlier and we'll even refuse to accept any below.
|
||||
*/
|
||||
for (n = 0; n < num_params; n++)
|
||||
if (tee_param_is_memref(params + n) && params[n].u.memref.shm)
|
||||
tee_shm_put(params[n].u.memref.shm);
|
||||
|
||||
/*
|
||||
* We only expect parameters as TEE_IOCTL_PARAM_ATTR_TYPE_NONE with
|
||||
* or without the TEE_IOCTL_PARAM_ATTR_META bit set.
|
||||
*/
|
||||
for (n = 0; n < num_params; n++)
|
||||
if (params[n].attr &&
|
||||
params[n].attr != TEE_IOCTL_PARAM_ATTR_META)
|
||||
return -EINVAL;
|
||||
|
||||
/* At most we'll need one meta parameter so no need to check for more */
|
||||
if (params->attr == TEE_IOCTL_PARAM_ATTR_META)
|
||||
*num_meta = 1;
|
||||
else
|
||||
*num_meta = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* optee_supp_recv() - receive request for supplicant
|
||||
* @ctx: context receiving the request
|
||||
|
@ -135,65 +231,99 @@ int optee_supp_recv(struct tee_context *ctx, u32 *func, u32 *num_params,
|
|||
struct tee_device *teedev = ctx->teedev;
|
||||
struct optee *optee = tee_get_drvdata(teedev);
|
||||
struct optee_supp *supp = &optee->supp;
|
||||
struct optee_supp_req *req = NULL;
|
||||
int id;
|
||||
size_t num_meta;
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* In case two threads in one supplicant is calling this function
|
||||
* simultaneously we need to protect the data with a mutex which
|
||||
* we'll release before returning.
|
||||
*/
|
||||
mutex_lock(&supp->supp_mutex);
|
||||
rc = supp_check_recv_params(*num_params, param, &num_meta);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (supp->supp_next_send) {
|
||||
/*
|
||||
* optee_supp_recv() has been called again without
|
||||
* a optee_supp_send() in between. Supplicant has
|
||||
* probably been restarted before it was able to
|
||||
* write back last result. Abort last request and
|
||||
* wait for a new.
|
||||
*/
|
||||
if (supp->req_posted) {
|
||||
supp->ret = TEEC_ERROR_COMMUNICATION;
|
||||
supp->supp_next_send = false;
|
||||
complete(&supp->data_from_supp);
|
||||
while (true) {
|
||||
mutex_lock(&supp->mutex);
|
||||
req = supp_pop_entry(supp, *num_params - num_meta, &id);
|
||||
mutex_unlock(&supp->mutex);
|
||||
|
||||
if (req) {
|
||||
if (IS_ERR(req))
|
||||
return PTR_ERR(req);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This is where supplicant will be hanging most of the
|
||||
* time, let's make this interruptable so we can easily
|
||||
* restart supplicant if needed.
|
||||
*/
|
||||
if (wait_for_completion_interruptible(&supp->data_to_supp)) {
|
||||
rc = -ERESTARTSYS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* We have exlusive access to the data */
|
||||
|
||||
if (*num_params < supp->num_params) {
|
||||
/*
|
||||
* Not enough room for parameters, tell supplicant
|
||||
* it failed and abort last request.
|
||||
* If we didn't get a request we'll block in
|
||||
* wait_for_completion() to avoid needless spinning.
|
||||
*
|
||||
* This is where supplicant will be hanging most of
|
||||
* the time, let's make this interruptable so we
|
||||
* can easily restart supplicant if needed.
|
||||
*/
|
||||
supp->ret = TEEC_ERROR_COMMUNICATION;
|
||||
rc = -EINVAL;
|
||||
complete(&supp->data_from_supp);
|
||||
goto out;
|
||||
if (wait_for_completion_interruptible(&supp->reqs_c))
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
|
||||
*func = supp->func;
|
||||
*num_params = supp->num_params;
|
||||
memcpy(param, supp->param,
|
||||
sizeof(struct tee_param) * supp->num_params);
|
||||
if (num_meta) {
|
||||
/*
|
||||
* tee-supplicant support meta parameters -> requsts can be
|
||||
* processed asynchronously.
|
||||
*/
|
||||
param->attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT |
|
||||
TEE_IOCTL_PARAM_ATTR_META;
|
||||
param->u.value.a = id;
|
||||
param->u.value.b = 0;
|
||||
param->u.value.c = 0;
|
||||
} else {
|
||||
mutex_lock(&supp->mutex);
|
||||
supp->req_id = id;
|
||||
mutex_unlock(&supp->mutex);
|
||||
}
|
||||
|
||||
/* Allow optee_supp_send() below to do its work */
|
||||
supp->supp_next_send = true;
|
||||
*func = req->func;
|
||||
*num_params = req->num_params + num_meta;
|
||||
memcpy(param + num_meta, req->param,
|
||||
sizeof(struct tee_param) * req->num_params);
|
||||
|
||||
rc = 0;
|
||||
out:
|
||||
mutex_unlock(&supp->supp_mutex);
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct optee_supp_req *supp_pop_req(struct optee_supp *supp,
|
||||
size_t num_params,
|
||||
struct tee_param *param,
|
||||
size_t *num_meta)
|
||||
{
|
||||
struct optee_supp_req *req;
|
||||
int id;
|
||||
size_t nm;
|
||||
const u32 attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT |
|
||||
TEE_IOCTL_PARAM_ATTR_META;
|
||||
|
||||
if (!num_params)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (supp->req_id == -1) {
|
||||
if (param->attr != attr)
|
||||
return ERR_PTR(-EINVAL);
|
||||
id = param->u.value.a;
|
||||
nm = 1;
|
||||
} else {
|
||||
id = supp->req_id;
|
||||
nm = 0;
|
||||
}
|
||||
|
||||
req = idr_find(&supp->idr, id);
|
||||
if (!req)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
if ((num_params - nm) != req->num_params)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
req->busy = false;
|
||||
idr_remove(&supp->idr, id);
|
||||
supp->req_id = -1;
|
||||
*num_meta = nm;
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -211,63 +341,42 @@ int optee_supp_send(struct tee_context *ctx, u32 ret, u32 num_params,
|
|||
struct tee_device *teedev = ctx->teedev;
|
||||
struct optee *optee = tee_get_drvdata(teedev);
|
||||
struct optee_supp *supp = &optee->supp;
|
||||
struct optee_supp_req *req;
|
||||
size_t n;
|
||||
int rc = 0;
|
||||
size_t num_meta;
|
||||
|
||||
/*
|
||||
* We still have exclusive access to the data since that's how we
|
||||
* left it when returning from optee_supp_read().
|
||||
*/
|
||||
mutex_lock(&supp->mutex);
|
||||
req = supp_pop_req(supp, num_params, param, &num_meta);
|
||||
mutex_unlock(&supp->mutex);
|
||||
|
||||
/* See comment on mutex in optee_supp_read() above */
|
||||
mutex_lock(&supp->supp_mutex);
|
||||
|
||||
if (!supp->supp_next_send) {
|
||||
/*
|
||||
* Something strange is going on, supplicant shouldn't
|
||||
* enter optee_supp_send() in this state
|
||||
*/
|
||||
rc = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (num_params != supp->num_params) {
|
||||
/*
|
||||
* Something is wrong, let supplicant restart. Next call to
|
||||
* optee_supp_recv() will give an error to the requesting
|
||||
* thread and release it.
|
||||
*/
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
if (IS_ERR(req)) {
|
||||
/* Something is wrong, let supplicant restart. */
|
||||
return PTR_ERR(req);
|
||||
}
|
||||
|
||||
/* Update out and in/out parameters */
|
||||
for (n = 0; n < num_params; n++) {
|
||||
struct tee_param *p = supp->param + n;
|
||||
for (n = 0; n < req->num_params; n++) {
|
||||
struct tee_param *p = req->param + n;
|
||||
|
||||
switch (p->attr) {
|
||||
switch (p->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) {
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT:
|
||||
p->u.value.a = param[n].u.value.a;
|
||||
p->u.value.b = param[n].u.value.b;
|
||||
p->u.value.c = param[n].u.value.c;
|
||||
p->u.value.a = param[n + num_meta].u.value.a;
|
||||
p->u.value.b = param[n + num_meta].u.value.b;
|
||||
p->u.value.c = param[n + num_meta].u.value.c;
|
||||
break;
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
|
||||
p->u.memref.size = param[n].u.memref.size;
|
||||
p->u.memref.size = param[n + num_meta].u.memref.size;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
supp->ret = ret;
|
||||
|
||||
/* Allow optee_supp_recv() above to do its work */
|
||||
supp->supp_next_send = false;
|
||||
req->ret = ret;
|
||||
|
||||
/* Let the requesting thread continue */
|
||||
complete(&supp->data_from_supp);
|
||||
out:
|
||||
mutex_unlock(&supp->supp_mutex);
|
||||
return rc;
|
||||
complete(&req->c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ static int tee_open(struct inode *inode, struct file *filp)
|
|||
goto err;
|
||||
}
|
||||
|
||||
kref_init(&ctx->refcount);
|
||||
ctx->teedev = teedev;
|
||||
INIT_LIST_HEAD(&ctx->list_shm);
|
||||
filp->private_data = ctx;
|
||||
|
@ -68,19 +69,40 @@ err:
|
|||
return rc;
|
||||
}
|
||||
|
||||
void teedev_ctx_get(struct tee_context *ctx)
|
||||
{
|
||||
if (ctx->releasing)
|
||||
return;
|
||||
|
||||
kref_get(&ctx->refcount);
|
||||
}
|
||||
|
||||
static void teedev_ctx_release(struct kref *ref)
|
||||
{
|
||||
struct tee_context *ctx = container_of(ref, struct tee_context,
|
||||
refcount);
|
||||
ctx->releasing = true;
|
||||
ctx->teedev->desc->ops->release(ctx);
|
||||
kfree(ctx);
|
||||
}
|
||||
|
||||
void teedev_ctx_put(struct tee_context *ctx)
|
||||
{
|
||||
if (ctx->releasing)
|
||||
return;
|
||||
|
||||
kref_put(&ctx->refcount, teedev_ctx_release);
|
||||
}
|
||||
|
||||
static void teedev_close_context(struct tee_context *ctx)
|
||||
{
|
||||
tee_device_put(ctx->teedev);
|
||||
teedev_ctx_put(ctx);
|
||||
}
|
||||
|
||||
static int tee_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct tee_context *ctx = filp->private_data;
|
||||
struct tee_device *teedev = ctx->teedev;
|
||||
struct tee_shm *shm;
|
||||
|
||||
ctx->teedev->desc->ops->release(ctx);
|
||||
mutex_lock(&ctx->teedev->mutex);
|
||||
list_for_each_entry(shm, &ctx->list_shm, link)
|
||||
shm->ctx = NULL;
|
||||
mutex_unlock(&ctx->teedev->mutex);
|
||||
kfree(ctx);
|
||||
tee_device_put(teedev);
|
||||
teedev_close_context(filp->private_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -114,8 +136,6 @@ static int tee_ioctl_shm_alloc(struct tee_context *ctx,
|
|||
if (data.flags)
|
||||
return -EINVAL;
|
||||
|
||||
data.id = -1;
|
||||
|
||||
shm = tee_shm_alloc(ctx, data.size, TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
|
||||
if (IS_ERR(shm))
|
||||
return PTR_ERR(shm);
|
||||
|
@ -138,6 +158,43 @@ static int tee_ioctl_shm_alloc(struct tee_context *ctx,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
tee_ioctl_shm_register(struct tee_context *ctx,
|
||||
struct tee_ioctl_shm_register_data __user *udata)
|
||||
{
|
||||
long ret;
|
||||
struct tee_ioctl_shm_register_data data;
|
||||
struct tee_shm *shm;
|
||||
|
||||
if (copy_from_user(&data, udata, sizeof(data)))
|
||||
return -EFAULT;
|
||||
|
||||
/* Currently no input flags are supported */
|
||||
if (data.flags)
|
||||
return -EINVAL;
|
||||
|
||||
shm = tee_shm_register(ctx, data.addr, data.length,
|
||||
TEE_SHM_DMA_BUF | TEE_SHM_USER_MAPPED);
|
||||
if (IS_ERR(shm))
|
||||
return PTR_ERR(shm);
|
||||
|
||||
data.id = shm->id;
|
||||
data.flags = shm->flags;
|
||||
data.length = shm->size;
|
||||
|
||||
if (copy_to_user(udata, &data, sizeof(data)))
|
||||
ret = -EFAULT;
|
||||
else
|
||||
ret = tee_shm_get_fd(shm);
|
||||
/*
|
||||
* When user space closes the file descriptor the shared memory
|
||||
* should be freed or if tee_shm_get_fd() failed then it will
|
||||
* be freed immediately.
|
||||
*/
|
||||
tee_shm_put(shm);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int params_from_user(struct tee_context *ctx, struct tee_param *params,
|
||||
size_t num_params,
|
||||
struct tee_ioctl_param __user *uparams)
|
||||
|
@ -152,11 +209,11 @@ static int params_from_user(struct tee_context *ctx, struct tee_param *params,
|
|||
return -EFAULT;
|
||||
|
||||
/* All unused attribute bits has to be zero */
|
||||
if (ip.attr & ~TEE_IOCTL_PARAM_ATTR_TYPE_MASK)
|
||||
if (ip.attr & ~TEE_IOCTL_PARAM_ATTR_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
params[n].attr = ip.attr;
|
||||
switch (ip.attr) {
|
||||
switch (ip.attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) {
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_NONE:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT:
|
||||
break;
|
||||
|
@ -221,18 +278,6 @@ static int params_to_user(struct tee_ioctl_param __user *uparams,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool param_is_memref(struct tee_param *param)
|
||||
{
|
||||
switch (param->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) {
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int tee_ioctl_open_session(struct tee_context *ctx,
|
||||
struct tee_ioctl_buf_data __user *ubuf)
|
||||
{
|
||||
|
@ -296,7 +341,7 @@ out:
|
|||
if (params) {
|
||||
/* Decrease ref count for all valid shared memory pointers */
|
||||
for (n = 0; n < arg.num_params; n++)
|
||||
if (param_is_memref(params + n) &&
|
||||
if (tee_param_is_memref(params + n) &&
|
||||
params[n].u.memref.shm)
|
||||
tee_shm_put(params[n].u.memref.shm);
|
||||
kfree(params);
|
||||
|
@ -358,7 +403,7 @@ out:
|
|||
if (params) {
|
||||
/* Decrease ref count for all valid shared memory pointers */
|
||||
for (n = 0; n < arg.num_params; n++)
|
||||
if (param_is_memref(params + n) &&
|
||||
if (tee_param_is_memref(params + n) &&
|
||||
params[n].u.memref.shm)
|
||||
tee_shm_put(params[n].u.memref.shm);
|
||||
kfree(params);
|
||||
|
@ -406,8 +451,8 @@ static int params_to_supp(struct tee_context *ctx,
|
|||
struct tee_ioctl_param ip;
|
||||
struct tee_param *p = params + n;
|
||||
|
||||
ip.attr = p->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK;
|
||||
switch (p->attr) {
|
||||
ip.attr = p->attr;
|
||||
switch (p->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) {
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT:
|
||||
ip.a = p->u.value.a;
|
||||
|
@ -471,6 +516,10 @@ static int tee_ioctl_supp_recv(struct tee_context *ctx,
|
|||
if (!params)
|
||||
return -ENOMEM;
|
||||
|
||||
rc = params_from_user(ctx, params, num_params, uarg->params);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
rc = ctx->teedev->desc->ops->supp_recv(ctx, &func, &num_params, params);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
@ -500,11 +549,11 @@ static int params_from_supp(struct tee_param *params, size_t num_params,
|
|||
return -EFAULT;
|
||||
|
||||
/* All unused attribute bits has to be zero */
|
||||
if (ip.attr & ~TEE_IOCTL_PARAM_ATTR_TYPE_MASK)
|
||||
if (ip.attr & ~TEE_IOCTL_PARAM_ATTR_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
p->attr = ip.attr;
|
||||
switch (ip.attr) {
|
||||
switch (ip.attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) {
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT:
|
||||
/* Only out and in/out values can be updated */
|
||||
|
@ -586,6 +635,8 @@ static long tee_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|||
return tee_ioctl_version(ctx, uarg);
|
||||
case TEE_IOC_SHM_ALLOC:
|
||||
return tee_ioctl_shm_alloc(ctx, uarg);
|
||||
case TEE_IOC_SHM_REGISTER:
|
||||
return tee_ioctl_shm_register(ctx, uarg);
|
||||
case TEE_IOC_OPEN_SESSION:
|
||||
return tee_ioctl_open_session(ctx, uarg);
|
||||
case TEE_IOC_INVOKE:
|
||||
|
|
|
@ -21,68 +21,15 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct tee_device;
|
||||
|
||||
/**
|
||||
* struct tee_shm - shared memory object
|
||||
* @teedev: device used to allocate the object
|
||||
* @ctx: context using the object, if NULL the context is gone
|
||||
* @link link element
|
||||
* @paddr: physical address of the shared memory
|
||||
* @kaddr: virtual address of the shared memory
|
||||
* @size: size of shared memory
|
||||
* @dmabuf: dmabuf used to for exporting to user space
|
||||
* @flags: defined by TEE_SHM_* in tee_drv.h
|
||||
* @id: unique id of a shared memory object on this device
|
||||
*/
|
||||
struct tee_shm {
|
||||
struct tee_device *teedev;
|
||||
struct tee_context *ctx;
|
||||
struct list_head link;
|
||||
phys_addr_t paddr;
|
||||
void *kaddr;
|
||||
size_t size;
|
||||
struct dma_buf *dmabuf;
|
||||
u32 flags;
|
||||
int id;
|
||||
};
|
||||
|
||||
struct tee_shm_pool_mgr;
|
||||
|
||||
/**
|
||||
* struct tee_shm_pool_mgr_ops - shared memory pool manager operations
|
||||
* @alloc: called when allocating shared memory
|
||||
* @free: called when freeing shared memory
|
||||
*/
|
||||
struct tee_shm_pool_mgr_ops {
|
||||
int (*alloc)(struct tee_shm_pool_mgr *poolmgr, struct tee_shm *shm,
|
||||
size_t size);
|
||||
void (*free)(struct tee_shm_pool_mgr *poolmgr, struct tee_shm *shm);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tee_shm_pool_mgr - shared memory manager
|
||||
* @ops: operations
|
||||
* @private_data: private data for the shared memory manager
|
||||
*/
|
||||
struct tee_shm_pool_mgr {
|
||||
const struct tee_shm_pool_mgr_ops *ops;
|
||||
void *private_data;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tee_shm_pool - shared memory pool
|
||||
* @private_mgr: pool manager for shared memory only between kernel
|
||||
* and secure world
|
||||
* @dma_buf_mgr: pool manager for shared memory exported to user space
|
||||
* @destroy: called when destroying the pool
|
||||
* @private_data: private data for the pool
|
||||
*/
|
||||
struct tee_shm_pool {
|
||||
struct tee_shm_pool_mgr private_mgr;
|
||||
struct tee_shm_pool_mgr dma_buf_mgr;
|
||||
void (*destroy)(struct tee_shm_pool *pool);
|
||||
void *private_data;
|
||||
struct tee_shm_pool_mgr *private_mgr;
|
||||
struct tee_shm_pool_mgr *dma_buf_mgr;
|
||||
};
|
||||
|
||||
#define TEE_DEVICE_FLAG_REGISTERED 0x1
|
||||
|
@ -126,4 +73,7 @@ int tee_shm_get_fd(struct tee_shm *shm);
|
|||
bool tee_device_get(struct tee_device *teedev);
|
||||
void tee_device_put(struct tee_device *teedev);
|
||||
|
||||
void teedev_ctx_get(struct tee_context *ctx);
|
||||
void teedev_ctx_put(struct tee_context *ctx);
|
||||
|
||||
#endif /*TEE_PRIVATE_H*/
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
static void tee_shm_release(struct tee_shm *shm)
|
||||
{
|
||||
struct tee_device *teedev = shm->teedev;
|
||||
struct tee_shm_pool_mgr *poolm;
|
||||
|
||||
mutex_lock(&teedev->mutex);
|
||||
idr_remove(&teedev->idr, shm->id);
|
||||
|
@ -31,12 +30,32 @@ static void tee_shm_release(struct tee_shm *shm)
|
|||
list_del(&shm->link);
|
||||
mutex_unlock(&teedev->mutex);
|
||||
|
||||
if (shm->flags & TEE_SHM_DMA_BUF)
|
||||
poolm = &teedev->pool->dma_buf_mgr;
|
||||
else
|
||||
poolm = &teedev->pool->private_mgr;
|
||||
if (shm->flags & TEE_SHM_POOL) {
|
||||
struct tee_shm_pool_mgr *poolm;
|
||||
|
||||
if (shm->flags & TEE_SHM_DMA_BUF)
|
||||
poolm = teedev->pool->dma_buf_mgr;
|
||||
else
|
||||
poolm = teedev->pool->private_mgr;
|
||||
|
||||
poolm->ops->free(poolm, shm);
|
||||
} else if (shm->flags & TEE_SHM_REGISTER) {
|
||||
size_t n;
|
||||
int rc = teedev->desc->ops->shm_unregister(shm->ctx, shm);
|
||||
|
||||
if (rc)
|
||||
dev_err(teedev->dev.parent,
|
||||
"unregister shm %p failed: %d", shm, rc);
|
||||
|
||||
for (n = 0; n < shm->num_pages; n++)
|
||||
put_page(shm->pages[n]);
|
||||
|
||||
kfree(shm->pages);
|
||||
}
|
||||
|
||||
if (shm->ctx)
|
||||
teedev_ctx_put(shm->ctx);
|
||||
|
||||
poolm->ops->free(poolm, shm);
|
||||
kfree(shm);
|
||||
|
||||
tee_device_put(teedev);
|
||||
|
@ -76,6 +95,10 @@ static int tee_shm_op_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
|
|||
struct tee_shm *shm = dmabuf->priv;
|
||||
size_t size = vma->vm_end - vma->vm_start;
|
||||
|
||||
/* Refuse sharing shared memory provided by application */
|
||||
if (shm->flags & TEE_SHM_REGISTER)
|
||||
return -EINVAL;
|
||||
|
||||
return remap_pfn_range(vma, vma->vm_start, shm->paddr >> PAGE_SHIFT,
|
||||
size, vma->vm_page_prot);
|
||||
}
|
||||
|
@ -89,26 +112,20 @@ static const struct dma_buf_ops tee_shm_dma_buf_ops = {
|
|||
.mmap = tee_shm_op_mmap,
|
||||
};
|
||||
|
||||
/**
|
||||
* tee_shm_alloc() - Allocate shared memory
|
||||
* @ctx: Context that allocates the shared memory
|
||||
* @size: Requested size of shared memory
|
||||
* @flags: Flags setting properties for the requested shared memory.
|
||||
*
|
||||
* Memory allocated as global shared memory is automatically freed when the
|
||||
* TEE file pointer is closed. The @flags field uses the bits defined by
|
||||
* TEE_SHM_* in <linux/tee_drv.h>. TEE_SHM_MAPPED must currently always be
|
||||
* set. If TEE_SHM_DMA_BUF global shared memory will be allocated and
|
||||
* associated with a dma-buf handle, else driver private memory.
|
||||
*/
|
||||
struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags)
|
||||
static struct tee_shm *__tee_shm_alloc(struct tee_context *ctx,
|
||||
struct tee_device *teedev,
|
||||
size_t size, u32 flags)
|
||||
{
|
||||
struct tee_device *teedev = ctx->teedev;
|
||||
struct tee_shm_pool_mgr *poolm = NULL;
|
||||
struct tee_shm *shm;
|
||||
void *ret;
|
||||
int rc;
|
||||
|
||||
if (ctx && ctx->teedev != teedev) {
|
||||
dev_err(teedev->dev.parent, "ctx and teedev mismatch\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (!(flags & TEE_SHM_MAPPED)) {
|
||||
dev_err(teedev->dev.parent,
|
||||
"only mapped allocations supported\n");
|
||||
|
@ -135,13 +152,13 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags)
|
|||
goto err_dev_put;
|
||||
}
|
||||
|
||||
shm->flags = flags;
|
||||
shm->flags = flags | TEE_SHM_POOL;
|
||||
shm->teedev = teedev;
|
||||
shm->ctx = ctx;
|
||||
if (flags & TEE_SHM_DMA_BUF)
|
||||
poolm = &teedev->pool->dma_buf_mgr;
|
||||
poolm = teedev->pool->dma_buf_mgr;
|
||||
else
|
||||
poolm = &teedev->pool->private_mgr;
|
||||
poolm = teedev->pool->private_mgr;
|
||||
|
||||
rc = poolm->ops->alloc(poolm, shm, size);
|
||||
if (rc) {
|
||||
|
@ -171,9 +188,13 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags)
|
|||
goto err_rem;
|
||||
}
|
||||
}
|
||||
mutex_lock(&teedev->mutex);
|
||||
list_add_tail(&shm->link, &ctx->list_shm);
|
||||
mutex_unlock(&teedev->mutex);
|
||||
|
||||
if (ctx) {
|
||||
teedev_ctx_get(ctx);
|
||||
mutex_lock(&teedev->mutex);
|
||||
list_add_tail(&shm->link, &ctx->list_shm);
|
||||
mutex_unlock(&teedev->mutex);
|
||||
}
|
||||
|
||||
return shm;
|
||||
err_rem:
|
||||
|
@ -188,8 +209,145 @@ err_dev_put:
|
|||
tee_device_put(teedev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* tee_shm_alloc() - Allocate shared memory
|
||||
* @ctx: Context that allocates the shared memory
|
||||
* @size: Requested size of shared memory
|
||||
* @flags: Flags setting properties for the requested shared memory.
|
||||
*
|
||||
* Memory allocated as global shared memory is automatically freed when the
|
||||
* TEE file pointer is closed. The @flags field uses the bits defined by
|
||||
* TEE_SHM_* in <linux/tee_drv.h>. TEE_SHM_MAPPED must currently always be
|
||||
* set. If TEE_SHM_DMA_BUF global shared memory will be allocated and
|
||||
* associated with a dma-buf handle, else driver private memory.
|
||||
*/
|
||||
struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags)
|
||||
{
|
||||
return __tee_shm_alloc(ctx, ctx->teedev, size, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tee_shm_alloc);
|
||||
|
||||
struct tee_shm *tee_shm_priv_alloc(struct tee_device *teedev, size_t size)
|
||||
{
|
||||
return __tee_shm_alloc(NULL, teedev, size, TEE_SHM_MAPPED);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tee_shm_priv_alloc);
|
||||
|
||||
struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr,
|
||||
size_t length, u32 flags)
|
||||
{
|
||||
struct tee_device *teedev = ctx->teedev;
|
||||
const u32 req_flags = TEE_SHM_DMA_BUF | TEE_SHM_USER_MAPPED;
|
||||
struct tee_shm *shm;
|
||||
void *ret;
|
||||
int rc;
|
||||
int num_pages;
|
||||
unsigned long start;
|
||||
|
||||
if (flags != req_flags)
|
||||
return ERR_PTR(-ENOTSUPP);
|
||||
|
||||
if (!tee_device_get(teedev))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (!teedev->desc->ops->shm_register ||
|
||||
!teedev->desc->ops->shm_unregister) {
|
||||
tee_device_put(teedev);
|
||||
return ERR_PTR(-ENOTSUPP);
|
||||
}
|
||||
|
||||
teedev_ctx_get(ctx);
|
||||
|
||||
shm = kzalloc(sizeof(*shm), GFP_KERNEL);
|
||||
if (!shm) {
|
||||
ret = ERR_PTR(-ENOMEM);
|
||||
goto err;
|
||||
}
|
||||
|
||||
shm->flags = flags | TEE_SHM_REGISTER;
|
||||
shm->teedev = teedev;
|
||||
shm->ctx = ctx;
|
||||
shm->id = -1;
|
||||
start = rounddown(addr, PAGE_SIZE);
|
||||
shm->offset = addr - start;
|
||||
shm->size = length;
|
||||
num_pages = (roundup(addr + length, PAGE_SIZE) - start) / PAGE_SIZE;
|
||||
shm->pages = kcalloc(num_pages, sizeof(*shm->pages), GFP_KERNEL);
|
||||
if (!shm->pages) {
|
||||
ret = ERR_PTR(-ENOMEM);
|
||||
goto err;
|
||||
}
|
||||
|
||||
rc = get_user_pages_fast(start, num_pages, 1, shm->pages);
|
||||
if (rc > 0)
|
||||
shm->num_pages = rc;
|
||||
if (rc != num_pages) {
|
||||
if (rc >= 0)
|
||||
rc = -ENOMEM;
|
||||
ret = ERR_PTR(rc);
|
||||
goto err;
|
||||
}
|
||||
|
||||
mutex_lock(&teedev->mutex);
|
||||
shm->id = idr_alloc(&teedev->idr, shm, 1, 0, GFP_KERNEL);
|
||||
mutex_unlock(&teedev->mutex);
|
||||
|
||||
if (shm->id < 0) {
|
||||
ret = ERR_PTR(shm->id);
|
||||
goto err;
|
||||
}
|
||||
|
||||
rc = teedev->desc->ops->shm_register(ctx, shm, shm->pages,
|
||||
shm->num_pages, start);
|
||||
if (rc) {
|
||||
ret = ERR_PTR(rc);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (flags & TEE_SHM_DMA_BUF) {
|
||||
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
|
||||
|
||||
exp_info.ops = &tee_shm_dma_buf_ops;
|
||||
exp_info.size = shm->size;
|
||||
exp_info.flags = O_RDWR;
|
||||
exp_info.priv = shm;
|
||||
|
||||
shm->dmabuf = dma_buf_export(&exp_info);
|
||||
if (IS_ERR(shm->dmabuf)) {
|
||||
ret = ERR_CAST(shm->dmabuf);
|
||||
teedev->desc->ops->shm_unregister(ctx, shm);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_lock(&teedev->mutex);
|
||||
list_add_tail(&shm->link, &ctx->list_shm);
|
||||
mutex_unlock(&teedev->mutex);
|
||||
|
||||
return shm;
|
||||
err:
|
||||
if (shm) {
|
||||
size_t n;
|
||||
|
||||
if (shm->id >= 0) {
|
||||
mutex_lock(&teedev->mutex);
|
||||
idr_remove(&teedev->idr, shm->id);
|
||||
mutex_unlock(&teedev->mutex);
|
||||
}
|
||||
if (shm->pages) {
|
||||
for (n = 0; n < shm->num_pages; n++)
|
||||
put_page(shm->pages[n]);
|
||||
kfree(shm->pages);
|
||||
}
|
||||
}
|
||||
kfree(shm);
|
||||
teedev_ctx_put(ctx);
|
||||
tee_device_put(teedev);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tee_shm_register);
|
||||
|
||||
/**
|
||||
* tee_shm_get_fd() - Increase reference count and return file descriptor
|
||||
* @shm: Shared memory handle
|
||||
|
@ -197,10 +355,9 @@ EXPORT_SYMBOL_GPL(tee_shm_alloc);
|
|||
*/
|
||||
int tee_shm_get_fd(struct tee_shm *shm)
|
||||
{
|
||||
u32 req_flags = TEE_SHM_MAPPED | TEE_SHM_DMA_BUF;
|
||||
int fd;
|
||||
|
||||
if ((shm->flags & req_flags) != req_flags)
|
||||
if (!(shm->flags & TEE_SHM_DMA_BUF))
|
||||
return -EINVAL;
|
||||
|
||||
fd = dma_buf_fd(shm->dmabuf, O_CLOEXEC);
|
||||
|
@ -238,6 +395,8 @@ EXPORT_SYMBOL_GPL(tee_shm_free);
|
|||
*/
|
||||
int tee_shm_va2pa(struct tee_shm *shm, void *va, phys_addr_t *pa)
|
||||
{
|
||||
if (!(shm->flags & TEE_SHM_MAPPED))
|
||||
return -EINVAL;
|
||||
/* Check that we're in the range of the shm */
|
||||
if ((char *)va < (char *)shm->kaddr)
|
||||
return -EINVAL;
|
||||
|
@ -258,6 +417,8 @@ EXPORT_SYMBOL_GPL(tee_shm_va2pa);
|
|||
*/
|
||||
int tee_shm_pa2va(struct tee_shm *shm, phys_addr_t pa, void **va)
|
||||
{
|
||||
if (!(shm->flags & TEE_SHM_MAPPED))
|
||||
return -EINVAL;
|
||||
/* Check that we're in the range of the shm */
|
||||
if (pa < shm->paddr)
|
||||
return -EINVAL;
|
||||
|
@ -284,6 +445,8 @@ EXPORT_SYMBOL_GPL(tee_shm_pa2va);
|
|||
*/
|
||||
void *tee_shm_get_va(struct tee_shm *shm, size_t offs)
|
||||
{
|
||||
if (!(shm->flags & TEE_SHM_MAPPED))
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (offs >= shm->size)
|
||||
return ERR_PTR(-EINVAL);
|
||||
return (char *)shm->kaddr + offs;
|
||||
|
@ -335,17 +498,6 @@ struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(tee_shm_get_from_id);
|
||||
|
||||
/**
|
||||
* tee_shm_get_id() - Get id of a shared memory object
|
||||
* @shm: Shared memory handle
|
||||
* @returns id
|
||||
*/
|
||||
int tee_shm_get_id(struct tee_shm *shm)
|
||||
{
|
||||
return shm->id;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tee_shm_get_id);
|
||||
|
||||
/**
|
||||
* tee_shm_put() - Decrease reference count on a shared memory handle
|
||||
* @shm: Shared memory handle
|
||||
|
|
|
@ -44,49 +44,18 @@ static void pool_op_gen_free(struct tee_shm_pool_mgr *poolm,
|
|||
shm->kaddr = NULL;
|
||||
}
|
||||
|
||||
static void pool_op_gen_destroy_poolmgr(struct tee_shm_pool_mgr *poolm)
|
||||
{
|
||||
gen_pool_destroy(poolm->private_data);
|
||||
kfree(poolm);
|
||||
}
|
||||
|
||||
static const struct tee_shm_pool_mgr_ops pool_ops_generic = {
|
||||
.alloc = pool_op_gen_alloc,
|
||||
.free = pool_op_gen_free,
|
||||
.destroy_poolmgr = pool_op_gen_destroy_poolmgr,
|
||||
};
|
||||
|
||||
static void pool_res_mem_destroy(struct tee_shm_pool *pool)
|
||||
{
|
||||
gen_pool_destroy(pool->private_mgr.private_data);
|
||||
gen_pool_destroy(pool->dma_buf_mgr.private_data);
|
||||
}
|
||||
|
||||
static int pool_res_mem_mgr_init(struct tee_shm_pool_mgr *mgr,
|
||||
struct tee_shm_pool_mem_info *info,
|
||||
int min_alloc_order)
|
||||
{
|
||||
size_t page_mask = PAGE_SIZE - 1;
|
||||
struct gen_pool *genpool = NULL;
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* Start and end must be page aligned
|
||||
*/
|
||||
if ((info->vaddr & page_mask) || (info->paddr & page_mask) ||
|
||||
(info->size & page_mask))
|
||||
return -EINVAL;
|
||||
|
||||
genpool = gen_pool_create(min_alloc_order, -1);
|
||||
if (!genpool)
|
||||
return -ENOMEM;
|
||||
|
||||
gen_pool_set_algo(genpool, gen_pool_best_fit, NULL);
|
||||
rc = gen_pool_add_virt(genpool, info->vaddr, info->paddr, info->size,
|
||||
-1);
|
||||
if (rc) {
|
||||
gen_pool_destroy(genpool);
|
||||
return rc;
|
||||
}
|
||||
|
||||
mgr->private_data = genpool;
|
||||
mgr->ops = &pool_ops_generic;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tee_shm_pool_alloc_res_mem() - Create a shared memory pool from reserved
|
||||
* memory range
|
||||
|
@ -104,43 +73,110 @@ struct tee_shm_pool *
|
|||
tee_shm_pool_alloc_res_mem(struct tee_shm_pool_mem_info *priv_info,
|
||||
struct tee_shm_pool_mem_info *dmabuf_info)
|
||||
{
|
||||
struct tee_shm_pool *pool = NULL;
|
||||
int ret;
|
||||
|
||||
pool = kzalloc(sizeof(*pool), GFP_KERNEL);
|
||||
if (!pool) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
struct tee_shm_pool_mgr *priv_mgr;
|
||||
struct tee_shm_pool_mgr *dmabuf_mgr;
|
||||
void *rc;
|
||||
|
||||
/*
|
||||
* Create the pool for driver private shared memory
|
||||
*/
|
||||
ret = pool_res_mem_mgr_init(&pool->private_mgr, priv_info,
|
||||
3 /* 8 byte aligned */);
|
||||
if (ret)
|
||||
goto err;
|
||||
rc = tee_shm_pool_mgr_alloc_res_mem(priv_info->vaddr, priv_info->paddr,
|
||||
priv_info->size,
|
||||
3 /* 8 byte aligned */);
|
||||
if (IS_ERR(rc))
|
||||
return rc;
|
||||
priv_mgr = rc;
|
||||
|
||||
/*
|
||||
* Create the pool for dma_buf shared memory
|
||||
*/
|
||||
ret = pool_res_mem_mgr_init(&pool->dma_buf_mgr, dmabuf_info,
|
||||
PAGE_SHIFT);
|
||||
if (ret)
|
||||
goto err;
|
||||
rc = tee_shm_pool_mgr_alloc_res_mem(dmabuf_info->vaddr,
|
||||
dmabuf_info->paddr,
|
||||
dmabuf_info->size, PAGE_SHIFT);
|
||||
if (IS_ERR(rc))
|
||||
goto err_free_priv_mgr;
|
||||
dmabuf_mgr = rc;
|
||||
|
||||
pool->destroy = pool_res_mem_destroy;
|
||||
return pool;
|
||||
err:
|
||||
if (ret == -ENOMEM)
|
||||
pr_err("%s: can't allocate memory for res_mem shared memory pool\n", __func__);
|
||||
if (pool && pool->private_mgr.private_data)
|
||||
gen_pool_destroy(pool->private_mgr.private_data);
|
||||
kfree(pool);
|
||||
return ERR_PTR(ret);
|
||||
rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr);
|
||||
if (IS_ERR(rc))
|
||||
goto err_free_dmabuf_mgr;
|
||||
|
||||
return rc;
|
||||
|
||||
err_free_dmabuf_mgr:
|
||||
tee_shm_pool_mgr_destroy(dmabuf_mgr);
|
||||
err_free_priv_mgr:
|
||||
tee_shm_pool_mgr_destroy(priv_mgr);
|
||||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tee_shm_pool_alloc_res_mem);
|
||||
|
||||
struct tee_shm_pool_mgr *tee_shm_pool_mgr_alloc_res_mem(unsigned long vaddr,
|
||||
phys_addr_t paddr,
|
||||
size_t size,
|
||||
int min_alloc_order)
|
||||
{
|
||||
const size_t page_mask = PAGE_SIZE - 1;
|
||||
struct tee_shm_pool_mgr *mgr;
|
||||
int rc;
|
||||
|
||||
/* Start and end must be page aligned */
|
||||
if (vaddr & page_mask || paddr & page_mask || size & page_mask)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
|
||||
if (!mgr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mgr->private_data = gen_pool_create(min_alloc_order, -1);
|
||||
if (!mgr->private_data) {
|
||||
rc = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
gen_pool_set_algo(mgr->private_data, gen_pool_best_fit, NULL);
|
||||
rc = gen_pool_add_virt(mgr->private_data, vaddr, paddr, size, -1);
|
||||
if (rc) {
|
||||
gen_pool_destroy(mgr->private_data);
|
||||
goto err;
|
||||
}
|
||||
|
||||
mgr->ops = &pool_ops_generic;
|
||||
|
||||
return mgr;
|
||||
err:
|
||||
kfree(mgr);
|
||||
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tee_shm_pool_mgr_alloc_res_mem);
|
||||
|
||||
static bool check_mgr_ops(struct tee_shm_pool_mgr *mgr)
|
||||
{
|
||||
return mgr && mgr->ops && mgr->ops->alloc && mgr->ops->free &&
|
||||
mgr->ops->destroy_poolmgr;
|
||||
}
|
||||
|
||||
struct tee_shm_pool *tee_shm_pool_alloc(struct tee_shm_pool_mgr *priv_mgr,
|
||||
struct tee_shm_pool_mgr *dmabuf_mgr)
|
||||
{
|
||||
struct tee_shm_pool *pool;
|
||||
|
||||
if (!check_mgr_ops(priv_mgr) || !check_mgr_ops(dmabuf_mgr))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
pool = kzalloc(sizeof(*pool), GFP_KERNEL);
|
||||
if (!pool)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pool->private_mgr = priv_mgr;
|
||||
pool->dma_buf_mgr = dmabuf_mgr;
|
||||
|
||||
return pool;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tee_shm_pool_alloc);
|
||||
|
||||
/**
|
||||
* tee_shm_pool_free() - Free a shared memory pool
|
||||
* @pool: The shared memory pool to free
|
||||
|
@ -150,7 +186,10 @@ EXPORT_SYMBOL_GPL(tee_shm_pool_alloc_res_mem);
|
|||
*/
|
||||
void tee_shm_pool_free(struct tee_shm_pool *pool)
|
||||
{
|
||||
pool->destroy(pool);
|
||||
if (pool->private_mgr)
|
||||
tee_shm_pool_mgr_destroy(pool->private_mgr);
|
||||
if (pool->dma_buf_mgr)
|
||||
tee_shm_pool_mgr_destroy(pool->dma_buf_mgr);
|
||||
kfree(pool);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tee_shm_pool_free);
|
||||
|
|
|
@ -68,7 +68,6 @@ const struct consw dummy_con = {
|
|||
.con_switch = DUMMY,
|
||||
.con_blank = DUMMY,
|
||||
.con_font_set = DUMMY,
|
||||
.con_font_get = DUMMY,
|
||||
.con_font_default = DUMMY,
|
||||
.con_font_copy = DUMMY,
|
||||
.con_set_palette = DUMMY,
|
||||
|
|
|
@ -1120,7 +1120,7 @@ static int atmel_lcdfb_of_init(struct atmel_lcdfb_info *sinfo)
|
|||
goto put_display_node;
|
||||
}
|
||||
|
||||
timings_np = of_find_node_by_name(display_np, "display-timings");
|
||||
timings_np = of_get_child_by_name(display_np, "display-timings");
|
||||
if (!timings_np) {
|
||||
dev_err(dev, "failed to find display-timings node\n");
|
||||
ret = -ENODEV;
|
||||
|
@ -1141,6 +1141,12 @@ static int atmel_lcdfb_of_init(struct atmel_lcdfb_info *sinfo)
|
|||
fb_add_videomode(&fb_vm, &info->modelist);
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: Make sure we are not referencing any fields in display_np
|
||||
* and timings_np and drop our references to them before returning to
|
||||
* avoid leaking the nodes on probe deferral and driver unbind.
|
||||
*/
|
||||
|
||||
return 0;
|
||||
|
||||
put_timings_node:
|
||||
|
|
|
@ -1292,8 +1292,11 @@ next_slot:
|
|||
leaf = path->nodes[0];
|
||||
if (path->slots[0] >= btrfs_header_nritems(leaf)) {
|
||||
ret = btrfs_next_leaf(root, path);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
if (cow_start != (u64)-1)
|
||||
cur_offset = cow_start;
|
||||
goto error;
|
||||
}
|
||||
if (ret > 0)
|
||||
break;
|
||||
leaf = path->nodes[0];
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "print-tree.h"
|
||||
#include "backref.h"
|
||||
#include "hash.h"
|
||||
#include "inode-map.h"
|
||||
|
||||
/* magic values for the inode_only field in btrfs_log_inode:
|
||||
*
|
||||
|
@ -2445,6 +2446,9 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
|
|||
next);
|
||||
btrfs_wait_tree_block_writeback(next);
|
||||
btrfs_tree_unlock(next);
|
||||
} else {
|
||||
if (test_and_clear_bit(EXTENT_BUFFER_DIRTY, &next->bflags))
|
||||
clear_extent_buffer_dirty(next);
|
||||
}
|
||||
|
||||
WARN_ON(root_owner !=
|
||||
|
@ -2524,6 +2528,9 @@ static noinline int walk_up_log_tree(struct btrfs_trans_handle *trans,
|
|||
next);
|
||||
btrfs_wait_tree_block_writeback(next);
|
||||
btrfs_tree_unlock(next);
|
||||
} else {
|
||||
if (test_and_clear_bit(EXTENT_BUFFER_DIRTY, &next->bflags))
|
||||
clear_extent_buffer_dirty(next);
|
||||
}
|
||||
|
||||
WARN_ON(root_owner != BTRFS_TREE_LOG_OBJECTID);
|
||||
|
@ -2600,6 +2607,9 @@ static int walk_log_tree(struct btrfs_trans_handle *trans,
|
|||
clean_tree_block(trans, log->fs_info, next);
|
||||
btrfs_wait_tree_block_writeback(next);
|
||||
btrfs_tree_unlock(next);
|
||||
} else {
|
||||
if (test_and_clear_bit(EXTENT_BUFFER_DIRTY, &next->bflags))
|
||||
clear_extent_buffer_dirty(next);
|
||||
}
|
||||
|
||||
WARN_ON(log->root_key.objectid !=
|
||||
|
@ -5514,6 +5524,23 @@ again:
|
|||
path);
|
||||
}
|
||||
|
||||
if (!ret && wc.stage == LOG_WALK_REPLAY_ALL) {
|
||||
struct btrfs_root *root = wc.replay_dest;
|
||||
|
||||
btrfs_release_path(path);
|
||||
|
||||
/*
|
||||
* We have just replayed everything, and the highest
|
||||
* objectid of fs roots probably has changed in case
|
||||
* some inode_item's got replayed.
|
||||
*
|
||||
* root->objectid_mutex is not acquired as log replay
|
||||
* could only happen during mount.
|
||||
*/
|
||||
ret = btrfs_find_highest_objectid(root,
|
||||
&root->highest_objectid);
|
||||
}
|
||||
|
||||
key.offset = found_key.offset - 1;
|
||||
wc.replay_dest->log_root = NULL;
|
||||
free_extent_buffer(log->node);
|
||||
|
|
|
@ -688,6 +688,7 @@ __acquires(bitlock)
|
|||
}
|
||||
|
||||
ext4_unlock_group(sb, grp);
|
||||
ext4_commit_super(sb, 1);
|
||||
ext4_handle_error(sb);
|
||||
/*
|
||||
* We only get here in the ERRORS_RO case; relocking the group
|
||||
|
|
|
@ -169,11 +169,6 @@ static inline bool wq_has_sleeper(wait_queue_head_t *wq)
|
|||
return waitqueue_active(wq);
|
||||
}
|
||||
|
||||
static inline void inode_nohighmem(struct inode *inode)
|
||||
{
|
||||
mapping_set_gfp_mask(inode->i_mapping, GFP_USER);
|
||||
}
|
||||
|
||||
/**
|
||||
* current_time - Return FS time
|
||||
* @inode: inode.
|
||||
|
|
|
@ -2015,6 +2015,9 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
|
|||
int retval = 0;
|
||||
const char *s = nd->name->name;
|
||||
|
||||
if (!*s)
|
||||
flags &= ~LOOKUP_RCU;
|
||||
|
||||
nd->last_type = LAST_ROOT; /* if there are only slashes... */
|
||||
nd->flags = flags | LOOKUP_JUMPED | LOOKUP_PARENT;
|
||||
nd->depth = 0;
|
||||
|
|
|
@ -32,7 +32,7 @@ static inline void kaiser_init(void)
|
|||
{
|
||||
}
|
||||
static inline int kaiser_add_mapping(unsigned long addr,
|
||||
unsigned long size, unsigned long flags)
|
||||
unsigned long size, u64 flags)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include <linux/types.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/tee.h>
|
||||
|
||||
|
@ -25,8 +26,12 @@
|
|||
* specific TEE driver.
|
||||
*/
|
||||
|
||||
#define TEE_SHM_MAPPED 0x1 /* Memory mapped by the kernel */
|
||||
#define TEE_SHM_DMA_BUF 0x2 /* Memory with dma-buf handle */
|
||||
#define TEE_SHM_MAPPED BIT(0) /* Memory mapped by the kernel */
|
||||
#define TEE_SHM_DMA_BUF BIT(1) /* Memory with dma-buf handle */
|
||||
#define TEE_SHM_EXT_DMA_BUF BIT(2) /* Memory with dma-buf handle */
|
||||
#define TEE_SHM_REGISTER BIT(3) /* Memory registered in secure world */
|
||||
#define TEE_SHM_USER_MAPPED BIT(4) /* Memory mapped in user space */
|
||||
#define TEE_SHM_POOL BIT(5) /* Memory allocated from pool */
|
||||
|
||||
struct device;
|
||||
struct tee_device;
|
||||
|
@ -38,11 +43,17 @@ struct tee_shm_pool;
|
|||
* @teedev: pointer to this drivers struct tee_device
|
||||
* @list_shm: List of shared memory object owned by this context
|
||||
* @data: driver specific context data, managed by the driver
|
||||
* @refcount: reference counter for this structure
|
||||
* @releasing: flag that indicates if context is being released right now.
|
||||
* It is needed to break circular dependency on context during
|
||||
* shared memory release.
|
||||
*/
|
||||
struct tee_context {
|
||||
struct tee_device *teedev;
|
||||
struct list_head list_shm;
|
||||
void *data;
|
||||
struct kref refcount;
|
||||
bool releasing;
|
||||
};
|
||||
|
||||
struct tee_param_memref {
|
||||
|
@ -76,6 +87,8 @@ struct tee_param {
|
|||
* @cancel_req: request cancel of an ongoing invoke or open
|
||||
* @supp_revc: called for supplicant to get a command
|
||||
* @supp_send: called for supplicant to send a response
|
||||
* @shm_register: register shared memory buffer in TEE
|
||||
* @shm_unregister: unregister shared memory buffer in TEE
|
||||
*/
|
||||
struct tee_driver_ops {
|
||||
void (*get_version)(struct tee_device *teedev,
|
||||
|
@ -94,6 +107,10 @@ struct tee_driver_ops {
|
|||
struct tee_param *param);
|
||||
int (*supp_send)(struct tee_context *ctx, u32 ret, u32 num_params,
|
||||
struct tee_param *param);
|
||||
int (*shm_register)(struct tee_context *ctx, struct tee_shm *shm,
|
||||
struct page **pages, size_t num_pages,
|
||||
unsigned long start);
|
||||
int (*shm_unregister)(struct tee_context *ctx, struct tee_shm *shm);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -149,6 +166,97 @@ int tee_device_register(struct tee_device *teedev);
|
|||
*/
|
||||
void tee_device_unregister(struct tee_device *teedev);
|
||||
|
||||
/**
|
||||
* struct tee_shm - shared memory object
|
||||
* @teedev: device used to allocate the object
|
||||
* @ctx: context using the object, if NULL the context is gone
|
||||
* @link link element
|
||||
* @paddr: physical address of the shared memory
|
||||
* @kaddr: virtual address of the shared memory
|
||||
* @size: size of shared memory
|
||||
* @offset: offset of buffer in user space
|
||||
* @pages: locked pages from userspace
|
||||
* @num_pages: number of locked pages
|
||||
* @dmabuf: dmabuf used to for exporting to user space
|
||||
* @flags: defined by TEE_SHM_* in tee_drv.h
|
||||
* @id: unique id of a shared memory object on this device
|
||||
*
|
||||
* This pool is only supposed to be accessed directly from the TEE
|
||||
* subsystem and from drivers that implements their own shm pool manager.
|
||||
*/
|
||||
struct tee_shm {
|
||||
struct tee_device *teedev;
|
||||
struct tee_context *ctx;
|
||||
struct list_head link;
|
||||
phys_addr_t paddr;
|
||||
void *kaddr;
|
||||
size_t size;
|
||||
unsigned int offset;
|
||||
struct page **pages;
|
||||
size_t num_pages;
|
||||
struct dma_buf *dmabuf;
|
||||
u32 flags;
|
||||
int id;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tee_shm_pool_mgr - shared memory manager
|
||||
* @ops: operations
|
||||
* @private_data: private data for the shared memory manager
|
||||
*/
|
||||
struct tee_shm_pool_mgr {
|
||||
const struct tee_shm_pool_mgr_ops *ops;
|
||||
void *private_data;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tee_shm_pool_mgr_ops - shared memory pool manager operations
|
||||
* @alloc: called when allocating shared memory
|
||||
* @free: called when freeing shared memory
|
||||
* @destroy_poolmgr: called when destroying the pool manager
|
||||
*/
|
||||
struct tee_shm_pool_mgr_ops {
|
||||
int (*alloc)(struct tee_shm_pool_mgr *poolmgr, struct tee_shm *shm,
|
||||
size_t size);
|
||||
void (*free)(struct tee_shm_pool_mgr *poolmgr, struct tee_shm *shm);
|
||||
void (*destroy_poolmgr)(struct tee_shm_pool_mgr *poolmgr);
|
||||
};
|
||||
|
||||
/**
|
||||
* tee_shm_pool_alloc() - Create a shared memory pool from shm managers
|
||||
* @priv_mgr: manager for driver private shared memory allocations
|
||||
* @dmabuf_mgr: manager for dma-buf shared memory allocations
|
||||
*
|
||||
* Allocation with the flag TEE_SHM_DMA_BUF set will use the range supplied
|
||||
* in @dmabuf, others will use the range provided by @priv.
|
||||
*
|
||||
* @returns pointer to a 'struct tee_shm_pool' or an ERR_PTR on failure.
|
||||
*/
|
||||
struct tee_shm_pool *tee_shm_pool_alloc(struct tee_shm_pool_mgr *priv_mgr,
|
||||
struct tee_shm_pool_mgr *dmabuf_mgr);
|
||||
|
||||
/*
|
||||
* tee_shm_pool_mgr_alloc_res_mem() - Create a shm manager for reserved
|
||||
* memory
|
||||
* @vaddr: Virtual address of start of pool
|
||||
* @paddr: Physical address of start of pool
|
||||
* @size: Size in bytes of the pool
|
||||
*
|
||||
* @returns pointer to a 'struct tee_shm_pool_mgr' or an ERR_PTR on failure.
|
||||
*/
|
||||
struct tee_shm_pool_mgr *tee_shm_pool_mgr_alloc_res_mem(unsigned long vaddr,
|
||||
phys_addr_t paddr,
|
||||
size_t size,
|
||||
int min_alloc_order);
|
||||
|
||||
/**
|
||||
* tee_shm_pool_mgr_destroy() - Free a shared memory manager
|
||||
*/
|
||||
static inline void tee_shm_pool_mgr_destroy(struct tee_shm_pool_mgr *poolm)
|
||||
{
|
||||
poolm->ops->destroy_poolmgr(poolm);
|
||||
}
|
||||
|
||||
/**
|
||||
* struct tee_shm_pool_mem_info - holds information needed to create a shared
|
||||
* memory pool
|
||||
|
@ -210,6 +318,40 @@ void *tee_get_drvdata(struct tee_device *teedev);
|
|||
*/
|
||||
struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags);
|
||||
|
||||
/**
|
||||
* tee_shm_priv_alloc() - Allocate shared memory privately
|
||||
* @dev: Device that allocates the shared memory
|
||||
* @size: Requested size of shared memory
|
||||
*
|
||||
* Allocates shared memory buffer that is not associated with any client
|
||||
* context. Such buffers are owned by TEE driver and used for internal calls.
|
||||
*
|
||||
* @returns a pointer to 'struct tee_shm'
|
||||
*/
|
||||
struct tee_shm *tee_shm_priv_alloc(struct tee_device *teedev, size_t size);
|
||||
|
||||
/**
|
||||
* tee_shm_register() - Register shared memory buffer
|
||||
* @ctx: Context that registers the shared memory
|
||||
* @addr: Address is userspace of the shared buffer
|
||||
* @length: Length of the shared buffer
|
||||
* @flags: Flags setting properties for the requested shared memory.
|
||||
*
|
||||
* @returns a pointer to 'struct tee_shm'
|
||||
*/
|
||||
struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr,
|
||||
size_t length, u32 flags);
|
||||
|
||||
/**
|
||||
* tee_shm_is_registered() - Check if shared memory object in registered in TEE
|
||||
* @shm: Shared memory handle
|
||||
* @returns true if object is registered in TEE
|
||||
*/
|
||||
static inline bool tee_shm_is_registered(struct tee_shm *shm)
|
||||
{
|
||||
return shm && (shm->flags & TEE_SHM_REGISTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* tee_shm_free() - Free shared memory
|
||||
* @shm: Handle to shared memory to free
|
||||
|
@ -259,12 +401,48 @@ void *tee_shm_get_va(struct tee_shm *shm, size_t offs);
|
|||
*/
|
||||
int tee_shm_get_pa(struct tee_shm *shm, size_t offs, phys_addr_t *pa);
|
||||
|
||||
/**
|
||||
* tee_shm_get_size() - Get size of shared memory buffer
|
||||
* @shm: Shared memory handle
|
||||
* @returns size of shared memory
|
||||
*/
|
||||
static inline size_t tee_shm_get_size(struct tee_shm *shm)
|
||||
{
|
||||
return shm->size;
|
||||
}
|
||||
|
||||
/**
|
||||
* tee_shm_get_pages() - Get list of pages that hold shared buffer
|
||||
* @shm: Shared memory handle
|
||||
* @num_pages: Number of pages will be stored there
|
||||
* @returns pointer to pages array
|
||||
*/
|
||||
static inline struct page **tee_shm_get_pages(struct tee_shm *shm,
|
||||
size_t *num_pages)
|
||||
{
|
||||
*num_pages = shm->num_pages;
|
||||
return shm->pages;
|
||||
}
|
||||
|
||||
/**
|
||||
* tee_shm_get_page_offset() - Get shared buffer offset from page start
|
||||
* @shm: Shared memory handle
|
||||
* @returns page offset of shared buffer
|
||||
*/
|
||||
static inline size_t tee_shm_get_page_offset(struct tee_shm *shm)
|
||||
{
|
||||
return shm->offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* tee_shm_get_id() - Get id of a shared memory object
|
||||
* @shm: Shared memory handle
|
||||
* @returns id
|
||||
*/
|
||||
int tee_shm_get_id(struct tee_shm *shm);
|
||||
static inline int tee_shm_get_id(struct tee_shm *shm)
|
||||
{
|
||||
return shm->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* tee_shm_get_from_id() - Find shared memory object and increase reference
|
||||
|
@ -275,4 +453,16 @@ int tee_shm_get_id(struct tee_shm *shm);
|
|||
*/
|
||||
struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id);
|
||||
|
||||
static inline bool tee_param_is_memref(struct tee_param *param)
|
||||
{
|
||||
switch (param->attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) {
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT:
|
||||
case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /*__TEE_DRV_H*/
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
|
||||
#define TEE_GEN_CAP_GP (1 << 0)/* GlobalPlatform compliant TEE */
|
||||
#define TEE_GEN_CAP_PRIVILEGED (1 << 1)/* Privileged device (for supplicant) */
|
||||
#define TEE_GEN_CAP_REG_MEM (1 << 2)/* Supports registering shared memory */
|
||||
|
||||
/*
|
||||
* TEE Implementation ID
|
||||
|
@ -154,6 +155,13 @@ struct tee_ioctl_buf_data {
|
|||
*/
|
||||
#define TEE_IOCTL_PARAM_ATTR_TYPE_MASK 0xff
|
||||
|
||||
/* Meta parameter carrying extra information about the message. */
|
||||
#define TEE_IOCTL_PARAM_ATTR_META 0x100
|
||||
|
||||
/* Mask of all known attr bits */
|
||||
#define TEE_IOCTL_PARAM_ATTR_MASK \
|
||||
(TEE_IOCTL_PARAM_ATTR_TYPE_MASK | TEE_IOCTL_PARAM_ATTR_META)
|
||||
|
||||
/*
|
||||
* Matches TEEC_LOGIN_* in GP TEE Client API
|
||||
* Are only defined for GP compliant TEEs
|
||||
|
@ -332,6 +340,35 @@ struct tee_iocl_supp_send_arg {
|
|||
#define TEE_IOC_SUPPL_SEND _IOR(TEE_IOC_MAGIC, TEE_IOC_BASE + 7, \
|
||||
struct tee_ioctl_buf_data)
|
||||
|
||||
/**
|
||||
* struct tee_ioctl_shm_register_data - Shared memory register argument
|
||||
* @addr: [in] Start address of shared memory to register
|
||||
* @length: [in/out] Length of shared memory to register
|
||||
* @flags: [in/out] Flags to/from registration.
|
||||
* @id: [out] Identifier of the shared memory
|
||||
*
|
||||
* The flags field should currently be zero as input. Updated by the call
|
||||
* with actual flags as defined by TEE_IOCTL_SHM_* above.
|
||||
* This structure is used as argument for TEE_IOC_SHM_REGISTER below.
|
||||
*/
|
||||
struct tee_ioctl_shm_register_data {
|
||||
__u64 addr;
|
||||
__u64 length;
|
||||
__u32 flags;
|
||||
__s32 id;
|
||||
};
|
||||
|
||||
/**
|
||||
* TEE_IOC_SHM_REGISTER - Register shared memory argument
|
||||
*
|
||||
* Registers shared memory between the user space process and secure OS.
|
||||
*
|
||||
* Returns a file descriptor on success or < 0 on failure
|
||||
*
|
||||
* The shared memory is unregisterred when the descriptor is closed.
|
||||
*/
|
||||
#define TEE_IOC_SHM_REGISTER _IOWR(TEE_IOC_MAGIC, TEE_IOC_BASE + 9, \
|
||||
struct tee_ioctl_shm_register_data)
|
||||
/*
|
||||
* Five syscalls are used when communicating with the TEE driver.
|
||||
* open(): opens the device associated with the driver
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
|
||||
#include "internal.h"
|
||||
|
||||
#ifdef LAST_CPUPID_NOT_IN_PAGE_FLAGS
|
||||
#if defined(LAST_CPUPID_NOT_IN_PAGE_FLAGS) && !defined(CONFIG_COMPILE_TEST)
|
||||
#warning Unfortunate NUMA and NUMA Balancing config, growing page-frame for last_cpupid.
|
||||
#endif
|
||||
|
||||
|
|
|
@ -1012,7 +1012,7 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf,
|
|||
{
|
||||
struct snd_seq_client *client = file->private_data;
|
||||
int written = 0, len;
|
||||
int err = -EINVAL;
|
||||
int err;
|
||||
struct snd_seq_event event;
|
||||
|
||||
if (!(snd_seq_file_flags(file) & SNDRV_SEQ_LFLG_OUTPUT))
|
||||
|
@ -1027,11 +1027,15 @@ static ssize_t snd_seq_write(struct file *file, const char __user *buf,
|
|||
|
||||
/* allocate the pool now if the pool is not allocated yet */
|
||||
if (client->pool->size > 0 && !snd_seq_write_pool_allocated(client)) {
|
||||
if (snd_seq_pool_init(client->pool) < 0)
|
||||
mutex_lock(&client->ioctl_mutex);
|
||||
err = snd_seq_pool_init(client->pool);
|
||||
mutex_unlock(&client->ioctl_mutex);
|
||||
if (err < 0)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* only process whole events */
|
||||
err = -EINVAL;
|
||||
while (count >= sizeof(struct snd_seq_event)) {
|
||||
/* Read in the event header from the user */
|
||||
len = sizeof(event);
|
||||
|
@ -2196,7 +2200,6 @@ static int snd_seq_do_ioctl(struct snd_seq_client *client, unsigned int cmd,
|
|||
void __user *arg)
|
||||
{
|
||||
struct seq_ioctl_table *p;
|
||||
int ret;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_SEQ_IOCTL_PVERSION:
|
||||
|
@ -2210,12 +2213,8 @@ static int snd_seq_do_ioctl(struct snd_seq_client *client, unsigned int cmd,
|
|||
if (! arg)
|
||||
return -EFAULT;
|
||||
for (p = ioctl_tables; p->cmd; p++) {
|
||||
if (p->cmd == cmd) {
|
||||
mutex_lock(&client->ioctl_mutex);
|
||||
ret = p->func(client, arg);
|
||||
mutex_unlock(&client->ioctl_mutex);
|
||||
return ret;
|
||||
}
|
||||
if (p->cmd == cmd)
|
||||
return p->func(client, arg);
|
||||
}
|
||||
pr_debug("ALSA: seq unknown ioctl() 0x%x (type='%c', number=0x%02x)\n",
|
||||
cmd, _IOC_TYPE(cmd), _IOC_NR(cmd));
|
||||
|
@ -2226,11 +2225,15 @@ static int snd_seq_do_ioctl(struct snd_seq_client *client, unsigned int cmd,
|
|||
static long snd_seq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct snd_seq_client *client = file->private_data;
|
||||
long ret;
|
||||
|
||||
if (snd_BUG_ON(!client))
|
||||
return -ENXIO;
|
||||
|
||||
return snd_seq_do_ioctl(client, cmd, (void __user *) arg);
|
||||
mutex_lock(&client->ioctl_mutex);
|
||||
ret = snd_seq_do_ioctl(client, cmd, (void __user *) arg);
|
||||
mutex_unlock(&client->ioctl_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
|
|
|
@ -3130,6 +3130,19 @@ static void alc269_fixup_pincfg_no_hp_to_lineout(struct hda_codec *codec,
|
|||
spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
|
||||
}
|
||||
|
||||
static void alc269_fixup_pincfg_U7x7_headset_mic(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix,
|
||||
int action)
|
||||
{
|
||||
unsigned int cfg_headphone = snd_hda_codec_get_pincfg(codec, 0x21);
|
||||
unsigned int cfg_headset_mic = snd_hda_codec_get_pincfg(codec, 0x19);
|
||||
|
||||
if (cfg_headphone && cfg_headset_mic == 0x411111f0)
|
||||
snd_hda_codec_set_pincfg(codec, 0x19,
|
||||
(cfg_headphone & ~AC_DEFCFG_DEVICE) |
|
||||
(AC_JACK_MIC_IN << AC_DEFCFG_DEVICE_SHIFT));
|
||||
}
|
||||
|
||||
static void alc269_fixup_hweq(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix, int action)
|
||||
{
|
||||
|
@ -4782,6 +4795,7 @@ enum {
|
|||
ALC269_FIXUP_LIFEBOOK_EXTMIC,
|
||||
ALC269_FIXUP_LIFEBOOK_HP_PIN,
|
||||
ALC269_FIXUP_LIFEBOOK_NO_HP_TO_LINEOUT,
|
||||
ALC255_FIXUP_LIFEBOOK_U7x7_HEADSET_MIC,
|
||||
ALC269_FIXUP_AMIC,
|
||||
ALC269_FIXUP_DMIC,
|
||||
ALC269VB_FIXUP_AMIC,
|
||||
|
@ -4972,6 +4986,10 @@ static const struct hda_fixup alc269_fixups[] = {
|
|||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc269_fixup_pincfg_no_hp_to_lineout,
|
||||
},
|
||||
[ALC255_FIXUP_LIFEBOOK_U7x7_HEADSET_MIC] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc269_fixup_pincfg_U7x7_headset_mic,
|
||||
},
|
||||
[ALC269_FIXUP_AMIC] = {
|
||||
.type = HDA_FIXUP_PINS,
|
||||
.v.pins = (const struct hda_pintbl[]) {
|
||||
|
@ -5687,6 +5705,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
|||
SND_PCI_QUIRK(0x10cf, 0x159f, "Lifebook E780", ALC269_FIXUP_LIFEBOOK_NO_HP_TO_LINEOUT),
|
||||
SND_PCI_QUIRK(0x10cf, 0x15dc, "Lifebook T731", ALC269_FIXUP_LIFEBOOK_HP_PIN),
|
||||
SND_PCI_QUIRK(0x10cf, 0x1757, "Lifebook E752", ALC269_FIXUP_LIFEBOOK_HP_PIN),
|
||||
SND_PCI_QUIRK(0x10cf, 0x1629, "Lifebook U7x7", ALC255_FIXUP_LIFEBOOK_U7x7_HEADSET_MIC),
|
||||
SND_PCI_QUIRK(0x10cf, 0x1845, "Lifebook U904", ALC269_FIXUP_LIFEBOOK_EXTMIC),
|
||||
SND_PCI_QUIRK(0x144d, 0xc109, "Samsung Ativ book 9 (NP900X3G)", ALC269_FIXUP_INV_DMIC),
|
||||
SND_PCI_QUIRK(0x1458, 0xfa53, "Gigabyte BXBT-2807", ALC283_FIXUP_BXBT2807_MIC),
|
||||
|
@ -5975,6 +5994,11 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
|
|||
{0x12, 0xb7a60130},
|
||||
{0x14, 0x90170110},
|
||||
{0x21, 0x02211020}),
|
||||
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
|
||||
{0x12, 0x90a60130},
|
||||
{0x14, 0x90170110},
|
||||
{0x14, 0x01011020},
|
||||
{0x21, 0x0221101f}),
|
||||
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
|
||||
ALC256_STANDARD_PINS),
|
||||
SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC280_FIXUP_HP_GPIO4,
|
||||
|
@ -6031,6 +6055,10 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
|
|||
{0x12, 0x90a60120},
|
||||
{0x14, 0x90170110},
|
||||
{0x21, 0x0321101f}),
|
||||
SND_HDA_PIN_QUIRK(0x10ec0289, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
|
||||
{0x12, 0xb7a60130},
|
||||
{0x14, 0x90170110},
|
||||
{0x21, 0x04211020}),
|
||||
SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
|
||||
ALC290_STANDARD_PINS,
|
||||
{0x15, 0x04211040},
|
||||
|
|
|
@ -355,17 +355,20 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request,
|
|||
int validx, int *value_ret)
|
||||
{
|
||||
struct snd_usb_audio *chip = cval->head.mixer->chip;
|
||||
unsigned char buf[4 + 3 * sizeof(__u32)]; /* enough space for one range */
|
||||
/* enough space for one range */
|
||||
unsigned char buf[sizeof(__u16) + 3 * sizeof(__u32)];
|
||||
unsigned char *val;
|
||||
int idx = 0, ret, size;
|
||||
int idx = 0, ret, val_size, size;
|
||||
__u8 bRequest;
|
||||
|
||||
val_size = uac2_ctl_value_size(cval->val_type);
|
||||
|
||||
if (request == UAC_GET_CUR) {
|
||||
bRequest = UAC2_CS_CUR;
|
||||
size = uac2_ctl_value_size(cval->val_type);
|
||||
size = val_size;
|
||||
} else {
|
||||
bRequest = UAC2_CS_RANGE;
|
||||
size = sizeof(buf);
|
||||
size = sizeof(__u16) + 3 * val_size;
|
||||
}
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
@ -398,16 +401,17 @@ error:
|
|||
val = buf + sizeof(__u16);
|
||||
break;
|
||||
case UAC_GET_MAX:
|
||||
val = buf + sizeof(__u16) * 2;
|
||||
val = buf + sizeof(__u16) + val_size;
|
||||
break;
|
||||
case UAC_GET_RES:
|
||||
val = buf + sizeof(__u16) * 3;
|
||||
val = buf + sizeof(__u16) + val_size * 2;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*value_ret = convert_signed_value(cval, snd_usb_combine_bytes(val, sizeof(__u16)));
|
||||
*value_ret = convert_signed_value(cval,
|
||||
snd_usb_combine_bytes(val, val_size));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -343,6 +343,15 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs,
|
|||
ep = 0x81;
|
||||
iface = usb_ifnum_to_if(dev, 2);
|
||||
|
||||
if (!iface || iface->num_altsetting == 0)
|
||||
return -EINVAL;
|
||||
|
||||
alts = &iface->altsetting[1];
|
||||
goto add_sync_ep;
|
||||
case USB_ID(0x1397, 0x0002):
|
||||
ep = 0x81;
|
||||
iface = usb_ifnum_to_if(dev, 1);
|
||||
|
||||
if (!iface || iface->num_altsetting == 0)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue