/*
    Copyright 2007 (c) by S. Ryffel,
    Swiss Federal Institute of Technology,
    Computer Engineering and Networks Laboratory.

    TOPSY -- A Teachable Operating System.
             Implementation of a tiny and simple
             micro kernel for teaching purposes.

    For further information, please visit http://www.tik.ee.ethz.ch/~topsy

    This software is provided under the terms of the GNU General Public 
    Licence. A full copy of the GNU GPL is provided in the file COPYING 
    found in the development root of Topsy.
*/
/*

    File:                  $Source$
    Affiliation:           ETH Zuerich, TIK
    Version:               $Revision$
    Last Date of Change:   $Date$  by: $Author$

*/


#include "asm.h"
#include "MemoryLayout.h"

#define __SASM__
#include "TMHalAsm.h"
#undef  __SASM__


/*Initalize APIC***************************************************************/ 

////
// int __is_apic_present(void) 
////
ASM_PROC_FRAME(__is_apic_present)
        ENTER
        pushl   %ebx        // these registers are overwritten by cpuid 
        pushl   %ecx
        pushl   %edx

        // Test, if cpuid is available
        //  Processors up to the early 468 Processors do not support
        //  the cpuid instruction. They do not have an APIC either, therefore
        //  we abort here with an error.
        //  For details see chapter 14 of the "Intel® 64 and IA-32 Architectures
        //  Software Developer’s Manual, Volume 1"
        // If we can set the ID Flag (bit 21 of the EFLAGS), the CPU supports cpuid.
        pushfl			            // push EFLAGS
        popl    %eax		        // get EFLAGS
        movl    %eax,%ecx		    // save original EFLAGS
        xorl    $0x200000,%eax	// flip ID bit in EFLAGS
        pushl   %eax		        // copy to EFLAGS
        popfl			              // set EFLAGS
        pushfl			            // get new EFLAGS
        popl    %eax		        // put it in eax
        xorl    %ecx,%eax		    // change in flags
        pushl   %ecx		        // restore original EFLAGS
        popfl
        testl   $0x200000,%eax	// check if ID bit changed
        jne     no_apic

        movl    $1, %eax        // Argument to CPUID
        cpuid

        xorl    %eax, %eax      // clear %eax
        andl    $0x200, %edx    // test bit 9: Presence of APIC
        je      no_apic
        movl    $1, %eax        // APIC present ;)

no_apic:
        popl    %edx
        popl    %ecx
        popl    %ebx
        LEAVE

////
// int __enable_apic(void)
////
ASM_PROC_FRAME(__enable_apic)
        ENTER
        pushl   %eax
        pushl   %ecx
        pushl   %edx
       
        movl    $IA32_APIC_BASE_MSR, %ecx   // MSR address in %ecx
        rdmsr                               // Read MSR: result is in %edx:%eax

        orl     $0x800, %eax                // APIC global enable: bit 11

        wrmsr                               // write MSR back

        popl    %edx
        popl    %ecx
        popl    %eax

        /* as long as we do not disable APIC, we do not have to reenable it... */

        

        movl    $1, %eax

        LEAVE

////
// int __disable_apic(void)
////
ASM_PROC_FRAME(__disable_apic)
        ENTER
        
        /* if you implement this, you have to implement __enable_apic() too 
         */
        xorl    %eax, %eax

        LEAVE

////
// int __cpu_by_intel(void);
////
intel_cpu_ebx: .ascii "Genu"
intel_cpu_edx: .ascii "ineI"
intel_cpu_ecx: .ascii "ntel"

ASM_PROC_FRAME(__cpu_by_intel)
        ENTER
        pushl   %ebx        // these registers are overwritten by cpuid 
        pushl   %ecx
        pushl   %edx

        // EAX = 0: Test for Genuine Intel Processor
        xorl    %eax, %eax
        cpuid   
        xorl    %eax, %eax              // clear %eax
        cmpl    %ebx, (intel_cpu_ebx)
        jne     cbi_err
        cmpl    %edx, (intel_cpu_edx)
        jne     cbi_err
        cmpl    %ecx, (intel_cpu_ecx)
        jne     cbi_err
        
        movl    $1, %eax                // all ok, return true
cbi_err:
        popl    %edx
        popl    %ecx
        popl    %ebx
        LEAVE

////
// int __max_cpuid(void);
////

ASM_PROC_FRAME(__max_cpuid)
        ENTER
        pushl   %ebx        // these registers are overwritten by cpuid 
        pushl   %ecx
        pushl   %edx

        // EAX = 0
        xorl    %eax, %eax
        cpuid                   // %eax holds the max cpuid
        
        popl    %edx
        popl    %ecx
        popl    %ebx
        LEAVE


