1 /*
2  * Copyright (c) 2017, 2020 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "mem_protect.h"
8 #include <kernel_internal.h> /* For z_main_thread */
9 #include <zephyr/sys/libc-hooks.h> /* for z_libc_partition */
10 
11 struct k_thread child_thread;
12 K_THREAD_STACK_DEFINE(child_stack, KOBJECT_STACK_SIZE);
13 
14 /* Special memory domain for test case purposes */
15 static struct k_mem_domain test_domain;
16 
17 #if Z_LIBC_PARTITION_EXISTS
18 #define PARTS_USED	3
19 #else
20 #define PARTS_USED	2
21 #endif
22 /* Maximum number of allowable memory partitions defined by the build */
23 #define NUM_RW_PARTS	(CONFIG_MAX_DOMAIN_PARTITIONS - PARTS_USED)
24 
25 /* Max number of allowable partitions, derived at runtime. Might be less. */
26 ZTEST_BMEM int num_rw_parts;
27 
28 /* Set of read-write buffers each in their own partition */
29 static volatile uint8_t __aligned(MEM_REGION_ALLOC)
30 	rw_bufs[NUM_RW_PARTS][MEM_REGION_ALLOC];
31 static struct k_mem_partition rw_parts[NUM_RW_PARTS];
32 
33 /* A single read-only partition */
34 static volatile uint8_t __aligned(MEM_REGION_ALLOC) ro_buf[MEM_REGION_ALLOC];
35 K_MEM_PARTITION_DEFINE(ro_part, ro_buf, sizeof(ro_buf),
36 		       K_MEM_PARTITION_P_RO_U_RO);
37 /* A partition to test overlap that has same ro_buf as a partition ro_part */
38 K_MEM_PARTITION_DEFINE(overlap_part, ro_buf, sizeof(ro_buf),
39 		       K_MEM_PARTITION_P_RW_U_RW);
40 
41 /* Static thread, used by a couple tests */
zzz_entry(void * p1,void * p2,void * p3)42 static void zzz_entry(void *p1, void *p2, void *p3)
43 {
44 	k_sleep(K_FOREVER);
45 }
46 
47 static K_THREAD_DEFINE(zzz_thread, 256 + CONFIG_TEST_EXTRA_STACK_SIZE,
48 		       zzz_entry, NULL, NULL, NULL, 0, 0, 0);
49 
test_mem_domain_setup(void)50 void test_mem_domain_setup(void)
51 {
52 	int max_parts = arch_mem_domain_max_partitions_get();
53 	struct k_mem_partition *parts[] = {
54 #if Z_LIBC_PARTITION_EXISTS
55 		&z_libc_partition,
56 #endif
57 		&ro_part, &ztest_mem_partition
58 	};
59 
60 	num_rw_parts = max_parts - PARTS_USED;
61 	zassert_true(num_rw_parts <= NUM_RW_PARTS,
62 			"CONFIG_MAX_DOMAIN_PARTITIONS incorrectly tuned, %d should be at least %d",
63 			CONFIG_MAX_DOMAIN_PARTITIONS, max_parts);
64 	zassert_true(num_rw_parts > 0, "no free memory partitions");
65 
66 	zassert_equal(
67 		k_mem_domain_init(&test_domain, ARRAY_SIZE(parts), parts),
68 		0, "failed to initialize memory domain");
69 
70 	for (unsigned int i = 0; i < num_rw_parts; i++) {
71 		rw_parts[i].start = (uintptr_t)&rw_bufs[i];
72 		rw_parts[i].size = MEM_REGION_ALLOC;
73 		rw_parts[i].attr = K_MEM_PARTITION_P_RW_U_RW;
74 
75 		for (unsigned int j = 0; j < MEM_REGION_ALLOC; j++) {
76 			rw_bufs[i][j] = (j % 256U);
77 		}
78 
79 		zassert_equal(
80 			k_mem_domain_add_partition(&test_domain, &rw_parts[i]),
81 			0, "cannot add memory partition");
82 	}
83 
84 	for (unsigned int j = 0; j < MEM_REGION_ALLOC; j++) {
85 		ro_buf[j] = (j % 256U);
86 	}
87 }
88 
89 /* Helper function; run a function under a child user thread.
90  * If domain is not NULL, add the child thread to that domain, instead of
91  * whatever it would inherit.
92  */
spawn_child_thread(k_thread_entry_t entry,struct k_mem_domain * domain,bool should_fault)93 static void spawn_child_thread(k_thread_entry_t entry,
94 			       struct k_mem_domain *domain, bool should_fault)
95 {
96 	set_fault_valid(should_fault);
97 
98 	k_thread_create(&child_thread, child_stack,
99 			K_THREAD_STACK_SIZEOF(child_stack), entry,
100 			NULL, NULL, NULL, 0, K_USER, K_FOREVER);
101 	k_thread_name_set(&child_thread, "child_thread");
102 	if (domain != NULL) {
103 		k_mem_domain_add_thread(domain, &child_thread);
104 	}
105 	k_thread_start(&child_thread);
106 	k_thread_join(&child_thread, K_FOREVER);
107 
108 	if (should_fault && valid_fault) {
109 		/* valid_fault gets cleared if an expected exception
110 		 * took place
111 		 */
112 		printk("test function %p was supposed to fault but didn't\n",
113 		       entry);
114 		ztest_test_fail();
115 	}
116 }
117 
118 /* read and write to all the rw_parts */
rw_part_access(void * p1,void * p2,void * p3)119 static void rw_part_access(void *p1, void *p2, void *p3)
120 {
121 	for (unsigned int i = 0; i < num_rw_parts; i++) {
122 		for (unsigned int j = 0; j < MEM_REGION_ALLOC; j++) {
123 			/* Test read */
124 			zassert_equal(rw_bufs[i][j], j % 256U,
125 				      "bad data in rw_buf[%d][%d]", i, j);
126 			/* Test writes */
127 			rw_bufs[i][j]++;
128 			rw_bufs[i][j]--;
129 		}
130 	}
131 }
132 
133 /* read the ro_part */
ro_part_access(void * p1,void * p2,void * p3)134 static void ro_part_access(void *p1, void *p2, void *p3)
135 {
136 	for (unsigned int i = 0; i < MEM_REGION_ALLOC; i++) {
137 		zassert_equal(ro_buf[i], i % 256U,
138 			      "bad data in ro_buf[%d]", i);
139 	}
140 }
141 
142 /* attempt to write to ro_part */
ro_write_entry(void * p1,void * p2,void * p3)143 static void ro_write_entry(void *p1, void *p2, void *p3)
144 {
145 	/* Should fault here */
146 	ro_buf[0] = 200;
147 }
148 
149 /**
150  * @brief Check if the mem_domain is configured and accessible for userspace
151  *
152  * Join a memory domain with a read-write memory partition and a read-only
153  * partition within it, and show that the data in the partition is accessible
154  * as expected by the permissions provided.
155  *
156  * @ingroup kernel_memprotect_tests
157  */
ZTEST(mem_protect_domain,test_mem_domain_valid_access)158 ZTEST(mem_protect_domain, test_mem_domain_valid_access)
159 {
160 	spawn_child_thread(rw_part_access, &test_domain, false);
161 	spawn_child_thread(ro_part_access, &test_domain, false);
162 }
163 
164 /**
165  * @brief Show that a user thread can't touch partitions not in its domain
166  *
167  * @ingroup kernel_memprotect_tests
168  */
ZTEST(mem_protect_domain,test_mem_domain_invalid_access)169 ZTEST(mem_protect_domain, test_mem_domain_invalid_access)
170 {
171 	/* child not added to test_domain, will fault for both */
172 	spawn_child_thread(rw_part_access, NULL, true);
173 	spawn_child_thread(ro_part_access, NULL, true);
174 }
175 
176 /**
177  * @brief Show that a read-only partition can't be written to
178  *
179  * @ingroup kernel_memgroup_tests
180  */
ZTEST(mem_protect_domain,test_mem_domain_no_writes_to_ro)181 ZTEST(mem_protect_domain, test_mem_domain_no_writes_to_ro)
182 {
183 	/* Show that trying to write to a read-only partition causes a fault */
184 	spawn_child_thread(ro_write_entry, &test_domain, true);
185 }
186 
187 /**
188  * @brief Show that adding/removing partitions works
189  *
190  * Show that removing a partition doesn't affect access to other partitions.
191  * Show that removing a partition generates a fault if its data is accessed.
192  * Show that adding a partition back restores access from a user thread.
193  *
194  * @ingroup kernel_memprotect_tests
195  */
ZTEST(mem_protect_domain,test_mem_domain_remove_add_partition)196 ZTEST(mem_protect_domain, test_mem_domain_remove_add_partition)
197 {
198 	zassert_equal(
199 		k_mem_domain_remove_partition(&test_domain, &rw_parts[0]),
200 		0, "failed to remove memory partition");
201 
202 	/* Should still work, we didn't remove ro_part */
203 	spawn_child_thread(ro_part_access, &test_domain, false);
204 
205 	/* This will fault, we removed one of the rw_part from the domain */
206 	spawn_child_thread(rw_part_access, &test_domain, true);
207 
208 	/* Restore test_domain contents so we don't mess up other tests */
209 	zassert_equal(
210 		k_mem_domain_add_partition(&test_domain, &rw_parts[0]),
211 		0, "failed to add memory partition");
212 
213 	/* Should work again */
214 	spawn_child_thread(rw_part_access, &test_domain, false);
215 }
216 
217 /* user mode will attempt to initialize this and fail */
218 static struct k_mem_domain no_access_domain;
219 
220 /* Extra partition that a user thread can't add to a domain */
221 static volatile uint8_t __aligned(MEM_REGION_ALLOC)
222 	no_access_buf[MEM_REGION_ALLOC];
223 K_MEM_PARTITION_DEFINE(no_access_part, no_access_buf, sizeof(no_access_buf),
224 		       K_MEM_PARTITION_P_RW_U_RW);
225 
mem_domain_init_entry(void * p1,void * p2,void * p3)226 static void mem_domain_init_entry(void *p1, void *p2, void *p3)
227 {
228 	zassert_equal(
229 		k_mem_domain_init(&no_access_domain, 0, NULL),
230 		0, "failed to initialize memory domain");
231 }
232 
mem_domain_add_partition_entry(void * p1,void * p2,void * p3)233 static void mem_domain_add_partition_entry(void *p1, void *p2, void *p3)
234 {
235 	zassert_equal(
236 		k_mem_domain_add_partition(&test_domain, &no_access_part),
237 		0, "failed to add memory partition");
238 }
239 
mem_domain_remove_partition_entry(void * p1,void * p2,void * p3)240 static void mem_domain_remove_partition_entry(void *p1, void *p2, void *p3)
241 {
242 	zassert_equal(
243 		k_mem_domain_remove_partition(&test_domain, &ro_part),
244 		0, "failed to remove memory partition");
245 }
246 
mem_domain_add_thread_entry(void * p1,void * p2,void * p3)247 static void mem_domain_add_thread_entry(void *p1, void *p2, void *p3)
248 {
249 	k_mem_domain_add_thread(&test_domain, zzz_thread);
250 }
251 
252 /**
253  * @brief Test access memory domain APIs allowed to supervisor threads only
254  *
255  * Show that invoking any of the memory domain APIs from user mode leads to
256  * a fault.
257  *
258  * @ingroup kernel_memprotect_tests
259  *
260  * @see k_mem_domain_init(), k_mem_domain_add_partition(),
261  *	k_mem_domain_remove_partition(), k_mem_domain_add_thread()
262  */
ZTEST(mem_protect_domain,test_mem_domain_api_supervisor_only)263 ZTEST(mem_protect_domain, test_mem_domain_api_supervisor_only)
264 {
265 	/* All of these should fault when invoked from a user thread */
266 	spawn_child_thread(mem_domain_init_entry, NULL, true);
267 	spawn_child_thread(mem_domain_add_partition_entry, NULL, true);
268 	spawn_child_thread(mem_domain_remove_partition_entry, NULL, true);
269 	spawn_child_thread(mem_domain_add_thread_entry, NULL, true);
270 }
271 
272 /**
273  * @brief Show that boot threads belong to the default memory domain
274  *
275  * Static threads and the main thread are supposed to start as members of
276  * the default memory domain. Prove this is the case by examining the
277  * memory domain membership of z_main_thread and a static thread.
278  *
279  * @ingroup kernel_memprotect_tests
280  */
ZTEST(mem_protect_domain,test_mem_domain_boot_threads)281 ZTEST(mem_protect_domain, test_mem_domain_boot_threads)
282 {
283 	/* Check that a static thread got put in the default memory domain */
284 	zassert_true(zzz_thread->mem_domain_info.mem_domain ==
285 		     &k_mem_domain_default, "unexpected mem domain %p",
286 		     zzz_thread->mem_domain_info.mem_domain);
287 
288 	/* Check that the main thread is also a member of the default domain */
289 	zassert_true(z_main_thread.mem_domain_info.mem_domain ==
290 		     &k_mem_domain_default, "unexpected mem domain %p",
291 		     z_main_thread.mem_domain_info.mem_domain);
292 
293 	k_thread_abort(zzz_thread);
294 }
295 
296 static ZTEST_BMEM volatile bool spin_done;
297 static K_SEM_DEFINE(spin_sem, 0, 1);
298 
spin_entry(void * p1,void * p2,void * p3)299 static void spin_entry(void *p1, void *p2, void *p3)
300 {
301 	printk("spin thread entry\n");
302 	k_sem_give(&spin_sem);
303 
304 	while (!spin_done) {
305 		k_busy_wait(1);
306 	}
307 	printk("spin thread completed\n");
308 }
309 
310 /**
311  * @brief Show that moving a thread from one domain to another works
312  *
313  * Start a thread and have it spin. Then while it is spinning, show that
314  * adding it to another memory domain doesn't cause any faults.
315  *
316  * This test is of particular importance on SMP systems where the child
317  * thread is spinning on a different CPU concurrently with the migration
318  * operation.
319  *
320  * @ingroup kernel_memprotect_tests
321  *
322  * @see k_mem_domain_add_thread()
323  */
324 
325 #if CONFIG_MP_MAX_NUM_CPUS > 1
326 #define PRIO	K_PRIO_COOP(0)
327 #else
328 #define PRIO	K_PRIO_PREEMPT(1)
329 #endif
330 
ZTEST(mem_protect_domain,test_mem_domain_migration)331 ZTEST(mem_protect_domain, test_mem_domain_migration)
332 {
333 	int ret;
334 
335 	set_fault_valid(false);
336 
337 	k_thread_create(&child_thread, child_stack,
338 			K_THREAD_STACK_SIZEOF(child_stack), spin_entry,
339 			NULL, NULL, NULL,
340 			PRIO, K_USER | K_INHERIT_PERMS, K_FOREVER);
341 	k_thread_name_set(&child_thread, "child_thread");
342 	k_object_access_grant(&spin_sem, &child_thread);
343 	k_thread_start(&child_thread);
344 
345 	/* Ensure that the child thread has started */
346 	ret = k_sem_take(&spin_sem, K_FOREVER);
347 	zassert_equal(ret, 0, "k_sem_take failed");
348 
349 	/* Now move it to test_domain. This domain also has the ztest partition,
350 	 * so the child thread should keep running and not explode
351 	 */
352 	printk("migrate to new domain\n");
353 	k_mem_domain_add_thread(&test_domain, &child_thread);
354 
355 	/**TESTPOINT: add to existing domain will do nothing */
356 	k_mem_domain_add_thread(&test_domain, &child_thread);
357 
358 	/* set spin_done so the child thread completes */
359 	printk("set test completion\n");
360 	spin_done = true;
361 
362 	k_thread_join(&child_thread, K_FOREVER);
363 }
364 
365 /**
366  * @brief Test system assert when new partition overlaps the existing partition
367  *
368  * @details
369  * Test Objective:
370  * - Test assertion if the new partition overlaps existing partition in domain
371  *
372  * Testing techniques:
373  * - System testing
374  *
375  * Prerequisite Conditions:
376  * - N/A
377  *
378  * Input Specifications:
379  * - N/A
380  *
381  * Test Procedure:
382  * -# Define testing memory partition overlap_part with the same start ro_buf
383  *  as has the existing memory partition ro_part
384  * -# Try to add overlap_part to the memory domain. When adding the new
385  *  partition to the memory domain the system will assert that new partition
386  *  overlaps with the existing partition ro_part .
387  *
388  * Expected Test Result:
389  * - Must happen an assertion error indicating that the new partition overlaps
390  *   the existing one.
391  *
392  * Pass/Fail Criteria:
393  * - Success if the overlap assertion will happen.
394  * - Failure if the overlap assertion will not happen.
395  *
396  * Assumptions and Constraints:
397  * - N/A
398  *
399  * @ingroup kernel_memprotect_tests
400  *
401  * @see k_mem_domain_add_partition()
402  */
ZTEST(mem_protect_domain,test_mem_part_overlap)403 ZTEST(mem_protect_domain, test_mem_part_overlap)
404 {
405 	set_fault_valid(false);
406 
407 	zassert_not_equal(
408 		k_mem_domain_add_partition(&test_domain, &overlap_part),
409 		0, "should fail to add memory partition");
410 }
411 
412 extern struct k_spinlock z_mem_domain_lock;
413 
414 static struct k_mem_domain test_domain_fail;
415 
416 static volatile uint8_t __aligned(MEM_REGION_ALLOC)
417 	exceed_buf[MEM_REGION_ALLOC];
418 
419 K_MEM_PARTITION_DEFINE(exceed_part, exceed_buf, sizeof(exceed_buf),
420 		      K_MEM_PARTITION_P_RW_U_RW);
421 
422 /**
423  * @brief Test system assert when adding memory partitions more than possible
424  *
425  * @details
426  * - Add memory partitions one by one and more than architecture allows to add.
427  * - When partitions added more than it is allowed by architecture, test that
428  *   k_mem_domain_add_partition() returns non-zero.
429  *
430  * @ingroup kernel_memprotect_tests
431  */
ZTEST(mem_protect_domain,test_mem_part_assert_add_overmax)432 ZTEST(mem_protect_domain, test_mem_part_assert_add_overmax)
433 {
434 	int max_parts = num_rw_parts + PARTS_USED;
435 
436 	/* Make sure the partitions of the domain is full, used in
437 	 * previous test cases.
438 	 */
439 	zassert_equal(max_parts, arch_mem_domain_max_partitions_get(),
440 			"domain still have room of partitions(%d).",
441 			max_parts);
442 
443 	set_fault_valid(false);
444 
445 	/* Add one more partition will fail due to exceeding */
446 	zassert_not_equal(
447 		k_mem_domain_add_partition(&test_domain, &exceed_part),
448 		0, "should fail to add memory partition");
449 }
450 
451 
452 #if defined(CONFIG_ASSERT)
453 static volatile uint8_t __aligned(MEM_REGION_ALLOC) misc_buf[MEM_REGION_ALLOC];
454 K_MEM_PARTITION_DEFINE(find_no_part, misc_buf, sizeof(misc_buf),
455 		       K_MEM_PARTITION_P_RO_U_RO);
456 
457 /**
458  * @brief Test error case of removing memory partition fail
459  *
460  * @details Try to remove a partition not in the domain.
461  * k_mem_domain_remove_partition() should return non-zero.
462  *
463  * @ingroup kernel_memprotect_tests
464  */
ZTEST(mem_protect_domain,test_mem_domain_remove_part_fail)465 ZTEST(mem_protect_domain, test_mem_domain_remove_part_fail)
466 {
467 	struct k_mem_partition *no_parts = &find_no_part;
468 
469 	set_fault_valid(false);
470 
471 	zassert_not_equal(
472 		k_mem_domain_remove_partition(&test_domain, no_parts),
473 		0, "should fail to remove memory partition");
474 }
475 #else
ZTEST(mem_protect_domain,test_mem_domain_remove_part_fail)476 ZTEST(mem_protect_domain, test_mem_domain_remove_part_fail)
477 {
478 	ztest_test_skip();
479 }
480 #endif
481 
482 /**
483  * @brief Test error case of initializing memory domain fail
484  *
485  * @details Try to initialize a domain with invalid partition.
486  * k_mem_domain_init() should return non-zero.
487  *
488  * @ingroup kernel_memprotect_tests
489  */
ZTEST(mem_protect_domain,test_mem_domain_init_fail)490 ZTEST(mem_protect_domain, test_mem_domain_init_fail)
491 {
492 	struct k_mem_partition *no_parts[] = {&ro_part, 0};
493 
494 	/* init another domain fail */
495 	set_fault_valid(false);
496 
497 	zassert_not_equal(
498 		k_mem_domain_init(&test_domain_fail, ARRAY_SIZE(no_parts),
499 				  no_parts),
500 		0, "should fail to initialize memory domain");
501 }
502 
503 /**
504  * @brief Test error case of adding null memory partition fail
505  *
506  * @details Try to add a null partition to memory domain.
507  * k_mem_domain_add_partition() should return error.
508  *
509  * @ingroup kernel_memprotect_tests
510  */
ZTEST(mem_protect_domain,test_mem_part_add_error_null)511 ZTEST(mem_protect_domain, test_mem_part_add_error_null)
512 {
513 	/* add partition fail */
514 	set_fault_valid(false);
515 
516 	zassert_not_equal(
517 		k_mem_domain_add_partition(&test_domain_fail, NULL),
518 		0, "should fail to add memory partition");
519 }
520 
521 static volatile uint8_t __aligned(MEM_REGION_ALLOC) nosize_buf[MEM_REGION_ALLOC];
522 K_MEM_PARTITION_DEFINE(nonsize_part, nosize_buf, sizeof(nosize_buf),
523 			K_MEM_PARTITION_P_RO_U_RO);
524 
525 /**
526  * @brief Test error case of adding zero sized memory partition fail
527  *
528  * @details Try to add a zero sized partition to memory domain.
529  * k_mem_domain_add_partition() should return error.
530  *
531  * @ingroup kernel_memprotect_tests
532  */
ZTEST(mem_protect_domain,test_mem_part_add_error_zerosize)533 ZTEST(mem_protect_domain, test_mem_part_add_error_zerosize)
534 {
535 	struct k_mem_partition *nosize_part = &nonsize_part;
536 
537 	nosize_part->size = 0U;
538 
539 	/* add partition fail */
540 	set_fault_valid(false);
541 
542 	zassert_not_equal(
543 		k_mem_domain_add_partition(&test_domain_fail, nosize_part),
544 		0, "should fail to add memory partition");
545 }
546 
547 /**
548  * @brief Test error case of memory partition address wraparound
549  *
550  * @details Try to add a partition whose address is wraparound.
551  * k_mem_domain_add_partition() should return error.
552  *
553  * @ingroup kernel_memprotect_tests
554  */
ZTEST(mem_protect_domain,test_mem_part_error_wraparound)555 ZTEST(mem_protect_domain, test_mem_part_error_wraparound)
556 {
557 #ifdef CONFIG_64BIT
558 	K_MEM_PARTITION_DEFINE(wraparound_part, 0xfffffffffffff800, 2048,
559 		       K_MEM_PARTITION_P_RO_U_RO);
560 #else
561 	K_MEM_PARTITION_DEFINE(wraparound_part, 0xfffff800, 2048,
562 		       K_MEM_PARTITION_P_RO_U_RO);
563 #endif
564 
565 	/* add partition fail */
566 	set_fault_valid(false);
567 
568 	zassert_not_equal(
569 		k_mem_domain_add_partition(&test_domain_fail, &wraparound_part),
570 		0, "should fail to add memory partition");
571 }
572 
573 /**
574  * @brief Test error case of removing memory partition fail
575  *
576  * @details Try to remove a partition size mismatched will result
577  * in k_mem_domain_remove_partition() returning error.
578  *
579  * @ingroup kernel_memprotect_tests
580  */
ZTEST(mem_protect_domain,test_mem_part_remove_error_zerosize)581 ZTEST(mem_protect_domain, test_mem_part_remove_error_zerosize)
582 {
583 	struct k_mem_partition *no_parts = &find_no_part;
584 
585 	zassert_equal(
586 		k_mem_domain_remove_partition(&test_domain, &rw_parts[0]),
587 		0, "failed to remove memory partition");
588 
589 	zassert_equal(
590 		k_mem_domain_add_partition(&test_domain, no_parts),
591 		0, "failed to add memory partition");
592 
593 	no_parts->size = 0U;
594 
595 	/* remove partition fail */
596 	set_fault_valid(false);
597 
598 	zassert_not_equal(
599 		k_mem_domain_remove_partition(&test_domain, no_parts),
600 		0, "should fail to remove memory partition");
601 }
602 
603 /* setup function */
mem_domain_setup(void)604 void *mem_domain_setup(void)
605 {
606 	test_mem_domain_setup();
607 
608 	return NULL;
609 }
610 
611 ZTEST_SUITE(mem_protect_domain, NULL, mem_domain_setup, NULL,
612 		NULL, NULL);
613