x86 core id reordering
authorBarret Rhoden <brho@cs.berkeley.edu>
Thu, 4 Feb 2010 01:07:27 +0000 (17:07 -0800)
committerBarret Rhoden <brho@cs.berkeley.edu>
Thu, 4 Feb 2010 01:22:17 +0000 (17:22 -0800)
Provides support for non-contiguous hardware core ids, since x86 encodes
topological information in their lapic ids.  If you use send_ipi, you
usually will want to convert to the hardware id using
get_hw_coreid(dest).

Also, if you want to send a group IPI (logical), there is a separate
function for that now.

kern/arch/i386/apic.h
kern/arch/i386/arch.h
kern/arch/i386/init.c
kern/arch/i386/pmap.c
kern/arch/i386/smp.c
kern/arch/i386/smp_boot.c
kern/arch/i386/trap.c
kern/src/testing.c

index 72f7b46..aa5d7b2 100644 (file)
@@ -120,7 +120,8 @@ static inline void send_startup_ipi(uint8_t vector);
 static inline void send_self_ipi(uint8_t vector);
 static inline void send_broadcast_ipi(uint8_t vector);
 static inline void send_all_others_ipi(uint8_t vector);
-static inline void send_ipi(uint8_t dest, bool logical_mode, uint8_t vector);
+static inline void send_ipi(uint8_t hw_coreid, uint8_t vector);
+static inline void send_group_ipi(uint8_t hw_groupid, uint8_t vector);
 
 #define mask_lapic_lvt(entry) \
        write_mmreg32(entry, read_mmreg32(entry) | LAPIC_LVT_MASK)
@@ -201,33 +202,56 @@ static inline void lapic_enable(void)
 
 static inline void send_init_ipi(void)
 {
+       lapic_wait_to_send();
        write_mmreg32(LAPIC_IPI_ICR_LOWER, 0x000c4500);
 }
 
 static inline void send_startup_ipi(uint8_t vector)
 {
+       lapic_wait_to_send();
        write_mmreg32(LAPIC_IPI_ICR_LOWER, 0x000c4600 | vector);
 }
 
 static inline void send_self_ipi(uint8_t vector)
 {
+       lapic_wait_to_send();
        write_mmreg32(LAPIC_IPI_ICR_LOWER, 0x00044000 | vector);
 }
 
 static inline void send_broadcast_ipi(uint8_t vector)
 {
+       lapic_wait_to_send();
        write_mmreg32(LAPIC_IPI_ICR_LOWER, 0x00084000 | vector);
 }
 
 static inline void send_all_others_ipi(uint8_t vector)
 {
+       lapic_wait_to_send();
        write_mmreg32(LAPIC_IPI_ICR_LOWER, 0x000c4000 | vector);
 }
 
-static inline void send_ipi(uint8_t dest, bool logical_mode, uint8_t vector)
+static inline void __send_ipi(uint8_t hw_coreid, uint8_t vector)
 {
-       write_mmreg32(LAPIC_IPI_ICR_UPPER, dest << 24);
-       write_mmreg32(LAPIC_IPI_ICR_LOWER, 0x00004000 | (logical_mode << 11) | vector);
+       lapic_wait_to_send();
+       write_mmreg32(LAPIC_IPI_ICR_UPPER, hw_coreid << 24);
+       write_mmreg32(LAPIC_IPI_ICR_LOWER, 0x00004000 | vector);
+}
+
+static inline void send_ipi(uint8_t hw_coreid, uint8_t vector)
+{
+       /* 255 is a broadcast, which should use send_broadcast_ipi, and it is also
+        * what would come in if you tried sending an IPI to an os_coreid that
+        * doesn't exist (since they are initialized to -1). */
+       if (hw_coreid == 255)
+               return;
+       __send_ipi(hw_coreid, vector);
+}
+
+static inline void send_group_ipi(uint8_t hw_groupid, uint8_t vector)
+{
+       lapic_wait_to_send();
+       write_mmreg32(LAPIC_IPI_ICR_UPPER, hw_groupid << 24);
+       write_mmreg32(LAPIC_IPI_ICR_LOWER, 0x00004800 | vector);
 }
 
 /* To change the LAPIC Base (not recommended):
index 6c8f95c..f9e812c 100644 (file)
@@ -1,8 +1,8 @@
 #ifndef ROS_INC_ARCH_H
 #define ROS_INC_ARCH_H
 
-#include <arch/x86.h>
 #include <ros/common.h>
+#include <arch/x86.h>
 #include <arch/trap.h>
 #include <arch/apic.h>
 
@@ -23,7 +23,10 @@ static __inline void cpu_relax(void) __attribute__((always_inline));
 static __inline void cpu_halt(void) __attribute__((always_inline));
 static __inline void clflush(uintptr_t* addr) __attribute__((always_inline));
 static __inline int irq_is_enabled(void) __attribute__((always_inline));
-static __inline uint32_t core_id(void) __attribute__((always_inline));
+static __inline int get_hw_coreid(int coreid);
+static __inline int hw_core_id(void) __attribute__((always_inline));
+static __inline int get_os_coreid(int hw_coreid);
+static __inline int core_id(void) __attribute__((always_inline));
 static __inline void cache_flush(void) __attribute__((always_inline));
 static __inline void reboot(void) __attribute__((always_inline)) __attribute__((noreturn));
 
@@ -31,6 +34,10 @@ void print_cpuinfo(void);
 void show_mapping(uintptr_t start, size_t size);
 void backtrace(void);
 
+/* declared in smp.c */
+int hw_coreid_lookup[MAX_NUM_CPUS];
+int os_coreid_lookup[MAX_NUM_CPUS];
+
 static __inline void
 breakpoint(void)
 {
@@ -133,12 +140,35 @@ irq_is_enabled(void)
        return read_eflags() & FL_IF;
 }
 
