Makogan
Posts: 71
Joined: Tue May 16, 2017 9:17 pm

Interrupts time and memory?

Sat Jun 17, 2017 11:14 pm

This is 2 questions merged into one but...

First, according to the documentation the timer interrupt is flagged by bit 0 of the basic irq register. So If I understand things correctly, when a timer interrupt is triggered, the vector table will call the irq handling function (assuming interrupts are enabled) which then needs to check which interrupt needs handling, and from there it will call the timer handling routine (this is of course assuming I have added the right code to do all of this). However I know you can specify a "time" for the timer interrupt to be triggered, but how? what register, arm address do I write to, to specify an interrupt should be called when the timer reaches that value? Also since the timer is split amongst 2 registers, does that mean I need 2 write to 2 registers to specify the time?

The other question is harder. I have found absolutely no documentation about setting interrupts when an area of memory is accessed. Basically I am trying to make a segmentation fault, so I want the system to react when somebody tries to write to a protected section of memory. Is this possible?

Thank you in advance

timanu90
Posts: 44
Joined: Sat Dec 24, 2016 11:54 am

Re: Interrupts time and memory?

Mon Jun 19, 2017 8:21 am

Hi, for the timer part you can take a look here

http://www.valvers.com/open-software/ra ... -in-c-pt3/

code here:

https://github.com/BrianSidebotham/arm- ... ter/part-3

For the second part, you can do what you want, but you need to configure the MMU to do that and set access permissions to memory regions. When you set a region to read only for example and some code try to write there an external abort is generated and it jumps to the correct entry in the vector table, then you put your code there to deal with the abort the way you want.

Hope it helps.

Cheers
Tiago

LdB
Posts: 440
Joined: Wed Dec 07, 2016 2:29 pm

Re: Interrupts time and memory?

Mon Jun 19, 2017 2:32 pm

@ timanu90
That is of limited help as that code is for a Pi1 on an ARM6 processor. He has not helped by not telling you it is a Pi2 or 3 which I now from earlier posts because of his 0x3F000000 IO base address.

The timer interrupt is pretty trivial and he has basically described it all correctly even down to realizing the timer is 64bits causes a problem (Valvers code doesn't do it right). A good reference for the correct way is the Xinu repo and its description.
Description: http://xinu-os.org/BCM2835_System_Timer
Code: https://github.com/xinu-os/xinu/blob/ma ... pi/timer.c

I was hoping Brian or Ultibo would answer the segmentation fault interrupt question because I have tried and failed a number of times. Stuff that works on the ARM6 doesn't on the ARM 7/8.

Quick backdrop on the ARM7/8 they have special opcode extensions the ARM6 doesn't and some of those are aligned to 32 or 64 bits. If you wrongly use those sets they also raise a segmentation fault. I am pretty sure some register access does as well.
To really rub salt in ARM states the CPU bus can actually be scrambled under some of those conditions and if writing you have no idea what was written or to where. You can see all the fault types here
http://infocenter.arm.com/help/index.js ... DJDID.html

So in trying to deal with segmentation faults on an ARM7/8 the first question you confront is from what?
This is the linux code to deal with just that (https://git.kernel.org/pub/scm/linux/ke ... lignment.c)

Code: Select all

static int alignment_proc_show(struct seq_file *m, void *v)
{
	seq_printf(m, "User:\t\t%lu\n", ai_user);
	seq_printf(m, "System:\t\t%lu (%pF)\n", ai_sys, ai_sys_last_pc);
	seq_printf(m, "Skipped:\t%lu\n", ai_skipped);
	seq_printf(m, "Half:\t\t%lu\n", ai_half);
	seq_printf(m, "Word:\t\t%lu\n", ai_word);
	if (cpu_architecture() >= CPU_ARCH_ARMv5TE)
		seq_printf(m, "DWord:\t\t%lu\n", ai_dword);
	seq_printf(m, "Multi:\t\t%lu\n", ai_multi);
	seq_printf(m, "User faults:\t%i (%s)\n", ai_usermode,
			usermode_action[ai_usermode]);

	return 0;
}
Then you have to do actions.
If you want some insight to what the linux code does on the Pi when running code I suggest
http://antirez.com/news/111

ARM's basic advice is if you lift a segmentation fault close the offending application down. Which is fine if you are on an OS with an application but if you are on baremetal or on the O/S you are deadmeat ... reset the processor is the only option I have found.

Now I have only tried and failed so take all that with a grain of salt but that is the basic background to my musings with it.

Makogan
Posts: 71
Joined: Tue May 16, 2017 9:17 pm

Re: Interrupts time and memory?

Tue Jun 20, 2017 1:51 am

I think that I am doing something wrong, and I feel ashamed because this one should be super easy considering how many resources there are. Something is getting called when I enable interrupts, by this I mean that, if I set up an interrupt to be called in 10, 13, 20... seconds, something gets called (I verify this through ACT LED activation), however it doesn't seem to be any of my interrupt vector subroutines because they all call visual subroutines (i.e debug things that are obvious) and nothing appears. Any insight on what could be getting called aside from my vector subroutines?

This is the exact way I am doing things:

- Enable interrupts only (no FIQ) by doing: "cpsie i"
- Enable time based interrupts by setting the second bit in the IRQ Enable 1 register (this should be similar to setting the first bit in IRQ Enable basic, note I am referring to registers in the interrupt controller as explained in xinu pi).
-Declare that in 10 seconds an interrupt is to be called by getting the value of the low counter in the system timer, adding 10 million (10 seconds assuming 1 ghz) to this value, then writing this value to the system timer compare 1 (should work just as well as 3 according to the documentation)
- In the interrupt vector handler all I am doing is disabling interrupts by doing: cpsid if
Then I try to turn the LED on with a subroutine that I know works.

The behaviour so far is LED blinks for about 10 seconds (expected this is exactly what I told it to do), then the led remains OFF after this delay. If I comment out my interrupt enabling code the led blinks indefinetely, if I change the time the led blinks for that amount. So it seems that I am partially doing things correctly since SOMETHING get's called after 10 seconds, but it doesn't seem to be any of my interrupt subroutines.

LdB
Posts: 440
Joined: Wed Dec 07, 2016 2:29 pm

Re: Interrupts time and memory?

Tue Jun 20, 2017 5:43 pm

You are doing several things wrong (IRQ stack pointer not set, you are disabling interrupts rather than clearing irq) ..... how about I give you simple timer blink code in C

Ok you need to provide RPI_Activity_Led function for turning the LED on and OFF .. which I know you have :-)

I am defining RPi_IO_Base_Addr to 0x3F000000 for the Pi2/3 at the top (that is an autodetected from my code if your remember). I have defined the registers we need to hit as structs etc from the BCM2835 ARM Peripheral manual.

Code: Select all

#include <stdbool.h>		// C standard needed for bool
#include <stdint.h>			// C standard for uint8_t, uint16_t, uint32_t etc

#define RPi_IO_Base_Addr 0x3F000000
/*--------------------------------------------------------------------------}
{ 	IRQ_BASIC_PENDING REGISTER BCM2835 ARM Peripheral manual page 113/114	}
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__)) IrqBasicPendingReg {
	union {
		struct __attribute__((__packed__)) {
			bool Timer_IRQ_pending : 1;					// @0 Timer Irq pending
			bool Mailbox_IRQ_pending : 1;				// @1 Mailbox Irq pending
			bool Doorbell0_IRQ_pending : 1;				// @2 Arm Doorbell0 Irq pending
			bool Doorbell1_IRQ_pending : 1;				// @3 Arm Doorbell0 Irq pending
			bool GPU0_halted_IRQ_pending : 1;			// @4 GPU0 halted IRQ pending
			bool GPU1_halted_IRQ_pending : 1;			// @5 GPU1 halted IRQ pending
			bool Illegal_access_type1_pending : 1;		// @6 Illegal access type 1 IRQ pending
			bool Illegal_access_type0_pending : 1;		// @7 Illegal access type 0 IRQ pending
			bool bits_set_in_pending_register_1 : 1;	// @8 One or more bits set in pending register 1
			bool bits_set_in_pending_register_2 : 1;	// @9 One or more bits set in pending register 2
			bool GPU_IRQ_7_pending : 1;					// @10 GPU irq 7 pending
			bool GPU_IRQ_9_pending : 1;					// @11 GPU irq 9 pending
			bool GPU_IRQ_10_pending : 1;				// @12 GPU irq 10 pending
			bool GPU_IRQ_18_pending : 1;				// @13 GPU irq 18 pending
			bool GPU_IRQ_19_pending : 1;				// @14 GPU irq 19 pending
			bool GPU_IRQ_53_pending : 1;				// @15 GPU irq 53 pending
			bool GPU_IRQ_54_pending : 1;				// @16 GPU irq 54 pending
			bool GPU_IRQ_55_pending : 1;				// @17 GPU irq 55 pending
			bool GPU_IRQ_56_pending : 1;				// @18 GPU irq 56 pending
			bool GPU_IRQ_57_pending : 1;				// @19 GPU irq 57 pending
			bool GPU_IRQ_62_pending : 1;				// @20 GPU irq 62 pending
			unsigned reserved : 12;						// @21-31 reserved
		};
		uint32_t RawIrqBasicPending;					// Union to access all 32 bits as a uint32_t
	};
};

/*--------------------------------------------------------------------------}
{	ENABLE_BASIC_IRQ REGISTER BCM2835 ARM Peripheral manual page 117		}
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__)) EnableBasicIrqReg {
	union {
		struct __attribute__((__packed__)) {
			bool Enable_Timer_IRQ : 1;					// @0 Timer Irq enable
			bool Enable_Mailbox_IRQ : 1;				// @1 Mailbox Irq enable
			bool Enable_Doorbell0_IRQ : 1;				// @2 Arm Doorbell0 Irq enable
			bool Enable_Doorbell1_IRQ : 1;				// @3 Arm Doorbell0 Irq enable
			bool Enable_GPU0_halted_IRQ : 1;			// @4 GPU0 halted IRQ enable
			bool Enable_GPU1_halted_IRQ : 1;			// @5 GPU1 halted IRQ enable
			bool Enable_Illegal_access_type1 : 1;		// @6 Illegal access type 1 IRQ enable
			bool Enable_Illegal_access_type0 : 1;		// @7 Illegal access type 0 IRQ enable
			unsigned reserved : 24;						// @8-31 reserved
		};
		uint32_t RawEnableIrqBasic;						// Union to access all 32 bits as a uint32_t
	};
};

/* Irq registers BCM2835 ARM Peripherals manual, section 7.5  page 112 */
#define IRQ_BASIC_PENDING		((volatile const struct IrqBasicPendingReg*)(RPi_IO_Base_Addr + 0xB200))   /* read only hence const */
#define ENABLE_BASIC_IRQ		((volatile struct EnableBasicIrqReg*)(RPi_IO_Base_Addr + 0xB218))



/*--------------------------------------------------------------------------}
{	            TIMER_CONTROL PRE_SCALE VALUE ENUMERATION					}
{--------------------------------------------------------------------------*/
typedef enum {
	Clkdiv1 = 0,
	Clkdiv16 = 1,
	Clkdiv256 = 2,
	Prescale_invalid = 3
} TIMER_PRESCALE;

/*--------------------------------------------------------------------------}
{	   TIMER_CONTROL REGISTER BCM2835 ARM Peripheral manual page 197		}
{--------------------------------------------------------------------------*/
struct __attribute__((__packed__)) TimerControlReg {
	union {
		struct __attribute__((__packed__)) {
			unsigned unused : 1;						// @0 Unused bit
			bool Counter32Bit : 1;						// @1 Counter32 bit (16bit if false)
			TIMER_PRESCALE Prescale : 2;				// @2-3 Prescale  
			unsigned unused1 : 1;						// @4 Unused bit
			bool TimerIrqEnable : 1;					// @5 Timer irq enable
			unsigned unused2 : 1;						// @6 Unused bit
			bool TimerEnable : 1;						// @7 Timer enable
			unsigned reserved : 24;						// @8-31 reserved
		};
		uint32_t RawTimerControl;						// Union to access all 32 bits as a uint32_t
	};
};

/* Timer registers BCM2835 ARM Peripherals manual, section 14.2  page 196 */
#define TIMER_LOAD		((volatile uint32_t*)(RPi_IO_Base_Addr + 0xB400))
#define TIMER_CONTROL	((volatile struct TimerControlReg*)(RPi_IO_Base_Addr + 0xB408))
#define TIMER_IRQCLEAR	((volatile uint32_t*)(RPi_IO_Base_Addr + 0xB40C))


/*-Enable_Interrupts---------------------------------------------------------
 Self explanatory enables ARM core processor interrupts
 --------------------------------------------------------------------------*/
void Enable_Interrupts (void) {
	asm("mrs     r0, cpsr		\n\t"
		"bic     r0, r0, #0x80	\n\t"
		"msr     cpsr_c, r0");
}

/ * The interrupt routine we will call */
void __attribute__((interrupt("IRQ"))) c_irq_handler (void) {

	static int lit = 0;

	/* Clear the ARM Timer interrupt - it's the only interrupt we have
	enabled, so we want don't have to work out which interrupt source
	caused us to interrupt */
	*TIMER_IRQCLEAR = 0;				// Write any value to register to clear irq ... PAGE 198

	/* Flip the LED */
	if (lit) {
		RPI_Activity_Led(0);			// Turn Led off
		lit = 0;
	} else{
		RPI_Activity_Led(1);			// Turn LED on
		lit = 1;
	}
}


int main (void) {
    /* Enable the timer interrupt IRQ */
	ENABLE_BASIC_IRQ->Enable_Timer_IRQ = true;


    /* Setup the system timer interrupt */
	*TIMER_LOAD = 0x400;

    /* Setup the ARM Timer */
	TIMER_CONTROL->Counter32Bit = true;
	TIMER_CONTROL->Prescale = Clkdiv256;
	TIMER_CONTROL->TimerIrqEnable = true;
	TIMER_CONTROL->TimerEnable = true;

    /* Enable interrupts! */
    Enable_Interrupts();

	while (1) {

	}

	return(0);
}
So now you need to put the c irq handler address in the vector table

Code: Select all

_reset_h:                           .word   _start
_undefined_instruction_vector_h:    .word  hang
_software_interrupt_vector_h:       .word   hang
_prefetch_abort_vector_h:           .word   hang
_data_abort_vector_h:               .word   hang
_unused_handler_h:                  .word   _start
_interrupt_vector_h:                .word   c_irq_handler     /* HERE IT IS */
_fast_interrupt_vector_h:           .word   hang	
Finally you need to make sure you setup the stack pointer for IRQ. IRQ mode has it's own stack pointer it doesnt share it with other modes. So lets deal with proper way to do that. First your boot code at startup needs to change a little. What we do is go to each of the modes and set the stack pointer for that mode to a different place in memory.

Code: Select all

_start:
	ldr sp, =__usrsys_stack					;@ This is just a safety if all hell breaks loose
;@"================================================================"
;@ Now we need to put the processor in a state we can work with. 
;@ The bootloader could have left the CPU in any operation mode.
;@ Currently that is usually HYP_MODE and here we want to also save
;@ that initial CPU boot mode and address we might need later.
;@"================================================================"
	bl  RPi_CheckAndExitHypModeToSvcMode;	;@ First check for HYP_MODE and if in it drop out to SRV_MODE

;@"================================================================"
;@ Now setup stack pointers for the different CPU operation modes.
;@"================================================================"
    msr CPSR_c, #0xD1						;@ Switch to FIQ_MODE
	ldr sp, =__fiq_stack					;@ Set the stack pointer for that mode
	msr CPSR_c, #0xD2						;@ Switch to IRQ_MODE
    ldr sp, =__irq_stack					;@ Set the stack pointer for that mode
	msr CPSR_c, #0xD3						;@ Switch back to SRV_MODE
    ldr sp, =__svc_stack					;@ Set the stack pointer for that mode
Now finally we use the linker file and add a stack segment which will position the stack pointers. So this gets added to your linker directive file you called rpi.ld.

Code: Select all

.stack :
    {
        . = ALIGN(8);  /* Stack must always be aligned to 8 byte boundary AAPCS call standard */
        __stack_start__ = .;
        . = . + 512;    /* fiq stack size */
        __fiq_stack = .;
        . = . + 16384;   /* usr & sys stack size (common) */
        __usrsys_stack = .;
        . = . + 16384;  /* svc stack size (start-up) */
        __svc_stack = .;
        . = . + 4096;   /* irq stack size */
        __irq_stack = .;
        . = . + 512;    /* mon stack size */
        __mon_stack = .;
        . = . + 512;    /* hyp stack size */
        __hyp_stack = .;
        . = . + 512;    /* und stack size */
        __und_stack = .;
        . = ALIGN(8);
        __stack_end__ = .;      
    }
By now it should be obvious to you what it does .. AKA it allocates memory blocks to each of the stacks for each different mode. You can control the size and position of each stack from there.

It you added the code correct you should now have the good old blinky led running on your Pi3 :-)

