/*-
* Copyright (c) 2002-2018 The UbixOS Project.
* All rights reserved.
*
* This was developed by Christopher W. Olsen for the UbixOS Project.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met:
*
* 1) Redistributions of source code must retain the above copyright notice, this list of
* conditions, the following disclaimer and the list of authors.
* 2) Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions, the following disclaimer and the list of authors in the documentation and/or
* other materials provided with the distribution.
* 3) Neither the name of the UbixOS Project nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <ubixos/smp.h>
#include <ubixos/spinlock.h>
#include <ubixos/kpanic.h>
#include <lib/kprintf.h>
#include <lib/string.h>
#include <sys/io.h>
static struct spinLock initSpinLock = SPIN_LOCK_INITIALIZER;
static struct spinLock cpuInfoLock = SPIN_LOCK_INITIALIZER;
static uInt32 cpus = 0;
struct cpuinfo_t cpuinfo[8];
uInt8 kernel_function(void);
uInt8 *vram = (uInt8 *)0xB8000;
static inline unsigned int apicRead(address) {
return *(volatile unsigned int *) (0xFEE00000 + address);
}
static inline void apicWrite(unsigned int address,unsigned int data) {
*(volatile unsigned int *) (0xFEE00000 + address) = data;
}
static __inline__ void setDr3 (void *dr3) {
register uInt32 value = (uInt32)dr3;
__asm__ __volatile__ ("mov %0, %%dr3" :: "r" (value));
}
static __inline__ uInt32 getDr3 (void) {
register uInt32 value;
__asm__ __volatile__ ("mov %%dr3, %0" : "=r" (value));
return value;
}
struct gdt_descr {
uInt16 limit;
uInt32 *base __attribute__ ((packed));
};
static void GDT_fixer() {
struct gdt_descr gdt_descr;
uInt32 *gdt = (uInt32 *)0x20000; // 128KB
gdt[0] = 0;
gdt[1] = 0;
gdt[2] = 0x0000ffff;// seg 0x8 -- DPL 0 4GB code
gdt[3] = 0x00cf9a00;
gdt[4] = 0x0000ffff;// seg 0x10 -- DPL 0 4GB data
gdt[5] = 0x00cf9200;
gdt[6] = 0x0000ffff;// seg 0x1b -- DPL 3 4GB code
gdt[7] = 0x00cffa00;
gdt[8] = 0x0000ffff;// seg 0x23 -- DPL 3 4GB data
gdt[9] = 0x00cff200;
gdt_descr.limit = 32 * 4;
gdt_descr.base = gdt;
/*
asm("lgdt %0;" : : "m" (gdt_descr));
__asm__ __volatile__ ("ljmp %0,$1f; 1:" :: "i" (0x08));
__asm__ __volatile__ ("movw %w0,%%ds" :: "r" (0x10));
__asm__ __volatile__ ("movw %w0,%%es" :: "r" (0x10));
__asm__ __volatile__ ("movw %w0,%%ss" :: "r" (0x10));
*/
}
void cpu0_thread(void) {
for(;;) {
vram[40+640] = kernel_function();
vram[42+640]++;
}
}
void cpu1_thread(void) {
for(;;) {
vram[60+640] = kernel_function();
vram[62+640]++;
}
}
void cpu2_thread(void) {
for(;;) {
vram[80+640] = kernel_function();
vram[82+640]++;
}
}
void cpu3_thread(void) {
for(;;) {
vram[100+640] = kernel_function();
vram[102+640]++;
}
}
static struct spinLock bkl = SPIN_LOCK_INITIALIZER;
uInt8 kernel_function(void) {
struct cpuinfo_t *cpu;
spinLock(&bkl);
cpu = (struct cpuinfo_t *)getDr3();
spinUnlock(&bkl);
return('0' + cpu->id);
}
void c_ap_boot(void) {
while(spinLockLocked(&initSpinLock));
switch(cpuInfo()) {
case 1:
cpu1_thread();
break;
case 2:
cpu2_thread();
break;
case 3:
cpu3_thread();
break;
}
outportByte(0xe9,'5');
for(;;) {
asm("nop");
}
}
void smpInit() {
spinLock(&initSpinLock);
GDT_fixer();
cpuidDetect();
cpuInfo();
apicMagic();
spinUnlock(&initSpinLock);
//cpu0_thread();
}
void cpuidDetect() {
if (!(getEflags() & (1<<21)) ) {
setEflags(getEflags() | (1<<21));
if( !(getEflags() & (1<<21)) ) {
kpanic("CPU doesn't support CPUID, get a newer machine\n");
}
}
}
uInt8 cpuInfo() {
uInt32 data[4],i;
if( !(getEflags() & (1<<21)) ) { // If the cpuid bit in eflags not set..
setEflags(getEflags() | (1<<21));// ..try and set it to see if it comes on..
if( !(getEflags() & (1<<21)) ) { // It didn't.. This CPU suck
kpanic("CPU doesn't support CPUID, get a newer machine\n");
}
}
spinLock(&cpuInfoLock);
cpuinfo[cpus].ok = 1;
cpuinfo[cpus].apic_id = apicRead(0x20) >> 24;
cpuinfo[cpus].apic_ver = apicRead(0x30) & 0xFF;
cpuid(0,data);
*(uInt32 *)&cpuinfo[cpus].ident[0] = data[1];
*(uInt32 *)&cpuinfo[cpus].ident[4] = data[3];
*(uInt32 *)&cpuinfo[cpus].ident[8] = data[2];
cpuinfo[cpus].ident[17] = 0;
cpuinfo[cpus].max = data[0];
cpuid(1,data);
cpuinfo[cpus].signature = data[0];
cpuinfo[cpus].feature = data[3];
cpuid(0x80000000,data);
if(data[0]>=0x80000004) {
for(i=0;i<3;i++) {
cpuid(0x80000002 + i,data);
*(unsigned int *)&cpuinfo[cpus].brand[16*i+0] = data[0];
*(unsigned int *)&cpuinfo[cpus].brand[16*i+4] = data[1];
*(unsigned int *)&cpuinfo[cpus].brand[16*i+8] = data[2];
*(unsigned int *)&cpuinfo[cpus].brand[16*i+12] = data[3];
}
cpuinfo[cpus].brand[48] = 0;
}
else {
cpuinfo[cpus].brand[0] = 0;
}
setDr3(&cpuinfo[cpus]); // DR3 always points to the cpu-struct for that CPU (should be thread-struct of current thread)
cpuinfo[cpus].id = cpus;
cpus++;
spinUnlock(&cpuInfoLock);
return(cpus - 1);
}
extern void ap_trampoline_start(),
ap_trampoline_end();
void apicMagic(void) {
uInt32 tmp;
kprintf("Copying %u bytes from 0x%x to 0x00\n", ap_trampoline_end - ap_trampoline_start, ap_trampoline_start);
memcpy(0x0, (char *) ap_trampoline_start, ap_trampoline_end - ap_trampoline_start);
apicWrite(0x280, 0);
apicRead(0x280);
apicWrite(0x300, 0x000C4500); // INIT IPI to all CPUs
for (tmp = 0; tmp < 800000; tmp++)
asm("nop");
// Sleep a little (should be 10ms)
apicWrite(0x300, 0x000C4600); // INIT SIPI to all CPUs
for (tmp = 0; tmp < 800000; tmp++)
asm("nop");
// Sleep a little (should be 200ms)
apicWrite(0x300, 0x000C4600); // Second INIT SIPI
for (tmp = 0; tmp < 800000; tmp++)
asm("nop");
// Sleep a little (should be 200ms)
}
uInt32 getEflags() {
uInt32 eflags = 0x0;
asm(
"pushfl \n"
"popl %%eax \n"
: "=a" (eflags)
);
return (eflags);
}
void setEflags(uInt32 eflags) {
asm(
"pushl %%eax \n"
"popfl \n"
:
: "a" (eflags)
);
}
asm(
".globl cpuid \n"
"cpuid: \n"
" pushl %ebx \n"
" pushl %edi \n"
" movl 12(%esp),%eax \n"
" movl 16(%esp),%edi \n"
" cpuid \n"
" movl %eax,0(%edi) \n"
" movl %ebx,4(%edi) \n"
" movl %ecx,8(%edi) \n"
" movl %edx,12(%edi) \n"
" popl %edi \n"
" popl %ebx \n"
" ret \n"
);
/***
END
***/