-static __inline uint32_t
-core_id(void)
+/* os_coreid -> hw_coreid */
+static __inline int
+get_hw_coreid(int coreid)
+{
+       return hw_coreid_lookup[coreid];
+}
+
+static __inline int
+hw_core_id(void)
 {
        return lapic_get_id();
 }
 
+/* hw_coreid -> os_coreid */
+static __inline int
+get_os_coreid(int hw_coreid)
+{
+       return os_coreid_lookup[hw_coreid];
+}
+
+/* core_id() returns the OS core number, not to be confused with the
+ * hardware-specific core identifier (such as the lapic id) returned by
+ * hw_core_id() */
+static __inline int
+core_id(void)
+{
+       return get_os_coreid(hw_core_id());
+}
+
 static __inline void
 cache_flush(void)
 {
index b4a7e35..c98fbee 100644 (file)
@@ -21,7 +21,11 @@ void arch_init()
        // TODO: move these back to regular init.  requires fixing the __NETWORK__
        // inits to not need multiple cores running.
        // this returns when all other cores are done and ready to receive IPIs
-       smp_boot();
+       #ifndef __SINGLE_CORE__
+               smp_boot();
+       #else
+               smp_percpu_init();
+       #endif
        proc_init();
 
        /* EXPERIMENTAL NETWORK FUNCTIONALITY
index 9cf620f..602c884 100644 (file)
@@ -417,10 +417,6 @@ vm_init(void)
 
        // Final mapping: KERNBASE+x => KERNBASE+x => x.
 
-       // need to store this for access to change the LDT later
-       assert(core_id() == 0);
-       per_cpu_info[core_id()].gdt = gdt;
-
        // This mapping was only used after paging was turned on but
        // before the segment registers were reloaded.
        pgdir[0] = 0;
index 84a1d35..ae50b13 100644 (file)
 #include <env.h>
 #include <trap.h>
 
+/* Lookup table for core_id and per_cpu_inf, indexed by real __core_id() */
+int hw_coreid_lookup[MAX_NUM_CPUS] = {[0 ... (MAX_NUM_CPUS - 1)] -1};
+int os_coreid_lookup[MAX_NUM_CPUS] = {[0 ... (MAX_NUM_CPUS - 1)] -1};
+
 /*************************** 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
@@ -45,7 +49,7 @@ static int smp_call_function(uint8_t type, uint32_t dest, poly_isr_t handler, TV
 
        // assumes our cores are numbered in order
        if ((type == 4) && (dest >= num_cpus))
-               panic("Destination CPU does not exist!");
+               panic("Destination CPU %d does not exist!", dest);
 
        // build the mask based on the type and destination
        INIT_CHECKLIST_MASK(cpu_mask, MAX_NUM_CPUS);
@@ -127,10 +131,10 @@ static int smp_call_function(uint8_t type, uint32_t dest, poly_isr_t handler, TV
                        send_all_others_ipi(wrapper->vector);
                        break;
                case 4: // physical mode
-                       send_ipi(dest, 0, wrapper->vector);
+                       send_ipi(get_hw_coreid(dest), wrapper->vector);
                        break;
                case 5: // logical mode
-                       send_ipi(dest, 1, wrapper->vector);
+                       send_group_ipi(dest, wrapper->vector);
                        break;
                default:
                        panic("Invalid type for cross-core function call!");
index 0036595..5a68842 100644 (file)
@@ -59,12 +59,13 @@ static void init_smp_call_function(void)
 /******************************************************************************/
 
 #ifdef __IVY__
-static void smp_mtrr_handler(trapframe_t *tf, barrier_t *data)
+static void smp_final_core_init(trapframe_t *tf, barrier_t *data)
 #else
-static void smp_mtrr_handler(trapframe_t *tf, void *data)
+static void smp_final_core_init(trapframe_t *tf, void *data)
 #endif
 {
        setup_default_mtrrs(data);
+       smp_percpu_init();
 }
 
 // this needs to be set in smp_entry too...
@@ -86,8 +87,33 @@ get_smp_bootlock()
        return (uint32_t *COUNT(1))TC(smp_boot_lock - smp_entry + trampoline_pg);
 }
 
