| /* | 
 |  * Copyright (c) 2009 The Regents of the University of California | 
 |  * Barret Rhoden <brho@cs.berkeley.edu> | 
 |  * See LICENSE for details. | 
 |  */ | 
 |  | 
 | #include <arch/arch.h> | 
 | #include <arch/topology.h> | 
 | #include <bitmask.h> | 
 | #include <smp.h> | 
 |  | 
 | #include <atomic.h> | 
 | #include <error.h> | 
 | #include <stdio.h> | 
 | #include <string.h> | 
 | #include <assert.h> | 
 | #include <pmap.h> | 
 | #include <env.h> | 
 | #include <trap.h> | 
 |  | 
 | /*************************** IPI Wrapper Stuff ********************************/ | 
 | // checklists to protect the global interrupt_handlers for 0xf0, f1, f2, f3, f4 | 
 | // need to be global, since there is no function that will always exist for them | 
 | handler_wrapper_t handler_wrappers[NUM_HANDLER_WRAPPERS]; | 
 |  | 
 | static int smp_call_function(uint8_t type, uint32_t dest, isr_t handler, | 
 |                              void *data, handler_wrapper_t **wait_wrapper) | 
 | { | 
 | 	int8_t state = 0; | 
 | 	uint32_t wrapper_num; | 
 | 	handler_wrapper_t* wrapper; | 
 | 	extern atomic_t outstanding_calls; | 
 |  | 
 | 	// prevents us from ever having more than NUM_HANDLER_WRAPPERS callers | 
 | 	// in the process of competing for vectors.  not decremented until both | 
 | 	// after the while(1) loop and after it's been waited on. | 
 | 	atomic_inc(&outstanding_calls); | 
 | 	if (atomic_read(&outstanding_calls) > NUM_HANDLER_WRAPPERS) { | 
 | 		atomic_dec(&outstanding_calls); | 
 | 		return -EBUSY; | 
 | 	} | 
 |  | 
 | 	// assumes our cores are numbered in order | 
 | 	if ((type == 4) && (dest >= num_cores)) | 
 | 		panic("Destination CPU %d does not exist!", dest); | 
 |  | 
 | 	// build the mask based on the type and destination | 
 | 	INIT_CHECKLIST_MASK(cpu_mask, MAX_NUM_CORES); | 
 | 	// set checklist mask's size dynamically to the num cpus actually | 
 | 	// present | 
 | 	cpu_mask.size = num_cores; | 
 | 	switch (type) { | 
 | 	case 1: // self | 
 | 		SET_BITMASK_BIT(cpu_mask.bits, core_id()); | 
 | 		break; | 
 | 	case 2: // all | 
 | 		FILL_BITMASK(cpu_mask.bits, num_cores); | 
 | 		break; | 
 | 	case 3: // all but self | 
 | 		FILL_BITMASK(cpu_mask.bits, num_cores); | 
 | 		CLR_BITMASK_BIT(cpu_mask.bits, core_id()); | 
 | 		break; | 
 | 	case 4: // physical mode | 
 | 		// note this only supports sending to one specific physical id | 
 | 		// (only sets one bit, so if multiple cores have the same phys | 
 | 		// id the first one through will set this). | 
 | 		SET_BITMASK_BIT(cpu_mask.bits, dest); | 
 | 		break; | 
 | 	case 5: // logical mode | 
 | 		// TODO | 
 | 		warn("Logical mode bitmask handler protection unimplemented!"); | 
 | 		break; | 
 | 	default: | 
 | 		panic("Invalid type for cross-core function call!"); | 
 | 	} | 
 |  | 
 | 	// Find an available vector/wrapper.  Starts with this core's id (mod | 
 | 	// the number of wrappers).  Walk through on conflict. | 
 | 	// Commit returns an error if it wanted to give up for some reason, | 
 | 	// like taking too long to acquire the lock or clear the mask, at which | 
 | 	// point, we try the next one. | 
 | 	// When we are done, wrapper points to the one we finally got. | 
 | 	// this wrapper_num trick doesn't work as well if you send a bunch in a | 
 | 	// row and wait, since you always check your main one (which is | 
 | 	// currently busy). | 
 | 	wrapper_num = core_id() % NUM_HANDLER_WRAPPERS; | 
 | 	while(1) { | 
 | 		wrapper = &handler_wrappers[wrapper_num]; | 
 | 		if (!commit_checklist_wait(wrapper->cpu_list, &cpu_mask)) | 
 | 			break; | 
 | 		wrapper_num = (wrapper_num + 1) % NUM_HANDLER_WRAPPERS; | 
 | 		/* | 
 | 		uint32_t count = 0; | 
 | 		// instead of deadlock, smp_call can fail with this.  makes it | 
 | 		// harder to use (have to check your return value).  consider | 
 | 		// putting a delay here too (like if wrapper_num == | 
 | 		// initial_wrapper_num) | 
 | 		// note 1000 isn't enough... | 
 | 		if (count++ > NUM_HANDLER_WRAPPERS * 1000) | 
 | 			return -EBUSY; | 
 | 		*/ | 
 | 	} | 
 |  | 
 | 	// Wanting to wait is expressed by having a non-NULL handler_wrapper_t** | 
 | 	// passed in.  Pass out our reference to wrapper, to wait later.  If we | 
 | 	// don't want to wait, release the checklist (though it is still not | 
 | 	// clear, so it can't be used til everyone checks in). | 
 | 	if (wait_wrapper) | 
 | 		*wait_wrapper = wrapper; | 
 | 	else { | 
 | 		release_checklist(wrapper->cpu_list); | 
 | 		atomic_dec(&outstanding_calls); | 
 | 	} | 
 |  | 
 | 	/* TODO: once we can unregister, we can reregister.  This here assumes | 
 | 	 * that there is only one IRQ registered, and its the one for SMP call | 
 | 	 * function.  We're waiting on RCU to do a nice unregister. */ | 
 | 	extern struct irq_handler *irq_handlers[]; | 
 |  | 
 | 	if (!irq_handlers[wrapper->vector]) { | 
 | 		register_irq(wrapper->vector, handler, data, MKBUS(BusIPI, 0, 0, | 
 | 								   0)); | 
 | 	} else { | 
 | 		/* we're replacing the old one.  hope it was ours, and the IRQ | 
 | 		 * is firing concurrently (if it is, there's an smp_call bug)! | 
 | 		 * */ | 
 | 		irq_handlers[wrapper->vector]->isr = handler; | 
 | 		irq_handlers[wrapper->vector]->data = data; | 
 | 	} | 
 |  | 
 | 	// WRITE MEMORY BARRIER HERE | 
 | 	enable_irqsave(&state); | 
 | 	// Send the proper type of IPI.  I made up these numbers. | 
 | 	switch (type) { | 
 | 	case 1: | 
 | 		send_self_ipi(wrapper->vector); | 
 | 		break; | 
 | 	case 2: | 
 | 		send_broadcast_ipi(wrapper->vector); | 
 | 		break; | 
 | 	case 3: | 
 | 		send_all_others_ipi(wrapper->vector); | 
 | 		break; | 
 | 	case 4: // physical mode | 
 | 		send_ipi(dest, wrapper->vector); | 
 | 		break; | 
 | 	case 5: // logical mode | 
 | 		send_group_ipi(dest, wrapper->vector); | 
 | 		break; | 
 | 	default: | 
 | 		panic("Invalid type for cross-core function call!"); | 
 | 	} | 
 | 	// wait long enough to receive our own broadcast (PROBABLY WORKS) TODO | 
 | 	disable_irqsave(&state); | 
 | 	return 0; | 
 | } | 
 |  | 
 | // Wrapper functions.  Add more as they are needed. | 
 | int smp_call_function_self(isr_t handler, void *data, | 
 |                            handler_wrapper_t **wait_wrapper) | 
 | { | 
 | 	return smp_call_function(1, 0, handler, data, wait_wrapper); | 
 | } | 
 |  | 
 | int smp_call_function_all(isr_t handler, void *data, | 
 |                           handler_wrapper_t **wait_wrapper) | 
 | { | 
 | 	return smp_call_function(2, 0, handler, data, wait_wrapper); | 
 | } | 
 |  | 
 | int smp_call_function_single(uint32_t dest, isr_t handler, void *data, | 
 |                              handler_wrapper_t **wait_wrapper) | 
 | { | 
 | 	return smp_call_function(4, dest, handler, data, wait_wrapper); | 
 | } | 
 |  | 
 | // If you want to wait, pass the address of a pointer up above, then call | 
 | // this to do the actual waiting.  Be somewhat careful about uninitialized | 
 | // or old wrapper pointers. | 
 | int smp_call_wait(handler_wrapper_t* wrapper) | 
 | { | 
 | 	if (wrapper) { | 
 | 		waiton_checklist(wrapper->cpu_list); | 
 | 		return 0; | 
 | 	} else { | 
 | 		warn("Attempting to wait on null wrapper!  Check your return values!"); | 
 | 		return -EFAIL; | 
 | 	} | 
 | } | 
 |  |