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 <sys/stat.h>
35 #include <sys/types.h>
36 #include <unistd.h>
37
38 /*****************************************************************************/
39 /* Ideally, this should be in <linux/citadel.h> */
40 #define CITADEL_IOC_MAGIC 'c'
41 struct citadel_ioc_tpm_datagram {
42 __u64 buf;
43 __u32 len;
44 __u32 command;
45 };
46 #define CITADEL_IOC_TPM_DATAGRAM _IOW(CITADEL_IOC_MAGIC, 1, \
47 struct citadel_ioc_tpm_datagram)
48 #define CITADEL_IOC_RESET _IO(CITADEL_IOC_MAGIC, 2)
49 /*****************************************************************************/
50
51 #define DEV_CITADEL "/dev/citadel0"
52 #define DEV_DAUNTLESS "/dev/gsc0"
53
54 static pthread_mutex_t in_buf_mutex = PTHREAD_MUTEX_INITIALIZER;
55 static uint8_t in_buf[MAX_DEVICE_TRANSFER];
read_datagram(void * ctx,uint32_t command,uint8_t * buf,uint32_t len)56 static int read_datagram(void *ctx, uint32_t command, uint8_t *buf, uint32_t len) {
57 struct citadel_ioc_tpm_datagram dg = {
58 .buf = (unsigned long)in_buf,
59 .len = len,
60 .command = command,
61 };
62 int ret;
63 int fd;
64
65 if (!ctx) {
66
67 ALOGE("%s: invalid (NULL) device\n", __func__);
68 return -ENODEV;
69 }
70 fd = *(int *)ctx;
71 if (fd < 0) {
72 ALOGE("%s: invalid device\n", __func__);
73 return -ENODEV;
74 }
75
76 if (len > MAX_DEVICE_TRANSFER) {
77 ALOGE("%s: invalid len (%d > %d)\n", __func__,
78 len, MAX_DEVICE_TRANSFER);
79 return -E2BIG;
80 }
81
82 /* Lock the in buffer while it is used for this transaction */
83 if (pthread_mutex_lock(&in_buf_mutex) != 0) {
84 ALOGE("%s: failed to lock in_buf_mutex: %s", __func__, strerror(errno));
85 return -errno;
86 }
87
88 ret = ioctl(fd, CITADEL_IOC_TPM_DATAGRAM, &dg);
89 if (ret < 0) {
90 ALOGE("can't send spi message: %s", strerror(errno));
91 ret = -errno;
92 goto out;
93 }
94
95 memcpy(buf, in_buf, len);
96
97 out:
98 if (pthread_mutex_unlock(&in_buf_mutex) != 0) {
99 ALOGE("%s: failed to unlock in_buf_mutex: %s", __func__, strerror(errno));
100 ret = -errno;
101 }
102 return ret;
103 }
104
105 static pthread_mutex_t out_buf_mutex = PTHREAD_MUTEX_INITIALIZER;
106 static uint8_t out_buf[MAX_DEVICE_TRANSFER];
write_datagram(void * ctx,uint32_t command,const uint8_t * buf,uint32_t len)107 static int write_datagram(void *ctx, uint32_t command, const uint8_t *buf, uint32_t len) {
108 struct citadel_ioc_tpm_datagram dg = {
109 .buf = (unsigned long)out_buf,
110 .len = len,
111 .command = command,
112 };
113 int ret;
114 int fd;
115
116 if (!ctx) {
117 ALOGE("%s: invalid (NULL) device\n", __func__);
118 return -ENODEV;
119 }
120 fd = *(int *)ctx;
121 if (fd < 0) {
122 ALOGE("%s: invalid device\n", __func__);
123 return -ENODEV;
124 }
125
126 if (len > MAX_DEVICE_TRANSFER) {
127 ALOGE("%s: invalid len (%d > %d)\n", __func__, len,
128 MAX_DEVICE_TRANSFER);
129 return -E2BIG;
130 }
131
132 /* Lock the out buffer while it is used for this transaction */
133 if (pthread_mutex_lock(&out_buf_mutex) != 0) {
134 ALOGE("%s: failed to lock out_buf_mutex: %s", __func__, strerror(errno));
135 return -errno;
136 }
137
138 memcpy(out_buf, buf, len);
139
140 ret = ioctl(fd, CITADEL_IOC_TPM_DATAGRAM, &dg);
141 if (ret < 0) {
142 ALOGE("can't send spi message: %s", strerror(errno));
143 ret = -errno;
144 goto out;
145 }
146
147 out:
148 if (pthread_mutex_unlock(&out_buf_mutex) != 0) {
149 ALOGE("%s: failed to unlock out_buf_mutex: %s", __func__, strerror(errno));
150 ret = -errno;
151 }
152 return ret;
153 }
154
wait_for_interrupt(void * ctx,int msecs)155 static int wait_for_interrupt(void *ctx, int msecs) {
156 int fd = *(int *)ctx;
157 struct pollfd fds = {fd, POLLIN, 0};
158 int rv;
159
160 rv = poll(&fds, 1 /*nfds*/, msecs);
161 if (rv < 0) {
162 ALOGE("poll: %s", strerror(errno));
163 }
164
165 return rv;
166 }
167
reset(void * ctx)168 static int reset(void *ctx) {
169 int ret;
170 int fd;
171
172 if (!ctx) {
173
174 ALOGE("%s: invalid (NULL) device\n", __func__);
175 return -ENODEV;
176 }
177 fd = *(int *)ctx;
178 if (fd < 0) {
179 ALOGE("%s: invalid device\n", __func__);
180 return -ENODEV;
181 }
182
183 ret = ioctl(fd, CITADEL_IOC_RESET);
184 if (ret < 0) {
185 ALOGE("can't reset Citadel: %s", strerror(errno));
186 return -errno;
187 }
188 return 0;
189 }
190
close_device(void * ctx)191 static void close_device(void *ctx) {
192 int fd;
193
194 if (!ctx) {
195 ALOGE("%s: invalid (NULL) device (ignored)\n", __func__);
196 return;
197 }
198 fd = *(int *)ctx;
199 if (fd < 0) {
200 ALOGE("%s: invalid device (ignored)\n", __func__);
201 return;
202 }
203
204 if (close(fd) < 0)
205 ALOGE("Problem closing device (ignored): %s", strerror(errno));
206 free(ctx);
207 }
208
default_device(void)209 static const char *default_device(void) {
210 struct stat statbuf;
211 int rv;
212
213 rv = stat(DEV_CITADEL, &statbuf);
214 if (!rv) {
215 return DEV_CITADEL;
216 }
217
218 rv = stat(DEV_DAUNTLESS, &statbuf);
219 if (!rv) {
220 return DEV_DAUNTLESS;
221 }
222
223 return 0;
224 }
225
nos_device_open(const char * device_name,struct nos_device * dev)226 int nos_device_open(const char *device_name, struct nos_device *dev) {
227 int fd, *new_ctx;
228
229 if (!device_name) {
230 device_name = default_device();
231 }
232
233 if (!device_name) {
234 ALOGE("can't find device node\n");
235 return -ENODEV;
236 }
237
238 fd = open(device_name, O_RDWR);
239 if (fd < 0) {
240 ALOGE("can't open device \"%s\": %s", device_name, strerror(errno));
241 return -errno;
242 }
243
244 /* Our context is just a pointer to an int holding the fd */
245 new_ctx = (int *)malloc(sizeof(int));
246 if (!new_ctx) {
247 ALOGE("can't malloc new ctx: %s", strerror(errno));
248 close(fd);
249 return -ENOMEM;
250 }
251 *new_ctx = fd;
252
253 dev->ctx = new_ctx;
254 dev->ops.read = read_datagram;
255 dev->ops.write = write_datagram;
256 dev->ops.wait_for_interrupt = wait_for_interrupt;
257 dev->ops.reset = reset;
258 dev->ops.close = close_device;
259 return 0;
260 }
261