aboutsummaryrefslogtreecommitdiff
path: root/src/arm/PL1/kernel/scheduler.c
blob: 141ba1d6cb3f28e37aba67d720750d13a448eeaa (about) (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#include "scheduler.h"
#include "uart.h"
#include "strings.h"
#include "armclock.h"
#include "memory.h"
#include "io.h"

// for now we only have 1 process in "queue"
// later there is going to be an actual queue
uint32_t PL0_regs[14] = {0}; // contains r0-r12, pc
uint32_t PL0_sp;
uint32_t PL0_lr;

PSR_t PL0_PSR; // to be put into spsr when jumping to user mode

PSR_t PL1_PSR;

// when set, it means process used GETCHAR system call and once we get
// a char, we have to return it
_Bool waiting_for_input = 0;

// when set, it means process used PUTCHAR system call and once we
// manage to put the char, we can return to process
_Bool waiting_for_output = 0;
char waiting_output;

// 0 is kernel code in system mode is being run
// 1 if our process is being run
// later when we have many processes and this will hold process id
uint32_t current_process;

void setup_scheduler_structures(void)
{
  PL1_PSR = read_CPSR();
}

void scheduler_try_output(void)
{
  if (waiting_for_output)
    if (!putchar_non_blocking(waiting_output))
      {
	waiting_for_output = 0;
	uart_send_irq_disable();
      }
}

void scheduler_try_input(void)
{
  if (waiting_for_input)
    if ((PL0_regs[0] = getchar_non_blocking()) != (uint32_t) (-1))
      {
	waiting_for_input = 0;
	uart_recv_irq_disable();
      }
}

void __attribute__((noreturn))
schedule_new(uint32_t pc, uint32_t sp)
{
  PL0_regs[13] = pc;
  PL0_sp = sp;
  PL0_lr = 0;
    
  PL0_PSR = read_CPSR();
  PL0_PSR.fields.PSR_MODE_4_0 = MODE_USER;
  PL0_PSR.fields.PSR_IRQ_MASK_BIT = 0;

  schedule();
}

void __attribute__((noreturn))
schedule_wait_for_output(uint32_t regs[14], char c)
{
  if (current_process == 0)
    error("SYSTEM tried waiting for output!");

  waiting_for_output = 1;
  waiting_output = c;
  uart_send_irq_enable();
  
  schedule_save_context(regs);
}

void __attribute__((noreturn))
schedule_wait_for_input(uint32_t regs[14])
{
  if (current_process == 0)
    error("SYSTEM tried waiting for input!");

  waiting_for_input = 1;
  uart_recv_irq_enable();
	
  schedule_save_context(regs);
}

void __attribute__((noreturn))
schedule_save_context(uint32_t regs[14])
{
  memcpy(PL0_regs, regs, sizeof(PL0_regs));
  
  PL0_PSR = read_SPSR();
  
  asm volatile("cps %[sysmode]\n\r"
	       "isb\n\r"
	       "mov %[sp_transfer], sp\n\r"
	       "mov %[lr_transfer], lr\n\r"
	       "cps %[supmode]\n\r"
	       "isb\n\r" :
	       [sp_transfer]"=r" (PL0_sp),
	       [lr_transfer]"=r" (PL0_lr):
	       [sysmode]"I" (MODE_SYSTEM),
	       [supmode]"I" (MODE_SUPERVISOR) : "memory");

  schedule();
}

void __attribute__((noreturn)) schedule(void)
{
  current_process = 0;
  armclk_disable_timer_irq();
  
  if (waiting_for_input || waiting_for_output)
    {
      PSR_t new_CPSR = PL1_PSR;
      new_CPSR.fields.PSR_IRQ_MASK_BIT = 0;
  
      write_CPSR(new_CPSR);
      
      asm volatile("wfi");

      __builtin_unreachable();
    }

  current_process = 1;

  asm volatile("cps %[sysmode]\n\r"
  	       "isb\n\r"
  	       "mov sp, %[stackaddr]\n\r"
  	       "mov lr, %[linkaddr]\n\r"
  	       "cps %[supmode]\n\r"
  	       "isb" ::
  	       [sysmode]"I" (MODE_SYSTEM),
  	       [supmode]"I" (MODE_SUPERVISOR),
  	       [stackaddr]"r" (PL0_sp),
  	       [linkaddr]"r" (PL0_lr) : "memory");

  armclk_irq_settimeout(0x00100000);
  armclk_enable_timer_irq();

  write_SPSR(PL0_PSR);
  
  asm volatile("ldm %0, {r0 - r12, pc} ^" ::
	       "r" (PL0_regs) : "memory");

  __builtin_unreachable();
}