aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--kernel.c9
-rw-r--r--psr.h123
2 files changed, 105 insertions, 27 deletions
diff --git a/kernel.c b/kernel.c
index 40b8a01..7a10b24 100644
--- a/kernel.c
+++ b/kernel.c
@@ -1,6 +1,6 @@
#include "uart.h"
-#include "psr.h"
#include "strings.h"
+#include "psr.h"
#include "translation_table_descriptors.h"
#include "cp_regs.h"
@@ -43,14 +43,13 @@ void kernel_main(uint32_t r0, uint32_t r1, uint32_t atags)
uart_puts(paging);
- uint32_t CPSR;
// get content of current program status register to check the current
// processor mode
- asm("mrs %0, cpsr" : "=r" (CPSR) :: "memory");
-
+ PSR_t CPSR = read_CPSR();
+
char *mode_name;
- switch(read_processor_mode())
+ switch(CPSR.fields.PSR_MODE_4_0)
{
case MODE_USER : mode_name = "User (PL0)\r\n"; break;
case MODE_FIQ : mode_name = "FIQ (PL1)\r\n"; break;
diff --git a/psr.h b/psr.h
index 327bdf0..9a3caa9 100644
--- a/psr.h
+++ b/psr.h
@@ -1,39 +1,118 @@
#include <stdint.h>
enum execution_mode {
- MODE_USER = 0x10,
- MODE_FIQ = 0x11,
- MODE_IRQ = 0x12,
- MODE_SUPERVISOR = 0x13,
- MODE_MONITOR = 0x16,
- MODE_ABORT = 0x17,
- MODE_HYPERVISOR = 0x1a,
- MODE_UNDEFINED = 0x1b,
- MODE_SYSTEM = 0x1f,
+ MODE_USER = 0b10000,
+ MODE_FIQ = 0b10001,
+ MODE_IRQ = 0b10010,
+ MODE_SUPERVISOR = 0b10011,
+ MODE_MONITOR = 0b10110,
+ MODE_ABORT = 0b10111,
+ MODE_HYPERVISOR = 0b11010,
+ MODE_UNDEFINED = 0b11011,
+ MODE_SYSTEM = 0b11111,
};
-inline static uint32_t read_CPSR(void)
+typedef union
{
- uint32_t CPSR;
+ uint32_t raw;
+ struct
+ {
+ uint32_t M_4_0 : 5; // bits 4:0
+ uint32_t T : 1; // bit 5
+ uint32_t F : 1; // bit 6
+ uint32_t I : 1; // bit 7
+ uint32_t A : 1; // bit 8
+ uint32_t E : 1; // bit 9
+ uint32_t IT_7_2 : 6; // bits 15:10
+ uint32_t GE_3_0 : 4; // bits 19:16
+ uint32_t Bits_23_20 : 4; // bits 23:20
+ uint32_t J : 1; // bit 24
+ uint32_t IT_1_0 : 2; // bits 26:25
+ uint32_t Q : 1; // bit 27
+ uint32_t V : 1; // bit 28
+ uint32_t C : 1; // bit 29
+ uint32_t Z : 1; // bit 30
+ uint32_t N : 1; // bit 31
+#define PSR_MODE_4_0 M_4_0
+#define PSR_THUMB_BIT T
+#define PSR_FIQ_MASKK_BIT F
+#define PSR_IRQ_MASK_BIT I
+#define PSR_ASYNC_ABORT_MASK_BIT A
+#define PSR_ENDIANNESS_BIT E
+#define PSR_IF_THEN_STATE_7_2 IT_7_2
+#define PSR_GREATER_THAN_OR_EQUAL_FLAGS GE_3_0
+ // bits 23:20 are reserved
+#define PSR_JAZELLE_BIT J
+#define PSR_IF_THEN_STATE_1_0 IT_1_0
+#define PSR_CUMULATIVE_SATURATION_BIT Q
+#define PSR_OVERFLOW_CONDITION_BIT V
+#define PSR_CARRY_CONDITION_BIT C
+#define PSR_ZERO_CONDITION_BIT Z
+#define PSR_NEGATIVE_CONDITION_BIT N
+ } fields;
+} PSR_t;
+
+inline static PSR_t read_CPSR(void)
+{
+ PSR_t CPSR;
// get content of current program status register
- asm("mrs %0, cpsr" : "=r" (CPSR) ::);
+ asm("mrs %0, cpsr" : "=r" (CPSR.raw) :: "memory");
return CPSR;
}
-inline static enum execution_mode read_processor_mode(void)
-{
- /* lowest 5 bits indicate processor mode */
- return read_CPSR() & 0x1f;
-}
+//// Write function not working for some reason... Assembly from gcc
+//// looks ok. Needs checking on real hw.
+
+//inline static void write_CPSR(PSR_t CPSR)
+//{
+ // write to current program status register and synchronize context
+ // asm("msr cpsr, %0\n\r"
+ // "isb" :: "r" (CPSR.raw) : "memory");
+//}
inline static void set_system_mode(void)
{
- uint32_t CPSR = read_CPSR();
+ // hack to fix an unexplained bug; volatile needed in case of
+ // compilation with optimizations
+ volatile PSR_t CPSR __attribute__((unused)) = read_CPSR();
- CPSR = (CPSR >> 5) << 5;
+ //// this is 2 ways of changing mode, both with the same
+ //// problem (see the long comment below)
- CPSR |= MODE_SYSTEM;
-
- asm("msr cpsr, %0" :: "r" (CPSR) : "memory");
+ //// way 1
+ // CPSR.fields.M_4_0 = MODE_SYSTEM;
+ // write to current program status register and synchronize context
+ // asm("msr cpsr, %0\n\r"
+ // "isb":: "r" (CPSR.raw) : "memory");
+
+ //// way 2
+ asm("cps #0b11111\n\r"
+ "isb" ::: "memory");
}
+
+// The thing with writing to cpsr is weird. I used to have a single
+// function that would set the system mode by:
+// 1. reading the cpsr
+// 2. modifying the value using bit shifts and logical or
+// 3. writing the value back
+// When introducing structs and bitfields I wanted to have separate
+// functions for reading and writing the cpsr and have code up the
+// call stack handle value modification. For some reason this didn't
+// work - all would just hang when writing the CPSR. It turned out
+// everything works if i call read_CPSR() from the same function, in
+// which i write to cpsr. And I don't even need to use the value
+// I read. I can just discard it (we're compiling without
+// optimizations, so read_CPSR() is still called) and use the value
+// passed as argument. I noticed an even weirder thing: the 2nd way
+// of changing mode (cps instruction) doesn't work normally, but,
+// just as msr, it does work when I call read_CPSR() before it. Even
+// weirder - I cannot replace read_CPSR() with the actual assembly it
+// does, because it stops working again! Asm generated by gcc looks
+// ok, so Idk, maybe its some qemu issue (haven't tested on real RPi
+// yet). I seem to have experienced uart printing inserted here and
+// there do the same magic read_CPSR() does (but, again, not always).
+// Unfortunately, nop repeated several hundreds times (or a recursive
+// nop function, that just calls itself a given number of times)
+// doesn't have this magical property :(
+