munmap(addr, PAGE_SIZE);
 }
 
+/*
+ * SGX2 page type modification test in two phases:
+ * Phase 1:
+ * Create a new TCS, consisting out of three new pages (stack page with regular
+ * page type, SSA page with regular page type, and TCS page with TCS page
+ * type) in an initialized enclave and run a simple workload within it.
+ * Phase 2:
+ * Remove the three pages added in phase 1, add a new regular page at the
+ * same address that previously hosted the TCS page and verify that it can
+ * be modified.
+ */
+TEST_F(enclave, tcs_create)
+{
+       struct encl_op_init_tcs_page init_tcs_page_op;
+       struct sgx_enclave_remove_pages remove_ioc;
+       struct encl_op_get_from_addr get_addr_op;
+       struct sgx_enclave_modify_types modt_ioc;
+       struct encl_op_put_to_addr put_addr_op;
+       struct encl_op_get_from_buf get_buf_op;
+       struct encl_op_put_to_buf put_buf_op;
+       void *addr, *tcs, *stack_end, *ssa;
+       struct encl_op_eaccept eaccept_op;
+       size_t total_size = 0;
+       uint64_t val_64;
+       int errno_save;
+       int ret, i;
+
+       ASSERT_TRUE(setup_test_encl(ENCL_HEAP_SIZE_DEFAULT, &self->encl,
+                                   _metadata));
+
+       memset(&self->run, 0, sizeof(self->run));
+       self->run.tcs = self->encl.encl_base;
+
+       /*
+        * Hardware (SGX2) and kernel support is needed for this test. Start
+        * with check that test has a chance of succeeding.
+        */
+       memset(&modt_ioc, 0, sizeof(modt_ioc));
+       ret = ioctl(self->encl.fd, SGX_IOC_ENCLAVE_MODIFY_TYPES, &modt_ioc);
+
+       if (ret == -1) {
+               if (errno == ENOTTY)
+                       SKIP(return,
+                            "Kernel does not support SGX_IOC_ENCLAVE_MODIFY_TYPES ioctl()");
+               else if (errno == ENODEV)
+                       SKIP(return, "System does not support SGX2");
+       }
+
+       /*
+        * Invalid parameters were provided during sanity check,
+        * expect command to fail.
+        */
+       EXPECT_EQ(ret, -1);
+
+       /*
+        * Add three regular pages via EAUG: one will be the TCS stack, one
+        * will be the TCS SSA, and one will be the new TCS. The stack and
+        * SSA will remain as regular pages, the TCS page will need its
+        * type changed after populated with needed data.
+        */
+       for (i = 0; i < self->encl.nr_segments; i++) {
+               struct encl_segment *seg = &self->encl.segment_tbl[i];
+
+               total_size += seg->size;
+       }
+
+       /*
+        * Actual enclave size is expected to be larger than the loaded
+        * test enclave since enclave size must be a power of 2 in bytes while
+        * test_encl does not consume it all.
+        */
+       EXPECT_LT(total_size + 3 * PAGE_SIZE, self->encl.encl_size);
+
+       /*
+        * mmap() three pages at end of existing enclave to be used for the
+        * three new pages.
+        */
+       addr = mmap((void *)self->encl.encl_base + total_size, 3 * PAGE_SIZE,
+                   PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED,
+                   self->encl.fd, 0);
+       EXPECT_NE(addr, MAP_FAILED);
+
+       self->run.exception_vector = 0;
+       self->run.exception_error_code = 0;
+       self->run.exception_addr = 0;
+
+       stack_end = (void *)self->encl.encl_base + total_size;
+       tcs = (void *)self->encl.encl_base + total_size + PAGE_SIZE;
+       ssa = (void *)self->encl.encl_base + total_size + 2 * PAGE_SIZE;
+
+       /*
+        * Run EACCEPT on each new page to trigger the
+        * EACCEPT->(#PF)->EAUG->EACCEPT(again without a #PF) flow.
+        */
+
+       eaccept_op.epc_addr = (unsigned long)stack_end;
+       eaccept_op.flags = SGX_SECINFO_R | SGX_SECINFO_W | SGX_SECINFO_REG | SGX_SECINFO_PENDING;
+       eaccept_op.ret = 0;
+       eaccept_op.header.type = ENCL_OP_EACCEPT;
+
+       EXPECT_EQ(ENCL_CALL(&eaccept_op, &self->run, true), 0);
+
+       if (self->run.exception_vector == 14 &&
+           self->run.exception_error_code == 4 &&
+           self->run.exception_addr == (unsigned long)stack_end) {
+               munmap(addr, 3 * PAGE_SIZE);
+               SKIP(return, "Kernel does not support adding pages to initialized enclave");
+       }
+
+       EXPECT_EEXIT(&self->run);
+       EXPECT_EQ(self->run.exception_vector, 0);
+       EXPECT_EQ(self->run.exception_error_code, 0);
+       EXPECT_EQ(self->run.exception_addr, 0);
+       EXPECT_EQ(eaccept_op.ret, 0);
+
+       eaccept_op.epc_addr = (unsigned long)ssa;
+
+       EXPECT_EQ(ENCL_CALL(&eaccept_op, &self->run, true), 0);
+
+       EXPECT_EEXIT(&self->run);
+       EXPECT_EQ(self->run.exception_vector, 0);
+       EXPECT_EQ(self->run.exception_error_code, 0);
+       EXPECT_EQ(self->run.exception_addr, 0);
+       EXPECT_EQ(eaccept_op.ret, 0);
+
+       eaccept_op.epc_addr = (unsigned long)tcs;
+
+       EXPECT_EQ(ENCL_CALL(&eaccept_op, &self->run, true), 0);
+
+       EXPECT_EEXIT(&self->run);
+       EXPECT_EQ(self->run.exception_vector, 0);
+       EXPECT_EQ(self->run.exception_error_code, 0);
+       EXPECT_EQ(self->run.exception_addr, 0);
+       EXPECT_EQ(eaccept_op.ret, 0);
+
+       /*
+        * Three new pages added to enclave. Now populate the TCS page with
+        * needed data. This should be done from within enclave. Provide
+        * the function that will do the actual data population with needed
+        * data.
+        */
+
+       /*
+        * New TCS will use the "encl_dyn_entry" entrypoint that expects
+        * stack to begin in page before TCS page.
+        */
+       val_64 = encl_get_entry(&self->encl, "encl_dyn_entry");
+       EXPECT_NE(val_64, 0);
+
+       init_tcs_page_op.tcs_page = (unsigned long)tcs;
+       init_tcs_page_op.ssa = (unsigned long)total_size + 2 * PAGE_SIZE;
+       init_tcs_page_op.entry = val_64;
+       init_tcs_page_op.header.type = ENCL_OP_INIT_TCS_PAGE;
+
+       EXPECT_EQ(ENCL_CALL(&init_tcs_page_op, &self->run, true), 0);
+
+       EXPECT_EEXIT(&self->run);
+       EXPECT_EQ(self->run.exception_vector, 0);
+       EXPECT_EQ(self->run.exception_error_code, 0);
+       EXPECT_EQ(self->run.exception_addr, 0);
+
+       /* Change TCS page type to TCS. */
+       memset(&modt_ioc, 0, sizeof(modt_ioc));
+
+       modt_ioc.offset = total_size + PAGE_SIZE;
+       modt_ioc.length = PAGE_SIZE;
+       modt_ioc.page_type = SGX_PAGE_TYPE_TCS;
+
+       ret = ioctl(self->encl.fd, SGX_IOC_ENCLAVE_MODIFY_TYPES, &modt_ioc);
+       errno_save = ret == -1 ? errno : 0;
+
+       EXPECT_EQ(ret, 0);
+       EXPECT_EQ(errno_save, 0);
+       EXPECT_EQ(modt_ioc.result, 0);
+       EXPECT_EQ(modt_ioc.count, 4096);
+
+       /* EACCEPT new TCS page from enclave. */
+       eaccept_op.epc_addr = (unsigned long)tcs;
+       eaccept_op.flags = SGX_SECINFO_TCS | SGX_SECINFO_MODIFIED;
+       eaccept_op.ret = 0;
+       eaccept_op.header.type = ENCL_OP_EACCEPT;
+
+       EXPECT_EQ(ENCL_CALL(&eaccept_op, &self->run, true), 0);
+
+       EXPECT_EEXIT(&self->run);
+       EXPECT_EQ(self->run.exception_vector, 0);
+       EXPECT_EQ(self->run.exception_error_code, 0);
+       EXPECT_EQ(self->run.exception_addr, 0);
+       EXPECT_EQ(eaccept_op.ret, 0);
+
+       /* Run workload from new TCS. */
+       self->run.tcs = (unsigned long)tcs;
+
+       /*
+        * Simple workload to write to data buffer and read value back.
+        */
+       put_buf_op.header.type = ENCL_OP_PUT_TO_BUFFER;
+       put_buf_op.value = MAGIC;
+
+       EXPECT_EQ(ENCL_CALL(&put_buf_op, &self->run, true), 0);
+
+       EXPECT_EEXIT(&self->run);
+       EXPECT_EQ(self->run.exception_vector, 0);
+       EXPECT_EQ(self->run.exception_error_code, 0);
+       EXPECT_EQ(self->run.exception_addr, 0);
+
+       get_buf_op.header.type = ENCL_OP_GET_FROM_BUFFER;
+       get_buf_op.value = 0;
+
+       EXPECT_EQ(ENCL_CALL(&get_buf_op, &self->run, true), 0);
+
+       EXPECT_EQ(get_buf_op.value, MAGIC);
+       EXPECT_EEXIT(&self->run);
+       EXPECT_EQ(self->run.exception_vector, 0);
+       EXPECT_EQ(self->run.exception_error_code, 0);
+       EXPECT_EQ(self->run.exception_addr, 0);
+
+       /*
+        * Phase 2 of test:
+        * Remove pages associated with new TCS, create a regular page
+        * where TCS page used to be and verify it can be used as a regular
+        * page.
+        */
+
+       /* Start page removal by requesting change of page type to PT_TRIM. */
+       memset(&modt_ioc, 0, sizeof(modt_ioc));
+
+       modt_ioc.offset = total_size;
+       modt_ioc.length = 3 * PAGE_SIZE;
+       modt_ioc.page_type = SGX_PAGE_TYPE_TRIM;
+
+       ret = ioctl(self->encl.fd, SGX_IOC_ENCLAVE_MODIFY_TYPES, &modt_ioc);
+       errno_save = ret == -1 ? errno : 0;
+
+       EXPECT_EQ(ret, 0);
+       EXPECT_EQ(errno_save, 0);
+       EXPECT_EQ(modt_ioc.result, 0);
+       EXPECT_EQ(modt_ioc.count, 3 * PAGE_SIZE);
+
+       /*
+        * Enter enclave via TCS #1 and approve page removal by sending
+        * EACCEPT for each of three removed pages.
+        */
+       self->run.tcs = self->encl.encl_base;
+
+       eaccept_op.epc_addr = (unsigned long)stack_end;
+       eaccept_op.flags = SGX_SECINFO_TRIM | SGX_SECINFO_MODIFIED;
+       eaccept_op.ret = 0;
+       eaccept_op.header.type = ENCL_OP_EACCEPT;
+
+       EXPECT_EQ(ENCL_CALL(&eaccept_op, &self->run, true), 0);
+
+       EXPECT_EEXIT(&self->run);
+       EXPECT_EQ(self->run.exception_vector, 0);
+       EXPECT_EQ(self->run.exception_error_code, 0);
+       EXPECT_EQ(self->run.exception_addr, 0);
+       EXPECT_EQ(eaccept_op.ret, 0);
+
+       eaccept_op.epc_addr = (unsigned long)tcs;
+       eaccept_op.ret = 0;
+
+       EXPECT_EQ(ENCL_CALL(&eaccept_op, &self->run, true), 0);
+
+       EXPECT_EEXIT(&self->run);
+       EXPECT_EQ(self->run.exception_vector, 0);
+       EXPECT_EQ(self->run.exception_error_code, 0);
+       EXPECT_EQ(self->run.exception_addr, 0);
+       EXPECT_EQ(eaccept_op.ret, 0);
+
+       eaccept_op.epc_addr = (unsigned long)ssa;
+       eaccept_op.ret = 0;
+
+       EXPECT_EQ(ENCL_CALL(&eaccept_op, &self->run, true), 0);
+
+       EXPECT_EEXIT(&self->run);
+       EXPECT_EQ(self->run.exception_vector, 0);
+       EXPECT_EQ(self->run.exception_error_code, 0);
+       EXPECT_EQ(self->run.exception_addr, 0);
+       EXPECT_EQ(eaccept_op.ret, 0);
+
+       /* Send final ioctl() to complete page removal. */
+       memset(&remove_ioc, 0, sizeof(remove_ioc));
+
+       remove_ioc.offset = total_size;
+       remove_ioc.length = 3 * PAGE_SIZE;
+
+       ret = ioctl(self->encl.fd, SGX_IOC_ENCLAVE_REMOVE_PAGES, &remove_ioc);
+       errno_save = ret == -1 ? errno : 0;
+
+       EXPECT_EQ(ret, 0);
+       EXPECT_EQ(errno_save, 0);
+       EXPECT_EQ(remove_ioc.count, 3 * PAGE_SIZE);
+
+       /*
+        * Enter enclave via TCS #1 and access location where TCS #3 was to
+        * trigger dynamic add of regular page at that location.
+        */
+       eaccept_op.epc_addr = (unsigned long)tcs;
+       eaccept_op.flags = SGX_SECINFO_R | SGX_SECINFO_W | SGX_SECINFO_REG | SGX_SECINFO_PENDING;
+       eaccept_op.ret = 0;
+       eaccept_op.header.type = ENCL_OP_EACCEPT;
+
+       EXPECT_EQ(ENCL_CALL(&eaccept_op, &self->run, true), 0);
+
+       EXPECT_EEXIT(&self->run);
+       EXPECT_EQ(self->run.exception_vector, 0);
+       EXPECT_EQ(self->run.exception_error_code, 0);
+       EXPECT_EQ(self->run.exception_addr, 0);
+       EXPECT_EQ(eaccept_op.ret, 0);
+
+       /*
+        * New page should be accessible from within enclave - write to it.
+        */
+       put_addr_op.value = MAGIC;
+       put_addr_op.addr = (unsigned long)tcs;
+       put_addr_op.header.type = ENCL_OP_PUT_TO_ADDRESS;
+
+       EXPECT_EQ(ENCL_CALL(&put_addr_op, &self->run, true), 0);
+
+       EXPECT_EEXIT(&self->run);
+       EXPECT_EQ(self->run.exception_vector, 0);
+       EXPECT_EQ(self->run.exception_error_code, 0);
+       EXPECT_EQ(self->run.exception_addr, 0);
+
+       /*
+        * Read memory from newly added page that was just written to,
+        * confirming that data previously written (MAGIC) is present.
+        */
+       get_addr_op.value = 0;
+       get_addr_op.addr = (unsigned long)tcs;
+       get_addr_op.header.type = ENCL_OP_GET_FROM_ADDRESS;
+
+       EXPECT_EQ(ENCL_CALL(&get_addr_op, &self->run, true), 0);
+
+       EXPECT_EQ(get_addr_op.value, MAGIC);
+       EXPECT_EEXIT(&self->run);
+       EXPECT_EQ(self->run.exception_vector, 0);
+       EXPECT_EQ(self->run.exception_error_code, 0);
+       EXPECT_EQ(self->run.exception_addr, 0);
+
+       munmap(addr, 3 * PAGE_SIZE);
+}
+
 TEST_HARNESS_MAIN