Makogan
Posts: 71
Joined: Tue May 16, 2017 9:17 pm

Re: Interrupts time and memory?

Tue Jun 20, 2017 9:15 pm

I am not sure why, but the msr instructions are destroying my code (If I comment them out the dreaded color palette appears and never goes away).

LdB
Posts: 440
Joined: Wed Dec 07, 2016 2:29 pm

Re: Interrupts time and memory?

Wed Jun 21, 2017 1:32 am

Your repo is not up todate :-(
I really need to see your linker file it has to be correct now we are going to use it to set the stack pointers

Update: Okay you need to read http://wiki.osdev.org/ARM_RaspberryPi
start.elf loads the kernel.img at 0x00008000, puts a few opcodes at 0x00000000 and the ATAGS at 0x00000100 and at last the ARM CPU is started. The CPU starts executing at 0x00000000, where it will initialize r0, r1 and r2 and jump to 0x00008000 where the kernel image starts.
You can't load the vector table how you currently are trying to do via the linker :-)

Just turn your vector table to a data block and copy it to address 0x00000000 removing the section from the linker file and you should be good to roll.

Anyhow here is the patched boot.S file (Update: You will need to add the vector table copy code before exit to c code)

Code: Select all

//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/*
* Authors: Camilo Talero
*
*
* File type: ARM Assembly
*
* This file contains assembly code needed to initialize the hardware of the
*Raspberry PI™ before execution of the main kernel loop
*/
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.section .init
.global _start

/* CHANGES ADDED BY LdB */
_start:
	ldr sp, =__usrsys_stack					;@ This is just a safety if all hell breaks loose
    bl  RPi_CheckAndExitHypModeToSvcMode;   ;@ First check for HYP_MODE and if in it drop out to SRV_MODE

;@"================================================================"
;@ Now setup stack pointers for the different CPU operation modes.
;@"================================================================"
    msr CPSR_c, #0xD1						;@ Switch to FIQ_MODE
	ldr sp, =__fiq_stack					;@ Set the stack pointer for that mode
	msr CPSR_c, #0xD2						;@ Switch to IRQ_MODE
    ldr sp, =__irq_stack					;@ Set the stack pointer for that mode
	msr CPSR_c, #0xD3						;@ Switch back to SRV_MODE
    ldr sp, =__svc_stack					;@ Set the stack pointer for that mode

@"================================================================"
;@ PI NSACR regsister setup for access to floating point unit
;@ Cortex A-7 => Section 4.3.34. Non-Secure Access Control Register
;@ Cortex A-53 => Section 4.5.32. Non-Secure Access Control Register
;@"================================================================"
   mrc p15, 0, r0, c1, c1, 2            ;@ Read NSACR into R0
   cmp r0, #0x00000C00                  ;@ Access turned on or in AARCH32 mode and can not touch register or EL3 fault
   beq .free_to_enable_fpu
   orr r0, r0, #0x3<<10               ;@ Set access to both secure and non secure modes
   mcr p15, 0, r0, c1, c1, 2            ;@ Write NSACR
;@"================================================================"
;@ Bring fpu online
;@"================================================================"
.free_to_enable_fpu:
   mrc p15, 0, r0, c1, c0, #2            ;@ R0 = Access Control Register
   orr r0, #(0x300000 + 0xC00000)         ;@ Enable Single & Double Precision
   mcr p15,0,r0,c1,c0, #2               ;@ Access Control Register = R0
   mov r0, #0x40000000                  ;@ R0 = Enable VFP
   vmsr fpexc, r0                     ;@ FPEXC = R0
 ;@"================================================================"
;@ Enable L1 cache
;@"================================================================"
    mrc p15,0,r0,c1,c0,0               ;@ R0 = System Control Register

/*==========================================================================}
;{           RASPBERRY PI LEVEL L1 CACHE CONSTANTS DEFINED                }
;{=========================================================================*/
#define SCTLR_ENABLE_DATA_CACHE         0x4
#define SCTLR_ENABLE_BRANCH_PREDICTION   0x800
#define SCTLR_ENABLE_INSTRUCTION_CACHE  0x1000

    // Enable caches and branch prediction
    orr r0, #SCTLR_ENABLE_BRANCH_PREDICTION
    orr r0, #SCTLR_ENABLE_DATA_CACHE
    orr r0, #SCTLR_ENABLE_INSTRUCTION_CACHE

    mcr p15,0,r0,c1,c0,0               ;@ System Control Register = R0*/
 /* END OF CHANGES ADDED BY LdB */
    
    
    b       _cstartup
.balign	4
.ltorg									;@ Tell assembler ltorg data for init code can go here

 /* CODED ADDED BY LdB */
/* "PROVIDE C FUNCTION: bool RPi_CheckAndExitHypModeToSvcMode (void);" */
/*.section .text.RPi_CheckAndExitHypModeToSvcMode, "ax", %progbits
.balign   4
.type RPi_CheckAndExitHypModeToSvcMode, %function
.syntax unified
.arm
;@"================================================================"
;@ RPi_CheckAndExitHypModeToSvcMode -- Composite Pi1, Pi2 & Pi3 code
;@ Return: Drops out from HYP_MODE to SRV_MODE and FIQ/IRQ disabled
;@         If not in HYP_MODE will exit unchanged
;@"================================================================"
RPi_CheckAndExitHypModeToSvcMode:
   mrs r0, cpsr                     ;@ Fetch the cpsr register             
   and r1, r0, #0x1F                  ;@ Mask off the arm mode bits in register                            
    cmp r1, #0x1A                     ;@ check we are in HYP_MODE AKA register reads 1A                     
   beq .WeHaveHyperMode
   mov r0, #0;                        ;@ return false
   bx lr                           ;@ Return we are not in hypermode
.WeHaveHyperMode:
   bic r0, r0, #0x1F                  ;@ Clear the mode bits                   
   orr r0, r0, #0xD3                  ;@ We want SRV_MODE with IRQ/FIQ disabled         
   mov r1, #0                        ;@ Make sure CNTVOFF to 0 before exit HYP mode
   mcrr p15, #4, r1, r1, cr14            ;@ We do not want our clocks going fwd or backwards
   orr r0, r0, #0x100                  ;@ Set data abort mask    
   msr spsr_cxsf, r0                  ;@ Load our request into return status register
   mov r0, #1;                        ;@ return true
   /* I borrowed this trick from Ultibo because ARM6 code running on an ARM7/8 needs this opcode  */
   /* The ARM6 compiler does not know these instructions so it is a way to get needed opcode here */
    /* So our ARM6 code can drop an arm7 or arm8 out of HYP mode and run on an ARM7/8.             */
    /* Native ARM7/8 compilers already understand the OPCODE but do not mind it this way either      */        
   /*.long 0xE12EF30E                  ;@ "msr ELR_hyp, lr"
   .long 0xE160006E                  ;@ "eret"   
;@ No ltorg data required for this function 
.size   RPi_CheckAndExitHypModeToSvcMode, .-RPi_CheckAndExitHypModeToSvcMode*/
Here is the my guess at your kernel.ld linker file given what is in the old repo. I was consistent and gave you a label in each section so you can get the start and end address of each section in code if you need. I know you were setting up a malloc so I gave you a __heap_start__ label so you can know the address that marks the start of the free space beyond your code. I have removed the vector table section.

Code: Select all

SECTIONS 
{
 .init 0x8000 : 
  {
    KEEP(*(.init))
  }

  .text : 
  {
    . = ALIGN(4);
    __text_start__ = .;              /* Just a label to get text section start addr if you need */
    *(.text .text.* .gnu.linkonce.t.*)
    . = ALIGN(4);
    __text_end__ = .;               /* Just a label to get text section end addr if you need */
  }

  .data : 
  {
    . = ALIGN(4);
    __data_start__ = .;            /* Just a label to get data section start addr if you need */
    *(.data .data.* .gnu.linkonce.d.*)
    . = ALIGN(4);
    __data_end__ = .;             /* Just a label to get data section end addr if you need */
  }

.stack :
    {
        . = ALIGN(8);  	   /* Stack must always be aligned to 8 byte boundary AAPCS call standard */
        __stack_start__ = .;     /* Just a label to get stack section start addr if you need */
        . = . + 512;    /* fiq stack size */
        __fiq_stack = .;
        . = . + 16384;   /* usr & sys stack size (common) */
        __usrsys_stack = .;
        . = . + 16384;  /* svc stack size (start-up) */
        __svc_stack = .;
        . = . + 4096;   /* irq stack size */
        __irq_stack = .;
        . = . + 512;    /* mon stack size */
        __mon_stack = .;
        . = . + 512;    /* hyp stack size */
        __hyp_stack = .;
        . = . + 512;    /* und stack size */
        __und_stack = .;
        . = ALIGN(8);
        __stack_end__ = .;      /* Just a label to get stack section end addr if you need */
         
    }

  .bss :
    {
        . = ALIGN(4);
        __bss_start__ = .;     /* Just a label to get BSS section start addr if you need */
        *(.bss)
        . = ALIGN(4);
        __bss_end__ = .;      /* Just a label to get BSS section end addr if you need */
   }
  
  .kernel_end :
  {
    KEEP(*(.kernel_end))
  }

  .ARM.attributes 0 : { *(.ARM.attributes) }

 /* This must be last section */
 /* Any memory from here is free to use so this is end of code and start of heap */
.Heap :
    {
     . = ALIGN(4);
     __heap_start__ = .;     /* Just a label to get HEAP section start addr if you need */; 
    }

}
Last edited by LdB on Wed Jun 21, 2017 3:31 am, edited 4 times in total.

Makogan
Posts: 71
Joined: Tue May 16, 2017 9:17 pm

Re: Interrupts time and memory?

Wed Jun 21, 2017 3:35 am

Weird, it won't let me print the contents of any address

(i.e doing print(0x8000) works, but doing print(uint32_t *(uint32_t)0x8000) won't)

About msr breaking the code. Well, yes it's destroying it, If I call set_LED(ON) immediately before the msr instructions the led turns on, if I do it immediately after the led remains off. I am not sure why either of these things would happen.

LdB
Posts: 440
Joined: Wed Dec 07, 2016 2:29 pm

Re: Interrupts time and memory?

Wed Jun 21, 2017 3:41 am

You might want to think more carefully about this line :-)
print(uint32_t *(uint32_t)0x8000)

Perhaps this might help
int* pt;
print(*pt);

Anyhow I don't need the print result I found it documented about the boot sequence and you can't do what you were doing so I updated the post. You must have been reading before I changed it :-)

