/*
 * boot_entry.S - TSAR bootloader entry point.
 * 
 * Authors :   Alain Greiner / Vu Son  (2016)
 *
 * Copyright (c) UPMC Sorbonne Universites
 *
 * This file is part of ALMOS-MKH.
 *
 * ALMOS-MKH is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2.0 of the License.
 *
 * ALMOS-MKH is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ALMOS-MKH; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

/*************************************************************************************************
 * This file contains the entry point of the ALMOS-MK boot-loader for TSAR architecture.         *
 * It supports a generic multi-clusters / multi-processors architecture                          *
 *                                                                                               *
 * - The number of clusters is defined by the (X_SIZE, Y_SIZE) parameters in the                 *
 *   hard_config.h file (up to 256 clusters).                                                    *
 * - The number of processors per cluster is defined by the NB_PROCS_MAX parameter in the        *
 *   hard_config.h file (up to 4 processors per cluster).                                        *
 *                                                                                               *
 * This assembly code is executed by all cores. It has 2 versions (in order to see if the        *
 * contention created by the ARCHINFO core descriptor table scanning loops is acceptable):       *
 * with or without the assumption that the core hardware identifier gid has a fixed format:      *
 *                                                                                               *
 * - Version with fixed format: gid == (((x << Y_WIDTH) + y) << PADDR_WIDTH) + lid               *
 *   It does 3 things:                                                                           *
 *      + It initializes the stack pointer depending on the lid extracted from the gid,          *
 *        using the BOOT_STACK_BASE and BOOT_STACK_SIZE parameters defined in the                *
 *        'boot_config.h' file,                                                                  *
 *      + It changes the value of the address extension registers using the cxy extracted        *
 *        from the gid,                                                                          *
 *      + It jumps to the boot_loader() function defined in the 'boot.c' file and passes 2       *
 *        arguments which are the cxy and lid of each core to this function.                     *
 *                                                                                               *
 * - Version without fixed format                                                                *
 *   It has to perform an additional step in order to extract the (cxy,lid) values from the      *
 *   arch_info.bin structure that has been loaded in the cluster (0,0) memory by the bscpu.      *
 *      + Each core other than the bscpu scans the core descriptor table in the arch_info.bin    *
 *        structure to make an associative search on the (gid), and get the (cxy,lid).           *
 *      + It initializes the stack pointer depending on the lid, using the BOOT_STACK_BASE       *
 *        and BOOT_STACK_SIZE parameters defined in the 'boot_config.h' file,                    *
 *      + It changes the value of the address extension registers using cxy obtained             *
 *        previously,                                                                            *
 *      + It jumps to the boot_loader() function defined in the 'boot.c' file and passes 2       *
 *        arguments which are the cxy and lid of each core to this function.                     *
 *************************************************************************************************/

#include "mips32_registers.h"
#include "hard_config.h"
#include "boot_config.h"

    .section    .text, "ax", @progbits

    .globl      boot_entry
    .ent        boot_entry

    .align      2
    .set        noreorder

boot_entry:

#if USE_FIXED_FORMAT

/*************
 * VERSION 1 *
 *************/

    /* 
     * Get (cxy, lid) values from gid contained in coprocessor0 register.
     */

    mfc0    k0,     CP0_PROCID          
    andi    k0,     k0,     0xFFF                       /* k0 <= gid                        */
    andi    t1,     k0,     ((1 << P_WIDTH) - 1)        /* t1 <= lid                        */
    srl     t2,     k0,     P_WIDTH                     /* t2 <= cxy                        */
    
    /* Initialize stack pointer from previously retrieved lid value  */
    
    la      t0,     BOOT_STACK_BASE                     /* t0 <= BOOT_STACK_BASE            */
    li      k1,     BOOT_STACK_SIZE                     /* k1 <= BOOT_STACK_SIZE            */
    multu   k1,     t1
    mflo    k0                                          /* k0 <= BOOT_STACK_SIZE * lid      */
    subu    sp,     t0,     k0                          /* P[cxy,lid] stack top initialized */ 

    /* Switch to local DSPACE by changing the value of the address extension registers  */

    mtc2    t2,     CP2_DATA_PADDR_EXT

    /* Jump to boot_loader() function after passing 2 arguments in the registers  */

    or      a0,     zero,   t1                          /* a0 <= lid                        */     
    or      a1,     zero,   t2                          /* a1 <= cxy                        */
    la      ra,     boot_loader
    jr      ra
    nop

#else

/*************
 * VERSION 2 *
 *************/

    /* Test if this is bscpu  */

    mfc0    k0,     CP0_PROCID          
    andi    k0,     k0,     0xFFF                       /* k0 <= gid                        */

    li      t1,     BOOT_CORE_GID                       /* t1 <= bscpu gid                  */
    or      t3,     zero,   zero                        /* t3 <= bscpu lid = 0              */
    beq     k0,     t1,     bscpu_exit                  /* if bscpu, skip scanning core tbl */
    li      t4,     BOOT_CORE_CXY                       /* t4 <= bscpu cxy                  */

    /* Get base address of the core descriptor table in 'arch_info.bin' file */

    la      t0,     ARCHINFO_BASE                       /* t0 <= ARCHINFO_BASE              */
    li      t1,     0x80                                /* t1 <= ARCHINFO_HEADER_SIZE       */
    addu    t2,     t0,     t1                          /* t2 <= ARCHINFO_CORE_BASE         */

    /* scan the core descriptor table if this is not bscpu. TODO If not found?  */

    li      t3,     0x8                                 /* t3 <= ARCHINFO_CORE_SIZE         */
    
scanning_core_table:
    lw      t1,     0(t2)                               /* t1 <= archinfo_core.gid          */
    bne     t1,     k0,     scanning_core_table         /* if (t1 != k0) => loop            */
    addu    t2,     t2,     t3                          /* t2 <= @ next archinfo_core       */

    /* Get (cxy, lid) values from the found core descriptor  */
    
    lw      t3,     -8(t2)                              /* t3 <= lid                        */
    lw      t4,     -4(t2)                              /* t4 <= cxy                        */

    /* Initialize stack pointer from previously retrieved lid value  */

bscpu_exit:    
    la      t0,     BOOT_STACK_BASE                     /* t0 <= BOOT_STACK_BASE            */
    li      k1,     BOOT_STACK_SIZE                     /* k1 <= BOOT_STACK_SIZE            */
    multu   k1,     t3
    mflo    k0                                          /* k0 <= BOOT_STACK_SIZE * lid      */
    subu    sp,     t0,     k0                          /* P[cxy,lid] stack top initialized */ 

    /* Switch to local DSPACE by changing the value of the address extension registers  */

    mtc2    t4,     CP2_DATA_PADDR_EXT

    /* Jumping to boot_loader() function after passing 2 arguments in registers */

    or      a0,     zero,   t3                          /* a0 <= lid                        */     
    or      a1,     zero,   t4                          /* a1 <= cxy                        */
    la      ra,     boot_loader
    jr      ra
    nop

#endif

    .end boot_entry

    .set reorder