+/* hw_coreid_lookup will get packed, but keep it's hw values.  
+ * os_coreid_lookup will remain sparse, but it's values will be consecutive.
+ * for both arrays, -1 means an empty slot.  hw_step tracks the next valid entry
+ * in hw_coreid_lookup, jumping over gaps of -1's. */
+static void smp_remap_coreids(void)
+{
+       for (int i = 0, hw_step = 0; i < num_cpus; i++, hw_step++) {
+               if (hw_coreid_lookup[i] == -1) {
+                       while (hw_coreid_lookup[hw_step] == -1) {
+                               hw_step++;
+                               if (hw_step == MAX_NUM_CPUS)
+                                       panic("Mismatch in num_cpus and hw_step");
+                       }
+                       hw_coreid_lookup[i] = hw_coreid_lookup[hw_step];
+                       hw_coreid_lookup[hw_step] = -1;
+                       os_coreid_lookup[hw_step] = i;
+               }
+       }
+}
+
 void smp_boot(void)
 {
+       /* set core0's mappings */
+       assert(lapic_get_id() == 0);
+       os_coreid_lookup[0] = 0;
+       hw_coreid_lookup[0] = 0;
+
        page_t *smp_stack;
        // NEED TO GRAB A LOWMEM FREE PAGE FOR AP BOOTUP CODE
        // page1 (2nd page) is reserved, hardcoded in pmap.c
@@ -134,6 +160,7 @@ void smp_boot(void)
        // on the trampoline (which we must be careful to not deallocate)
        __spin_lock(get_smp_bootlock());
        cprintf("Num_Cpus Detected: %d\n", num_cpus);
+       smp_remap_coreids();
 
        // Remove the mapping of the page used by the trampoline
        page_remove(boot_pgdir, (void*SNT)trampoline_pg);
@@ -150,13 +177,10 @@ void smp_boot(void)
        // Set up the generic remote function call facility
        init_smp_call_function();
 
-       // Set up all cores to use the proper MTRRs
+       /* Final core initialization */
        barrier_t generic_barrier;
-       init_barrier(&generic_barrier, num_cpus); // barrier used by smp_mtrr_handler
-       smp_call_function_all(smp_mtrr_handler, &generic_barrier, 0);
-
-       // initialize my per-cpu info
-       smp_percpu_init();
+       init_barrier(&generic_barrier, num_cpus);
+       smp_call_function_all(smp_final_core_init, &generic_barrier, 0);
 
        // Should probably flush everyone's TLB at this point, to get rid of
        // temp mappings that were removed.  TODO
@@ -185,10 +209,12 @@ get_my_ts(page_t *my_stack)
                sizeof(taskstate_t);
 }
 
-/*
- * This is called from smp_entry by each core to finish the core bootstrapping.
- * There is a spinlock around this entire function in smp_entry, for a few reasons,
- * the most important being that all cores use the same stack when entering here.
+/* This is called from smp_entry by each core to finish the core bootstrapping.
+ * There is a spinlock around this entire function in smp_entry, for a few
+ * reasons, the most important being that all cores use the same stack when
+ * entering here.
+ *
+ * Do not use per_cpu_info in here.  Do whatever you need in smp_percpu_init().
  */
 uint32_t smp_main(void)
 {
@@ -203,6 +229,10 @@ uint32_t smp_main(void)
                cprintf("I am an Application Processor\n");
        cprintf("Num_Cpus: %d\n\n", num_cpus);
        */
+       /* set up initial mappings.  core0 will adjust it later */
+       unsigned long my_hw_id = lapic_get_id();
+       os_coreid_lookup[my_hw_id] = my_hw_id;
+       hw_coreid_lookup[my_hw_id] = my_hw_id;
 
        // Get a per-core kernel stack
        page_t *my_stack;
@@ -215,7 +245,6 @@ uint32_t smp_main(void)
        // GDT should be 4-byte aligned.  TS isn't aligned.  Not sure if it matters.
        pseudodesc_t *my_gdt_pd = get_my_gdt_pd(my_stack);
        segdesc_t *COUNT(SEG_COUNT) my_gdt = get_my_gdt(my_stack);
-       per_cpu_info[core_id()].gdt = my_gdt;
        // TS also needs to be permanent
        taskstate_t *my_ts = get_my_ts(my_stack);
        // Usable portion of the KSTACK grows down from here
@@ -259,17 +288,23 @@ uint32_t smp_main(void)
        // set a default logical id for now
        lapic_set_logid(lapic_get_id());
 
-       // initialize my per-cpu info
-       smp_percpu_init();
-
        return my_stack_top; // will be loaded in smp_entry.S
 }
 
