---
# System prepended metadata

title: Lista 6
tags: [sju21, ćwiczenia]

---

# Lista 6

###### tags: `sju21` `ćwiczenia`

## 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
|                       | 6-1 | 6-2 | 6-3 | 6-4 | 6-5 | 6-6 |
| ---------------------:| --- | --- | --- | --- | --- | --- |
|           Jacek Bizub |  X  |  X  |  X  |  X  |  X  |  X  |
|     Wojciech Jasiński |     |     |     |     |     |     |
|        Artur Juraszek |  X  |==X==|  X  |  X  |     |     |
|     Michał Myczkowski |     |     |     |     |     |     |
|       Michał Odorczuk |     |     |     |     |     |     |
|       Damian Ratajski |     |     |     |     |     |     |
|         Łukasz Siudek |     |     |     |     |     |     |
|           Błażej Sowa |  X  |  X  |     |     |     |     |
|         Andrzej Turko |  X  |  X  |  X  |  X  |  X  |==X==|
|  Maksymilian Zawartko |     |  X  |     |  X  |     |==X==|
:::

## Zadanie 6-1

:::success
Autor: Błażej Sowa
:::

Implementacja [sys_ioctl]:
1. Sprawdzamy rozmiar danych do przekopiowania [sys_generic.c:674]
2. Weryfikujemy poprawność komendy ioctl.
3. Alokujemy pamięć w kernel-space na dane [sys_generic.c:692]. Jeśli rozmiar danych jest odpowiednio mały, bierzemy pamięć ze stosu.
4. Jeśli jest to operacja IN, używamy [copyin(9)] do przekopiowania danych z user-space do naszego bufora. Dla operacji OUT, zerujemy bufor, żeby mieć deterministyczny wynik.
5. Uruchamiamy kern_ioctl
6. Dla operacji OUT, używamy [copyout(9)], żeby przekopiować bufor do user-space.

Gdyby jądro nie używało funkcji [copy(9)], wywołanie systemowe ioctl mogłoby zakończyłoby się awarią jeśli, np:
 - przekażemy mu adres wskazujący na pamięć należącą do jądra przy operacji OUT,
 - przekażemu mu adres, pod który nie odwzorowano pamięci

[sys_ioctl]: https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/kern/sys_generic.c?r=80f39bd9#sys_ioctl
[sys_generic.c:674]: https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/kern/sys_generic.c?r=80f39bd9#674
[sys_generic.c:692]: https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/kern/sys_generic.c?r=80f39bd9#692
[copyout(9)]: https://www.freebsd.org/cgi/man.cgi?query=copyout&sektion=9
[copyin(9)]: https://www.freebsd.org/cgi/man.cgi?query=copyin&sektion=9
[copy(9)]: https://www.freebsd.org/cgi/man.cgi?query=copy&sektion=9

  
## Zadanie 6-2

:::success
Autor: Artur Juraszek
:::

