From 4cf128e76b57795eb61a087ebe544fbcb7ee4077 Mon Sep 17 00:00:00 2001 From: AnilKumar Chimata Date: Thu, 13 Jul 2017 01:22:31 +0530 Subject: [PATCH] msm: mink: smcinvoke: Add SMCinvoke driver snapshot This is a snapshot of smcinvoke driver as of msm-4.4 commit <92ccb2945524a> (cnss2: Set PCIe to D3hot state before suspending PCIe link). Change-Id: Idc6aea685bbe26d7bccba3a7d6de7a1c215209e4 Signed-off-by: AnilKumar Chimata --- .../misc-devices/qcom_invoke_driver.txt | 54 ++ drivers/soc/qcom/Kconfig | 8 + drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/smcinvoke.c | 575 ++++++++++++++++++ drivers/soc/qcom/smcinvoke_object.h | 51 ++ 5 files changed, 689 insertions(+) create mode 100644 Documentation/misc-devices/qcom_invoke_driver.txt create mode 100644 drivers/soc/qcom/smcinvoke.c create mode 100644 drivers/soc/qcom/smcinvoke_object.h diff --git a/Documentation/misc-devices/qcom_invoke_driver.txt b/Documentation/misc-devices/qcom_invoke_driver.txt new file mode 100644 index 000000000000..38c976af5f8d --- /dev/null +++ b/Documentation/misc-devices/qcom_invoke_driver.txt @@ -0,0 +1,54 @@ +Introduction: +============= +Invoke driver is a misc driver which helps communication between non secure +and secure world. Invoke driver communicates with secure side using SCM +driver. To use invoke driver, open must be called on invoke device i.e. +/dev/invoke. Invoke driver exposes only one IOCTL invoke which passes +userspace request to TZ. + +SW Architecture +=============== +Following is SW stack for Invoke driver. + ++++++++++++++++++++++++++++++++++++++++++ ++ Applications + ++++++++++++++++++++++++++++++++++++++++++ ++ System Layer + ++++++++++++++++++++++++++++++++++++++++++ ++ Kernel + ++ +++++++++++++++++++ + ++ + Invoke driver + + ++ +++++++++++++++++++ + ++ + SCM Driver + + ++++++++++++++++++++++++++++++++++++++++++ + || + || + \/ ++++++++++++++++++++++++++++++++++++++++++ ++ Trust Zone + ++ +++++++++++ +++++++++++ + ++ + TZ App1 + + TZ App2 + + ++++++++++++++++++++++++++++++++++++++++++ + + +Interfaces +========== +Invoke driver exposes INVOKE_IOCTL_INVOKE_REQ IOCTL for userspace to +communicate with driver. More details of IOCTL are avilable in +corresponding header file. + + +Driver Parameters +================= +This driver is built and statically linked into the kernel; therefore, +there are no module parameters supported by this driver. + +There are no kernel command line parameters supported by this driver. + +Power Management +================ +TBD + +Dependencies +============ +Invoke driver depends on SCM driver to communicate with TZ. diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index e6c2aa3aeacd..1f7286b1b0f4 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -632,6 +632,14 @@ config WCD_DSP_GLINK between MSM and WCD DSP over glink transport protocol. This driver provides read and write interface via char device. +config QCOM_SMCINVOKE + bool "Secure QSEE Support" + help + Enable SMCInvoke driver which supports capability based secure + communication between QTI Secure Execution Environment (QSEE) + and high level operating system. It exposes APIs for both + userspace and kernel clients. + config MSM_EVENT_TIMER bool "Event timer" help diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 64fb7a057f87..7820f8b4a7c9 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -77,3 +77,4 @@ obj-$(CONFIG_MSM_IDLE_STATS) += lpm-stats.o obj-$(CONFIG_APSS_CORE_EA) += msm-core.o debug_core.o obj-$(CONFIG_QCOM_DCC_V2) += dcc_v2.o obj-$(CONFIG_QTI_RPM_STATS_LOG) += rpm_stats.o +obj-$(CONFIG_QCOM_SMCINVOKE) += smcinvoke.o diff --git a/drivers/soc/qcom/smcinvoke.c b/drivers/soc/qcom/smcinvoke.c new file mode 100644 index 000000000000..3f31fb1216b2 --- /dev/null +++ b/drivers/soc/qcom/smcinvoke.c @@ -0,0 +1,575 @@ +/* + * SMC Invoke driver + * + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "smcinvoke_object.h" +#include "../../misc/qseecom_kernel.h" + +#define SMCINVOKE_DEV "smcinvoke" +#define SMCINVOKE_TZ_PARAM_ID 0x224 +#define SMCINVOKE_TZ_CMD 0x32000600 +#define SMCINVOKE_TZ_ROOT_OBJ 1 +#define SMCINVOKE_TZ_MIN_BUF_SIZE 4096 +#define SMCINVOKE_ARGS_ALIGN_SIZE (sizeof(uint64_t)) +#define SMCINVOKE_TZ_OBJ_NULL 0 + +#define FOR_ARGS(ndxvar, counts, section) \ + for (ndxvar = object_counts_index_##section(counts); \ + ndxvar < (object_counts_index_##section(counts) \ + + object_counts_num_##section(counts)); \ + ++ndxvar) + +static long smcinvoke_ioctl(struct file *, unsigned int, unsigned long); +static int smcinvoke_open(struct inode *, struct file *); +static int smcinvoke_release(struct inode *, struct file *); + +static const struct file_operations smcinvoke_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = smcinvoke_ioctl, + .compat_ioctl = smcinvoke_ioctl, + .open = smcinvoke_open, + .release = smcinvoke_release, +}; + +struct smcinvoke_buf_hdr { + uint32_t offset; + uint32_t size; +}; + +union smcinvoke_tz_args { + struct smcinvoke_buf_hdr b; + uint32_t tzhandle; +}; +struct smcinvoke_msg_hdr { + uint32_t tzhandle; + uint32_t op; + uint32_t counts; +}; + +struct smcinvoke_tzobj_context { + uint32_t tzhandle; +}; + +static dev_t smcinvoke_device_no; +struct cdev smcinvoke_cdev; +struct class *driver_class; +struct device *class_dev; + +/* + * size_add saturates at SIZE_MAX. If integer overflow is detected, + * this function would return SIZE_MAX otherwise normal a+b is returned. + */ +static inline size_t size_add(size_t a, size_t b) +{ + return (b > (SIZE_MAX - a)) ? SIZE_MAX : a + b; +} + +/* + * pad_size is used along with size_align to define a buffer overflow + * protected version of ALIGN + */ +static inline size_t pad_size(size_t a, size_t b) +{ + return (~a + 1) % b; +} + +/* + * size_align saturates at SIZE_MAX. If integer overflow is detected, this + * function would return SIZE_MAX otherwise next aligned size is returned. + */ +static inline size_t size_align(size_t a, size_t b) +{ + return size_add(a, pad_size(a, b)); +} + +/* + * This function retrieves file pointer corresponding to FD provided. It stores + * retrived file pointer until IOCTL call is concluded. Once call is completed, + * all stored file pointers are released. file pointers are stored to prevent + * other threads from releasing that FD while IOCTL is in progress. + */ +static int get_tzhandle_from_fd(int64_t fd, struct file **filp, + uint32_t *tzhandle) +{ + int ret = -EBADF; + struct file *tmp_filp = NULL; + struct smcinvoke_tzobj_context *tzobj = NULL; + + if (fd == SMCINVOKE_USERSPACE_OBJ_NULL) { + *tzhandle = SMCINVOKE_TZ_OBJ_NULL; + ret = 0; + goto out; + } else if (fd < SMCINVOKE_USERSPACE_OBJ_NULL) { + goto out; + } + + tmp_filp = fget(fd); + if (!tmp_filp) + goto out; + + /* Verify if filp is smcinvoke device's file pointer */ + if (!tmp_filp->f_op || !tmp_filp->private_data || + (tmp_filp->f_op != &smcinvoke_fops)) { + fput(tmp_filp); + goto out; + } + + tzobj = tmp_filp->private_data; + *tzhandle = tzobj->tzhandle; + *filp = tmp_filp; + ret = 0; +out: + return ret; +} + +static int get_fd_from_tzhandle(uint32_t tzhandle, int64_t *fd) +{ + int unused_fd = -1, ret = -1; + struct file *f = NULL; + struct smcinvoke_tzobj_context *cxt = NULL; + + if (tzhandle == SMCINVOKE_TZ_OBJ_NULL) { + *fd = SMCINVOKE_USERSPACE_OBJ_NULL; + ret = 0; + goto out; + } + + cxt = kzalloc(sizeof(*cxt), GFP_KERNEL); + if (!cxt) { + ret = -ENOMEM; + goto out; + } + unused_fd = get_unused_fd_flags(O_RDWR); + if (unused_fd < 0) + goto out; + + f = anon_inode_getfile(SMCINVOKE_DEV, &smcinvoke_fops, cxt, O_RDWR); + if (IS_ERR(f)) + goto out; + + *fd = unused_fd; + fd_install(*fd, f); + ((struct smcinvoke_tzobj_context *) + (f->private_data))->tzhandle = tzhandle; + return 0; +out: + if (unused_fd >= 0) + put_unused_fd(unused_fd); + kfree(cxt); + + return ret; +} + +static int prepare_send_scm_msg(const uint8_t *in_buf, size_t in_buf_len, + const uint8_t *out_buf, size_t out_buf_len, + int32_t *smcinvoke_result) +{ + int ret = 0; + struct scm_desc desc = {0}; + size_t inbuf_flush_size = (1UL << get_order(in_buf_len)) * PAGE_SIZE; + size_t outbuf_flush_size = (1UL << get_order(out_buf_len)) * PAGE_SIZE; + + desc.arginfo = SMCINVOKE_TZ_PARAM_ID; + desc.args[0] = (uint64_t)virt_to_phys(in_buf); + desc.args[1] = inbuf_flush_size; + desc.args[2] = (uint64_t)virt_to_phys(out_buf); + desc.args[3] = outbuf_flush_size; + + dmac_flush_range(in_buf, in_buf + inbuf_flush_size); + dmac_flush_range(out_buf, out_buf + outbuf_flush_size); + + ret = scm_call2(SMCINVOKE_TZ_CMD, &desc); + + /* process listener request */ + if (!ret && (desc.ret[0] == QSEOS_RESULT_INCOMPLETE || + desc.ret[0] == QSEOS_RESULT_BLOCKED_ON_LISTENER)) + ret = qseecom_process_listener_from_smcinvoke(&desc); + + *smcinvoke_result = (int32_t)desc.ret[1]; + if (ret || desc.ret[1] || desc.ret[2] || desc.ret[0]) + pr_err("SCM call failed with ret val = %d %d %d %d\n", + ret, (int)desc.ret[0], + (int)desc.ret[1], (int)desc.ret[2]); + + dmac_inv_range(in_buf, in_buf + inbuf_flush_size); + dmac_inv_range(out_buf, out_buf + outbuf_flush_size); + return ret; +} + +static int marshal_out(void *buf, uint32_t buf_size, + struct smcinvoke_cmd_req *req, + union smcinvoke_arg *args_buf) +{ + int ret = -EINVAL, i = 0; + union smcinvoke_tz_args *tz_args = NULL; + size_t offset = sizeof(struct smcinvoke_msg_hdr) + + object_counts_total(req->counts) * + sizeof(union smcinvoke_tz_args); + + if (offset > buf_size) + goto out; + + tz_args = (union smcinvoke_tz_args *) + (buf + sizeof(struct smcinvoke_msg_hdr)); + + tz_args += object_counts_num_BI(req->counts); + + FOR_ARGS(i, req->counts, BO) { + args_buf[i].b.size = tz_args->b.size; + if ((buf_size - tz_args->b.offset < tz_args->b.size) || + tz_args->b.offset > buf_size) { + pr_err("%s: buffer overflow detected\n", __func__); + goto out; + } + if (copy_to_user((void __user *)(uintptr_t)(args_buf[i].b.addr), + (uint8_t *)(buf) + tz_args->b.offset, + tz_args->b.size)) { + pr_err("Error %d copying ctxt to user\n", ret); + goto out; + } + tz_args++; + } + tz_args += object_counts_num_OI(req->counts); + + FOR_ARGS(i, req->counts, OO) { + /* + * create a new FD and assign to output object's + * context + */ + ret = get_fd_from_tzhandle(tz_args->tzhandle, + &(args_buf[i].o.fd)); + if (ret) + goto out; + tz_args++; + } + ret = 0; +out: + return ret; +} + +/* + * SMC expects arguments in following format + * --------------------------------------------------------------------------- + * | cxt | op | counts | ptr|size |ptr|size...|ORef|ORef|...| rest of payload | + * --------------------------------------------------------------------------- + * cxt: target, op: operation, counts: total arguments + * offset: offset is from beginning of buffer i.e. cxt + * size: size is 8 bytes aligned value + */ +static size_t compute_in_msg_size(const struct smcinvoke_cmd_req *req, + const union smcinvoke_arg *args_buf) +{ + uint32_t i = 0; + + size_t total_size = sizeof(struct smcinvoke_msg_hdr) + + object_counts_total(req->counts) * + sizeof(union smcinvoke_tz_args); + + /* Computed total_size should be 8 bytes aligned from start of buf */ + total_size = ALIGN(total_size, SMCINVOKE_ARGS_ALIGN_SIZE); + + /* each buffer has to be 8 bytes aligned */ + while (i < object_counts_num_buffers(req->counts)) + total_size = size_add(total_size, + size_align(args_buf[i++].b.size, SMCINVOKE_ARGS_ALIGN_SIZE)); + + /* Since we're using get_free_pages, no need for explicit PAGE align */ + return total_size; +} + +static int marshal_in(const struct smcinvoke_cmd_req *req, + const union smcinvoke_arg *args_buf, uint32_t tzhandle, + uint8_t *buf, size_t buf_size, struct file **arr_filp) +{ + int ret = -EINVAL, i = 0; + union smcinvoke_tz_args *tz_args = NULL; + struct smcinvoke_msg_hdr msg_hdr = {tzhandle, req->op, req->counts}; + uint32_t offset = sizeof(struct smcinvoke_msg_hdr) + + sizeof(union smcinvoke_tz_args) * + object_counts_total(req->counts); + + if (buf_size < offset) + goto out; + + *(struct smcinvoke_msg_hdr *)buf = msg_hdr; + tz_args = (union smcinvoke_tz_args *) + (buf + sizeof(struct smcinvoke_msg_hdr)); + + FOR_ARGS(i, req->counts, BI) { + offset = size_align(offset, SMCINVOKE_ARGS_ALIGN_SIZE); + if ((offset > buf_size) || + (args_buf[i].b.size > (buf_size - offset))) + goto out; + + tz_args->b.offset = offset; + tz_args->b.size = args_buf[i].b.size; + tz_args++; + + if (copy_from_user(buf+offset, + (void __user *)(uintptr_t)(args_buf[i].b.addr), + args_buf[i].b.size)) + goto out; + + offset += args_buf[i].b.size; + } + FOR_ARGS(i, req->counts, BO) { + offset = size_align(offset, SMCINVOKE_ARGS_ALIGN_SIZE); + if ((offset > buf_size) || + (args_buf[i].b.size > (buf_size - offset))) + goto out; + + tz_args->b.offset = offset; + tz_args->b.size = args_buf[i].b.size; + tz_args++; + + offset += args_buf[i].b.size; + } + FOR_ARGS(i, req->counts, OI) { + if (get_tzhandle_from_fd(args_buf[i].o.fd, + &arr_filp[i], &(tz_args->tzhandle))) + goto out; + tz_args++; + } + ret = 0; +out: + return ret; +} + +long smcinvoke_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int ret = -1, i = 0, nr_args = 0; + struct smcinvoke_cmd_req req = {0}; + void *in_msg = NULL; + size_t inmsg_size = 0; + void *out_msg = NULL; + union smcinvoke_arg *args_buf = NULL; + struct file *filp_to_release[object_counts_max_OO] = {NULL}; + struct smcinvoke_tzobj_context *tzobj = filp->private_data; + + switch (cmd) { + case SMCINVOKE_IOCTL_INVOKE_REQ: + if (_IOC_SIZE(cmd) != sizeof(req)) { + ret = -EINVAL; + goto out; + } + ret = copy_from_user(&req, (void __user *)arg, sizeof(req)); + if (ret) { + ret = -EFAULT; + goto out; + } + + nr_args = object_counts_num_buffers(req.counts) + + object_counts_num_objects(req.counts); + + if (req.argsize != sizeof(union smcinvoke_arg)) { + ret = -EINVAL; + goto out; + } + + if (nr_args) { + + args_buf = kzalloc(nr_args * req.argsize, GFP_KERNEL); + if (!args_buf) { + ret = -ENOMEM; + goto out; + } + + ret = copy_from_user(args_buf, + (void __user *)(uintptr_t)(req.args), + nr_args * req.argsize); + + if (ret) { + ret = -EFAULT; + goto out; + } + } + + inmsg_size = compute_in_msg_size(&req, args_buf); + in_msg = (void *)__get_free_pages(GFP_KERNEL, + get_order(inmsg_size)); + if (!in_msg) { + ret = -ENOMEM; + goto out; + } + + out_msg = (void *)__get_free_page(GFP_KERNEL); + if (!out_msg) { + ret = -ENOMEM; + goto out; + } + + ret = marshal_in(&req, args_buf, tzobj->tzhandle, in_msg, + inmsg_size, filp_to_release); + if (ret) + goto out; + + ret = prepare_send_scm_msg(in_msg, inmsg_size, out_msg, + SMCINVOKE_TZ_MIN_BUF_SIZE, &req.result); + if (ret) + goto out; + + /* + * if invoke op results in an err, no need to marshal_out and + * copy args buf to user space + */ + if (!req.result) { + ret = marshal_out(in_msg, inmsg_size, &req, args_buf); + + ret |= copy_to_user( + (void __user *)(uintptr_t)(req.args), + args_buf, nr_args * req.argsize); + } + ret |= copy_to_user((void __user *)arg, &req, sizeof(req)); + if (ret) + goto out; + + break; + default: + ret = -ENOIOCTLCMD; + break; + } +out: + free_page((long)out_msg); + free_pages((long)in_msg, get_order(inmsg_size)); + kfree(args_buf); + for (i = 0; i < object_counts_max_OO; i++) { + if (filp_to_release[i]) + fput(filp_to_release[i]); + } + + return ret; +} + +static int smcinvoke_open(struct inode *nodp, struct file *filp) +{ + struct smcinvoke_tzobj_context *tzcxt = NULL; + + tzcxt = kzalloc(sizeof(*tzcxt), GFP_KERNEL); + if (!tzcxt) + return -ENOMEM; + + tzcxt->tzhandle = SMCINVOKE_TZ_ROOT_OBJ; + filp->private_data = tzcxt; + + return 0; +} + +static int smcinvoke_release(struct inode *nodp, struct file *filp) +{ + int ret = 0, smcinvoke_result = 0; + uint8_t *in_buf = NULL; + uint8_t *out_buf = NULL; + struct smcinvoke_msg_hdr hdr = {0}; + struct smcinvoke_tzobj_context *tzobj = filp->private_data; + uint32_t tzhandle = tzobj->tzhandle; + + /* Root object is special in sense it is indestructible */ + if (!tzhandle || tzhandle == SMCINVOKE_TZ_ROOT_OBJ) + goto out; + + in_buf = (uint8_t *)__get_free_page(GFP_KERNEL); + out_buf = (uint8_t *)__get_free_page(GFP_KERNEL); + if (!in_buf || !out_buf) + goto out; + + hdr.tzhandle = tzhandle; + hdr.op = object_op_RELEASE; + hdr.counts = 0; + *(struct smcinvoke_msg_hdr *)in_buf = hdr; + + ret = prepare_send_scm_msg(in_buf, SMCINVOKE_TZ_MIN_BUF_SIZE, + out_buf, SMCINVOKE_TZ_MIN_BUF_SIZE, &smcinvoke_result); +out: + kfree(filp->private_data); + free_page((long)in_buf); + free_page((long)out_buf); + + return ret; +} + +static int __init smcinvoke_init(void) +{ + unsigned int baseminor = 0; + unsigned int count = 1; + int rc = 0; + + rc = alloc_chrdev_region(&smcinvoke_device_no, baseminor, count, + SMCINVOKE_DEV); + if (rc < 0) { + pr_err("chrdev_region failed %d for %s\n", rc, SMCINVOKE_DEV); + return rc; + } + driver_class = class_create(THIS_MODULE, SMCINVOKE_DEV); + if (IS_ERR(driver_class)) { + rc = -ENOMEM; + pr_err("class_create failed %d\n", rc); + goto exit_unreg_chrdev_region; + } + class_dev = device_create(driver_class, NULL, smcinvoke_device_no, + NULL, SMCINVOKE_DEV); + if (!class_dev) { + pr_err("class_device_create failed %d\n", rc); + rc = -ENOMEM; + goto exit_destroy_class; + } + + cdev_init(&smcinvoke_cdev, &smcinvoke_fops); + smcinvoke_cdev.owner = THIS_MODULE; + + rc = cdev_add(&smcinvoke_cdev, MKDEV(MAJOR(smcinvoke_device_no), 0), + count); + if (rc < 0) { + pr_err("cdev_add failed %d for %s\n", rc, SMCINVOKE_DEV); + goto exit_destroy_device; + } + return 0; + +exit_destroy_device: + device_destroy(driver_class, smcinvoke_device_no); +exit_destroy_class: + class_destroy(driver_class); +exit_unreg_chrdev_region: + unregister_chrdev_region(smcinvoke_device_no, count); + + return rc; +} + +static void __exit smcinvoke_exit(void) +{ + int count = 1; + + cdev_del(&smcinvoke_cdev); + device_destroy(driver_class, smcinvoke_device_no); + class_destroy(driver_class); + unregister_chrdev_region(smcinvoke_device_no, count); +} +device_initcall(smcinvoke_init); +module_exit(smcinvoke_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("SMC Invoke driver"); diff --git a/drivers/soc/qcom/smcinvoke_object.h b/drivers/soc/qcom/smcinvoke_object.h new file mode 100644 index 000000000000..670b425cabe5 --- /dev/null +++ b/drivers/soc/qcom/smcinvoke_object.h @@ -0,0 +1,51 @@ +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __SMCINVOKE_OBJECT_H +#define __SMCINVOKE_OBJECT_H + +#include + +#define object_op_METHOD_MASK ((uint32_t)0x0000FFFFu) +#define object_op_RELEASE (object_op_METHOD_MASK - 0) +#define object_op_RETAIN (object_op_METHOD_MASK - 1) + +#define object_counts_max_BI 0xF +#define object_counts_max_BO 0xF +#define object_counts_max_OI 0xF +#define object_counts_max_OO 0xF + +/* unpack counts */ + +#define object_counts_num_BI(k) ((size_t) (((k) >> 0) & object_counts_max_BI)) +#define object_counts_num_BO(k) ((size_t) (((k) >> 4) & object_counts_max_BO)) +#define object_counts_num_OI(k) ((size_t) (((k) >> 8) & object_counts_max_OI)) +#define object_counts_num_OO(k) ((size_t) (((k) >> 12) & object_counts_max_OO)) +#define object_counts_num_buffers(k) \ + (object_counts_num_BI(k) + object_counts_num_BO(k)) + +#define object_counts_num_objects(k) \ + (object_counts_num_OI(k) + object_counts_num_OO(k)) + +/* Indices into args[] */ + +#define object_counts_index_BI(k) 0 +#define object_counts_index_BO(k) \ + (object_counts_index_BI(k) + object_counts_num_BI(k)) +#define object_counts_index_OI(k) \ + (object_counts_index_BO(k) + object_counts_num_BO(k)) +#define object_counts_index_OO(k) \ + (object_counts_index_OI(k) + object_counts_num_OI(k)) +#define object_counts_total(k) \ + (object_counts_index_OO(k) + object_counts_num_OO(k)) + + +#endif /* __SMCINVOKE_OBJECT_H */