( bzt | 2024. 08. 03., szo – 12:02 )

Szerkesztve: 2024. 08. 03., szo – 12:16

Ahogy az előre tudható volt, a nagypofájú trollok közül egyetlen egy sem volt képes válaszolni.
És most a várható időjárás: kellemes napos, de szórványosan habzó szájú trollpanaszeső fordulhat elő.

Minden más fórumtársnak ellenben nagyon szívesen, remélem hasznotokra válik ez az alacsony szintű leírás.

A helyes válaszok:

- exception

Ezt a CPU generálja, valamilyen rendszerregiszterbeállítás vagy utasítás hatására (értsd: NEM egy dedikált utasítás hatására), például DIV előidézhet division by zero-t, vagy az STR utasítás translation fault-ot.
Ezek akkor is kiváltódnak, ha a megszakítások egyébként le vannak tiltva a CPU-ban (x86-on CPU interrupt flag, ARM-on a DAIF regiszter).

- soft int

Ezt is a CPU generálja, azonban egy dedikált utasítás hatására (x86-on INT, ARM-on BRK utasítás, vagy ha privilégiumszintváltást is akarunk, SVC, HVC stb.). Mivel ez a forráskódban is megjelenik, ezért hívják ezt szoftveres, azaz "soft int"-nek.

- hard int

Ezt NEM a CPU generálja, hanem valamilyen, CPU-tól független áramkör, amit jellemzően interrupt controllernek hívnak (IC). Régen PC-ken ez a PIC volt, manapság az APIC, x2APIC vagy épp az IOAPIC. ARM-on ugyanez a helyzet, ott is független, például régi Pi-ken BroadcomIC volt, Pi4-n QA, Pi5-ön meg már GIC. Szóval NEM a CPU, hanem attól független a forrása. Mivel nem szoftver, hanem hardver generálja, ezért "hard int" a neve.

Ezeknek annyira nincs közük a CPU-hoz, hogy például x86-on a RESET-et követően a hard int-ek átfedésben leledzenek a CPU exception-jeivel, és a rendszerprogramozó dolga, hogy átprogramozza az IC-t és az ütközést megszüntesse.
A fogadásukat a CPU egységesen (értsd: nem egyenként) tilthatja vagy engedélyezheti (x86-on CPU interrupt flag, ARM-on a DAIF regiszter).

- IRQ

Valamilyen külső periféria generálja az IC felé, ami hard int-et idéz elő ennek hatására a CPU-ban. Ezt nyilván a csak magas szintet ismerő trollok úgysem lesznek képesek felfogni, nekik csak "IRQ" minden, de valójában áramkörileg ez a periféria-IC és az IC-CPU jel tök "más dróton megy", és külön is kell ezeket konfigurálni.

Hiába vannak a megszakítások engedélyezve a CPU-ban, mégsem jön létre csak úgy egy hard int se, mivel az IRQ-kat még külön-külön is engedélyezni kell az IC-ben. Valamint vice versa, hiába vannak az IRQ-k engedélyezve, ha a hard int egységesen le van tiltva a CPU-ban.

Az IC-ben konfigurálható, hogy adott IRQ-hoz milyen hard int jöjjön létre (x86-on IRQ-nként állítható ez, ARM-on meg az, normál vagy a fast hard int ISR-je hívódjon-e meg).

- interrupt vs. trap

A kétféle megszakítás között az a különbség, hogy az utasításszámláló a következő utasításra (interrupt) vagy a megszakítást kiváltó utasításra (trap) mutat-e. Ez azért lényeges, mert előbbi nem újrajátszható, míg az utóbbi igen.

Például x86-on an INT 3 utasítás kiváltja a megszakítást, ami interrupt típusú, tehát az INT 3 utasítás UTÁNNI cím mentődik le, és az utasítás maga nem újrajátszható. Ezzel szemben mondjuk egy érvénytelen címre hivatkozó MOV utasítás trap-et vált ki, mondjuk page fault-ot, aminél az azt kiváltó utasítás utasításszámlálója mentődik, így tehát az az ISR lefutása után a MOV újra végrehajtásra kerül.

- RET vs. IRET/ERET

A sima RET utasítás, amit alapesetben a C fordító generál csak visszatér a hívóhoz, de nem idézhet elő privilégiumszintváltást (x86-on kiveszi a veremből a CALL által lerakott címet (csakis egyetlen címet, semmi mást!) és odaugrik, ARM-on a visszatérési címet egy GPR tárolja (hangsúlyozom, egy GPR), amit a BL utasítás helyez oda).

Ezzel szemben az IRET/ERET úgy tér vissza a hívóhoz, hogy közben privilégiumszintváltás történhet (ehhez x86-on több mindent kell kivenni a veremből, ún. interrupt stack frame-t, míg ARM-on a a visszatérési címet NEM egy GPR-ből, hanem egy rendszerregiszterből, az egyik ELR valamelyikéből veszi és más rendszerregiszterek is közrejátszanak, tehát hangsúlyozom, NEM egy GPR-ből).

Magyarán a kettő működési mechanizmusa nagyon nem azonos, ezért nem lehetséges szabvány C függvénnyel megszakításkezelőt írni (abban a pillanatban, hogy bármilyen hívásvezérlő attribútumot biggyeszt valaki a függvényhez, az már NEM szabvány ABI). Amikor egyesek az NVIC_SetVector()-t hívták, az valójában NEM az alacsony szintű megszakításvezérlőt állította, hanem a megszakításvezérlő által hívott lekezelő függvény címét (ami már lehet szabvány C függvény, hiszen az ERET-et nem az adja ki, hanem az alacsony szintű ISR).

ARM8 esetén nincs is megszakításivektortábla, de még csak semmilyen tábla se, hanem fix méretű megszakítástípusfüggvények vannak. Azaz megszakítástípusokra van egy-egy fix méretű ISR a VBAR rendszerregiszter által mutatott kódterületen. Ha például exception történik, akkor mindegy, hogy milyen exception, ugyanaz az ISR kód fut le, ami aztán konzultál az Exception Syndrome Register nevű rendszerregiszterrel, és az alapján dönti el, mi is történt és melyik lekezelő függvényt is kell meghívnia.
Ha IRQ történik, ugyanez, akkor is egységesen ugyanaz az ISR fut le, csak ilyenkor ennek az ISR-nek IC típus-specifikus (értsd: CPU-tól független) módon kell kiderítenie, hogy melyik IRQ is történt valójában, és az annak megfelelő lekezelő függvényt hívnia. Hangsúlyozom, hogy ARM8-on ez szoftveresen történik egy alacsony szintű ISR által, aminek MUSZÁJ az ERET utasítással visszatérnie.

Ezzel szemben x86-on van megszakításvektortábla, viszont az itt beállított minden egyes funkciónak MUSZÁJ az IRET utasítással visszatérnie, tehát ezek sem lehetnek szabványos C függvények (továbbá attól függően, hogy exception trap/abort vagy interrupt történt-e, eltérő a stack frame, szóval még gatyába is kell rázni a vermet egy esetleges C függvény hívása és az IRET utasítás kiadása előtt, ami csak Assembly nyelven lehetséges).

Mégegyszer hangsúlyozom, bár már előre megírtam, hogy a trollok picsogására nem fogok válaszolni.