• Non ci sono risultati.

4.2 Analisi del codice

4.2.3 Routine di interruzione

4.2.3.1 Interrupt Test

Quando viene sollevata la linea delle interruzioni da un dispositivo hardware esterno (in questo caso può essere causata solamente dal timer in quanto per il momento è l'unico dispositivo in grado di generare interruzioni), l'interrupt test successivo vengono eseguire una serie di operazioni hardware di salvataggio dei registri più sensibili della CPU per poter garantire la ricostruibilità dello stato precedente. Nella fattispecie, le istruzioni eettuate sono le seguenti:

/∗ i n t e r r u p t t e s t ∗/ i f ( i n t e r r u p t && ( ! d i s a b l e _ i n t e r r u p t s ) ) { i f ( read_flag_kernel_mode (NOTRACE) == 0) { s e t _ s p e c i a l _ r e g i s t e r (SAVED_STACK_POINTER, r e a d _ r e g i s t e r (STACK_POINTER, NOTRACE) ) ;

4.2. ANALISI DEL CODICE 63 s e t _ r e g i s t e r (STACK_POINTER,KERNEL_STACK_POINTER) ; } s e t _ s p e c i a l _ r e g i s t e r (SAVED_FLAG_REGISTER, r e a d _ s p e c i a l _ r e g i s t e r (FLAG_REGISTER, NOTRACE) ) ; s e t _ s p e c i a l _ r e g i s t e r (SAVED_RETURN_ADDRESS, r e a d _ r e g i s t e r (RETURN_ADDRESS, NOTRACE) ) ; d i s a b l e _ i n t e r r u p t s =1; set_flag_kernel_mode ( 1 ) ; i n t e r r u p t =0; s e t _ r e g i s t e r (RETURN_ADDRESS, read_program_counter ( ) ) ; set_program_counter (INTERRUPT_ROUTINE_PRESET_ADDRESS) ; clear_TLB ( ) ; }

La condizione per la quale l'interrupt test viene eseguito è il fatto che le interruzioni stesse non siano disabilitate. Se le interruzioni sono abilitate e se la linea di interrupt è sollevata viene per prima cosa valutato il ag di kernel. Qualora esso sia alto, signica che l'interruzione è avvenuta durante l'esecuzione di codice kernel, mentre in caso contrario, ovvero qualora risulti basso, l'interrupt ha comportato l'interruzione del usso di un programma utente. Questa distinzione risulta fondamentale in quanto durante il passaggio da codice utente a codice kernel, e solamente in questo caso, vi è necessità di impostare lo stack pointer al valore predenito del sistema operativo. In questo frangente viene inoltre preventivamente salvato il precedente stack pointer, ovvero quello del programma utente, nel relativo registro speciale in maniera tale da garantire che questa specie di cambio di contesto a favore del kernel non sia distruttivo per lo stato posseduto dalla CPU prima dell'avvento dell'interruzione.

Vengono inne salvati i registri FR e RA nei relativi registri speciali. Il registro dei ags deve essere salvato, da un lato perché i ag aritmetici potrebbero venire inavvertitamente modicati dalle routine del kernel, dall'altro perché cosi facendo si contribuisce anche a salvare lo stato del ag di kernel. Il salvataggio dei registri e il recupero degli stessi con l'istruzione di ritorno è di fondamentale importanza in quanto con il ripristino dei registri si ripristina anche lo stato del ag di kernel. Ad esempio, se quello a venire interrotto è stato il programma utente allora verrà ripristinato a zero il ag di kernel, se quello ad essere interrotto sarà una routine dello stesso sistema operativo, il ag di kernel verrà comunque ripristinato correttamente. In sostanza quello che si va a ripristinare è il registro dei ag e dunque il ag di kernel del programma interrotto, indipendentemente dal fatto che si trattasse di sistema operativo o programma utente.

Il Return Address deve inne venire salvato in un registro speciale perché, qua-lora il programma interrotto stesse eseguendo del codice all'interno di una CALL, esso deve venire ripristinato correttamente, altrimenti, in seguito all'esecuzione della relativa istruzione di RET, il ritorno corretto non sarebbe garantito.

Le operazioni che seguono il salvataggio dei registri riguardano la disabilita-zione delle interrupt, il settaggio del kernel mode (che modica eventualmente il

Flag Register, ma il suo salvataggio è ormai già stato eettuato) e l'imposizione a zero la linea di interrupt. Quest'ultima operazione è fondamentale in quanto se la linea rimanesse alta, l'istruzione dopo verrebbe eseguita nuovamente la procedura appena descritta.

Indirizzo di ritorno da interrupt, call e trap Le due istruzioni che seguono coincidono con quelle eseguite da qualsiasi istruzione di CALL eccetto per il fatto che in RA non viene messo il program counter incrementato di due (come nella istruzione di CALL), ma il program counter senza alcuna modica. Ciò è dovuto al fatto che il ritorno da una routine di interruzione deve prevedere il fetching dell'istruzione che si stava per eseguire nel momento in cui è risultato positivo l'interrupt test. Questa istruzione è individuabile in quanto il suo indirizzo nel segmento corrisponde proprio al valore del program counter salvato in RA. Vi-ceversa, le istruzioni di CALL sono caratterizzate da un incremento del program counter di due unità in quanto l'istruzione di ritorno è quella successiva alla CALL stessa. L'istruzione di CALL è seguita da una label che identica un indirizzo su una word e ciò comporta che l'istruzione successiva si trovi quattro locazioni dopo la posizione della CALL. Il primo incremento di due locazioni viene esegui-to durante l'instruction fetch, il secondo incremenesegui-to si ha durante l'esecuzione della CALL. Si vedrà inne che, per quanto riguarda le trap il discorso è ancora leggermente diverso. La trap, infatti, viene generalmente chiamata mediante una istruzione e questo comporta il fatto che l'instruciton fetch è già stato esegui-to e il valore del program counter è già staesegui-to incrementaesegui-to di 2 puntando alla istruzione successiva a quella di INT. L'istruzione di INT infatti, occupa sola-mente una word in quanto l'informazione riguardante la routine da eseguire viene memorizzata all'interno del byte meno signicativo dell'instruction register.

Da notare che il salto avviene ad una locazione di memoria predenita e deno-minata INTERRUPT_ROUTINE_PRESET_ADDRESS che punta all'indirizzo 0004 del segmento del kernel. A questa locazione di memoria vi è una istruzione di Branch (salto) alla routine di interrupt che può a questo punto venire eseguita. Il passaggio da programma utente a programma kernel richiede inne l'annul-lamento dei campi della TLB. Con l'abilitazione del ag di kernel, si fa riferimento ora alla tabella delle pagine del kernel per la traduzione da indirizzo virtuale ad indirizzo sico. Tuttavia la ricerca della pagina viene prima eettuata all'interno della TLB. Qualora capiti che la stessa pagina di cui ora è alla ricerca il kernel, fosse stata precedentemente usata da un programma utente e dunque si trovasse nella TLB, la traduzione da indirizzo virtuale ad indirizzo sico sarebbe errato in quanto si andrebbe a fare riferimento all'area di memoria principale in cui è mappata la relativa pagina del programma utente, e non quella del kernel.

Ricapitolando, le operazioni hardware necessarie da eseguire per garantire codice rientrante e trasparente sono:

4.2. ANALISI DEL CODICE 65

Salvataggio di SP nel relativo registro speciale SSP solo se vi è passaggio da utente a kernel

Salvataggio di FR in SFR Salvataggio di RA in SRA Disabilitazione interrupt Settaggio ag di kernel ad 1

Linea di interrupt mascherata a zero

Chiamata all'indirizzo di memoria (con program counter non incrementato) Azzeramento della TLB

Documenti correlati