Thursday, October 29, 2015

BARE METAL HA HA HA HA

Fundamentals ::: First stepping stones of making an embedded system. BARE METALS.
BARE METAL

METAL

http://www.wiki.xilinx.com/Linux+Drivers

IDEAS for future projects

OS development
Applications:

Simple OS with scheduler algorithm.
When too many interrupts (or other stuff) -> does not respect deadlines, misses deadlines. 
When deadlines are missed for too long, the processor takes time to generate bitstream and program an external fpga (using foss tools) which implements some function accelerated in hw. 
Use http://yosefk.com/blog/how-fpgas-work-and-why-youll-buy-one.html to get inspired on which function to accelerate.
Languages (for bare-metal development of OS):
Goes baremetal?

http://www.embedded.com/design/programming-languages-and-tools/4428704/2/Alternatives-to-C-C--for-system-programming-in-a-distributed-multicore-world

http://www.slideshare.net/xen_com_mgr/next-generation-cloud-rise-of-the-unikernel-v2-updated?next_slideshow=1

http://programmers.stackexchange.com/questions/274927/is-functional-language-without-runtime-written-in-c-possible

http://www.reddit.com/r/haskell/comments/29tgjd/ideal_programming_language_for_a_new_modern_os/

https://www.reddit.com/r/rust/comments/2web3t/is_rust_going_to_replace_cc_when_it_comes_to/

http://www.reddit.com/r/haskell/comments/2sahpi/why_no_embedded_systems/
- Projects:

http://repetae.net/computer/jhc/manual.html

https://zinc.rs/

Good for multicore?

http://www.embedded.com/design/programming-languages-and-tools/4438718/Programming-languages-for-multicore-systems-

Very good reading:

Educating Embedded Systems Hackers:
https://www.kth.se/polopoly_fs/1.580282!/wese2014.pdf

http://elib.dlr.de/96449/1/Thesis_report_Wei_final.pdf
MULTICORE RASPI 2

https://www.raspberrypi.org/forums/viewtopic.php?f=72&t=98904&start=25
https://github.com/PeterLemon/RaspberryPi/tree/master/SMP/SMPINIT (smp)
OPENCORES:
- K7 Gtx sim model. 8b10 from opencores, what about rx_align?
- Rchitecture
EYE OPEN ON:
- opencl openmp for embedded
- xen hypervisor
- functional haskell/erlang/rust

ARM Linux Exception handlers implementation


Aborts Exception Handling

Aborts can be generated either on failed instruction fetches (prefetch aborts) or failed data accesses (data aborts). They can come from the external memory system giving an error response on a memory access (indicating perhaps that the specified address does not correspond to real memory in the system).
Alternatively, the abort can be generated by the Memory Management Unit (MMU) of the processor. An operating system can use MMU aborts to dynamically allocate memory to applications.

 Prefetch Abort Implementation


1. After getting prefetch abort exception, current mode PC will store in exception_LR and 
    CPSR into exception_SPSR, then PC will point to prefetch abort vector address.
        __vectors_start:
        ...
             W(b)    vector_pabt + stubs_offset
      