////
// int __intel_get_brand_index(ushort* brand, int* version_info, int* apic_id );
////

ASM_PROC_FRAME(__intel_get_brand_index)
        ENTER
        pushl   %ebx        // these registers are overwritten by cpuid 
        pushl   %ecx
        pushl   %edx
        
        // EAX = 1: Returns Model, Family, Stepping Information
        movl    $1, %eax
        cpuid
        movl    ARG1, %edx
        movb    %bl, (%edx)   // bits 7-0 of %ebx are the Brand index

        shrl    $24, %ebx    // bits 31-24 are the initial APIC ID of the Processor
        movl    ARG3, %edx
        movl    %ebx, (%edx)

        movl    ARG2, %edx
        movl    %eax, (%edx)  // move Version Information 

        movl    $1, %eax

        popl    %edx
        popl    %ecx
        popl    %ebx
        LEAVE

////
// int __intel_get_brand_string(uchar* brand_str);
////
ASM_PROC_FRAME(__intel_get_brand_string)
        ENTER
        pushl   %ebx        // these registers are overwritten by cpuid 
        pushl   %ecx
        pushl   %edx
        pushl   %esi
        
        // Test if Brand String Method is supported
        movl    $0x80000000, %eax
        cpuid
        andl    $0x80000000, %eax   // if bit is set, brand string is supported
        movl    $0, %eax            
        je      igbs_end            // return with error


        // Get Brand String
        movl    ARG1, %esi            // get destination
        
        movl    $0x80000002, %eax     // get first characters
        cpuid
        movl    %eax,   (%esi)
        movl    %ebx,  4(%esi)
        movl    %ecx,  8(%esi)
        movl    %edx, 12(%esi)

        movl    $0x80000003, %eax     // get more characters
        cpuid
        movl    %eax, 16(%esi)
        movl    %ebx, 20(%esi)
        movl    %ecx, 24(%esi)
        movl    %edx, 28(%esi)

        movl    $0x80000004, %eax     // get last characters
        cpuid
        movl    %eax, 32(%esi)
        movl    %ebx, 36(%esi)
        movl    %ecx, 40(%esi)
        movl    %edx, 44(%esi)

        movl    $1, %eax              // return true
igbs_end:
        popl    %esi
        popl    %edx
        popl    %ecx
        popl    %ebx
        LEAVE

////
// void __get_apic_base(address_t* apic_base, int* is_bsr);
////
ASM_PROC_FRAME(__get_apic_base)
        ENTER
        pushl   %eax
        pushl   %ecx
        pushl   %edx
       
        movl    $IA32_APIC_BASE_MSR, %ecx   // MSR address in %ecx
        rdmsr                               // Read MSR: result is in %edx:%eax
        movl    %eax, %edx                  // copy original MSR

        // get APIC Base: Bits 35...12 
        andl    $0xFFFFF000, %eax     // aligned to the page boundary
        movl    ARG1, %ecx
        movl    %eax, (%ecx)

        // get BSP: Bit 8
        shrl    $8, %edx
        andl    $1, %edx
        movl    ARG2, %ecx
        movl    %edx, (%ecx)
        
        popl    %edx
        popl    %ecx
        popl    %eax
        LEAVE

       

////
// int __intel_get_scalable_bus_speed();
////

ASM_PROC_FRAME(__intel_get_scalable_bus_speed)
        ENTER
        pushl   %ebx        // these registers are overwritten by cpuid 
        pushl   %ecx
        pushl   %edx
        
        // EAX = 0x2c: current scalable bus speed in bits 18:16
        // movl    $0x2c, %eax
        // cpuid
        xorl  %eax, %eax

        popl    %edx
        popl    %ecx
        popl    %ebx
        LEAVE


////
// void __endOf_APIC_IRQ(void);
////

ASM_PROC_FRAME(__endOf_APIC_IRQ)
        ENTER
        pushl   %eax

        // To signal an EOI to the APIC we write 0x0 in the EOI Register
        // which is located at APIC_BASE+0xB0 and 32bits wide.

        movl    (apic_base), %eax
        addl    $0xB0, %eax

        movl    $0x0, (%eax)

        popl    %eax
        LEAVE


/*APIC Timer********************************************************************/ 


/* void _timerSet(unsigned short pDIVISOR, 
                  unsigned char  pCtrlWord,
                  unsigned char  pTimerChannel); */
ASM_PROC_FRAME(__timerSet)
        ENTER
        pushl   %eax
        pushl   %edx
        
        
        popl    %edx
        popl    %eax
        LEAVE






