1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright 2013 (c) Joyent, Inc. All rights reserved.
  14  * Copyright (c) 2015 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
  15  */
  16 
  17 #include <sys/asm_linkage.h>
  18 #include <sys/machparam.h>
  19 #include <sys/cpu_asm.h>
  20 
  21 #include "assym.h"
  22 
  23 /*
  24  * Every story needs a beginning. This is ours.
  25  */
  26 
  27 /*
  28  * Each of the different machines has its own locore.s to take care of getting
  29  * the machine specific setup done.  Just before jumping into fakebop the
  30  * first time, we call this machine specific code.
  31  */
  32 
  33 /*
  34  * We are in a primordial world here. The loader is going to come along and
  35  * boot us at _start. As we've started the world, we also need to set up a
  36  * few things about us, for example our stack pointer. To help us out, it's
  37  * useful to remember what the loader set up for us:
  38  *
  39  * - unaligned access are allowed (A = 0, U = 1)
  40  * - virtual memory is enabled
  41  *   - we (unix) are mapped right were we want to be
  42  *   - a UART has been enabled & any memory mapped registers have been 1:1
  43  *     mapped
  44  *   - ATAGs have been updated to tell us what the mappings are
  45  * - I/D L1 caches have been enabled
  46  */
  47 
  48         /*
  49          * External globals
  50          */
  51         .globl  _locore_start
  52         .globl  mlsetup
  53         .globl  sysp
  54         .globl  bootops
  55         .globl  bootopsp
  56         .globl  t0
  57 
  58         .data
  59         .comm   t0stack, DEFAULTSTKSZ, 32
  60         .comm   t0, 4094, 32
  61 
  62 
  63 /*
  64  * Recall that _start is the traditional entry point for an ELF binary.
  65  */
  66         ENTRY(_start)
  67         ldr     sp, =t0stack
  68         ldr     r4, =DEFAULTSTKSZ
  69         add     sp, r4
  70         bic     sp, sp, #0xff
  71 
  72         /*
  73          * establish bogus stacks for exceptional CPU states, our exception
  74          * code should never make use of these, and we want loud and violent
  75          * failure should we accidentally try.
  76          */
  77         cps     #(CPU_MODE_UND)
  78         mov     sp, #-1
  79         cps     #(CPU_MODE_ABT)
  80         mov     sp, #-1
  81         cps     #(CPU_MODE_FIQ)
  82         mov     sp, #-1
  83         cps     #(CPU_MODE_IRQ)
  84         mov     sp, #-1
  85         cps     #(CPU_MODE_SVC)
  86 
  87         /* Enable highvecs (moves the base of the exception vector) */
  88         mrc     p15, 0, r3, c1, c0, 0
  89         orr     r3, r3, #(1 << 13)
  90         mcr     p15, 0, r3, c1, c0, 0
  91 
  92         /* invoke machine specific setup */
  93         bl      _mach_start
  94 
  95         bl      _fakebop_start
  96         SET_SIZE(_start)
  97 
  98 
  99 #if defined(__lint)
 100 
 101 /* ARGSUSED */
 102 void
 103 _locore_start(struct boot_syscalls *sysp, struct bootops *bop)
 104 {}
 105 
 106 #else   /* __lint */
 107 
 108         /*
 109          * We got here from _kobj_init() via exitto().  We have a few different
 110          * tasks that we need to take care of before we hop into mlsetup and
 111          * then main. We're never going back so we shouldn't feel compelled to
 112          * preserve any registers.
 113          *
 114          *  o Enable our I/D-caches
 115          *  o Save the boot syscalls and bootops for later
 116          *  o Set up our stack to be the real stack of t0stack.
 117          *  o Save t0 as curthread
 118          *  o Set up a struct REGS for mlsetup
 119          *  o Make sure that we're 8 byte aligned for the call
 120          */
 121 
 122         ENTRY(_locore_start)
 123 
 124 
 125         /*
 126          * We've been running in t0stack anyway, up to this point, but
 127          * _locore_start represents what is in effect a fresh start in the
 128          * real kernel -- We'll never return back through here.
 129          *
 130          * So reclaim those few bytes
 131          */
 132         ldr     sp, =t0stack
 133         ldr     r4, =(DEFAULTSTKSZ - REGSIZE)
 134         add     sp, r4
 135         bic     sp, sp, #0xff
 136 
 137         /*
 138          * Save flags and arguments for potential debugging
 139          */
 140         str     r0, [sp, #REGOFF_R0]
 141         str     r1, [sp, #REGOFF_R1]
 142         str     r2, [sp, #REGOFF_R2]
 143         str     r3, [sp, #REGOFF_R3]
 144         mrs     r4, CPSR
 145         str     r4, [sp, #REGOFF_CPSR]
 146 
 147         /*
 148          * Save back the bootops and boot_syscalls.
 149          */
 150         ldr     r2, =sysp
 151         str     r0, [r2]
 152         ldr     r2, =bootops
 153         str     r1, [r2]
 154         ldr     r2, =bootopsp
 155         ldr     r2, [r2]
 156         str     r1, [r2]
 157 
 158         /*
 159          * Set up our curthread pointer
 160          */
 161         ldr     r0, =t0
 162         mcr     p15, 0, r0, c13, c0, 4
 163 
 164         /*
 165          * Go ahead now and enable the L1 I/D caches.  
 166          */
 167         mrc     p15, 0, r0, c1, c0, 0
 168         orr     r0, #0x04       /* D-cache */
 169         orr     r0, #0x1000     /* I-cache */
 170         mcr     p15, 0, r0, c1, c0, 0
 171 
 172         /*
 173          * mlsetup() takes the struct regs as an argument. main doesn't take
 174          * any and should never return. Currently, we have an 8-byte aligned
 175          * stack.  We want to push a zero frame pointer to terminate any
 176          * stack walking, but that would cause us to end up with only a
 177          * 4-byte aligned stack.  So, to keep things nice and correct, we
 178          * push a zero value twice - it's similar to a typical function
 179          * entry:
 180          *      push { r9, lr }
 181          */
 182         mov     r9,#0
 183         push    { r9 }          /* link register */
 184         push    { r9 }          /* frame pointer */
 185         mov     r0, sp
 186         bl      mlsetup
 187         bl      main
 188         /* NOTREACHED */
 189         ldr     r0,=__return_from_main
 190         ldr     r0,[r0]
 191         bl      panic
 192         SET_SIZE(_locore_start)
 193 
 194 __return_from_main:
 195         .string "main() returned"
 196 #endif  /* __lint */
 197 
 198         ENTRY(arm_reg_read)
 199         ldr r0, [r0]
 200         bx lr
 201         SET_SIZE(arm_reg_read)
 202 
 203         ENTRY(arm_reg_write)
 204         str r1, [r0]
 205         bx lr
 206         SET_SIZE(arm_reg_write)