I just physically define the table as a normal block in the equivalent of your Boot.s file

Code: Select all

_isr_Table:
    ldr pc, _reset_h
    ldr pc, _undefined_instruction_vector_h
    ldr pc, _software_interrupt_vector_h
    ldr pc, _prefetch_abort_vector_h
    ldr pc, _data_abort_vector_h
    ldr pc, _unused_handler_h
    ldr pc, _interrupt_vector_h
    ldr pc, _fast_interrupt_vector_h

_reset_h:                           .word   _start
_undefined_instruction_vector_h:    .word  hang
_software_interrupt_vector_h:       .word   hang
_prefetch_abort_vector_h:           .word   hang
_data_abort_vector_h:               .word   hang
_unused_handler_h:                  .word   _start
_interrupt_vector_h:                .word   c_irq_handler
_fast_interrupt_vector_h:           .word   hang	
Then copy it into 0x0000 sometime before I enter the C code

Code: Select all

;@"================================================================"
;@  We are getting close to handing over to C so we need to copy the 
;@  ISR table to position 0x0000 so interrupts can be used if wanted 
;@"================================================================"
	ldr r0, = _isr_Table					;@ Address of isr_Table
	mov     r1, #0x0000						;@ Destination 0x0000
    ldmia   r0!,{r2, r3, r4, r5, r6, r7, r8, r9}
    stmia   r1!,{r2, r3, r4, r5, r6, r7, r8, r9}
    ldmia   r0!,{r2, r3, r4, r5, r6, r7, r8, r9}
    stmia   r1!,{r2, r3, r4, r5, r6, r7, r8, r9}
Last edited by LdB on Wed Jun 21, 2017 4:02 am, edited 1 time in total.

Makogan
Posts: 71
Joined: Tue May 16, 2017 9:17 pm

Re: Interrupts time and memory?

Wed Jun 21, 2017 3:56 am

Update. You are right about the linker part. I couldn't print the addresses but changing it so that it's similar to the valvers version (i.e load the vector table at running time and not linking time) seems to give better results (I can set the led on the interrupts now) but I still can't fix the stack problem.....

The lesson to be here is, if somebody else figured it out, don't try to re-invent the wheel.

LdB
Posts: 440
Joined: Wed Dec 07, 2016 2:29 pm