2. So when ARM refers to the vector table it follows the branch and lands up here. At this moment ARM is in Abort mode, IRQs are disabled, LR contains PC of when abort occurred and SPSR contains CPSR of when abort occurred. Since we are in abort mode, so r13 (SP) is banked, so load SP with address of a small stack frame (size of 3 words) that we have created at cpu_init(). This is abort exception stack  
     
    .macro  vector_stub, name, mode, correction=0
     vector_\name: (in this case vector_pabt)
   ...
   
        @
        @ Save r0, lr_ (parent PC) and spsr_
        @ (parent CPSR)
        @
        stmia   sp, {r0, lr}            @ save r0, lr
        mrs     lr, spsr
        str     lr, [sp, #8]            @ save spsr


        @
        @ Prepare for SVC32 mode.  IRQs remain disabled.
        @
        mrs     r0, cpsr
        eor     r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
        msr     spsr_cxsf, r0

        movs    pc, lr                  @ branch to handler in SVC mode

3. After this basic setup is done, depending on the mode, in which ARM was, when exception occurred
    we switch to specific handler. We'll assume that ARM was executing in SVC mode, so we'll look into the
    details of __pabt_svc

       /*
        * Prefetch abort dispatcher
        * Enter in ABT mode, spsr = USR CPSR, lr = USR PC
        */
        vector_stub     pabt, ABT_MODE, 4

        .long   __pabt_usr                      @  0 (USR_26 / USR_32)
        .long   __pabt_invalid                  @  1 (FIQ_26 / FIQ_32)
        .long   __pabt_invalid                  @  2 (IRQ_26 / IRQ_32)
        .long   __pabt_svc                      @  3 (SVC_26 / SVC_32)

 4. __pabt_svc saves r0-12 on SVC mode stack (i.e kernel stack of process which was interrupted), reads
     LR and SPSR from temporary IRQ stack and saves them on SVC mode stack. After that it will call
     pabt_helper and increments the preempt count.
   
     _pabt_svc:
         svc_entry
         mov     r2, sp                          @ regs
         pabt_helper


5.
      .macro  pabt_helper
        @ PABORT handler takes pt_regs in r2, fault address in r4 and psr in r5
         
            #ifdef MULTI_PABORT
                     ldr     ip, .LCprocfns
                     mov     lr, pc
                     ldr     pc, [ip, #PROCESSOR_PABT_FUNC]
            #else
                     bl      CPU_PABORT_HANDLER
            #endif
      .endm

6.
     arch/arm/include/asm/glue-pf.h
     #define CPU_PABORT_HANDLER v7_pabort

     arch/arm/mm/pabort-v7.S
             .align  5
     ENTRY(v7_pabort)
             mrc     p15, 0, r0, c6, c0, 2           @ get IFAR
             mrc     p15, 0, r1, c5, c0, 1           @ get IFSR
             b       do_PrefetchAbort
     ENDPROC(v7_pabort)

7. here r0 contains Fault Addr Reg and r1 contains Fault Status Reg, based on status, respective function
    will be called

    arch/arm/mm/fault.c

    asmlinkage void __exception 
    do_PrefetchAbort(unsigned long addr, unsigned int ifsr, struct pt_regs *regs)

    arch/arm/mm/fsr-2level.c
         static struct fsr_info ifsr_info[] = {
                  ...
                 { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "section translation fault"        },
                 { do_bad,               SIGSEGV, SEGV_ACCERR,   "page access flag fault"           },
                 { do_page_fault,        SIGSEGV, SEGV_MAPERR,   "page translation fault"           },
                 { do_sect_fault,        SIGSEGV, SEGV_ACCERR,   "section permission fault"         },
                 { do_sect_fault,        SIGSEGV, SEGV_ACCERR,   "section permission fault"         },
                  ... 
         }

8. On error case will do Unhandled prefetch abort and broadcast die notification
   
          if (!inf->fn(addr, ifsr | FSR_LNX_PF, regs))
                 return;
          printk(KERN_ALERT "Unhandled prefetch abort: %s (0x%03x) at 0x%08lx\n",
                inf->name, ifsr, addr);

          arm_notify_die("", regs, &info, ifsr, 0);

9. if we are in user mode will just send SIGSEGV and kill that process, or in SVC mode will trigger oops

    arch/arm/kernel/traps.c
          arm_notify_die:
                 if (user_mode(regs)) {
                       force_sig_info(info->si_signo, info, current);
                 } else {                
                      die(str, regs, err);
                 }

Data Abort Implementation


       Data abort and prefetch abort implementation is almost same. Only do_DataAbort() is get called.


Interrupt handler implementation

  • Interrupt Setup

  • Interrupt Handling 

      When a IRQ is raised, ARM stops what it is processing ( Asuming it is not processing a FIQ!),
      disables further IRQs (not FIQs), puts CPSR in SPSR, puts current PC to LR and swithes to IRQ
      mode, refers to the vector table and jumps to the exception handler. In our case it jumps to the
      exception handler of IRQ.          

ARM Linux Interrupt Handling

When a IRQ is raised, ARM stops what it is processing ( Asuming it is not processing a FIQ!), disables further IRQs (not FIQs),
puts CPSR in SPSR, puts current PC to LR and swithes to IRQ mode, refers to the vector table and jumps to the exception handler.
In our case it jumps to the exception handler of IRQ.

following is the snippet of code for exception handler code for IRQ (again from arch/arm/kernel/entry-armV.S file):

__vectors_start:
 ARM(   swi     SYS_ERROR0      )
 THUMB( svc     #0              )
 THUMB( nop                     )
W(b)    vector_und + stubs_offset
W(ldr)  pc, .LCvswi + stubs_offset
W(b)    vector_pabt + stubs_offset
W(b)    vector_dabt + stubs_offset
W(b)    vector_addrexcptn + stubs_offset
W(b)    vector_irq + stubs_offset
W(b)    vector_fiq + stubs_offset

.globl  __vectors_end
__vectors_end:


/*
 * Vector stubs.
 *
 * This code is copied to 0xffff0200 so we can use branches in the
 * vectors, rather than ldr's.  Note that this code must not
 * exceed 0x300 bytes.
 *
 * Common stub entry macro:
 *   Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
 *
 * SP points to a minimal amount of processor-private memory, the address
 * of which is copied into r0 for the mode specific abort handler.
 */
.macro  vector_stub, name, mode, correction=0
.align  5

1. In our case vector_irq
vector_\name:
  
2.      .if \correction
sub     lr, lr, #\correction
.endif
3. So when ARM refers to the vector table it follows the branch and lands up here
   At this moment ARM is in IRQ mode, IRQs are disabled, LR contains PC of when interrupt occured and SPSR contains CPSR of when interrupt occured.
   Since we are in IRQ mode so r13 (SP) is banked, so we load SP with address of a small stack frame that we have created at cpu_init().
   This stack is only used when we are in IRQ mode.

@
@ Save r0, lr_ (parent PC) and spsr_
@ (parent CPSR)
@
stmia   sp, {r0, lr}            @ save r0, lr
mrs     lr, spsr
str     lr, [sp, #8]            @ save spsr

4. We save LR_ and SPSR_ on the temporary IRQ stack and we switch to SVC mode
  
@
@ Prepare for SVC32 mode.  IRQs remain disabled.
@
mrs     r0, cpsr
eor     r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
msr     spsr_cxsf, r0

@
@ the branch table must immediately follow this code
@
and     lr, lr, #0x0f
 THUMB( adr     r0, 1f                  )
 THUMB( ldr     lr, [r0, lr, lsl #2]    )
mov     r0, sp
 ARM(   ldr     lr, [pc, lr, lsl #2]    )

5. After this basic setup is done depending on the mode in which ARM was there when interrupt occured we switch to specific handler.
   We'll assume that ARM was executing in SVC mode, so we'll look ino the details of __irq_svc

/*
 * Interrupt dispatcher
 */
vector_stub     irq, IRQ_MODE, 4

.long   __irq_usr                       @  0  (USR_26 / USR_32)
.long   __irq_invalid                   @  1  (FIQ_26 / FIQ_32)
.long   __irq_invalid                   @  2  (IRQ_26 / IRQ_32)
.long   __irq_svc                       @  3  (SVC_26 / SVC_32)

6. __irq_svc saves r0-12 on SVC mode stack (i.e kernel stack of process which was interrupted), reads LR and SPSR from temporary IRQ stack
   and saves them on SVC mode stack. After that it will call irq_handler and increments the preemt count.

__irq_svc:
svc_entry
irq_handler

#ifdef CONFIG_PREEMPT
get_thread_info tsk
ldr     r8, [tsk, #TI_PREEMPT]          @ get preempt count
ldr     r0, [tsk, #TI_FLAGS]            @ get flags
teq     r8, #0                          @ if preempt count != 0
movne   r0, #0                          @ force flags to 0
tst     r0, #_TIF_NEED_RESCHED
blne    svc_preempt
#endif

#ifdef CONFIG_TRACE_IRQFLAGS
@ The parent context IRQs must have been enabled to get here in
@ the first place, so there's no point checking the PSR I bit.
bl      trace_hardirqs_on
#endif
svc_exit r5                             @ return from exception
 UNWIND(.fnend          )
ENDPROC(__irq_svc)


7. After this arch specific handler get called.

/*
 * Interrupt handling.
 */
.macro  irq_handler
#ifdef CONFIG_MULTI_IRQ_HANDLER
ldr     r1, =handle_arch_irq
mov     r0, sp
adr     lr, BSYM(9997f)
ldr     pc, [r1]
#else
arch_irq_handler_default
#endif
9997:
.endm


8. For the SoCs which are using ARM GIC:

arch/arm/kernel/setup.c:
 handle_arch_irq = mdesc->handle_irq

   arch/arm/mach-ux500/board-mop500.c:
  .handle_irq     = gic_handle_irq,

arch/arm/kernel/irq.c:
  handle_IRQ:generic_handle_irq

kernel/irq/irqdesc.c:
  generic_handle_irq:generic_handle_irq_desc

include/linux/irqdesc.h:
  generic_handle_irq_desc:desc->handle_irq

9. handle_irq is the actual call for the flow handler which we registered as handle_fasteoi_irq

kernel/irq/chip.c
handle_fasteoi_irq:handle_irq_event

kernel/irq/handle.c
handle_irq_event:handle_irq_event_percpu

kernel/irq/handle.c
handle_irq_event_percpu

do {
res = action->handler(irq, action->dev_id);
action = action->next;
} while (action);  

No comments:

Post a Comment