- [odsianie warunków brzegowych](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/riscv/riscv/copyinout.S?r=80f39bd9#117) - dane o rozmiarze `0`, adres spoza dostępnego dla użytkownika zakresu
- przygotowanie [punktu powrotu](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/riscv/riscv/copyinout.S?r=80f39bd9#62) w razie niepowodzenia; mechanizm ten przypomina nieco skoki nielokalne - w odpowiednim miejscu zostawiamy adres, do którego chcielibyśmy skoczyć, gdy nastąpi "twardy" page fault, a procedura obsługi page faulta, gdy ów adres zauważy, zmodyfikuje licznik instrukcji przerwanego przez wyjątek procesu, wykonując tym samym skok do `copyio_fault`
  Wspomnianym "odpowiednim miejscem" jest:
  - [pole `pcb_onfault`](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/riscv/include/pcb.h?r=80f39bd9#55) struktury `pcb`, którą znajdziemy w...
  - ...[strukturze `thread`](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/sys/proc.h?r=80f39bd9#342) (opisującej wątek)...
  - ...na którą z kolei wskazuje [odpowiednie pole `pcpu`](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/sys/pcpu.h?r=80f39bd9#178)
- [Włączamy możliwość dostępów do pamięci należącej do użytkownika](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/riscv/include/asm.h?r=80f39bd9#66)
- Przechodzimy do właściwego kopiowania - pod adresem docelowym nie ma jednak żadnej zmapowanej pamięci, więc któraś z instrukcji wywoła pagefault

Gdy trafimy już do procedury obsługi wyjątku strony:

- [wydobywamy błędny adres](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/riscv/riscv/trap.c?r=80f39bd9#196)
- [upewniamy się, że zarejestrowano](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/riscv/riscv/trap.c?r=80f39bd9#215) adres procedury onfault - błędy strony, które nastąpiły w trybie jądra podczas dostępu do adresów spoza przestrzeni kernela są śmiertelne
- [wyrównujemy adres do początku zawierającej go ramki](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/riscv/riscv/trap.c?r=80f39bd9#221)
- [z zapisanej wartości rejestru `scause` dowiadujemy się, że problem wystąpił podczas próby zapisu](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/riscv/riscv/trap.c?r=80f39bd9#224)
- [naiwnie sprawdzamy, czy pagefault nie jest może jedynie efektem pierwszego dostępu do danej strony](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/riscv/riscv/trap.c?r=80f39bd9#231) - ze względu na software'owe zarządzanie bitem **A**ccess, generuje ono wyjątek
- [Trawersujemy po drzewie stron, jednak prędzej czy później natrafimy na stronę-liść bądź stronę pośrednią, której bit **V**alid nie jest zapalony](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/riscv/riscv/pmap.c?r=80f39bd9&fi=pmap_fault_fixup#2394)
- [Pozbywamy się początkowego entuzjazmu - problem był poważniejszy niż jeden czy dwa bity](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/riscv/riscv/pmap.c?r=80f39bd9&fi=pmap_fault_fixup#2426)
- [Przeszukujemy mapę pamięci procesu](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/vm/vm_fault.c?r=80f39bd9&fi=vm_fault_trap#vm_fault_trap), w celu odnalezienia obiektu, na który problematyczny adres miałby wskazywać - takiego obiektu jednak nie ma
- [Wychodzimy z procedury z błędem `KERN_INVALID_ADDRESS`](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/vm/vm_fault.c?r=80f39bd9&fi=vm_fault_trap#689)
- [Poddajemy się](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/riscv/riscv/trap.c?r=80f39bd9#239) - w `pcb_onfault` zauważamy, że zarejestrowano adres, do którego w dokładnie takiej sytuacji powinniśmy skoczyć. Modyfikujemy zatem licznik instrukcji przerwanego procesu i wychodzimy z funkcji. Ponieważ cały ten czas działaliśmy w ramach procedury obsługi pułapki, po zakończeniu jej (gdzieś niżej w stosie wywołań) przywrócimy stan procesu ze zmodyfikowanym licznikiem.
- [Czyścimy wpis informujący o handlerze](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/riscv/riscv/copyinout.S?r=80f39bd9#47) (rejestr `x0` to tak naprawdę stała `0`) i zwracamy `EFAULT` jako wynik `copyout`.

## Zadanie 6-3

:::info
Autor: Jacek Bizub
:::

> Ustalając procedurę obsługi sygnału [sigaction(2)](https://www.freebsd.org/cgi/man.cgi?query=sigaction&sektion=2) można podać flagę `SA_RESTART`, żeby pewna grupa wywołań systemowych była **restartowalna**. Przeprowadź uczestników zajęć przez implementację [syscallenter](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/kern/subr_syscall.c?r=80f39bd9#syscallenter) i [syscallret](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/kern/subr_syscall.c?r=80f39bd9#syscallret),  po  czym  powiedz  jak  jądro  (a)  zwraca  wynik  wywołania  `td_retval` oraz  kod  błędu  `td_errno`  (b)  restartuje  przerwane  wywołanie?  Należy  znaleźć  odpowiednie  procedury dla architektury RISC-V do pobierania argumentów wywołania systemowego i ustawiania zwracanych wartościoperujących na kontekście użytkownika `td_frame`.

-----

> If	a signal is caught during the system calls listed below, the call may
     be	forced to terminate with the error EINTR, the call may return with a
     data transfer shorter than	requested, or the call may be restarted.
     Restart of	pending	calls is requested by setting the SA_RESTART bit in
     sa_flags.	The affected system calls include open(2), read(2), write(2),
     sendto(2),	recvfrom(2), sendmsg(2)	and recvmsg(2) on a communications
     channel or	a slow device (such as a terminal, but not a regular file) and
     during a wait(2) or ioctl(2).  However, calls that	have already committed
     are not restarted,	but instead return a partial success (for example, a
     short read	count).

----

- [syscallenter:retval](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/kern/subr_syscall.c?r=80f39bd9#197)
- [RISC-V cpu_set_syscall_retval](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/riscv/riscv/vm_machdep.c?r=80f39bd9#129)
- [RISC-V cpu_fetch_syscall_args](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/riscv/riscv/trap.c?r=80f39bd9#96)
- [Ponowienie syscalla](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/riscv/riscv/vm_machdep.c?r=80f39bd9#144)

----

W skrócie:
- Ściągamy argumenty
- Wywołujemy obsługę syscalla
- Modyfikujemy kontekst interesanta

## Zadanie 6-4

:::info
Autor: Maksymilian Zawartko
:::

`trapsignal` wysyła do zadanego procesu sygnał będący wynikiem pułapki. Procedura ta bierze jako argument `ksiginfo_t *ksi`: https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/kern/kern_sig.c?r=80f39bd9&fi=trapsignal#trapsignal.

`do_trap_user` wykonuje jedno z poniższych zadań:
1. Jeśli przerwanie: obsłuż je.
2. Jeśli błąd dostępu: zawołaj `trapsignal`, zmodyfikuj kontekst użytkownika.
3. Jeśli błąd strony: zawołaj procedurę obsługi.
4. Jeśli wywołanie systemowe: obsłuż je.
5. Jeśli nieprawidłowa instrukcja: zawołaj `trapsignal`, zmodyfikuj kontekst użytkownika.
6. Jeśli breakpoint: zawołaj `trapsignal`, zmodyfikuj kontekst użytkownika.
7. W przeciwnym przypadku: zapisz rejestry i panikuj.

Główne zadania `page_fault_handler`:
1. Zawołaj `pmap_fault_fixup` przekazując mu informacje o typie błędu (`VM_PROT_WRITE`, `VM_PROT_EXECUTE` lub `VM_PROT_READ`.
2. Jeśli zadziała, to zmodyfikuj kontekst i zakończ.
3. Jeśli nie, sprawdź, co poszło nie tak za pomocą procedury `vm_fault_trap`. Zawołaj `trapsignal` i zmodyfikuj kontekst użytkownika.


## Zadanie 6-5

:::info
Autor: Jacek Bizub
:::

> Na podstawie glosariusza powiedz czym się różni pamięć zadrutowana (ang. wired memory) od pamięci stronicowalnej (ang. pageable memory)?  Czemu  nie  można  trzymać  blokady `mutex` (ani `MTX_DEF`, ani `MTX_SPIN`) na czas wykonania procedur `copyin` i `copyout`.

Pamięć stronnicowalna to taka, która w pewnym momencie może zostać zeswapowana (usunięta z RAMu ale przechowywana dalej na dysku).

Pamięć zadrutowana nie może zostać zeswapowana. Ergo zawsze jest obecna w RAMie.

-----
> No mutexes should be held (except for Giant) across functions which access memory in userspace, such as copyin(9), copyout(9), uiomove(9), fuword(9),	etc.  No locks are needed when calling these functions.
     
> Sleeping while holding a mutex (except for Giant) is never safe and should be avoided.	 There are numerous assertions which will fail if this is attempted.

Komentarz: pamięć użytkownika może zostać zeswapowana, więc kopiowanie z niej może spowodować sen (nieograniczony?) z powodu potrzeby ściągnięcia danych z dysku.

## Zadanie 6-6

:::info
Autor: Andrzej Turko
:::

[kern_wait6](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/kern/kern_exit.c?r=80f39bd9#kern_wait6):
1. Iterujemy się po dzieciach: jeśli jakieś się zakończyło, przetwarzamy je i wracamy.
...
Niuanse związane z debuggerem i synchronizacją.
...
2. Zasypiamy czekając, aż jakieś nasze dziecko się skończy. Podajemy mutex, który synchronizuje dostęp do structa z danymi wątku. Kończące się dziecko będzie chciało pisać do tego structa. Dlatego musimy oddać ten mutex (ale musimy robić to atomowo, więc nie robimy tego ręcznie, tylko msleep robi to za nas).
3. Po wybudzeniu przechodzimy do kroku 1.
msleep(q, &q->p_mtx, PWAIT | PCATCH | PDROP, "wait", 0)
q -- etykieta wydrzenia (wskaźnik na naszego structa)
q->p_mtx -- mutex do oddania przed zaśnięciem, nie będziemy go brać ponownie (PDROP)
PCATCH -- sen może być przerwany przez sygnały
"wait" -- information string o naszym stanie (na potrzeby np. programu ps)
0 -- nie ustawiamy timeoutu

[wybudzanie rodzica](https://mimiker.ii.uni.wroc.pl/source/xref/FreeBSD/sys/kern/kern_exit.c?r=80f39bd9#600)
wakeup(pp) -- podajemy etykietę wydarzenia, na którym może czekać rodzic.