Re: Interrupts time and memory?

Wed Jun 21, 2017 4:04 am

Update your repo and I will have a quick look :-)

Makogan
Posts: 71
Joined: Tue May 16, 2017 9:17 pm

Re: Interrupts time and memory?

Wed Jun 21, 2017 4:07 am

Should be up to date now

LdB
Posts: 440
Joined: Wed Dec 07, 2016 2:29 pm

Re: Interrupts time and memory?

Wed Jun 21, 2017 4:07 am

Yep it's still wrong one sec fixing it :-)

Okay here is patched Boot.S now onto linker file

Code: Select all

//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/*
* Authors: Camilo Talero, LDB
*
*
* File type: ARM Assembly
*
* This file contains assembly code needed to initialize the hardware of the
*Raspberry PI™ before execution of the main kernel loop
*/
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.section.init
.global _start

_start :
ldr     sp, =0x8000 // initialize stack pointer

/* CHANGES ADDED BY LdB */
bl  RPi_CheckAndExitHypModeToSvcMode; ; @ First check for HYP_MODE and if in it drop out to SRV_MODE
; @"=============================================================== = "
; @ Now setup stack pointers for the different CPU operation modes.
; @"=============================================================== = "
msr CPSR_c, #0xD1; @ Switch to FIQ_MODE
ldr sp, =__fiq_stack; @ Set the stack pointer for that mode
msr CPSR_c, #0xD2; @ Switch to IRQ_MODE
ldr sp, =__irq_stack; @ Set the stack pointer for that mode
msr CPSR_c, #0xD3; @ Switch back to SRV_MODE
ldr sp, =__svc_stack; @ Set the stack pointer for that mode

@"=============================================================== = "
; @ PI NSACR regsister setup for access to floating point unit
; @ Cortex A - 7 = > Section 4.3.34.Non - Secure Access Control Register
; @ Cortex A - 53 = > Section 4.5.32.Non - Secure Access Control Register
; @"=============================================================== = "
mrc p15, 0, r0, c1, c1, 2; @ Read NSACR into R0
cmp r0, #0x00000C00; @ Access turned on or in AARCH32 mode and can not touch register or EL3 fault
beq.free_to_enable_fpu
orr r0, r0, #0x3 << 10; @ Set access to both secure and non secure modes
mcr p15, 0, r0, c1, c1, 2; @ Write NSACR
; @"=============================================================== = "
; @ Bring fpu online
; @"=============================================================== = "
.free_to_enable_fpu:
mrc p15, 0, r0, c1, c0, #2; @ R0 = Access Control Register
orr r0, #(0x300000 + 0xC00000); @ Enable Single & Double Precision
mcr p15, 0, r0, c1, c0, #2; @ Access Control Register = R0
mov r0, #0x40000000; @ R0 = Enable VFP
vmsr fpexc, r0; @ FPEXC = R0
; @"=============================================================== = "
; @ Enable L1 cache
; @"=============================================================== = "
mrc p15, 0, r0, c1, c0, 0; @ R0 = System Control Register

/*==========================================================================}
;{           RASPBERRY PI LEVEL L1 CACHE CONSTANTS DEFINED                }
;{=========================================================================*/
#define SCTLR_ENABLE_DATA_CACHE         0x4
#define SCTLR_ENABLE_BRANCH_PREDICTION   0x800
#define SCTLR_ENABLE_INSTRUCTION_CACHE  0x1000

// Enable caches and branch prediction
orr r0, #SCTLR_ENABLE_BRANCH_PREDICTION
orr r0, #SCTLR_ENABLE_DATA_CACHE
orr r0, #SCTLR_ENABLE_INSTRUCTION_CACHE

mcr p15, 0, r0, c1, c0, 0; @ System Control Register = R0

; @"=============================================================== = "
; @  We are getting close to handing over to C so we need to copy the
; @  ISR table to position 0x0000 so interrupts can be used if wanted
; @"=============================================================== = "
ldr r0, = _v_table; @ Address of _v_Table
mov     r1, #0x0000; @ Destination 0x0000
ldmia   r0!, { r2, r3, r4, r5, r6, r7, r8, r9 }
stmia   r1!, { r2, r3, r4, r5, r6, r7, r8, r9 }
ldmia   r0!, { r2, r3, r4, r5, r6, r7, r8, r9 }
stmia   r1!, { r2, r3, r4, r5, r6, r7, r8, r9 }
/* END OF CHANGES ADDED BY LdB */


b       _cstartup
.balign	4
.ltorg; @ Tell assembler ltorg data for this code can go here

/* This is the vector table can go anywhere it's moved to 0x0000 by start code */

_v_table :
ldr pc, _reset_h
ldr pc, _undefined_instruction_vector_h
ldr pc, _software_interrupt_vector_h
ldr pc, _prefetch_abort_vector_h
ldr pc, _data_abort_vector_h
ldr pc, _unused_handler_h
ldr pc, _interrupt_vector_h
ldr pc, _fast_interrupt_vector_h

	_reset_h : .word   _start
	_undefined_instruction_vector_h : .word  undefined_instruction_vector
	_software_interrupt_vector_h : .word   software_interrupt_vector
	_prefetch_abort_vector_h : .word   prefetch_abort_vector
	_data_abort_vector_h : .word   _start
	_unused_handler_h : .word   _start
	_interrupt_vector_h : .word   interrupt_vector
	_fast_interrupt_vector_h : .word   fast_interrupt_vector



/* CODED ADDED BY LdB */
/* "PROVIDE C FUNCTION: bool RPi_CheckAndExitHypModeToSvcMode (void);" */
.section.text.RPi_CheckAndExitHypModeToSvcMode, "ax", %progbits
.balign   4
.type RPi_CheckAndExitHypModeToSvcMode, %function
.syntax unified
.arm
; @"=============================================================== = "
; @ RPi_CheckAndExitHypModeToSvcMode -- Composite Pi1, Pi2 & Pi3 code
; @ Return: Drops out from HYP_MODE to SRV_MODE and FIQ / IRQ disabled
; @         If not in HYP_MODE will exit unchanged
; @"=============================================================== = "
RPi_CheckAndExitHypModeToSvcMode:
mrs r0, cpsr; @ Fetch the cpsr register
and r1, r0, #0x1F; @ Mask off the arm mode bits in register
cmp r1, #0x1A; @ check we are in HYP_MODE AKA register reads 1A
beq.WeHaveHyperMode
mov r0, #0; ; @ return false
bx lr; @ Return we are not in hypermode
.WeHaveHyperMode:
bic r0, r0, #0x1F; @ Clear the mode bits
orr r0, r0, #0xD3; @ We want SRV_MODE with IRQ / FIQ disabled
mov r1, #0; @ Make sure CNTVOFF to 0 before exit HYP mode
mcrr p15, #4, r1, r1, cr14; @ We do not want our clocks going fwd or backwards
orr r0, r0, #0x100; @ Set data abort mask
msr spsr_cxsf, r0; @ Load our request into return status register
mov r0, #1; ; @ return true
bx lr
/* I borrowed this trick from Ultibo because ARM6 code running on an ARM7/8 needs this opcode  */
/* The ARM6 compiler does not know these instructions so it is a way to get needed opcode here */
/* So our ARM6 code can drop an arm7 or arm8 out of HYP mode and run on an ARM7/8.             */
/* Native ARM7/8 compilers already understand the OPCODE but do not mind it this way either      */
.long 0xE12EF30E; @ "msr ELR_hyp, lr"
.long 0xE160006E; @ "eret"
; @ No ltorg data required for this function
.size   RPi_CheckAndExitHypModeToSvcMode, . - RPi_CheckAndExitHypModeToSvcMode
Okay found real problem you changed the linker file .. remember the bit about the text section
. .. this won't work.

Code: Select all

text : 
  {
    KEEP(*(.text))
}
You need to understand why .. look at my code for coming out of HYP_MODE and what section I declare

Code: Select all

.section.text.RPi_CheckAndExitHypModeToSvcMode
It doesn't match your text section statement *.text, it does meet section *.text.* !!!!!
I changed you line to

Code: Select all

*(.text .text.* .gnu.linkonce.t.*)
The .text does all your code sections
.text.* is for the section on that hyp change code (.section.text.RPi_CheckAndExitHypModeToSvcMode)
.gnu.linkonce.t is for GCC compiler if you use initialized text constants

I would have suggested you just remove the section name from my code so it goes to default .text but you will run into the same problem if you start writing object code the GCC compiler will put subsections after the text.

So the line I gave you is the minimum safe line for a text section on a C/C++ compiler ... shorten it at your own peril.
Last edited by LdB on Wed Jun 21, 2017 4:33 am, edited 2 times in total.

Makogan
Posts: 71
Joined: Tue May 16, 2017 9:17 pm

Re: Interrupts time and memory?

Wed Jun 21, 2017 4:29 am

I changed the script by copy pasting the relevant sections in your linker script (pushed changes but msr instructions commented on latest push) However uncommenting the msr instructions still results in the color palette of doom

In other words this is the current linker:

Code: Select all

