Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

firmware: arm_ffa: Setup in-kernel users of FFA partitions

Parse the FFA nodes from the device-tree and register all the partitions
whose services will be used in the kernel.

In order to also enable in-kernel users of FFA interface, let us add
simple set of operations for such devices.

The in-kernel users are registered without the character device interface.

Link: https://lore.kernel.org/r/20210521151033.181846-5-sudeep.holla@arm.com
Tested-by: Jens Wiklander <jens.wiklander@linaro.org>
Reviewed-by: Jens Wiklander <jens.wiklander@linaro.org>
Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>

+271 -1
+9
drivers/firmware/arm_ffa/bus.c
··· 24 24 ffa_dev = to_ffa_dev(dev); 25 25 26 26 while (!uuid_is_null(&id_table->uuid)) { 27 + /* 28 + * FF-A v1.0 doesn't provide discovery of UUIDs, just the 29 + * partition IDs, so fetch the partitions IDs for this 30 + * id_table UUID and assign the UUID to the device if the 31 + * partition ID matches 32 + */ 33 + if (uuid_is_null(&ffa_dev->uuid)) 34 + ffa_device_match_uuid(ffa_dev, &id_table->uuid); 35 + 27 36 if (uuid_equal(&ffa_dev->uuid, &id_table->uuid)) 28 37 return 1; 29 38 id_table++;
+3
drivers/firmware/arm_ffa/common.h
··· 6 6 #ifndef _FFA_COMMON_H 7 7 #define _FFA_COMMON_H 8 8 9 + #include <linux/arm_ffa.h> 9 10 #include <linux/arm-smccc.h> 10 11 #include <linux/err.h> 11 12 ··· 16 15 17 16 int arm_ffa_bus_init(void); 18 17 void arm_ffa_bus_exit(void); 18 + bool ffa_device_is_valid(struct ffa_device *ffa_dev); 19 + void ffa_device_match_uuid(struct ffa_device *ffa_dev, const uuid_t *uuid); 19 20 20 21 #ifdef CONFIG_ARM_FFA_SMCCC 21 22 int __init ffa_transport_init(ffa_fn **invoke_ffa_fn);
+221
drivers/firmware/arm_ffa/driver.c
··· 24 24 25 25 #include <linux/arm_ffa.h> 26 26 #include <linux/bitfield.h> 27 + #include <linux/device.h> 27 28 #include <linux/io.h> 29 + #include <linux/kernel.h> 28 30 #include <linux/module.h> 29 31 #include <linux/slab.h> 32 + #include <linux/uuid.h> 30 33 31 34 #include "common.h" 32 35 ··· 188 185 return 0; 189 186 } 190 187 188 + static int ffa_rx_release(void) 189 + { 190 + ffa_value_t ret; 191 + 192 + invoke_ffa_fn((ffa_value_t){ 193 + .a0 = FFA_RX_RELEASE, 194 + }, &ret); 195 + 196 + if (ret.a0 == FFA_ERROR) 197 + return ffa_to_linux_errno((int)ret.a2); 198 + 199 + /* check for ret.a0 == FFA_RX_RELEASE ? */ 200 + 201 + return 0; 202 + } 203 + 191 204 static int ffa_rxtx_map(phys_addr_t tx_buf, phys_addr_t rx_buf, u32 pg_cnt) 192 205 { 193 206 ffa_value_t ret; ··· 233 214 return 0; 234 215 } 235 216 217 + /* buffer must be sizeof(struct ffa_partition_info) * num_partitions */ 218 + static int 219 + __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3, 220 + struct ffa_partition_info *buffer, int num_partitions) 221 + { 222 + int count; 223 + ffa_value_t partition_info; 224 + 225 + mutex_lock(&drv_info->rx_lock); 226 + invoke_ffa_fn((ffa_value_t){ 227 + .a0 = FFA_PARTITION_INFO_GET, 228 + .a1 = uuid0, .a2 = uuid1, .a3 = uuid2, .a4 = uuid3, 229 + }, &partition_info); 230 + 231 + if (partition_info.a0 == FFA_ERROR) { 232 + mutex_unlock(&drv_info->rx_lock); 233 + return ffa_to_linux_errno((int)partition_info.a2); 234 + } 235 + 236 + count = partition_info.a2; 237 + 238 + if (buffer && count <= num_partitions) 239 + memcpy(buffer, drv_info->rx_buffer, sizeof(*buffer) * count); 240 + 241 + ffa_rx_release(); 242 + 243 + mutex_unlock(&drv_info->rx_lock); 244 + 245 + return count; 246 + } 247 + 248 + /* buffer is allocated and caller must free the same if returned count > 0 */ 249 + static int 250 + ffa_partition_probe(const uuid_t *uuid, struct ffa_partition_info **buffer) 251 + { 252 + int count; 253 + u32 uuid0_4[4]; 254 + struct ffa_partition_info *pbuf; 255 + 256 + export_uuid((u8 *)uuid0_4, uuid); 257 + count = __ffa_partition_info_get(uuid0_4[0], uuid0_4[1], uuid0_4[2], 258 + uuid0_4[3], NULL, 0); 259 + if (count <= 0) 260 + return count; 261 + 262 + pbuf = kcalloc(count, sizeof(*pbuf), GFP_KERNEL); 263 + if (!pbuf) 264 + return -ENOMEM; 265 + 266 + count = __ffa_partition_info_get(uuid0_4[0], uuid0_4[1], uuid0_4[2], 267 + uuid0_4[3], pbuf, count); 268 + if (count <= 0) 269 + kfree(pbuf); 270 + else 271 + *buffer = pbuf; 272 + 273 + return count; 274 + } 275 + 236 276 #define VM_ID_MASK GENMASK(15, 0) 237 277 static int ffa_id_get(u16 *vm_id) 238 278 { ··· 307 229 *vm_id = FIELD_GET(VM_ID_MASK, (id.a2)); 308 230 309 231 return 0; 232 + } 233 + 234 + static int ffa_msg_send_direct_req(u16 src_id, u16 dst_id, bool mode_32bit, 235 + struct ffa_send_direct_data *data) 236 + { 237 + u32 req_id, resp_id, src_dst_ids = PACK_TARGET_INFO(src_id, dst_id); 238 + ffa_value_t ret; 239 + 240 + if (mode_32bit) { 241 + req_id = FFA_MSG_SEND_DIRECT_REQ; 242 + resp_id = FFA_MSG_SEND_DIRECT_RESP; 243 + } else { 244 + req_id = FFA_FN_NATIVE(MSG_SEND_DIRECT_REQ); 245 + resp_id = FFA_FN_NATIVE(MSG_SEND_DIRECT_RESP); 246 + } 247 + 248 + invoke_ffa_fn((ffa_value_t){ 249 + .a0 = req_id, .a1 = src_dst_ids, .a2 = 0, 250 + .a3 = data->data0, .a4 = data->data1, .a5 = data->data2, 251 + .a6 = data->data3, .a7 = data->data4, 252 + }, &ret); 253 + 254 + while (ret.a0 == FFA_INTERRUPT) 255 + invoke_ffa_fn((ffa_value_t){ 256 + .a0 = FFA_RUN, .a1 = ret.a1, 257 + }, &ret); 258 + 259 + if (ret.a0 == FFA_ERROR) 260 + return ffa_to_linux_errno((int)ret.a2); 261 + 262 + if (ret.a0 == resp_id) { 263 + data->data0 = ret.a3; 264 + data->data1 = ret.a4; 265 + data->data2 = ret.a5; 266 + data->data3 = ret.a6; 267 + data->data4 = ret.a7; 268 + return 0; 269 + } 270 + 271 + return -EINVAL; 272 + } 273 + 274 + static u32 ffa_api_version_get(void) 275 + { 276 + return drv_info->version; 277 + } 278 + 279 + static int ffa_partition_info_get(const char *uuid_str, 280 + struct ffa_partition_info *buffer) 281 + { 282 + int count; 283 + uuid_t uuid; 284 + struct ffa_partition_info *pbuf; 285 + 286 + if (uuid_parse(uuid_str, &uuid)) { 287 + pr_err("invalid uuid (%s)\n", uuid_str); 288 + return -ENODEV; 289 + } 290 + 291 + count = ffa_partition_probe(&uuid_null, &pbuf); 292 + if (count <= 0) 293 + return -ENOENT; 294 + 295 + memcpy(buffer, pbuf, sizeof(*pbuf) * count); 296 + kfree(pbuf); 297 + return 0; 298 + } 299 + 300 + static void ffa_mode_32bit_set(struct ffa_device *dev) 301 + { 302 + dev->mode_32bit = true; 303 + } 304 + 305 + static int ffa_sync_send_receive(struct ffa_device *dev, 306 + struct ffa_send_direct_data *data) 307 + { 308 + return ffa_msg_send_direct_req(drv_info->vm_id, dev->vm_id, 309 + dev->mode_32bit, data); 310 + } 311 + 312 + static const struct ffa_dev_ops ffa_ops = { 313 + .api_version_get = ffa_api_version_get, 314 + .partition_info_get = ffa_partition_info_get, 315 + .mode_32bit_set = ffa_mode_32bit_set, 316 + .sync_send_receive = ffa_sync_send_receive, 317 + }; 318 + 319 + const struct ffa_dev_ops *ffa_dev_ops_get(struct ffa_device *dev) 320 + { 321 + if (ffa_device_is_valid(dev)) 322 + return &ffa_ops; 323 + 324 + return NULL; 325 + } 326 + EXPORT_SYMBOL_GPL(ffa_dev_ops_get); 327 + 328 + void ffa_device_match_uuid(struct ffa_device *ffa_dev, const uuid_t *uuid) 329 + { 330 + int count, idx; 331 + struct ffa_partition_info *pbuf, *tpbuf; 332 + 333 + count = ffa_partition_probe(uuid, &pbuf); 334 + if (count <= 0) 335 + return; 336 + 337 + for (idx = 0, tpbuf = pbuf; idx < count; idx++, tpbuf++) 338 + if (tpbuf->id == ffa_dev->vm_id) 339 + uuid_copy(&ffa_dev->uuid, uuid); 340 + kfree(pbuf); 341 + } 342 + 343 + static void ffa_setup_partitions(void) 344 + { 345 + int count, idx; 346 + struct ffa_device *ffa_dev; 347 + struct ffa_partition_info *pbuf, *tpbuf; 348 + 349 + count = ffa_partition_probe(&uuid_null, &pbuf); 350 + if (count <= 0) { 351 + pr_info("%s: No partitions found, error %d\n", __func__, count); 352 + return; 353 + } 354 + 355 + for (idx = 0, tpbuf = pbuf; idx < count; idx++, tpbuf++) { 356 + /* Note that the &uuid_null parameter will require 357 + * ffa_device_match() to find the UUID of this partition id 358 + * with help of ffa_device_match_uuid(). Once the FF-A spec 359 + * is updated to provide correct UUID here for each partition 360 + * as part of the discovery API, we need to pass the 361 + * discovered UUID here instead. 362 + */ 363 + ffa_dev = ffa_device_register(&uuid_null, tpbuf->id); 364 + if (!ffa_dev) { 365 + pr_err("%s: failed to register partition ID 0x%x\n", 366 + __func__, tpbuf->id); 367 + continue; 368 + } 369 + 370 + ffa_dev_set_drvdata(ffa_dev, drv_info); 371 + } 372 + kfree(pbuf); 310 373 } 311 374 312 375 static int __init ffa_init(void) ··· 500 281 501 282 mutex_init(&drv_info->rx_lock); 502 283 mutex_init(&drv_info->tx_lock); 284 + 285 + ffa_setup_partitions(); 503 286 504 287 return 0; 505 288 free_pages:
+38 -1
include/linux/arm_ffa.h
··· 6 6 #ifndef _LINUX_ARM_FFA_H 7 7 #define _LINUX_ARM_FFA_H 8 8 9 - #include <linux/cdev.h> 10 9 #include <linux/device.h> 11 10 #include <linux/module.h> 12 11 #include <linux/types.h> ··· 14 15 /* FFA Bus/Device/Driver related */ 15 16 struct ffa_device { 16 17 int vm_id; 18 + bool mode_32bit; 17 19 uuid_t uuid; 18 20 struct device dev; 19 21 }; ··· 48 48 const char *mod_name); 49 49 void ffa_driver_unregister(struct ffa_driver *driver); 50 50 bool ffa_device_is_valid(struct ffa_device *ffa_dev); 51 + const struct ffa_dev_ops *ffa_dev_ops_get(struct ffa_device *dev); 51 52 52 53 #else 53 54 static inline ··· 71 70 static inline 72 71 bool ffa_device_is_valid(struct ffa_device *ffa_dev) { return false; } 73 72 73 + static inline 74 + const struct ffa_dev_ops *ffa_dev_ops_get(struct ffa_device *dev) 75 + { 76 + return NULL; 77 + } 74 78 #endif /* CONFIG_ARM_FFA_TRANSPORT */ 75 79 76 80 #define ffa_register(driver) \ ··· 93 87 */ 94 88 #define module_ffa_driver(__ffa_driver) \ 95 89 module_driver(__ffa_driver, ffa_register, ffa_unregister) 90 + 91 + /* FFA transport related */ 92 + struct ffa_partition_info { 93 + u16 id; 94 + u16 exec_ctxt; 95 + /* partition supports receipt of direct requests */ 96 + #define FFA_PARTITION_DIRECT_RECV BIT(0) 97 + /* partition can send direct requests. */ 98 + #define FFA_PARTITION_DIRECT_SEND BIT(1) 99 + /* partition can send and receive indirect messages. */ 100 + #define FFA_PARTITION_INDIRECT_MSG BIT(2) 101 + u32 properties; 102 + }; 103 + 104 + /* For use with FFA_MSG_SEND_DIRECT_{REQ,RESP} which pass data via registers */ 105 + struct ffa_send_direct_data { 106 + unsigned long data0; /* w3/x3 */ 107 + unsigned long data1; /* w4/x4 */ 108 + unsigned long data2; /* w5/x5 */ 109 + unsigned long data3; /* w6/x6 */ 110 + unsigned long data4; /* w7/x7 */ 111 + }; 112 + 113 + struct ffa_dev_ops { 114 + u32 (*api_version_get)(void); 115 + int (*partition_info_get)(const char *uuid_str, 116 + struct ffa_partition_info *buffer); 117 + void (*mode_32bit_set)(struct ffa_device *dev); 118 + int (*sync_send_receive)(struct ffa_device *dev, 119 + struct ffa_send_direct_data *data); 120 + }; 96 121 97 122 #endif /* _LINUX_ARM_FFA_H */