diff --git a/headers/private/kernel/smp.h b/headers/private/kernel/smp.h index 42c1c7746f..ea88a93639 100644 --- a/headers/private/kernel/smp.h +++ b/headers/private/kernel/smp.h @@ -21,14 +21,18 @@ enum { SMP_MSG_GLOBAL_INVL_PAGE, SMP_MSG_RESCHEDULE, SMP_MSG_CPU_HALT, - SMP_MSG_1, + SMP_MSG_CALL_FUNCTION, }; enum { - SMP_MSG_FLAG_ASYNC = 0, - SMP_MSG_FLAG_SYNC, + SMP_MSG_FLAG_ASYNC = 0x0, + SMP_MSG_FLAG_SYNC = 0x1, + SMP_MSG_FLAG_FREE_ARG = 0x2, }; +typedef void (*smp_call_func)(uint32 data1, int32 currentCPU, uint32 data2, uint32 data3); + + #ifdef __cplusplus extern "C" { #endif diff --git a/src/system/kernel/arch/x86/arch_system_info.c b/src/system/kernel/arch/x86/arch_system_info.c index 387fb17616..13e963ab79 100644 --- a/src/system/kernel/arch/x86/arch_system_info.c +++ b/src/system/kernel/arch/x86/arch_system_info.c @@ -1,5 +1,5 @@ /* - * Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved. * Distributed under the terms of the MIT License. */ @@ -15,25 +15,44 @@ #include -extern void cpuid(uint32 selector, cpuid_info *info); -extern uint32 cv_factor; - - uint32 sCpuType; int32 sCpuRevision; int64 sCpuClockSpeed; -status_t -get_cpuid(cpuid_info *info, uint32 eaxRegister, uint32 cpuNum) +static bool +get_cpuid_for(cpuid_info *info, uint32 currentCPU, uint32 eaxRegister, uint32 forCPU) { - if (cpuNum >= (uint32)smp_get_num_cpus()) + if (currentCPU != forCPU) + return false; + + get_current_cpuid(info, eaxRegister); + return true; +} + + +status_t +get_cpuid(cpuid_info *info, uint32 eaxRegister, uint32 forCPU) +{ + uint32 numCPUs = (uint32)smp_get_num_cpus(); + cpu_status state; + + if (forCPU >= numCPUs) return B_BAD_VALUE; - // ToDo: cpu_num is ignored, we should use the call_all_cpus() function - // to fix this. + // prevent us from being rescheduled + state = disable_interrupts(); - return get_current_cpuid(info, eaxRegister); + // ToDo: as long as we only run on pentium-class systems, we can assume + // that the CPU supports cpuid. + + if (!get_cpuid_for(info, smp_get_current_cpu(), eaxRegister, forCPU)) { + smp_send_broadcast_ici(SMP_MSG_CALL_FUNCTION, (uint32)info, + eaxRegister, forCPU, (void *)get_cpuid_for, SMP_MSG_FLAG_SYNC); + } + + restore_interrupts(state); + return B_OK; } diff --git a/src/system/kernel/smp.c b/src/system/kernel/smp.c index 7d10e98bfd..86f77bbf80 100644 --- a/src/system/kernel/smp.c +++ b/src/system/kernel/smp.c @@ -184,9 +184,10 @@ release_spinlock(spinlock *lock) } -// finds a free message and gets it -// NOTE: has side effect of disabling interrupts -// return value is interrupt state +/** Finds a free message and gets it. + * NOTE: has side effect of disabling interrupts + * return value is the former interrupt state + */ static cpu_status find_free_message(struct smp_msg **msg) @@ -328,10 +329,10 @@ finish_message_processing(int currentCPU, struct smp_msg *msg, int source_mailbo release_spinlock(spinlock); - if (msg->data_ptr != NULL) + if ((msg->flags & SMP_MSG_FLAG_FREE_ARG) != 0 && msg->data_ptr != NULL) free(msg->data_ptr); - if (msg->flags == SMP_MSG_FLAG_SYNC) { + if (msg->flags & SMP_MSG_FLAG_SYNC) { msg->done = true; // the caller cpu should now free the message } else { @@ -373,7 +374,13 @@ process_pending_ici(int32 currentCPU) halt = true; dprintf("cpu %ld halted!\n", currentCPU); break; - case SMP_MSG_1: + case SMP_MSG_CALL_FUNCTION: + { + smp_call_func func = (smp_call_func)msg->data_ptr; + func(msg->data, currentCPU, msg->data2, msg->data3); + break; + } + default: dprintf("smp_intercpu_int_handler: got unknown message %ld\n", msg->message); } @@ -392,6 +399,9 @@ process_pending_ici(int32 currentCPU) } +// #pragma mark - + + int smp_intercpu_int_handler(void) { @@ -449,7 +459,7 @@ smp_send_ici(int32 targetCPU, int32 message, uint32 data, uint32 data2, uint32 d arch_smp_send_ici(targetCPU); - if (flags == SMP_MSG_FLAG_SYNC) { + if (flags & SMP_MSG_FLAG_SYNC) { // wait for the other cpu to finish processing it // the interrupt handler will ref count it to <0 // if the message is sync after it has removed it from the mailbox @@ -508,7 +518,7 @@ smp_send_broadcast_ici(int32 message, uint32 data, uint32 data2, uint32 data3, TRACE(("smp_send_broadcast_ici: sent interrupt\n")); - if (flags == SMP_MSG_FLAG_SYNC) { + if (flags & SMP_MSG_FLAG_SYNC) { // wait for the other cpus to finish processing it // the interrupt handler will ref count it to <0 // if the message is sync after it has removed it from the mailbox @@ -652,9 +662,18 @@ smp_get_current_cpu(void) void -call_all_cpus(void (*f)(void *, int), void *cookie) +call_all_cpus(void (*func)(void *, int), void *cookie) { - // ToDo: this is a dummy, but at least it works for single CPU machines - f(cookie, smp_get_current_cpu()); + cpu_status state = disable_interrupts(); + + if (smp_get_num_cpus() > 1) { + smp_send_broadcast_ici(SMP_MSG_CALL_FUNCTION, (uint32)cookie, + 0, 0, (void *)func, SMP_MSG_FLAG_SYNC); + } + + // we need to call this function ourselves as well + func(cookie, smp_get_current_cpu()); + + restore_interrupts(state); }