finish assignment 4.4

This commit is contained in:
Laila van Reenen 2025-10-02 13:27:49 +02:00
parent e08f304c89
commit b72fea6fc4
Signed by: LailaTheElf
GPG Key ID: 8A3EF0226518C12D
7 changed files with 612 additions and 49 deletions

View File

@ -170,47 +170,105 @@ and when the 'malicious' code is removed, it functions as normal.
> Tasks are no longer able to access the SysTick peripheral. It is time to implement a system call using the SVC interrupt mechanism. The `taskYield()` function would also classify as a system call. Write a system call function that will allow a task to change the SysTick period to a value between 1 and 10 ms . The system call should utilize the SVC mechanism by passing a different number than the `taskYield()` call. Make sure both calls keep working! Within the SVC interrupt service routine you will have to find the used SVC instruction using the PC value and look at the LSB to determine the system call number. You can also utilize the tasks stack to find the passed SysTick period parameter. When this parameter is invalid the system call should simply return.
### user
```
instrucion: 0x08000600 -> 00060008
SP: 0x20000484
To find the instruction I first added the function to that contains the different `SVC` insurrections. I did this by adding the following code to `VersOS.c` and add the corresponding lines to `VersOS.h`.
```c
void setSysTick_10ms(void)
{
asm(" svc #2");
}
void setSysTick_1ms(void)
{
asm(" svc #3");
}
```
### kernel
And made use of them in the `toggelBlue` task by updating it to the following.
```c
void toggleBlue(void)
{
while(1)
{
GPIOD->ODR ^= 1 << BLUE;
delay(800-1);
setSysTick_10ms();
GPIOD->ODR ^= 1 << BLUE;
delay(800-1);
setSysTick_1ms();
}
}
```
Then added an break-point at each `SVC` instruction and noted `PS` and `PC`. Then stepped to the `SVC_Handler`, and looked at the memory at the address stored in the `PSP` register.
```
psp: 0x20000460
task:
PC: 0x08000660
SP: 0x200005ac
SVC_Handler:
PSP: 0x20000588
0x20000400 : 0x20000400 <Hex>
0x20000588 : 0x20000588 <Hex>
Address 0 - 3 4 - 7 8 - B C - F
20000430 04000000 05000000 06000000 74040020
20000440 08000000 09000000 0A000000 0B000000
20000450 1F030000 01000000 1F030000 B0000020
20000460 1F030000 01000000 1F030000 B0000020
20000470 0C000000 F1060008 62060008 00020001
20000480 88040020 88040020 07000000 FDFFFFFF
20000490 807505D0 022C0CBF 4FF40075 4FF44075
200004A0 082815D1 CB4C2068 20F44070 20602068
200004B0 28432060 206840F0 04002060 206840F4
200004C0 80302060 00F040F9 216821F0 04012160
200004D0 F0000020 00000000 00000000 14050020
PSP+(8*4): 88040020 -> 20000488
0x20000400 : 0x20000400 <Hex>
Address 0 - 3 4 - 7 8 - B C - F
20000440 08000000 09000000 0A000000 0B000000
20000450 1F030000 01000000 1F030000 B0000020
20000460 1F030000 01000000 1F030000 B0000020
20000470 0C000000 F1060008 62060008 00020001
20000480 88040020 88040020 07000000 FDFFFFFF
20000490 807505D0 022C0CBF 4FF40075 4FF44075
200004A0 082815D1 CB4C2068 20F44070 20602068
200004B0 28432060 206840F0 04002060 206840F4
200004C0 80302060 00F040F9 216821F0 04012160
200004D0 F0000020 00000000 00000000 14050020
200004E0 00000000 00000000 00000000 00000000
20000580 1F030000 9C000020 1F030000 01000000
20000590 1F030000 9C000020 0C000000 F9060008
200005A0 62060008 00020001 B0050020 B0050020
200005B0 07000000 FDFFFFFF 0030012B 11D10260
200005C0 08230C68 E40348BF 0123F2D4 0C68E406
200005D0 48BF0523 EDD40C68 14F0EF0F E5D00623
200005E0 E7E79DF8 00004A68 52085200 4A6032BD
200005F0 78B40822 784B8DF8 14020020 00000000
20000600 00000000 3C060020 00000000 00000000
20000610 00000000 00000000 00000000 01000000
20000620 02000000 03000000 0C000000 FDFFFFFF
20000630 28020008 00020001 23020008 E8FF0120
20000640 11D11460 08251E68 F60348BF 0125F2D4
20000650 1E68F606 48BF0525 EDD41E68 16F0EF0F
20000660 E5D00625 E7E79DF8 0020082A 47D15A68
20000670 22F44072 5A605A68 5A605A68 42F00102
```
In the memory map I found a littleendien encoded address witch was very close to the tasks `PC` at `0x200005A0`. This could very well be the place the `PC` is stored. To test this I updated the `SVC_Handler` to the following.
```c
void SVC_Handler(void)
{
uint32_t* sp = readPSP();
// get PC location
sp = sp + 6;
// get PC
uint16_t* pc = (uint16_t*)*sp;
// get SVC intruction
uint16_t instruction = *(pc - 1);
if ((instruction & 0xFF00) != 0xDF00) {
return;
}
uint8_t svc_id = instruction & 0x00FF;
switch (svc_id)
{
case 1: // taskYield
taskToExecute = schedule();
SCB->ICSR |= (1<<28);
break;
case 2: // setSysTick_10ms
SysTick->LOAD = 10 * CLOCK_FREQ_IN_KHz - 1;
break;
case 3: // setSysTick_1ms
SysTick->LOAD = 1 * CLOCK_FREQ_IN_KHz - 1;
break;
}
}
```
This code worked. The resulting code can be found at [/report-1/week_1.4/assignment_4.4/](https://git.gay/LailaTheElf/RTS10_reports/src/branch/main/report-2/week_1.4/assignment_4.4).
![](https://live.kladjes.nl/uploads/fb408589-67d1-4b37-8eb2-f852c65575f8.png)

View File

@ -37,7 +37,6 @@ void writePSP(void * ptr);
void returnToPSP(void);
void returnToMSP(void);
void toUnprivileged(void);
uint32_t* getStackpointer(void);
#define TASK_MASK MAX_TASKS-1
#define IDLE_TASK MAX_TASKS
@ -251,11 +250,18 @@ void PendSV_Handler(void)
void SVC_Handler(void)
{
uint32_t* sp = getStackpointer();
uint32_t* sp = readPSP();
// get PC location
sp = sp + 6;
// get PC
uint16_t* pc = (uint16_t*)*sp;
// get SVC intruction
uint16_t instruction = *(pc - 1);
if ((instruction & 0xFF00) != 0xDF00) {
return;
}
uint8_t svc_id = 1;
uint8_t svc_id = instruction & 0x00FF;
switch (svc_id)
{

View File

@ -13,7 +13,6 @@
.global returnToPSP
.global returnToMSP
.global toUnprivileged
.global getStackpointer
// .global SVC_Handler
pushRegistersToCurrentPSP:
@ -52,7 +51,3 @@ toUnprivileged:
ORR R0, R0, #1 // clear bit 0 (nPRIV)
msr CONTROL, R0 // copy R0 to control register
bx lr
getStackpointer:
mov R0, SP
bx lr

View File

@ -0,0 +1,304 @@
/*
* Copyright 2015 Daniel Versluis
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* schedule.h
*
* Created on: 12 nov. 2013
* Author: Daniel Versluis
* Email: VersD @ hr.nl
*
* Description:
* Contains the scheduler and the systick ISR.
* Simple and incomplete implementation.
*
*/
#include <stdlib.h>
#include <stdbool.h>
#include <stm32f4xx.h>
#include "VersdOS.h"
//The (external) assembly functions
void pushRegistersToCurrentPSP(void);
void popRegistersFromCurrentPSP(void);
void * readPSP(void);
void writePSP(void * ptr);
void returnToPSP(void);
void returnToMSP(void);
void toUnprivileged(void);
#define TASK_MASK MAX_TASKS-1
#define IDLE_TASK MAX_TASKS
//What states can our task have?
enum taskState {UNUSED = 0, RUNNING, READY, SLEEPING_DELAY};
//The task itself
typedef struct _task{
int *stack; // pointer to the stack on the heap
uint32_t counter; // a counter for delays
void (*function)(void); // function to execute
enum taskState state; // state
int8_t priority; // priority
} task;
// List of tasks
//add one space for idle task
task taskList[MAX_TASKS+1];
task * currentTask;
task * taskToExecute;
// Idle task
void idleTask(void)
{
while (1)
{
__asm(" wfi"); // Sleep until next SysTick
}
}
void addTaskToListAtIndex(void (*function)(void), uint32_t stackSize, int8_t priority, size_t pos)
{
task *taskToAdd = &taskList[pos];
taskToAdd->function = function;
// Allocate memory... do we wanna use malloc or our own implementation ;-) ?
taskToAdd->stack = (int *)malloc(stackSize)+stackSize;
/*
* For debugging purposes we initialize the stack with
* values that we can recognize.
*/
*(--(taskToAdd->stack)) = 0x01000000; //XSPR Thumb bit set
*(--(taskToAdd->stack)) = (int)taskToAdd->function; //set PC to function pointer, cast as int to silence the compiler
*(--(taskToAdd->stack)) = 0xFFFFFFFD; //LR, return with process stack (PSP)
*(--(taskToAdd->stack)) = 0x0000000C; //R12 Initial values used for debugging purposes
*(--(taskToAdd->stack)) = 0x00000003; //R3
*(--(taskToAdd->stack)) = 0x00000002; //R2
*(--(taskToAdd->stack)) = 0x00000001; //R1
*(--(taskToAdd->stack)) = 0x00000000; //R0
if(pos!=IDLE_TASK)
{
*(--(taskToAdd->stack)) = 0x0000000B; //R11
*(--(taskToAdd->stack)) = 0x0000000A; //R10
*(--(taskToAdd->stack)) = 0x00000009; //R9
*(--(taskToAdd->stack)) = 0x00000008; //R8
*(--(taskToAdd->stack)) = 0x00000007; //R7
*(--(taskToAdd->stack)) = 0x00000006; //R6
*(--(taskToAdd->stack)) = 0x00000005; //R5
*(--(taskToAdd->stack)) = 0x00000004; //R4
// Initialize the task properties
taskToAdd->state = READY;
}else{
taskToAdd->state = RUNNING;
currentTask = taskToAdd;
// Update the CPU PSP with our new stack pointer
writePSP(taskToAdd->stack);
}
taskToAdd->priority = priority;
}
/*
* Function to add a new task to the taskList
* Not sorted, prioritized or any of that kind.
*
* It simply allocates memory for the new task stack,
* fills the stack up so that the context switch will
* successfully pop these registers and start running
* at the correct address when returning from the SysTick ISR
*/
void addTaskToList(void (*function)(void), uint32_t stackSize, int8_t priority)
{
size_t i = 0;
// Simply find the next empty slot
// Loops when no more slots are available
while(taskList[i].state != UNUSED)
{
//increment i and roll back at the limit
i++;
i &= TASK_MASK;
}
addTaskToListAtIndex(function, stackSize, priority, i);
}
void startVersdOS(uint16_t sysTickPeriodIn_ms) {
// Configure SysTick of 1 ms
SysTick->LOAD = sysTickPeriodIn_ms * CLOCK_FREQ_IN_KHz - 1;
SysTick->VAL = 0;
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk //Clock source selection = Processor clock (AHB)
| SysTick_CTRL_TICKINT_Msk //Counting down to zero to asserts the SysTick exception request
| SysTick_CTRL_ENABLE_Msk; //Counter enable
//set systick and pendsv interrupt priority to lowest.
//svc will be highest.
SCB->SHP[2] |= 0xFF<<24;
SCB->SHP[2] |= 0xFF<<16;
// Create Idle task
addTaskToListAtIndex(idleTask, 128, -1, IDLE_TASK);
__asm(" wfi"); // Sleep until next SysTick
}
// currentTask is running now, return next task to run
task * schedule()
{
task* tempTaskPtr = currentTask;
task *idleTaskPtr = &taskList[IDLE_TASK];
// tempTaskPtr->state = READY;
int teller=0;
//Find next ready, non idle task.
do
{
tempTaskPtr++;
if( (tempTaskPtr-1) == idleTaskPtr || tempTaskPtr == idleTaskPtr)
{
//since idle task is the last in the list, we've reached the end
//and need to continue at the beginning
tempTaskPtr = &taskList[0];
}
}while(tempTaskPtr->state != READY && teller++ <= MAX_TASKS);
//if no task was found
if(tempTaskPtr->state != READY)
{
//idle task
tempTaskPtr = idleTaskPtr;
}
return tempTaskPtr;
}
// flag used in blocking_delay function
bool SysTick_flag;
void decrement_sleeping_delay()
{
for (size_t i=0; i < MAX_TASKS; i++)
{
if (taskList[i].state == SLEEPING_DELAY)
{
if (taskList[i].counter == 0)
{
taskList[i].state = READY;
}
else
{
taskList[i].counter--;
}
}
}
}
/*
* Interrupt routine for the Systick timer
* simply calls the scheduler
* */
void SysTick_Handler(void)
{
SysTick_flag = true;
//decrement counter for task in SLEEPING_DELAY
decrement_sleeping_delay();
//select the next task
taskToExecute = schedule();
//request context switch
SCB->ICSR |= (1<<28);
}
__attribute__((naked)) // No function entry and exit code
void PendSV_Handler(void)
{
//Push {R4-R11} context to PSP
pushRegistersToCurrentPSP();
//Save the new stack pointer after the push
currentTask->stack = readPSP();
currentTask = taskToExecute;
//Load the new stack pointer from (new) currentTask
writePSP(currentTask->stack);
//Pop {R4-R11} context from PSP
popRegistersFromCurrentPSP();
toUnprivileged(); // <--- this line was added
returnToPSP();
}
/* The most simple SVC implementation
*/
void SVC_Handler(void)
{
uint32_t* sp = readPSP();
// get PC location
sp = sp + 6;
// get PC
uint16_t* pc = (uint16_t*)*sp;
// get SVC intruction
uint16_t instruction = *(pc - 1);
if ((instruction & 0xFF00) != 0xDF00) {
return;
}
uint8_t svc_id = instruction & 0x00FF;
switch (svc_id)
{
case 1: // taskYield
taskToExecute = schedule();
SCB->ICSR |= (1<<28);
break;
case 2: // setSysTick_10ms
SysTick->LOAD = 10 * CLOCK_FREQ_IN_KHz - 1;
break;
case 3: // setSysTick_1ms
SysTick->LOAD = 1 * CLOCK_FREQ_IN_KHz - 1;
break;
}
}
//Call Super Visor
void taskYield(void)
{
asm(" svc #1");
}
void delay(uint32_t ticks)
{
currentTask->state = SLEEPING_DELAY;
currentTask->counter = ticks;
taskYield();
}
void setSysTick_10ms(void)
{
asm(" svc #2");
}
void setSysTick_1ms(void)
{
asm(" svc #3");
}

View File

@ -0,0 +1,29 @@
/*
* VersOS.h
*
* Created on: 12 nov. 2013
* Author: VersD @ hr.nl
*
*/
#ifndef VERSDOS_H_
#define VERSDOS_H_
#include <stdint.h>
// define the clock frequency you use. You have to configure it YOURSELF!
#define CLOCK_FREQ_IN_KHz 100000
// define the max number of task you can use
#define MAX_TASKS 4 // MUST be a power of 2
// Add new task to the taskList
// priority can be between 0 and 127, higher number means higher priority
void addTaskToList(void(*function)(void), uint32_t stackSize, int8_t priority);
// Start OS
void startVersdOS(uint16_t sysTickPeriodIn_ms);
void delay(uint32_t ticks);
void setSysTick_10ms(void);
void setSysTick_1ms(void);
#endif /* VERSDOS_H_ */

View File

@ -0,0 +1,53 @@
// Author: Daniel Versluis
// Email: VersD@hr.nl
// Description: Contains all neccesary low-level functions
// for pushing and popping CPU/Stack
.thumb
.syntax unified
.global pushRegistersToCurrentPSP
.global popRegistersFromCurrentPSP
.global readPSP
.global writePSP
.global returnToPSP
.global returnToMSP
.global toUnprivileged
// .global SVC_Handler
pushRegistersToCurrentPSP:
MRS r0 , psp //Move PSP to register R0 (the function argument)
STMDB r0! , {r4-r11} //Store multiple registers decrement address before each access,
//when done write back last used address to %0 (new SP location)
MSR psp , r0 //Move register %0 to psp to update to the new SP, output to scratch
MOV PC , LR //return by moving link-register to program counter
popRegistersFromCurrentPSP:
MRS R0 , psp //Move PSP to register R0
LDMIA R0! , {r4-r11} //Load multiple registers increment address after each access,
//when done write back last used address to R0 (new SP location)
MSR psp , R0 //Move register R0 to psp to update to the new SP, output to scratch
MOV PC , LR //return by moving link-register to program counter
readPSP:
MRS R0 , psp //move psp to r0
MOV PC , LR //return by moving link-register to program counter
writePSP:
MSR psp , R0 //Move r0 to psp.
ISB //flush pipeline
MOV PC , LR //return by moving link-register to program counter
returnToPSP:
ldr lr, =0xFFFFFFFD //terug met process stack
bx lr
returnToMSP:
ldr lr, =0xFFFFFFF9 //terug met process stack
bx lr
toUnprivileged:
mrs R0, CONTROL // copy control register to R0
ORR R0, R0, #1 // clear bit 0 (nPRIV)
msr CONTROL, R0 // copy R0 to control register
bx lr

View File

@ -0,0 +1,118 @@
#include <stdint.h>
#include <stdbool.h>
#include <stm32f4xx.h>
#include "VersdOS.h"
/*-------------------------------pin numbers for leds ---------------------------*/
#define GREEN 12
#define ORANGE 13
#define RED 14
#define BLUE 15
/*------------------------------------DEMO---------------------------------------*/
// blocking delay function
// keep the CPU busy for at least ticks SysTicks
void blocking_delay(unsigned int ticks)
{
while (ticks != 0) {
extern bool SysTick_flag;
while (SysTick_flag == false); // busy wait
SysTick_flag = false;
ticks--;
}
}
void toggleGreen(void)
{
while(1)
{
GPIOD->ODR ^= 1 << GREEN;
delay(100-1);
}
}
void toggleOrange(void)
{
while(1)
{
GPIOD->ODR ^= 1 << ORANGE;
delay(200-1);
}
}
void toggleRed(void)
{
while(1)
{
GPIOD->ODR ^= 1 << RED;
delay(400-1);
}
}
void toggleBlue(void)
{
while(1)
{
GPIOD->ODR ^= 1 << BLUE;
delay(800-1);
setSysTick_10ms();
GPIOD->ODR ^= 1 << BLUE;
delay(800-1);
setSysTick_1ms();
}
}
int main(void)
{
// Use HSE (8 MHz)
// Enable HSE
RCC->CR |= RCC_CR_HSEON;
// Wait until HSE is stable
while ((RCC->CR & RCC_CR_HSERDY) == 0);
// Power interface clock enable
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
// Regulator voltage scaling output selection Scale 1 mode <= 100 MHz
PWR->CR |= PWR_CR_VOS;
// Use 3 wait states when reading Flash at 100 MHz.
FLASH->ACR = FLASH_ACR_LATENCY_3WS;
// Wait until 3 wait states are used
while ((FLASH->ACR & FLASH_ACR_LATENCY_3WS) == 0);
// Enable flash prefetch buffer, instruction and data cache
FLASH->ACR |= FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN;
// Use PLL to generate 100 MHz clock
// PLL output = 8 / M * N / P: M = 4, N = 100, P = 2 to generate 100 MHz
RCC->PLLCFGR = 0x20000000
| RCC_PLLCFGR_PLLSRC_HSE
| (0 << RCC_PLLCFGR_PLLP_Pos)
| (100 << RCC_PLLCFGR_PLLN_Pos)
| (4 << RCC_PLLCFGR_PLLM_Pos);
// Enable PLL
RCC->CR |= RCC_CR_PLLON;
// Wait until PLL is locked
while ((RCC->CR & RCC_CR_PLLRDY) == 0);
// Select PLL as the system clock. AHB clock divided by 2.
RCC->CFGR |= RCC_CFGR_SW_PLL | RCC_CFGR_PPRE1_DIV2;
// Wait until PLL used as the system clock
while ((RCC->CFGR & RCC_CFGR_SWS_PLL) == 0);
// Disable HSI
RCC->CR &= ~RCC_CR_HSION;
// GPIO Port D Clock Enable
RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN;
// GPIO Port D led pins Push/Pull Output
GPIOD->MODER |= 1 << 2 * GREEN | 1 << 2 * ORANGE | 1 << 2 * RED | 1 << 2 * BLUE;
// Create tasks. Provide fpointer, stacksize, and priority:
addTaskToList(toggleGreen, 128, 2);
addTaskToList(toggleOrange, 128, 2);
addTaskToList(toggleRed, 128, 2);
addTaskToList(toggleBlue, 128, 2);
// Start VersdOS with 1 ms sysTick
startVersdOS(1);
while(1);
}