/* **********************************************************
 * Copyright 2002 VMware, Inc.  All rights reserved. -- VMware Confidential
 * **********************************************************/


#ifndef _SMP_H_
#define _SMP_H_
#include "stdint.h"
#include "defs.h"
#define MAX_CPUS 16 // "16 CPUs ought to be enough for everybody."


#define FPSignature ('_' | 'M' << 8 | 'P' << 16 | '_' << 24)

typedef struct {
   uint32_t signature;   // "_MP_"
   uint32_t phys_addr;
   uint8_t  length;
   uint8_t  spec_rev;
   uint8_t  checksum;
   uint8_t  feature[5];
} floating_pointer_struct_t;

#define MPCSignature ('P' | 'C' << 8 | 'M' << 16 | 'P' << 24)
typedef struct {
   uint32_t signature;   // "PCMP"
   uint16_t length;
   uint8_t  spec_rev;
   uint8_t  checksum;
   char   oem[8];
   char   productid[12];
   uint32_t oem_ptr;
   uint16_t oem_size;
   uint16_t oem_count;
   uint32_t lapic_addr;
   uint32_t reserved;
} mp_config_table_header_t;

/* Followed by entries */

#define MP_PROCESSOR    0
#define MP_BUS          1
#define MP_IOAPIC       2
#define MP_INTSRC       3
#define MP_LINTSRC      4

typedef struct {
   uint8_t type;          /* MP_PROCESSOR */
   uint8_t apic_id;       /* Local APIC number */
   uint8_t apic_ver;      /* Its versions */
   uint8_t cpu_flag;
#define CPU_ENABLED             1       /* Processor is available */
#define CPU_BOOTPROCESSOR       2       /* Processor is the BP */
   uint32_t cpu_signature;           
#define CPU_STEPPING_MASK 0x0F
#define CPU_MODEL_MASK  0xF0
#define CPU_FAMILY_MASK 0xF00
   uint32_t featureflag;  /* CPUID feature value */
   uint32_t reserved[2];
} mp_processor_entry_t;


typedef struct {
   uint8_t type;   // has value MP_BUS
   uint8_t busid;
   char  bustype[6];
} mp_bus_entry_t;

#define BUSTYPE_EISA    "EISA"
#define BUSTYPE_ISA     "ISA"
#define BUSTYPE_INTERN  "INTERN"
#define BUSTYPE_MCA     "MCA"
#define BUSTYPE_VL      "VL"
#define BUSTYPE_PCI     "PCI"
#define BUSTYPE_PCMCIA  "PCMCIA"

/* We don't understand the others */

typedef struct {
   uint8_t  type;   // set to MP_IOAPIC
   uint8_t  apicid;
   uint8_t  apicver;
   uint8_t  flags;
#define MPC_APIC_USABLE         0x01
   uint32_t apicaddr;
} mp_io_apic_entry_t;


typedef struct {
   uint8_t  type;
   uint8_t  irqtype;
   uint16_t irqflag;
   uint8_t  srcbus;
   uint8_t  srcbusirq;
   uint8_t  dstapic;
   uint8_t  dstirq;
} mp_interrupt_entry_t;

#define MP_INT_VECTORED         0
#define MP_INT_NMI              1
#define MP_INT_SMI              2
#define MP_INT_EXTINT           3

#define MP_IRQDIR_DEFAULT       0
#define MP_IRQDIR_HIGH          1
#define MP_IRQDIR_LOW           3


typedef struct {
   uint8_t  type;
   uint8_t  irqtype;
   uint16_t irqflag;
   uint8_t  srcbusid;
   uint8_t  srcbusirq;
   uint8_t  destapic;     
#define MP_APIC_ALL     0xFF
   uint8_t  destapiclint;
} mp_local_interrupt_entry_t;

/* APIC definitions */
/*
 * APIC registers
 */
#define APICR_ID         0x02
#define APICR_ESR        0x28
#define APICR_ICRLO      0x30
#define APICR_ICRHI      0x31

/* APIC destination shorthands */
#define APIC_DEST_DEST        0
#define APIC_DEST_LOCAL       1
#define APIC_DEST_ALL_INC     2
#define APIC_DEST_ALL_EXC     3

