TrustZone: WSM Notify Policy Introduction
WSM Notify Policy Introduction
1. Backgroud:
Page migration issue: physical memory page migration means the pages which belong to linux memory may move from one place to another place in some cases.
Because of the basic structure of trustzone, as we know, once for invoking the memory in swd, need to register it first in swd, page migration may cause data inconsistency.
For sharing large data among multi-OSes , we design the memory share between nwd and swd as below:
Actually, additional design of page migration is CMA (contiguous memory allocator). That is linux reserve some memory for Camera, GPU, HDMI. On FoxB, the size of the memory is 77MB.
Our goal is when page migration happen, swd can walk on the right page which nwd remapped.
2. Design:
2.1 Idea of design:
Key point:
1. How to bring cpu1 to nwd let it come into swd until the end of kernel migration.
---->check 4.3.1;
2. How to update the page index in SWD? This is concern about performance of Search and update.
--->check 4.3.2;
3. Data Structure:
3.1 Notify type
Tzdrv make an agreement about the notify type with linux kernel.
typedef enum
{
TZ_MIGRATE_INVOKE,
TZ_MIGRATE_START,
TZ_MIGRATE_ROOLBACK,
}TZ_PM_NotifyType;
3.2 kernel part:
int (*tzwsm_do_page_migrate)(struct page *pold_page, struct page *pnew_page, int notify_type) = NULL; EXPORT_SYMBOL(tzwsm_do_page_migrate); |
It is very important to find a place to hijack kernel code, current kernel decided that only migrate one page for each time.
And this policy can be changed based on the test result of current decision.
3.3 tzdrv part:
According to the mechanism:
Tzdrv need to assign the function point and Implement then function.
extern int (*tzwsm_do_page_migrate)(struct page *, struct page *, int ); static int TZMigrate_Manage_impl( struct page *, struct page *, int ); |
4. Code and implantation
4.1 Kernel part:
For kernel part, kernel should do add Page flag, Export the API tzwsm_do_page_migrate, then find the place to call the function.
include/linue/page-flags.h
include/linue/page-flags.h enum pageflags { PG_locked, /* Page is locked. Don't touch. */ PG_error, PG_referenced, PG_uptodate, PG_dirty, ... PG_tzmapped, /*add page flag*/ __NR_PAGEFLAGS, |
mm/page_alloc.c
static const struct trace_print_flags pageflag_names[] = { {1UL << PG_locked, "locked" }, {1UL << PG_error, "error" }, {1UL << PG_referenced, "referenced" }, {1UL << PG_uptodate, "uptodate" }, {1UL << PG_dirty, "dirty" }, ... {1UL << PG_tzmapped, "tzmapped" }, |
mm/migrate.c
--->migrate_pages
--->unmap_and_move
--->__unmap_and_move
--->move_to_new_page
static int move_to_new_page(struct page *newpage, struct page *page, int remap_swapcache, enum migrate_mode mode) { struct address_space *mapping; int rc; /* * Block others from accessing the page when we get around to * establishing additional references. We are the only one * holding a reference to the new page at this point. */ if (!trylock_page(newpage)) BUG(); if(tzwsm_do_page_migrate != NULL && (page->flags & ~(1<<PG_tzmapped))) { tzwsm_do_page_migrate(page, newpage, 1); => migration start (notitype is 1) }
/* Prepare mapping for the new page.*/ newpage->index = page->index; newpage->mapping = page->mapping; if (PageSwapBacked(page)) SetPageSwapBacked(newpage); mapping = page_mapping(page); if (!mapping) rc = migrate_page(mapping, newpage, page, mode); else if (mapping->a_ops->migratepage) /* * Most pages have a mapping and most filesystems provide a * migratepage callback. Anonymous pages are part of swap * space which also has its own migratepage callback. This * is the most common path for page migration. */ rc = mapping->a_ops->migratepage(mapping, newpage, page, mode); else rc = fallback_migrate_page(mapping, newpage, page, mode); if (rc != MIGRATEPAGE_SUCCESS) { newpage->mapping = NULL; if(tzwsm_do_page_migrate != NULL && (page->flags & ~(1<<PG_tzmapped))) { /*migrate failed , notify to tzdrv to rollback.*/ tzwsm_do_page_migrate(page, newpage, 2); => migrated with ok ... (notitype is 2) } } else { if (remap_swapcache) remove_migration_ptes(page, newpage); page->mapping = NULL; if(tzwsm_do_page_migrate != NULL && (page->flags & ~(1<<PG_tzmapped))) { /*migrate success , then notify to tzdrv*/ tzwsm_do_page_migrate(page, newpage, 0); => migrated with ok ... (notitype is 0) } } unlock_page(newpage); return rc; } |
4.2 Tzdrv part:
Makefile:
-rwxrwxrwx 1 xichen scrc_dtv 3183 Mar 5 15:16 Makefile.TZAPI
-rwxrwxrwx 1 xichen scrc_dtv 5911 Mar 5 15:16 Makefile.TZDriver
Two things need to be done.
- comment BIT-MAP solution:
- add MACRO “CONFIG_WSM_MIGRATE_NOTIFY_POLICY”
For example:
#enable full-aysnc export TZALL_CFG_FULL_ASYNC=y #enbale bit-map export CONFIG_BIT_MAP_WSM_MODE=n #unable dedicate solution export CONFIG_WSM_DEDICATE_MEMORY=n |
@~INT/Source
[xichen@SSDEV002 Source]$ grep -nr CONFIG_WSM_MIGRATE_NOTIFY_POLICY ./ ./NWD/TZDriver/src/tzst_comm_drv.c:891:#ifdef CONFIG_WSM_MIGRATE_NOTIFY_POLICY ./NWD/TZDriver/src/tzst_comm_drv.c:965:#ifdef CONFIG_WSM_MIGRATE_NOTIFY_POLICY ./NWD/TZDriver/src/tzst_comm_drv.c:1357:#ifdef CONFIG_WSM_MIGRATE_NOTIFY_POLICY ./NWD/TZDriver/src/tzst_comm_drv_wsm_migrate_notify.c:15:#ifdef CONFIG_WSM_MIGRATE_NOTIFY_POLICY ./NWD/TZDriver/src/tzst_comm_drv_memory_manager.c:49:#elif defined(CONFIG_WSM_MIGRATE_NOTIFY_POLICY) ./NWD/TZDriver/src/tzst_comm_drv_memory_manager.c:93:#elif defined(CONFIG_WSM_MIGRATE_NOTIFY_POLICY) ./NWD/TZDriver/src/tzst_comm_drv_memory_manager.c:147:#elif defined(CONFIG_WSM_MIGRATE_NOTIFY_POLICY) ./NWD/TZDriver/include/tzst_comm_drv_wsm_migrate_notify.h:17:#ifdef CONFIG_WSM_MIGRATE_NOTIFY_POLICY ./NWD/TZDriver/include/system_services/svc_memory_manager.h:25:#ifdef CONFIG_WSM_MIGRATE_NOTIFY_POLICY ./SWD/secos/nuttx-6.13/configs/foxb_2014/trustzone/defconfig:711:CONFIG_WSM_MIGRATE_NOTIFY_POLICY=y ./SWD/secos/nuttx-6.13/configs/foxb_2014/trustzone/defconfig.base:711:CONFIG_WSM_MIGRATE_NOTIFY_POLICY=y ./SWD/secos/nuttx-6.13/trustzone/system/svc_memory_manager.c:201:#ifdef CONFIG_WSM_MIGRATE_NOTIFY_POLICY ./SWD/secos/nuttx-6.13/trustzone/system/svc_memory_manager.c:1169:#ifdef CONFIG_WSM_MIGRATE_NOTIFY_POLICY ./SWD/secos/nuttx-6.13/trustzone/system/svc_memory_manager.c:1339:#ifdef CONFIG_WSM_MIGRATE_NOTIFY_POLICY ./SWD/secos/nuttx-6.13/trustzone/system/svc_memory_manager.c:1384:#ifdef CONFIG_WSM_MIGRATE_NOTIFY_POLICY ./SWD/secos/nuttx-6.13/trustzone/system/svc_memory_manager.c:1896:#ifdef CONFIG_WSM_MIGRATE_NOTIFY_POLICY ./SWD/secos/nuttx-6.13/trustzone/system/svc_memory_manager.h:46:#ifdef CONFIG_WSM_MIGRATE_NOTIFY_POLICY |
PS:
We need to add mlock/munlock in userspace.
Details please check:
https://www.kernel.org/doc/Documentation/vm/page_migration
4.3 Strategy:
4.3.1: prevent data inconsistency issue
Extreme Case1: when cpu(cpu0,2,3) was migrating page in nwd, we need to prevent cpu1 touch the memory region.
The answer: About TZSF, we have only one entry to enter into swd, DrvCore_SendSMC.
In order to send SMC to swd, Client application need to acquire the semaphore lock, and every time only one operation can get the semaphore, so in the case of this, we just acquire the lock, other CA should be wait for the lock.
down(&gsDrvRec.oSem); /* Acquire lock. */ up(&gsDrvRec.oSem); /* Release lock. */ |
Extreme Case2: when kernel migrate end, swd migrating in swd, we need to prevent other cpu touch the memory region.
The answer: Kernel will lock the page until kernel migrate end, this means swd migrate should be done before the end the kernel migrate.
---->migrate_pages
--->unmap_and_move
--->lock_page
--->__lock_page
--->PG_locked
4.3.2 Migrate page in swd:
In swd, page migrate means the content of the page table should be update.
But the challenge is that each page was remapped individual.
For example, there are such amount of wsm which size is only 8bytes, then it has a high probability that these wsms may located in one page, but in swd, individual wsm handle was created and each wsm have its own virtual address although it belong to one page.
When kernel migrate the page, the challenge is that we need to find all the wsms then update the page index. Only in this case, all the wsms can walk and approach the correct memory region.
! On TZSF, this can be realized based on the centralized management of page index.
This has performance issue: for 16M memory region for wsm in swd,
The page index need about 4096; 16*1024*1024/PAGE_SIZE ==4096
Worst case: pre-condition: FOXB HTS board
WT: write through WB: write back
L1 |
L2 |
performance |
disabled |
disabled |
~400us |
WT |
WT |
~170 |
WB |
disabled |
~80us |
WT |
disable |
~400us |
PS: L2 was controlled by PL310. L1 donot support WT on cortex A9;
The data just get from FoxB; other board gets better performance than FoxB.
5. Test, Enhance, Risk
Step 1:
Simple test case:
- When register the memory to swd, first we get the physical address of the memory, register to swd.
- Call migrate_pages to migrate one page by linux kernel.
- Update the page index with new physical address in swd.
Result: success; PASS+
Step 2:
- Hijack exedsp image with hijacked tzdrv.ko and libTZ.so.
- Hijack kernel to get the uImage.
- Pop-up button.
This case is hard to say that the page migration will appear or not.
Because the notify policy works only when kernel migrate the memory which was remapped by tzdrv. So we need to find a way to let kernel migrate the tzdrv remapped page.
Step 3:
Directly map HDMA region when memory allocate for TZ, then automatic play Netflix then press pop-up button.
Actually, it is extreme case to test page migration, as in the real scenario, this could not happen, and so much pressure for test is needed.
6. Additional:
Principles of design
- worst case:
In order to prevent issue happening in extreme case, we need to consider all kinds of complicated and extreme case, although the majority of cases, probably all of these are not existing or a remote possibility.
The best way to solve this kind of problem is that considering complex situations is fine , but make decision according to result of high strength test is a good choice.
As we all know , Linux kernel is not that kind of perfect , but 99% of the cases are perfect , that is enough and this is the industry way.
- Preference for NWD operation then SWD operation:
Board: FoxB HTS.
NWD: 20us.
SWD: ~45us.
Because of the performance, or consider some other limited factors, we need to make choose work in NWD then work in SWD.
- Light the normal operation:
This may have conflict with the above situation, but it cannot affect the normal situation just because of some extreme case, since it seems stupid.
For example:
If there is 10% case of page migration which was regarded as abnormal case , but for 90% cases , increase the time cost on large mount cases to maintain some additional information ,such as list info ,the tree, is not good idea except it have to do.