From 42dcc2cf34436f9f424b29097b844679616d7d05 Mon Sep 17 00:00:00 2001 From: Nan Li Date: Wed, 22 Feb 2017 20:04:55 +0800 Subject: [PATCH] unifykey: add key manage driver support PD#138714: initial add unifykey driver Change-Id: Iab32caa7b2b8921f8c9dfb66de339cf4cc04477c Signed-off-by: Nan Li --- MAINTAINERS | 15 + arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts | 96 ++ arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts | 95 ++ arch/arm64/boot/dts/amlogic/gxm_q200_2g.dts | 94 ++ arch/arm64/configs/meson64_defconfig | 1 + drivers/amlogic/Kconfig | 2 + drivers/amlogic/Makefile | 2 + drivers/amlogic/key_manage/Kconfig | 15 + drivers/amlogic/key_manage/Makefile | 4 + drivers/amlogic/key_manage/amlkey_if.h | 79 ++ drivers/amlogic/key_manage/storagekey.c | 368 +++++ drivers/amlogic/key_manage/unifykey.c | 1419 +++++++++++++++++++ drivers/amlogic/key_manage/unifykey.h | 97 ++ drivers/amlogic/key_manage/unifykey_dts.c | 271 ++++ drivers/amlogic/mmc/Makefile | 2 +- drivers/amlogic/mmc/emmc_key.c | 212 +++ drivers/amlogic/mmc/emmc_key.h | 74 + drivers/amlogic/mmc/emmc_partitions.c | 4 + include/linux/amlogic/key_manage.h | 37 + 19 files changed, 2886 insertions(+), 1 deletion(-) create mode 100644 drivers/amlogic/key_manage/Kconfig create mode 100644 drivers/amlogic/key_manage/Makefile create mode 100644 drivers/amlogic/key_manage/amlkey_if.h create mode 100644 drivers/amlogic/key_manage/storagekey.c create mode 100644 drivers/amlogic/key_manage/unifykey.c create mode 100644 drivers/amlogic/key_manage/unifykey.h create mode 100644 drivers/amlogic/key_manage/unifykey_dts.c create mode 100644 drivers/amlogic/mmc/emmc_key.c create mode 100644 drivers/amlogic/mmc/emmc_key.h create mode 100644 include/linux/amlogic/key_manage.h diff --git a/MAINTAINERS b/MAINTAINERS index e400308df2c1..6c4065abb988 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13639,3 +13639,18 @@ F: drivers\amlogic\securitykey\Kconfig F: drivers\amlogic\securitykey\Makefile F: drivers\amlogic\securitykey\securitykey.c F: include\linux\amlogic\security_key.h + +AMLOGIC key_manage +M: Nan Li +F: arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts +F: arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts +F: arch/arm64/boot/dts/amlogic/gxm_q200_2g.dts +F: arch/arm64/configs/meson64_defconfig +F: drivers/amlogic/Kconfig +F: drivers/amlogic/Makefile +F: drivers/amlogic/mmc/Makefile +F: drivers/amlogic/mmc/emmc_partitions.c +F: drivers/amlogic/key_manage/* +F: drivers/amlogic/mmc/emmc_key.c +F: drivers/amlogic/mmc/emmc_key.h +F: include/linux/amlogic/key_manage.h diff --git a/arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts b/arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts index 5771e5caeaa5..16c8a6e7390b 100644 --- a/arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts +++ b/arch/arm64/boot/dts/amlogic/gxl_p212_1g.dts @@ -714,6 +714,102 @@ */ tv_bit_mode = <1>; }; + + unifykey{ + compatible = "amlogic, unifykey"; + status = "ok"; + + unifykey-num = <14>; + unifykey-index-0 = <&keysn_0>; + unifykey-index-1 = <&keysn_1>; + unifykey-index-2 = <&keysn_2>; + unifykey-index-3 = <&keysn_3>; + unifykey-index-4 = <&keysn_4>; + unifykey-index-5 = <&keysn_5>; + unifykey-index-6 = <&keysn_6>; + unifykey-index-7 = <&keysn_7>; + unifykey-index-8 = <&keysn_8>; + unifykey-index-9 = <&keysn_9>; + unifykey-index-10= <&keysn_10>; + unifykey-index-11= <&keysn_11>; + unifykey-index-12= <&keysn_12>; + unifykey-index-13= <&keysn_13>; + + keysn_0: key_0{ + key-name = "usid"; + key-device = "normal"; + key-permit = "read","write","del"; + }; + keysn_1:key_1{ + key-name = "mac"; + key-device = "normal"; + key-permit = "read","write","del"; + }; + keysn_2:key_2{ + key-name = "hdcp"; + key-device = "secure"; + key-type = "sha1"; + key-permit = "read","write","del"; + }; + keysn_3:key_3{ + key-name = "secure_boot_set"; + key-device = "efuse"; + key-permit = "write"; + }; + keysn_4:key_4{ + key-name = "mac_bt"; + key-device = "normal"; + key-permit = "read","write","del"; + key-type = "mac"; + }; + keysn_5:key_5{ + key-name = "mac_wifi"; + key-device = "normal"; + key-permit = "read","write","del"; + key-type = "mac"; + }; + keysn_6:key_6{ + key-name = "hdcp2_tx"; + key-device = "normal"; + key-permit = "read","write","del"; + }; + keysn_7:key_7{ + key-name = "hdcp2_rx"; + key-device = "normal"; + key-permit = "read","write","del"; + }; + keysn_8:key_8{ + key-name = "widevinekeybox"; + key-device = "secure"; + key-permit = "read","write","del"; + }; + keysn_9:key_9{ + key-name = "deviceid"; + key-device = "normal"; + key-permit = "read","write","del"; + }; + keysn_10:key_10{ + key-name = "hdcp22_fw_private"; + key-device = "secure"; + key-permit = "read","write","del"; + }; + keysn_11:key_11{ + key-name = "PlayReadykeybox25"; + key-device = "secure"; + key-permit = "read","write","del"; + }; + keysn_12:key_12{ + key-name = "prpubkeybox";// PlayReady + key-device = "secure"; + key-permit = "read","write","del"; + }; + keysn_13:key_13{ + key-name = "prprivkeybox";// PlayReady + key-device = "secure"; + key-permit = "read","write","del"; + }; + };//End unifykey + }; &efuse { status = "ok"; diff --git a/arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts b/arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts index 3e1a7cc085ee..0bf441714aef 100644 --- a/arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts +++ b/arch/arm64/boot/dts/amlogic/gxl_p212_2g.dts @@ -712,6 +712,101 @@ */ tv_bit_mode = <1>; }; + + unifykey{ + compatible = "amlogic, unifykey"; + status = "ok"; + + unifykey-num = <14>; + unifykey-index-0 = <&keysn_0>; + unifykey-index-1 = <&keysn_1>; + unifykey-index-2 = <&keysn_2>; + unifykey-index-3 = <&keysn_3>; + unifykey-index-4 = <&keysn_4>; + unifykey-index-5 = <&keysn_5>; + unifykey-index-6 = <&keysn_6>; + unifykey-index-7 = <&keysn_7>; + unifykey-index-8 = <&keysn_8>; + unifykey-index-9 = <&keysn_9>; + unifykey-index-10= <&keysn_10>; + unifykey-index-11= <&keysn_11>; + unifykey-index-12= <&keysn_12>; + unifykey-index-13= <&keysn_13>; + + keysn_0: key_0{ + key-name = "usid"; + key-device = "normal"; + key-permit = "read","write","del"; + }; + keysn_1:key_1{ + key-name = "mac"; + key-device = "normal"; + key-permit = "read","write","del"; + }; + keysn_2:key_2{ + key-name = "hdcp"; + key-device = "secure"; + key-type = "sha1"; + key-permit = "read","write","del"; + }; + keysn_3:key_3{ + key-name = "secure_boot_set"; + key-device = "efuse"; + key-permit = "write"; + }; + keysn_4:key_4{ + key-name = "mac_bt"; + key-device = "normal"; + key-permit = "read","write","del"; + key-type = "mac"; + }; + keysn_5:key_5{ + key-name = "mac_wifi"; + key-device = "normal"; + key-permit = "read","write","del"; + key-type = "mac"; + }; + keysn_6:key_6{ + key-name = "hdcp2_tx"; + key-device = "normal"; + key-permit = "read","write","del"; + }; + keysn_7:key_7{ + key-name = "hdcp2_rx"; + key-device = "normal"; + key-permit = "read","write","del"; + }; + keysn_8:key_8{ + key-name = "widevinekeybox"; + key-device = "secure"; + key-permit = "read","write","del"; + }; + keysn_9:key_9{ + key-name = "deviceid"; + key-device = "normal"; + key-permit = "read","write","del"; + }; + keysn_10:key_10{ + key-name = "hdcp22_fw_private"; + key-device = "secure"; + key-permit = "read","write","del"; + }; + keysn_11:key_11{ + key-name = "PlayReadykeybox25"; + key-device = "secure"; + key-permit = "read","write","del"; + }; + keysn_12:key_12{ + key-name = "prpubkeybox";// PlayReady + key-device = "secure"; + key-permit = "read","write","del"; + }; + keysn_13:key_13{ + key-name = "prprivkeybox";// PlayReady + key-device = "secure"; + key-permit = "read","write","del"; + }; + };//End unifykey }; &efuse { status = "ok"; diff --git a/arch/arm64/boot/dts/amlogic/gxm_q200_2g.dts b/arch/arm64/boot/dts/amlogic/gxm_q200_2g.dts index c18d8dec6120..6320ea62e601 100644 --- a/arch/arm64/boot/dts/amlogic/gxm_q200_2g.dts +++ b/arch/arm64/boot/dts/amlogic/gxm_q200_2g.dts @@ -713,6 +713,100 @@ */ tv_bit_mode = <1>; }; + unifykey{ + compatible = "amlogic, unifykey"; + status = "ok"; + + unifykey-num = <14>; + unifykey-index-0 = <&keysn_0>; + unifykey-index-1 = <&keysn_1>; + unifykey-index-2 = <&keysn_2>; + unifykey-index-3 = <&keysn_3>; + unifykey-index-4 = <&keysn_4>; + unifykey-index-5 = <&keysn_5>; + unifykey-index-6 = <&keysn_6>; + unifykey-index-7 = <&keysn_7>; + unifykey-index-8 = <&keysn_8>; + unifykey-index-9 = <&keysn_9>; + unifykey-index-10= <&keysn_10>; + unifykey-index-11= <&keysn_11>; + unifykey-index-12= <&keysn_12>; + unifykey-index-13= <&keysn_13>; + + keysn_0: key_0{ + key-name = "usid"; + key-device = "normal"; + key-permit = "read","write","del"; + }; + keysn_1:key_1{ + key-name = "mac"; + key-device = "normal"; + key-permit = "read","write","del"; + }; + keysn_2:key_2{ + key-name = "hdcp"; + key-device = "secure"; + key-type = "sha1"; + key-permit = "read","write","del"; + }; + keysn_3:key_3{ + key-name = "secure_boot_set"; + key-device = "efuse"; + key-permit = "write"; + }; + keysn_4:key_4{ + key-name = "mac_bt"; + key-device = "normal"; + key-permit = "read","write","del"; + key-type = "mac"; + }; + keysn_5:key_5{ + key-name = "mac_wifi"; + key-device = "normal"; + key-permit = "read","write","del"; + key-type = "mac"; + }; + keysn_6:key_6{ + key-name = "hdcp2_tx"; + key-device = "normal"; + key-permit = "read","write","del"; + }; + keysn_7:key_7{ + key-name = "hdcp2_rx"; + key-device = "normal"; + key-permit = "read","write","del"; + }; + keysn_8:key_8{ + key-name = "widevinekeybox"; + key-device = "secure"; + key-permit = "read","write","del"; + }; + keysn_9:key_9{ + key-name = "deviceid"; + key-device = "normal"; + key-permit = "read","write","del"; + }; + keysn_10:key_10{ + key-name = "hdcp22_fw_private"; + key-device = "secure"; + key-permit = "read","write","del"; + }; + keysn_11:key_11{ + key-name = "PlayReadykeybox25"; + key-device = "secure"; + key-permit = "read","write","del"; + }; + keysn_12:key_12{ + key-name = "prpubkeybox";// PlayReady + key-device = "secure"; + key-permit = "read","write","del"; + }; + keysn_13:key_13{ + key-name = "prprivkeybox";// PlayReady + key-device = "secure"; + key-permit = "read","write","del"; + }; + };//End unifykey }; &efuse { status = "ok"; diff --git a/arch/arm64/configs/meson64_defconfig b/arch/arm64/configs/meson64_defconfig index 52a6e70f7147..9496ef593df1 100644 --- a/arch/arm64/configs/meson64_defconfig +++ b/arch/arm64/configs/meson64_defconfig @@ -219,6 +219,7 @@ CONFIG_AMLOGIC_MMC=y CONFIG_AMLOGIC_VRTC=y CONFIG_AMLOGIC_SMARTCARD=y CONFIG_AMLOGIC_SECURITY_KEY=y +CONFIG_AMLOGIC_KEY_MANAGE=y CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y diff --git a/drivers/amlogic/Kconfig b/drivers/amlogic/Kconfig index fe04b704b4c4..3fb882ab4e6e 100644 --- a/drivers/amlogic/Kconfig +++ b/drivers/amlogic/Kconfig @@ -61,5 +61,7 @@ source "drivers/amlogic/cec/Kconfig" source "drivers/amlogic/securitykey/Kconfig" +source "drivers/amlogic/key_manage/Kconfig" + endmenu endif diff --git a/drivers/amlogic/Makefile b/drivers/amlogic/Makefile index ea4b612902c2..ec2f084bf7c0 100644 --- a/drivers/amlogic/Makefile +++ b/drivers/amlogic/Makefile @@ -57,3 +57,5 @@ obj-$(CONFIG_AMLOGIC_AO_CEC) += cec/ obj-$(CONFIG_AMLOGIC_SECURITY_KEY) += securitykey/ +obj-$(CONFIG_AMLOGIC_KEY_MANAGE) += key_manage/ + diff --git a/drivers/amlogic/key_manage/Kconfig b/drivers/amlogic/key_manage/Kconfig new file mode 100644 index 000000000000..ad0f5b61c9bb --- /dev/null +++ b/drivers/amlogic/key_manage/Kconfig @@ -0,0 +1,15 @@ +# +# Amlogic key device configuration +# + +menu "key management Support" + +config AMLOGIC_KEY_MANAGE + bool "key management Driver" + default n + help + key management device driver. + y here, means select key manage + n here, means don't use key manage + +endmenu diff --git a/drivers/amlogic/key_manage/Makefile b/drivers/amlogic/key_manage/Makefile new file mode 100644 index 000000000000..a6e69c0f5914 --- /dev/null +++ b/drivers/amlogic/key_manage/Makefile @@ -0,0 +1,4 @@ +# +# Makefile for unifykey +# +obj-$(CONFIG_AMLOGIC_KEY_MANAGE) += unifykey.o unifykey_dts.o storagekey.o diff --git a/drivers/amlogic/key_manage/amlkey_if.h b/drivers/amlogic/key_manage/amlkey_if.h new file mode 100644 index 000000000000..ef62131d0f59 --- /dev/null +++ b/drivers/amlogic/key_manage/amlkey_if.h @@ -0,0 +1,79 @@ +/* + * drivers/amlogic/key_manage/amlkey_if.h + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 __AMLKEY_IF_H__ +#define __AMLKEY_IF_H__ + +#define AMLKEY_NAME_LEN_MAX (80) + +/* + * init + */ +int32_t amlkey_init(uint8_t *seed, uint32_t len); +/* + * query if the key already programmed + * exsit 1, non 0 + */ +int32_t amlkey_isexsit(const uint8_t *name); +/* + * query if the prgrammed key is secure + * secure 1, non 0 + */ +int32_t amlkey_issecure(const uint8_t *name); +/* + * query if the prgrammed key is encrypt + * return encrypt 1, non 0; + */ +int32_t amlkey_isencrypt(const uint8_t *name); +/* + * actual bytes of key value + */ +ssize_t amlkey_size(const uint8_t *name); +/* + * read non-secure key in bytes, return byets readback actully. + */ +ssize_t amlkey_read(const uint8_t *name, uint8_t *buffer, uint32_t len); + +/* + * write key with attr in bytes , return bytes readback actully + * attr: bit0, secure/non-secure + * bit8, encrypt/non-encrypt + */ +ssize_t amlkey_write(const uint8_t *name, + uint8_t *buffer, + uint32_t len, + uint32_t attr); + +/* + * get the hash value of programmed secure key | 32bytes length, sha256 + */ +int32_t amlkey_hash_4_secure(const uint8_t *name, uint8_t *hash); + +extern int32_t nand_key_read(uint8_t *buf, + uint32_t len, uint32_t *actual_length); + +extern int32_t nand_key_write(uint8_t *buf, + uint32_t len, uint32_t *actual_length); + +extern int32_t emmc_key_read(uint8_t *buf, + uint32_t len, uint32_t *actual_length); + +extern int32_t emmc_key_write(uint8_t *buf, + uint32_t len, uint32_t *actual_length); + +#endif + diff --git a/drivers/amlogic/key_manage/storagekey.c b/drivers/amlogic/key_manage/storagekey.c new file mode 100644 index 000000000000..36b41090353c --- /dev/null +++ b/drivers/amlogic/key_manage/storagekey.c @@ -0,0 +1,368 @@ +/* + * drivers/amlogic/key_manage/storagekey.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + */ + +/* extern from bl31 */ +/* + * when RET_OK + * query: retval=1: key exsit,=0: key not exsit; + * tell: retvak = key size + * status: retval=1: secure, retval=0: non-secure + + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "unifykey.h" +#include "amlkey_if.h" +/* #include */ + +/* key buffer status */ +/* bit0, dirty flag*/ +#define KEYBUFFER_CLEAN (0 << 0) +#define KEYBUFFER_DIRTY (1 << 0) +#define SECUESTORAGE_HEAD_SIZE (256) +#define SECUESTORAGE_WHOLE_SIZE (0x40000) + +#define OTHER_METHOD_CALL + +struct storagekey_info_t { + uint8_t *buffer; + uint32_t size; + uint32_t status; +}; + +static struct storagekey_info_t storagekey_info = { + .buffer = NULL, + /* default size */ + .size = SECUESTORAGE_WHOLE_SIZE, + .status = KEYBUFFER_CLEAN, +}; + + +store_key_ops store_key_read; +store_key_ops store_key_write; + +#ifndef OTHER_METHOD_CALL +int store_operation_init(void) +{ + int ret = 0; + + if (kallsyms_lookup_name("nand_key_read")) { + pr_info(" %s() nand storeage ops!\n", __func__); + store_key_read = nand_key_read; + store_key_write = nand_key_write; + } else if (kallsyms_lookup_name("emmc_key_read")) { + pr_info(" %s() emmc storeage ops!\n", __func__); + store_key_read = emmc_key_read; + store_key_write = emmc_key_write; + } else { + ret = -1; + pr_err(" %s() fail!\n", __func__); + } + + return ret; +} +#endif + +void storage_ops_read(store_key_ops read) +{ +#ifdef OTHER_METHOD_CALL + store_key_read = read; +#endif +} +EXPORT_SYMBOL(storage_ops_read); + +void storage_ops_write(store_key_ops write) +{ +#ifdef OTHER_METHOD_CALL + store_key_write = write; +#endif +} +EXPORT_SYMBOL(storage_ops_write); + +/** + *1.init + * return ok 0, fail 1 + */ +int32_t amlkey_init(uint8_t *seed, uint32_t len) +{ + int32_t ret = 0; + uint32_t buffer_size, actual_size; + +#ifndef OTHER_METHOD_CALL + ret = store_operation_init(); + if (ret < 0) { + ret = -1; + pr_err(" %s store_operation_init fail!\n", __func__); + goto _out; + } +#endif + /* do nothing for now*/ + pr_info("%s() enter!\n", __func__); + if (storagekey_info.buffer != NULL) { + pr_err("%s() %d: already init!\n", __func__, __LINE__); + goto _out; + } + + /* get buffer from bl31 */ + storagekey_info.buffer = secure_storage_getbuffer(&buffer_size); + if (storagekey_info.buffer == NULL) { + pr_err("%s() %d: can't get buffer from bl31!\n", + __func__, __LINE__); + ret = -1; + goto _out; + } + + /* full fill key infos from storage. */ + if (store_key_read) + ret = store_key_read(storagekey_info.buffer, + storagekey_info.size, &actual_size); + + storagekey_info.size = actual_size; + pr_info("%s() storagekey_info.buffer=%p, storagekey_info.size = %0x!\n", + __func__, + storagekey_info.buffer, + storagekey_info.size); + + if (ret) { + /* memset head info for bl31 */ + memset(storagekey_info.buffer, 0, SECUESTORAGE_HEAD_SIZE); + ret = 0; + goto _out; + } +_out: + return ret; +} + +/** + *2. query if the key already programmed + * return: exsit 1, non 0 + */ +int32_t amlkey_isexsit(const uint8_t *name) +{ + int32_t ret = 0; + uint32_t retval; + + if (name == NULL) { + pr_err("%s() %d, invalid key ", __func__, __LINE__); + return 0; + } + + ret = secure_storage_query((uint8_t *)name, &retval); + if (ret) { + pr_err("%s() %d: ret %d\n", __func__, __LINE__, ret); + retval = 0; + } + + return (int32_t)retval; +} + +/** + * 3. query if the prgrammed key is secure + * return secure 1, non 0; + */ +int32_t amlkey_get_attr(const uint8_t *name) +{ + int32_t ret = 0; + uint32_t retval; + + if (name == NULL) { + pr_err("%s() %d, invalid key ", __func__, __LINE__); + return 0; + } + + ret = secure_storage_status((uint8_t *)name, &retval); + if (ret) { + pr_err("%s() %d: ret %d\n", __func__, __LINE__, ret); + retval = 0; + } + + return (int32_t)retval; +} + +/** + * 3.1 query if the prgrammed key is secure + * return secure 1, non 0; + */ +int32_t amlkey_issecure(const uint8_t *name) +{ + return amlkey_get_attr(name) & KEY_UNIFY_ATTR_SECURE_MASK; +} + +/** + * 3.2 query if the prgrammed key is encrypt + * return encrypt 1, non 0; + */ +int32_t amlkey_isencrypt(const uint8_t *name) +{ + return amlkey_get_attr(name) & KEY_UNIFY_ATTR_ENCRYPT_MASK; +} + +/** + * 4. actual bytes of key value + * return actual size. + */ +ssize_t amlkey_size(const uint8_t *name) +{ + ssize_t size = 0; + int32_t ret = 0; + uint32_t retval; + + if (name == NULL) { + pr_err("%s() %d, invalid key ", __func__, __LINE__); + return 0; + } + + ret = secure_storage_tell((uint8_t *)name, &retval); + if (ret) { + pr_err("%s() %d: ret %d\n", __func__, __LINE__, ret); + retval = 0; + } + size = (ssize_t)retval; + return size; +} + +/** + *5. read non-secure key in bytes, return bytes readback actully. + * return actual size read back. + */ +ssize_t amlkey_read(const uint8_t *name, uint8_t *buffer, uint32_t len) +{ + int32_t ret = 0; + ssize_t retval = 0; + uint32_t actul_len; + + if (name == NULL) { + pr_err("%s() %d, invalid key ", __func__, __LINE__); + return 0; + } + ret = secure_storage_read((uint8_t *)name, buffer, len, &actul_len); + if (ret) { + pr_err("%s() %d: return %d\n", __func__, __LINE__, ret); + retval = 0; + goto _out; + } + retval = actul_len; +_out: + return retval; +} + +/** + * 6.write key with attr in bytes , return bytes readback actully + * attr: bit0, secure/non-secure + * bit8, encrypt/non-encrypt + * return actual size write down. + */ +ssize_t amlkey_write(const uint8_t *name, + uint8_t *buffer, + uint32_t len, + uint32_t attr) +{ + int32_t ret = 0; + ssize_t retval = 0; + uint32_t actual_length; + uint8_t *buf = NULL; + + if (name == NULL) { + pr_err("%s() %d, invalid key ", __func__, __LINE__); + return retval; + } + + pr_info("%s %d\n", __func__, __LINE__); + ret = secure_storage_write((uint8_t *)name, + buffer, len, + attr); + if (ret) { + pr_err("%s() %d: return %d\n", __func__, __LINE__, ret); + retval = 0; + goto _out; + } else { + retval = (ssize_t)len; + /* write down! */ + if (storagekey_info.buffer != NULL) { + buf = kzalloc(storagekey_info.size, GFP_KERNEL); + memcpy(buf, storagekey_info.buffer, + storagekey_info.size); + if (store_key_write) + ret = store_key_write(buf, + storagekey_info.size, + &actual_length); + if (ret) { + pr_err("%s() %d, store_key_write fail\n", + __func__, __LINE__); + retval = 0; + } + kfree(buf); + } + } +_out: + return retval; +} +/** + * 7. get the hash value of programmed secure key | 32bytes length, sha256 + * return success 0, fail -1 + */ +int32_t amlkey_hash_4_secure(const uint8_t *name, uint8_t *hash) +{ + int32_t ret = 0; + + ret = secure_storage_verify((uint8_t *)name, hash); + + return ret; +} + +/** + * 7. del key by name + * return success 0, fail -1 + */ +int32_t amlkey_del(const uint8_t *name) +{ + int32_t ret = 0; + uint32_t actual_length; + + if ((ret == 0) && (storagekey_info.buffer != NULL)) { + /* flush back */ + if (store_key_write) + ret = store_key_write(storagekey_info.buffer, + storagekey_info.size, &actual_length); + if (ret) { + pr_err("%s() %d, store_key_write fail\n", + __func__, + __LINE__); + } + } else { + pr_err("%s() %d, remove key fail\n", + __func__, + __LINE__); + } + + return ret; +} + + diff --git a/drivers/amlogic/key_manage/unifykey.c b/drivers/amlogic/key_manage/unifykey.c new file mode 100644 index 000000000000..dcae7bf0b626 --- /dev/null +++ b/drivers/amlogic/key_manage/unifykey.c @@ -0,0 +1,1419 @@ +/* + * drivers/amlogic/key_manage/unifykey.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "unifykey.h" +#include "amlkey_if.h" + +#define UNIFYKEYS_MODULE_NAME "aml_keys-t" +#define UNIFYKEYS_DRIVER_NAME "aml_keys-t" +#define UNIFYKEYS_DEVICE_NAME "unifykeys" +#define UNIFYKEYS_CLASS_NAME "unifykeys" + +#define KEY_NO_EXIST 0 +#define KEY_BURNED 1 + +#define KEY_READ_PERMIT 10 +#define KEY_READ_PROHIBIT 11 + +#define KEY_WRITE_MASK (0x0f<<4) +#define KEY_WRITE_PERMIT (10<<4) +#define KEY_WRITE_PROHIBIT (11<<4) + +#define SHA256_SUM_LEN 32 +#define DBG_DUMP_DATA (0) +static struct unifykey_dev_t *unifykey_devp; +static dev_t unifykey_devno; +static struct device *unifykey_device; + +struct key_item_t *curkey; +typedef int (*key_unify_dev_init)(char *buf, unsigned int len); +typedef int (*key_unify_dev_uninit)(void); + +static int init_flag; +static int module_init_flag; +static int lock_flag; + +static int key_storage_init(char *buf, unsigned int len) +{ + /* fixme, todo. */ + return amlkey_init((uint8_t *)buf, len); + +} + +static int key_storage_size(char *keyname) +{ + return amlkey_size((uint8_t *)keyname); +} + +static int key_storage_write(char *keyname, unsigned char *keydata, + unsigned int datalen, int flag) +{ + int ret = 0; + ssize_t writenLen = 0; + /* fixme, todo write down this. */ + + pr_info("%s %d\n", __func__, __LINE__); + pr_info("%s, %s, %d\n", keyname, keydata, datalen); + writenLen = amlkey_write((uint8_t *)keyname, + (uint8_t *)keydata, + datalen, + flag); + pr_info("%s %d\n", __func__, __LINE__); + if (writenLen != datalen) { + pr_err("Want to write %u bytes, but only %zd Bytes\n", + datalen, + writenLen); + return -EINVAL; + } + + return ret; +} + +static int key_secure_read_hash(char *keyname, unsigned char *keydata, + unsigned int datalen, unsigned int *reallen, int flag) +{ + int ret; + + ret = amlkey_hash_4_secure((uint8_t *)keyname, keydata); + if (ret) { + pr_err("Failed when gen hash for sec-key[%s],ret=%d\n", + keyname, + ret); + return -EINVAL; + } + *reallen = SHA256_SUM_LEN; + return ret; +} + +static int key_storage_read(char *keyname, unsigned char *keydata, + unsigned int datalen, unsigned int *reallen, int flag) +{ + int ret = 0; + ssize_t readLen = 0; + int encrypt, encrypt_dts; + struct key_item_t *unifykey; + /* fixme, todo */ + + ret = amlkey_issecure((uint8_t *)keyname); + if (ret) { + pr_err("key[%s] can't read, is configured secured?\n", keyname); + return -EINVAL; + } + + /* make sure attr in storage & dts are the same! */ + encrypt = amlkey_isencrypt((uint8_t *)keyname); + + unifykey = unifykey_find_item_by_name(keyname); + if (unifykey == NULL) { + pr_err("%s:%d,%s key name is not exist\n", + __func__, __LINE__, keyname); + return -EINVAL; + } + encrypt_dts = (unifykey->attr & KEY_UNIFY_ATTR_ENCRYPT_MASK) ? + 1:0; + if (encrypt != encrypt_dts) { + pr_err("key[%s] can't read, encrypt?\n", keyname); + return -EINVAL; + } + + readLen = amlkey_read((uint8_t *)keyname, + (uint8_t *)keydata, + datalen); + if (readLen != datalen) { + pr_err("key[%s], want read %u Bytes, but %zd bytes\n", + keyname, + datalen, + readLen); + return -EINVAL; + } + *reallen = readLen; + return ret; +} + +static int key_storage_query(char *keyname, unsigned int *keystate) +{ + int ret = 0; + /*fixme, todo */ + ret = amlkey_isexsit((uint8_t *)keyname); + if (ret) + *keystate = 1; + else + *keystate = 0; + + return ret; +} + +static int key_efuse_init(char *buf, unsigned int len) +{ + char ver; + + ver = unifykey_get_efuse_version(); + return 0; +} + +static int key_efuse_write(char *keyname, unsigned char *keydata, + unsigned int datalen) +{ +#ifdef CONFIG_EFUSE + char *title = keyname; + struct efusekey_info info; + + if (efuse_getinfo(title, &info) < 0) + return -EINVAL; + + if (efuse_user_attr_store(keyname, + (const char *)keydata, (size_t)datalen) < 0) { + pr_err("error: efuse write fail.\n"); + return -1; + } + + pr_err("%s written done.\n", info.keyname); + return 0; +#else + return -EINVAL; +#endif +} + +static int key_efuse_read(char *keyname, unsigned char *keydata, + unsigned int datalen, unsigned int *reallen) +{ +#ifdef CONFIG_EFUSE + char *title = keyname; + struct efusekey_info info; + int err = 0; + char *buf; + + if (efuse_getinfo(title, &info) < 0) + return -EINVAL; + + buf = kzalloc(info.size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + memset(buf, 0, info.size); + + err = efuse_user_attr_show(title, buf); + if (err >= 0) { + *reallen = info.size; + if (datalen > info.size) + datalen = info.size; + memcpy(keydata, buf, datalen); + } + kfree(buf); + return err; +#else + return -EINVAL; +#endif +} + +static int key_efuse_query(char *keyname, unsigned int *keystate) +{ + int err = -EINVAL; +#ifdef CONFIG_EFUSE + int i; + char *title = keyname; + struct efusekey_info info; + char *buf; + + if (efuse_getinfo(title, &info) < 0) + return -EINVAL; + buf = kzalloc(info.size, GFP_KERNEL); + if (buf == NULL) { + pr_err("%s:%d,kzalloc mem fail\n", __func__, __LINE__); + return -ENOMEM; + } + memset(buf, 0, info.size); + err = efuse_user_attr_show(title, buf); + *keystate = KEY_NO_EXIST; + if (err > 0) { + for (i = 0; i < info.size; i++) { + if (buf[i] != 0) { + *keystate = KEY_BURNED; + break; + } + } + } + kfree(buf); +#endif + return err; +} + +int key_unify_init(char *buf, unsigned int len) +{ + int bakerr, err = -EINVAL; + char *dev_node[] + = {"unknown", "efuse", "normal", "secure"}; + + key_unify_dev_init dev_initfunc[] = {NULL, key_efuse_init, + key_storage_init}; + int i, cnt; + + if (init_flag == 1) { + pr_err(" %s() already inited!\n", __func__); + return 0; + } + + bakerr = 0; + cnt = ARRAY_SIZE(dev_initfunc); + for (i = 0; i < cnt; i++) { + if (dev_initfunc[i]) { + err = dev_initfunc[i](buf, len); + if (err < 0) { + pr_err("%s:%d,%s device ini fail\n", + __func__, __LINE__, dev_node[i]); + bakerr = err; + } + } + } + /* fixme, */ + init_flag = 1; + + return bakerr; +} +EXPORT_SYMBOL(key_unify_init); + +/* + * function name: key_unify_write + * keyname : key name is ascii string + * keydata : key data buf + * datalen : key buf len + * return 0: ok, -0x1fe: no space, other fail + */ +int key_unify_write(char *keyname, unsigned char *keydata, + unsigned int datalen) +{ + int err = 0; + int attr; + struct key_item_t *unifykey; + + unifykey = unifykey_find_item_by_name(keyname); + if (unifykey == NULL) { + pr_err("%s:%d,%s key name is not exist\n", + __func__, __LINE__, keyname); + return -EINVAL; + } + if (unifykey_item_verify_check(unifykey)) { + pr_err("%s:%d,%s key name is invalid\n", + __func__, __LINE__, keyname); + return -EINVAL; + } + if (unifykey->permit & KEY_M_PERMIT_WRITE) { + err = -EINVAL; + + switch (unifykey->dev) { + case KEY_M_EFUSE: + err = key_efuse_write(keyname, keydata, datalen); + break; + case KEY_M_SECURE: + case KEY_M_NORMAL: + attr = ((unifykey->dev == KEY_M_SECURE) ? + KEY_UNIFY_ATTR_SECURE : 0); + attr |= (unifykey->attr & KEY_UNIFY_ATTR_ENCRYPT_MASK) ? + KEY_UNIFY_ATTR_ENCRYPT : 0; + err = key_storage_write(keyname, keydata, + datalen, attr); + break; + case KEY_M_UNKNOWN_DEV: + default: + pr_err("%s:%d,%s key not know device\n", + __func__, __LINE__, keyname); + break; + } + } + return err; +} +EXPORT_SYMBOL(key_unify_write); + +/* + *function name: key_unify_read + * keyname : key name is ascii string + * keydata : key data buf + * datalen : key buf len + * reallen : key real len + * return : <0 fail, >=0 ok + */ +int key_unify_read(char *keyname, unsigned char *keydata, + unsigned int datalen, unsigned int *reallen) +{ + int err = 0; + struct key_item_t *unifykey; + + if (!keydata) { + pr_err("%s:%d, keydata is NULL\n", + __func__, __LINE__); + return -EINVAL; + } + + unifykey = unifykey_find_item_by_name(keyname); + if (unifykey == NULL) { + pr_err("%s:%d,%s key name is not exist\n", + __func__, __LINE__, keyname); + return -EINVAL; + } + if (unifykey_item_verify_check(unifykey)) { + pr_err("%s:%d,%s key name is invalid\n", + __func__, __LINE__, keyname); + return -EINVAL; + } + if (unifykey->permit & KEY_M_PERMIT_READ) { + err = -EINVAL; + + switch (unifykey->dev) { + case KEY_M_EFUSE: + err = key_efuse_read(keyname, keydata, + datalen, reallen); + break; + case KEY_M_SECURE: + err = key_secure_read_hash(keyname, keydata, + datalen, reallen, 1); + break; + case KEY_M_NORMAL: + err = key_storage_read(keyname, keydata, + datalen, reallen, 0); + break; + case KEY_M_UNKNOWN_DEV: + default: + pr_err("%s:%d,%s key not know device\n", + __func__, __LINE__, keyname); + break; + } + } + return err; +} +EXPORT_SYMBOL(key_unify_read); + + +/* + *function name: key_unify_size + * keyname : key name is ascii string + * reallen : key real len, only valid while return ok. + * return : <0 fail, >=0 ok + */ +int key_unify_size(char *keyname, unsigned int *reallen) +{ + int err = 0; + struct key_item_t *unifykey; + + unifykey = unifykey_find_item_by_name(keyname); + if (unifykey == NULL) { + pr_err("%s:%d,%s key name is not exist\n", + __func__, + __LINE__, + keyname); + return -EINVAL; + } + + if (unifykey_item_verify_check(unifykey)) { + pr_err("%s:%d,%s key name is invalid\n", + __func__, + __LINE__, + keyname); + return -EINVAL; + } + + if (unifykey->permit & KEY_M_PERMIT_READ) { + switch (unifykey->dev) { + case KEY_M_EFUSE: + { + struct efusekey_info info; + + if (efuse_getinfo(keyname, &info) < 0) + return -EINVAL; + *reallen = info.size; + break; + } + case KEY_M_SECURE: + *reallen = key_storage_size(keyname); + break; + case KEY_M_NORMAL: + *reallen = key_storage_size(keyname); + break; + case KEY_M_UNKNOWN_DEV: + default: + pr_err("%s:%d,%s key not know device\n", + __func__, __LINE__, keyname); + break; + } + } + + return err; +} +EXPORT_SYMBOL(key_unify_size); + +/* + * key_unify_query - query whether key was burned. + * @keyname : key name will be queried. + * @keystate: query state value, + * 0: key was NOT burned; + * 1: key was burned; others: reserved. + * keypermit: + * return: 0: successful; others: failed. + */ +int key_unify_query(char *keyname, unsigned int *keystate, + unsigned int *keypermit) +{ + int err = 0; + struct key_item_t *unifykey; + + unifykey = unifykey_find_item_by_name(keyname); + if (unifykey == NULL) { + pr_err("%s:%d,%s key name is not exist\n", + __func__, __LINE__, keyname); + return -EINVAL; + } + if (unifykey_item_verify_check(unifykey)) { + pr_err("%s:%d,%s key name is invalid\n", + __func__, __LINE__, keyname); + return -EINVAL; + } + if (unifykey->permit & KEY_M_PERMIT_READ) { + err = -EINVAL; + switch (unifykey->dev) { + case KEY_M_EFUSE: + err = key_efuse_query(keyname, keystate); + *keypermit = KEY_READ_PERMIT; + if (err >= 0) { + if (*keystate == KEY_BURNED) + *keypermit |= KEY_WRITE_PROHIBIT; + else if (*keystate == KEY_NO_EXIST) + *keypermit |= KEY_WRITE_PERMIT; + } + break; + case KEY_M_SECURE: + err = key_storage_query(keyname, keystate); + *keypermit = KEY_READ_PROHIBIT; + *keypermit |= KEY_WRITE_PERMIT; + break; + case KEY_M_NORMAL: + err = key_storage_query(keyname, keystate); + *keypermit = KEY_READ_PERMIT; + *keypermit |= KEY_WRITE_PERMIT; + break; + case KEY_M_UNKNOWN_DEV: + pr_err("%s:%d,%s key not know device\n", + __func__, __LINE__, keyname); + default: + break; + } + } + return err; +} +EXPORT_SYMBOL(key_unify_query); + + +/* + *function name: key_unify_encrypt + * keyname : key name is ascii string + * encrypt : key encrypt status. + * return : <0 fail, >=0 ok + */ +int key_unify_encrypt(char *keyname, unsigned int *encrypt) +{ + int ret = 0; + struct key_item_t *unifykey; + unsigned int keystate, keypermit; + + unifykey = unifykey_find_item_by_name(keyname); + if (unifykey == NULL) { + pr_err("%s:%d,%s key name is not exist\n", + __func__, + __LINE__, + keyname); + return -EINVAL; + } + + if (unifykey_item_verify_check(unifykey)) { + pr_err("%s:%d,%s key name is invalid\n", + __func__, + __LINE__, + keyname); + return -EINVAL; + } + + /* check key burned or not */ + ret = key_unify_query(unifykey->name, &keystate, &keypermit); + if (ret < 0) { + pr_err("%s:%d, key_unify_query failed!\n", + __func__, __LINE__); + return -EINVAL; + } + + *encrypt = 0; + /* if burned, ask bl31, else using dts */ + if (keystate) { + ret = amlkey_isencrypt(unifykey->name); + if (ret < 0) + goto _out; + *encrypt = ret; + } else { + if (unifykey->attr & KEY_UNIFY_ATTR_ENCRYPT_MASK) + *encrypt = 1; + } + +_out: + return ret; +} +EXPORT_SYMBOL(key_unify_encrypt); + +int key_unify_uninit(void) +{ + int bakerr, err = -EINVAL; + int i, cnt; + key_unify_dev_uninit dev_uninit[] = {NULL}; + + bakerr = 0; + cnt = ARRAY_SIZE(dev_uninit); + for (i = 0; i < cnt; i++) { + if (dev_uninit[i]) { + err = dev_uninit[i](); + if (err) + bakerr = err; + } + } + return bakerr; +} +EXPORT_SYMBOL(key_unify_uninit); + +int key_unify_get_init_flag(void) +{ + return module_init_flag; +} +EXPORT_SYMBOL(key_unify_get_init_flag); + +static int unifykey_open(struct inode *inode, struct file *file) +{ + struct unifykey_dev_t *devp; + + devp = container_of(inode->i_cdev, struct unifykey_dev_t, cdev); + file->private_data = devp; + return 0; +} + +static int unifykey_release(struct inode *inode, struct file *file) +{ + struct unifykey_dev_t *devp; + + devp = file->private_data; + return 0; +} + +static loff_t unifykey_llseek(struct file *filp, loff_t off, int whence) +{ + loff_t newpos; + + switch (whence) { + case 0: /* SEEK_SET (start postion)*/ + newpos = off; + break; + + case 1: /* SEEK_CUR */ + newpos = filp->f_pos + off; + break; + + case 2: /* SEEK_END */ + newpos = (loff_t)unifykey_count_key() - 1; + newpos = newpos - off; + break; + + default: /* can't happen */ + return -EINVAL; + } + + if (newpos < 0) + return -EINVAL; + if (newpos >= (loff_t)unifykey_count_key()) + return -EINVAL; + filp->f_pos = newpos; + + return newpos; + +} + +static long unifykey_unlocked_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + switch (cmd) { + case KEYUNIFY_ATTACH: + { + struct key_item_t *appitem; + char initvalue[KEY_UNIFY_NAME_LEN]; + int ret; + + appitem = (struct key_item_t *)arg; + memcpy(initvalue, appitem->name, KEY_UNIFY_NAME_LEN); + ret = key_unify_init(initvalue, KEY_UNIFY_NAME_LEN); + if (ret < 0) { + pr_err("%s:%d,key unify init fail\n", + __func__, __LINE__); + return ret; + } + } + break; + case KEYUNIFY_GET_INFO: + { + /* struct key_item_info_t*/ + unsigned int index, reallen; + unsigned int keypermit, keystate; + struct key_item_t *kkey; + struct key_item_info_t *key_item_info; + char *keyname; + int ret; + + key_item_info = (struct key_item_info_t *)arg; + index = key_item_info->id; + keyname = key_item_info->name; + if (strlen(keyname)) + kkey = unifykey_find_item_by_name(keyname); + else + kkey = unifykey_find_item_by_id(index); + if (kkey == NULL) { + pr_err("%s() %d, find name fail\n", + __func__, __LINE__); + return -ENOTTY; + } + pr_err("%s() %d, %d, %s\n", __func__, + __LINE__, kkey->id, kkey->name); + + ret = key_unify_query(kkey->name, + &keystate, &keypermit); + if (ret < 0) { + pr_err("%s() %d, get size fail\n", + __func__, __LINE__); + return -EFAULT; + } + key_item_info->permit = keypermit; + key_item_info->flag = keystate; + key_item_info->id = kkey->id; + strncpy(key_item_info->name, + kkey->name, strlen(kkey->name)); + ret = key_unify_size(kkey->name, &reallen); + if (ret < 0) { + pr_err("%s() %d, get size fail\n", + __func__, __LINE__); + return -EFAULT; + } + /* set key info */ + key_item_info->size = reallen; + + return 0; + } + break; + default: + pr_err("%s() %d\n", __func__, __LINE__); + return -ENOTTY; + } + return 0; +} + +#ifdef CONFIG_COMPAT +static long unifykey_compat_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + return unifykey_unlocked_ioctl(file, cmd, + (unsigned long) compat_ptr(arg)); +} +#endif + +static ssize_t unifykey_read(struct file *file, + char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret; + int id; + unsigned int reallen; + struct key_item_t *item; + char *local_buf = NULL; + + id = (int)(*ppos); + item = unifykey_find_item_by_id(id); + if (!item) { + ret = -EINVAL; + goto exit; + } + + if (item->dev == KEY_M_SECURE) + count = SHA256_SUM_LEN; + local_buf = kzalloc(count, GFP_KERNEL); + if (!local_buf) { + pr_err("memory not enough,%s:%d\n", + __func__, __LINE__); + return -ENOMEM; + } + + ret = key_unify_read(item->name, local_buf, count, &reallen); + if (ret < 0) + goto exit; + if (count > reallen) + count = reallen; + if (copy_to_user((void *)buf, (void *)local_buf, count)) { + ret = -EFAULT; + goto exit; + } + ret = count; +exit: + kfree(local_buf); + return ret; +} +#if (DBG_DUMP_DATA) +static void _dump_data(char *buffer, unsigned int len, char *name) +{ + int i; + + pr_err("%s(%s, %d)\n", __func__, name, len); + for (i = 0; i < len; i++) + pr_err("%02x ", buffer[i]); + pr_err("\n"); +} +#endif + +static ssize_t unifykey_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret; + int id; + struct key_item_t *item; + char *local_buf; + + local_buf = kzalloc(count, GFP_KERNEL); + if (!local_buf) + return -ENOMEM; + + id = (int)(*ppos); + item = unifykey_find_item_by_id(id); + if (!item) { + ret = -EINVAL; + goto exit; + } + if (copy_from_user(local_buf, buf, count)) { + ret = -EFAULT; + goto exit; + } +#if (DBG_DUMP_DATA) + _dump_data(local_buf, count, item->name); +#endif + ret = key_unify_write(item->name, local_buf, count); + if (ret < 0) + goto exit; + ret = count; +exit: + kfree(local_buf); + return ret; +} + +static ssize_t version_show(struct class *cla, + struct class_attribute *attr, + char *buf) +{ + ssize_t n = 0; + + n += sprintf(&buf[n], "version:1.0\n"); + buf[n] = 0; + return n; +} + +static ssize_t list_show(struct class *cla, + struct class_attribute *attr, + char *buf) +{ + ssize_t n = 0; + int index, key_cnt; + struct key_item_t *unifykey; + static const char * const keydev[] + = {"unknown", "efuse", "normal", "secure"}; + + key_cnt = unifykey_count_key(); + n += sprintf(&buf[n], "%d keys installed\n", key_cnt); + /* show all the keys*/ + for (index = 0; index < key_cnt; index++) { + unifykey = unifykey_find_item_by_id(index); + n += sprintf(&buf[n], "%02d: %s, %s, %x\n", + index, unifykey->name, + keydev[unifykey->dev], unifykey->permit); + } + buf[n] = 0; + return n; +} + + +static ssize_t exist_show(struct class *cla, + struct class_attribute *attr, + char *buf) +{ + ssize_t n = 0; + int ret; + unsigned int keystate, keypermit; + static const char * const state[] = {"none", "exist", "unknown"}; + + if (curkey == NULL) { + pr_err("please set key name 1st, %s:%d\n", + __func__, __LINE__); + return -EINVAL; + } + /* using current key*/ + ret = key_unify_query(curkey->name, &keystate, &keypermit); + if (ret < 0) { + pr_err("%s:%d, key_unify_query failed!\n", + __func__, __LINE__); + return 0; + } + + if (keystate > 2) + keystate = 2; + + n += sprintf(&buf[n], "%s\n", state[keystate]); + buf[n] = 0; + return n; +} + +static ssize_t encrypt_show(struct class *cla, + struct class_attribute *attr, + char *buf) +{ + ssize_t n = 0; + int ret; + unsigned int encrypt; + static const char * const state[] = {"false", "true", "error"}; + + if (curkey == NULL) { + pr_err("please set key name 1st, %s:%d\n", + __func__, __LINE__); + return -EINVAL; + } + + /* using current key*/ + ret = key_unify_encrypt(curkey->name, &encrypt); + if (ret < 0) { + pr_err("%s:%d, key_unify_query failed!\n", + __func__, __LINE__); + encrypt = 2; + goto _out; + } + + if (encrypt > 1) + encrypt = 1; +_out: + n += sprintf(&buf[n], "%s\n", state[encrypt]); + buf[n] = 0; + return n; +} + +static ssize_t size_show(struct class *cla, + struct class_attribute *attr, + char *buf) +{ + ssize_t n = 0; + int ret; + unsigned int reallen; + + if (curkey == NULL) { + pr_err("please set key name 1st, %s:%d\n", + __func__, + __LINE__); + return -EINVAL; + } + /* using current key*/ + ret = key_unify_size(curkey->name, &reallen); + if (ret < 0) { + pr_err("%s:%d, key_unify_query failed!\n", + __func__, + __LINE__); + return 0; + } + + n += sprintf(&buf[n], "%d\n", reallen); + buf[n] = 0; + + return n; +} + +static ssize_t name_show(struct class *cla, + struct class_attribute *attr, + char *buf) +{ + ssize_t n = 0; + + if (curkey == NULL) { + pr_err("please set cur key name,%s:%d\n", __func__, __LINE__); + return 0; + } + + n += sprintf(&buf[n], "%s\n", curkey->name); + buf[n] = 0; + return n; +} + +static ssize_t name_store(struct class *cla, + struct class_attribute *attr, + const char *buf, + size_t count) +{ + char *name; + int index, key_cnt; + struct key_item_t *unifykey = NULL; + size_t query_name_len; + size_t reval; + + if (count >= KEY_UNIFY_NAME_LEN) + count = KEY_UNIFY_NAME_LEN - 1; + + key_cnt = unifykey_count_key(); + name = kzalloc(count, GFP_KERNEL); + if (!name) { + pr_err("can't kzalloc mem,%s:%d\n", + __func__, + __LINE__); + return -EINVAL; + } + /* check '\n' and del */ + if (buf[count - 1] == '\n') + memcpy(name, buf, count-1); + else + memcpy(name, buf, count); + + query_name_len = strlen(name); + pr_info("%s() %d, name %s, %d\n", + __func__, + __LINE__, + name, + (int)query_name_len); + + curkey = NULL; + + for (index = 0; index < key_cnt; index++) { + unifykey = unifykey_find_item_by_id(index); + if (unifykey != NULL) { + if (!strncmp(name, unifykey->name, + ((strlen(unifykey->name) > query_name_len) + ? strlen(unifykey->name) : query_name_len))) { + pr_info("%s() %d\n", __func__, __LINE__); + curkey = unifykey; + break; + } + } + } + reval = count; + if (curkey == NULL) { + /* count = 0; */ + reval++; + pr_err("could not found key %s\n", name); + } + if (!IS_ERR_OR_NULL(name)) + kfree(name); + + return reval; +} + +static ssize_t read_show(struct class *cla, + struct class_attribute *attr, + char *buf) +{ + ssize_t n = 0; + unsigned int keysize, reallen; + int ret; + unsigned char *keydata = NULL; + + if (curkey != NULL) { + /* get key value */ + ret = key_unify_size(curkey->name, &keysize); + if (ret < 0) { + pr_err("%s() %d: get key size fail\n", + __func__, + __LINE__); + goto _out; + } + if (keysize == 0) { + pr_err("%s() %d: key %s may not burned yet!\n", + __func__, + __LINE__, + curkey->name); + goto _out; + } + if (curkey->dev == KEY_M_SECURE) + keysize = SHA256_SUM_LEN; + pr_err("name: %s, size %d\n", curkey->name, keysize); + keydata = kzalloc(keysize, GFP_KERNEL); + if (keydata == NULL) { + pr_err("%s() %d: no enough memory\n", + __func__, __LINE__); + goto _out; + } + ret = key_unify_read(curkey->name, + keydata, + keysize, + &reallen); + if (ret < 0) { + pr_err("%s() %d: get key size fail\n", + __func__, + __LINE__); + goto _out; + } + /* fixme */ + memcpy(buf, keydata, keysize); + n += keysize; + /* n += sprintf(&buf[n], "%s\n", keydata); */ + buf[n] = 0; + } +_out: + if (!IS_ERR_OR_NULL(keydata)) + kfree(keydata); + + return n; +} + +static ssize_t write_store(struct class *cla, + struct class_attribute *attr, + const char *buf, + size_t count) +{ + int ret; + unsigned char *keydata = NULL; + size_t key_len = 0; + + if (curkey != NULL) { + keydata = kzalloc(count, GFP_KERNEL); + + if (keydata == NULL) + goto _out; + + /* check string */ + for (key_len = 0; key_len < count; key_len++) + if (!isascii(buf[key_len])) + break; + /* check '\n' and del while string */ + if ((key_len == count) && (buf[count - 1] == '\n')) { + pr_err("%s() is a string\n", __func__); + memcpy(keydata, buf, count-1); + key_len = count - 1; + } else { + memcpy(keydata, buf, count); + key_len = count; + } + #if (DBG_DUMP_DATA) + _dump_data(keydata, key_len, curkey->name); + #endif + ret = key_unify_write(curkey->name, keydata, key_len); + if (ret < 0) { + pr_err("%s() %d: key write fail\n", + __func__, __LINE__); + goto _out; + } + + } +_out: + if (!IS_ERR_OR_NULL(keydata)) + kfree(keydata); + + return count; +} + +static ssize_t attach_show(struct class *cla, + struct class_attribute *attr, + char *buf) +{ + ssize_t n = 0; + /* show attach status. */ + n += sprintf(&buf[n], "%s\n", (init_flag == 0 ? "no":"yes")); + buf[n] = 0; + + return n; +} + +static ssize_t attach_store(struct class *cla, + struct class_attribute *attr, + const char *buf, + size_t count) +{ + /* todo, do attach */ + key_unify_init(NULL, KEY_UNIFY_NAME_LEN); + return count; +} + +static ssize_t lock_show(struct class *cla, + struct class_attribute *attr, + char *buf) +{ + ssize_t n = 0; + /* show lock state. */ + n += sprintf(&buf[n], "%d\n", lock_flag); + buf[n] = 0; + pr_info("%s\n", (lock_flag == 1 ? "locked":"unlocked")); + + return n; +} + +static ssize_t lock_store(struct class *cla, + struct class_attribute *attr, + const char *buf, + size_t count) +{ + unsigned int state, len; + + /* check '\n' and del */ + if (buf[count - 1] == '\n') + len = count - 1; + else + len = count; + + if (!strncmp(buf, "1", len)) + state = 1; + else if (!strncmp(buf, "0", len)) + state = 0; + else { + pr_info("unifykey lock set fail\n"); + goto _out; + } + + lock_flag = state; +_out: + pr_info("unifykey is %s\n", (lock_flag == 1 ? "locked":"unlocked")); + return count; +} + + +static const char *unifykeys_help_str = { +"Usage:\n" +"echo 1 > attach //initialise unifykeys\n" +"cat lock //get lock status\n" +"//if lock=1,you must wait, lock=0, you can go on\n" +"//so you must set unifykey lock first, then do other operations\n" +"echo 1 > lock //1:locked, 0:unlocked\n" +"echo \"key name\" > name //set current key name->\"key name\"\n" +"cat name //get current key name\n" +"echo \"key value\" > write //set current key value->\"key value\"\n" +"cat read //get current key value\n" +"cat size //get current key value\n" +"cat exist //get whether current key is exist or not\n" +"cat list //get all unifykeys\n" +"cat version //get unifykeys versions\n" +"//at last, you must set lock=0 when you has done all operations\n" +"echo 0 > lock //set unlock\n" +}; + +static ssize_t help_show(struct class *cla, + struct class_attribute *attr, + char *buf) +{ + ssize_t n = 0; + + n += sprintf(buf, "%s", unifykeys_help_str); + buf[n] = 0; + return n; +} + +static const struct of_device_id unifykeys_dt_match[]; + +static char *get_unifykeys_drv_data(struct platform_device *pdev) +{ + char *key_dev = NULL; + + if (pdev->dev.of_node) { + const struct of_device_id *match; + + match = of_match_node(unifykeys_dt_match, + pdev->dev.of_node); + if (match) + key_dev = (char *)match->data; + } + + return key_dev; +} + +static const struct file_operations unifykey_fops = { + .owner = THIS_MODULE, + .llseek = unifykey_llseek, + .open = unifykey_open, + .release = unifykey_release, + .read = unifykey_read, + .write = unifykey_write, + .unlocked_ioctl = unifykey_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = unifykey_compat_ioctl, +#endif +}; + + +#define KEY_READ_ATTR (0440) +#define KEY_WRITE_ATTR (0220) +#define KEY_RW_ATTR (KEY_READ_ATTR | KEY_WRITE_ATTR) + +static struct class_attribute unifykey_class_attrs[] = { + __ATTR_RO(version), + __ATTR_RO(list), + __ATTR_RO(exist), + __ATTR_RO(encrypt), + __ATTR_RO(size), + __ATTR_RO(help), + __ATTR(name, KEY_RW_ATTR, name_show, name_store), + __ATTR(write, KEY_WRITE_ATTR, NULL, write_store), + __ATTR(read, KEY_READ_ATTR, read_show, NULL), + __ATTR(attach, KEY_RW_ATTR, attach_show, attach_store), + __ATTR(lock, KEY_RW_ATTR, lock_show, lock_store), + __ATTR_NULL +}; + +static struct class unifykey_class = { + .name = UNIFYKEYS_CLASS_NAME, + .class_attrs = unifykey_class_attrs, +}; + +static int aml_unifykeys_probe(struct platform_device *pdev) +{ + int ret = -1; + struct device *devp; + + if (pdev->dev.of_node) + ret = unifykey_dt_create(pdev); + + ret = alloc_chrdev_region(&unifykey_devno, + 0, 1, + UNIFYKEYS_DEVICE_NAME); + if (ret < 0) { + pr_err("unifykey: failed to allocate major number\n "); + ret = -ENODEV; + goto out; + } + + pr_info("%s:%d=============unifykey_devno:%x\n", + __func__, + __LINE__, + unifykey_devno); + + ret = class_register(&unifykey_class); + if (ret) + goto error1; + + unifykey_devp = kzalloc(sizeof(struct unifykey_dev_t), GFP_KERNEL); + if (!unifykey_devp) { + ret = -ENOMEM; + goto error2; + } + + /* connect the file operations with cdev */ + cdev_init(&unifykey_devp->cdev, &unifykey_fops); + unifykey_devp->cdev.owner = THIS_MODULE; + /* connect the major/minor number to the cdev */ + ret = cdev_add(&unifykey_devp->cdev, unifykey_devno, 1); + if (ret) { + pr_err("unifykey: failed to add device\n"); + goto error3; + } + + devp = device_create(&unifykey_class, NULL, + unifykey_devno, NULL, UNIFYKEYS_DEVICE_NAME); + if (IS_ERR(devp)) { + pr_err("unifykey: failed to create device node\n"); + ret = PTR_ERR(devp); + goto error4; + } + devp->platform_data = get_unifykeys_drv_data(pdev); + + unifykey_device = devp; + + pr_info("unifykey: device %s created ok\n", + UNIFYKEYS_DEVICE_NAME); + + return 0; +error4: + cdev_del(&unifykey_devp->cdev); +error3: + kfree(unifykey_devp); +error2: + class_unregister(&unifykey_class); +error1: + unregister_chrdev_region(unifykey_devno, 1); +out: + return ret; +} + +static int aml_unifykeys_remove(struct platform_device *pdev) +{ + if (pdev->dev.of_node) + unifykey_dt_release(pdev); + + unregister_chrdev_region(unifykey_devno, 1); + device_destroy(&unifykey_class, unifykey_devno); + cdev_del(&unifykey_devp->cdev); + kfree(unifykey_devp); + class_unregister(&unifykey_class); + + return 0; +} + +static const struct of_device_id unifykeys_dt_match[] = { + { .compatible = "amlogic, unifykey", + .data = NULL, + }, + {}, +}; + +static struct platform_driver unifykey_platform_driver = { + .probe = aml_unifykeys_probe, + .remove = aml_unifykeys_remove, + .driver = { + .name = UNIFYKEYS_DEVICE_NAME, + .owner = THIS_MODULE, + .of_match_table = unifykeys_dt_match, + }, +}; + +static int __init aml_unifykeys_init(void) +{ + int ret = -1; + + ret = platform_driver_register(&unifykey_platform_driver); + if (ret != 0) { + pr_err("failed to register unifykey driver, error %d\n", ret); + return -ENODEV; + } + module_init_flag = 1; + pr_info("%s done!\n", __func__); + + return ret; +} + +static void __exit aml_unifykeys_exit(void) +{ + key_unify_uninit(); + platform_driver_unregister(&unifykey_platform_driver); +} + +module_init(aml_unifykeys_init); +module_exit(aml_unifykeys_exit); + +MODULE_DESCRIPTION("Amlogic unifykeys management driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("bl zhou"); + + diff --git a/drivers/amlogic/key_manage/unifykey.h b/drivers/amlogic/key_manage/unifykey.h new file mode 100644 index 000000000000..952e57c01a04 --- /dev/null +++ b/drivers/amlogic/key_manage/unifykey.h @@ -0,0 +1,97 @@ +/* + * drivers/amlogic/key_manage/unifykey.h + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 __UNIFYKEY_MANAGE_H__ +#define __UNIFYKEY_MANAGE_H__ + +#include +#include + +#define KEYUNIFY_ATTACH _IO('f', 0x60) +#define KEYUNIFY_GET_INFO _IO('f', 0x62) + + + +/* need query! */ + +enum key_manager_dev_e { + KEY_M_UNKNOWN_DEV = 0, + KEY_M_EFUSE, + KEY_M_NORMAL,/*include general key(nand key,emmc key)*/ + KEY_M_SECURE, + KEY_M_MAX_DEV, +}; + +enum key_manager_permit_e { + KEY_M_PERMIT_READ = (1<<0), + KEY_M_PERMIT_WRITE = (1<<1), + KEY_M_PERMIT_DEL = (1<<2), + KEY_M_PERMIT_MASK = 0Xf, +}; + +enum key_manager_flag_e { + KEY_M_FLAG_EMPTY = (0<<0), + KEY_M_FLAG_EXSIT = (1<<0), +}; +#define KEY_UNIFY_NAME_LEN 48 +/* for ioctrl transfer parameters. */ +struct key_item_info_t { + unsigned int id; + char name[KEY_UNIFY_NAME_LEN]; + unsigned int size; + unsigned int permit; + unsigned int flag; /*bit 0: 1 exsit, 0-none;*/ + unsigned int reserve; +}; + +#define KEY_UNIFY_ATTR_ENCRYPT (1 << 8) +#define KEY_UNIFY_ATTR_ENCRYPT_MASK (KEY_UNIFY_ATTR_ENCRYPT) +#define KEY_UNIFY_ATTR_SECURE (1 << 0) +#define KEY_UNIFY_ATTR_SECURE_MASK (KEY_UNIFY_ATTR_SECURE) + +struct key_item_t { + char name[KEY_UNIFY_NAME_LEN]; + int id; + unsigned int dev; /* key save in device //efuse, */ + /* unsigned int df; */ + unsigned int permit; + int attr; + int reserve; + struct key_item_t *next; +}; + +struct key_info_t { + int key_num; + int efuse_version; + int key_flag; +}; + +struct unifykey_dev_t { + struct cdev cdev; + unsigned int flags; +}; + +extern int unifykey_dt_create(struct platform_device *pdev); +extern int unifykey_dt_release(struct platform_device *pdev); +extern struct key_item_t *unifykey_find_item_by_name(char *name); +extern struct key_item_t *unifykey_find_item_by_id(int id); +extern int unifykey_item_verify_check(struct key_item_t *key_item); +extern int unifykey_count_key(void); +extern char unifykey_get_efuse_version(void); + +#endif + diff --git a/drivers/amlogic/key_manage/unifykey_dts.c b/drivers/amlogic/key_manage/unifykey_dts.c new file mode 100644 index 000000000000..dea64c0dd010 --- /dev/null +++ b/drivers/amlogic/key_manage/unifykey_dts.c @@ -0,0 +1,271 @@ +/* + * drivers/amlogic/key_manage/unifykey_dts.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "unifykey.h" + +/* storages where key stored */ +#define KEY_DEV_EFUSE "efuse" +#define KEY_DEV_NORMAL "normal" +#define KEY_DEV_SECURE "secure" + +/* permision */ +#define KEY_PERMIT_READ "read" +#define KEY_PERMIT_WRITE "write" +#define KEY_PERMIT_DEL "del" + +/* attribute */ +#define KEY_ATTR_TRUE "true" +#define KEY_ATTR_FALSE "false" + +static struct key_info_t unify_key_info = {.key_num = 0, .key_flag = 0}; +static struct key_item_t *unifykey_item; + +int unifykey_item_verify_check(struct key_item_t *key_item) +{ + if (!key_item) { + pr_err("%s:%d unify key item is invalid\n", __func__, __LINE__); + return -1; + } + if (key_item->dev == KEY_M_UNKNOWN_DEV) { + pr_err("%s:%d unify key item is invalid\n", __func__, __LINE__); + return -1; + } + return 0; +} + +struct key_item_t *unifykey_find_item_by_name(char *name) +{ + struct key_item_t *pre_item; + + pre_item = unifykey_item; + while (pre_item) { + if (!strncmp(pre_item->name, name, + ((strlen(pre_item->name) > strlen(name)) + ? strlen(pre_item->name) : strlen(name)))) + return pre_item; + pre_item = pre_item->next; + } + return NULL; +} + +struct key_item_t *unifykey_find_item_by_id(int id) +{ + struct key_item_t *pre_item; + + pre_item = unifykey_item; + while (pre_item) { + if (pre_item->id == id) + return pre_item; + pre_item = pre_item->next; + } + return NULL; +} + +int unifykey_count_key(void) +{ + int count = 0; + struct key_item_t *pre_item; + + pre_item = unifykey_item; + while (pre_item) { + count++; + pre_item = pre_item->next; + } + + return count; +} + +char unifykey_get_efuse_version(void) +{ + char ver = 0; + + if (unify_key_info.efuse_version != -1) + ver = (char)unify_key_info.efuse_version; + return ver; +} + +static int unifykey_add_to_list(struct key_item_t *item) +{ + struct key_item_t *pre_item; + + if (unifykey_item == NULL) { + unifykey_item = item; + } else { + pre_item = unifykey_item; + while (pre_item->next != NULL) + pre_item = pre_item->next; + pre_item->next = item; + } + return 0; +} + +static int unifykey_free_list(void) +{ + struct key_item_t *pre_item; + + pre_item = unifykey_item; + while (pre_item) { + unifykey_item = unifykey_item->next; + kfree(pre_item); + pre_item = unifykey_item; + } + return 0; +} + +static int unifykey_item_parse_dt(struct device_node *node, int id) +{ + int count; + int ret = -1; + const char *propname; + struct key_item_t *temp_item = NULL; + + temp_item = kzalloc(sizeof(struct key_item_t), GFP_KERNEL); + if (!temp_item) { + ret = -ENOMEM; + return ret; + } + propname = NULL; + ret = of_property_read_string(node, "key-name", &propname); + if (ret < 0) { + pr_err("%s:%d,get key-name fail\n", __func__, __LINE__); + ret = -EINVAL; + goto exit; + } + if (propname) { + count = strlen(propname); + memset(temp_item->name, 0, KEY_UNIFY_NAME_LEN); + if (count >= KEY_UNIFY_NAME_LEN) + count = KEY_UNIFY_NAME_LEN-1; + strncpy(temp_item->name, propname, count); + } + + propname = NULL; + ret = of_property_read_string(node, "key-device", &propname); + if (ret < 0) { + pr_err("%s:%d,get key-device fail\n", __func__, __LINE__); + ret = -EINVAL; + goto exit; + } + if (propname) { + if (strcmp(propname, KEY_DEV_EFUSE) == 0) + temp_item->dev = KEY_M_EFUSE; + else if (strcmp(propname, KEY_DEV_NORMAL) == 0) + temp_item->dev = KEY_M_NORMAL; + else if (strcmp(propname, KEY_DEV_SECURE) == 0) + temp_item->dev = KEY_M_SECURE; + else + temp_item->dev = KEY_M_UNKNOWN_DEV; + } + + temp_item->permit = 0; + if (of_property_match_string(node, "key-permit", KEY_PERMIT_READ) >= 0) + temp_item->permit |= KEY_M_PERMIT_READ; + if (of_property_match_string(node, "key-permit", KEY_PERMIT_WRITE) >= 0) + temp_item->permit |= KEY_M_PERMIT_WRITE; + if (of_property_match_string(node, "key-permit", KEY_PERMIT_DEL) >= 0) + temp_item->permit |= KEY_M_PERMIT_DEL; + temp_item->id = id; + + temp_item->attr = 0; + ret = of_property_read_string(node, "key-encrypt", &propname); + if (ret < 0) + goto _next_attr; + if (propname) { + if (strcmp(propname, KEY_ATTR_TRUE) == 0) + temp_item->attr = KEY_UNIFY_ATTR_ENCRYPT; + } + +_next_attr: + /*todo, add new attribute here*/ + + unifykey_add_to_list(temp_item); + + return 0; +exit: + kfree(temp_item); + return ret; +} + + + +static int unifykey_item_create(struct platform_device *pdev, int num) +{ + int ret = -1; + int index; + struct device_node *child; + struct device_node *np = pdev->dev.of_node; + + of_node_get(np); + index = 0; + for_each_child_of_node(np, child) { + ret = unifykey_item_parse_dt(child, index); + if (!ret) + index++; + } + pr_info("key unify fact unifykey-num is %d\n", index); + + return 0; +} + +int unifykey_dt_create(struct platform_device *pdev) +{ + int ret = -1; + int key_num; + + ret = of_property_read_u32(pdev->dev.of_node, + "unifykey-num", &key_num); + if (ret) { + pr_err("%s:%d,don't find to match unifykey-num\n", + __func__, + __LINE__); + return ret; + } + /* set default efuse version info */ + unify_key_info.efuse_version = -1; + of_property_read_u32(pdev->dev.of_node, "efuse-version", + &unify_key_info.efuse_version); + + pr_info("key unify config unifykey-num is %d\n", key_num); + unify_key_info.key_num = key_num; + if (!unify_key_info.key_flag) { + unifykey_item_create(pdev, key_num); + unify_key_info.key_flag = 1; + } + + return ret; +} + +int unifykey_dt_release(struct platform_device *pdev) +{ + if (pdev->dev.of_node) + of_node_put(pdev->dev.of_node); + unifykey_free_list(); + unify_key_info.key_flag = 0; + return 0; +} + + + diff --git a/drivers/amlogic/mmc/Makefile b/drivers/amlogic/mmc/Makefile index 759fdb33bd7d..eb7250176f7e 100644 --- a/drivers/amlogic/mmc/Makefile +++ b/drivers/amlogic/mmc/Makefile @@ -2,4 +2,4 @@ # Amlogic MMC specific Makefile # -obj-$(CONFIG_AMLOGIC_MMC) += amlsd.o amlsd_of.o emmc_partitions.o aml_sd_emmc.o +obj-$(CONFIG_AMLOGIC_MMC) += amlsd.o amlsd_of.o emmc_partitions.o aml_sd_emmc.o emmc_key.o diff --git a/drivers/amlogic/mmc/emmc_key.c b/drivers/amlogic/mmc/emmc_key.c new file mode 100644 index 000000000000..c393a9bf3418 --- /dev/null +++ b/drivers/amlogic/mmc/emmc_key.c @@ -0,0 +1,212 @@ +/* + * drivers/amlogic/mmc/emmc_key.c + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "emmc_key.h" + +#define EMMC_BLOCK_SIZE (0x100) +#define MAX_EMMC_BLOCK_SIZE (128*1024) + +/* + * kernel head file + * + */ +static struct mmc_card *mmc_card_key; +static struct aml_emmckey_info_t *emmckey_info; + +static int aml_emmc_key_check(void) +{ + u8 keypart_cnt; + u64 part_size; + struct emmckey_valid_node_t *emmckey_valid_node, *temp_valid_node; + + emmckey_info->key_part_count = + emmckey_info->keyarea_phy_size / EMMC_KEYAREA_SIZE; + + if (emmckey_info->key_part_count + > EMMC_KEYAREA_COUNT) { + emmckey_info->key_part_count = EMMC_KEYAREA_COUNT; + } + keypart_cnt = 0; + part_size = EMMC_KEYAREA_SIZE; + do { + emmckey_valid_node = kmalloc( + sizeof(*emmckey_valid_node), GFP_KERNEL); + + if (emmckey_valid_node == NULL) { + pr_info("%s:%d,kmalloc memory fail\n", + __func__, __LINE__); + return -ENOMEM; + } + emmckey_valid_node->phy_addr = emmckey_info->keyarea_phy_addr + + part_size * keypart_cnt; + emmckey_valid_node->phy_size = EMMC_KEYAREA_SIZE; + emmckey_valid_node->next = NULL; + emmckey_info->key_valid = 0; + if (emmckey_info->key_valid_node == NULL) { + + emmckey_info->key_valid_node = emmckey_valid_node; + + } else{ + temp_valid_node = emmckey_info->key_valid_node; + + while (temp_valid_node->next != NULL) + temp_valid_node = temp_valid_node->next; + + temp_valid_node->next = emmckey_valid_node; + } + } while (++keypart_cnt < emmckey_info->key_part_count); + + emmckey_info->key_valid = 1; + return 0; +} + +int32_t emmc_key_read(uint8_t *buffer, + uint32_t length, uint32_t *actual_length) +{ + int ret; + u64 addr = 0; + u32 size = 0; + int blk, cnt; + unsigned char *dst = NULL; + struct mmc_card *card = mmc_card_key; + int bit = card->csd.read_blkbits; + + size = length; + *actual_length = length; + addr = get_reserve_partition_off_from_tbl() + EMMCKEY_RESERVE_OFFSET; + blk = addr >> bit; + cnt = size >> bit; + dst = (unsigned char *)buffer; + mmc_claim_host(card->host); + do { + ret = mmc_read_internal(card, blk, EMMC_BLOCK_SIZE, dst); + if (ret) { + pr_err("%s [%d] mmc_write_internal error\n", + __func__, __LINE__); + return ret; + } + blk += EMMC_BLOCK_SIZE; + cnt -= EMMC_BLOCK_SIZE; + dst = (unsigned char *)buffer + MAX_EMMC_BLOCK_SIZE; + } while (cnt != 0); + pr_info("%s:%d, read %s\n", __func__, __LINE__, (ret) ? "error":"ok"); + + mmc_release_host(card->host); + return ret; +} +EXPORT_SYMBOL(emmc_key_read); + +int32_t emmc_key_write(uint8_t *buffer, + uint32_t length, uint32_t *actual_length) +{ + int ret; + u64 addr = 0; + u32 size = 0; + int blk, cnt; + unsigned char *src = NULL; + struct mmc_card *card = mmc_card_key; + int bit = card->csd.read_blkbits; + + size = length; + addr = get_reserve_partition_off_from_tbl() + EMMCKEY_RESERVE_OFFSET; + blk = addr >> bit; + cnt = size >> bit; + src = (unsigned char *)buffer; + mmc_claim_host(card->host); + do { + ret = mmc_write_internal(card, blk, EMMC_BLOCK_SIZE, src); + if (ret) { + pr_err("%s [%d] mmc_write_internal error\n", + __func__, __LINE__); + return ret; + } + blk += EMMC_BLOCK_SIZE; + cnt -= EMMC_BLOCK_SIZE; + src = (unsigned char *)buffer + MAX_EMMC_BLOCK_SIZE; + } while (cnt != 0); + pr_info("%s:%d, write %s\n", __func__, __LINE__, (ret) ? "error":"ok"); + mmc_release_host(card->host); + return ret; +} +EXPORT_SYMBOL(emmc_key_write); + +int emmc_key_init(struct mmc_card *card) +{ + u64 addr = 0; + u32 size = 0; + u64 lba_start = 0, lba_end = 0; + int err = 0; + int bit = card->csd.read_blkbits; + + pr_info("card key: card_blk_probe.\n"); + emmckey_info = kmalloc(sizeof(*emmckey_info), GFP_KERNEL); + if (emmckey_info == NULL) { + pr_info("%s:%d,kmalloc memory fail\n", __func__, __LINE__); + return -ENOMEM; + } + memset(emmckey_info, 0, sizeof(*emmckey_info)); + emmckey_info->key_init = 0; + + size = EMMCKEY_AREA_PHY_SIZE; + addr = get_reserve_partition_off_from_tbl() + EMMCKEY_RESERVE_OFFSET; + if (addr < 0) { + err = -EINVAL; + goto exit_err; + } + lba_start = addr >> bit; + lba_end = (addr + size) >> bit; + emmckey_info->key_init = 1; + + pr_info("%s:%d emmc key lba_start:0x%llx,lba_end:0x%llx\n", + __func__, __LINE__, lba_start, lba_end); + + if (!emmckey_info->key_init) { + err = -EINVAL; + + pr_info("%s:%d,emmc key init fail\n", __func__, __LINE__); + goto exit_err; + } + emmckey_info->keyarea_phy_addr = addr; + emmckey_info->keyarea_phy_size = size; + emmckey_info->lba_start = lba_start; + emmckey_info->lba_end = lba_end; + mmc_card_key = card; + err = aml_emmc_key_check(); + if (err) { + pr_info("%s:%d,emmc key check fail\n", __func__, __LINE__); + goto exit_err; + } + + storage_ops_read(emmc_key_read); + storage_ops_write(emmc_key_write); + + pr_info("emmc key: %s:%d ok.\n", __func__, __LINE__); + return err; + +exit_err: + kfree(emmckey_info); + return err; +} + diff --git a/drivers/amlogic/mmc/emmc_key.h b/drivers/amlogic/mmc/emmc_key.h new file mode 100644 index 000000000000..67b56759baf7 --- /dev/null +++ b/drivers/amlogic/mmc/emmc_key.h @@ -0,0 +1,74 @@ +/* + * drivers/amlogic/mmc/emmc_key.h + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 __EMMC_KEY_H__ +#define __EMMC_KEY_H__ + +#include +#include +#include +#include + +#define EMMC_KEY_AREA_SIGNAL "emmckeys" +#define EMMC_KEY_AREA_SIGNAL_LEN 16 + +#define EMMC_KEYAREA_SIZE (128*1024) +#define EMMC_KEYAREA_COUNT 2 + +/* we store partition table in the previous 16KB space */ +#define EMMCKEY_RESERVE_OFFSET 0x4000 +#define EMMCKEY_AREA_PHY_SIZE (EMMC_KEYAREA_COUNT * EMMC_KEYAREA_SIZE) + +struct emmckey_valid_node_t { + u64 phy_addr; + u64 phy_size; + struct emmckey_valid_node_t *next; +}; + +struct aml_emmckey_info_t { + /* struct memory_card *card; */ + struct emmckey_valid_node_t *key_valid_node; + u64 keyarea_phy_addr; + u64 keyarea_phy_size; + u64 lba_start; + u64 lba_end; + u32 blk_size; + u32 blk_shift; + u8 key_init; + u8 key_valid; + u8 key_part_count; +}; + +#define EMMCKEY_DATA_VALID_LEN \ + (EMMC_KEYAREA_SIZE - EMMC_KEY_AREA_SIGNAL_LEN - 4 - 4 - 4) +struct emmckey_data_t { + u8 keyarea_mark[EMMC_KEY_AREA_SIGNAL_LEN]; + u32 keyarea_mark_checksum; + u32 checksum; + u32 reserve; + u8 data[EMMCKEY_DATA_VALID_LEN]; +}; + +int emmc_key_init(struct mmc_card *card); + +int32_t emmc_key_read(uint8_t *buffer, + uint32_t length, uint32_t *actual_length); +int32_t emmc_key_write(uint8_t *buffer, + uint32_t length, uint32_t *actual_length); + +#endif + diff --git a/drivers/amlogic/mmc/emmc_partitions.c b/drivers/amlogic/mmc/emmc_partitions.c index 2eeda6d1d189..c97fc5d1486a 100644 --- a/drivers/amlogic/mmc/emmc_partitions.c +++ b/drivers/amlogic/mmc/emmc_partitions.c @@ -33,6 +33,7 @@ #include #include #include +#include "emmc_key.h" #define DTB_NAME "dtb" #define SZ_1M 0x00100000 @@ -957,6 +958,9 @@ int aml_emmc_partition_ops(struct mmc_card *card, struct gendisk *disk) } mmc_release_host(card->host); + if (ret == 0) /* ok */ + ret = emmc_key_init(card); + amlmmc_dtb_init(card); aml_store_class = class_create(THIS_MODULE, "aml_store"); diff --git a/include/linux/amlogic/key_manage.h b/include/linux/amlogic/key_manage.h new file mode 100644 index 000000000000..b6df6a7dd1dd --- /dev/null +++ b/include/linux/amlogic/key_manage.h @@ -0,0 +1,37 @@ +/* + * include/linux/amlogic/key_manage.h + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 __KEYMANAGE1__ +#define __KEYMANAGE1__ + +typedef int32_t (*store_key_ops)(uint8_t *buf, + uint32_t len, uint32_t *actual_length); + +#ifdef CONFIG_AMLOGIC_KEY_MANAGE +void storage_ops_read(store_key_ops read); +void storage_ops_write(store_key_ops write); +#else +void storage_ops_read(store_key_ops read) +{ +} + +void storage_ops_write(store_key_ops read) +{ +} +#endif /*CONFIG_KEY_MANAGE*/ + +#endif /*__KEYMANAGE1__*/