# Lista 1
###### tags: `sju21` `ćwiczenia`
:::warning
<small>**Uwaga!** Zadanie 1-7 jest za dwa punkty!</small>
:::
## Deklaracje
Gotowość rozwiązania zadania należy wyrazić poprzez postawienie X w odpowiedniej kolumnie! Jeśli pożądasz zreferować dane zadanie w trakcie dyskusji (co najwyżej jedno!) oznacz je znakiem ==X== na żółtym tle.
**UWAGA: Tabelkę wolno edytować tylko wtedy, gdy jest na zielonym tle!**
:::danger
| | 1-1 | 1-2 | 1-3 | 1-4 | 1-5 | 1-6 |1-7(2)|
| ---------------------:| --- | --- | --- | --- | --- | --- | --- |
| Jacek Bizub | X | X | X |==X==| X | X | X | 8
| Maciej Dudek | X | X | X | X | | | | 4
| Wojciech Jasiński | X | X | X | | | | | 3
| Artur Juraszek | X | X | | | | | | 2
| Aleksandra Kosińska | | | | | | | | 0
| Michał Myczkowski | | | | | | | | 0
| Michał Jan Odorczuk | X | X | X | X |==X==| | | 5
| Damian Ratajski | | | | | | | | 0
| Łukasz Siudek | | | | | | | | 0
| Błażej Sowa | | | | | | | | 0
| Cezary Stajszczyk | | | | | | | | 0
| Andrzej Turko | X | X | X | X |==X==| X | X | 8
| Maksymilian Zawartko | X | X | X | X | X | X | | 6
:::
## Zadanie 1-1
:::info
Autor: Artur Juraszek
:::
tryb użytkownika - przeznaczony dla programów wykonywanych pod kontrolą systemu operacyjnego stan procesora, podczas trwania którego każda próba wykonania instrukcji uprzywilejowanej skończy się wygenerowaniem wyjątku
tryb nadzorcy - stan przeznaczony dla systemu operacyjnego, w którym dozwolone jest wykonywanie wszystkich instrukcji
instrukcje uprzywilejowane - takie, które mogą być wykonane jedynie w trybie nadzorcy.
Gdyby pozwolić na ich wykonanie programom działającym w przestrzeni użytkownika, mogłyby one dokonać niepożądanych w ich przypadku działań takich jak STOP lub RESET lub, w przypadku instrukcji operujących na rejestrze SR, po prostu zmienić swój tryb na uprzywilejowany.
Lista instrukcji uprzywilejowanych:
- ANDI to SR -> AND na rejestrze stanu procesora (przy użyciu wartości _immediate_)
- EORI to SR -> eXclusive OR na SR (jak wyżej, przy użyciu _immediate_)
- MOVE from SR -> wykonanie kopii rejestru SR (uprzywilejowana jedynie na Motoroli 68010 [i późniejszych?], na której to wprowadzono instrukcję MOVE from CCR odczytującą fragment SR kodujący stan _condition codes_, czyli tę jego część, która może okazać się interesująca dla programów uruchomionych w trybie użytkownika)
- MOVE to SR -> zapisanie wartości operandu źródłowego w rejestrze SR
- MOVE USP -> kopia z/do rejestru stosu użytkownika - analogiczny efekt w trybie użytkownika można osiągnąć używając po prostu MOVE SP/A7, w trybie nadzorcy jednak SP oraz A7 wskazują na stos odrębny
- MOVEC -> operacje na rejestrach kontrolnych, np VBR
- MOVES -> operacja na adresach z przestrzeni adresowych oznaczonych w rejestrach SFC lub DFC
- ORI to SR -> bitowa alternatywa wartości immediate oraz rejestru SR
- RESET -> wysyła sygnał rozkazu zresetowania się do podłączonych urządzeń
- RTE -> powrót z procedury obsługi wyjątku, przywraca zapisane wcześniej wartości SR oraz PC
- STOP -> wstrzymuje działanie procesora do momentu nadejścia przerwania
## Zadanie 1-2
:::info
Autor: Wojciech Jasiński
:::
* Kiedy procesor znajduje się w trybie uprzywilejowanym?
- kiedy bit S (Supervisor State) w SR jest zapalony
* Czemu procesor ma odrębny wskaźnik stosu dla trybu użytkownika «USP» i nadzorcy «SSP»?
- żeby system nie martwił się co nabroił użytkownik na swoim stosie (i na odwrót). Użytkownik mógłby np. ustawić swój wskaźnik stosu na dziwne miejsce (jak początek pamięci), z którego brakowałoby miejsca dla systemu na rozwinięcie ramek stosu do obsługi przerwania; albo na adres, w którym jądro trzyma ważne dane (które zostałyby nadpisane)
* Czemu skonstruowanie bezpiecznego systemu operacyjnego jest niemożliwe bez takiego wsparcia ze strony procesora?
- patrz wyżej
## Zadanie 1-3
:::info
Autor: Maciej Dudek
:::
Aby procesor przeszedł z trybu użytkownika do trybu nadzorcy dzieje się tylko gdy:
* program użytkownika wywoła doprowadzi do przerwania(trapy, nielegalne instrukcji)
* przyszło zewnętrzne przerwanie
Aby przejśc z trybu nadzorcy do trybu użytkownika można wykonać kilka instrukcji:
* RTE (return from exception)
* MOVE to SR
* ANDI to SR
* EORI to SR
Każda instrukcja poza RTE pozwala na zmienienie SR w tym bitu S, ale kontynuacja odbywa się bez zmiany wskaźnika instrukcji.
Instruckja RTE ładuje nowy stan procesora ze stosu.
Inne zachowanie wynika z wprowadzenia nowych formatów ramek stosu fraz z serią 68010.
## Zadanie 1-4
:::danger
Autor: Jacek Bizub
:::
> Załóżmy, że program działający w trybie użytkownika na procesorze M68010 wykonał dzielenie przez zero. Co robi procesor [2, 6.2] zanim wskoczy pod etykietę «ZeroDivTrap»?
Procesor musi zapisać na stosie obecny stan rejestru `SR` (status register) oraz przejść w tryb uprzywilejowany ustawiając flagę `S` oraz wyłączyć śledzenie poprzez zgaszenie bitu`T`.
Konstruuje ramkę wyjątku (`SR`, `PC` oraz `vector offset`).
> Przeprowadź uczestników zajęć przez procedurę `vPortSetupExceptionVector`, w której zainicjowano wektor wyjątków procesora. Na podstawie tabeli [2, 6-2] omów warunki jakie zmuszą procesor do wejścia do poszczególnych procedur.
```c=
ExcVecBase = (ExcVec_t *)aBootData->bd_vbr;
```
Uwzględniamy offset z rejestru `VBR`
```c
/* Set up magic number and pointer to boot data for debugger. */
111 ExcVec[0] = (ESR_t)0x1EE7C0DE;
112 ExcVec[1] = (ESR_t)aBootData;
```
Początek wektora wyjątków zawiera obsługę resetu. Ustawiamy kolejno początkową wartość stack pointera oraz adres programu bootującego.
```c
/* Initialize M68k interrupt vector. */
115 for (int i = EXC_BUSERR; i <= EXC_LAST; i++)
116 ExcVec[i] = BadTrap;
```
Inicializujemy wektor domyślnym adresem. Nie będziemy wypełniać wszystkich 255 pól a nie chcemy wylecieć w kosmos.
```c
118 /* Initialize exception handlers. */
119 ExcVec[EXC_BUSERR] = BusErrTrap;
120 ExcVec[EXC_ADDRERR] = AddrErrTrap;
121 ExcVec[EXC_ILLEGAL] = IllegalTrap;
122 ExcVec[EXC_ZERODIV] = ZeroDivTrap;
123 ExcVec[EXC_CHK] = ChkInstTrap;
124 ExcVec[EXC_TRAPV] = TrapvInstTrap;
125 ExcVec[EXC_PRIV] = PrivInstTrap;
126 ExcVec[EXC_TRACE] = TraceTrap;
127 ExcVec[EXC_LINEA] = IllegalTrap;
128 ExcVec[EXC_LINEF] = IllegalTrap;
129 ExcVec[EXC_FMTERR] = FmtErrTrap;
```
Wpisujemy odpowiednie handlery dla danych wyjątków. Opis znaczenia wyjątków:
| wyjątek | powód |
| --- | --- |
| EXC_BUSERR | najczęściej: bug w programie, odwołanie do nieistniejącej pamięci ;; rzadziej: uszkodzenie hardware'u|
| EXC_ADDRERR | próba odczytania słowa/longa z nieparzystego adresu |
| EXC_ILLEGAL | procesor nie rozpoznał słowa jako znanej instrukcji |
| EXC_ZERODIV | dzielenie przez zero |
| EXC_CHK | "asercja" z instrukcji CHK nie została spełniona |
| EXC_TRAPV | robiliśmy arytmetykę i się przekręciliśmy |
| EXC_PRIV | próba wykonania instrukcji uprzywilejowanej z konstekstu użytkownika |
| EXC_TRACE | jeśli program działa w trybie śledzenia to wyjątek jest rzucany po każdej instrukcji |
| EXC_LINEA/EXC_LINEF | instrukcja oddelegowana do emulatora (linia 1010 lub 1111) |
| EXC_FMTERR | niepoprawny format ramki wyjątku |
```c=
/* Initialize level 1-7 interrupt autovector in Amiga specific way. */
132 ExcVec[EXC_INTLVL(1)] = AmigaLvl1Handler;
133 ExcVec[EXC_INTLVL(2)] = AmigaLvl2Handler;
134 ExcVec[EXC_INTLVL(3)] = AmigaLvl3Handler;
135 ExcVec[EXC_INTLVL(4)] = AmigaLvl4Handler;
136 ExcVec[EXC_INTLVL(5)] = AmigaLvl5Handler;
137 ExcVec[EXC_INTLVL(6)] = AmigaLvl6Handler;
139 for (int i = INTB_TBE; i <= INTB_EXTER; i++)
140 IntVec[i].code = DummyInterruptHandler;
```
Ustawiamy domyślną obsługę przerwań
```c=
152 /* Intialize TRAP instruction handlers. */
153 for (int i = EXC_TRAP(0); i <= EXC_TRAP(15); i++)
154 ExcVec[i] = TrapInstTrap;
```
Kierujemy wszystkie 15 trapów do handlera `TrapInstTrap`
## Zadanie 1-5
:::info
Autor: Michał Jan Odorczuk
:::
"W pliku trap.S zaimplementowano wejście do jądra w wyniku otrzymania wyjątku procesora
lub pułapki. Wykonaj deasemblację pliku «trap.o» przy pomocy polecenia m68k-elf-objdump. Następnie
przeprowadź uczestników zajęć od «ZeroDivTrap» do końca procedury «EnterTrap». Co znajduje się
na stosie przed wejściem do «EnterTrap», a co w momencie wołania «vPortTrapHandler»? Co robią
poszczególne instrukcje manipulujące wskaźnikiem stosu?"
```c=
#define HANDLER(name,trap) \
ENTRY(name); \
move.w __IMMEDIATE trap,-(sp); /* save trap number */ \
// Odkładamy na stos numer pułapki (stan przed EnterTrap)
jra EnterTrap; \
// Wkraczamy w handler
END(name)
# List of trap handlers
HANDLER(BadTrap,T_UNKNOWN)
HANDLER(BusErrTrap,T_BUSERR)
HANDLER(AddrErrTrap,T_ADDRERR)
HANDLER(IllegalTrap,T_ILLINST)
HANDLER(ZeroDivTrap,T_ZERODIV)
HANDLER(ChkInstTrap,T_CHKINST)
HANDLER(TrapvInstTrap,T_TRAPVINST)
HANDLER(PrivInstTrap,T_PRIVINST)
HANDLER(TraceTrap,T_TRACE)
HANDLER(FmtErrTrap,T_FMTERR)
HANDLER(TrapInstTrap,T_TRAPINST)
ENTRY(EnterTrap)
movem.l d0-a7,-(sp) /* save all registers */
move.l usp,a0
move.l a0,-(sp) /* save user stack pointer */
move.l sp,-(sp) /* pass trap frame as an argument */
// Stan przed vPortTrapHandler:
// - numer pułapki
// - rejestry procesu
// - wskaźnik na stos użytkownika
// - ramka stosu
jsr vPortTrapHandler
addq.l #4,sp
move.l (sp)+,a0
move.l a0,usp /* restore user stack pointer */
movem.l (sp)+,d0-a7 /* restore all registers */
addq.l #2,sp /* pop trap number */
rte
END(EnterTrap)
# vim: ft=gas:ts=8:sw=8:noet:
```
## Zadanie 1-6
:::info
Autor: Maksymilian Zawartko
:::
Skrót procedury `vPortDefaultTrapHandler`:
```
set pc and sr depending on processor model
if supervisor mode {
Determine real stack pointer value, as processor pushes data on stack before it enters the trap handler.
}
else {
sp = frame->usp
}
print registers contents
if memory fault {
print info what data/instruction caused the fault
}
```
Pola TrapFrame odkładane na stos przez procesor to sr, pc i inne pola zawarte w jednej ze struktur:
```c=
typedef struct {
uint16_t sr;
uint32_t pc;
} trap_stk_000_t;
typedef struct {
uint16_t status;
uint32_t address;
uint16_t instreg; // never used
uint16_t sr;
uint32_t pc;
} trap_stk_000_memacc_t;
typedef struct {
uint16_t sr;
uint32_t pc;
uint16_t format;
} trap_stk_010_t;
typedef struct {
uint16_t sr;
uint32_t pc;
uint16_t format;
uint16_t ssw;
uint32_t address;
uint16_t pad[22];
} trap_stk_010_memacc_t;
```
usp, d0-a6, sp, trapnum są odkładane przez kod wejścia do procedury obsługi pułapki.
Po napotkaniu błędu adresu M68010 odkłada na stos te same informacje, co w przypadku błędu szyny (bus error), czyli to, co na rysunku 6-8 z "M68000 Microprocessor’s User Manual".

Jądro może chcieć zmienić zawartość TrapFrame i wrócić do przerwanego kontekstu, gdy użytkownik woła coś w stylu printStackTrace.
## Zadanie 1-7
:::info
Autor: Andrzej Turko
:::
Kod (preemption/main.c):
```c=
extern void vPortDefaultTrapHandler(struct TrapFrame *);
void vPortTrapHandler(struct TrapFrame *frame) {
static int cnt = 0;
if (frame->trapnum == T_TRAPINST) {
printf ("It's a trap! -- %d\n", cnt++);
return;
}
vPortDefaultTrapHandler(frame);
}
```
Uruchomienie debuggera:
```
examples/preemption$ make debug-rom
```
1. trap #1 -- okłada status register (ps) oraz program counter na stos
2. Zapisanie id trapa (trap.S: **niekoniecznie ten sam, z którym trap był zawołany!**)
3. zapisanie stanu procesora (pozostałe rejestry)
4. branch do obsługi wyjątku
...
obsługa wyjątku
...
6. Odzyskujemy wartości rejestrów zapisane w punkcie 3.
5. RTE -- przywracamy to, co trap wrzuciło na stos