2 * Copyright (c) 2013-2015 TRUSTONIC LIMITED
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the TRUSTONIC LIMITED nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * Filesystem v2 delegate.
35 * Handles incoming storage requests from TA through STH
51 #define LOG_TAG "TeeFilesystem"
53 #include "tee_client_api.h" /* TEEC_Result */
54 #include "MobiCoreDriverApi.h" /* MC session */
55 #include "sth2ProxyApi.h"
56 #include "sfs_error.h"
59 #define MAX_SECTOR_SIZE 4096
61 #define SFS_L2_CACHE_SLOT_SPACE 12 // Hard coded size, cf. sfs_internal.h
62 #define DEFAULT_WORKSPACE_SIZE (SECTOR_NUM * (MAX_SECTOR_SIZE + SFS_L2_CACHE_SLOT_SPACE))
64 extern const std::string
& getTbStoragePath();
66 /*----------------------------------------------------------------------------
68 *----------------------------------------------------------------------------*/
69 static TEEC_Result
errno2serror() {
72 return S_ERROR_BAD_PARAMETERS
;
74 return S_ERROR_NO_MORE_HANDLES
;
76 return S_ERROR_ITEM_NOT_FOUND
;
78 return S_ERROR_ITEM_EXISTS
;
80 return S_ERROR_STORAGE_NO_SPACE
;
82 return S_ERROR_OUT_OF_MEMORY
;
86 return S_ERROR_STORAGE_UNREACHABLE
;
95 Partition(std::string name
): name_(name
), fd_(NULL
), size_(0) {}
96 const char* name() const {
102 if (::stat(name(), &st
)) {
109 TEEC_Result
create() {
110 LOG_I("%s: Create storage file \"%s\"", __func__
, name());
111 fd_
= ::fopen(name(), "w+b");
113 LOG_E("%s: %s creating storage file \"%s\"", __func__
,
114 strerror(errno
), name());
115 return errno2serror();
119 TEEC_Result
destroy() {
121 /* The partition is not open */
122 return S_ERROR_BAD_STATE
;
125 /* Try to erase the file */
126 if (::unlink(name())) {
127 /* File in use or OS didn't allow the operation */
128 return errno2serror();
135 LOG_I("%s: Open storage file \"%s\"", __func__
, name());
136 fd_
= ::fopen(name(), "r+b");
138 LOG_E("%s: %s opening storage file \"%s\"", __func__
,
139 strerror(errno
), name());
140 return errno2serror();
142 LOG_I("%s: storage file \"%s\" successfully open (size: %ld KB / %ld B))",
143 __func__
, name(), size() / 1024, size());
146 TEEC_Result
close() {
148 /* The partition is not open */
149 return S_ERROR_BAD_STATE
;
157 TEEC_Result
read(uint8_t* buf
, uint32_t length
, uint32_t offset
) {
159 /* The partition is not open */
160 return S_ERROR_BAD_STATE
;
163 if (::fseek(fd_
, offset
, SEEK_SET
)) {
164 LOG_E("%s: fseek error: %s", __func__
, strerror(errno
));
165 return errno2serror();
168 if (::fread(buf
, length
, 1, fd_
) != 1) {
170 LOG_E("%s: fread error: End-Of-File detected", __func__
);
171 return S_ERROR_ITEM_NOT_FOUND
;
173 LOG_E("%s: fread error: %s", __func__
, strerror(errno
));
174 return errno2serror();
179 TEEC_Result
write(const uint8_t* buf
, uint32_t length
, uint32_t offset
) {
181 /* The partition is not open */
182 return S_ERROR_BAD_STATE
;
185 if (::fseek(fd_
, offset
, SEEK_SET
)) {
186 LOG_E("%s: fseek error: %s", __func__
, strerror(errno
));
187 return errno2serror();
190 if (::fwrite(buf
, length
, 1, fd_
) != 1) {
191 LOG_E("%s: fwrite error: %s", __func__
, strerror(errno
));
192 return errno2serror();
198 /* The partition is not open */
199 return S_ERROR_BAD_STATE
;
203 * First make sure that the data in the stdio buffers is flushed to the
207 return errno2serror();
210 /* Then synchronize the file descriptor with the file-system */
211 if (::fdatasync(fileno(fd_
))) {
212 return errno2serror();
217 TEEC_Result
resize(off_t new_size
) {
219 /* The partition is not open */
220 return S_ERROR_BAD_STATE
;
223 if (new_size
== size()) {
227 if (new_size
> size()) {
229 * Enlarge the partition file. Make sure we actually write some
230 * non-zero data into the new sectors. Otherwise, some file-system
231 * might not really reserve the storage space but use a sparse
232 * representation. In this case, a subsequent write instruction
233 * could fail due to out-of-space, which we want to avoid.
235 if (::fseek(fd_
, 0, SEEK_END
)) {
236 LOG_E("%s: fseek error: %s", __func__
, strerror(errno
));
237 return errno2serror();
240 off_t count
= new_size
- size();
242 if (::fputc(0xA5, fd_
) != 0xA5) {
243 LOG_E("%s: fputc error: %s", __func__
, strerror(errno
));
244 return errno2serror();
249 /* Truncate the partition file */
250 if (::ftruncate(fileno(fd_
), new_size
)) {
251 return errno2serror();
260 struct FileSystem::Impl
{
264 * Communication buffer, includes the workspace
267 STH2_delegation_exchange_buffer_t exchange_buffer
;
268 uint8_t workspace
[DEFAULT_WORKSPACE_SIZE
];
272 * Provided by the SWd
274 uint32_t sector_size
;
277 * The 16 possible partitions
279 Partition
* partitions
[16];
281 // thread value does not matter, only initialised for code checkers
282 Impl(): thread(pthread_self()) {
283 ::memset(&dci
, 0, sizeof(dci
));
285 for (int i
= 0; i
< 16; i
++) {
286 partitions
[i
] = NULL
;
290 int executeCommand();
291 const char* getCommandtypeString(uint32_t nInstructionID
);
294 FileSystem::FileSystem(): pimpl_(new Impl
) {}
296 FileSystem::~FileSystem() {
300 void* FileSystem::run(void* arg
) {
301 FileSystem::Impl
* pimpl
= static_cast<FileSystem::Impl
*>(arg
);
306 int FileSystem::open() {
307 const std::string storage_dir_name
= getTbStoragePath();
309 // Create Tbase storage directory if necessary, parent is assumed to exist
310 if (::mkdir(storage_dir_name
.c_str(), 0700) && (errno
!= EEXIST
)) {
311 LOG_ERRNO("creating storage folder");
312 // Do not return any error and block deamon boot flow or stop FSD thread.
313 // Just print a "warning/not critical error message".
314 // Directory could also be created by platform at initialization time.
318 // Create partitions with default names
319 for (int i
= 0; i
< 16; i
++) {
321 snprintf(file_name
, sizeof(file_name
), "/Store_%1X.tf", i
);
322 pimpl_
->partitions
[i
] = new Partition(storage_dir_name
+ file_name
);
325 /* Prepare DCI message buffer */
326 pimpl_
->dci
.exchange_buffer
.nWorkspaceLength
= DEFAULT_WORKSPACE_SIZE
;
328 // Create listening thread
329 int ret
= pthread_create(&pimpl_
->thread
, NULL
, run
, pimpl_
);
331 LOG_E("pthread_create failed with error code %d", ret
);
335 pthread_setname_np(pimpl_
->thread
, "McDaemon.FileSystem");
339 int FileSystem::close() {
340 // Stop listening thread
341 pthread_kill(pimpl_
->thread
, SIGUSR1
);
342 pthread_join(pimpl_
->thread
, NULL
);
344 /* Clear DCI message buffer */
345 ::memset(&pimpl_
->dci
, 0, sizeof(pimpl_
->dci
));
352 void FileSystem::Impl::run() {
353 // Only accept USR1, to give up
355 sigemptyset(&sigmask
);
356 sigaddset(&sigmask
, SIGUSR1
);
357 pthread_sigmask(SIG_UNBLOCK
, &sigmask
, (sigset_t
*)0);
359 mcResult_t mc_ret
= mcOpenDevice(MC_DEVICE_ID_DEFAULT
);
360 if (MC_DRV_OK
!= mc_ret
) {
361 LOG_E("%s: mcOpenDevice returned: 0x%08X\n", __func__
, mc_ret
);
365 const mcUuid_t uuid
= SERVICE_DELEGATION_UUID
;
366 mcSessionHandle_t session_handle
;
367 session_handle
.deviceId
= MC_DEVICE_ID_DEFAULT
;
368 mc_ret
= mcOpenSession(&session_handle
, &uuid
, reinterpret_cast<uint8_t*>(&dci
),
370 if (MC_DRV_OK
!= mc_ret
) {
371 LOG_E("%s: mcOpenSession returned: 0x%08X\n", __func__
, mc_ret
);
372 mcCloseDevice(MC_DEVICE_ID_DEFAULT
);
376 LOG_I("%s: Start listening for request from STH2", __func__
);
378 /* Wait for notification from SWd */
379 LOG_D("%s: Waiting for notification\n", __func__
);
380 mc_ret
= mcWaitNotification(&session_handle
, MC_INFINITE_TIMEOUT_INTERRUPTIBLE
);
381 if (mc_ret
!= MC_DRV_OK
) {
382 LOG_E("%s: mcWaitNotification failed, error=0x%08X\n", __func__
, mc_ret
);
386 /* Read sector size */
387 if (sector_size
== 0) {
388 sector_size
= dci
.exchange_buffer
.nSectorSize
;
389 LOG_D("%s: Sector Size: %d bytes", __func__
, sector_size
);
391 /* Check sector size */
392 if (!(sector_size
== 512 || sector_size
== 1024 ||
393 sector_size
== 2048 || sector_size
== 4096)) {
394 LOG_E("%s: Incorrect sector size: terminating...", __func__
);
399 LOG_D("%s: Received Command from STH2\n", __func__
);
400 if (executeCommand()) {
404 /* Set "listening" flag before notifying SPT2 */
405 dci
.exchange_buffer
.nDaemonState
= STH2_DAEMON_LISTENING
;
406 mc_ret
= mcNotify(&session_handle
);
407 if (mc_ret
!= MC_DRV_OK
) {
408 LOG_E("%s: mcNotify() returned: 0x%08X\n", __func__
, mc_ret
);
413 mc_ret
= mcCloseSession(&session_handle
);
414 if (MC_DRV_OK
!= mc_ret
) {
415 LOG_E("%s: mcCloseSession returned: 0x%08X\n", __func__
, mc_ret
);
418 mc_ret
= mcCloseDevice(MC_DEVICE_ID_DEFAULT
);
419 if (MC_DRV_OK
!= mc_ret
) {
420 LOG_E("%s: mcCloseDevice returned: 0x%08X\n", __func__
, mc_ret
);
423 LOG_W("%s: Exiting Filesystem thread", __func__
);
426 /*----------------------------------------------------------------------------
428 *----------------------------------------------------------------------------*/
430 /*----------------------------------------------------------------------------
431 * Command dispatcher function
432 *----------------------------------------------------------------------------*/
433 /* Debug function to show the command name */
434 const char* FileSystem::Impl::getCommandtypeString(uint32_t nInstructionID
) {
435 switch (nInstructionID
) {
436 case DELEGATION_INSTRUCTION_PARTITION_CREATE
:
437 return "PARTITION_CREATE";
438 case DELEGATION_INSTRUCTION_PARTITION_OPEN
:
439 return "PARTITION_OPEN";
440 case DELEGATION_INSTRUCTION_PARTITION_READ
:
441 return "PARTITION_READ";
442 case DELEGATION_INSTRUCTION_PARTITION_WRITE
:
443 return "PARTITION_WRITE";
444 case DELEGATION_INSTRUCTION_PARTITION_SET_SIZE
:
445 return "PARTITION_SET_SIZE";
446 case DELEGATION_INSTRUCTION_PARTITION_SYNC
:
447 return "PARTITION_SYNC";
448 case DELEGATION_INSTRUCTION_PARTITION_CLOSE
:
449 return "PARTITION_CLOSE";
450 case DELEGATION_INSTRUCTION_PARTITION_DESTROY
:
451 return "PARTITION_DESTROY";
452 case DELEGATION_INSTRUCTION_SHUTDOWN
:
454 case DELEGATION_INSTRUCTION_NOTIFY
:
461 int FileSystem::Impl::executeCommand() {
463 uint32_t nInstructionsIndex
;
464 uint32_t nInstructionsBufferSize
=
465 dci
.exchange_buffer
.nInstructionsBufferSize
;
468 LOG_D("%s: nInstructionsBufferSize=%d", __func__
, nInstructionsBufferSize
);
470 /* Reset the operation results */
471 nError
= TEEC_SUCCESS
;
472 dci
.exchange_buffer
.sAdministrativeData
.nSyncExecuted
= 0;
473 ::memset(dci
.exchange_buffer
.sAdministrativeData
.nPartitionErrorStates
, 0,
474 sizeof(dci
.exchange_buffer
.sAdministrativeData
.nPartitionErrorStates
));
475 ::memset(dci
.exchange_buffer
.sAdministrativeData
.nPartitionOpenSizes
, 0,
476 sizeof(dci
.exchange_buffer
.sAdministrativeData
.nPartitionOpenSizes
));
478 /* Execute the instructions */
479 nInstructionsIndex
= 0;
481 DELEGATION_INSTRUCTION
* pInstruction
;
482 uint32_t nInstructionID
;
483 pInstruction
= (DELEGATION_INSTRUCTION
*)(
484 &dci
.exchange_buffer
.sInstructions
[nInstructionsIndex
/4]);
485 if (nInstructionsIndex
+ 4 > nInstructionsBufferSize
) {
486 LOG_D("%s: Instruction buffer end, size = %i", __func__
,
487 nInstructionsBufferSize
);
491 nInstructionID
= pInstruction
->sGeneric
.nInstructionID
;
492 nInstructionsIndex
+= 4;
494 LOG_D("%s: nInstructionID=0x%02X [%s], nInstructionsIndex=%d", __func__
,
495 nInstructionID
, getCommandtypeString(nInstructionID
), nInstructionsIndex
);
497 if ((nInstructionID
& 0x0F) == 0) {
498 /* Partition-independent instruction */
499 switch (nInstructionID
) {
500 case DELEGATION_INSTRUCTION_SHUTDOWN
: {
503 case DELEGATION_INSTRUCTION_NOTIFY
: {
504 /* Parse the instruction parameters */
505 wchar_t pMessage
[100];
506 uint32_t nMessageType
;
507 uint32_t nMessageSize
;
508 ::memset(pMessage
, 0, 100*sizeof(wchar_t));
510 if (nInstructionsIndex
+ 8 > nInstructionsBufferSize
) {
514 nMessageType
= pInstruction
->sNotify
.nMessageType
;
515 nMessageSize
= pInstruction
->sNotify
.nMessageSize
;
516 nInstructionsIndex
+= 8;
517 if (nMessageSize
> (99)*sizeof(wchar_t)) {
518 /* How to handle the error correctly in this case ? */
522 if (nInstructionsIndex
+ nMessageSize
> nInstructionsBufferSize
) {
526 ::memcpy(pMessage
, &pInstruction
->sNotify
.nMessage
[0], nMessageSize
);
527 nInstructionsIndex
+= nMessageSize
;
528 /* Align the pInstructionsIndex on 4 bytes */
529 nInstructionsIndex
= (nInstructionsIndex
+ 3)&~3;
530 switch (nMessageType
) {
531 case DELEGATION_NOTIFY_TYPE_ERROR
:
532 LOG_E("%s: %ls", __func__
, pMessage
);
534 case DELEGATION_NOTIFY_TYPE_WARNING
:
535 LOG_W("%s: %ls", __func__
, pMessage
);
537 case DELEGATION_NOTIFY_TYPE_DEBUG
:
538 LOG_D("%s: DEBUG: %ls", __func__
, pMessage
);
541 LOG_D("%s: %ls", __func__
, pMessage
);
546 LOG_E("%s: Unknown instruction identifier: %02X", __func__
, nInstructionID
);
547 nError
= S_ERROR_BAD_PARAMETERS
;
552 /* Partition-specific instruction */
553 uint32_t nPartitionID
= (nInstructionID
& 0xF0) >> 4;
554 Partition
* partition
= partitions
[nPartitionID
];
555 if (dci
.exchange_buffer
.sAdministrativeData
.nPartitionErrorStates
[nPartitionID
]
557 /* Execute the instruction only if there is currently no error on the partition */
558 switch (nInstructionID
& 0x0F) {
559 case DELEGATION_INSTRUCTION_PARTITION_CREATE
: {
560 nError
= partition
->create();
561 LOG_D("%s: INSTRUCTION: ID=0x%x pid=%d err=0x%08X", __func__
,
562 (nInstructionID
& 0x0F), nPartitionID
, nError
);
565 case DELEGATION_INSTRUCTION_PARTITION_OPEN
: {
566 nError
= partition
->open();
567 LOG_D("%s: INSTRUCTION: ID=0x%x pid=%d pSize=%ld err=0x%08X", __func__
,
568 (nInstructionID
& 0x0F), nPartitionID
, partition
->size() / sector_size
,
570 if (nError
== S_SUCCESS
) {
571 dci
.exchange_buffer
.sAdministrativeData
.nPartitionOpenSizes
[nPartitionID
] =
572 static_cast<unsigned int>(partition
->size() / sector_size
);
576 case DELEGATION_INSTRUCTION_PARTITION_READ
: {
577 /* Parse parameters */
579 uint32_t nWorkspaceOffset
;
580 if (nInstructionsIndex
+ 8 > nInstructionsBufferSize
) {
584 nSectorID
= pInstruction
->sReadWrite
.nSectorID
;
585 nWorkspaceOffset
= pInstruction
->sReadWrite
.nWorkspaceOffset
;
586 nInstructionsIndex
+= 8;
587 LOG_D("%s: >Partition %1X: read sector 0x%08X into workspace at offset 0x%08X",
588 __func__
, nPartitionID
, nSectorID
, nWorkspaceOffset
);
589 nError
= partition
->read(dci
.exchange_buffer
.sWorkspace
+ nWorkspaceOffset
,
590 sector_size
, nSectorID
* sector_size
);
591 LOG_D("%s: INSTRUCTION: ID=0x%x pid=%d sid=%d woff=%d err=0x%08X", __func__
,
592 (nInstructionID
& 0x0F), nPartitionID
, nSectorID
, nWorkspaceOffset
, nError
);
595 case DELEGATION_INSTRUCTION_PARTITION_WRITE
: {
596 /* Parse parameters */
598 uint32_t nWorkspaceOffset
;
599 if (nInstructionsIndex
+ 8 > nInstructionsBufferSize
) {
603 nSectorID
= pInstruction
->sReadWrite
.nSectorID
;
604 nWorkspaceOffset
= pInstruction
->sReadWrite
.nWorkspaceOffset
;
605 nInstructionsIndex
+= 8;
606 LOG_D("%s: >Partition %1X: write sector 0x%X from workspace at offset 0x%X",
607 __func__
, nPartitionID
, nSectorID
, nWorkspaceOffset
);
608 nError
= partition
->write(dci
.exchange_buffer
.sWorkspace
+ nWorkspaceOffset
,
609 sector_size
, nSectorID
* sector_size
);
610 LOG_D("%s: INSTRUCTION: ID=0x%x pid=%d sid=%d woff=%d err=0x%08X", __func__
,
611 (nInstructionID
& 0x0F), nPartitionID
, nSectorID
, nWorkspaceOffset
, nError
);
614 case DELEGATION_INSTRUCTION_PARTITION_SYNC
: {
615 nError
= partition
->sync();
616 LOG_D("%s: INSTRUCTION: ID=0x%x pid=%d err=0x%08X", __func__
,
617 (nInstructionID
& 0x0F), nPartitionID
, nError
);
618 if (nError
== S_SUCCESS
) {
619 dci
.exchange_buffer
.sAdministrativeData
.nSyncExecuted
++;
623 case DELEGATION_INSTRUCTION_PARTITION_SET_SIZE
: {
624 // nNewSize is a number of sectors
626 if (nInstructionsIndex
+ 4 > nInstructionsBufferSize
) {
630 nNewSize
= pInstruction
->sSetSize
.nNewSize
;
631 nInstructionsIndex
+= 4;
632 nError
= partition
->resize(nNewSize
* sector_size
);
633 LOG_D("%s: INSTRUCTION: ID=0x%x pid=%d nNewSize=%d err=0x%08X", __func__
,
634 (nInstructionID
& 0x0F), nPartitionID
, nNewSize
, nError
);
637 case DELEGATION_INSTRUCTION_PARTITION_CLOSE
: {
638 nError
= partition
->close();
639 LOG_D("%s: INSTRUCTION: ID=0x%x pid=%d err=0x%08X", __func__
,
640 (nInstructionID
& 0x0F), nPartitionID
, nError
);
643 case DELEGATION_INSTRUCTION_PARTITION_DESTROY
: {
644 nError
= partition
->destroy();
645 LOG_D("%s: INSTRUCTION: ID=0x%x pid=%d err=0x%08X", __func__
,
646 (nInstructionID
& 0x0F), nPartitionID
, nError
);
650 LOG_E("%s: Unknown instruction identifier: %02X", __func__
, nInstructionID
);
651 nError
= S_ERROR_BAD_PARAMETERS
;
655 dci
.exchange_buffer
.sAdministrativeData
.nPartitionErrorStates
[nPartitionID
] =
658 /* Skip the instruction if there is currently error on the partition */
659 switch (nInstructionID
& 0x0F) {
660 case DELEGATION_INSTRUCTION_PARTITION_CREATE
:
661 case DELEGATION_INSTRUCTION_PARTITION_OPEN
:
662 case DELEGATION_INSTRUCTION_PARTITION_SYNC
:
663 case DELEGATION_INSTRUCTION_PARTITION_CLOSE
:
664 case DELEGATION_INSTRUCTION_PARTITION_DESTROY
:
666 case DELEGATION_INSTRUCTION_PARTITION_READ
:
667 case DELEGATION_INSTRUCTION_PARTITION_WRITE
: {
668 if (nInstructionsIndex
+ 8 > nInstructionsBufferSize
) {
671 nInstructionsIndex
+= 8;
674 case DELEGATION_INSTRUCTION_PARTITION_SET_SIZE
: {
675 if (nInstructionsIndex
+ 4 > nInstructionsBufferSize
) {
678 nInstructionsIndex
+= 4;
682 LOG_E("%s: Unknown instruction identifier: %02X", __func__
, nInstructionID
);
683 /* OMS: update partition error with BAD PARAM ? */