363 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2022 Microchip.4 */5 6#include <linux/device.h>7#include <linux/kernel.h>8#include <linux/module.h>9#include <linux/rtc.h>10#include <linux/tee_drv.h>11 12#define RTC_INFO_VERSION 0x113 14#define TA_CMD_RTC_GET_INFO 0x015#define TA_CMD_RTC_GET_TIME 0x116#define TA_CMD_RTC_SET_TIME 0x217#define TA_CMD_RTC_GET_OFFSET 0x318#define TA_CMD_RTC_SET_OFFSET 0x419 20#define TA_RTC_FEATURE_CORRECTION BIT(0)21 22struct optee_rtc_time {23 u32 tm_sec;24 u32 tm_min;25 u32 tm_hour;26 u32 tm_mday;27 u32 tm_mon;28 u32 tm_year;29 u32 tm_wday;30};31 32struct optee_rtc_info {33 u64 version;34 u64 features;35 struct optee_rtc_time range_min;36 struct optee_rtc_time range_max;37};38 39/**40 * struct optee_rtc - OP-TEE RTC private data41 * @dev: OP-TEE based RTC device.42 * @ctx: OP-TEE context handler.43 * @session_id: RTC TA session identifier.44 * @shm: Memory pool shared with RTC device.45 * @features: Bitfield of RTC features46 */47struct optee_rtc {48 struct device *dev;49 struct tee_context *ctx;50 u32 session_id;51 struct tee_shm *shm;52 u64 features;53};54 55static int optee_rtc_readtime(struct device *dev, struct rtc_time *tm)56{57 struct optee_rtc *priv = dev_get_drvdata(dev);58 struct tee_ioctl_invoke_arg inv_arg = {0};59 struct optee_rtc_time *optee_tm;60 struct tee_param param[4] = {0};61 int ret;62 63 inv_arg.func = TA_CMD_RTC_GET_TIME;64 inv_arg.session = priv->session_id;65 inv_arg.num_params = 4;66 67 /* Fill invoke cmd params */68 param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;69 param[0].u.memref.shm = priv->shm;70 param[0].u.memref.size = sizeof(struct optee_rtc_time);71 72 ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);73 if (ret < 0 || inv_arg.ret != 0)74 return ret ? ret : -EPROTO;75 76 optee_tm = tee_shm_get_va(priv->shm, 0);77 if (IS_ERR(optee_tm))78 return PTR_ERR(optee_tm);79 80 if (param[0].u.memref.size != sizeof(*optee_tm))81 return -EPROTO;82 83 tm->tm_sec = optee_tm->tm_sec;84 tm->tm_min = optee_tm->tm_min;85 tm->tm_hour = optee_tm->tm_hour;86 tm->tm_mday = optee_tm->tm_mday;87 tm->tm_mon = optee_tm->tm_mon;88 tm->tm_year = optee_tm->tm_year - 1900;89 tm->tm_wday = optee_tm->tm_wday;90 tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);91 92 return 0;93}94 95static int optee_rtc_settime(struct device *dev, struct rtc_time *tm)96{97 struct optee_rtc *priv = dev_get_drvdata(dev);98 struct tee_ioctl_invoke_arg inv_arg = {0};99 struct tee_param param[4] = {0};100 struct optee_rtc_time optee_tm;101 void *rtc_data;102 int ret;103 104 optee_tm.tm_sec = tm->tm_sec;105 optee_tm.tm_min = tm->tm_min;106 optee_tm.tm_hour = tm->tm_hour;107 optee_tm.tm_mday = tm->tm_mday;108 optee_tm.tm_mon = tm->tm_mon;109 optee_tm.tm_year = tm->tm_year + 1900;110 optee_tm.tm_wday = tm->tm_wday;111 112 inv_arg.func = TA_CMD_RTC_SET_TIME;113 inv_arg.session = priv->session_id;114 inv_arg.num_params = 4;115 116 param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;117 param[0].u.memref.shm = priv->shm;118 param[0].u.memref.size = sizeof(struct optee_rtc_time);119 120 rtc_data = tee_shm_get_va(priv->shm, 0);121 if (IS_ERR(rtc_data))122 return PTR_ERR(rtc_data);123 124 memcpy(rtc_data, &optee_tm, sizeof(struct optee_rtc_time));125 126 ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);127 if (ret < 0 || inv_arg.ret != 0)128 return ret ? ret : -EPROTO;129 130 return 0;131}132 133static int optee_rtc_readoffset(struct device *dev, long *offset)134{135 struct optee_rtc *priv = dev_get_drvdata(dev);136 struct tee_ioctl_invoke_arg inv_arg = {0};137 struct tee_param param[4] = {0};138 int ret;139 140 if (!(priv->features & TA_RTC_FEATURE_CORRECTION))141 return -EOPNOTSUPP;142 143 inv_arg.func = TA_CMD_RTC_GET_OFFSET;144 inv_arg.session = priv->session_id;145 inv_arg.num_params = 4;146 147 param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;148 149 ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);150 if (ret < 0 || inv_arg.ret != 0)151 return ret ? ret : -EPROTO;152 153 *offset = param[0].u.value.a;154 155 return 0;156}157 158static int optee_rtc_setoffset(struct device *dev, long offset)159{160 struct optee_rtc *priv = dev_get_drvdata(dev);161 struct tee_ioctl_invoke_arg inv_arg = {0};162 struct tee_param param[4] = {0};163 int ret;164 165 if (!(priv->features & TA_RTC_FEATURE_CORRECTION))166 return -EOPNOTSUPP;167 168 inv_arg.func = TA_CMD_RTC_SET_OFFSET;169 inv_arg.session = priv->session_id;170 inv_arg.num_params = 4;171 172 param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;173 param[0].u.value.a = offset;174 175 ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);176 if (ret < 0 || inv_arg.ret != 0)177 return ret ? ret : -EPROTO;178 179 return 0;180}181 182static const struct rtc_class_ops optee_rtc_ops = {183 .read_time = optee_rtc_readtime,184 .set_time = optee_rtc_settime,185 .set_offset = optee_rtc_setoffset,186 .read_offset = optee_rtc_readoffset,187};188 189static int optee_rtc_read_info(struct device *dev, struct rtc_device *rtc,190 u64 *features)191{192 struct optee_rtc *priv = dev_get_drvdata(dev);193 struct tee_ioctl_invoke_arg inv_arg = {0};194 struct tee_param param[4] = {0};195 struct optee_rtc_info *info;196 struct optee_rtc_time *tm;197 int ret;198 199 inv_arg.func = TA_CMD_RTC_GET_INFO;200 inv_arg.session = priv->session_id;201 inv_arg.num_params = 4;202 203 param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;204 param[0].u.memref.shm = priv->shm;205 param[0].u.memref.size = sizeof(*info);206 207 ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);208 if (ret < 0 || inv_arg.ret != 0)209 return ret ? ret : -EPROTO;210 211 info = tee_shm_get_va(priv->shm, 0);212 if (IS_ERR(info))213 return PTR_ERR(info);214 215 if (param[0].u.memref.size != sizeof(*info))216 return -EPROTO;217 218 if (info->version != RTC_INFO_VERSION)219 return -EPROTO;220 221 *features = info->features;222 223 tm = &info->range_min;224 rtc->range_min = mktime64(tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min,225 tm->tm_sec);226 tm = &info->range_max;227 rtc->range_max = mktime64(tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min,228 tm->tm_sec);229 230 return 0;231}232 233static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)234{235 if (ver->impl_id == TEE_IMPL_ID_OPTEE)236 return 1;237 else238 return 0;239}240 241static int optee_rtc_probe(struct device *dev)242{243 struct tee_client_device *rtc_device = to_tee_client_device(dev);244 struct tee_ioctl_open_session_arg sess_arg;245 struct optee_rtc *priv;246 struct rtc_device *rtc;247 struct tee_shm *shm;248 int ret, err;249 250 memset(&sess_arg, 0, sizeof(sess_arg));251 252 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);253 if (!priv)254 return -ENOMEM;255 256 rtc = devm_rtc_allocate_device(dev);257 if (IS_ERR(rtc))258 return PTR_ERR(rtc);259 260 /* Open context with TEE driver */261 priv->ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, NULL);262 if (IS_ERR(priv->ctx))263 return -ENODEV;264 265 /* Open session with rtc Trusted App */266 export_uuid(sess_arg.uuid, &rtc_device->id.uuid);267 sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL;268 269 ret = tee_client_open_session(priv->ctx, &sess_arg, NULL);270 if (ret < 0 || sess_arg.ret != 0) {271 dev_err(dev, "tee_client_open_session failed, err: %x\n", sess_arg.ret);272 err = -EINVAL;273 goto out_ctx;274 }275 priv->session_id = sess_arg.session;276 277 shm = tee_shm_alloc_kernel_buf(priv->ctx, sizeof(struct optee_rtc_info));278 if (IS_ERR(shm)) {279 dev_err(priv->dev, "tee_shm_alloc_kernel_buf failed\n");280 err = PTR_ERR(shm);281 goto out_sess;282 }283 284 priv->shm = shm;285 priv->dev = dev;286 dev_set_drvdata(dev, priv);287 288 rtc->ops = &optee_rtc_ops;289 290 err = optee_rtc_read_info(dev, rtc, &priv->features);291 if (err) {292 dev_err(dev, "Failed to get RTC features from OP-TEE\n");293 goto out_shm;294 }295 296 err = devm_rtc_register_device(rtc);297 if (err)298 goto out_shm;299 300 /*301 * We must clear this bit after registering because rtc_register_device302 * will set it if it sees that .set_offset is provided.303 */304 if (!(priv->features & TA_RTC_FEATURE_CORRECTION))305 clear_bit(RTC_FEATURE_CORRECTION, rtc->features);306 307 return 0;308 309out_shm:310 tee_shm_free(priv->shm);311out_sess:312 tee_client_close_session(priv->ctx, priv->session_id);313out_ctx:314 tee_client_close_context(priv->ctx);315 316 return err;317}318 319static int optee_rtc_remove(struct device *dev)320{321 struct optee_rtc *priv = dev_get_drvdata(dev);322 323 tee_client_close_session(priv->ctx, priv->session_id);324 tee_client_close_context(priv->ctx);325 326 return 0;327}328 329static const struct tee_client_device_id optee_rtc_id_table[] = {330 {UUID_INIT(0xf389f8c8, 0x845f, 0x496c,331 0x8b, 0xbe, 0xd6, 0x4b, 0xd2, 0x4c, 0x92, 0xfd)},332 {}333};334 335MODULE_DEVICE_TABLE(tee, optee_rtc_id_table);336 337static struct tee_client_driver optee_rtc_driver = {338 .id_table = optee_rtc_id_table,339 .driver = {340 .name = "optee_rtc",341 .bus = &tee_bus_type,342 .probe = optee_rtc_probe,343 .remove = optee_rtc_remove,344 },345};346 347static int __init optee_rtc_mod_init(void)348{349 return driver_register(&optee_rtc_driver.driver);350}351 352static void __exit optee_rtc_mod_exit(void)353{354 driver_unregister(&optee_rtc_driver.driver);355}356 357module_init(optee_rtc_mod_init);358module_exit(optee_rtc_mod_exit);359 360MODULE_LICENSE("GPL v2");361MODULE_AUTHOR("Clément Léger <clement.leger@bootlin.com>");362MODULE_DESCRIPTION("OP-TEE based RTC driver");363