/* APIC IPI Command Register format */
#define APIC_ICRHI_RESERVED		0x00ffffff
#define APIC_ICRHI_DEST_MASK		0xff000000
#define APIC_ICRHI_DEST_OFFSET		24

#define APIC_ICRLO_RESERVED		0xfff32000
#define APIC_ICRLO_DEST_MASK		0x000c0000
#define APIC_ICRLO_DEST_OFFSET		18
#define APIC_ICRLO_TRIGGER_MASK		0x00008000
#define APIC_ICRLO_TRIGGER_OFFSET	15
#define APIC_ICRLO_LEVEL_MASK		0x00004000
#define APIC_ICRLO_LEVEL_OFFSET		14
#define APIC_ICRLO_STATUS_MASK		0x00001000
#define APIC_ICRLO_STATUS_OFFSET	12
#define APIC_ICRLO_DESTMODE_MASK	0x00000800
#define APIC_ICRLO_DESTMODE_OFFSET	11
#define APIC_ICRLO_DELMODE_MASK		0x00000700
#define APIC_ICRLO_DELMODE_OFFSET	8
#define APIC_ICRLO_VECTOR_MASK		0x000000ff
#define APIC_ICRLO_VECTOR_OFFSET	0

/* APIC trigger types (edge/level) */
#define APIC_TRIGGER_EDGE     0
#define APIC_TRIGGER_LEVEL    1

/* APIC delivery modes */
#define APIC_DELMODE_FIXED    0
#define APIC_DELMODE_LOWEST   1
#define APIC_DELMODE_SMI      2
#define APIC_DELMODE_NMI      4
#define APIC_DELMODE_INIT     5
#define APIC_DELMODE_STARTUP  6
#define APIC_DELMODE_EXTINT   7
typedef uint32_t apic_register_t[4];

extern volatile apic_register_t *APIC;

unsigned smp_num_cpus();
unsigned smp_my_cpu_num();

void smp_init_bsp(void);
void smp_init_aps(void);

void smp_boot_ap(unsigned cpu_num);
void smp_ap_booted(unsigned cpu_num);

static inline void
__GET_CPUID(int ax, uint32_t *regs)
{
   __asm__ __volatile__("\t"
   	/* save ebx in case -fPIC is being used */
      "push %%ebx; cpuid; mov %%ebx, %%edi; pop %%ebx"
      : "=a" (regs[0]), "=D" (regs[1]), "=c" (regs[2]), "=d" (regs[3])
      : "a" (ax)
      : "memory"
   );
}

#define GET_CPUID(_ax,_bx,_cx,_dx) { \
   uint32_t regs[4];                   \
   __GET_CPUID(_ax,regs);            \
   _ax = regs[0];                    \
   _bx = regs[1];                    \
   _cx = regs[2];                    \
   _dx = regs[3];                    \
}

/*
 * Checked against the Intel manual and GCC --hpreg
 *
 * volatile because the tsc always changes without the compiler knowing it.
 */
static inline uint64_t
RDTSC(void)
{
   uint64_t tim;

   __asm__ __volatile__(
      "rdtsc"
      : "=A" (tim)
   );

   return tim;
}

static inline uint64_t __GET_MSR(int cx)
{
   uint64_t msr;

   __asm__ __volatile__(
      "rdmsr"
      : "=A" (msr)
      : "c" (cx)
   );

   return msr;
}

#define __GCC_OUT(s, s2, port, val) do { \
   __asm__(                              \
      "out" #s " %" #s2 "1, %w0"         \
      :                                  \
      : "Nd" (port), "a" (val)           \
   );                                    \
} while (0)
#define OUTB(port, val) __GCC_OUT(b, b, port, val)

typedef struct {
        unsigned int slock;
} spinlock_t;

static inline void spin_lock(volatile spinlock_t *lock)
{
        asm volatile("\n1:\t"
                     " ; lock;decb %0\n\t"
                     "jns 3f\n"
                     "2:\t"
                     "rep;nop\n\t"
                     "cmpb $0,%0\n\t"
                     "jle 2b\n\t"
                     "jmp 1b\n"
                     "3:\n\t"
                     : "+m" (lock->slock) : : "memory");
}
static inline void spin_unlock(volatile spinlock_t *lock)
{
        asm volatile("movb $1,%0" : "+m" (lock->slock) :: "memory");
}


#endif /* _SMP_H_ */
