1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "libnos_datagram"
18 #include <log/log.h>
19 #include <nos/device.h>
20
21 #include <ctype.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <getopt.h>
25 #include <linux/types.h>
26 #include <poll.h>
27 #include <pthread.h>
28 #include <stdarg.h>
29 #include <stdint.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/ioctl.h>
34 #include <unistd.h>
35
36 /*****************************************************************************/
37 /* Ideally, this should be in <linux/citadel.h> */
38 #define CITADEL_IOC_MAGIC 'c'
39 struct citadel_ioc_tpm_datagram {
40 __u64 buf;
41 __u32 len;
42 __u32 command;
43 };
44 #define CITADEL_IOC_TPM_DATAGRAM _IOW(CITADEL_IOC_MAGIC, 1, \
45 struct citadel_ioc_tpm_datagram)
46 #define CITADEL_IOC_RESET _IO(CITADEL_IOC_MAGIC, 2)
47 /*****************************************************************************/
48
49 #define DEV_CITADEL "/dev/citadel0"
50
51 static pthread_mutex_t in_buf_mutex = PTHREAD_MUTEX_INITIALIZER;
52 static uint8_t in_buf[MAX_DEVICE_TRANSFER];
read_datagram(void * ctx,uint32_t command,uint8_t * buf,uint32_t len)53 static int read_datagram(void *ctx, uint32_t command, uint8_t *buf, uint32_t len) {
54 struct citadel_ioc_tpm_datagram dg = {
55 .buf = (unsigned long)in_buf,
56 .len = len,
57 .command = command,
58 };
59 int ret;
60 int fd;
61
62 if (!ctx) {
63
64 ALOGE("%s: invalid (NULL) device\n", __func__);
65 return -ENODEV;
66 }
67 fd = *(int *)ctx;
68 if (fd < 0) {
69 ALOGE("%s: invalid device\n", __func__);
70 return -ENODEV;
71 }
72
73 if (len > MAX_DEVICE_TRANSFER) {
74 ALOGE("%s: invalid len (%d > %d)\n", __func__,
75 len, MAX_DEVICE_TRANSFER);
76 return -E2BIG;
77 }
78
79 /* Lock the in buffer while it is used for this transaction */
80 if (pthread_mutex_lock(&in_buf_mutex) != 0) {
81 ALOGE("%s: failed to lock in_buf_mutex: %s", __func__, strerror(errno));
82 return -errno;
83 }
84
85 ret = ioctl(fd, CITADEL_IOC_TPM_DATAGRAM, &dg);
86 if (ret < 0) {
87 ALOGE("can't send spi message: %s", strerror(errno));
88 ret = -errno;
89 goto out;
90 }
91
92 memcpy(buf, in_buf, len);
93
94 out:
95 if (pthread_mutex_unlock(&in_buf_mutex) != 0) {
96 ALOGE("%s: failed to unlock in_buf_mutex: %s", __func__, strerror(errno));
97 ret = -errno;
98 }
99 return ret;
100 }
101
102 static pthread_mutex_t out_buf_mutex = PTHREAD_MUTEX_INITIALIZER;
103 static uint8_t out_buf[MAX_DEVICE_TRANSFER];
write_datagram(void * ctx,uint32_t command,const uint8_t * buf,uint32_t len)104 static int write_datagram(void *ctx, uint32_t command, const uint8_t *buf, uint32_t len) {
105 struct citadel_ioc_tpm_datagram dg = {
106 .buf = (unsigned long)out_buf,
107 .len = len,
108 .command = command,
109 };
110 int ret;
111 int fd;
112
113 if (!ctx) {
114 ALOGE("%s: invalid (NULL) device\n", __func__);
115 return -ENODEV;
116 }
117 fd = *(int *)ctx;
118 if (fd < 0) {
119 ALOGE("%s: invalid device\n", __func__);
120 return -ENODEV;
121 }
122
123 if (len > MAX_DEVICE_TRANSFER) {
124 ALOGE("%s: invalid len (%d > %d)\n", __func__, len,
125 MAX_DEVICE_TRANSFER);
126 return -E2BIG;
127 }
128
129 /* Lock the out buffer while it is used for this transaction */
130 if (pthread_mutex_lock(&out_buf_mutex) != 0) {
131 ALOGE("%s: failed to lock out_buf_mutex: %s", __func__, strerror(errno));
132 return -errno;
133 }
134
135 memcpy(out_buf, buf, len);
136
137 ret = ioctl(fd, CITADEL_IOC_TPM_DATAGRAM, &dg);
138 if (ret < 0) {
139 ALOGE("can't send spi message: %s", strerror(errno));
140 ret = -errno;
141 goto out;
142 }
143
144 out:
145 if (pthread_mutex_unlock(&out_buf_mutex) != 0) {
146 ALOGE("%s: failed to unlock out_buf_mutex: %s", __func__, strerror(errno));
147 ret = -errno;
148 }
149 return ret;
150 }
151
wait_for_interrupt(void * ctx,int msecs)152 static int wait_for_interrupt(void *ctx, int msecs) {
153 int fd = *(int *)ctx;
154 struct pollfd fds = {fd, POLLIN, 0};
155 int rv;
156
157 rv = poll(&fds, 1 /*nfds*/, msecs);
158 if (rv < 0) {
159 ALOGE("poll: %s", strerror(errno));
160 }
161
162 return rv;
163 }
164
reset(void * ctx)165 static int reset(void *ctx) {
166 int ret;
167 int fd;
168
169 if (!ctx) {
170
171 ALOGE("%s: invalid (NULL) device\n", __func__);
172 return -ENODEV;
173 }
174 fd = *(int *)ctx;
175 if (fd < 0) {
176 ALOGE("%s: invalid device\n", __func__);
177 return -ENODEV;
178 }
179
180 ret = ioctl(fd, CITADEL_IOC_RESET);
181 if (ret < 0) {
182 ALOGE("can't reset Citadel: %s", strerror(errno));
183 return -errno;
184 }
185 return 0;
186 }
187
close_device(void * ctx)188 static void close_device(void *ctx) {
189 int fd;
190
191 if (!ctx) {
192 ALOGE("%s: invalid (NULL) device (ignored)\n", __func__);
193 return;
194 }
195 fd = *(int *)ctx;
196 if (fd < 0) {
197 ALOGE("%s: invalid device (ignored)\n", __func__);
198 return;
199 }
200
201 if (close(fd) < 0)
202 ALOGE("Problem closing device (ignored): %s", strerror(errno));
203 free(ctx);
204 }
205
nos_device_open(const char * device_name,struct nos_device * dev)206 int nos_device_open(const char *device_name, struct nos_device *dev) {
207 int fd, *new_fd;
208
209 fd = open(device_name ? device_name : DEV_CITADEL, O_RDWR);
210 if (fd < 0) {
211 ALOGE("can't open device: %s", strerror(errno));
212 return -errno;
213 }
214
215 new_fd = (int *)malloc(sizeof(int));
216 if (!new_fd) {
217 ALOGE("can't malloc new fd: %s", strerror(errno));
218 close(fd);
219 return -ENOMEM;
220 }
221 *new_fd = fd;
222
223 dev->ctx = new_fd;
224 dev->ops.read = read_datagram;
225 dev->ops.write = write_datagram;
226 dev->ops.wait_for_interrupt = wait_for_interrupt;
227 dev->ops.reset = reset;
228 dev->ops.close = close_device;
229 return 0;
230 }
231