tryby rzeczywistości 5/9 #Konstytutka do wiązania rąk Bogu;
//wróć do spisu treści; tekst jest fragmentem większej całości;
#Konstytutka do wiązania rąk Bogu;
Kolejne kwestie opiszę na razie w sposób chaotyczny, aby odnieść się do mniej więcej obecnie używanych systemów, ale jak ta logika wyrosła zarządzania klockiem (jako rozbudowanym układem) wyrosła (i sam klocek się upasł) opiszę w #Uprośćmy – jak to wyrosło; To wcale nie jest skomplikowane i prawdopodobnie w taki sposób piszecie programy (względnie słyszeliście, że ktoś tak robi i na konfach mędrkowie od kręcenia ozorem, co żadnego użytecznego kodu przez całą prelekcję nie odpalą pomstują na to bo reusability i dostępność dla małp nie nadają się dla małp w biznesie). Przyczyna, dla której tego na razie nie strukturyzuję jest przejściowa. Do tego potrzebny jest OS, mając UEFI cała ta logika już jest obsługiwana (bo to jest OS), a na BIOS byłaby załadowana później (bo tu nie ma dość adresowania pamięci, żeby to wlepić). Więc to tylko taka kwestia przejściowa, czy mamy dostęp do hopsztylionów linii poleceń, które poustawiają omawiany dalej porządek. Tym jest właśnie OS. Klocek może chodzić na ułamek gwizdka w trybie rzeczywistym nie używając tych wszystkich stosów, kopców i biurokracji, po prostu za każdym razem będzie się odwoływał do kabli sygnałem i czekał aż wróci (jeśli ma na coś czekać). Koniec disklajmerstwa.
Control Register (CR) to abstrakt do przechowywania ustawień (takie “zworki”) na co się sami ze sobą umawiamy, że nam wolno lub byśmy chcieli się od zamysłu powstrzymać, jeśli nie wyrazimy go formułką wcześniej anulującą zapisy konstytutki ze wszystkimi konsekwencjami. Oczywiście później postawimy przy tym strażnika z ognistym mieczem (general protection fault) gdybyśmy się na własne Słowo zamachiwali. Fizycznie ten rejestr to takie zworki wewnątrz klocka (flipflopy) i nie jest to część RAM więc mają swoją własną instrukcję (ustawienie tranzystorów) zapisu i odczytu (Crn). Jak odetniecie prąd to flipflopy wracają do stanu zero (chyba, że są po prostu kablami/ścieżkami bez flipa) i dlatego po starcie kompa macie tryb rzeczywisty 16bit, bo cr0 ma wartość zero.
Gdybyście to próbowali przekładać na jakiś abstrakt wysokiego poziomu, żeby objaśnić małpie to są to takie ustawienia, które są potrzebne przed uruchomieniem programu. Na przykład w jakiej rozdzielczości będzie aplication surface, albo inne bzdety, które zwyczajowo zadajecie w setupie przed void main.
Warstwa zerowa.
Zerowa zworka podłącza nam do 16 bitowego rejestru 16 dodatkowych (czyli mamy FPU 32bit), pierwsza włącza obsługę tego urządzenia (fizycznego, czyli funkcji ALU), druga zaś blokuje tę funkcję każąc udawać w cyklach jej obsługę na 16bitach (czyli te antyczne układy wymieniane wcześniej), szesnasta ochronę zapisu (r/w – read only wyjaśniane przy odpalaniu trybu chronionego wcześniej), ostatnia włącza stronicowanie (czyli wirtualizację opisaną w #Małpowanie pamięci;), żeby Tygrysek się nie potykał o własne nogi.
Warstwy pierwszej się nie używa. Znaczy cr1 miał być, były takie plany, ale jak to w korporach i standaryzacji zarezerwowano i nie odebrano paczki. Są to niezdefiniowane kody opcji. ARM tego użył (dawno) do obsługi MMU i debugu, ale im przeszło. Do pewnych, zabytkowych działań PowerPC było wyzyskiwane, i Motorola sobie to podłączyła. Rejestr jak każdy, ale jego użycie wymaga konstrukcji klocka, gdzie coś jest z nim związane. W większości platform jakich używacie do kodoklepstwa te kable prowadzą w pustkę. Na razie, bo nie wiadomo co komu jeszcze strzeli do łba. Miała być tam piękna, długa, prosta, szeroka jak morze Trasa Łazienkowska.
W drugiej spisywane są błędy i wypaczenia. To będzie potrzebne później dla OS, jeśli PEBKAC odwoła się do czegoś, czego nie ma u archiwisty (pamięci) to z tego rejestru będzie OS pobierał adres na Berdyczów do którego się odwołano i zawinie go do wyświetlenia w opisie błędu, żeby użyszkodnik odpalający program mógł opluć monitor kląc w żywy kamień. Oczywiście na dużo wyższych poziomach abstrakcji klepiąc kod w loopach try/catch zbieramy to do logu (albo w ogóle ignorujemy^^) i każemy przejść nad tym do porządku dziennego, aż pociąg wykolei się zupełnie i OS nam zamknie program. W wielu wypadkach znacie to jako “program nie odpowiada”, a wynika to z faktu, że bajzel jaki mimo włączanych właśnie biurokracji porządkowych powstanie na tych nieogarnialnych przestrzeniach pamięci jest naprawdę nieogarnialny bez rygoru, jakiego trzymaliśmy się na 64kilo, a przy gigabajtach i szybkich cyklach klockach dopuściliśmy do nonszalancji.
Trzecia przechowuje pointer (ściągę – “gdzie ja to urwał zapisałem”) na tłumaczenie z wirtualnych adresów (możecie to prześledzić na przykład używając RAMMap) na fizyczne, a ustawienie zworek warstwy zero wskazuje ilu poziomów zagnieżdżeń ma ten archiwista (czyli 16bit ma mega na “płasko” [dwa poziomy, ulica-dom], 32 ma cztery gigi, 64 ma hopsztylion).
Czwarta ma zworki uruchamiające pewne fikołki na pamięci (czyli ile bajtów w paczce, ile paczek na paletę i jak duży jest wagon; czwarta i piąta) co pozwala adresować te hopsztyliony pamięci jakimi dysponujecie w kompie w taki sposób, żeby wysyłając sygnał “oddaj” po kablu oddawany był sygnał nie z jednego bita, a z paczki 8 czy 1024 bitów czy jakiejś innej liczby w sekwencji, co pozwala “naoszukiwać” (i uprościć życie) przy zrzucaniu z fizycznej pamięci na rejestr nie trapiąc ciągłymi zapytaniami o bit, a na rejestr (rozdzielający w dzielonym między rdzenie cache3, lub na c2 jeśli to kawalerka) wjeżdżają po kablu w cyklach kolejne, predefiniowane ustawieniem tego rejestru paczki.
I tu taj też znajdują się ustawienia dozorcy (flagowanie bitów SMEP i SMAP). Nazwijmy ten abstrakt hipervisor. Zworka 20 powstrzymuje Boga (supervisor) od wykonania kodu oflagowanego jako “użyszkodnik napisał” (ochrona przed egzekucją), a zworka 21 przed mieszaniem się Boga w notatki użytkownika (czyli “poprawianiem” mu zapisów, na takie jakie uważane są za słuszne i prawomyślne). Logika tego jest taka, że zabraniamy sobie arbitralnego zmieniania pamięci oflagowanej jaku user (ring3) oraz wykonywania jako Bóg tego co user ma napisane w swoich prośbach na ścianie płaczu. Wynika to z tego, że możliwe było (na kompie, z którego piszę ciągle jest) wrzucenie na rejestr klocka czegoś co znajduje się w pamięci oflagowanej jako user, czyli tej piaskownicy w jakiej na przykład mogę sobie napisać ten oto grafomański tekst. I wrzucić na rejestr do wykonania. Oczywiście brednie jakie tu wypisuje są nieszkodliwe i niezrozumiałe dla klocka, ale przyjmijmy, że w cache przeglądarki znalazłby się fragment jak najabardziej zrozumiałego dla klocka kodu i zostałby wykonany. I coś by tam napsocił. Aby psocić pomimo tego, że na drugi dzień byłaby na to łatka dość istotnym było aby pierwsze co taki psotnik uczyni była możliwość zapisu do tej mojej piaskownicy kolejnych poleceń wykonania już czegoś bardziej złożonego, a to oznaczało “olewaj łatkę, która by wyłączała mi to i tamto”.
Na czwartej znajdziecie też obsługę standardów (czyli konwencji zapisu) dla zmiennoprzecinkowych oraz wyjątków (czyli jaka wartość interpretowana jako zmiennoprzecinkowa jest głupia, jak NaN czy inf+/-; oraz stosunek z epsilonem). Stosunek z epsilonem dla operacji zmiennoprzecinkowych jest o tyle istotny, że… przyjmijmy iż konstruujecie ALU, które takie operacje wykonuje. I ma w cyklu przetworzyć magiczny zapis bitowy na dużo większą liczbę bitów int i sobie przepuścić przez mul i adder tak, żeby na końcu wypluć znowu w formacie znak, cecha, mantysa. Przyjmijmy, że wrzuciliście to na od razu na wyświetlacz kolejnych, wypadających w każdym cyklu wyników i dzielicie (mnożycie przez odwrotność) coś co da jakiś niekończący się cykl liczb, ale wartość rejestru pozostanie niepusta (czyli ma kolejny cykl do wykonania). Na wyświetlaczu zobaczycie wtedy jakąś wartość całkowitą przesuwającą się w prawo (przyjmijmy taką konwencję kalkulatora), za nią będzie zapier… przecinek, a za nią kolejne wypadające cyferki. W pewnym momencie wyświetlacz z lewej się fizycznie skończy, a to dalej będzie wypluwały kolejne cyferki z prawej. Rejestr jest takim wyświetlaczem i też się urwał kończy. Trzeba więc ustalić, kiedy liczba jest taka, że albo cecha, albo mantysa się wyczerpie i wystarczy tego liczenia, oraz kiedy jakaś liczba jest wystarczająco blisko innej aby nie przeprowadzać dyskryminacji (czyli, że 0.9999 to jest tyle ile jest, ale 0.9999999 to już jest jeden).
A dalej są różne ściągawki dla później odpalanego OS, gdzie może on sobie zapisać listę zakupów (TPL) z wagami ważności (czyli co na pewno kupić, żeby użyszkodniczka nie krzyczała).
Podobna procedura dotyczy urządzeń (komendy In/Out), które w trybie rzeczywistym działają dokładnie tak samo jak 50 lat temu. Jest pewien zakres fizycznyj pamięci, z którego urządzenie pobiera sobie ustawienia (Memory Małped I/O) tak jak dla przykładu VGA pobiera ze swojego “prostokąta” co tam ma za piksel się świecić na telezorce i jak bardzo. Chipset na płycie zajmuje się rozprowadzeniem tych sygnałów do i z rejestrów pamięci.
W innych trybach niż rzeczywisty (na przykład UEFI czy inny OS) jest dodatkowa pętla mapowania wykonywana przez “sterownik” i używane są adresy wirtualne pamięci (patrz #Małpowanie pamięci; bo rozmiar “prostokąta pamięci” dla ekranu nie jest odgórnie ustalony i w kamieniu zapisany jak w real mode). I dlatego używamy polecenia port, które dalej jest z abstraktu która pamięć (MMU) dla mikrokodu cpu (które urządzenie), wysyła na chipset warstość, przyjmując, że dokonano wcześniej prawidłowego ustawienia (In/Out) trybu w jakim urządzanie zrozumi. Przypadkiem, w którym użyszkodnik może się przekonać o sypaniu abstrakcji jest pisanie programu, który przechwytuje fullscreen i używa rozdzielczości device, a następnie używa innej dla aplication surface i jest zdziwiony, że dziwnie to jakoś na ekranie wygląda. Albo program zajmuje mały kawałek ekranu, albo widać mały kawałek programu. W językach wysokiego poziomu ten proces przechodzi przez tyle warstw abstrakcji, że z reguły dodawane są całe bloki kodu “wklej to na początku”, żeby się użyszkodnicy nie kopali z koniem (a obsługa ogl czy wulkana to kopiuj, wklej książkę). Problem jest czysto semantyczny, ponieważ składnia tych poleceń (ograniczonych kompilatorem, OS, uprawnieniami ring3) na pierwszy rzut oka wydaje się użyszkodnikom głupia. Są to na przykład takie fikołki jak “wejdź do/uruchom, zmień zakres, wejdź jeszcze raz/ uruchom bez wyłączania” co nastręcza pytania, czy mam gdzieś wyjść, bo to jedyne ustawienie początkowe i innych nie mam, a to niestety tak działa w imperatywach i ludzie mają poważny problem ze zrozumieniem tego w językach wysokiego poziomu, ale jak mają to ogarnięte na poziomie chipsetu wydaje się to proste i oczywiste.
Oczywiście możecie sobie (poprzednio opisywałem robieniem dziury w kadłubie) zachować prawo do wykonywania poleceń In/Out bez wirtualizacji (albo wyłącznie z wirtualizacją pamięci), tak czy tak albo smep albo smap, albo oba będziecie musieli supresować (wywalić w UEFI ich uruchomienie) i później sprawdzenie stanu przez kernel. Co oznacza taką listę modyfikacji, któr ama sens jeśli podłączacie coś wyjątkowo dziwnego (kartę rozszerzeń do sterowania jakimś wihajstrem), który akurat nie ma sterowników czy to EFI czy oesowych bo je właśnie klepiecie i przeprowadzacie testy. Oczywiście występują w naturze urządzenia do tego przystosowane, bo ktoś te sterowniki robi. Stoimy na ramionach krasnoludków. Bez tych zabiegów próba dobrania się do In/Out przez fizyczny adres urządzenia jest blokowana na Wasz Wcześniejszy Rozkaz i dawana do rozkminy w abstrachujących to pętlach o jaki chodzi port, i co tam wpisać (jaką wartość mieliście na bezmyśli w kodzie wysokiego poziomu). Czy ten port jest dostępny, czy czipset ma takie urządzenie podpięte to musicie sobie rozwiązać w wyższych poziomach abstrakcji, kiedy trafi to do wykonaniu w ring0 to będzie już argument w rejestrze pobrany z fizycznego adresu pamięci (po tłumaczeniach) i wbity na fizyczny kabel, niezależnie czy na jego końcu coś jest i jak ten imperatyw przyjmie.
