#include <sys/cpumask.h>
#include <sys/interrupt.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/memrange.h>
#include <sys/tls.h>
#include <sys/types.h>
#include <vm/vm_extern.h>
#include <vm/vm_kern.h>
#include <vm/vm_object.h>
#include <vm/vm_page.h>
#include <sys/mplock2.h>
#include <sys/thread2.h>
#include <machine/cpu.h>
#include <machine/cpufunc.h>
#include <machine/globaldata.h>
#include <machine/md_var.h>
#include <machine/pmap.h>
#include <machine/smp.h>
#include <machine/tls.h>
#include <machine/param.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
extern pt_entry_t *KPTphys;
volatile cpumask_t stopped_cpus;
cpumask_t smp_active_mask = CPUMASK_INITIALIZER_ONLYONE;
static int boot_address;
static cpumask_t smp_startup_mask = CPUMASK_INITIALIZER_ONLYONE;
static int mp_finish;
static int core_bits = 0;
static int logical_CPU_bits = 0;
void bootstrap_idle(void);
void single_cpu_ipi(int, int, int);
void selected_cpu_ipi(cpumask_t, int, int);
#if 0
void ipi_handler(int);
#endif
pt_entry_t *SMPpt;
char *bootSTK;
static int bootAP;
static int start_all_aps(u_int);
void init_secondary(void);
void *start_ap(void *);
static
void
ap_finish(void)
{
mp_finish = 1;
if (bootverbose)
kprintf("Finish MP startup\n");
mycpu->gd_other_cpus = smp_startup_mask;
CPUMASK_NANDBIT(mycpu->gd_other_cpus, mycpu->gd_cpuid);
rel_mplock();
while (CPUMASK_CMPMASKNEQ(smp_active_mask,smp_startup_mask)) {
DELAY(100000);
cpu_lfence();
}
while (try_mplock() == 0)
DELAY(100000);
if (bootverbose)
kprintf("Active CPU Mask: %08lx\n",
(long)CPUMASK_LOWMASK(smp_active_mask));
}
SYSINIT(finishsmp, SI_BOOT2_FINISH_SMP, SI_ORDER_FIRST, ap_finish, NULL);
void *
start_ap(void *arg __unused)
{
init_secondary();
setrealcpu();
bootstrap_idle();
return(NULL);
}
pthread_t ap_tids[MAXCPU];
int naps;
void
mp_start(void)
{
size_t ipiq_size;
int shift;
ncpus = optcpus;
naps = ncpus - 1;
for (shift = 0; (1 << shift) <= ncpus; ++shift)
;
--shift;
if ((1 << shift) < ncpus)
++shift;
ncpus_fit = 1 << shift;
ncpus_fit_mask = ncpus_fit - 1;
malloc_reinit_ncpus();
ipiq_size = sizeof(struct lwkt_ipiq) * ncpus;
mycpu->gd_ipiq = (void *)kmem_alloc(kernel_map, ipiq_size,
VM_SUBSYS_IPIQ);
bzero(mycpu->gd_ipiq, ipiq_size);
arc4_init_pcpu(0);
start_all_aps(boot_address);
}
void
mp_announce(void)
{
int x;
kprintf("DragonFly/MP: Multiprocessor\n");
kprintf(" cpu0 (BSP)\n");
for (x = 1; x <= naps; ++x)
kprintf(" cpu%d (AP)\n", x);
}
void
cpu_send_ipiq(int dcpu)
{
if (CPUMASK_TESTBIT(smp_active_mask, dcpu)) {
if (pthread_kill(ap_tids[dcpu], SIGUSR1) != 0)
panic("pthread_kill failed in cpu_send_ipiq");
}
#if 0
panic("XXX cpu_send_ipiq()");
#endif
}
void
single_cpu_ipi(int cpu, int vector, int delivery_mode)
{
kprintf("XXX single_cpu_ipi\n");
}
void
selected_cpu_ipi(cpumask_t target, int vector, int delivery_mode)
{
crit_enter();
while (CPUMASK_TESTNZERO(target)) {
int n = BSFCPUMASK(target);
CPUMASK_NANDBIT(target, n);
single_cpu_ipi(n, vector, delivery_mode);
}
crit_exit();
}
int
stop_cpus(cpumask_t map)
{
CPUMASK_ANDMASK(map, smp_active_mask);
crit_enter();
while (CPUMASK_TESTNZERO(map)) {
int n = BSFCPUMASK(map);
CPUMASK_NANDBIT(map, n);
ATOMIC_CPUMASK_ORBIT(stopped_cpus, n);
if (pthread_kill(ap_tids[n], SIGXCPU) != 0)
panic("stop_cpus: pthread_kill failed");
}
crit_exit();
#if 0
panic("XXX stop_cpus()");
#endif
return(1);
}
int
restart_cpus(cpumask_t map)
{
CPUMASK_ANDMASK(map, smp_active_mask);
crit_enter();
while (CPUMASK_TESTNZERO(map)) {
int n = BSFCPUMASK(map);
CPUMASK_NANDBIT(map, n);
ATOMIC_CPUMASK_NANDBIT(stopped_cpus, n);
if (pthread_kill(ap_tids[n], SIGXCPU) != 0)
panic("restart_cpus: pthread_kill failed");
}
crit_exit();
#if 0
panic("XXX restart_cpus()");
#endif
return(1);
}
void
ap_init(void)
{
ATOMIC_CPUMASK_ORBIT(smp_startup_mask, mycpu->gd_cpuid);
cpu_mfence();
while (mp_finish == 0) {
cpu_lfence();
DELAY(500000);
}
while (try_mplock() == 0)
DELAY(100000);
cpu_invltlb();
mycpu->gd_other_cpus = smp_startup_mask;
CPUMASK_NANDBIT(mycpu->gd_other_cpus, mycpu->gd_cpuid);
kprintf("SMP: AP CPU #%d Launched!\n", mycpu->gd_cpuid);
mem_range_AP_init();
KKASSERT(get_mplock_count(curthread) == 1);
ATOMIC_CPUMASK_ORBIT(smp_active_mask, mycpu->gd_cpuid);
mdcpu->gd_fpending = 0;
mdcpu->gd_ipending = 0;
initclocks_pcpu();
atomic_swap_int(&mycpu->gd_npoll, 0);
lwkt_process_ipiq();
rel_mplock();
KKASSERT((curthread->td_flags & TDF_RUNQ) == 0);
}
void
init_secondary(void)
{
int myid = bootAP;
struct mdglobaldata *md;
struct privatespace *ps;
ps = &CPU_prvspace[myid];
KKASSERT(ps->mdglobaldata.mi.gd_prvspace == ps);
tls_set_gs(&CPU_prvspace[myid], sizeof(struct privatespace));
md = mdcpu;
md->gd_common_tss.tss_rsp0 = 0;
}
static int
start_all_aps(u_int boot_addr)
{
int x, i;
struct mdglobaldata *gd;
struct privatespace *ps;
vm_page_t m;
vm_offset_t va;
pthread_attr_t attr;
size_t ipiq_size;
#if 0
struct lwp_params params;
#endif
ap_tids[0] = pthread_self();
pthread_attr_init(&attr);
vm_object_hold(kernel_object);
for (x = 1; x <= naps; ++x) {
for (i = 0; i < sizeof(struct mdglobaldata); i += PAGE_SIZE) {
va =(vm_offset_t)&CPU_prvspace[x].mdglobaldata + i;
m = vm_page_alloc(kernel_object, va, VM_ALLOC_SYSTEM);
pmap_kenter_quick(va, m->phys_addr);
}
for (i = 0; i < sizeof(CPU_prvspace[x].idlestack); i += PAGE_SIZE) {
va =(vm_offset_t)&CPU_prvspace[x].idlestack + i;
m = vm_page_alloc(kernel_object, va, VM_ALLOC_SYSTEM);
pmap_kenter_quick(va, m->phys_addr);
}
gd = &CPU_prvspace[x].mdglobaldata;
bzero(gd, sizeof(*gd));
gd->mi.gd_prvspace = ps = &CPU_prvspace[x];
mi_gdinit(&gd->mi, x);
cpu_gdinit(gd, x);
#if 0
gd->gd_CMAP1 = pmap_kpte((vm_offset_t)CPU_prvspace[x].CPAGE1);
gd->gd_CMAP2 = pmap_kpte((vm_offset_t)CPU_prvspace[x].CPAGE2);
gd->gd_CMAP3 = pmap_kpte((vm_offset_t)CPU_prvspace[x].CPAGE3);
gd->gd_PMAP1 = pmap_kpte((vm_offset_t)CPU_prvspace[x].PPAGE1);
gd->gd_CADDR1 = ps->CPAGE1;
gd->gd_CADDR2 = ps->CPAGE2;
gd->gd_CADDR3 = ps->CPAGE3;
gd->gd_PADDR1 = (vpte_t *)ps->PPAGE1;
#endif
ipiq_size = sizeof(struct lwkt_ipiq) * (naps + 1);
gd->mi.gd_ipiq = (void *)kmem_alloc(kernel_map, ipiq_size,
VM_SUBSYS_IPIQ);
bzero(gd->mi.gd_ipiq, ipiq_size);
arc4_init_pcpu(x);
bootSTK = &ps->idlestack[UPAGES*PAGE_SIZE/2];
bootAP = x;
cpu_disable_intr();
pthread_create(&ap_tids[x], &attr, start_ap, NULL);
cpu_enable_intr();
while (CPUMASK_TESTBIT(smp_startup_mask, x) == 0) {
cpu_lfence();
DELAY(1000);
}
}
vm_object_drop(kernel_object);
pthread_attr_destroy(&attr);
return(ncpus - 1);
}
void
detect_cpu_topology(void)
{
logical_CPU_bits = vkernel_b_arg;
core_bits = vkernel_B_arg;
}
int
get_chip_ID(int cpuid)
{
return get_apicid_from_cpuid(cpuid) >>
(logical_CPU_bits + core_bits);
}
int
get_chip_ID_from_APICID(int apicid)
{
return apicid >> (logical_CPU_bits + core_bits);
}
int
get_core_number_within_chip(int cpuid)
{
return ((get_apicid_from_cpuid(cpuid) >> logical_CPU_bits) &
((1 << core_bits) - 1));
}
int
get_logical_CPU_number_within_core(int cpuid)
{
return (get_apicid_from_cpuid(cpuid) &
((1 << logical_CPU_bits) - 1));
}