| /* | 
 |  * Postboot kernel tests: Tests to be ran after boot in kernel mode. | 
 |  * TODO: Some of the tests here may not necessarily be tests to be ran after | 
 |  *       boot. If that is the case, change them in | 
 |  */ | 
 |  | 
 | #include <arch/mmu.h> | 
 | #include <arch/arch.h> | 
 | #include <arch/uaccess.h> | 
 | #include <bitmask.h> | 
 | #include <smp.h> | 
 |  | 
 | #include <ros/memlayout.h> | 
 | #include <ros/common.h> | 
 | #include <ros/bcq.h> | 
 | #include <ros/ucq.h> | 
 |  | 
 | #include <atomic.h> | 
 | #include <stdio.h> | 
 | #include <assert.h> | 
 | #include <string.h> | 
 | #include <testing.h> | 
 | #include <trap.h> | 
 | #include <process.h> | 
 | #include <syscall.h> | 
 | #include <time.h> | 
 | #include <mm.h> | 
 | #include <multiboot.h> | 
 | #include <pmap.h> | 
 | #include <page_alloc.h> | 
 | #include <pmap.h> | 
 | #include <slab.h> | 
 | #include <kmalloc.h> | 
 | #include <hashtable.h> | 
 | #include <circular_buffer.h> | 
 | #include <monitor.h> | 
 | #include <kthread.h> | 
 | #include <schedule.h> | 
 | #include <umem.h> | 
 | #include <init.h> | 
 | #include <ucq.h> | 
 | #include <setjmp.h> | 
 | #include <sort.h> | 
 |  | 
 | #include <apipe.h> | 
 | #include <rwlock.h> | 
 | #include <rendez.h> | 
 | #include <ktest.h> | 
 | #include <smallidpool.h> | 
 | #include <linker_func.h> | 
 |  | 
 | KTEST_SUITE("POSTBOOT") | 
 |  | 
 | #ifdef CONFIG_X86 | 
 |  | 
 | // TODO: Do test if possible inside this function, and add assertions. | 
 | bool test_ipi_sending(void) | 
 | { | 
 | 	int8_t state = 0; | 
 |  | 
 | 	register_irq(I_TESTING, test_hello_world_handler, NULL, | 
 | 	             MKBUS(BusIPI, 0, 0, 0)); | 
 | 	enable_irqsave(&state); | 
 | 	cprintf("\nCORE 0 sending broadcast\n"); | 
 | 	send_broadcast_ipi(I_TESTING); | 
 | 	udelay(3000000); | 
 | 	cprintf("\nCORE 0 sending all others\n"); | 
 | 	send_all_others_ipi(I_TESTING); | 
 | 	udelay(3000000); | 
 | 	cprintf("\nCORE 0 sending self\n"); | 
 | 	send_self_ipi(I_TESTING); | 
 | 	udelay(3000000); | 
 | 	cprintf("\nCORE 0 sending ipi to physical 1\n"); | 
 | 	send_ipi(0x01, I_TESTING); | 
 | 	udelay(3000000); | 
 | 	cprintf("\nCORE 0 sending ipi to physical 2\n"); | 
 | 	send_ipi(0x02, I_TESTING); | 
 | 	udelay(3000000); | 
 | 	cprintf("\nCORE 0 sending ipi to physical 3\n"); | 
 | 	send_ipi(0x03, I_TESTING); | 
 | 	udelay(3000000); | 
 | 	cprintf("\nCORE 0 sending ipi to physical 15\n"); | 
 | 	send_ipi(0x0f, I_TESTING); | 
 | 	udelay(3000000); | 
 | 	cprintf("\nCORE 0 sending ipi to logical 2\n"); | 
 | 	send_group_ipi(0x02, I_TESTING); | 
 | 	udelay(3000000); | 
 | 	cprintf("\nCORE 0 sending ipi to logical 1\n"); | 
 | 	send_group_ipi(0x01, I_TESTING); | 
 | 	udelay(3000000); | 
 | 	cprintf("\nDone!\n"); | 
 | 	disable_irqsave(&state); | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | // TODO: Refactor to make it return and add assertions. | 
 | // Note this never returns and will muck with any other timer work | 
 | bool test_pic_reception(void) | 
 | { | 
 | 	register_irq(IdtPIC + IrqCLOCK, test_hello_world_handler, NULL, | 
 | 	             MKBUS(BusISA, 0, 0, 0)); | 
 | 	pit_set_timer(100,TIMER_RATEGEN); // totally arbitrary time | 
 | 	pic_unmask_irq(0, 0); | 
 | 	cprintf("PIC1 Mask = 0x%04x\n", inb(PIC1_DATA)); | 
 | 	cprintf("PIC2 Mask = 0x%04x\n", inb(PIC2_DATA)); | 
 | 	unmask_lapic_lvt(MSR_LAPIC_LVT_LINT0); | 
 | 	printk("Core %d's LINT0: 0x%08x\n", core_id(), | 
 | 	       apicrget(MSR_LAPIC_LVT_TIMER)); | 
 | 	enable_irq(); | 
 | 	while(1); | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | #endif // CONFIG_X86 | 
 |  | 
 | barrier_t test_cpu_array; | 
 |  | 
 | // TODO: Add assertions, try to do everything from within this same function. | 
 | bool test_barrier(void) | 
 | { | 
 | 	cprintf("Core 0 initializing barrier\n"); | 
 | 	init_barrier(&test_cpu_array, num_cores); | 
 | 	printk("Core 0 asking all cores to print ids, barrier, etc\n"); | 
 | 	smp_call_function_all(test_barrier_handler, NULL, 0); | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | // TODO: Maybe remove all the printing statements and instead use the | 
 | //       KT_ASSERT_M macro to include a message on assertions. | 
 | bool test_interrupts_irqsave(void) | 
 | { | 
 | 	int8_t state = 0; | 
 |  | 
 | 	printd("Testing Nesting Enabling first, turning ints off:\n"); | 
 | 	disable_irq(); | 
 | 	printd("Interrupts are: %x\n", irq_is_enabled()); | 
 | 	KT_ASSERT(!irq_is_enabled()); | 
 | 	printd("Enabling IRQSave\n"); | 
 | 	enable_irqsave(&state); | 
 | 	printd("Interrupts are: %x\n", irq_is_enabled()); | 
 | 	KT_ASSERT(irq_is_enabled()); | 
 | 	printd("Enabling IRQSave Again\n"); | 
 | 	enable_irqsave(&state); | 
 | 	printd("Interrupts are: %x\n", irq_is_enabled()); | 
 | 	KT_ASSERT(irq_is_enabled()); | 
 | 	printd("Disabling IRQSave Once\n"); | 
 | 	disable_irqsave(&state); | 
 | 	printd("Interrupts are: %x\n", irq_is_enabled()); | 
 | 	KT_ASSERT(irq_is_enabled()); | 
 | 	printd("Disabling IRQSave Again\n"); | 
 | 	disable_irqsave(&state); | 
 | 	printd("Interrupts are: %x\n", irq_is_enabled()); | 
 | 	KT_ASSERT(!irq_is_enabled()); | 
 | 	printd("Done.  Should have been 0, 200, 200, 200, 0\n"); | 
 |  | 
 | 	printd("Testing Nesting Disabling first, turning ints on:\n"); | 
 | 	state = 0; | 
 | 	enable_irq(); | 
 | 	printd("Interrupts are: %x\n", irq_is_enabled()); | 
 | 	KT_ASSERT(irq_is_enabled()); | 
 | 	printd("Disabling IRQSave Once\n"); | 
 | 	disable_irqsave(&state); | 
 | 	printd("Interrupts are: %x\n", irq_is_enabled()); | 
 | 	KT_ASSERT(!irq_is_enabled()); | 
 | 	printd("Disabling IRQSave Again\n"); | 
 | 	disable_irqsave(&state); | 
 | 	printd("Interrupts are: %x\n", irq_is_enabled()); | 
 | 	KT_ASSERT(!irq_is_enabled()); | 
 | 	printd("Enabling IRQSave Once\n"); | 
 | 	enable_irqsave(&state); | 
 | 	printd("Interrupts are: %x\n", irq_is_enabled()); | 
 | 	KT_ASSERT(!irq_is_enabled()); | 
 | 	printd("Enabling IRQSave Again\n"); | 
 | 	enable_irqsave(&state); | 
 | 	printd("Interrupts are: %x\n", irq_is_enabled()); | 
 | 	KT_ASSERT(irq_is_enabled()); | 
 | 	printd("Done.  Should have been 200, 0, 0, 0, 200 \n"); | 
 |  | 
 | 	state = 0; | 
 | 	disable_irq(); | 
 | 	printd("Ints are off, enabling then disabling.\n"); | 
 | 	enable_irqsave(&state); | 
 | 	printd("Interrupts are: %x\n", irq_is_enabled()); | 
 | 	KT_ASSERT(irq_is_enabled()); | 
 | 	disable_irqsave(&state); | 
 | 	printd("Interrupts are: %x\n", irq_is_enabled()); | 
 | 	KT_ASSERT(!irq_is_enabled()); | 
 | 	printd("Done.  Should have been 200, 0\n"); | 
 |  | 
 | 	state = 0; | 
 | 	enable_irq(); | 
 | 	printd("Ints are on, enabling then disabling.\n"); | 
 | 	enable_irqsave(&state); | 
 | 	printd("Interrupts are: %x\n", irq_is_enabled()); | 
 | 	KT_ASSERT(irq_is_enabled()); | 
 | 	disable_irqsave(&state); | 
 | 	printd("Interrupts are: %x\n", irq_is_enabled()); | 
 | 	KT_ASSERT(irq_is_enabled()); | 
 | 	printd("Done.  Should have been 200, 200\n"); | 
 |  | 
 | 	state = 0; | 
 | 	disable_irq(); | 
 | 	printd("Ints are off, disabling then enabling.\n"); | 
 | 	disable_irqsave(&state); | 
 | 	printd("Interrupts are: %x\n", irq_is_enabled()); | 
 | 	KT_ASSERT(!irq_is_enabled()); | 
 | 	enable_irqsave(&state); | 
 | 	printd("Interrupts are: %x\n", irq_is_enabled()); | 
 | 	KT_ASSERT(!irq_is_enabled()); | 
 | 	printd("Done.  Should have been 0, 0\n"); | 
 |  | 
 | 	state = 0; | 
 | 	enable_irq(); | 
 | 	printd("Ints are on, disabling then enabling.\n"); | 
 | 	disable_irqsave(&state); | 
 | 	printd("Interrupts are: %x\n", irq_is_enabled()); | 
 | 	KT_ASSERT(!irq_is_enabled()); | 
 | 	enable_irqsave(&state); | 
 | 	printd("Interrupts are: %x\n", irq_is_enabled()); | 
 | 	KT_ASSERT(irq_is_enabled()); | 
 | 	printd("Done.  Should have been 0, 200\n"); | 
 |  | 
 | 	disable_irq(); | 
 | 	return true; | 
 | } | 
 |  | 
 | // TODO: Maybe remove PRINT_BITMASK statements and use KT_ASSERT_M instead | 
 | //       somehow. | 
 | bool test_bitmasks(void) | 
 | { | 
 | #define masksize 67 | 
 | 	DECL_BITMASK(mask, masksize); | 
 | 	CLR_BITMASK(mask, masksize); | 
 | //	PRINT_BITMASK(mask, masksize); | 
 | 	SET_BITMASK_BIT(mask, 0); | 
 | 	SET_BITMASK_BIT(mask, 11); | 
 | 	SET_BITMASK_BIT(mask, 17); | 
 | 	SET_BITMASK_BIT(mask, masksize-1); | 
 | //	PRINT_BITMASK(mask, masksize); | 
 | 	DECL_BITMASK(mask2, masksize); | 
 | 	COPY_BITMASK(mask2, mask, masksize); | 
 | //	printk("copy of original mask, should be the same as the prev\n"); | 
 | //	PRINT_BITMASK(mask2, masksize); | 
 | 	CLR_BITMASK_BIT(mask, 11); | 
 | //	PRINT_BITMASK(mask, masksize); | 
 | 	KT_ASSERT_M("Bit 17 should be 1", 1 == GET_BITMASK_BIT(mask, 17)); | 
 | 	KT_ASSERT_M("Bit 11 should be 0", 0 == GET_BITMASK_BIT(mask, 11)); | 
 | 	FILL_BITMASK(mask, masksize); | 
 | //	PRINT_BITMASK(mask, masksize); | 
 | 	KT_ASSERT_M("Bitmask should not be clear after calling FILL_BITMASK", | 
 | 	            0 == BITMASK_IS_CLEAR(mask,masksize)); | 
 | 	CLR_BITMASK(mask, masksize); | 
 | //	PRINT_BITMASK(mask, masksize); | 
 | 	KT_ASSERT_M("Bitmask should be clear after calling CLR_BITMASK", | 
 | 	            1 == BITMASK_IS_CLEAR(mask,masksize)); | 
 | 	return true; | 
 | } | 
 |  | 
 | checklist_t *the_global_list; | 
 |  | 
 | static void test_checklist_handler(struct hw_trapframe *hw_tf, void *data) | 
 | { | 
 | 	udelay(1000000); | 
 | 	cprintf("down_checklist(%x,%d)\n", the_global_list, core_id()); | 
 | 	down_checklist(the_global_list); | 
 | } | 
 |  | 
 | // TODO: Add assertions | 
 | bool test_checklists(void) | 
 | { | 
 | 	INIT_CHECKLIST(a_list, MAX_NUM_CORES); | 
 | 	the_global_list = &a_list; | 
 | 	printk("Checklist Build, mask size: %d\n", sizeof(a_list.mask.bits)); | 
 | 	printk("mask\n"); | 
 | 	PRINT_BITMASK(a_list.mask.bits, a_list.mask.size); | 
 | 	SET_BITMASK_BIT(a_list.mask.bits, 11); | 
 | 	printk("Set bit 11\n"); | 
 | 	PRINT_BITMASK(a_list.mask.bits, a_list.mask.size); | 
 |  | 
 | 	CLR_BITMASK(a_list.mask.bits, a_list.mask.size); | 
 | 	INIT_CHECKLIST_MASK(a_mask, MAX_NUM_CORES); | 
 | 	FILL_BITMASK(a_mask.bits, num_cores); | 
 | 	//CLR_BITMASK_BIT(a_mask.bits, core_id()); | 
 | 	//SET_BITMASK_BIT(a_mask.bits, 1); | 
 | 	//printk("New mask (1, 17, 25):\n"); | 
 | 	printk("Created new mask, filled up to num_cores\n"); | 
 | 	PRINT_BITMASK(a_mask.bits, a_mask.size); | 
 | 	printk("committing new mask\n"); | 
 | 	commit_checklist_wait(&a_list, &a_mask); | 
 | 	printk("Old mask (copied onto):\n"); | 
 | 	PRINT_BITMASK(a_list.mask.bits, a_list.mask.size); | 
 | 	//smp_call_function_single(1, test_checklist_handler, 0, 0); | 
 |  | 
 | 	smp_call_function_all(test_checklist_handler, NULL, 0); | 
 |  | 
 | 	printk("Waiting on checklist\n"); | 
 | 	waiton_checklist(&a_list); | 
 | 	printk("Done Waiting!\n"); | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | atomic_t a, b, c; | 
 |  | 
 | static void test_incrementer_handler(struct hw_trapframe *tf, void *data) | 
 | { | 
 | 	assert(data); | 
 | 	atomic_inc(data); | 
 | } | 
 |  | 
 | static void test_null_handler(struct hw_trapframe *tf, void *data) | 
 | { | 
 | 	asm volatile("nop"); | 
 | } | 
 |  | 
 | // TODO: Add assertions. | 
 | bool test_smp_call_functions(void) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	atomic_init(&a, 0); | 
 | 	atomic_init(&b, 0); | 
 | 	atomic_init(&c, 0); | 
 | 	handler_wrapper_t *waiter0 = 0, *waiter1 = 0, *waiter2 = 0, | 
 | 			  *waiter3 = 0, *waiter4 = 0, *waiter5 = 0; | 
 | 	uint8_t me = core_id(); | 
 |  | 
 | 	printk("\nCore %d: SMP Call Self (nowait):\n", me); | 
 | 	printk("---------------------\n"); | 
 | 	smp_call_function_self(test_hello_world_handler, NULL, 0); | 
 | 	printk("\nCore %d: SMP Call Self (wait):\n", me); | 
 | 	printk("---------------------\n"); | 
 | 	smp_call_function_self(test_hello_world_handler, NULL, &waiter0); | 
 | 	smp_call_wait(waiter0); | 
 | 	printk("\nCore %d: SMP Call All (nowait):\n", me); | 
 | 	printk("---------------------\n"); | 
 | 	smp_call_function_all(test_hello_world_handler, NULL, 0); | 
 | 	printk("\nCore %d: SMP Call All (wait):\n", me); | 
 | 	printk("---------------------\n"); | 
 | 	smp_call_function_all(test_hello_world_handler, NULL, &waiter0); | 
 | 	smp_call_wait(waiter0); | 
 | 	printk("\nCore %d: SMP Call All-Else Individually, in order (nowait):\n", | 
 | 	       me); | 
 | 	printk("---------------------\n"); | 
 | 	for(i = 1; i < num_cores; i++) | 
 | 		smp_call_function_single(i, test_hello_world_handler, NULL, 0); | 
 | 	printk("\nCore %d: SMP Call Self (wait):\n", me); | 
 | 	printk("---------------------\n"); | 
 | 	smp_call_function_self(test_hello_world_handler, NULL, &waiter0); | 
 | 	smp_call_wait(waiter0); | 
 | 	printk("\nCore %d: SMP Call All-Else Individually, in order (wait):\n", | 
 | 	       me); | 
 | 	printk("---------------------\n"); | 
 | 	for(i = 1; i < num_cores; i++) | 
 | 	{ | 
 | 		smp_call_function_single(i, test_hello_world_handler, NULL, | 
 | 					 &waiter0); | 
 | 		smp_call_wait(waiter0); | 
 | 	} | 
 | 	printk("\nTesting to see if any IPI-functions are dropped when not waiting:\n"); | 
 | 	printk("A: %d, B: %d, C: %d (should be 0,0,0)\n", atomic_read(&a), | 
 | 	       atomic_read(&b), atomic_read(&c)); | 
 | 	smp_call_function_all(test_incrementer_handler, &a, 0); | 
 | 	smp_call_function_all(test_incrementer_handler, &b, 0); | 
 | 	smp_call_function_all(test_incrementer_handler, &c, 0); | 
 | 	// if i can clobber a previous IPI, the interleaving might do it | 
 | 	smp_call_function_single(1 % num_cores, test_incrementer_handler, &a, | 
 | 				 0); | 
 | 	smp_call_function_single(2 % num_cores, test_incrementer_handler, &b, | 
 | 				 0); | 
 | 	smp_call_function_single(3 % num_cores, test_incrementer_handler, &c, | 
 | 				 0); | 
 | 	smp_call_function_single(4 % num_cores, test_incrementer_handler, &a, | 
 | 				 0); | 
 | 	smp_call_function_single(5 % num_cores, test_incrementer_handler, &b, | 
 | 				 0); | 
 | 	smp_call_function_single(6 % num_cores, test_incrementer_handler, &c, | 
 | 				 0); | 
 | 	smp_call_function_all(test_incrementer_handler, &a, 0); | 
 | 	smp_call_function_single(3 % num_cores, test_incrementer_handler, &c, | 
 | 				 0); | 
 | 	smp_call_function_all(test_incrementer_handler, &b, 0); | 
 | 	smp_call_function_single(1 % num_cores, test_incrementer_handler, &a, | 
 | 				 0); | 
 | 	smp_call_function_all(test_incrementer_handler, &c, 0); | 
 | 	smp_call_function_single(2 % num_cores, test_incrementer_handler, &b, | 
 | 				 0); | 
 | 	// wait, so we're sure the others finish before printing. | 
 | 	// without this, we could (and did) get 19,18,19, since the B_inc | 
 | 	// handler didn't finish yet | 
 | 	smp_call_function_self(test_null_handler, NULL, &waiter0); | 
 | 	// need to grab all 5 handlers (max), since the code moves to the next | 
 | 	// free. | 
 | 	smp_call_function_self(test_null_handler, NULL, &waiter1); | 
 | 	smp_call_function_self(test_null_handler, NULL, &waiter2); | 
 | 	smp_call_function_self(test_null_handler, NULL, &waiter3); | 
 | 	smp_call_function_self(test_null_handler, NULL, &waiter4); | 
 | 	smp_call_wait(waiter0); | 
 | 	smp_call_wait(waiter1); | 
 | 	smp_call_wait(waiter2); | 
 | 	smp_call_wait(waiter3); | 
 | 	smp_call_wait(waiter4); | 
 | 	printk("A: %d, B: %d, C: %d (should be 19,19,19)\n", atomic_read(&a), | 
 | 	       atomic_read(&b), atomic_read(&c)); | 
 | 	printk("Attempting to deadlock by smp_calling with an outstanding wait:\n"); | 
 | 	smp_call_function_self(test_null_handler, NULL, &waiter0); | 
 | 	printk("Sent one\n"); | 
 | 	smp_call_function_self(test_null_handler, NULL, &waiter1); | 
 | 	printk("Sent two\n"); | 
 | 	smp_call_wait(waiter0); | 
 | 	printk("Wait one\n"); | 
 | 	smp_call_wait(waiter1); | 
 | 	printk("Wait two\n"); | 
 | 	printk("\tMade it through!\n"); | 
 | 	printk("Attempting to deadlock by smp_calling more than are available:\n"); | 
 | 	printk("\tShould see an Insufficient message and a kernel warning.\n"); | 
 | 	if (smp_call_function_self(test_null_handler, NULL, &waiter0)) | 
 | 		printk("\tInsufficient handlers to call function (0)\n"); | 
 | 	if (smp_call_function_self(test_null_handler, NULL, &waiter1)) | 
 | 		printk("\tInsufficient handlers to call function (1)\n"); | 
 | 	if (smp_call_function_self(test_null_handler, NULL, &waiter2)) | 
 | 		printk("\tInsufficient handlers to call function (2)\n"); | 
 | 	if (smp_call_function_self(test_null_handler, NULL, &waiter3)) | 
 | 		printk("\tInsufficient handlers to call function (3)\n"); | 
 | 	if (smp_call_function_self(test_null_handler, NULL, &waiter4)) | 
 | 		printk("\tInsufficient handlers to call function (4)\n"); | 
 | 	if (smp_call_function_self(test_null_handler, NULL, &waiter5)) | 
 | 		printk("\tInsufficient handlers to call function (5)\n"); | 
 | 	smp_call_wait(waiter0); | 
 | 	smp_call_wait(waiter1); | 
 | 	smp_call_wait(waiter2); | 
 | 	smp_call_wait(waiter3); | 
 | 	smp_call_wait(waiter4); | 
 | 	smp_call_wait(waiter5); | 
 | 	printk("\tMade it through!\n"); | 
 |  | 
 | 	printk("Done\n"); | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | #ifdef CONFIG_X86 | 
 | // TODO: Fix the KT_ASSERTs | 
 | bool test_lapic_status_bit(void) | 
 | { | 
 | 	register_irq(I_TESTING, test_incrementer_handler, &a, | 
 | 	             MKBUS(BusIPI, 0, 0, 0)); | 
 | 	#define NUM_IPI 100000 | 
 | 	atomic_set(&a,0); | 
 | 	printk("IPIs received (should be 0): %d\n", a); | 
 | 	// KT_ASSERT_M("IPIs received should be 0", (0 == a)); | 
 | 	for(int i = 0; i < NUM_IPI; i++) { | 
 | 		send_ipi(7, I_TESTING); | 
 | 	} | 
 | 	// need to wait a bit to let those IPIs get there | 
 | 	udelay(5000000); | 
 | 	printk("IPIs received (should be %d): %d\n", a, NUM_IPI); | 
 | 	// KT_ASSERT_M("IPIs received should be 100000", (NUM_IPI == a)); | 
 | 	// hopefully that handler never fires again.  leaving it registered for | 
 | 	// now. | 
 |  | 
 | 	return true; | 
 | } | 
 | #endif // CONFIG_X86 | 
 |  | 
 | /************************************************************/ | 
 | /* ISR Handler Functions */ | 
 |  | 
 | void test_hello_world_handler(struct hw_trapframe *hw_tf, void *data) | 
 | { | 
 | 	int trapno; | 
 | 	#if defined(CONFIG_X86) | 
 | 	trapno = hw_tf->tf_trapno; | 
 | 	#else | 
 | 	trapno = 0; | 
 | 	#endif | 
 |  | 
 | 	cprintf("Incoming IRQ, ISR: %d on core %d with tf at %p\n", | 
 | 	        trapno, core_id(), hw_tf); | 
 | } | 
 |  | 
 | void test_barrier_handler(struct hw_trapframe *hw_tf, void *data) | 
 | { | 
 | 	cprintf("Round 1: Core %d\n", core_id()); | 
 | 	waiton_barrier(&test_cpu_array); | 
 | 	waiton_barrier(&test_cpu_array); | 
 | 	waiton_barrier(&test_cpu_array); | 
 | 	waiton_barrier(&test_cpu_array); | 
 | 	waiton_barrier(&test_cpu_array); | 
 | 	waiton_barrier(&test_cpu_array); | 
 | 	cprintf("Round 2: Core %d\n", core_id()); | 
 | 	waiton_barrier(&test_cpu_array); | 
 | 	cprintf("Round 3: Core %d\n", core_id()); | 
 | 	// uncomment to see it fucked up | 
 | 	//cprintf("Round 4: Core %d\n", core_id()); | 
 | } | 
 |  | 
 | static void test_waiting_handler(struct hw_trapframe *hw_tf, void *data) | 
 | { | 
 | 	atomic_dec(data); | 
 | } | 
 |  | 
 | #ifdef CONFIG_X86 | 
 | // TODO: Add assertions. | 
 | bool test_pit(void) | 
 | { | 
 | 	cprintf("Starting test for PIT now (10s)\n"); | 
 | 	udelay_pit(10000000); | 
 | 	cprintf("End now\n"); | 
 | 	cprintf("Starting test for TSC (if stable) now (10s)\n"); | 
 | 	udelay(10000000); | 
 | 	cprintf("End now\n"); | 
 |  | 
 | 	cprintf("Starting test for LAPIC (if stable) now (10s)\n"); | 
 | 	enable_irq(); | 
 | 	lapic_set_timer(10000000, FALSE); | 
 |  | 
 | 	atomic_t waiting; | 
 | 	atomic_init(&waiting, 1); | 
 | 	register_irq(I_TESTING, test_waiting_handler, &waiting, | 
 | 	             MKBUS(BusIPI, 0, 0, 0)); | 
 | 	while(atomic_read(&waiting)) | 
 | 		cpu_relax(); | 
 | 	cprintf("End now\n"); | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | // TODO: Add assertions. | 
 | bool test_circ_buffer(void) | 
 | { | 
 | 	int arr[5] = {0, 1, 2, 3, 4}; | 
 |  | 
 | 	for (int i = 0; i < 5; i++) { | 
 | 		FOR_CIRC_BUFFER(i, 5, j) | 
 | 			printk("Starting with current = %d, each value = %d\n", | 
 | 			       i, j); | 
 | 	} | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | static void test_km_handler(uint32_t srcid, long a0, long a1, long a2) | 
 | { | 
 | 	printk("Received KM on core %d from core %d: arg0= %p, arg1 = %p, " | 
 | 	       "arg2 = %p\n", core_id(), srcid, a0, a1, a2); | 
 | 	return; | 
 | } | 
 |  | 
 | // TODO: Add assertions. Try to do everything inside this function. | 
 | bool test_kernel_messages(void) | 
 | { | 
 | 	printk("Testing Kernel Messages\n"); | 
 | 	/* Testing sending multiples, sending different types, alternating, and | 
 | 	 * precendence (the immediates should trump the others) */ | 
 | 	printk("sending 5 IMMED to core 1, sending (#,deadbeef,0)\n"); | 
 | 	for (int i = 0; i < 5; i++) | 
 | 		send_kernel_message(1, test_km_handler, (long)i, 0xdeadbeef, 0, | 
 | 		                    KMSG_IMMEDIATE); | 
 | 	udelay(5000000); | 
 | 	printk("sending 5 routine to core 1, sending (#,cafebabe,0)\n"); | 
 | 	for (int i = 0; i < 5; i++) | 
 | 		send_kernel_message(1, test_km_handler, (long)i, 0xcafebabe, 0, | 
 | 		                    KMSG_ROUTINE); | 
 | 	udelay(5000000); | 
 | 	printk("sending 10 routine and 3 immediate to core 2\n"); | 
 | 	for (int i = 0; i < 10; i++) | 
 | 		send_kernel_message(2, test_km_handler, (long)i, 0xcafebabe, 0, | 
 | 		                    KMSG_ROUTINE); | 
 | 	for (int i = 0; i < 3; i++) | 
 | 		send_kernel_message(2, test_km_handler, (long)i, 0xdeadbeef, 0, | 
 | 		                    KMSG_IMMEDIATE); | 
 | 	udelay(5000000); | 
 | 	printk("sending 5 ea alternating to core 2\n"); | 
 | 	for (int i = 0; i < 5; i++) { | 
 | 		send_kernel_message(2, test_km_handler, (long)i, 0xdeadbeef, 0, | 
 | 		                    KMSG_IMMEDIATE); | 
 | 		send_kernel_message(2, test_km_handler, (long)i, 0xcafebabe, 0, | 
 | 		                    KMSG_ROUTINE); | 
 | 	} | 
 | 	udelay(5000000); | 
 |  | 
 | 	return true; | 
 | } | 
 | #endif // CONFIG_X86 | 
 |  | 
 | static size_t test_hash_fn_col(void *k) | 
 | { | 
 | 	return (size_t)k % 2; // collisions in slots 0 and 1 | 
 | } | 
 |  | 
 | bool test_hashtable(void) | 
 | { | 
 | 	struct test {int x; int y;}; | 
 | 	struct test tstruct[10]; | 
 |  | 
 | 	struct hashtable *h; | 
 | 	uintptr_t k = 5; | 
 | 	struct test *v = &tstruct[0]; | 
 |  | 
 | 	h = create_hashtable(32, __generic_hash, __generic_eq); | 
 |  | 
 | 	// test inserting one item, then finding it again | 
 | 	KT_ASSERT_M("It should be possible to insert items to a hashtable", | 
 | 	            hashtable_insert(h, (void*)k, v)); | 
 | 	v = NULL; | 
 | 	KT_ASSERT_M("should be possible to find inserted stuff in a hashtable", | 
 | 	            (v = hashtable_search(h, (void*)k))); | 
 |  | 
 | 	KT_ASSERT_M("The extracted element should be the same we inserted", | 
 | 	            (v == &tstruct[0])); | 
 |  | 
 | 	v = NULL; | 
 |  | 
 | 	KT_ASSERT_M("should be possible to remove an existing element", | 
 | 	            (v = hashtable_remove(h, (void*)k))); | 
 |  | 
 | 	KT_ASSERT_M("element should not remain in a hashtable after deletion", | 
 | 	            !(v = hashtable_search(h, (void*)k))); | 
 |  | 
 | 	/* Testing a bunch of items, insert, search, and removal */ | 
 | 	for (int i = 0; i < 10; i++) { | 
 | 		k = i; // vary the key, we don't do KEY collisions | 
 | 		KT_ASSERT_M("It should be possible to insert elements to a hashtable", | 
 | 		            (hashtable_insert(h, (void*)k, &tstruct[i]))); | 
 | 	} | 
 | 	// read out the 10 items | 
 | 	for (int i = 0; i < 10; i++) { | 
 | 		k = i; | 
 | 		KT_ASSERT_M("It should be possible to find inserted stuff in a hashtable", | 
 | 		            (v = hashtable_search(h, (void*)k))); | 
 | 		KT_ASSERT_M("The extracted element should be the same we inserted", | 
 | 		            (v == &tstruct[i])); | 
 | 	} | 
 |  | 
 | 	KT_ASSERT_M("The total count of number of elements should be 10", | 
 | 	            (10 == hashtable_count(h))); | 
 |  | 
 | 	// remove the 10 items | 
 | 	for (int i = 0; i < 10; i++) { | 
 | 		k = i; | 
 | 		KT_ASSERT_M("It should be possible to remove an existing element", | 
 | 		            (v = hashtable_remove(h, (void*)k))); | 
 |  | 
 | 	} | 
 | 	// make sure they are all gone | 
 | 	for (int i = 0; i < 10; i++) { | 
 | 		k = i; | 
 | 		KT_ASSERT_M("An element should not remain in a hashtable after deletion", | 
 | 		            !(v = hashtable_search(h, (void*)k))); | 
 | 	} | 
 |  | 
 | 	KT_ASSERT_M("The hashtable should be empty", | 
 | 	            (0 == hashtable_count(h))); | 
 |  | 
 | 	hashtable_destroy(h); | 
 |  | 
 | 	// same test of a bunch of items, but with collisions. | 
 | 	/* Testing a bunch of items with collisions, etc. */ | 
 | 	h = create_hashtable(32, test_hash_fn_col, __generic_eq); | 
 | 	// insert 10 items | 
 | 	for (int i = 0; i < 10; i++) { | 
 | 		k = i; // vary the key, we don't do KEY collisions | 
 |  | 
 | 		KT_ASSERT_M("It should be possible to insert elements to a hashtable", | 
 | 		            (hashtable_insert(h, (void*)k, &tstruct[i]))); | 
 | 	} | 
 | 	// read out the 10 items | 
 | 	for (int i = 0; i < 10; i++) { | 
 | 		k = i; | 
 | 		KT_ASSERT_M("It should be possible to find inserted stuff in a hashtable", | 
 | 		            (v = hashtable_search(h, (void*)k))); | 
 | 		KT_ASSERT_M("The extracted element should be the same we inserted", | 
 | 		            (v == &tstruct[i])); | 
 | 	} | 
 |  | 
 | 	KT_ASSERT_M("The total count of number of elements should be 10", | 
 | 	            (10 == hashtable_count(h))); | 
 |  | 
 | 	// remove the 10 items | 
 | 	for (int i = 0; i < 10; i++) { | 
 | 		k = i; | 
 | 		KT_ASSERT_M("It should be possible to remove an existing element", | 
 | 		            (v = hashtable_remove(h, (void*)k))); | 
 | 	} | 
 | 	// make sure they are all gone | 
 | 	for (int i = 0; i < 10; i++) { | 
 | 		k = i; | 
 |  | 
 | 		KT_ASSERT_M("An element should not remain in a hashtable after deletion", | 
 | 		            !(v = hashtable_search(h, (void*)k))); | 
 | 	} | 
 |  | 
 | 	KT_ASSERT_M("The hashtable should be empty", | 
 | 	            (0 == hashtable_count(h))); | 
 |  | 
 | 	hashtable_destroy(h); | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | bool test_circular_buffer(void) | 
 | { | 
 | 	static const size_t cbsize = 4096; | 
 | 	struct circular_buffer cb; | 
 | 	char *bigbuf; | 
 | 	size_t csize, off, cnum, mxsize; | 
 | 	char buf[256]; | 
 |  | 
 | 	KT_ASSERT_M("Failed to build the circular buffer", | 
 | 				circular_buffer_init(&cb, cbsize, NULL)); | 
 |  | 
 | 	for (size_t i = 0; i < 8 * cbsize; i++) { | 
 | 		size_t len = snprintf(buf, sizeof(buf), "%lu\n", i); | 
 |  | 
 | 		KT_ASSERT_M("Circular buffer write failed", | 
 | 			    circular_buffer_write(&cb, buf, len) == len); | 
 | 	} | 
 | 	cnum = off = 0; | 
 | 	while ((csize = circular_buffer_read(&cb, buf, sizeof(buf), off)) != 0) | 
 | 	{ | 
 | 		char *top = buf + csize; | 
 | 		char *ptr = buf; | 
 | 		char *pnl; | 
 |  | 
 | 		while ((pnl = memchr(ptr, '\n', top - ptr)) != NULL) { | 
 | 			size_t num; | 
 |  | 
 | 			*pnl = 0; | 
 | 			num = strtoul(ptr, NULL, 10); | 
 | 			KT_ASSERT_M("Numbers should be ascending", num >= cnum); | 
 | 			cnum = num; | 
 | 			ptr = pnl + 1; | 
 | 		} | 
 |  | 
 | 		off += ptr - buf; | 
 | 	} | 
 |  | 
 | 	for (size_t i = 0; i < (cbsize / sizeof(buf) + 1); i++) { | 
 | 		memset(buf, (int) i, sizeof(buf)); | 
 |  | 
 | 		KT_ASSERT_M("Circular buffer write failed", | 
 | 			    circular_buffer_write(&cb, buf, sizeof(buf)) == | 
 | 			    sizeof(buf)); | 
 | 	} | 
 | 	cnum = off = 0; | 
 | 	while ((csize = circular_buffer_read(&cb, buf, sizeof(buf), off)) != 0) | 
 | 	{ | 
 | 		size_t num = buf[0]; | 
 |  | 
 | 		KT_ASSERT_M("Invalid record read size", csize == sizeof(buf)); | 
 |  | 
 | 		if (off != 0) | 
 | 			KT_ASSERT_M("Invalid record sequence number", | 
 | 						num == ((cnum + 1) % 256)); | 
 | 		cnum = num; | 
 | 		off += csize; | 
 | 	} | 
 |  | 
 | 	bigbuf = kzmalloc(cbsize, MEM_WAIT); | 
 | 	KT_ASSERT(bigbuf != NULL); | 
 |  | 
 | 	mxsize = circular_buffer_max_write_size(&cb); | 
 | 	KT_ASSERT_M("Circular buffer max write failed", | 
 | 		    circular_buffer_write(&cb, bigbuf, mxsize) == mxsize); | 
 |  | 
 | 	memset(bigbuf, 17, cbsize); | 
 | 	csize = circular_buffer_read(&cb, bigbuf, mxsize, 0); | 
 | 	KT_ASSERT_M("Invalid max record read size", csize == mxsize); | 
 |  | 
 | 	for (size_t i = 0; i < csize; i++) | 
 | 		KT_ASSERT_M("Invalid max record value", bigbuf[i] == 0); | 
 |  | 
 | 	kfree(bigbuf); | 
 |  | 
 | 	circular_buffer_destroy(&cb); | 
 |  | 
 | 	return TRUE; | 
 | } | 
 |  | 
 | /* Ghetto test, only tests one prod or consumer at a time */ | 
 | // TODO: Un-guetto test, add assertions. | 
 | bool test_bcq(void) | 
 | { | 
 | 	/* Tests a basic struct */ | 
 | 	struct my_struct { | 
 | 		int x; | 
 | 		int y; | 
 | 	}; | 
 | 	struct my_struct in_struct, out_struct; | 
 |  | 
 | 	DEFINE_BCQ_TYPES(test, struct my_struct, 16); | 
 | 	struct test_bcq t_bcq; | 
 | 	bcq_init(&t_bcq, struct my_struct, 16); | 
 |  | 
 | 	in_struct.x = 4; | 
 | 	in_struct.y = 5; | 
 | 	out_struct.x = 1; | 
 | 	out_struct.y = 2; | 
 |  | 
 | 	bcq_enqueue(&t_bcq, &in_struct, 16, 5); | 
 | 	bcq_dequeue(&t_bcq, &out_struct, 16); | 
 | 	printk("out x %d. out y %d\n", out_struct.x, out_struct.y); | 
 |  | 
 | 	/* Tests the BCQ a bit more, esp with overflow */ | 
 | 	#define NR_ELEM_A_BCQ 8 /* NOTE: this must be a power of 2! */ | 
 | 	DEFINE_BCQ_TYPES(my, int, NR_ELEM_A_BCQ); | 
 | 	struct my_bcq a_bcq; | 
 | 	bcq_init(&a_bcq, int, NR_ELEM_A_BCQ); | 
 |  | 
 | 	int y = 2; | 
 | 	int output[100]; | 
 | 	int retval[100]; | 
 |  | 
 | 	/* Helpful debugger */ | 
 | 	void print_a_bcq(struct my_bcq *bcq) | 
 | 	{ | 
 | 		printk("A BCQ (made of ints): %p\n", bcq); | 
 | 		printk("\tprod_idx: %p\n", bcq->hdr.prod_idx); | 
 | 		printk("\tcons_pub_idx: %p\n", bcq->hdr.cons_pub_idx); | 
 | 		printk("\tcons_pvt_idx: %p\n", bcq->hdr.cons_pvt_idx); | 
 | 		for (int i = 0; i < NR_ELEM_A_BCQ; i++) { | 
 | 			printk("Element %d, rdy_for_cons: %02p\n", i, | 
 | 			       bcq->wraps[i].rdy_for_cons); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* Put in more than it can take */ | 
 | 	for (int i = 0; i < 15; i++) { | 
 | 		y = i; | 
 | 		retval[i] = bcq_enqueue(&a_bcq, &y, NR_ELEM_A_BCQ, 10); | 
 | 		printk("enqueued: %d, had retval %d \n", y, retval[i]); | 
 | 	} | 
 | 	//print_a_bcq(&a_bcq); | 
 |  | 
 | 	/* Try to dequeue more than we put in */ | 
 | 	for (int i = 0; i < 15; i++) { | 
 | 		retval[i] = bcq_dequeue(&a_bcq, &output[i], NR_ELEM_A_BCQ); | 
 | 		printk("dequeued: %d with retval %d\n", output[i], retval[i]); | 
 | 	} | 
 | 	//print_a_bcq(&a_bcq); | 
 |  | 
 | 	/* Put in some it should be able to take */ | 
 | 	for (int i = 0; i < 3; i++) { | 
 | 		y = i; | 
 | 		retval[i] = bcq_enqueue(&a_bcq, &y, NR_ELEM_A_BCQ, 10); | 
 | 		printk("enqueued: %d, had retval %d \n", y, retval[i]); | 
 | 	} | 
 |  | 
 | 	/* Take those, and then a couple extra */ | 
 | 	for (int i = 0; i < 5; i++) { | 
 | 		retval[i] = bcq_dequeue(&a_bcq, &output[i], NR_ELEM_A_BCQ); | 
 | 		printk("dequeued: %d with retval %d\n", output[i], retval[i]); | 
 | 	} | 
 |  | 
 | 	/* Try some one-for-one */ | 
 | 	for (int i = 0; i < 5; i++) { | 
 | 		y = i; | 
 | 		retval[i] = bcq_enqueue(&a_bcq, &y, NR_ELEM_A_BCQ, 10); | 
 | 		printk("enqueued: %d, had retval %d \n", y, retval[i]); | 
 | 		retval[i] = bcq_dequeue(&a_bcq, &output[i], NR_ELEM_A_BCQ); | 
 | 		printk("dequeued: %d with retval %d\n", output[i], retval[i]); | 
 | 	} | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | /* Test a simple concurrent send and receive (one prod, one cons).  We spawn a | 
 |  * process that will go into _M mode on another core, and we'll do the test from | 
 |  * an alarm handler run on our core.  When we start up the process, we won't | 
 |  * return so we need to defer the work with an alarm. */ | 
 | // TODO: Check if we can add more assertions. | 
 | bool test_ucq(void) | 
 | { | 
 | 	struct timer_chain *tchain = &per_cpu_info[core_id()].tchain; | 
 | 	struct alarm_waiter *waiter = kmalloc(sizeof(struct alarm_waiter), 0); | 
 |  | 
 | 	/* Alarm handler: what we want to do after the process is up */ | 
 | 	void send_msgs(struct alarm_waiter *waiter) | 
 | 	{ | 
 | 		struct timer_chain *tchain; | 
 | 		struct proc *p = waiter->data; | 
 | 		uintptr_t old_proc; | 
 | 		struct ucq *ucq = (struct ucq*)USTACKTOP; | 
 | 		struct event_msg msg; | 
 |  | 
 | 		printk("Running the alarm handler!\n"); | 
 | 		printk("NR msg per page: %d\n", NR_MSG_PER_PAGE); | 
 | 		/* might not be mmaped yet, if not, abort.  We used to | 
 | 		 * user_mem_check, but now we just touch it and PF. */ | 
 | 		char touch = *(char*)ucq; | 
 | 		asm volatile ("" : : "r"(touch)); | 
 | 		/* load their address space */ | 
 | 		old_proc = switch_to(p); | 
 | 		/* So it's mmaped, see if it is ready (note that this is | 
 | 		 * dangerous) */ | 
 | 		if (!ucq->ucq_ready) { | 
 | 			printk("Not ready yet\n"); | 
 | 			switch_back(p, old_proc); | 
 | 			goto abort; | 
 | 		} | 
 | 		/* So it's ready, time to finally do the tests... */ | 
 | 		printk("[kernel] Finally starting the tests... \n"); | 
 | 		/* 1: Send a simple message */ | 
 | 		printk("[kernel] #1 Sending simple message (7, deadbeef)\n"); | 
 | 		msg.ev_type = 7; | 
 | 		msg.ev_arg2 = 0xdeadbeef; | 
 | 		send_ucq_msg(ucq, p, &msg); | 
 | 		printk("nr_pages: %d\n", atomic_read(&ucq->nr_extra_pgs)); | 
 | 		/* 2: Send a bunch.  In a VM, this causes one swap, and then a | 
 | 		 * bunch of mmaps. */ | 
 | 		printk("[kernel] #2 \n"); | 
 | 		for (int i = 0; i < 5000; i++) { | 
 | 			msg.ev_type = i; | 
 | 			send_ucq_msg(ucq, p, &msg); | 
 | 		} | 
 | 		printk("nr_pages: %d\n", atomic_read(&ucq->nr_extra_pgs)); | 
 | 		printk("[kernel] #3 \n"); | 
 | 		/* 3: make sure we chained pages (assuming 1k is enough) */ | 
 | 		for (int i = 0; i < 1000; i++) { | 
 | 			msg.ev_type = i; | 
 | 			send_ucq_msg(ucq, p, &msg); | 
 | 		} | 
 | 		printk("nr_pages: %d\n", atomic_read(&ucq->nr_extra_pgs)); | 
 | 		/* other things we could do: | 
 | 		 *  - concurrent producers / consumers...  ugh. | 
 | 		 *  - would require a kmsg to another core, instead of a local | 
 | 		 *  alarm | 
 | 		 */ | 
 | 		/* done, switch back and free things */ | 
 | 		switch_back(p, old_proc); | 
 | 		proc_decref(p); | 
 | 		kfree(waiter); /* since it was kmalloc()d */ | 
 | 		return; | 
 | 	abort: | 
 | 		tchain = &per_cpu_info[core_id()].tchain; | 
 | 		/* Set to run again */ | 
 | 		set_awaiter_rel(waiter, 1000000); | 
 | 		set_alarm(tchain, waiter); | 
 | 	} | 
 | 	/* Set up a handler to run the real part of the test */ | 
 | 	init_awaiter(waiter, send_msgs); | 
 | 	set_awaiter_rel(waiter, 1000000);	/* 1s should be long enough */ | 
 | 	set_alarm(tchain, waiter); | 
 | 	/* Just spawn the program */ | 
 | 	struct file_or_chan *program; | 
 |  | 
 | 	program = foc_open("/bin/ucq", O_READ, 0); | 
 |  | 
 | 	KT_ASSERT_M("We should be able to find /bin/ucq", | 
 | 	            program); | 
 |  | 
 | 	struct proc *p = proc_create(program, NULL, NULL); | 
 |  | 
 | 	proc_wakeup(p); | 
 | 	/* instead of getting rid of the reference created in proc_create, we'll | 
 | 	 * put it in the awaiter */ | 
 | 	waiter->data = p; | 
 | 	foc_decref(program); | 
 | 	/* Should never return from schedule (env_pop in there) also note you | 
 | 	 * may not get the process you created, in the event there are others | 
 | 	 * floating around that are runnable */ | 
 | 	run_scheduler(); | 
 | 	smp_idle(); | 
 |  | 
 | 	KT_ASSERT_M("We should never return from schedule", | 
 | 	            false); | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | /* Kernel message to restart our kthread */ | 
 | static void __test_up_sem(uint32_t srcid, long a0, long a1, long a2) | 
 | { | 
 | 	struct semaphore *sem = (struct semaphore*)a0; | 
 | 	printk("[kmsg] Upping the sem to start the kthread, stacktop is %p\n", | 
 | 		   get_stack_top()); | 
 | 	if (!sem_up(sem)) { | 
 | 		printk("[kmsg] Crap, the sem didn't have a kthread waiting!\n"); | 
 | 		return; | 
 | 	} | 
 | 	printk("Kthread will restart when we handle the __launch RKM\n"); | 
 | } | 
 |  | 
 | /* simple test - start one, do something else, and resume it.  For lack of a | 
 |  * better infrastructure, we send ourselves a kmsg to run the kthread, which | 
 |  * we'll handle in smp_idle (which you may have to manually call).  Note this | 
 |  * doesn't test things like memory being leaked, or dealing with processes. */ | 
 | // TODO: Add assertions. | 
 | bool test_kthreads(void) | 
 | { | 
 | 	struct semaphore sem = SEMAPHORE_INITIALIZER(sem, 1); | 
 |  | 
 | 	printk("We're a kthread!  Stacktop is %p.  Testing suspend, etc...\n", | 
 | 	       get_stack_top()); | 
 | 	/* So we have something that will wake us up.  Routine messages won't | 
 | 	 * get serviced in the kernel right away. */ | 
 | 	send_kernel_message(core_id(), __test_up_sem, (long)&sem, 0, 0, | 
 | 	                    KMSG_ROUTINE); | 
 | 	/* Actually block (or try to) */ | 
 | 	/* This one shouldn't block - but will test the unwind (if 1 above) */ | 
 | 	printk("About to sleep, but should unwind (signal beat us)\n"); | 
 | 	sem_down(&sem); | 
 | 	/* This one is for real, yo.  Run and tell that. */ | 
 | 	printk("About to sleep for real\n"); | 
 | 	sem_down(&sem); | 
 | 	printk("Kthread restarted!, Stacktop is %p.\n", get_stack_top()); | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | /* Second player's kmsg */ | 
 | static void __test_kref_2(uint32_t srcid, long a0, long a1, long a2) | 
 | { | 
 | 	struct kref *kref = (struct kref*)a0; | 
 | 	bool *done = (bool*)a1; | 
 | 	enable_irq(); | 
 | 	for (int i = 0; i < 10000000; i++) { | 
 | 		kref_get(kref, 1); | 
 | 		set_core_timer(1, TRUE); | 
 | 		udelay(2); | 
 | 		kref_put(kref); | 
 | 	} | 
 | 	*done = TRUE; | 
 | } | 
 |  | 
 | /* Runs a simple test between core 0 (caller) and core 2 */ | 
 | // TODO: I believe we need more assertions. | 
 | bool test_kref(void) | 
 | { | 
 | 	struct kref local_kref; | 
 | 	bool done = FALSE; | 
 |  | 
 | 	kref_init(&local_kref, fake_release, 1); | 
 | 	send_kernel_message(2, __test_kref_2, (long)&local_kref, (long)&done, 0, | 
 | 	                    KMSG_ROUTINE); | 
 | 	for (int i = 0; i < 10000000; i++) { | 
 | 		kref_get(&local_kref, 1); | 
 | 		udelay(2); | 
 | 		kref_put(&local_kref); | 
 | 	} | 
 | 	while (!done) | 
 | 		cpu_relax(); | 
 | 	KT_ASSERT(kref_refcnt(&local_kref) == 1); | 
 | 	printk("[TEST-KREF] Simple 2-core getting/putting passed.\n"); | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | // TODO: Add more descriptive assertion messages. | 
 | bool test_atomics(void) | 
 | { | 
 | 	/* subtract_and_test */ | 
 | 	atomic_t num; | 
 | 	/* Test subing to 0 */ | 
 | 	atomic_init(&num, 1); | 
 | 	KT_ASSERT(atomic_sub_and_test(&num, 1) == 1); | 
 | 	atomic_init(&num, 2); | 
 | 	KT_ASSERT(atomic_sub_and_test(&num, 2) == 1); | 
 | 	/* Test not getting to 0 */ | 
 | 	atomic_init(&num, 1); | 
 | 	KT_ASSERT(atomic_sub_and_test(&num, 0) == 0); | 
 | 	atomic_init(&num, 2); | 
 | 	KT_ASSERT(atomic_sub_and_test(&num, 1) == 0); | 
 | 	/* Test negatives */ | 
 | 	atomic_init(&num, -1); | 
 | 	KT_ASSERT(atomic_sub_and_test(&num, 1) == 0); | 
 | 	atomic_init(&num, -1); | 
 | 	KT_ASSERT(atomic_sub_and_test(&num, -1) == 1); | 
 | 	/* Test larger nums */ | 
 | 	atomic_init(&num, 265); | 
 | 	KT_ASSERT(atomic_sub_and_test(&num, 265) == 1); | 
 | 	atomic_init(&num, 265); | 
 | 	KT_ASSERT(atomic_sub_and_test(&num, 2) == 0); | 
 |  | 
 | 	/* CAS */ | 
 | 	/* Simple test, make sure the bool retval of CAS handles failure */ | 
 | 	bool test_cas_val(long init_val) | 
 | 	{ | 
 | 		atomic_t actual_num; | 
 | 		long old_num; | 
 | 		int attempt; | 
 | 		atomic_init(&actual_num, init_val); | 
 | 		attempt = 0; | 
 | 		do { | 
 | 			old_num = atomic_read(&actual_num); | 
 | 			/* First time, try to fail */ | 
 | 			if (attempt == 0) | 
 | 				old_num++; | 
 | 			attempt++; | 
 | 		} while (!atomic_cas(&actual_num, old_num, old_num + 10)); | 
 | 		if (atomic_read(&actual_num) != init_val + 10) { | 
 | 			return false; | 
 | 		} else { | 
 | 			return true; | 
 | 		} | 
 | 	} | 
 | 	KT_ASSERT_M("CAS test for 257 should be successful.", | 
 | 	            test_cas_val(257)); | 
 | 	KT_ASSERT_M("CAS test for 1 should be successful.", | 
 | 	            test_cas_val(1)); | 
 | 	return true; | 
 | } | 
 |  | 
 | /* Helper KMSG for test_abort.  Core 1 does this, while core 0 sends an IRQ. */ | 
 | static void __test_try_halt(uint32_t srcid, long a0, long a1, long a2) | 
 | { | 
 | 	disable_irq(); | 
 | 	/* wait 10 sec.  should have a bunch of ints pending */ | 
 | 	udelay(10000000); | 
 | 	printk("Core 1 is about to halt\n"); | 
 | 	cpu_halt(); | 
 | 	printk("Returned from halting on core 1\n"); | 
 | } | 
 |  | 
 | /* x86 test, making sure our cpu_halt() and handle_irq() work.  If you want to | 
 |  * see it fail, you'll probably need to put a nop in the asm for cpu_halt(), and | 
 |  * comment out abort_halt() in handle_irq(). */ | 
 | // TODO: Add assertions. | 
 | bool test_abort_halt(void) | 
 | { | 
 | #ifdef CONFIG_X86 | 
 | 	send_kernel_message(1, __test_try_halt, 0, 0, 0, KMSG_ROUTINE); | 
 | 	/* wait 1 sec, enough time to for core 1 to be in its KMSG */ | 
 | 	udelay(1000000); | 
 | 	/* Send an IPI */ | 
 | 	send_ipi(0x01, I_TESTING); | 
 | 	printk("Core 0 sent the IPI\n"); | 
 | #endif /* CONFIG_X86 */ | 
 | 	return true; | 
 | } | 
 |  | 
 | /* Funcs and global vars for test_cv() */ | 
 | static struct cond_var local_cv; | 
 | static atomic_t counter; | 
 | static struct cond_var *cv = &local_cv; | 
 | static volatile bool state = FALSE;		/* for test 3 */ | 
 |  | 
 | void __test_cv_signal(uint32_t srcid, long a0, long a1, long a2) | 
 | { | 
 | 	if (atomic_read(&counter) % 4) | 
 | 		cv_signal(cv); | 
 | 	else | 
 | 		cv_broadcast(cv); | 
 | 	atomic_dec(&counter); | 
 | } | 
 |  | 
 | void __test_cv_waiter(uint32_t srcid, long a0, long a1, long a2) | 
 | { | 
 | 	cv_lock(cv); | 
 | 	/* check state, etc */ | 
 | 	cv_wait_and_unlock(cv); | 
 | 	atomic_dec(&counter); | 
 | } | 
 |  | 
 | void __test_cv_waiter_t3(uint32_t srcid, long a0, long a1, long a2) | 
 | { | 
 | 	udelay(a0); | 
 | 	/* if state == false, we haven't seen the signal yet */ | 
 | 	cv_lock(cv); | 
 | 	while (!state) { | 
 | 		cpu_relax(); | 
 | 		cv_wait(cv);	/* unlocks and relocks */ | 
 | 	} | 
 | 	cv_unlock(cv); | 
 | 	/* Make sure we are done, tell the controller we are done */ | 
 | 	cmb(); | 
 | 	assert(state); | 
 | 	atomic_dec(&counter); | 
 | } | 
 |  | 
 | // TODO: Add more assertions. | 
 | bool test_cv(void) | 
 | { | 
 | 	int nr_msgs; | 
 |  | 
 | 	cv_init(cv); | 
 | 	/* Test 0: signal without waiting */ | 
 | 	cv_broadcast(cv); | 
 | 	cv_signal(cv); | 
 | 	kthread_yield(); | 
 | 	printk("test_cv: signal without waiting complete\n"); | 
 |  | 
 | 	/* Test 1: single / minimal shit */ | 
 | 	nr_msgs = num_cores - 1; /* not using cpu 0 */ | 
 | 	atomic_init(&counter, nr_msgs); | 
 | 	for (int i = 1; i < num_cores; i++) | 
 | 		send_kernel_message(i, __test_cv_waiter, 0, 0, 0, KMSG_ROUTINE); | 
 | 	udelay(1000000); | 
 | 	cv_signal(cv); | 
 | 	kthread_yield(); | 
 | 	while (atomic_read(&counter) != nr_msgs - 1) | 
 | 		cpu_relax(); | 
 | 	printk("test_cv: single signal complete\n"); | 
 | 	cv_broadcast(cv); | 
 | 	/* broadcast probably woke up the waiters on our core.  since we want to | 
 | 	 * spin on their completion, we need to yield for a bit. */ | 
 | 	kthread_yield(); | 
 | 	while (atomic_read(&counter)) | 
 | 		cpu_relax(); | 
 | 	printk("test_cv: broadcast signal complete\n"); | 
 |  | 
 | 	/* Test 2: shitloads of waiters and signalers */ | 
 | 	nr_msgs = 0x500;	/* any more than 0x20000 could go OOM */ | 
 | 	atomic_init(&counter, nr_msgs); | 
 | 	for (int i = 0; i < nr_msgs; i++) { | 
 | 		int cpu = (i % (num_cores - 1)) + 1; | 
 | 		if (atomic_read(&counter) % 5) | 
 | 			send_kernel_message(cpu, __test_cv_waiter, 0, 0, 0, | 
 | 					    KMSG_ROUTINE); | 
 | 		else | 
 | 			send_kernel_message(cpu, __test_cv_signal, 0, 0, 0, KMSG_ROUTINE); | 
 | 	} | 
 | 	kthread_yield();	/* run whatever messages we sent to ourselves */ | 
 | 	while (atomic_read(&counter)) { | 
 | 		cpu_relax(); | 
 | 		cv_broadcast(cv); | 
 | 		udelay(1000000); | 
 | 		kthread_yield();/* run whatever messages we sent to ourselves */ | 
 | 	} | 
 | 	KT_ASSERT(!cv->nr_waiters); | 
 | 	printk("test_cv: massive message storm complete\n"); | 
 |  | 
 | 	/* Test 3: basic one signaller, one receiver.  we want to vary the | 
 | 	 * amount of time the sender and receiver delays, starting with (1ms, | 
 | 	 * 0ms) and ending with (0ms, 1ms).  At each extreme, such as with the | 
 | 	 * sender waiting 1ms, the receiver/waiter should hit the "check and | 
 | 	 * wait" point well before the sender/signaller hits the "change state | 
 | 	 * and signal" point. */ | 
 | 	for (int i = 0; i < 1000; i++) { | 
 | 		/* some extra chances at each point */ | 
 | 		for (int j = 0; j < 10; j++) { | 
 | 			state = FALSE; | 
 | 			/* signal that the client is done */ | 
 | 			atomic_init(&counter, 1); | 
 | 			/* client waits for i usec */ | 
 | 			send_kernel_message(2, __test_cv_waiter_t3, i, 0, 0, | 
 | 					    KMSG_ROUTINE); | 
 | 			cmb(); | 
 | 			udelay(1000 - i);	/* senders wait time: 1000..0 */ | 
 | 			state = TRUE; | 
 | 			cv_signal(cv); | 
 | 			/* signal might have unblocked a kthread, let it run */ | 
 | 			kthread_yield(); | 
 | 			/* they might not have run at all yet (in which case | 
 | 			 * they lost the race and don't need the signal).  but | 
 | 			 * we need to wait til they're done */ | 
 | 			while (atomic_read(&counter)) | 
 | 				cpu_relax(); | 
 | 			KT_ASSERT(!cv->nr_waiters); | 
 | 		} | 
 | 	} | 
 | 	printk("test_cv: single sender/receiver complete\n"); | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | /* Based on a bug I noticed.  TODO: actual memset test... */ | 
 | bool test_memset(void) | 
 | { | 
 | 	#define ARR_SZ 256 | 
 |  | 
 | 	void print_array(char *c, size_t len) | 
 | 	{ | 
 | 		for (int i = 0; i < len; i++) | 
 | 			printk("%04d: %02x\n", i, *c++); | 
 | 	} | 
 |  | 
 | 	bool check_array(char *c, char x, size_t len) | 
 | 	{ | 
 | 		for (int i = 0; i < len; i++) { | 
 | 			#define ASSRT_SIZE 64 | 
 | 			char *assrt_msg = (char*) kmalloc(ASSRT_SIZE, 0); | 
 | 			snprintf(assrt_msg, ASSRT_SIZE, | 
 | 				 "Char %d is %c (%02x), should be %c (%02x)", i, | 
 | 				 *c, *c, x, x); | 
 | 			KT_ASSERT_M(assrt_msg, (*c == x)); | 
 | 			c++; | 
 | 		} | 
 | 		return true; | 
 | 	} | 
 |  | 
 | 	bool run_check(char *arr, int ch, size_t len) | 
 | 	{ | 
 | 		char *c = arr; | 
 | 		for (int i = 0; i < ARR_SZ; i++) | 
 | 			*c++ = 0x0; | 
 | 		memset(arr, ch, len - 4); | 
 | 		if (check_array(arr, ch, len - 4) && | 
 | 		    check_array(arr + len - 4, 0x0, 4)) { | 
 | 			return true; | 
 | 		} else { | 
 | 			return false; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	char bytes[ARR_SZ]; | 
 |  | 
 | 	if (!run_check(bytes, 0xfe, 20) || !run_check(bytes, 0xc0fe, 20)) { | 
 | 		return false; | 
 | 	} | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | void noinline __longjmp_wrapper(struct jmpbuf *jb) | 
 | { | 
 | 	asm (""); | 
 | 	printk("Starting: %s\n", __FUNCTION__); | 
 | 	longjmp(jb, 1); | 
 | 	// Should never get here | 
 | 	printk("Exiting: %s\n", __FUNCTION__); | 
 | } | 
 |  | 
 | // TODO: Add assertions. | 
 | bool test_setjmp(void) | 
 | { | 
 | 	struct jmpbuf jb; | 
 | 	printk("Starting: %s\n", __FUNCTION__); | 
 | 	if (setjmp(&jb)) { | 
 | 	  printk("After second setjmp return: %s\n", __FUNCTION__); | 
 | 	} | 
 | 	else { | 
 | 		printk("After first setjmp return: %s\n", __FUNCTION__); | 
 | 		__longjmp_wrapper(&jb); | 
 | 	} | 
 | 	printk("Exiting: %s\n", __FUNCTION__); | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | // TODO: add assertions. | 
 | bool test_apipe(void) | 
 | { | 
 | 	static struct atomic_pipe test_pipe; | 
 |  | 
 | 	struct some_struct { | 
 | 		long x; | 
 | 		int y; | 
 | 	}; | 
 | 	/* Don't go too big, or you'll run off the stack */ | 
 | 	#define MAX_BATCH 100 | 
 |  | 
 | 	void __test_apipe_writer(uint32_t srcid, long a0, long a1, long a2) | 
 | 	{ | 
 | 		int ret, count_todo; | 
 | 		int total = 0; | 
 | 		struct some_struct local_str[MAX_BATCH]; | 
 | 		for (int i = 0; i < MAX_BATCH; i++) { | 
 | 			local_str[i].x = 0xf00; | 
 | 			local_str[i].y = 0xba5; | 
 | 		} | 
 | 		/* testing 0, and max out at 50. [0, ... 50] */ | 
 | 		for (int i = 0; i < MAX_BATCH + 1; i++) { | 
 | 			count_todo = i; | 
 | 			while (count_todo) { | 
 | 				ret = apipe_write(&test_pipe, &local_str, | 
 | 						  count_todo); | 
 | 				/* Shouldn't break, based on the loop counters | 
 | 				 */ | 
 | 				if (!ret) { | 
 | 					printk("Writer breaking with %d left\n", | 
 | 					       count_todo); | 
 | 					break; | 
 | 				} | 
 | 				total += ret; | 
 | 				count_todo -= ret; | 
 | 			} | 
 | 		} | 
 | 		printk("Writer done, added %d elems\n", total); | 
 | 		apipe_close_writer(&test_pipe); | 
 | 	} | 
 |  | 
 | 	void __test_apipe_reader(uint32_t srcid, long a0, long a1, long a2) | 
 | 	{ | 
 | 		int ret, count_todo; | 
 | 		int total = 0; | 
 | 		struct some_struct local_str[MAX_BATCH] = {{0}}; | 
 | 		/* reversed loop compared to the writer [50, ... 0] */ | 
 | 		for (int i = MAX_BATCH; i >= 0; i--) { | 
 | 			count_todo = i; | 
 | 			while (count_todo) { | 
 | 				ret = apipe_read(&test_pipe, &local_str, | 
 | 						 count_todo); | 
 | 				if (!ret) { | 
 | 					printk("Reader breaking with %d left\n", | 
 | 					       count_todo); | 
 | 					break; | 
 | 				} | 
 | 				total += ret; | 
 | 				count_todo -= ret; | 
 | 			} | 
 | 		} | 
 | 		printk("Reader done, took %d elems\n", total); | 
 | 		for (int i = 0; i < MAX_BATCH; i++) { | 
 | 			assert(local_str[i].x == 0xf00); | 
 | 			assert(local_str[i].y == 0xba5); | 
 | 		} | 
 | 		apipe_close_reader(&test_pipe); | 
 | 	} | 
 |  | 
 | 	void *pipe_buf = kpage_alloc_addr(); | 
 | 	KT_ASSERT(pipe_buf); | 
 | 	apipe_init(&test_pipe, pipe_buf, PGSIZE, sizeof(struct some_struct)); | 
 | 	printd("*ap_buf %p\n", test_pipe.ap_buf); | 
 | 	printd("ap_ring_sz %p\n", test_pipe.ap_ring_sz); | 
 | 	printd("ap_elem_sz %p\n", test_pipe.ap_elem_sz); | 
 | 	printd("ap_rd_off %p\n", test_pipe.ap_rd_off); | 
 | 	printd("ap_wr_off %p\n", test_pipe.ap_wr_off); | 
 | 	printd("ap_nr_readers %p\n", test_pipe.ap_nr_readers); | 
 | 	printd("ap_nr_writers %p\n", test_pipe.ap_nr_writers); | 
 | 	send_kernel_message(0, __test_apipe_writer, 0, 0, 0, KMSG_ROUTINE); | 
 | 	/* Once we start synchronizing with a kmsg / kthread that could be on a | 
 | 	 * different core, we run the chance of being migrated when we block. */ | 
 | 	__test_apipe_reader(0, 0, 0, 0); | 
 | 	/* Wait til the first test is done */ | 
 | 	while (test_pipe.ap_nr_writers) { | 
 | 		kthread_yield(); | 
 | 		cpu_relax(); | 
 | 	} | 
 | 	/* Try cross core (though CV wake ups schedule on the waking core) */ | 
 | 	apipe_open_reader(&test_pipe); | 
 | 	apipe_open_writer(&test_pipe); | 
 | 	send_kernel_message(1, __test_apipe_writer, 0, 0, 0, KMSG_ROUTINE); | 
 | 	__test_apipe_reader(0, 0, 0, 0); | 
 | 	/* We could be on core 1 now.  If we were called from core0, our caller | 
 | 	 * might expect us to return while being on core 0 (like if we were | 
 | 	 * kfunc'd from the monitor.  Be careful if you copy this code. */ | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | static struct rwlock rwlock, *rwl = &rwlock; | 
 | static atomic_t rwlock_counter; | 
 | // TODO: Add assertions. | 
 | bool test_rwlock(void) | 
 | { | 
 | 	bool ret; | 
 | 	rwinit(rwl); | 
 | 	/* Basic: can i lock twice, recursively? */ | 
 | 	rlock(rwl); | 
 | 	ret = canrlock(rwl); | 
 | 	KT_ASSERT(ret); | 
 | 	runlock(rwl); | 
 | 	runlock(rwl); | 
 | 	/* Other simply tests */ | 
 | 	wlock(rwl); | 
 | 	wunlock(rwl); | 
 |  | 
 | 	/* Just some half-assed different operations */ | 
 | 	void __test_rwlock(uint32_t srcid, long a0, long a1, long a2) | 
 | 	{ | 
 | 		int rand = read_tsc() & 0xff; | 
 | 		for (int i = 0; i < 10000; i++) { | 
 | 			switch ((rand * i) % 5) { | 
 | 			case 0: | 
 | 			case 1: | 
 | 				rlock(rwl); | 
 | 				runlock(rwl); | 
 | 				break; | 
 | 			case 2: | 
 | 			case 3: | 
 | 				if (canrlock(rwl)) | 
 | 					runlock(rwl); | 
 | 				break; | 
 | 			case 4: | 
 | 				wlock(rwl); | 
 | 				wunlock(rwl); | 
 | 				break; | 
 | 			} | 
 | 		} | 
 | 		/* signal to allow core 0 to finish */ | 
 | 		atomic_dec(&rwlock_counter); | 
 | 	} | 
 |  | 
 | 	/* send 4 messages to each non core 0 */ | 
 | 	atomic_init(&rwlock_counter, (num_cores - 1) * 4); | 
 | 	for (int i = 1; i < num_cores; i++) | 
 | 		for (int j = 0; j < 4; j++) | 
 | 			send_kernel_message(i, __test_rwlock, 0, 0, 0, | 
 | 					    KMSG_ROUTINE); | 
 | 	while (atomic_read(&rwlock_counter)) | 
 | 		cpu_relax(); | 
 | 	printk("rwlock test complete\n"); | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | /* Funcs and global vars for test_rv() */ | 
 | static struct rendez local_rv; | 
 | static struct rendez *rv = &local_rv; | 
 | /* reusing state and counter from test_cv... */ | 
 |  | 
 | static int __rendez_cond(void *arg) | 
 | { | 
 | 	return *(bool*)arg; | 
 | } | 
 |  | 
 | void __test_rv_wakeup(uint32_t srcid, long a0, long a1, long a2) | 
 | { | 
 | 	if (atomic_read(&counter) % 4) | 
 | 		cv_signal(cv); | 
 | 	else | 
 | 		cv_broadcast(cv); | 
 | 	atomic_dec(&counter); | 
 | } | 
 |  | 
 | void __test_rv_sleeper(uint32_t srcid, long a0, long a1, long a2) | 
 | { | 
 | 	rendez_sleep(rv, __rendez_cond, (void*)&state); | 
 | 	atomic_dec(&counter); | 
 | } | 
 |  | 
 | void __test_rv_sleeper_timeout(uint32_t srcid, long a0, long a1, long a2) | 
 | { | 
 | 	/* half-assed amount of time. */ | 
 | 	rendez_sleep_timeout(rv, __rendez_cond, (void*)&state, a0); | 
 | 	atomic_dec(&counter); | 
 | } | 
 |  | 
 | // TODO: Add more assertions. | 
 | bool test_rv(void) | 
 | { | 
 | 	int nr_msgs; | 
 |  | 
 | 	rendez_init(rv); | 
 | 	/* Test 0: signal without waiting */ | 
 | 	rendez_wakeup(rv); | 
 | 	kthread_yield(); | 
 | 	printk("test_rv: wakeup without sleeping complete\n"); | 
 |  | 
 | 	/* Test 1: a few sleepers */ | 
 | 	nr_msgs = num_cores - 1; /* not using cpu 0 */ | 
 | 	atomic_init(&counter, nr_msgs); | 
 | 	state = FALSE; | 
 | 	for (int i = 1; i < num_cores; i++) | 
 | 		send_kernel_message(i, __test_rv_sleeper, 0, 0, 0, | 
 | 				    KMSG_ROUTINE); | 
 | 	udelay(1000000); | 
 | 	cmb(); | 
 | 	state = TRUE; | 
 | 	rendez_wakeup(rv); | 
 | 	/* broadcast probably woke up the waiters on our core.  since we want to | 
 | 	 * spin on their completion, we need to yield for a bit. */ | 
 | 	kthread_yield(); | 
 | 	while (atomic_read(&counter)) | 
 | 		cpu_relax(); | 
 | 	printk("test_rv: bulk wakeup complete\n"); | 
 |  | 
 | 	/* Test 2: different types of sleepers / timeouts */ | 
 | 	state = FALSE; | 
 | 	nr_msgs = 0x500;	/* any more than 0x20000 could go OOM */ | 
 | 	atomic_init(&counter, nr_msgs); | 
 | 	for (int i = 0; i < nr_msgs; i++) { | 
 | 		int cpu = (i % (num_cores - 1)) + 1; | 
 |  | 
 | 		/* timeouts from 0ms ..5000ms (enough that they should wake via | 
 | 		 * cond */ | 
 | 		if (atomic_read(&counter) % 5) | 
 | 			send_kernel_message(cpu, __test_rv_sleeper_timeout, i * | 
 | 					    4000, 0, 0, KMSG_ROUTINE); | 
 | 		else | 
 | 			send_kernel_message(cpu, __test_rv_sleeper, 0, 0, 0, | 
 | 					    KMSG_ROUTINE); | 
 | 	} | 
 | 	kthread_yield();	/* run whatever messages we sent to ourselves */ | 
 | 	state = TRUE; | 
 | 	while (atomic_read(&counter)) { | 
 | 		cpu_relax(); | 
 | 		rendez_wakeup(rv); | 
 | 		udelay(1000000); | 
 | 		kthread_yield();/* run whatever messages we sent to ourselves */ | 
 | 	} | 
 | 	KT_ASSERT(!rv->cv.nr_waiters); | 
 | 	printk("test_rv: lots of sleepers/timeouts complete\n"); | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | /* Cheap test for the alarm internal management */ | 
 | // TODO: Add assertions. | 
 | bool test_alarm(void) | 
 | { | 
 | 	uint64_t now = tsc2usec(read_tsc()); | 
 | 	struct alarm_waiter await1, await2; | 
 | 	struct timer_chain *tchain = &per_cpu_info[0].tchain; | 
 | 	void shouldnt_run(struct alarm_waiter *awaiter) | 
 | 	{ | 
 | 		printk("Crap, %p ran!\n", awaiter); | 
 | 	} | 
 | 	void empty_run(struct alarm_waiter *awaiter) | 
 | 	{ | 
 | 		printk("Yay, %p ran (hopefully twice)!\n", awaiter); | 
 | 	} | 
 | 	/* Test basic insert, move, remove */ | 
 | 	init_awaiter(&await1, shouldnt_run); | 
 | 	set_awaiter_abs(&await1, now + 1000000000); | 
 | 	set_alarm(tchain, &await1); | 
 | 	reset_alarm_abs(tchain, &await1, now + 1000000000 - 50); | 
 | 	reset_alarm_abs(tchain, &await1, now + 1000000000 + 50); | 
 | 	unset_alarm(tchain, &await1); | 
 | 	/* Test insert of one that fired already */ | 
 | 	init_awaiter(&await2, empty_run); | 
 | 	set_awaiter_rel(&await2, 1); | 
 | 	set_alarm(tchain, &await2); | 
 | 	enable_irq(); | 
 | 	udelay(1000); | 
 | 	reset_alarm_abs(tchain, &await2, now + 10); | 
 | 	udelay(1000); | 
 | 	unset_alarm(tchain, &await2); | 
 |  | 
 | 	printk("%s complete\n", __FUNCTION__); | 
 |  | 
 | 	return true; | 
 | } | 
 |  | 
 | bool test_kmalloc_incref(void) | 
 | { | 
 | 	/* this test is a bit invasive of the kmalloc internals */ | 
 | 	void *__get_unaligned_orig_buf(void *buf) | 
 | 	{ | 
 | 		int *tag_flags = (int*)(buf - sizeof(int)); | 
 | 		if ((*tag_flags & KMALLOC_FLAG_MASK) == KMALLOC_TAG_UNALIGN) | 
 | 			return (buf - (*tag_flags >> KMALLOC_ALIGN_SHIFT)); | 
 | 		else | 
 | 			return 0; | 
 | 	} | 
 |  | 
 | 	bool test_buftag(void *b, struct kmalloc_tag *btag, char *str) | 
 | 	{ | 
 | 		KT_ASSERT_M(str, kref_refcnt(&btag->kref) == 1); | 
 | 		kmalloc_incref(b); | 
 | 		KT_ASSERT_M(str, kref_refcnt(&btag->kref) == 2); | 
 | 		kfree(b); | 
 | 		KT_ASSERT_M(str, kref_refcnt(&btag->kref) == 1); | 
 | 		kfree(b); | 
 | 		/* dangerous read, it's been freed */ | 
 | 		KT_ASSERT_M(str, kref_refcnt(&btag->kref) == 0); | 
 | 		return TRUE; | 
 | 	} | 
 |  | 
 | 	void *b1, *b2, *b2o; | 
 | 	struct kmalloc_tag *b1tag, *b2tag; | 
 |  | 
 | 	/* no realigned case */ | 
 | 	b1 = kmalloc(55, 0); | 
 | 	KT_ASSERT(!__get_unaligned_orig_buf(b1)); | 
 | 	b1tag = (struct kmalloc_tag*)(b1 - sizeof(struct kmalloc_tag)); | 
 |  | 
 | 	/* realigned case.  alloc'd before b1's test, so we know we get | 
 | 	 * different buffers. */ | 
 | 	b2 = kmalloc_align(55, 0, 64); | 
 | 	b2o = __get_unaligned_orig_buf(b2); | 
 | 	KT_ASSERT(b2o); | 
 | 	b2tag = (struct kmalloc_tag*)(b2o - sizeof(struct kmalloc_tag)); | 
 |  | 
 | 	test_buftag(b1, b1tag, "b1, no realign"); | 
 | 	test_buftag(b2, b2tag, "b2, realigned"); | 
 |  | 
 | 	return TRUE; | 
 | } | 
 |  | 
 | /* Some ghetto things: | 
 |  * - ASSERT_M only lets you have a string, not a format string. | 
 |  * - put doesn't return, so we have a "loud" test for that.  alternatively, we | 
 |  *   could have put panic, but then we couldn't test it at all.  and i don't | 
 |  *   particularly want it to have a return value. | 
 |  * - ASSERT_M just blindly returns.  we're leaking memory. | 
 |  */ | 
 | bool test_u16pool(void) | 
 | { | 
 | 	#define AMT 4096 | 
 | 	int *t; | 
 | 	struct u16_pool *id = create_u16_pool(AMT); | 
 | 	int i, x, y; | 
 | 	int numalloc; | 
 | 	KT_ASSERT(id); | 
 |  | 
 | 	t = kzmalloc(sizeof(int) * (AMT + 1), MEM_WAIT); | 
 | 	for (x = 0; x < 1024; x++) { | 
 | 		KT_ASSERT_M("Should be empty", id->tos == 0); | 
 | 		for (i = 0; i < id->size; i++) { | 
 | 			int p = get_u16(id); | 
 | 			if (p < 0) | 
 | 				KT_ASSERT_M("Couldn't get enough", 0); | 
 | 			t[i] = p; | 
 | 		} | 
 | 		numalloc = i; | 
 | 		// free them at random. With luck, we don't get too many | 
 | 		// duplicate hits. | 
 | 		for (y = i = 0; i < numalloc; y++) { | 
 | 			/* could read genrand, but that could be offline */ | 
 | 			int f = (uint16_t)read_tsc() % numalloc; | 
 | 			if (!t[f]) | 
 | 				continue; | 
 | 			put_u16(id, t[f]); | 
 | 			t[f] = 0; | 
 | 			i++; | 
 | 			/* that's long enough... */ | 
 | 			if (y > 2 * id->size) | 
 | 				break; | 
 | 		} | 
 | 		/* grab the leftovers */ | 
 | 		for (i = 0; i < id->size; i++) { | 
 | 			if (!t[i]) | 
 | 				continue; | 
 | 			put_u16(id, t[i]); | 
 | 			t[i] = 0; | 
 | 		} | 
 | 		/* all of our previous checks failed to give back 0 */ | 
 | 		put_u16(id, 0); | 
 | 	} | 
 |  | 
 | 	// pop too many. | 
 | 	bool we_broke = FALSE; | 
 | 	for (i = 0; i < id->size * 2; i++) { | 
 | 		x = get_u16(id); | 
 | 		if (x == -1) { | 
 | 			we_broke = TRUE; | 
 | 			break; | 
 | 		} | 
 | 		t[i] = x; | 
 | 	} | 
 | 	KT_ASSERT_M("Should have failed to get too many", we_broke); | 
 |  | 
 | 	numalloc = i; | 
 |  | 
 | 	printd("Allocated %d items\n", numalloc); | 
 | 	for (i = 0; i < numalloc; i++) { | 
 | 		put_u16(id, t[i]); | 
 | 		t[i] = 0; | 
 | 	} | 
 | 	KT_ASSERT_M("Should be empty", id->tos == 0); | 
 |  | 
 | 	printk("Ignore next BAD, testing bad alloc\n"); | 
 | 	put_u16(id, 25);	// should get an error. | 
 | 	for (i = 0; i < id->size; i++) { | 
 | 		int v = get_u16(id); | 
 | 		if (t[v]) | 
 | 			printd("BAD: %d pops twice!\n", v); | 
 | 		KT_ASSERT_M("Popped twice!", t[v] == 0); | 
 | 		t[v] = 1; | 
 | 		//printk("%d,", v); | 
 | 	} | 
 |  | 
 | 	for (i = 1; i < id->size; i++) { | 
 | 		if (!t[i]) | 
 | 			printd("BAD: %d was not set\n", i); | 
 | 		KT_ASSERT_M("Wasn't set!", t[i]); | 
 | 	} | 
 |  | 
 | 	kfree(t); | 
 | 	return FALSE; | 
 | } | 
 |  | 
 | static bool uaccess_mapped(void *addr, char *buf, char *buf2) | 
 | { | 
 | 	KT_ASSERT_M( | 
 | 		"Copy to user (u8) to mapped address should not fail", | 
 | 		copy_to_user(addr, buf, 1) == 0); | 
 | 	KT_ASSERT_M( | 
 | 		"Copy to user (u16) to mapped address should not fail", | 
 | 		copy_to_user(addr, buf, 2) == 0); | 
 | 	KT_ASSERT_M( | 
 | 		"Copy to user (u32) to mapped address should not fail", | 
 | 		copy_to_user(addr, buf, 4) == 0); | 
 | 	KT_ASSERT_M( | 
 | 		"Copy to user (u64) to mapped address should not fail", | 
 | 		copy_to_user(addr, buf, 8) == 0); | 
 | 	KT_ASSERT_M( | 
 | 		"Copy to user (mem) to mapped address should not fail", | 
 | 		copy_to_user(addr, buf, sizeof(buf)) == 0); | 
 |  | 
 | 	KT_ASSERT_M( | 
 | 		"Copy from user (u8) to mapped address should not fail", | 
 | 		copy_from_user(buf, addr, 1) == 0); | 
 | 	KT_ASSERT_M( | 
 | 		"Copy from user (u16) to mapped address should not fail", | 
 | 		copy_from_user(buf, addr, 2) == 0); | 
 | 	KT_ASSERT_M( | 
 | 		"Copy from user (u32) to mapped address should not fail", | 
 | 		copy_from_user(buf, addr, 4) == 0); | 
 | 	KT_ASSERT_M( | 
 | 		"Copy from user (u64) to mapped address should not fail", | 
 | 		copy_from_user(buf, addr, 8) == 0); | 
 | 	KT_ASSERT_M( | 
 | 		"Copy from user (mem) to mapped address should not fail", | 
 | 		copy_from_user(buf, addr, sizeof(buf)) == 0); | 
 |  | 
 | 	KT_ASSERT_M( | 
 | 		"String copy to user to mapped address should not fail", | 
 | 		strcpy_to_user(current, addr, "Akaros") == 0); | 
 | 	KT_ASSERT_M( | 
 | 		"String copy from user to mapped address should not fail", | 
 | 		strcpy_from_user(current, buf, addr) == 0); | 
 | 	KT_ASSERT_M("The copied string content should be matching", | 
 | 				memcmp(buf, "Akaros", 7) == 0); | 
 |  | 
 | 	return TRUE; | 
 | } | 
 |  | 
 | static bool uaccess_unmapped(void *addr, char *buf, char *buf2) | 
 | { | 
 | 	KT_ASSERT_M("Copy to user (u8) to not mapped address should fail", | 
 | 		    copy_to_user(addr, buf, 1) == -EFAULT); | 
 | 	KT_ASSERT_M("Copy to user (u16) to not mapped address should fail", | 
 | 		    copy_to_user(addr, buf, 2) == -EFAULT); | 
 | 	KT_ASSERT_M("Copy to user (u32) to not mapped address should fail", | 
 | 		    copy_to_user(addr, buf, 4) == -EFAULT); | 
 | 	KT_ASSERT_M("Copy to user (u64) to not mapped address should fail", | 
 | 		    copy_to_user(addr, buf, 8) == -EFAULT); | 
 | 	KT_ASSERT_M("Copy to user (mem) to not mapped address should fail", | 
 | 		    copy_to_user(addr, buf, sizeof(buf)) == -EFAULT); | 
 |  | 
 | 	KT_ASSERT_M("Copy from user (u8) to not mapped address should fail", | 
 | 		    copy_from_user(buf, addr, 1) == -EFAULT); | 
 | 	KT_ASSERT_M("Copy from user (u16) to not mapped address should fail", | 
 | 		    copy_from_user(buf, addr, 2) == -EFAULT); | 
 | 	KT_ASSERT_M("Copy from user (u32) to not mapped address should fail", | 
 | 		    copy_from_user(buf, addr, 4) == -EFAULT); | 
 | 	KT_ASSERT_M("Copy from user (u64) to not mapped address should fail", | 
 | 		    copy_from_user(buf, addr, 8) == -EFAULT); | 
 | 	KT_ASSERT_M("Copy from user (mem) to not mapped address should fail", | 
 | 		    copy_from_user(buf, addr, sizeof(buf)) == -EFAULT); | 
 |  | 
 | 	KT_ASSERT_M("String copy to user to not mapped address should fail", | 
 | 		    strcpy_to_user(NULL, addr, "Akaros") == -EFAULT); | 
 | 	KT_ASSERT_M("String copy from user to not mapped address should fail", | 
 | 		    strcpy_from_user(NULL, buf, addr) == -EFAULT); | 
 |  | 
 | 	KT_ASSERT_M("Copy from user with kernel side source pointer should fail", | 
 | 		    copy_from_user(buf, buf2, sizeof(buf)) == -EFAULT); | 
 | 	KT_ASSERT_M("Copy to user with kernel side source pointer should fail", | 
 | 		    copy_to_user(buf, buf2, sizeof(buf)) == -EFAULT); | 
 |  | 
 | 	return TRUE; | 
 | } | 
 |  | 
 | bool test_uaccess(void) | 
 | { | 
 | 	char buf[128] = { 0 }; | 
 | 	char buf2[128] = { 0 }; | 
 | 	struct proc *tmp; | 
 | 	uintptr_t switch_tmp; | 
 | 	int err; | 
 | 	static const size_t mmap_size = 4096; | 
 | 	void *addr; | 
 | 	bool passed = FALSE; | 
 |  | 
 | 	err = proc_alloc(&tmp, 0, 0); | 
 | 	KT_ASSERT_M("Failed to alloc a temp proc", err == 0); | 
 | 	/* Tell everyone we're ready in case some ops don't work on PROC_CREATED | 
 | 	 */ | 
 | 	__proc_set_state(tmp, PROC_RUNNABLE_S); | 
 | 	switch_tmp = switch_to(tmp); | 
 | 	addr = mmap(tmp, 0, mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, -1, | 
 | 		    0); | 
 | 	if (addr == MAP_FAILED) | 
 | 		goto out; | 
 | 	passed = uaccess_mapped(addr, buf, buf2); | 
 | 	munmap(tmp, (uintptr_t) addr, mmap_size); | 
 | 	if (!passed) | 
 | 		goto out; | 
 | 	passed = uaccess_unmapped(addr, buf, buf2); | 
 | out: | 
 | 	switch_back(tmp, switch_tmp); | 
 | 	proc_decref(tmp); | 
 | 	return passed; | 
 | } | 
 |  | 
 | bool test_sort(void) | 
 | { | 
 | 	int cmp_longs_asc(const void *p1, const void *p2) | 
 | 	{ | 
 | 		const long v1 = *(const long *) p1; | 
 | 		const long v2 = *(const long *) p2; | 
 |  | 
 | 		return v1 < v2 ? -1 : (v1 > v2 ? 1 : 0); | 
 | 	} | 
 |  | 
 | 	int cmp_longs_desc(const void *p1, const void *p2) | 
 | 	{ | 
 | 		const long v1 = *(const long *) p1; | 
 | 		const long v2 = *(const long *) p2; | 
 |  | 
 | 		return v1 < v2 ? 1 : (v1 > v2 ? -1 : 0); | 
 | 	} | 
 |  | 
 | 	size_t i; | 
 | 	long long_set_1[] = { | 
 | 		-9, 11, 0, 23, 123, -99, 3, 11, 23, -999, 872, 17, 21 | 
 | 	}; | 
 | 	long long_set_2[] = { | 
 | 		31, 77, -1, 2, 0, 64, 11, 19, 69, 111, -89, 17, 21, 44, 77 | 
 | 	}; | 
 |  | 
 | 	sort(long_set_1, ARRAY_SIZE(long_set_1), sizeof(long), cmp_longs_asc); | 
 | 	for (i = 1; i < ARRAY_SIZE(long_set_1); i++) | 
 | 		KT_ASSERT(long_set_1[i - 1] <= long_set_1[i]); | 
 |  | 
 | 	sort(long_set_2, ARRAY_SIZE(long_set_2), sizeof(long), cmp_longs_desc); | 
 | 	for (i = 1; i < ARRAY_SIZE(long_set_2); i++) | 
 | 		KT_ASSERT(long_set_2[i - 1] >= long_set_2[i]); | 
 |  | 
 | 	return TRUE; | 
 | } | 
 |  | 
 | bool test_cmdline_parse(void) | 
 | { | 
 | 	static const char *fake_cmdline = | 
 | 		"kernel -root=/foo -simple -num=123 -quoted='abc \\'' -dup=311 " | 
 | 		"-dup='akaros' -empty='' -inner=-outer -outer=-inner=xyz"; | 
 | 	const char *opt; | 
 | 	char param[128]; | 
 |  | 
 | 	/* Note that the get_boot_option() API should be passed NULL the first | 
 | 	 * time it is called, in normal cases, and should be passed the value | 
 | 	 * returned by previous call to get_boot_option(), in case multiple | 
 | 	 * options with same name have to be fetched.  */ | 
 | 	opt = get_boot_option(fake_cmdline, "-root", param, sizeof(param)); | 
 | 	KT_ASSERT_M("Unable to parse -root option", opt); | 
 | 	KT_ASSERT_M("Invalid -root option value", strcmp(param, "/foo") == 0); | 
 |  | 
 | 	opt = get_boot_option(fake_cmdline, "-root", NULL, 0); | 
 | 	KT_ASSERT_M("Unable to parse -root option when param not provided", | 
 | 		    opt); | 
 |  | 
 | 	opt = get_boot_option(fake_cmdline, "-simple", param, sizeof(param)); | 
 | 	KT_ASSERT_M("Unable to parse -simple option", opt); | 
 | 	KT_ASSERT_M("Invalid -simple option value", strcmp(param, "") == 0); | 
 |  | 
 | 	opt = get_boot_option(fake_cmdline, "-num", param, sizeof(param)); | 
 | 	KT_ASSERT_M("Unable to parse -num option", opt); | 
 | 	KT_ASSERT_M("Invalid -num option value", strcmp(param, "123") == 0); | 
 |  | 
 | 	opt = get_boot_option(fake_cmdline, "-quoted", param, sizeof(param)); | 
 | 	KT_ASSERT_M("Unable to parse -quoted option", opt); | 
 | 	KT_ASSERT_M("Invalid -quoted option value", strcmp(param, "abc '") == | 
 | 		    0); | 
 |  | 
 | 	opt = get_boot_option(fake_cmdline, "-dup", param, sizeof(param)); | 
 | 	KT_ASSERT_M("Unable to parse -dup option", opt); | 
 | 	KT_ASSERT_M("Invalid -dup option first value", strcmp(param, "311") == | 
 | 		    0); | 
 |  | 
 | 	opt = get_boot_option(opt, "-dup", param, sizeof(param)); | 
 | 	KT_ASSERT_M("Unable to parse -dup option", opt); | 
 | 	KT_ASSERT_M("Invalid -dup option second value", | 
 | 				strcmp(param, "akaros") == 0); | 
 |  | 
 | 	opt = get_boot_option(fake_cmdline, "-inner", param, sizeof(param)); | 
 | 	KT_ASSERT_M("Unable to parse -inner option", opt); | 
 | 	KT_ASSERT_M("Invalid -inner option value", strcmp(param, "-outer") == | 
 | 		    0); | 
 |  | 
 | 	opt = get_boot_option(opt, "-inner", param, sizeof(param)); | 
 | 	KT_ASSERT_M("Should not be parsing -inner as value", !opt); | 
 |  | 
 | 	opt = get_boot_option(fake_cmdline, "-outer", param, sizeof(param)); | 
 | 	KT_ASSERT_M("Unable to parse -outer option", opt); | 
 | 	KT_ASSERT_M("Invalid -outer option value", | 
 | 				strcmp(param, "-inner=xyz") == 0); | 
 |  | 
 | 	opt = get_boot_option(fake_cmdline, "-missing", param, sizeof(param)); | 
 | 	KT_ASSERT_M("Should not be parsing -missing option", !opt); | 
 |  | 
 | 	opt = get_boot_option(fake_cmdline, "-inne", NULL, 0); | 
 | 	KT_ASSERT_M("Should not be parsing -inne option", !opt); | 
 |  | 
 | 	opt = get_boot_option(fake_cmdline, "-outera", NULL, 0); | 
 | 	KT_ASSERT_M("Should not be parsing -outera option", !opt); | 
 |  | 
 | 	opt = get_boot_option(fake_cmdline, "-empty", param, sizeof(param)); | 
 | 	KT_ASSERT_M("Unable to parse -empty option", opt); | 
 | 	KT_ASSERT_M("Invalid -empty option value", strcmp(param, "") == 0); | 
 |  | 
 | 	return TRUE; | 
 | } | 
 |  | 
 | static bool __pcpu_ptr_is_dyn(void *ptr) | 
 | { | 
 | 	char *p_c = ptr; | 
 |  | 
 | 	return (PERCPU_STOP_VAR <= p_c) && | 
 | 	       (p_c < PERCPU_STOP_VAR + PERCPU_DYN_SIZE); | 
 | } | 
 |  | 
 | static bool test_percpu_zalloc(void) | 
 | { | 
 | 	uint8_t *u8 = percpu_zalloc(*u8, MEM_WAIT); | 
 | 	uint64_t *u64 = percpu_zalloc(uint64_t, MEM_WAIT); | 
 | 	uint32_t *u32 = percpu_zalloc(uint32_t, MEM_WAIT); | 
 | 	uint64_t *old_u64; | 
 |  | 
 | 	KT_ASSERT(__pcpu_ptr_is_dyn(u8)); | 
 | 	KT_ASSERT(__pcpu_ptr_is_dyn(u64)); | 
 | 	KT_ASSERT(__pcpu_ptr_is_dyn(u32)); | 
 |  | 
 | 	/* The order here is a bit hokey too - the first alloc is usually 16 | 
 | 	 * byte aligned, so if we did a packed alloc, the u64 wouldn't be | 
 | 	 * aligned. */ | 
 | 	KT_ASSERT(ALIGNED(u8, __alignof__(*u8))); | 
 | 	KT_ASSERT(ALIGNED(u64, __alignof__(*u64))); | 
 | 	KT_ASSERT(ALIGNED(u32, __alignof__(*u32))); | 
 |  | 
 | 	/* Testing zalloc.  Though the first alloc ever is likely to be zero. */ | 
 | 	for_each_core(i) | 
 | 		KT_ASSERT(_PERCPU_VAR(*u64, i) == 0); | 
 | 	for_each_core(i) | 
 | 		_PERCPU_VAR(*u64, i) = i; | 
 | 	for_each_core(i) | 
 | 		KT_ASSERT(_PERCPU_VAR(*u64, i) == i); | 
 | 	/* If we free and realloc, we're likely to get the same one.  This is | 
 | 	 * due to the ARENA_BESTFIT policy with xalloc. */ | 
 | 	old_u64 = u64; | 
 | 	percpu_free(u64); | 
 | 	u64 = percpu_zalloc(uint64_t, MEM_WAIT); | 
 | 	/* If this trips, then we didn't test this as well as we'd like. */ | 
 | 	warn_on(u64 != old_u64); | 
 | 	for_each_core(i) | 
 | 		KT_ASSERT(_PERCPU_VAR(*u64, i) == 0); | 
 |  | 
 | 	/* Yes, if an assert failed, we leak memory. */ | 
 | 	percpu_free(u8); | 
 | 	percpu_free(u64); | 
 | 	percpu_free(u32); | 
 | 	return true; | 
 | } | 
 |  | 
 | static void __inc_foo(uint32_t srcid, long a0, long a1, long a2) | 
 | { | 
 | 	uint64_t *foos = (uint64_t*)a0; | 
 | 	atomic_t *check_in_p = (atomic_t*)a1; | 
 |  | 
 | 	for (int i = 0; i < core_id() + 1; i++) | 
 | 		PERCPU_VAR(*foos)++; | 
 | 	cmb(); | 
 | 	atomic_dec(check_in_p); | 
 | } | 
 |  | 
 | static bool test_percpu_increment(void) | 
 | { | 
 | 	uint64_t *foos = percpu_zalloc(uint64_t, MEM_WAIT); | 
 | 	atomic_t check_in; | 
 |  | 
 | 	atomic_set(&check_in, num_cores); | 
 | 	for_each_core(i) | 
 | 		send_kernel_message(i, __inc_foo, (long)foos, (long)&check_in, | 
 | 				    0, KMSG_IMMEDIATE); | 
 | 	while (atomic_read(&check_in)) | 
 | 		cpu_relax(); | 
 | 	for_each_core(i) | 
 | 		KT_ASSERT(_PERCPU_VAR(*foos, i) == i + 1); | 
 | 	/* Yes, if an assert failed, we leak memory. */ | 
 | 	percpu_free(foos); | 
 | 	return true; | 
 | } | 
 |  | 
 | static struct ktest ktests[] = { | 
 | #ifdef CONFIG_X86 | 
 | 	KTEST_REG(ipi_sending,        CONFIG_TEST_ipi_sending), | 
 | 	KTEST_REG(pic_reception,      CONFIG_TEST_pic_reception), | 
 | 	KTEST_REG(lapic_status_bit,   CONFIG_TEST_lapic_status_bit), | 
 | 	KTEST_REG(pit,                CONFIG_TEST_pit), | 
 | 	KTEST_REG(circ_buffer,        CONFIG_TEST_circ_buffer), | 
 | 	KTEST_REG(kernel_messages,    CONFIG_TEST_kernel_messages), | 
 | #endif // CONFIG_X86 | 
 | 	KTEST_REG(barrier,            CONFIG_TEST_barrier), | 
 | 	KTEST_REG(interrupts_irqsave, CONFIG_TEST_interrupts_irqsave), | 
 | 	KTEST_REG(bitmasks,           CONFIG_TEST_bitmasks), | 
 | 	KTEST_REG(checklists,         CONFIG_TEST_checklists), | 
 | 	KTEST_REG(smp_call_functions, CONFIG_TEST_smp_call_functions), | 
 | 	KTEST_REG(hashtable,          CONFIG_TEST_hashtable), | 
 | 	KTEST_REG(circular_buffer,    CONFIG_TEST_circular_buffer), | 
 | 	KTEST_REG(bcq,                CONFIG_TEST_bcq), | 
 | 	KTEST_REG(ucq,                CONFIG_TEST_ucq), | 
 | 	KTEST_REG(kthreads,           CONFIG_TEST_kthreads), | 
 | 	KTEST_REG(kref,               CONFIG_TEST_kref), | 
 | 	KTEST_REG(atomics,            CONFIG_TEST_atomics), | 
 | 	KTEST_REG(abort_halt,         CONFIG_TEST_abort_halt), | 
 | 	KTEST_REG(cv,                 CONFIG_TEST_cv), | 
 | 	KTEST_REG(memset,             CONFIG_TEST_memset), | 
 | 	KTEST_REG(setjmp,             CONFIG_TEST_setjmp), | 
 | 	KTEST_REG(apipe,              CONFIG_TEST_apipe), | 
 | 	KTEST_REG(rwlock,             CONFIG_TEST_rwlock), | 
 | 	KTEST_REG(rv,                 CONFIG_TEST_rv), | 
 | 	KTEST_REG(alarm,              CONFIG_TEST_alarm), | 
 | 	KTEST_REG(kmalloc_incref,     CONFIG_TEST_kmalloc_incref), | 
 | 	KTEST_REG(u16pool,            CONFIG_TEST_u16pool), | 
 | 	KTEST_REG(uaccess,            CONFIG_TEST_uaccess), | 
 | 	KTEST_REG(sort,               CONFIG_TEST_sort), | 
 | 	KTEST_REG(cmdline_parse,      CONFIG_TEST_cmdline_parse), | 
 | 	KTEST_REG(percpu_zalloc,      CONFIG_TEST_percpu_zalloc), | 
 | 	KTEST_REG(percpu_increment,   CONFIG_TEST_percpu_increment), | 
 | }; | 
 | static int num_ktests = sizeof(ktests) / sizeof(struct ktest); | 
 |  | 
 | static void __init register_pb_ktests(void) | 
 | { | 
 | 	REGISTER_KTESTS(ktests, num_ktests); | 
 | } | 
 | init_func_1(register_pb_ktests); |