SECTIONS 
{
  /*.vector_table 0x0 :
  {
    KEEP(*(.vector_table))
  } */

 .init 0x8000 : 
  {
    KEEP(*(.init))
  }

  .text : 
  {
    . = ALIGN(4);
    __text_start__ = .;              /* Just a label to get text section start addr if you need */
    *(.text .text.* .gnu.linkonce.t.*)
    . = ALIGN(4);
    __text_end__ = .;               /* Just a label to get text section end addr if you need */
  }

  .data : 
  {
    . = ALIGN(4);
    __data_start__ = .;            /* Just a label to get data section start addr if you need */
    *(.data .data.* .gnu.linkonce.d.*)
    . = ALIGN(4);
    __data_end__ = .;             /* Just a label to get data section end addr if you need */
  }

  .bss :
  {
      . = ALIGN(4);
      __bss_start__ = .;
      KEEP(*(.bss))
      . = ALIGN(4);
      __bss_end__ = .;
  }

  /* Made by LDB */
  .stack :
  {
      . = ALIGN(8);  /* Stack must always be aligned to 8 byte boundary AAPCS call standard */
      __stack_start__ = .;
      . = . + 512;    /* fiq stack size */
      __fiq_stack = .;
      . = . + 16384;   /* usr & sys stack size (common) */
      __usrsys_stack = .;
      . = . + 16384;  /* svc stack size (start-up) */
      __svc_stack = .;
      . = . + 4096;   /* irq stack size */
      __irq_stack = .;
      . = . + 512;    /* mon stack size */
      __mon_stack = .;
      . = . + 512;    /* hyp stack size */
      __hyp_stack = .;
      . = . + 512;    /* und stack size */
      __und_stack = .;
      . = ALIGN(8);
      __stack_end__ = .;      
  }
/* end of LDB contribution */
  
  .Heap :
    {
     . = ALIGN(4);
     kernel_end = .;     /* Just a label to get HEAP section start addr if you need */; 
    }
}
Last edited by Makogan on Wed Jun 21, 2017 4:39 am, edited 1 time in total.

LdB
Posts: 440
Joined: Wed Dec 07, 2016 2:29 pm

Re: Interrupts time and memory?

Wed Jun 21, 2017 4:37 am

You changed all your stuff in the repo back so I can't compile :-(

The repo is showing a good kernel.ld file but the boot.s is still dorked.

Can you map the kernel.elf file you are producing with
g:\pi\gcc_pi_6_2\bin\arm-none-eabi-nm kernel.elf > kernel.map

The path will be different for you :-)

I am going to give you my base code on a zip to try.

Makogan
Posts: 71
Joined: Tue May 16, 2017 9:17 pm

Re: Interrupts time and memory?

Wed Jun 21, 2017 4:42 am

Weird, It compiles on my system.

This is teh output of the map (arm-none-eabi-nm build/kernel.elf > kernel.map)

Code: Select all

00008b04 R basic_font
00008930 T blink
00008fd8 B __bss_end__
00008f30 B __bss_start__
0000857c T clear_time_irq
00008234 T _cstartup
00008f28 D __data_end__
00008f20 D __data_start__
0000821c T fast_interrupt_vector
000091d8 B __fiq_stack
00008f50 B first_block
00008f3c B font_size
0000804c t .free_to_enable_fpu
00008594 T get_time_cycle
00008f44 B heap_end
00008f40 B heap_start
000125d8 B __hyp_stack
00008fb8 B index
00008a10 T init_display
000086c8 T init_memory_manager
000081d8 T interrupt_vector
00008f20 D irq_controller
000121d8 B __irq_stack
00000000 N Kernel_End
000127d8 ? kernel_end
00008654 T kernel_main
00008f20 d .LANCHOR0
00008f30 b .LANCHOR0
00008f24 d .LANCHOR0
00008f40 b .LANCHOR0
00008f60 b .LANCHOR0
00008f04 r .LANCHOR1
00008f60 B mailbox_message
00008f30 B main_cursor
00008fbc B main_monitor
00008740 T memory_alloc
0000880c T memory_calloc
00008844 T memory_free
000087ec T memory_re_alloc
000123d8 B __mon_stack
000081c0 T prefetch_abort_vector
00008874 T read_from_mailbox
00008024 t _reset_
00008614 t RPi_CheckAndExitHypModeToSvcMode
00008f38 B selected_font
000088a0 T set_LED
00008560 T set_time_irq
000081ac T software_interrupt_vector
000127d8 B __stack_end__
00008f4c B stack_end
00008fd8 B __stack_start__
00008f48 B stack_start
00008000 T _start
000111d8 B __svc_stack
00008f24 D system_timer
00008ab4 T __text_end__
000080a0 T __text_start__
00008198 T undefined_instruction_vector
000127d8 B __und_stack
0000d1d8 B __usrsys_stack
00008004 t _v_table
000085a8 T wait
0000862c t .WeHaveHyperMode
000082a0 T _Z10init_printm
00008718 T _Z14increment_heapm
000082c4 T _Z15init_char_imagePKcmPm
00008850 T _Z16write_to_mailboxm7Channel
000080a0 T _Z17enable_select_irqh
000080f4 T _Z18disable_select_irqh
00008158 T _Z18interrupt_enable_Fv
00008150 T _Z18interrupt_enable_Iv
00008180 T _Z19interrupt_disable_Fv
00008170 T _Z19interrupt_disable_Iv
00008148 T _Z19interrupt_enable_IFv
00008160 T _Z20interrupt_disable_IFv
00008958 T _Z24set_init_display_messageP12Display_info
000083b0 T _Z4itosm
00008540 T _Z5printm
00008424 T _Z5printPc
0000855c T _Z5printPKc
00008190 T _Z7restorev
0000833c T _Z8drawCharPmmmm

LdB
Posts: 440
Joined: Wed Dec 07, 2016 2:29 pm

Re: Interrupts time and memory?

Wed Jun 21, 2017 5:01 am

Makogan wrote:Weird, It compiles on my system.
It's not weird you have your own file with the #includes to the diamond brackets < >. That is forcibly restricted to system files on my system for safety throughout our office. You can have some fool name a file the same as one of the system files (usually because they don't know them) and guess what happens :-)

You have set your paths up probably within the make file but it won't get past my code security checks on what we use as make and so I can't compile your repo. For us private code goes in #include " " .. system files goes in #include < > and we enforce it via our make system so fools can't waste our time. We could go further about the system files being precompiled to save us compilation time but you have enough detail and it's quite normal (https://www.programmerinterview.com/ind ... d-include/)

Map file shows the problem .. your Boot.S is as per Repo AKA wrong still
00008004 t _v_table ... That is the giveaway what the hell is that doing there

Anyhow here is my code if you want to look at it. I also have precompiled img files for Pi1,Pi2,Pi3 there.
http://dropcanvas.com/#qZh11BJw43T26c

LdB
Posts: 440
Joined: Wed Dec 07, 2016 2:29 pm

Re: Interrupts time and memory?

Wed Jun 21, 2017 7:24 am

Lets try and explain what is wrong with your vector table

Code: Select all

_v_table:
        b _reset_
        b undefined_instruction_vector
        b software_interrupt_vector
        b prefetch_abort_vector
        b _reset_
        b _reset_
        b interrupt_vector
        b fast_interrupt_vector
https://community.arm.com/processors/b/ ... -explained

Code: Select all

A relative branch is one where the target address is calculated based on the value of the current pc (program counter). Given the example above, an assembler would work out that the target label is eight bytes ahead of the b target instruction (in ARM code) and then generate a relative branch which means 'jump forward by eight bytes'. Relative branches are essential for position-independant code, which is expected to run correctly at any location in memory. The most common relative branches on ARM are single instructions and tend to be the most efficient branches available, though they have limited range.
So your table is a set of branches relative to where it currently is ... and then you are going to move it :-)

So lets look at your map file
00008004 t _v_table
00008024 t _reset_

So "b _reset_" translates as jump 0x20 bytes forward.
When you copy v_table to 0x0000 so _v_table is 0x0004 the it jumps to 0x0024 ... and guess what there is no code there it is just random rubbish.

That is why the vector table MUST BE ABSOLUTE and now go back and look how you can do that on the arm link above again.
The last form shown "ldr pc, =address" meets the requirements we need

Code: Select all

 ldr pc, _reset_h
/* .. later on */
_reset_h:                           .word   _start
I do it that way but you can use the literal macro if you want

Code: Select all

 ldr pc, =_start
I just find I have nice labels the first way so I know what vector entry I am changing.
Now review the vector table in the boot code I want you to use (you will have to scroll to even find it) :-)

Code: Select all

//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/*
* Authors: Camilo Talero, LDB
*
*
* File type: ARM Assembly
*
* This file contains assembly code needed to initialize the hardware of the
*Raspberry PI™ before execution of the main kernel loop
*/
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.section.init
.global _start

_start :
ldr     sp, =0x8000 // initialize stack pointer

/* CHANGES ADDED BY LdB */
bl  RPi_CheckAndExitHypModeToSvcMode; ; @ First check for HYP_MODE and if in it drop out to SRV_MODE
; @"=============================================================== = "
; @ Now setup stack pointers for the different CPU operation modes.
; @"=============================================================== = "
msr CPSR_c, #0xD1; @ Switch to FIQ_MODE
ldr sp, =__fiq_stack; @ Set the stack pointer for that mode
msr CPSR_c, #0xD2; @ Switch to IRQ_MODE
ldr sp, =__irq_stack; @ Set the stack pointer for that mode
msr CPSR_c, #0xD3; @ Switch back to SRV_MODE
ldr sp, =__svc_stack; @ Set the stack pointer for that mode

@"=============================================================== = "
; @ PI NSACR regsister setup for access to floating point unit
; @ Cortex A - 7 = > Section 4.3.34.Non - Secure Access Control Register
; @ Cortex A - 53 = > Section 4.5.32.Non - Secure Access Control Register
; @"=============================================================== = "
mrc p15, 0, r0, c1, c1, 2; @ Read NSACR into R0
cmp r0, #0x00000C00; @ Access turned on or in AARCH32 mode and can not touch register or EL3 fault
beq.free_to_enable_fpu
orr r0, r0, #0x3 << 10; @ Set access to both secure and non secure modes
mcr p15, 0, r0, c1, c1, 2; @ Write NSACR
; @"=============================================================== = "
; @ Bring fpu online
; @"=============================================================== = "
.free_to_enable_fpu:
mrc p15, 0, r0, c1, c0, #2; @ R0 = Access Control Register
orr r0, #(0x300000 + 0xC00000); @ Enable Single & Double Precision
mcr p15, 0, r0, c1, c0, #2; @ Access Control Register = R0
mov r0, #0x40000000; @ R0 = Enable VFP
vmsr fpexc, r0; @ FPEXC = R0
; @"=============================================================== = "
; @ Enable L1 cache
; @"=============================================================== = "
mrc p15, 0, r0, c1, c0, 0; @ R0 = System Control Register