-/* Perform any initialization needed by per_cpu_info.  Right now, this just
- * inits the amsg list (which sparc will probably also want).  Make sure every
- * core calls this at some point in the smp_boot process. */
+/* Perform any initialization needed by per_cpu_info.  Make sure every core
+ * calls this at some point in the smp_boot process.  If you don't smp_boot, you
+ * must still call this for core 0.  This must NOT be called from smp_main,
+ * since it relies on the kernel stack pointer to find the gdt.  Be careful not
+ * to call it on too deep of a stack frame. */
 void smp_percpu_init(void)
 {
        uint32_t coreid = core_id();
+
+       /* core 0 sets up via the global gdt symbol */
+       if (!coreid)
+               per_cpu_info[0].gdt = gdt;
+       else
+               per_cpu_info[coreid].gdt = (segdesc_t*)(ROUNDUP(read_esp(), PGSIZE)
+                                          - sizeof(segdesc_t)*SEG_COUNT);
        STAILQ_INIT(&per_cpu_info[coreid].active_msgs);
 }
index b4cac03..8915ca8 100644 (file)
@@ -399,7 +399,7 @@ uint32_t send_active_message(uint32_t dst, amr_t pc,
        spin_unlock_irqsave(&per_cpu_info[dst].amsg_lock);
        // since we touched memory the other core will touch (the lock), we don't
        // need an wmb_f()
-       send_ipi(dst, 0, I_ACTIVE_MSG);
+       send_ipi(get_hw_coreid(dst), I_ACTIVE_MSG);
        return 0;
 }
 
index f91eec3..4fe2b0c 100644 (file)
@@ -54,22 +54,22 @@ void test_ipi_sending(void)
        send_self_ipi(I_TESTING);
        udelay(3000000);
        cprintf("\nCORE 0 sending ipi to physical 1\n");
-       send_ipi(0x01, 0, I_TESTING);
+       send_ipi(get_hw_coreid(0x01), I_TESTING);
        udelay(3000000);
        cprintf("\nCORE 0 sending ipi to physical 2\n");
-       send_ipi(0x02, 0, I_TESTING);
+       send_ipi(get_hw_coreid(0x02), I_TESTING);
        udelay(3000000);
        cprintf("\nCORE 0 sending ipi to physical 3\n");
-       send_ipi(0x03, 0, I_TESTING);
+       send_ipi(get_hw_coreid(0x03), I_TESTING);
        udelay(3000000);
        cprintf("\nCORE 0 sending ipi to physical 15\n");
-       send_ipi(0x0f, 0, I_TESTING);
+       send_ipi(get_hw_coreid(0x0f), I_TESTING);
        udelay(3000000);
        cprintf("\nCORE 0 sending ipi to logical 2\n");
-       send_ipi(0x02, 1, I_TESTING);
+       send_group_ipi(0x02, I_TESTING);
        udelay(3000000);
        cprintf("\nCORE 0 sending ipi to logical 1\n");
-       send_ipi(0x01, 1, I_TESTING);
+       send_group_ipi(0x01, I_TESTING);
        udelay(3000000);
        cprintf("\nDone!\n");
        disable_irqsave(&state);
@@ -550,7 +550,7 @@ void test_lapic_status_bit(void)
        atomic_set(&a,0);
        printk("IPIs received (should be 0): %d\n", a);
        for(int i = 0; i < NUM_IPI; i++) {
-               send_ipi(7, 0, I_TESTING);
+               send_ipi(get_hw_coreid(7), I_TESTING);
                lapic_wait_to_send();
        }
        // need to wait a bit to let those IPIs get there
@@ -722,6 +722,7 @@ void test_print_info_handler(trapframe_t *tf, void* data)
        cprintf("----------------------------\n");
        cprintf("This is Core %d\n", core_id());
 #ifdef __i386__
+       cprintf("Hardware core %d\n", hw_core_id());
        cprintf("MTRR_DEF_TYPE = 0x%08x\n", read_msr(IA32_MTRR_DEF_TYPE));
        cprintf("MTRR Phys0 Base = 0x%016llx, Mask = 0x%016llx\n",
                read_msr(0x200), read_msr(0x201));