1 /*
2 * Copyright (C) 2015-2016 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 #include "block_device_tipc.h"
18
19 #include <errno.h>
20 #include <inttypes.h>
21 #include <stdint.h>
22 #include <string.h>
23
24 #include <lib/system_state/system_state.h>
25 #include <lib/tipc/tipc.h>
26 #include <lk/compiler.h>
27 #include <trusty_ipc.h>
28 #include <uapi/err.h>
29
30 #include <interface/storage/storage.h>
31
32 #include <openssl/mem.h>
33 #include <openssl/rand.h>
34
35 #include "block_cache.h"
36 #include "client_tipc.h"
37 #include "fs.h"
38 #include "ipc.h"
39 #include "rpmb.h"
40 #include "tipc_ns.h"
41
42 #ifdef APP_STORAGE_RPMB_BLOCK_SIZE
43 #define BLOCK_SIZE_RPMB (APP_STORAGE_RPMB_BLOCK_SIZE)
44 #else
45 #define BLOCK_SIZE_RPMB (512)
46 #endif
47 #ifdef APP_STORAGE_RPMB_BLOCK_COUNT
48 #define BLOCK_COUNT_RPMB (APP_STORAGE_RPMB_BLOCK_COUNT)
49 #else
50 #define BLOCK_COUNT_RPMB (0) /* Auto detect */
51 #endif
52 #ifdef APP_STORAGE_MAIN_BLOCK_SIZE
53 #define BLOCK_SIZE_MAIN (APP_STORAGE_MAIN_BLOCK_SIZE)
54 #else
55 #define BLOCK_SIZE_MAIN (2048)
56 #endif
57
58 /*
59 * This is here in case we're using an old storageproxyd that does not have
60 * support for STORAGE_FILE_GET_MAX_SIZE
61 */
62 #ifdef APP_STORAGE_MAIN_BLOCK_COUNT
63 #define BLOCK_COUNT_MAIN (APP_STORAGE_MAIN_BLOCK_COUNT)
64 #else
65 #define BLOCK_COUNT_MAIN (0x10000000000 / BLOCK_SIZE_MAIN)
66 #endif
67
68 #define BLOCK_SIZE_RPMB_BLOCKS (BLOCK_SIZE_RPMB / RPMB_BUF_SIZE)
69
70 STATIC_ASSERT(BLOCK_SIZE_RPMB_BLOCKS == 1 || BLOCK_SIZE_RPMB_BLOCKS == 2);
71 STATIC_ASSERT((BLOCK_SIZE_RPMB_BLOCKS * RPMB_BUF_SIZE) == BLOCK_SIZE_RPMB);
72
73 STATIC_ASSERT(BLOCK_COUNT_RPMB == 0 || BLOCK_COUNT_RPMB >= 8);
74
75 STATIC_ASSERT(BLOCK_SIZE_MAIN >= 256);
76 STATIC_ASSERT(BLOCK_COUNT_MAIN >= 8);
77 STATIC_ASSERT(BLOCK_SIZE_MAIN >= BLOCK_SIZE_RPMB);
78
79 /* Ensure that we can fit a superblock + backup in an RPMB block */
80 STATIC_ASSERT(BLOCK_SIZE_RPMB >= 256);
81
82 #define SS_ERR(args...) fprintf(stderr, "ss: " args)
83 #define SS_WARN(args...) fprintf(stderr, "ss: " args)
84
85 #ifdef SS_DATA_DEBUG_IO
86 #define SS_DBG_IO(args...) fprintf(stdout, "ss: " args)
87 #else
88 #define SS_DBG_IO(args...) \
89 do { \
90 } while (0)
91 #endif
92
93 const char file_system_id_td[] = "td";
94 const char file_system_id_tdea[] = "tdea";
95 const char file_system_id_tdp[] = "tdp";
96 const char file_system_id_tp[] = "tp";
97 const char file_system_id_nsp[] = "nsp";
98
99 const char ns_filename[] = "0";
100 const char ns_alternate_filename[] = "alternate/0";
101 const char tdp_filename[] = "persist/0";
102 const char nsp_filename[] = "persist/nsp";
103
104 struct rpmb_key_derivation_in {
105 uint8_t prefix[sizeof(struct key)];
106 uint8_t block_data[RPMB_BUF_SIZE];
107 };
108
109 struct rpmb_key_derivation_out {
110 struct rpmb_key rpmb_key;
111 uint8_t unused[sizeof(struct key)];
112 };
113
114 struct rpmb_span {
115 uint16_t start;
116 uint16_t block_count;
117 };
118
119 struct rpmb_spans {
120 struct rpmb_span key;
121 struct rpmb_span ns;
122 struct rpmb_span tdp;
123 /* Start of the rest of the RPMB, which is used for TP and TDEA */
124 uint16_t rpmb_start;
125 };
126
rpmb_check(struct rpmb_state * rpmb_state,uint16_t block)127 static int rpmb_check(struct rpmb_state* rpmb_state, uint16_t block) {
128 int ret;
129 uint8_t tmp[RPMB_BUF_SIZE];
130 ret = rpmb_read(rpmb_state, tmp, block, 1);
131 SS_DBG_IO("%s: check rpmb_block %d, ret %d\n", __func__, block, ret);
132 return ret;
133 }
134
rpmb_search_size(struct rpmb_state * rpmb_state,uint16_t hint)135 static uint32_t rpmb_search_size(struct rpmb_state* rpmb_state, uint16_t hint) {
136 int ret;
137 uint32_t low = 0;
138 uint16_t high = UINT16_MAX;
139 uint16_t curr = hint ? hint - 1 : UINT16_MAX;
140
141 while (low <= high) {
142 ret = rpmb_check(rpmb_state, curr);
143 switch (ret) {
144 case 0:
145 low = curr + 1;
146 break;
147 case -ENOENT:
148 high = curr - 1;
149 break;
150 default:
151 return 0;
152 };
153 if (ret || curr != hint) {
154 curr = (low + high) / 2;
155 hint = curr;
156 } else {
157 curr = curr + 1;
158 }
159 }
160 assert((uint32_t)high + 1 == low);
161 return low;
162 }
163
dev_rpmb_to_state(struct block_device * dev)164 static struct block_device_rpmb* dev_rpmb_to_state(struct block_device* dev) {
165 assert(dev);
166 return containerof(dev, struct block_device_rpmb, dev);
167 }
168
block_device_tipc_rpmb_start_read(struct block_device * dev,data_block_t block)169 static void block_device_tipc_rpmb_start_read(struct block_device* dev,
170 data_block_t block) {
171 int ret;
172 uint8_t tmp[BLOCK_SIZE_RPMB]; /* TODO: pass data in? */
173 uint16_t rpmb_block;
174 struct block_device_rpmb* dev_rpmb = dev_rpmb_to_state(dev);
175
176 assert(block < dev->block_count);
177 rpmb_block = block + dev_rpmb->base;
178
179 ret = rpmb_read(dev_rpmb->rpmb_state, tmp,
180 rpmb_block * BLOCK_SIZE_RPMB_BLOCKS,
181 BLOCK_SIZE_RPMB_BLOCKS);
182
183 SS_DBG_IO("%s: block %" PRIu64 ", base %d, rpmb_block %d, ret %d\n",
184 __func__, block, dev_rpmb->base, rpmb_block, ret);
185
186 block_cache_complete_read(dev, block, tmp, BLOCK_SIZE_RPMB,
187 ret ? BLOCK_READ_IO_ERROR : BLOCK_READ_SUCCESS);
188 }
189
translate_write_error(int rc)190 static inline enum block_write_error translate_write_error(int rc) {
191 switch (rc) {
192 case 0:
193 return BLOCK_WRITE_SUCCESS;
194 case -EUCLEAN:
195 return BLOCK_WRITE_FAILED_UNKNOWN_STATE;
196 case ERR_IO:
197 return BLOCK_WRITE_SYNC_FAILED;
198 default:
199 return BLOCK_WRITE_FAILED;
200 }
201 }
202
block_device_tipc_rpmb_start_write(struct block_device * dev,data_block_t block,const void * data,size_t data_size,bool sync)203 static void block_device_tipc_rpmb_start_write(struct block_device* dev,
204 data_block_t block,
205 const void* data,
206 size_t data_size,
207 bool sync) {
208 int ret;
209 uint16_t rpmb_block;
210 struct block_device_rpmb* dev_rpmb = dev_rpmb_to_state(dev);
211
212 /* We currently sync every rpmb write. TODO: can we avoid this? */
213 (void)sync;
214
215 assert(data_size == BLOCK_SIZE_RPMB);
216 assert(block < dev->block_count);
217
218 rpmb_block = block + dev_rpmb->base;
219
220 ret = rpmb_write(dev_rpmb->rpmb_state, data,
221 rpmb_block * BLOCK_SIZE_RPMB_BLOCKS,
222 BLOCK_SIZE_RPMB_BLOCKS, true, dev_rpmb->is_userdata);
223
224 SS_DBG_IO("%s: block %" PRIu64 ", base %d, rpmb_block %d, ret %d\n",
225 __func__, block, dev_rpmb->base, rpmb_block, ret);
226
227 block_cache_complete_write(dev, block, translate_write_error(ret));
228 }
229
block_device_tipc_rpmb_wait_for_io(struct block_device * dev)230 static void block_device_tipc_rpmb_wait_for_io(struct block_device* dev) {
231 assert(0); /* TODO: use async read/write */
232 }
233
to_block_device_ns(struct block_device * dev)234 static struct block_device_ns* to_block_device_ns(struct block_device* dev) {
235 assert(dev);
236 return containerof(dev, struct block_device_ns, dev);
237 }
238
block_device_tipc_ns_start_read(struct block_device * dev,data_block_t block)239 static void block_device_tipc_ns_start_read(struct block_device* dev,
240 data_block_t block) {
241 int ret;
242 enum block_read_error res;
243 uint8_t tmp[BLOCK_SIZE_MAIN]; /* TODO: pass data in? */
244 struct block_device_ns* dev_ns = to_block_device_ns(dev);
245
246 ret = ns_read_pos(dev_ns->ipc_handle, dev_ns->ns_handle,
247 block * BLOCK_SIZE_MAIN, tmp, BLOCK_SIZE_MAIN);
248 SS_DBG_IO("%s: block %" PRIu64 ", ret %d\n", __func__, block, ret);
249 if (ret == 0) {
250 res = BLOCK_READ_NO_DATA;
251 } else if (ret == BLOCK_SIZE_MAIN) {
252 res = BLOCK_READ_SUCCESS;
253 } else {
254 res = BLOCK_READ_IO_ERROR;
255 }
256 block_cache_complete_read(dev, block, tmp, BLOCK_SIZE_MAIN, res);
257 }
258
block_device_tipc_ns_start_write(struct block_device * dev,data_block_t block,const void * data,size_t data_size,bool sync)259 static void block_device_tipc_ns_start_write(struct block_device* dev,
260 data_block_t block,
261 const void* data,
262 size_t data_size,
263 bool sync) {
264 int ret;
265 enum block_write_error res = BLOCK_WRITE_FAILED;
266 struct block_device_ns* dev_ns = to_block_device_ns(dev);
267
268 assert(data_size == BLOCK_SIZE_MAIN);
269
270 ret = ns_write_pos(dev_ns->ipc_handle, dev_ns->ns_handle,
271 block * BLOCK_SIZE_MAIN, data, data_size,
272 dev_ns->is_userdata, sync);
273 SS_DBG_IO("%s: block %" PRIu64 ", ret %d\n", __func__, block, ret);
274 if (ret == BLOCK_SIZE_MAIN) {
275 res = BLOCK_WRITE_SUCCESS;
276 } else if (ret < 0) {
277 res = translate_write_error(ret);
278 }
279 block_cache_complete_write(dev, block, res);
280 }
281
block_device_tipc_ns_wait_for_io(struct block_device * dev)282 static void block_device_tipc_ns_wait_for_io(struct block_device* dev) {
283 assert(0); /* TODO: use async read/write */
284 }
285
block_device_tipc_init_dev_rpmb(struct block_device_rpmb * dev_rpmb,struct rpmb_state * rpmb_state,uint16_t base,uint32_t block_count,bool is_userdata)286 static void block_device_tipc_init_dev_rpmb(struct block_device_rpmb* dev_rpmb,
287 struct rpmb_state* rpmb_state,
288 uint16_t base,
289 uint32_t block_count,
290 bool is_userdata) {
291 dev_rpmb->dev.start_read = block_device_tipc_rpmb_start_read;
292 dev_rpmb->dev.start_write = block_device_tipc_rpmb_start_write;
293 dev_rpmb->dev.wait_for_io = block_device_tipc_rpmb_wait_for_io;
294 dev_rpmb->dev.block_count = block_count;
295 dev_rpmb->dev.block_size = BLOCK_SIZE_RPMB;
296 dev_rpmb->dev.block_num_size = 2;
297 dev_rpmb->dev.mac_size = 2;
298 dev_rpmb->dev.tamper_detecting = true;
299 list_initialize(&dev_rpmb->dev.io_ops);
300 dev_rpmb->rpmb_state = rpmb_state;
301 dev_rpmb->base = base;
302 dev_rpmb->is_userdata = is_userdata;
303 }
304
block_device_tipc_init_dev_ns(struct block_device_ns * dev_ns,handle_t ipc_handle,bool is_userdata)305 static void block_device_tipc_init_dev_ns(struct block_device_ns* dev_ns,
306 handle_t ipc_handle,
307 bool is_userdata) {
308 dev_ns->dev.start_read = block_device_tipc_ns_start_read;
309 dev_ns->dev.start_write = block_device_tipc_ns_start_write;
310 dev_ns->dev.wait_for_io = block_device_tipc_ns_wait_for_io;
311 dev_ns->dev.block_size = BLOCK_SIZE_MAIN;
312 dev_ns->dev.block_num_size = sizeof(data_block_t);
313 dev_ns->dev.mac_size = sizeof(struct mac);
314 dev_ns->dev.tamper_detecting = false;
315 list_initialize(&dev_ns->dev.io_ops);
316 dev_ns->ipc_handle = ipc_handle;
317 dev_ns->ns_handle = 0; /* Filled in later */
318 dev_ns->is_userdata = is_userdata;
319 }
320
321 /**
322 * hwkey_derive_rpmb_key() - Derive rpmb key through hwkey server.
323 * @session: The hwkey session handle.
324 * @in: The input data to derive rpmb key.
325 * @out: The output data from deriving rpmb key.
326 *
327 * Return: NO_ERROR on success, error code less than 0 on error.
328 */
hwkey_derive_rpmb_key(hwkey_session_t session,const struct rpmb_key_derivation_in * in,struct rpmb_key_derivation_out * out)329 static int hwkey_derive_rpmb_key(hwkey_session_t session,
330 const struct rpmb_key_derivation_in* in,
331 struct rpmb_key_derivation_out* out) {
332 uint32_t kdf_version = HWKEY_KDF_VERSION_1;
333 const void* in_buf = in;
334 void* out_buf = out;
335 uint32_t key_size = sizeof(*out);
336 STATIC_ASSERT(sizeof(*in) >= sizeof(*out));
337
338 int ret = hwkey_derive(session, &kdf_version, in_buf, out_buf, key_size);
339 if (ret < 0) {
340 SS_ERR("%s: failed to get key: %d\n", __func__, ret);
341 return ret;
342 }
343
344 return NO_ERROR;
345 }
346
347 /**
348 * block_device_tipc_program_key() - Program a rpmb key derived through hwkey
349 * server.
350 * @state: The rpmb state.
351 * @rpmb_key_part_base: The base of rpmb_key_part in rpmb partition.
352 * @in The input rpmb key derivation data.
353 * @out The output rpmb key derivation data.
354 * @hwkey_session: The hwkey session handle.
355 *
356 * Return: NO_ERROR on success, error code less than 0 on error.
357 */
block_device_tipc_program_key(struct rpmb_state * state,uint16_t rpmb_key_part_base,struct rpmb_key_derivation_in * in,struct rpmb_key_derivation_out * out,hwkey_session_t hwkey_session)358 static int block_device_tipc_program_key(struct rpmb_state* state,
359 uint16_t rpmb_key_part_base,
360 struct rpmb_key_derivation_in* in,
361 struct rpmb_key_derivation_out* out,
362 hwkey_session_t hwkey_session) {
363 int ret;
364
365 if (!system_state_provisioning_allowed()) {
366 ret = ERR_NOT_ALLOWED;
367 SS_ERR("%s: rpmb key provisioning is not allowed (%d)\n", __func__,
368 ret);
369 return ret;
370 }
371
372 STATIC_ASSERT(sizeof(in->block_data) >= sizeof(out->rpmb_key));
373 RAND_bytes(in->block_data, sizeof(out->rpmb_key.byte));
374 ret = hwkey_derive_rpmb_key(hwkey_session, in, out);
375 if (ret < 0) {
376 SS_ERR("%s: hwkey_derive_rpmb_key failed (%d)\n", __func__, ret);
377 return ret;
378 }
379
380 ret = rpmb_program_key(state, &out->rpmb_key);
381 if (ret < 0) {
382 SS_ERR("%s: rpmb_program_key failed (%d)\n", __func__, ret);
383 return ret;
384 }
385
386 rpmb_set_key(state, &out->rpmb_key);
387
388 ret = rpmb_write(state, in->block_data,
389 rpmb_key_part_base * BLOCK_SIZE_RPMB_BLOCKS, 1, false,
390 false);
391 if (ret < 0) {
392 SS_ERR("%s: rpmb_write failed (%d)\n", __func__, ret);
393 return ret;
394 }
395
396 return 0;
397 }
398
block_device_tipc_derive_rpmb_key(struct rpmb_state * state,uint16_t rpmb_key_part_base,hwkey_session_t hwkey_session)399 static int block_device_tipc_derive_rpmb_key(struct rpmb_state* state,
400 uint16_t rpmb_key_part_base,
401 hwkey_session_t hwkey_session) {
402 int ret;
403 struct rpmb_key_derivation_in in = {
404 .prefix = {
405 0x74, 0x68, 0x43, 0x49, 0x2b, 0xa2, 0x4f, 0x77,
406 0xb0, 0x8e, 0xd1, 0xd4, 0xb7, 0x01, 0x0e, 0xc6,
407 0x86, 0x4c, 0xa9, 0xe5, 0x28, 0xf0, 0x20, 0xb1,
408 0xb8, 0x1e, 0x73, 0x3d, 0x8c, 0x9d, 0xb9, 0x96,
409 }};
410 struct rpmb_key_derivation_out out;
411
412 ret = rpmb_read_no_mac(state, in.block_data,
413 rpmb_key_part_base * BLOCK_SIZE_RPMB_BLOCKS, 1);
414
415 if (ret < 0) {
416 ret = block_device_tipc_program_key(state, rpmb_key_part_base, &in,
417 &out, hwkey_session);
418 if (ret < 0) {
419 SS_ERR("%s: program_key failed (%d)\n", __func__, ret);
420 return ret;
421 }
422
423 return 0;
424 }
425
426 ret = hwkey_derive_rpmb_key(hwkey_session, &in, &out);
427 if (ret < 0) {
428 SS_ERR("%s: hwkey_derive_rpmb_key failed (%d)\n", __func__, ret);
429 return ret;
430 }
431
432 rpmb_set_key(state, &out.rpmb_key);
433
434 /*
435 * Validate that the derived rpmb key is correct as we use it to check
436 * both mac and content of the block_data.
437 */
438 ret = rpmb_verify(state, in.block_data,
439 rpmb_key_part_base * BLOCK_SIZE_RPMB_BLOCKS, 1);
440 if (ret < 0) {
441 SS_ERR("%s: rpmb_verify failed with the derived rpmb key (%d)\n",
442 __func__, ret);
443 return ret;
444 }
445
446 return 0;
447 }
448
block_device_tipc_init_rpmb_key(struct rpmb_state * state,const struct rpmb_key * rpmb_key,uint16_t rpmb_key_part_base,hwkey_session_t hwkey_session)449 static int block_device_tipc_init_rpmb_key(struct rpmb_state* state,
450 const struct rpmb_key* rpmb_key,
451 uint16_t rpmb_key_part_base,
452 hwkey_session_t hwkey_session) {
453 int ret = 0;
454
455 if (rpmb_key) {
456 rpmb_set_key(state, rpmb_key);
457 } else {
458 ret = block_device_tipc_derive_rpmb_key(state, rpmb_key_part_base,
459 hwkey_session);
460 }
461
462 return ret;
463 }
464
set_storage_size(handle_t handle,struct block_device_ns * dev_ns)465 static int set_storage_size(handle_t handle, struct block_device_ns* dev_ns) {
466 data_block_t sz;
467
468 int ret = ns_get_max_size(handle, dev_ns->ns_handle, &sz);
469 if (ret < 0) {
470 /* In case we have an old storageproxyd, use default */
471 if (ret == ERR_NOT_IMPLEMENTED) {
472 sz = BLOCK_COUNT_MAIN * dev_ns->dev.block_size;
473 ret = 0;
474 } else {
475 SS_ERR("%s: Could not get max size: %d\n", __func__, ret);
476 return ret;
477 }
478 } else if (sz < (dev_ns->dev.block_size * 8)) {
479 SS_ERR("%s: max storage file size %" PRIu64 " is too small\n", __func__,
480 sz);
481 return -1;
482 }
483
484 dev_ns->dev.block_count = sz / dev_ns->dev.block_size;
485 return ret;
486 }
487
block_device_tipc_has_ns(struct block_device_tipc * self)488 static bool block_device_tipc_has_ns(struct block_device_tipc* self) {
489 return self->dev_ns.dev.block_count;
490 }
491
492 /**
493 * init_rpmb_fs() - Initialize @self's RPMB fs and its backing block devices.
494 * @self: The struct block_device_tipc to modify
495 * @fs_key: The key to use for the filesystem.
496 * @partition_start: The first RPMB block in the partition to use for this fs.
497 *
498 * Return: NO_ERROR on success, error code less than 0 on error.
499 */
init_rpmb_fs(struct block_device_tipc * self,const struct key * fs_key,uint16_t partition_start)500 static int init_rpmb_fs(struct block_device_tipc* self,
501 const struct key* fs_key,
502 uint16_t partition_start) {
503 int ret;
504 uint32_t rpmb_block_count;
505
506 if (BLOCK_COUNT_RPMB) {
507 rpmb_block_count = BLOCK_COUNT_RPMB;
508 ret = rpmb_check(self->rpmb_state,
509 rpmb_block_count * BLOCK_SIZE_RPMB_BLOCKS - 1);
510 if (ret < 0) {
511 SS_ERR("%s: bad static rpmb size, %d\n", __func__,
512 rpmb_block_count);
513 goto err_bad_rpmb_size;
514 }
515 } else {
516 rpmb_block_count = rpmb_search_size(self->rpmb_state,
517 0); /* TODO: get hint from ns */
518 rpmb_block_count /= BLOCK_SIZE_RPMB_BLOCKS;
519 }
520 if (rpmb_block_count < partition_start) {
521 ret = -1;
522 SS_ERR("%s: bad rpmb size, %d\n", __func__, rpmb_block_count);
523 goto err_bad_rpmb_size;
524 }
525
526 block_device_tipc_init_dev_rpmb(&self->dev_rpmb, self->rpmb_state,
527 partition_start,
528 rpmb_block_count - partition_start, false);
529
530 /* TODO: allow non-rpmb based tamper proof storage */
531 ret = fs_init(&self->tr_state_rpmb, file_system_id_tp, fs_key,
532 &self->dev_rpmb.dev, &self->dev_rpmb.dev, FS_INIT_FLAGS_NONE);
533 if (ret < 0) {
534 SS_ERR("%s: failed to initialize TP: %d\n", __func__, ret);
535 goto err_init_tr_state_rpmb;
536 }
537 return 0;
538
539 err_init_tr_state_rpmb:
540 block_cache_dev_destroy(&self->dev_rpmb.dev);
541 err_bad_rpmb_size:
542 return ret;
543 }
544
545 /**
546 * destroy_rpmb_fs() - Destroy @self's RPMB fs and its backing block devices.
547 */
destroy_rpmb_fs(struct block_device_tipc * self)548 static void destroy_rpmb_fs(struct block_device_tipc* self) {
549 fs_destroy(&self->tr_state_rpmb);
550 block_cache_dev_destroy(&self->dev_rpmb.dev);
551 }
552
553 /**
554 * block_device_ns_open_file() - Open an ns backing file
555 *
556 * @self: The ns block device to use to open the file.
557 * @name: The name of the file to open.
558 * @create: Whether the file should be created if it doesn't already exist.
559 *
560 * Return: NO_ERROR on success, error code less than 0 if an error was
561 * encountered during initialization.
562 */
block_device_ns_open_file(struct block_device_ns * self,const char * name,bool create)563 static int block_device_ns_open_file(struct block_device_ns* self,
564 const char* name,
565 bool create) {
566 return ns_open_file(self->ipc_handle, name, &self->ns_handle, create);
567 }
568
569 /**
570 * block_device_ns_open_file_with_alternate() - Open an ns backing file,
571 * possibly falling back to an alternate if the primary is not available.
572 *
573 * @self: The ns block device to use to open the file.
574 * @name: The name of the primary file to open.
575 * @alternate_name: The name of the alternate file. Ignored if
576 * STORAGE_NS_ALTERNATE_SUPERBLOCK_ALLOWED is false.
577 * @create: Whether the file should be created if it doesn't already
578 * exist.
579 * @used_alternate: Out-param, set only on successful return. Will tell whether
580 * the opened file was the alternate.
581 *
582 * Return: NO_ERROR on success, error code less than 0 if an error was
583 * encountered during initialization.
584 */
block_device_ns_open_file_with_alternate(struct block_device_ns * self,const char * name,const char * alternate_name,bool create,bool * used_alternate)585 static int block_device_ns_open_file_with_alternate(
586 struct block_device_ns* self,
587 const char* name,
588 const char* alternate_name,
589 bool create,
590 bool* used_alternate) {
591 int ret = block_device_ns_open_file(self, name, create);
592 if (ret >= 0) {
593 *used_alternate = false;
594 return NO_ERROR;
595 }
596
597 #if STORAGE_NS_ALTERNATE_SUPERBLOCK_ALLOWED
598 ret = block_device_ns_open_file(self, alternate_name, create);
599 if (ret >= 0) {
600 *used_alternate = true;
601 return NO_ERROR;
602 }
603 #endif
604 return ret;
605 }
606
607 enum ns_init_result {
608 /* Negative codes reserved for other error values. */
609 NS_INIT_SUCCESS = 0,
610 NS_INIT_NOT_READY = 1,
611 };
612
613 /**
614 * init_ns_fs() - Initialize @self's NS fs and its backing block devices.
615 * @self: The struct block_device_tipc to modify
616 * @fs_key: The key to use for the filesystem.
617 * @partition: The RPMB blocks to use for the filesystem's superblocks.
618 *
619 * If no ns filesystems are available, return NS_INIT_NOT_READY and leave the NS
620 * fs uninitialized. (In that case, block_device_tipc_has_ns() will return
621 * false.)
622 *
623 * Return: NS_INIT_SUCCESS on success, NS_INIT_NOT_READY if ns is unavailable,
624 * or an error code less than 0 if an error was encountered during
625 * initialization.
626 */
init_ns_fs(struct block_device_tipc * self,const struct key * fs_key,struct rpmb_span partition)627 static int init_ns_fs(struct block_device_tipc* self,
628 const struct key* fs_key,
629 struct rpmb_span partition) {
630 block_device_tipc_init_dev_ns(&self->dev_ns, self->ipc_handle, true);
631
632 bool alternate_data_partition;
633 int ret = block_device_ns_open_file_with_alternate(
634 &self->dev_ns, ns_filename, ns_alternate_filename, true,
635 &alternate_data_partition);
636 if (ret < 0) {
637 /* NS not available; init RPMB fs only */
638 self->dev_ns.dev.block_count = 0;
639 return NS_INIT_NOT_READY;
640 }
641
642 ret = set_storage_size(self->ipc_handle, &self->dev_ns);
643 if (ret < 0) {
644 goto err_get_td_max_size;
645 }
646
647 /* Request empty file system if file is empty */
648 uint8_t probe;
649 uint32_t ns_init_flags = FS_INIT_FLAGS_NONE;
650 ret = ns_read_pos(self->ipc_handle, self->dev_ns.ns_handle, 0, &probe,
651 sizeof(probe));
652 if (ret < (int)sizeof(probe)) {
653 ns_init_flags |= FS_INIT_FLAGS_DO_CLEAR;
654 }
655
656 block_device_tipc_init_dev_rpmb(&self->dev_ns_rpmb, self->rpmb_state,
657 partition.start, partition.block_count,
658 true);
659
660 #if STORAGE_NS_RECOVERY_CLEAR_ALLOWED
661 ns_init_flags |= FS_INIT_FLAGS_RECOVERY_CLEAR_ALLOWED;
662 #endif
663
664 /*
665 * This must be false if STORAGE_NS_ALTERNATE_SUPERBLOCK_ALLOWED is
666 * false.
667 */
668 if (alternate_data_partition) {
669 ns_init_flags |= FS_INIT_FLAGS_ALTERNATE_DATA;
670 }
671
672 ret = fs_init(&self->tr_state_ns, file_system_id_td, fs_key,
673 &self->dev_ns.dev, &self->dev_ns_rpmb.dev, ns_init_flags);
674 if (ret < 0) {
675 SS_ERR("%s: failed to initialize TD: %d\n", __func__, ret);
676 goto err_init_fs_ns_tr_state;
677 }
678
679 return NS_INIT_SUCCESS;
680
681 err_init_fs_ns_tr_state:
682 block_cache_dev_destroy(&self->dev_ns.dev);
683 err_get_td_max_size:
684 ns_close_file(self->ipc_handle, self->dev_ns.ns_handle);
685 return ret;
686 }
687
688 /**
689 * destroy_ns_fs() - Destroy @self's NS fs and its backing block devices.
690 */
destroy_ns_fs(struct block_device_tipc * self)691 static void destroy_ns_fs(struct block_device_tipc* self) {
692 fs_destroy(&self->tr_state_ns);
693 block_cache_dev_destroy(&self->dev_ns.dev);
694 }
695
696 #if HAS_FS_TDP
697 /**
698 * init_tdp_fs() - Initialize @self's TDP fs and its backing block devices.
699 * @self: The struct block_device_tipc to modify
700 * @fs_key: The key to use for the filesystem.
701 * @partition: The RPMB blocks to use for the filesystem's superblocks.
702 *
703 * Return: NO_ERROR on success, error code less than 0 on error.
704 */
init_tdp_fs(struct block_device_tipc * self,const struct key * fs_key,struct rpmb_span partition)705 static int init_tdp_fs(struct block_device_tipc* self,
706 const struct key* fs_key,
707 struct rpmb_span partition) {
708 block_device_tipc_init_dev_ns(&self->dev_ns_tdp, self->ipc_handle, false);
709
710 int ret = block_device_ns_open_file(&self->dev_ns_tdp, tdp_filename, true);
711 if (ret < 0) {
712 SS_ERR("%s: failed to open tdp file (%d)\n", __func__, ret);
713 goto err_open_tdp;
714 }
715
716 ret = set_storage_size(self->ipc_handle, &self->dev_ns_tdp);
717 if (ret < 0) {
718 goto err_get_tdp_max_size;
719 }
720
721 block_device_tipc_init_dev_rpmb(&self->dev_ns_tdp_rpmb, self->rpmb_state,
722 partition.start, partition.block_count,
723 false);
724
725 uint32_t tdp_init_flags = FS_INIT_FLAGS_NONE;
726 #if STORAGE_TDP_AUTO_CHECKPOINT_ENABLED
727 if (!system_state_provisioning_allowed()) {
728 /*
729 * Automatically create a checkpoint if we are done provisioning but do
730 * not already have a checkpoint.
731 */
732 tdp_init_flags |= FS_INIT_FLAGS_AUTO_CHECKPOINT;
733 }
734 #endif
735
736 ret = fs_init(&self->tr_state_ns_tdp, file_system_id_tdp, fs_key,
737 &self->dev_ns_tdp.dev, &self->dev_ns_tdp_rpmb.dev,
738 tdp_init_flags);
739 if (ret < 0) {
740 goto err_init_fs_ns_tdp_tr_state;
741 }
742
743 #if STORAGE_TDP_RECOVERY_CHECKPOINT_RESTORE_ALLOWED
744 if (fs_check(&self->tr_state_ns_tdp) == FS_CHECK_INVALID_BLOCK) {
745 SS_ERR("%s: TDP filesystem check failed with invalid block, "
746 "attempting to restore checkpoint\n",
747 __func__);
748 fs_destroy(&self->tr_state_ns_tdp);
749 ret = fs_init(&self->tr_state_ns_tdp, file_system_id_tdp, fs_key,
750 &self->dev_ns_tdp.dev, &self->dev_ns_tdp_rpmb.dev,
751 tdp_init_flags | FS_INIT_FLAGS_RESTORE_CHECKPOINT);
752 if (ret < 0) {
753 SS_ERR("%s: failed to initialize TDP: %d\n", __func__, ret);
754 goto err_init_fs_ns_tdp_tr_state;
755 }
756 }
757 #endif
758
759 return 0;
760
761 err_init_fs_ns_tdp_tr_state:
762 block_cache_dev_destroy(&self->dev_ns_tdp.dev);
763 err_get_tdp_max_size:
764 ns_close_file(self->ipc_handle, self->dev_ns_tdp.ns_handle);
765 err_open_tdp:
766 return ret;
767 }
768
769 /**
770 * destroy_tdp_fs() - Destroy @self's TDP fs and its backing block devices.
771 */
destroy_tdp_fs(struct block_device_tipc * self)772 static void destroy_tdp_fs(struct block_device_tipc* self) {
773 fs_destroy(&self->tr_state_ns_tdp);
774 block_cache_dev_destroy(&self->dev_ns_tdp.dev);
775 }
776 #endif
777
778 #if HAS_FS_NSP
779 /**
780 * init_nsp_fs() - Initialize @self's NSP fs and its backing block devices.
781 * @self: The struct block_device_tipc to modify
782 * @fs_key: The key to use for the filesystem.
783 *
784 * Return: NO_ERROR on success, error code less than 0 on error.
785 */
init_nsp_fs(struct block_device_tipc * self,const struct key * fs_key)786 static int init_nsp_fs(struct block_device_tipc* self,
787 const struct key* fs_key) {
788 block_device_tipc_init_dev_ns(&self->dev_ns_nsp, self->ipc_handle, false);
789
790 int ret = block_device_ns_open_file(&self->dev_ns_nsp, nsp_filename, true);
791 if (ret < 0) {
792 SS_ERR("%s: failed to open NSP file (%d)\n", __func__, ret);
793 goto err_open_nsp;
794 }
795
796 ret = set_storage_size(self->ipc_handle, &self->dev_ns_nsp);
797 if (ret < 0) {
798 goto err_get_nsp_max_size;
799 }
800
801 ret = fs_init(&self->tr_state_ns_nsp, file_system_id_nsp, fs_key,
802 &self->dev_ns_nsp.dev, &self->dev_ns_nsp.dev,
803 FS_INIT_FLAGS_RECOVERY_CLEAR_ALLOWED |
804 FS_INIT_FLAGS_ALLOW_TAMPERING);
805 if (ret < 0) {
806 SS_ERR("%s: failed to initialize NSP: %d\n", __func__, ret);
807 goto err_init_fs_ns_nsp_tr_state;
808 }
809
810 /*
811 * Check that all files are accessible and attempt to clear the FS if files
812 * cannot be accessed.
813 */
814 if (fs_check(&self->tr_state_ns_nsp) != FS_CHECK_NO_ERROR) {
815 SS_ERR("%s: NSP filesystem check failed, attempting to clear\n",
816 __func__);
817 fs_destroy(&self->tr_state_ns_nsp);
818 block_cache_dev_destroy(&self->dev_ns_nsp.dev);
819
820 ret = fs_init(&self->tr_state_ns_nsp, file_system_id_nsp, fs_key,
821 &self->dev_ns_nsp.dev, &self->dev_ns_nsp.dev,
822 FS_INIT_FLAGS_DO_CLEAR | FS_INIT_FLAGS_ALLOW_TAMPERING);
823 if (ret < 0) {
824 SS_ERR("%s: failed to initialize NSP: %d\n", __func__, ret);
825 goto err_init_fs_ns_nsp_tr_state;
826 }
827 }
828 return 0;
829
830 err_init_fs_ns_nsp_tr_state:
831 block_cache_dev_destroy(&self->dev_ns_nsp.dev);
832 err_get_nsp_max_size:
833 ns_close_file(self->ipc_handle, self->dev_ns_nsp.ns_handle);
834 err_open_nsp:
835 return ret;
836 }
837
838 /**
839 * destroy_nsp_fs() - Destroy @self's NSP fs and its backing block devices.
840 */
destroy_nsp_fs(struct block_device_tipc * self)841 static void destroy_nsp_fs(struct block_device_tipc* self) {
842 fs_destroy(&self->tr_state_ns_nsp);
843 block_cache_dev_destroy(&self->dev_ns_nsp.dev);
844 }
845 #endif
846
block_device_ns_disconnect(struct block_device_ns * self)847 static void block_device_ns_disconnect(struct block_device_ns* self) {
848 if (self->ipc_handle != INVALID_IPC_HANDLE) {
849 ns_close_file(self->ipc_handle, self->ns_handle);
850 self->ipc_handle = INVALID_IPC_HANDLE;
851 }
852 }
853
init_ns_backed_filesystems(struct block_device_tipc * self,const struct key * fs_key,struct rpmb_span ns_partition,struct rpmb_span tdp_partition)854 static int init_ns_backed_filesystems(struct block_device_tipc* self,
855 const struct key* fs_key,
856 struct rpmb_span ns_partition,
857 struct rpmb_span tdp_partition) {
858 int ret = init_ns_fs(self, fs_key, ns_partition);
859 if (ret == NS_INIT_NOT_READY) {
860 /* If we don't currently have ns access, we didn't actually initialize
861 * `tr_state_ns`. Trying to init any other ns-dependent fs would fail,
862 * so skip them. */
863 assert(!block_device_tipc_has_ns(self));
864 return 0;
865 } else if (ret < 0) {
866 goto err_init_ns_fs;
867 }
868
869 #if HAS_FS_TDP
870 ret = init_tdp_fs(self, fs_key, tdp_partition);
871 if (ret < 0) {
872 goto err_init_tdp_fs;
873 }
874 #endif
875
876 #if HAS_FS_NSP
877 ret = init_nsp_fs(self, fs_key);
878 if (ret < 0) {
879 goto err_init_nsp_fs;
880 }
881 #endif
882
883 return 0;
884
885 #if HAS_FS_NSP
886 err_init_nsp_fs:
887 #endif
888 #if HAS_FS_TDP
889 block_device_ns_disconnect(&self->dev_ns_tdp);
890 destroy_tdp_fs(self);
891 err_init_tdp_fs:
892 #endif
893 block_device_ns_disconnect(&self->dev_ns);
894 destroy_ns_fs(self);
895 err_init_ns_fs:
896 return ret;
897 }
898
899 /**
900 * rpmb_span_end() - Calculates the first block past the end of @self.
901 */
rpmb_span_end(struct rpmb_span self)902 static uint16_t rpmb_span_end(struct rpmb_span self) {
903 return self.start + self.block_count;
904 }
905
906 /**
907 * calculate_rpmb_spans() - Determines the starts and sizes of RPMB partitions.
908 */
calculate_rpmb_spans(struct rpmb_spans * out)909 static void calculate_rpmb_spans(struct rpmb_spans* out) {
910 out->key.block_count = 1;
911 /* Used to store superblocks */
912 out->ns.block_count = 2;
913 #if HAS_FS_TDP
914 out->tdp.block_count = out->ns.block_count;
915 #else
916 out->tdp.block_count = 0;
917 #endif
918
919 out->key.start = 0;
920 out->ns.start = rpmb_span_end(out->key);
921 out->tdp.start = rpmb_span_end(out->ns);
922 out->rpmb_start = rpmb_span_end(out->tdp);
923 }
924
block_device_tipc_init(struct block_device_tipc * state,handle_t ipc_handle,const struct key * fs_key,const struct rpmb_key * rpmb_key,hwkey_session_t hwkey_session)925 int block_device_tipc_init(struct block_device_tipc* state,
926 handle_t ipc_handle,
927 const struct key* fs_key,
928 const struct rpmb_key* rpmb_key,
929 hwkey_session_t hwkey_session) {
930 int ret;
931 struct rpmb_spans partitions;
932 calculate_rpmb_spans(&partitions);
933
934 state->ipc_handle = ipc_handle;
935
936 /* init rpmb */
937 ret = rpmb_init(&state->rpmb_state, &state->ipc_handle);
938 if (ret < 0) {
939 SS_ERR("%s: rpmb_init failed (%d)\n", __func__, ret);
940 goto err_rpmb_init;
941 }
942
943 ret = block_device_tipc_init_rpmb_key(state->rpmb_state, rpmb_key,
944 partitions.key.start, hwkey_session);
945 if (ret < 0) {
946 SS_ERR("%s: block_device_tipc_init_rpmb_key failed (%d)\n", __func__,
947 ret);
948 goto err_init_rpmb_key;
949 }
950
951 ret = init_rpmb_fs(state, fs_key, partitions.rpmb_start);
952 if (ret < 0) {
953 goto err_init_rpmb_fs;
954 }
955
956 ret = init_ns_backed_filesystems(state, fs_key, partitions.ns,
957 partitions.tdp);
958 if (ret < 0) {
959 goto err_init_ns_fs;
960 }
961
962 return 0;
963
964 err_init_ns_fs:
965 destroy_rpmb_fs(state);
966 err_init_rpmb_fs:
967 err_init_rpmb_key:
968 rpmb_uninit(state->rpmb_state);
969 err_rpmb_init:
970 return ret;
971 }
972
block_device_tipc_destroy(struct block_device_tipc * state)973 void block_device_tipc_destroy(struct block_device_tipc* state) {
974 if (block_device_tipc_has_ns(state)) {
975 #if HAS_FS_NSP
976 destroy_nsp_fs(state);
977 #endif
978 #if HAS_FS_TDP
979 destroy_tdp_fs(state);
980 #endif
981 destroy_ns_fs(state);
982 }
983
984 destroy_rpmb_fs(state);
985 rpmb_uninit(state->rpmb_state);
986 }
987
block_device_tipc_fs_connected(struct block_device_tipc * self,enum storage_filesystem_type fs_type)988 bool block_device_tipc_fs_connected(struct block_device_tipc* self,
989 enum storage_filesystem_type fs_type) {
990 switch (fs_type) {
991 case STORAGE_TP:
992 return self->ipc_handle != INVALID_IPC_HANDLE;
993 case STORAGE_TDEA:
994 return self->ipc_handle != INVALID_IPC_HANDLE;
995 case STORAGE_TD:
996 return block_device_tipc_has_ns(self) &&
997 self->dev_ns.ipc_handle != INVALID_IPC_HANDLE;
998 case STORAGE_TDP:
999 #if HAS_FS_TDP
1000 return block_device_tipc_has_ns(self) &&
1001 self->dev_ns_tdp.ipc_handle != INVALID_IPC_HANDLE;
1002 #else
1003 return block_device_tipc_fs_connected(self, STORAGE_TP);
1004 #endif
1005 case STORAGE_NSP:
1006 #if HAS_FS_NSP
1007 return block_device_tipc_has_ns(self) &&
1008 self->dev_ns_nsp.ipc_handle != INVALID_IPC_HANDLE;
1009 #else
1010 return block_device_tipc_fs_connected(self, STORAGE_TDP);
1011 #endif
1012 case STORAGE_FILESYSTEMS_COUNT:
1013 default:
1014 SS_ERR("%s: Tried to check fs of unrecognized storage_filesystem type: (%d)\n",
1015 __func__, fs_type);
1016 return false;
1017 }
1018 }
1019
block_device_tipc_get_fs(struct block_device_tipc * self,enum storage_filesystem_type fs_type)1020 struct fs* block_device_tipc_get_fs(struct block_device_tipc* self,
1021 enum storage_filesystem_type fs_type) {
1022 assert(block_device_tipc_fs_connected(self, fs_type));
1023
1024 switch (fs_type) {
1025 case STORAGE_TP:
1026 return &self->tr_state_rpmb;
1027 case STORAGE_TDEA:
1028 return &self->tr_state_rpmb;
1029 case STORAGE_TD:
1030 return &self->tr_state_ns;
1031 case STORAGE_TDP:
1032 #if HAS_FS_TDP
1033 return &self->tr_state_ns_tdp;
1034 #else
1035 return block_device_tipc_get_fs(self, STORAGE_TP);
1036 #endif
1037 case STORAGE_NSP:
1038 #if HAS_FS_NSP
1039 return &self->tr_state_ns_nsp;
1040 #else
1041 return block_device_tipc_get_fs(self, STORAGE_TDP);
1042 #endif
1043 case STORAGE_FILESYSTEMS_COUNT:
1044 default:
1045 SS_ERR("%s: Tried to init fs of unrecognized storage_filesystem type: (%d)\n",
1046 __func__, fs_type);
1047 return NULL;
1048 }
1049 }
1050
block_device_tipc_reconnect(struct block_device_tipc * self,handle_t ipc_handle,const struct key * fs_key)1051 int block_device_tipc_reconnect(struct block_device_tipc* self,
1052 handle_t ipc_handle,
1053 const struct key* fs_key) {
1054 int ret;
1055
1056 assert(self->ipc_handle == INVALID_IPC_HANDLE);
1057 /* rpmb_state keeps a pointer to this handle, so updating here will cause
1058 * all the rpmb connections to use the new handle. */
1059 self->ipc_handle = ipc_handle;
1060
1061 bool has_ns = block_device_tipc_has_ns(self);
1062 if (!has_ns) {
1063 struct rpmb_spans partitions;
1064 calculate_rpmb_spans(&partitions);
1065 ret = init_ns_backed_filesystems(self, fs_key, partitions.ns,
1066 partitions.tdp);
1067 if (ret < 0) {
1068 SS_ERR("%s: failed to init NS backed filesystems (%d)\n", __func__,
1069 ret);
1070 return ret;
1071 }
1072 return 0;
1073 }
1074
1075 bool alternate_data_partition;
1076 self->dev_ns.ipc_handle = ipc_handle;
1077 ret = block_device_ns_open_file_with_alternate(&self->dev_ns, ns_filename,
1078 ns_alternate_filename, false,
1079 &alternate_data_partition);
1080 if (ret < 0) {
1081 /* NS not available right now; leave NS filesystems disconnected. */
1082 self->dev_ns.ipc_handle = INVALID_IPC_HANDLE;
1083 SS_ERR("%s: failed to reconnect ns filesystem (%d)\n", __func__, ret);
1084 return 0;
1085 }
1086 assert(alternate_data_partition == self->tr_state_ns.alternate_data);
1087 #if HAS_FS_TDP
1088 self->dev_ns_tdp.ipc_handle = ipc_handle;
1089 ret = block_device_ns_open_file(&self->dev_ns_tdp, tdp_filename, false);
1090 if (ret < 0) {
1091 SS_ERR("%s: failed to reconnect tdp filesystem (%d)\n", __func__, ret);
1092 self->dev_ns_tdp.ipc_handle = INVALID_IPC_HANDLE;
1093 goto err_reconnect_tdp;
1094 }
1095 #endif
1096 #if HAS_FS_NSP
1097 self->dev_ns_nsp.ipc_handle = ipc_handle;
1098 ret = block_device_ns_open_file(&self->dev_ns_nsp, nsp_filename, false);
1099 if (ret < 0) {
1100 SS_ERR("%s: failed to reconnect nsp filesystem (%d)\n", __func__, ret);
1101 self->dev_ns_nsp.ipc_handle = INVALID_IPC_HANDLE;
1102 goto err_reconnect_nsp;
1103 }
1104 #endif
1105
1106 return 0;
1107 #if HAS_FS_NSP
1108 err_reconnect_nsp:
1109 #endif
1110 #if HAS_FS_TDP
1111 block_device_ns_disconnect(&self->dev_ns_tdp);
1112 err_reconnect_tdp:
1113 #endif
1114 block_device_ns_disconnect(&self->dev_ns);
1115 return ret;
1116 }
1117
block_device_tipc_disconnect(struct block_device_tipc * self)1118 void block_device_tipc_disconnect(struct block_device_tipc* self) {
1119 /* Must currently be connected to disconnect */
1120 assert(self->ipc_handle != INVALID_IPC_HANDLE);
1121 /* Disconnects rpmb */
1122 self->ipc_handle = INVALID_IPC_HANDLE;
1123
1124 if (block_device_tipc_has_ns(self)) {
1125 block_device_ns_disconnect(&self->dev_ns);
1126 #if HAS_FS_TDP
1127 block_device_ns_disconnect(&self->dev_ns_tdp);
1128 #endif
1129 #if HAS_FS_NSP
1130 block_device_ns_disconnect(&self->dev_ns_nsp);
1131 #endif
1132 }
1133 }
1134