/*==========================================================================}
;{           RASPBERRY PI LEVEL L1 CACHE CONSTANTS DEFINED                }
;{=========================================================================*/
#define SCTLR_ENABLE_DATA_CACHE         0x4
#define SCTLR_ENABLE_BRANCH_PREDICTION   0x800
#define SCTLR_ENABLE_INSTRUCTION_CACHE  0x1000

// Enable caches and branch prediction
orr r0, #SCTLR_ENABLE_BRANCH_PREDICTION
orr r0, #SCTLR_ENABLE_DATA_CACHE
orr r0, #SCTLR_ENABLE_INSTRUCTION_CACHE

mcr p15, 0, r0, c1, c0, 0; @ System Control Register = R0

; @"=============================================================== = "
; @  We are getting close to handing over to C so we need to copy the
; @  ISR table to position 0x0000 so interrupts can be used if wanted
; @"=============================================================== = "
ldr r0, = _v_table; @ Address of _v_Table
mov     r1, #0x0000; @ Destination 0x0000
ldmia   r0!, { r2, r3, r4, r5, r6, r7, r8, r9 }
stmia   r1!, { r2, r3, r4, r5, r6, r7, r8, r9 }
ldmia   r0!, { r2, r3, r4, r5, r6, r7, r8, r9 }
stmia   r1!, { r2, r3, r4, r5, r6, r7, r8, r9 }
/* END OF CHANGES ADDED BY LdB */


b       _cstartup
.balign   4
.ltorg; @ Tell assembler ltorg data for this code can go here

/* This is the vector table can go anywhere it's moved to 0x0000 by start code */

_v_table :
ldr pc, _reset_h
ldr pc, _undefined_instruction_vector_h
ldr pc, _software_interrupt_vector_h
ldr pc, _prefetch_abort_vector_h
ldr pc, _data_abort_vector_h
ldr pc, _unused_handler_h
ldr pc, _interrupt_vector_h
ldr pc, _fast_interrupt_vector_h

   _reset_h : .word   _start
   _undefined_instruction_vector_h : .word  undefined_instruction_vector
   _software_interrupt_vector_h : .word   software_interrupt_vector
   _prefetch_abort_vector_h : .word   prefetch_abort_vector
   _data_abort_vector_h : .word   _start
   _unused_handler_h : .word   _start
   _interrupt_vector_h : .word   interrupt_vector
   _fast_interrupt_vector_h : .word   fast_interrupt_vector



/* CODED ADDED BY LdB */
/* "PROVIDE C FUNCTION: bool RPi_CheckAndExitHypModeToSvcMode (void);" */
.section.text.RPi_CheckAndExitHypModeToSvcMode, "ax", %progbits
.balign   4
.type RPi_CheckAndExitHypModeToSvcMode, %function
.syntax unified
.arm
; @"=============================================================== = "
; @ RPi_CheckAndExitHypModeToSvcMode -- Composite Pi1, Pi2 & Pi3 code
; @ Return: Drops out from HYP_MODE to SRV_MODE and FIQ / IRQ disabled
; @         If not in HYP_MODE will exit unchanged
; @"=============================================================== = "
RPi_CheckAndExitHypModeToSvcMode:
mrs r0, cpsr; @ Fetch the cpsr register
and r1, r0, #0x1F; @ Mask off the arm mode bits in register
cmp r1, #0x1A; @ check we are in HYP_MODE AKA register reads 1A
beq.WeHaveHyperMode
mov r0, #0; ; @ return false
bx lr; @ Return we are not in hypermode
.WeHaveHyperMode:
bic r0, r0, #0x1F; @ Clear the mode bits
orr r0, r0, #0xD3; @ We want SRV_MODE with IRQ / FIQ disabled
mov r1, #0; @ Make sure CNTVOFF to 0 before exit HYP mode
mcrr p15, #4, r1, r1, cr14; @ We do not want our clocks going fwd or backwards
orr r0, r0, #0x100; @ Set data abort mask
msr spsr_cxsf, r0; @ Load our request into return status register
mov r0, #1; ; @ return true
bx lr
/* I borrowed this trick from Ultibo because ARM6 code running on an ARM7/8 needs this opcode  */
/* The ARM6 compiler does not know these instructions so it is a way to get needed opcode here */
/* So our ARM6 code can drop an arm7 or arm8 out of HYP mode and run on an ARM7/8.             */
/* Native ARM7/8 compilers already understand the OPCODE but do not mind it this way either      */
.long 0xE12EF30E; @ "msr ELR_hyp, lr"
.long 0xE160006E; @ "eret"
; @ No ltorg data required for this function
.size   RPi_CheckAndExitHypModeToSvcMode, . - RPi_CheckAndExitHypModeToSvcMode

Makogan
Posts: 71
Joined: Tue May 16, 2017 9:17 pm

Re: Interrupts time and memory?

Wed Jun 21, 2017 9:51 pm

The reason why I am using <> instead of "" is because these are system files (I mean they are not linux or x86 system files, but they are my PiOS and rpi system files). I modified my boot.s file so that i looks like yours (i.e I used the relative labels trick and pushed the changes). And although i understand this is definitely yet another error on my part, it's not what is causing the most obscure errors. The main error I need to solve right now is the fact that the msr (i.e switching modes) instructions are breaking program execution. If I comment them out, regardless of the state of my code, I see my silly message. But when I try to change modes to setup the stack the system breaks. I need to look into this more. Also, I apologize, I went to bed yesterday and missed your canvas window so I cannot look at your code anymore :p

dwelch67
Posts: 803
Joined: Sat May 26, 2012 5:32 pm

Re: Interrupts time and memory?

Wed Jun 21, 2017 11:02 pm

This is a pi3 right? you can set the VTOR to point at where you have/had your vector/exception table assuming it is aligned properly (0x80000 is definitely aligned properly).

dwelch67
Posts: 803
Joined: Sat May 26, 2012 5:32 pm

Re: Interrupts time and memory?

Wed Jun 21, 2017 11:03 pm

of course setting VTOR might be tricky with the modes and protections..

Makogan
Posts: 71
Joined: Tue May 16, 2017 9:17 pm

Re: Interrupts time and memory?

Wed Jun 21, 2017 11:52 pm

Hello David, this could potentially solve the branching issue (although I have only learnt about the VTOR just now so I'd need to read more into it). But this wouldn't solve the stack initialization problem I am currently facing. I need to set the stack pointers for the different modes, else even if I can solve the branching problem, the interrupt handling subroutines written in C will face issues, as they'd have no proper stack to work with. What I don't understand, is how trying to initialize the stacks through:

Code: Select all

        msr CPSR_c, #0xD1                  ;@ Switch to FIQ_MODE
        ldr sp, =__fiq_stack               ;@ Set the stack pointer for that mode
        msr CPSR_c, #0xD2                  ;@ Switch to IRQ_MODE
        ldr sp, =__irq_stack               ;@ Set the stack pointer for that mode
        msr CPSR_c, #0xD3                  ;@ Switch back to SRV_MODE
        ldr sp, =__svc_stack               ;@ Set the stack pointer for that mode
Can break the code, considering every single piece of documentation I can find including LdB's advice, says this is the proper way to do it.

LdB
Posts: 440
Joined: Wed Dec 07, 2016 2:29 pm

Re: Interrupts time and memory?

Thu Jun 22, 2017 12:55 am

What you need to understand is there isn't one stack there is a stack for each operation mode. IRQ and FIQ are operation modes of the ARM so forget how things happen on a normal mirco-controller it is nothing a single stack micro.

The stack pointers must be set to physically different addresses for each mode so they map to a memory allocation you have setup for them to work. If you can work out a different way to set the stack pointers in each of the different modes then do it.

David uses the exact same code in pretty much every sample of his I have seen. I am not even sure it's possible to set the SP register of a different mode from the mode you are in on the ARM. If you look at his first example he uses the interrupt in blinker07
https://github.com/dwelch67/raspberrypi ... /vectors.s
Notice this section and now you should know what he is doing because he is using hard set address to areas of memory he has decided he wants to put the stacks. He uses exactly the same code with the dredded "msr" code you have fixated on as well.

Code: Select all

    ;@ (PSR_IRQ_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
    mov r0,#0xD2
    msr cpsr_c,r0
    mov sp,#0x8000

    ;@ (PSR_FIQ_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
    mov r0,#0xD1
    msr cpsr_c,r0
    mov sp,#0x4000

    ;@ (PSR_SVC_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
    mov r0,#0xD3
    msr cpsr_c,r0
    mov sp,#0x8000000
As your boot code is usually generic and you have different projects with different setups, the usual trick is move the stack out to the linker. That way you don't have to change the assembler file for every project and as it is usual to have a different linker file for each project it makes sense to put the stack labels out in it. David has gone the hard coded way in the above code in that he has got some memory map in his head of where those stacks are physically sitting in memory.

So long as the ARM is in SVC_MODE before you execute those opcodes they work as expected and produce the exact result desired and can not crash the program. The reality is you "test the code" by calling some function and first that call probably uses the stack and second that code itself may be crashing. The most likely answer is either the stack labels aren't linking correctly and the stack is at some undefined placed, the ARM isnt in SVC_MODE or the test code is bugged but you ignore those options and fixate on some opcodes.

Anyhow you seem to have fixated on that code being wrong and I therefore can't help you because I don't know any other way to set the stack pointers for the different modes.

dwelch67
Posts: 803
Joined: Sat May 26, 2012 5:32 pm

Re: Interrupts time and memory?

Thu Jun 22, 2017 1:38 am

I dont think it is just a microcontroller thing, other processors dont do this or maybe I dont know enough about them or the many I know are in the minority.

Either way if you stick to HYP mode then you can I think get away with one or a primary one for svc, then for each thread you give them their own in their space (which is true for multi threading no matter what processor (well there are always exceptions) or what mode).

But my code is not special it is taken from the endless number of examples that predated it from the ARM7TDMI days. If you read the arm documentation (ARM ARM) in the programmers model section, it talks about the modes, it shows a map of the banked registers in this mode you have your own this through that and share this and that. From that you have to understand that to set the IRQ r13 (sp) for example you have to be in irq mode and you can force your way into that mode by changing the mode bits. so you walk through the modes, the doc also tells you that you cant get from user to other modes that way you have to use svc (called swi then), but system and user share the same stack so in system mode you can set that stack. Probably dont care initially, for examples like mine what matters is when you are in supervisor and get an interrupt, an irq lets say, the docs tell you the mode changes it saves a wee bit of state and executes an instruction at a specific address. i am coming into this cold, not fully following this thread so I may be off base...

change the mode bits to IRQ

;@ (PSR_IRQ_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
mov r0,#0xD2
msr cpsr_c,r0

and set the IRQ sp13

mov sp,#0x8000

change the mode bits to fiq

;@ (PSR_FIQ_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
mov r0,#0xD1
msr cpsr_c,r0

and set the fiq stack pointer to somewhere else

mov sp,#0x4000

then change to superviser mode

;@ (PSR_SVC_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
mov r0,#0xD3
msr cpsr_c,r0

set the stack pointer for supervisor mode

mov sp,#0x8000000

and then you can hit the C entry point and run your program, when you properly set everything else up to get an irq or fiq then it has a stack space while the handler is running.

I am guessing this is my code for a pi2 interrupt setup, sets the VTOR instead of copying the vector table to the default 0x0000

reset:
mov r0,#0x8000
MCR p15, 4, r0, c12, c0, 0

mov sp,#0x8000
bl notmain
hang: b hang

and you have to use an eret to return not subs pc,lr,#4

irq:
push {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,lr}
bl c_irq_handler
pop {r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,lr}
;@subs pc,lr,#4
eret

(note the arm documentation clearly states for each mode/exception, what the correct return instruction is for example subs pc,lr,#4 is one of them).

now sometimes for quick and dirty figuring stuff out or dealing with undefined instructions or alignment data aborts or whatever I may setup a dead end handler and as you enter the handler I set the stack pointer

mov sp,#0x1000
print some info out
b .

just so I can figure out where the processor is going when it dies so I can then have a small clue as to where to look or what to look for.

Again out of context, not sure exactly what the real issue is here. Interrupts are advanced, difficult on a good day because there are so many ways to trip up and crash, take them very slowly is my advice. I have some examples that do that, poll your way as far as you possibly can tracing the interrupt right up to the edge of the cpu plus understanding completely how to clear it, using polling, then the last thing is to release it into the cpu with all of that newly acquired experience (although the last week or two every time I type something like this I seem to stick my foot in my mouth and regret it, probably the same here). Implement what you know and hope for the best. I would make a completely isolated experiment for an interrupt if possible, dont do this in your primary application, only add it in after the experiment can successfully walk it into the cpu and safely get back out. Many years of experience, take it or leave it.

For the older cores irq and fiq are physical not pins but signals on the edge of the core, I often see chips offer most/all of the interrupts to be tied to either, programmers choice, but they are separate so that is part of the discovery (sometimes a desire for two experimental handlers that spit a character out the uart and to into an infinite loop to see which one fired).

Newer cores have separate signals 32, 64, 128, they come into the traditional exception, but then you use something in the interrupt controller to determine which one fired, and often that is not completely documented, so here again an experimental handler that reads these items prints them out and goes into an infinite loop, so you can add that knowledge to your handler.

I generally dont publish those experiments, it looks like I just magically know all this stuff.

reading your initial question, I guess you didnt look at my stuff, now it is pi1/zero based sure, but the methods apply

The last example is just demonstrating an access violation. Changing
the domain to that one domain we did not set full access to
what it does show is that it is the exception that is at address
0x00000010 that gets hit which is data abort
Plus I have some timer interrupt examples that walk through each step in the life of the interrupt...from (a/)the timer...

diving into all of this first time with something newer than an ARMv6 just adds a significant amount of pain...good luck, your deadline is any day now yes?

Makogan
Posts: 71
Joined: Tue May 16, 2017 9:17 pm

Re: Interrupts time and memory?

Thu Jun 22, 2017 1:47 am

Official deadline is 27, I can probably push it by a couple of weeks if I really want to but I'd rather get some minimalistic scheduling working and stop there because I start working on a machine learning research project once this is over :p

I just don't understand what the problem is, I keep reading documentation, I read you examples. Let me be very explicit this time. I have commented out anything related with interrupts and I am simply trying to set the different stack pointers for the different modes.

In David's original code (the example he has very kindly provided) he does as follows:

Code: Select all

.globl _start
_start:
    ldr pc,reset_handler
    ldr pc,undefined_handler
    ldr pc,swi_handler
    ldr pc,prefetch_handler
    ldr pc,data_handler
    ldr pc,unused_handler
    ldr pc,irq_handler
    ldr pc,fiq_handler
reset_handler:      .word reset
undefined_handler:  .word hang
swi_handler:        .word hang
prefetch_handler:   .word hang
data_handler:       .word hang
unused_handler:     .word hang
irq_handler:        .word irq
fiq_handler:        .word hang

reset:
    mov r0,#0x8000
    mov r1,#0x0000
    ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9}
    stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9}
    ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9}
    stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9}

    ;@ (PSR_IRQ_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
    mov r0,#0xD2
    msr cpsr_c,r0
    mov sp,#0x8000

    ;@ (PSR_FIQ_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
    mov r0,#0xD1
    msr cpsr_c,r0
    mov sp,#0x4000

    ;@ (PSR_SVC_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
    mov r0,#0xD3
    msr cpsr_c,r0
    mov sp,#0x8000000

    ;@ SVC MODE, IRQ ENABLED, FIQ DIS
    ;@mov r0,#0x53
    ;@msr cpsr_c, r0

    bl notmain
This is a very straightforward code, code starts executing in start, branches to the reset label, reset loads the table reference address in r0 and the relative label addresses in r1, he then puts everything in low memory to install the vector table, he then uses the mcr instruction to setup each one of the different stack pointers for each mode, then he goes to the C code.

What I am doing is:

Code: Select all

.section .init
.global _start

_start:
// Based on the valvers tutorial
_reset_:
        ldr     r0, =_v_table
        mov     r1, #0x0000
        ldmia   r0!,{r2, r3, r4, r5, r6, r7, r8, r9}
        stmia   r1!,{r2, r3, r4, r5, r6, r7, r8, r9}
        ldmia   r0!,{r2, r3, r4, r5, r6, r7, r8, r9}
        stmia   r1!,{r2, r3, r4, r5, r6, r7, r8, r9}

/* CHANGES ADDED BY LdB */
        //ldr sp, =__usrsys_stack               ;@ This is just a safety if all hell breaks loose


        bl  RPi_CheckAndExitHypModeToSvcMode;   ;@ First check for HYP_MODE and if in it drop out to SRV_MODE

;@"================================================================"
;@ Now setup stack pointers for the different CPU operation modes.
;@"================================================================"
        ldr     sp, =0x8000 // initialize stack pointer
        mov     r0, #1
        bl      set_LED

        ;@ (PSR_IRQ_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
        mov r0,#0xD2
        msr cpsr_c,r0
        //ldr sp,=__irq_stack

        ;@ (PSR_FIQ_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
        mov r0,#0xD1
        msr cpsr_c,r0
        //ldr sp,=__fiq_stack

        ;@ (PSR_SVC_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
        mov r0,#0xD3
        msr cpsr_c,r0
        ldr sp, =0x8000//=__svc_stack

        mov r0, #0
        bl set_LED 

@"================================================================"
;@ PI NSACR regsister setup for access to floating point unit
;@ Cortex A-7 => Section 4.3.34. Non-Secure Access Control Register
;@ Cortex A-53 => Section 4.5.32. Non-Secure Access Control Register
;@"================================================================"
   mrc p15, 0, r0, c1, c1, 2            ;@ Read NSACR into R0
   cmp r0, #0x00000C00                  ;@ Access turned on or in AARCH32 mode and can not touch register or EL3 fault
   beq .free_to_enable_fpu
   orr r0, r0, #0x3<<10               ;@ Set access to both secure and non secure modes
   mcr p15, 0, r0, c1, c1, 2            ;@ Write NSACR
;@"================================================================"
;@ Bring fpu online
;@"================================================================"
.free_to_enable_fpu:
   mrc p15, 0, r0, c1, c0, #2            ;@ R0 = Access Control Register
   orr r0, #(0x300000 + 0xC00000)         ;@ Enable Single & Double Precision
   mcr p15,0,r0,c1,c0, #2               ;@ Access Control Register = R0
   mov r0, #0x40000000                  ;@ R0 = Enable VFP
   vmsr fpexc, r0                     ;@ FPEXC = R0
 ;@"================================================================"
;@ Enable L1 cache
;@"================================================================"
    mrc p15,0,r0,c1,c0,0               ;@ R0 = System Control Register

/*==========================================================================}
;{           RASPBERRY PI LEVEL L1 CACHE CONSTANTS DEFINED                }
;{=========================================================================*/
#define SCTLR_ENABLE_DATA_CACHE         0x4
#define SCTLR_ENABLE_BRANCH_PREDICTION   0x800
#define SCTLR_ENABLE_INSTRUCTION_CACHE  0x1000

    // Enable caches and branch prediction
    orr r0, #SCTLR_ENABLE_BRANCH_PREDICTION
    orr r0, #SCTLR_ENABLE_DATA_CACHE
    orr r0, #SCTLR_ENABLE_INSTRUCTION_CACHE

    mcr p15,0,r0,c1,c0,0               ;@ System Control Register = R0

 /* END OF CHANGES ADDED BY LdB */
    
    b       _cstartup

//Based on the valvers tutorial
 _v_table:
        ldr     pc, _reset_h
        ldr     pc, _undefined_instruction_vector_h
        ldr     pc, _software_interrupt_vector_h
        ldr     pc, _prefetch_abort_vector_h
        ldr     pc, _data_abort_vector_h
        ldr     pc, _unused_handler_h
        ldr     pc, _interrupt_vector_h
        ldr     pc, _fast_interrupt_vector_h

_reset_h:                           .word   _reset_
_undefined_instruction_vector_h:    .word   undefined_instruction_vector
_software_interrupt_vector_h:       .word   software_interrupt_vector
_prefetch_abort_vector_h:           .word   prefetch_abort_vector
_data_abort_vector_h:               .word   _reset_
_unused_handler_h:                  .word   _reset_
_interrupt_vector_h:                .word   interrupt_vector
_fast_interrupt_vector_h:           .word   fast_interrupt_vector



 /* CODED ADDED BY LdB */
/* "PROVIDE C FUNCTION: bool RPi_CheckAndExitHypModeToSvcMode (void);" */
.section .text.RPi_CheckAndExitHypModeToSvcMode, "ax", %progbits
.balign   4
.type RPi_CheckAndExitHypModeToSvcMode, %function
.syntax unified
.arm
;@"================================================================"
;@ RPi_CheckAndExitHypModeToSvcMode -- Composite Pi1, Pi2 & Pi3 code
;@ Return: Drops out from HYP_MODE to SRV_MODE and FIQ/IRQ disabled
;@         If not in HYP_MODE will exit unchanged
;@"================================================================"
RPi_CheckAndExitHypModeToSvcMode:
   mrs r0, cpsr                     ;@ Fetch the cpsr register             
   and r1, r0, #0x1F                  ;@ Mask off the arm mode bits in register                            
    cmp r1, #0x1A                     ;@ check we are in HYP_MODE AKA register reads 1A                     
   beq .WeHaveHyperMode
   mov r0, #0;                        ;@ return false
   bx lr                           ;@ Return we are not in hypermode
.WeHaveHyperMode:
   bic r0, r0, #0x1F                  ;@ Clear the mode bits                   
   orr r0, r0, #0xD3                  ;@ We want SRV_MODE with IRQ/FIQ disabled         
   mov r1, #0                        ;@ Make sure CNTVOFF to 0 before exit HYP mode
   mcrr p15, #4, r1, r1, cr14            ;@ We do not want our clocks going fwd or backwards
   orr r0, r0, #0x100                  ;@ Set data abort mask    
   msr spsr_cxsf, r0                  ;@ Load our request into return status register
   mov r0, #1;                        ;@ return true
   bx lr
   /* I borrowed this trick from Ultibo because ARM6 code running on an ARM7/8 needs this opcode  */
   /* The ARM6 compiler does not know these instructions so it is a way to get needed opcode here */
    /* So our ARM6 code can drop an arm7 or arm8 out of HYP mode and run on an ARM7/8.             */
    /* Native ARM7/8 compilers already understand the OPCODE but do not mind it this way either      */        
   .long 0xE12EF30E                  ;@ "msr ELR_hyp, lr"
   .long 0xE160006E                  ;@ "eret"   
;@ No ltorg data required for this function 
.size   RPi_CheckAndExitHypModeToSvcMode, .-RPi_CheckAndExitHypModeToSvcMode
This is doing a lot more but I'll say what I think I am doing. Execution starts in _start, which overlaps with _reset_;

From there the interrupt vector table is installed almost identically as how David's, LdB, Valvers... do it, only difference being the reference table is after the code instead of being nested into it;

Then I use LdB's generously provided subroutine to make sure the processor is running on supervisor mode. I then initialize a stack pointer just so that I can call the set_LED function with no fear of accessing invalid memory.

Then I try to initialize all different stack pointers by imitating what David does in his code.

Then LdB's code to setup the FPU, branch prediction and so on runs...

And finally we move to the C part of the code.

This doesn't work (The color palette remains there and the LED remains on, indicating that any instruction after initializing the different stack pointers was not executed).

So Then I tried to correct this, first commenting out only the lines related to setting the mode stack pointers (anything related to the msr instruction) works, I see my silly message and the LED goes off.

So it could be that the extra work being done by LdB's is somehow responsible for the problem. I thus proceed to comment everything but the most fundamental sections of the code out (i.e no fpu. no branch prediction no checking for hyp mode and switching to svc mode...) This produces the following .elf:

Disassembly of section .init:

Code: Select all

00008000 <_start>:
    8000:       e59f008c        ldr     r0, [pc, #140]  ; 8094 <_fast_interrupt_vector_h+0x4>
    8004:       e3a01000        mov     r1, #0
    8008:       e8b003fc        ldm     r0!, {r2, r3, r4, r5, r6, r7, r8, r9}
    800c:       e8a103fc        stmia   r1!, {r2, r3, r4, r5, r6, r7, r8, r9}
    8010:       e8b003fc        ldm     r0!, {r2, r3, r4, r5, r6, r7, r8, r9}
    8014:       e8a103fc        stmia   r1!, {r2, r3, r4, r5, r6, r7, r8, r9}
    8018:       e3a0d902        mov     sp, #32768      ; 0x8000
    801c:       e3a00001        mov     r0, #1
    8020:       eb0001f9        bl      880c <set_LED>
    8024:       e3a000d2        mov     r0, #210        ; 0xd2
    8028:       e121f000        msr     CPSR_c, r0
    802c:       e59fd064        ldr     sp, [pc, #100]  ; 8098 <_fast_interrupt_vector_h+0x8>
    8030:       e3a000d1        mov     r0, #209        ; 0xd1
    8034:       e121f000        msr     CPSR_c, r0
    8038:       e59fd05c        ldr     sp, [pc, #92]   ; 809c <_fast_interrupt_vector_h+0xc>
    803c:       e3a000d3        mov     r0, #211        ; 0xd3
    8040:       e121f000        msr     CPSR_c, r0
    8044:       e59fd054        ldr     sp, [pc, #84]   ; 80a0 <_fast_interrupt_vector_h+0x10>
    8048:       e3a00000        mov     r0, #0
    804c:       eb0001ee        bl      880c <set_LED>
    8050:       ea000078        b       8238 <_cstartup>
Alas once again the color palette remains and the LED stays on.

So then it is possible that the labels themselves are wrong and this is causing a problem so I commented out the sp intialization instructions and initialized the sp after that just in case. So I did:

Code: Select all

;@ (PSR_IRQ_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
        mov r0,#0xD2
        msr cpsr_c,r0
        //ldr sp,=__irq_stack

        ;@ (PSR_FIQ_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
        mov r0,#0xD1
        msr cpsr_c,r0
        //ldr sp,=__fiq_stack

        ;@ (PSR_SVC_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS)
        mov r0,#0xD3
        msr cpsr_c,r0
        ldr sp, =0x8000//=__svc_stack
In the code. This should simply switch the modes around without touching anything and then only at the end do I set the stack pointer to something that should be safe.

I also tried hard coding the the addresses. I get the following executable:

Code: Select all

00008000 <_start>:
    8000:       e59f0080        ldr     r0, [pc, #128]  ; 8088 <_fast_interrupt_vector_h+0x4>
    8004:       e3a01000        mov     r1, #0
    8008:       e8b003fc        ldm     r0!, {r2, r3, r4, r5, r6, r7, r8, r9}
    800c:       e8a103fc        stmia   r1!, {r2, r3, r4, r5, r6, r7, r8, r9}
    8010:       e8b003fc        ldm     r0!, {r2, r3, r4, r5, r6, r7, r8, r9}
    8014:       e8a103fc        stmia   r1!, {r2, r3, r4, r5, r6, r7, r8, r9}
    8018:       e3a0d902        mov     sp, #32768      ; 0x8000
    801c:       e3a00001        mov     r0, #1
    8020:       eb0001f3        bl      87f4 <set_LED>
    8024:       e321f0d1        msr     CPSR_c, #209    ; 0xd1
    8028:       e3a0da07        mov     sp, #28672      ; 0x7000
    802c:       e321f0d2        msr     CPSR_c, #210    ; 0xd2
    8030:       e3a0da06        mov     sp, #24576      ; 0x6000
    8034:       e321f0d3        msr     CPSR_c, #211    ; 0xd3
    8038:       e3a0da05        mov     sp, #20480      ; 0x5000
    803c:       e3a00000        mov     r0, #0
    8040:       eb0001eb        bl      87f4 <set_LED>
    8044:       ea000075        b       8220 <_cstartup>

00008048 <_v_table>:
    8048:       e59ff018        ldr     pc, [pc, #24]   ; 8068 <_reset_h>
    804c:       e59ff018        ldr     pc, [pc, #24]   ; 806c <_undefined_instruction_vector_h>
    8050:       e59ff018        ldr     pc, [pc, #24]   ; 8070 <_software_interrupt_vector_h>
    8054:       e59ff018        ldr     pc, [pc, #24]   ; 8074 <_prefetch_abort_vector_h>
    8058:       e59ff018        ldr     pc, [pc, #24]   ; 8078 <_data_abort_vector_h>
    805c:       e59ff018        ldr     pc, [pc, #24]   ; 807c <_unused_handler_h>
    8060:       e59ff018        ldr     pc, [pc, #24]   ; 8080 <_interrupt_vector_h>
    8064:       e59ff018        ldr     pc, [pc, #24]   ; 8084 <_fast_interrupt_vector_h>
However in this state, when all of LdB's code has been commented out, If I remove all msr related code, leaving only the instruction:

ldr sp, =0x8000

Then the code runs and I see my silly message.

I must sound both retarded and annoying, but I hope that you understand why I am so confused. It is not the interrupts that are causing problems (I mean based on my history they are probably very broken), but just switching modes, or rather, trying to switch modes.

The only thing I can think off is that I ebgin executing on user mode rather than SVC or HYP mode and that when I try to switch modes I am triggering an uninitialized interrupt, but this sounds too convoluted.
Last edited by Makogan on Thu Jun 22, 2017 3:34 am, edited 4 times in total.

Return to “Bare metal”

Who is online

Users browsing this forum: No registered users and 8 guests