Progettazione e Realizzazione di un Emulatore di Xerox Alto per Raspberry Pi - Dipartimento di Matematica e Informatica Corso di Laurea in Informatica
←
→
Trascrizione del contenuto della pagina
Se il tuo browser non visualizza correttamente la pagina, ti preghiamo di leggere il contenuto della pagina quaggiù
Dipartimento di Matematica e Informatica Corso di Laurea in Informatica Progettazione e Realizzazione di un Emulatore di Xerox Alto per Raspberry Pi Relatore: Prof. Federico Bergenti Candidato: Beretta Davide Matricola: 222565 Anno Accademico 2013/2014
Indice Prefazione 1 1 Descrizione del progetto 3 1.1 Xerox Alto . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 1.1.1 Tastiera . . . . . . . . . . . . . . . . . . . . . . . . . . 4 1.1.2 Mouse . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 1.1.3 Display graco . . . . . . . . . . . . . . . . . . . . . . 5 1.1.4 Disco sso/processore . . . . . . . . . . . . . . . . . . 5 1.1.5 Software . . . . . . . . . . . . . . . . . . . . . . . . . . 5 1.2 Salto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 1.2.1 Architettura di Salto . . . . . . . . . . . . . . . . . . . 9 1.3 Raspberry Pi . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 1.4 Strumenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 2 Sviluppo di piAlto 16 2.1 Bare metal su Raspberry Pi . . . . . . . . . . . . . . . . . . . 16 2.2 Il kernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 2.3 Newlib . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 2.4 Salto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 2.4.1 Il le salto.c . . . . . . . . . . . . . . . . . . . . . . . . 30 2.4.2 Analisi e modica di Salto . . . . . . . . . . . . . . . . 33 2.5 CSUD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 Conclusioni 51 Bibliograa 53
Prefazione In questo testo verrà descritto lo sviluppo di piAlto, un emulatore del personal computer Xerox Alto pensato per funzionare su Raspberry Pi (un mini PC ARM) in ambiente bare metal, ovvero senza sistema operativo residente. La scelta di utilizzare il Raspberry Pi in questo modo nasce dalla volontà di ottenere un comportamento nale del sistema il più simile possibile a quello dello Xerox Alto in quanto si riducono i tempi di avvio e non ci sono altri elementi che possono far capire all'utente che eettivamente non si tratta della macchina originale. Date le dimensioni del PC scelto infatti è possibile, reperendo l'hardware adatto, riprodurre anche esteticamente l'aspetto dello Xerox Alto per esempio collegando un vecchio monitor CRT girato su di un anco al Raspberry Pi ed utilizzando un mouse ed una tastiera somiglianti a quelli dell'epoca. Il progetto piAlto è stato infatti concepito allo scopo di costruire una macchina da poter collocare nel Museo del Dipartimento di Matematica e Informatica. Nel caso non fosse possibile riprodurre in modo fedele l'aspetto della macchina originale si potrebbe utilizzare un normale monitor LCD ed un mouse e tastiera USB standard oppure modicare hardware recente per farlo sembrare storico, anche se quest'ultima ipotesi comporterebbe una quantità di lavoro non indierente. É stato scelto proprio l'Alto a causa del numero limitato di esemplari funzionanti al mondo, il che lo rende estremamente raro, e perché fu un computer molto innovativo, in particolar modo dal punto di vista dell'interazione con l'utente. Lo Xerox Alto rappresenta una importante tappa storica dell'informatica moderna, con lo sviluppo di questo computer infatti vennero introdotte le prime interfacce grache così come le conosciamo oggi. La parte del progetto che eettivamente riproduce il comportamento dello Xerox Alto non è stata scritta da zero ma si è partiti da un emulatore scritto in C già parzialmente funzionante scaricabile dal Web: Salto ([11]). É un progetto che non è più attivo da tempo ma emula già sucientemente bene le varie parti della macchina; non si tratta di un emulatore funzionale in quanto all'interno cerca di riprodurre in modo fedele il comportamento delle varie parti dell'Alto compreso la gestione dei task. 1
INDICE É stato quindi necessario adattare Salto in modo che potesse funzionare sul dispositivo nale, per fare questo è stato necessario implementare la parte di sistema che gestisce il boot del Raspberry Pi. Dato che l'emulatore utilizza le librerie SDL per la scrittura a video è stato poi necessario scrivere le funzioni per la gestione del framebuer che andassero a sostituire le librerie originali. Un altro problema è stata l'assenza delle librerie standard del C; per evitare di riscrivere funzioni scritte ad hoc non ottimizzate sono state utilizzate Newlib (https://sourceware.org/newlib), un set di librerie standard per ambiente embedded. Nel prossimo capitolo verrà descritta la macchina oggetto dell'emulazione ovvero lo Xerox Alto, successivamente sarà più dettagliatamente spiegato il funzionamento di Salto e le applicazioni disponibili. Verrà poi fatta una piccola introduzione sul Raspberry Pi ed inne saranno introdotti gli strumenti utilizzati per lo sviluppo di piAlto. Nel capitolo 3 sarà invece descritto lo sviluppo eettivo e verrà inizialmente spiegato quindi cosa signica sviluppare per la piattaforma utilizzata senza sistema operativo. In seguito è presente la documentazione riguardante il kernel sviluppato a supporto di Salto ed i problemi riscontrati nello sviluppo. Inne si parlerà dell'integrazione del progetto con Newlib e delle modiche apportate all'emulatore per ottenere un prodotto funzionante. Nel capitolo 4 verranno inne tratte le conclusioni e saranno introdotti possibili aggiustamenti e funzionalità aggiuntive per piAlto. Occorre evidenziare che sarebbe stato estremamente dicile documentare ogni prova fatta per cui quello che si cerca di fare in questo testo è di presentare un riassunto riguardante lo sviluppo delle parti importanti del progetto. 2
Capitolo 1 Descrizione del progetto In questo capitolo sarà prima presentata la storia e l'architettura dello Xerox Alto, in seguito verrà descritto l'emulatore Salto presente in rete e le applicazioni da questo supportate. Verrà poi fatta una piccola introduzione sul Raspberry Pi, il dispositivo hardware per il quale è stato sviluppato piAlto. Inne verranno presentati gli strumenti utilizzati per lo sviluppo come il compilatore e l'emulatore. 1.1 Xerox Alto Lo Xerox Alto fu uno dei primi computer ad essere pensati per uso individuale, quello che noi oggi chiamiamo Personal Computer. Fu prodotto nel 1972 da Xerox Corporation per motivi di ricerca e il suo nome deriva dal centro dove fu sviluppato (Xerox PARC). L'obbiettivo era quello di ottenere un dispositivo che fosse abbastanza piccolo da poter stare in un ucio ma abbastanza potente da poter supportare un sistema operativo stabile e di alta qualità oltre ad un display graco. Lo Xerox Alto era concepito per provvedere a tutte le necessità dell'utente ed in particolare per permettere agli utilizzatori di scambiarsi informazioni facilmente. Non era pensato per la produzione di massa e il costo era proibitivo per permetterne l'utilizzo privato, i pochi esemplari prodotti (circa 2000) vennero utilizzati perlopiù nelle università e nei centri di ricerca. I progettisti dello Xerox PARC si ispirarono all'oN Line System di Douglas Engelbart e anche se non fu mai realmente commercializzato molte idee introdotte con l'Alto si ritrovano anche nei computer dei giorni nostri. Lo Xerox Alto è stato ad esempio il primo computer della storia ad utilizzare la metafora della scrivania: nacque infatti l'interfaccia graca di tipo WIMP (Window, Icon, Menu e Pointing device), oggi la norma nella maggior parte dei computer. Molte altre furono le innovazioni implementate per la prima volta, come ad esempio la tecnologia Ethernet. 3
1.1 Xerox Alto A livello hardware l'Alto consiste in 4 parti principali: la tastiera, il mouse, il display graco e il disco sso/processore. Il cabinet è metallico di color beige, ben progettato ed il tutto era pensato per stare su di una scrivania o un tavolo da lavoro. Figura 1.1: Xerox Alto 1.1.1 Tastiera La tastiera a prima vista è simile a quelle delle macchine da scrivere con l'aggiunta di alcuni tasti speciali. É removibile ed abbastanza comoda per scrivere; ha la particolarità per cui i tasti non sono codicati per cui è presente un segnale apposito per ogni pulsante, il che permette ai programmi di sapere facilmente se due o più tasti sono premuti contemporaneamente. 1.1.2 Mouse Il mouse è una piccola scatola con tre bottoni al di sopra e diverse rotelle al di sotto, un cavo sottile lo collega all'Alto e i bottoni sono chiamati red, yellow e blue. Il movimento viene rilevato usando le sfere presenti al di sotto della scatola ed è riportato in modo che sia elaborato dalla macchina. Molte applicazioni possono essere controllate esclusivamente con il mouse ed anche in questo caso i tasti non sono codicati. 4
1.1 Xerox Alto 1.1.3 Display graco Il display è forse la parte più impressionante della macchina, le dimensioni siche sono 8 pollici di larghezza e 10 di altezza con una risoluzione di 606x808 in bianco e nero. É di tipo bit-mapped raster scan, il che signica che ogni punto sul display è indirizzabile come un bit in memoria ed anche se la RAM richiesta è maggiore il rendering risulta più veloce. Questa tecnica mette a disposizione dell'utente un metodo conveniente per l'accesso al display e permette di controllare in ogni momento cosa viene visualizzato. L'Alto può gestire 60 linee di 90 caratteri ciascuna ed il rendering non è realizzato in hardware. Un set di caratteri può essere creato dall'utente e visualizzato ed è possibile utilizzare font di dimensioni e forme diverse contemporaneamente. Essendo ogni punto rappresentato in memoria come un bit non esiste la possibilità di visualizzare le varie tonalità di grigio che però possono essere rappresentate combinando i colori nero e bianco per formare una sorta di texture che assomigli al colore desiderato. 1.1.4 Disco sso/processore Il processore e il disco sso sono montati in un contenitore grande circa come un piccolo frigorifero, l'Alto dispone di due dischi ssi da 3 MB ciascuno mentre la CPU è a 16 bit costruita su misura utilizzando circuiti integrati TTL. Il processore può eseguire circa 400000 istruzioni per secondo e la macchina dispone di 128 KB di RAM (espandibile no a 512 KB). Lo Xerox Alto può eseguire no a 16 task in modo concorrente ma l'utente ha il pieno controllo solo del proprio; essendo questo il processo a più bassa priorità gli altri task (controllo tastiera, mouse, disco, rete) possono interromperlo quando necessario. 1.1.5 Software L'Alto ha la particolarità di utilizzare il software per eseguire delle operazioni tipicamente svolte in hardware. La macchina può eseguire tutto il software utilizzando la rete ed è presente una ROM contente un piccolo programma che imposta la rete locale utilizzata nel caso qualche parte dei programmi di sistema non funzioni. Il sistema operativo è scritto in BCPL (il linguaggio da cui deriva il C) ed è chiamato Alto Operating System, questo provvede a fornire delle semplicazioni nella comunicazione tra gli applicativi ed il dispositivo. Uno dei programmi principali che operano sotto il controllo dell'Alto OS è l'Executive che provvede a interfacciarsi direttamente con l'utente permettendo l'esecuzione di altri programmi e la manipolazione di le. Una funzionalità molto interessante dell'Executive è l'autocompletamento dei nomi; per sfruttare questa caratteristica si scrive parzialmente il nome di un le o di un programma e si digita un Escape, se 5
1.2 Salto l'Executive è in grado di identicare in modo univoco il le allora il suo nome viene completato in automatico. Se invece è digitato `?' allora viene mostrata la lista di tutti i le che possono corrispondere alla ricerca. Le funzionalità più interessanti dell'Alto sono relative all'interfaccia utente. Lo schermo infatti può essere diviso in nestre ed è possibile usare mouse o tastiera per controllare le applicazioni. Per esempio si possono selezionare voci ed elementi semplicemente premendo un tasto ed il cursore del mouse cambia forma nel passare da una zona ad un'altra dello schermo. Sono presenti programmi per i compiti più comuni come text editor, calcolatrice, gestore dei le e addirittura un programma di disegno per la creazione e manipolazione di immagini fatte di punti, curve e testo. 1.2 Salto Salto è un emulatore dello Xerox Alto scaricabile dal Web che realizza le principali funzioni della macchina originale cercando di emulare il più precisamente possibile tutti i componenti della stessa. Sfortunatamente non è perfettamente stabile e le applicazioni possono chiudersi inaspettatamente, inoltre presenta dei problemi con il cursore del mouse che sfarfalla in modo anomalo. Non implementa funzionalità di rete e non si possono fare scritture permanenti di dati tuttavia è uno strumento abbastanza completo e permette di rendersi conto sucientemente bene del funzionamento della macchina originale. Dopo l'avvio di Salto viene eseguito Alto Executive. Come già detto l'Executive è un interprete a riga di comandi utilizzato per gestire i le ed eseguire i programmi. Sono disponibili diverse versioni: Executive/7 (14/10/1976), Executive/11 (26/06/1980) e Executive/12 (8/01/1983). Input e output del programma sono gestiti solamente in una nestra di 16 linee e si possono usare le wildcard per riferirsi ai le e rendere più facile l'utilizzo del sistema. Lo Xerox Alto è un ambiente monotasking dove le applicazioni prendono il completo controllo della macchina durante l'esecuzione. Siccome la risoluzione dello Xerox Alto era 606x808 e Salto apre una nestra che ha almeno queste dimensioni (è presente anche un pannello con alcuni pulsanti), questo richiede un monitor con una risoluzione di almeno 1280x1024 per poter visualizzare tutta la nestra. La velocità di Salto è dipendente dalla macchina su cui lo si esegue per cui su computer più lenti si avranno prestazioni inferiori e viceversa. Questo è dovuto al fatto che il ciclo macchina principale è implementato utilizzando un concetto di tempo virtuale legato all'uso di contatori che rappresentano nanosecondi. 6
1.2 Salto Figura 1.2: Salto con Executive in attesa di input Saranno di seguito descritte le principali applicazioni disponibili per Salto: Neptune è un le manager graco che permette di gestire drive e cartelle utilizzando esclusivamente il mouse; altre applicazioni per Alto invece lo utilizzano solo per puntare e richiedono poi la tastiera per iniziare l'operazione. É presente una scrollbar per gestire la lista di voci e il cursore cambia icona se ci si trova in quest'area. Il programma permette anche di modicare o creare un le di testo. Esistevano diversi tipi di mouse per lo Xerox Alto: quello più conosciuto aveva tre tasti orizzontali, ognuno di un diverso colore e tutti i manuali fanno riferimento a questo modello in particolare. Bravo è un word processor ed era il primo editor del tipo What You See Is What You Get. Il testo è formattato tenendo conto dei font e delle relative dimensioni. Le modiche al documento sono visibili immediatamente sullo schermo e, a dierenza dei moderni editor, per abilitare l'input era necessario immettere un comando da tastiera e non c'erano menù da poter utilizzare. La lista dei comandi disponibili era presente sui manuali. Il mouse era prevalentemente usato per selezionare testo e, come nel caso di Neptune, è disponibile una scrollbar sulla sinistra dello schermo per scorrere i documenti. 7
1.2 Salto Figura 1.3: Neptune (alla sinistra) e Bravo (alla destra) Draw è un programma di disegno vettoriale. Invece di trattare le immagini come griglie di pixel queste vengono viste come un insieme di punti connessi con le loro proprietà e si può copiare e trasformare ogni oggetto indipendentemente. É presente un insieme di tool utilizzabili sul lato sinistro dello schermo e si può selezionare qualsiasi strumento tramite il mouse. Come nel caso di Bravo per utilizzare funzionalità avanzate è necessario immettere comandi con la tastiera (vengono usate anche combinazioni di tasti sfruttando il tasto Control) ed anche in questo caso la lista di comandi si trova sul manuale. Markup è un programma di disegno i cui le immagini sono viste come griglie di pixel. Per eettuare operazioni è possibile, a dierenza di altre applicazioni, usare menù che vengono richiamati tramite il tasto centrale. Su Salto questa applicazione è molto instabile. Laurel è un client e-mail per Xerox Alto che permette l'invio e la ricezione di e-mail da parte di altri utenti attraverso un server centralizzato. Questo programma non è utilizzabile in Salto in quanto le funzionalità di rete non sono implementate; è tuttavia presente un le e-mail di esempio. L'interfaccia graca è caratterizzata da interessanti funzionalità: è possibile utilizzare parte del testo come pulsante selezionabile per lanciare un comando e la toolbar si espande o meno a seconda si faccia richiesta di informazioni aggiuntive. 8
1.2 Salto Figura 1.4: Bravo (alla sinistra) e Laurel (alla destra) Sono inoltre disponibili una calcolatrice (Calculator), un programma di utilità per la gestione dei dischi (DIEX) e diversi giochi tra cui Galaxian, Maze, Pac-Man e Astro-roids (un clone di Asteroids). 1.2.1 Architettura di Salto Sul Web è possibile scaricare i sorgenti oppure la versione precompilata di Salto per Windows. Utilizzando Linux sono stati scaricati i sorgenti ed essendo Salto scritto in C sono stati compilati utilizzando GCC preinstallato nel sistema. Perché la compilazione vada a buon ne occorre che siano installate almeno le librerie SDL e i relativi le di sviluppo (tipicamente il pacchetto libsdlxx-dev, dove xx è la versione desiderata). All'interno del progetto Salto, una volta eseguito GCC, è possibile trovare diversi eseguibili: aar, aasm, adasm, aldump, convbdf, dumpdsk, edasm, ppm2c, salto. Quest'ultimo è l'emulatore vero e proprio mentre gli altri sono semplici utilità di supporto. Salto ore molte funzionalità al di là della semplice emulazione, permette infatti di eettuare screenshot oltre a poter registrare la nestra per produrre video. Permette inoltre di fare il dump della memoria su di un le 9
1.2 Salto Figura 1.5: Draw (alla sinistra) e un clone di Galaxian (alla destra) ed è presente anche una modalità di debug che mostra in tempo reale il contenuto della RAM e dei registri. L'emulatore utilizza diversi le di ROM che carica all'avvio e richiede all'utente di specicare un'immagine del contenuto del disco. Questo conterrà quindi dati e applicazioni da poter eseguire. Sul Web sono presenti diversi le di questo tipo, ognuno contenente uno specico insieme di programmi, dai giochi no alle applicazioni da ucio. Sono presenti in tutto 48 le rappresentanti la ROM dello Xerox Alto e 9 le contenenti applicazioni. É possibile poi cambiare l'associazione tra i pulsanti della propria tastiera e quella della macchina emulata semplicemente scrivendo nel le keyboard.conf, oltre a poter metter in pausa l'emulatore in qualsiasi momento e far procedere l'esecuzione per passi. Dato che è possibile utilizzare come immagini del contenuto del disco anche le compressi, Salto utilizza internamente le librerie zlib (http://zlib.net) per la decompressione ove necessario. Durante la compilazione è possibile poi decidere se attivare la modalità di debug, la quale prevede molto più output testuale sulla console di sistema durante l'esecuzione, ed è anche possibile decidere se attivare o meno il pannello presente in alto nella nestra da dove si abilita o meno l'utilizzo del mouse e le funzioni di registrazioni video. Sullo stesso pannello sono presenti a destra due indicatori dell'utilizzo del disco. 10
1.3 Raspberry Pi 1.3 Raspberry Pi Figura 1.6: Raspberry Pi Il Raspberry Pi è un computer a basso costo delle dimensioni di una carta di credito che può utilizzare monitor dotati di ingresso HDMI o RCA. Utilizza mouse e tastiere USB standard ed è stato concepito per motivare persone di tutte le età ad esplorare il mondo della programmazione attraverso i linguaggi Python e Scratch. É capace di assolvere a tutte le principali funzioni richieste ad un PC desktop anche se le prestazioni sono comunque limitate. Proprio per come è stato concepito il Raspberry Pi viene molto utilizzato per progetti dove il rapporto costo-prestazioni è importante. É stato scelto come piattaforma di sviluppo proprio per le dimensioni, il costo basso e la essibilità nell'impiego. É un computer che è stato pensato per far sperimentare ed imparare le persone e questo è stato un motivo in più per utilizzarlo. A livello software sono presenti diverse distribuzioni Linux come Raspbian (Debian), Arch Linux, Pidora (Fedora) ed anche alcune in grado di trasformare il dispositivo in un player audio o addirittura un media center completo. É disponibile inoltre un porting ancora non uciale di Android. Sono presenti in rete diversi progetti che mostrano come possa essere usato nei modi più disparati, dal server BitTorrent alla stazione meteo no ad arrivare al cluster di Raspberry Pi per creare un (mini) supercomputer. 11
1.3 Raspberry Pi Le speciche tecniche sono di seguito riportate: Modello A Modello B SoC Broadcom BCM2835 (CPU + GPU + DSP + SDRAM) CPU 700 MHz ARM1176JZF-S core (famiglia ARM11) GPU Broadcom VideoCore IV, OpenGL ES 2.0, 1080p30 H.264 RAM 256 MB 512 MB Porte USB 1 2 Output Video RCA e HDMI Output Audio 3,5 mm jack e audio tramite HDMI Memoria Slot SD Rete (Ethernet) Nessuna Ethernet 10/100 (RJ-45) Periferiche SSPI, I C, UART, 2x13 pin di GPIO 2 RTC Non presente Potenza assorbita 300 mA, (1,5 W) 700 mA, (3,5 W) Alimentazione 5 V tramite MicroUSB oppure GPIO Dimesioni 85,60 mm × 53,98 mm Come è possibile notare guardando la tabella, a livello hardware sono presenti numerose periferiche utilizzabili. Un'altra cosa molto interessante è il fatto che la fase di boot è controllata dalla GPU la quale carica il rmware dalla scheda SD; in questo modo aggiornare il rmware non è un'operazione dicile o pericolosa in quanto basta sostituire pochi le sulla scheda. Tutte le prove sono state eettuate utilizzando un Raspberry Pi modello B in quanto dotato di due porte USB: una per il mouse e l'altra per la tastiera. Per l'alimentazione è stato usato un alimentatore dedicato a 1.25A mentre per la scheda è stata usata una microSD da 16Gb classe 6 con relativo adattatore. La macchina è stata collegata ad un Monitor TV tramite HDMI. Per eettuare le prove è stata prima installata Raspbian sulla scheda SD, in questo modo si è partiti da una congurazione funzionante. Una volta completata l'installazione la scheda è suddivisa in due partizioni FAT32: una di boot e l'altra contenente i rimanenti le di sistema. I le di avvio più importanti sono 4: bootloader.bin: il bootloader caricato nella GPU all'avvio. start.elf : il rmware proprietario del SoC Broadcom. kernel.img: il le contenente il kernel linux il quale viene caricato dal rmware all'avvio della macchina. cong.txt: viene usato per congurare il PC e permette tra l'altro di decidere la frequenza di lavoro della CPU, quella della RAM, la risoluzione del monitor e molte altre cose. Utilizzando questo le e le informazioni presenti in [6] è stato congurato il monitor in modo da visualizzare correttamente la nestra di Salto. 12
1.4 Strumenti Sfruttando il meccanismo del le di congurazione è possibile togliere della complessità al sistema operativo che non è obbligato nella fase di boot a congurare l'intera macchina. Per vericare il comportamento di piAlto basta sostituire al le kernel.img originale quello generato dal processo di compilazione. Un grosso svantaggio di questo metodo è che per sostituire il kernel occorre ogni volta estrarre dal Raspberry la scheda SD e reinserirla una volta nito; tutto questo può portare all'usura del lettore presente sul dispositivo. 1.4 Strumenti Prima di iniziare lo sviluppo eettivo è stato necessario stabilire quali strumenti software utilizzare. Invece di eettuare le compilazioni direttamente sul dispositivo nale si è deciso di utilizzare un notebook con installato Linux (Xubuntu 14.04) ed un cross-compilatore. In questo modo è stato molto più semplice e veloce eettuare le prove avendo a disposizione solamente un Raspberry Pi. Durante la prima fase dello sviluppo è stato usato un emulatore dell'hardware nale per velocizzare ancora di più lo sviluppo. Come cross-compilatore è stato scelto GCC perché è open source, ben integrato in Linux ed è quello più utilizzato dalla comunità di sviluppatori del Raspberry Pi. Salto utilizza le librerie standard C per cui era indispensabile ottenere anche solo un sottoinsieme di queste funzionanti per ARM bare metal. Per risolvere questo problema sono state prese in considerazione le librerie Newlib essendo queste open source e molto usate nel mondo embedded. Sono stati quindi individuati dei le di esempio per vericare il funzionamento di GCC, questi non utilizzano le librerie standard e sono scaricabili portandosi nella cartella di destinazione usando il terminale e digitando il comando: git clone https://github.com/brianwiddas/pi-baremetal.git. I sorgenti utilizzati congurano lo stack, la paginazione, gli interrupt ed il framebuer. Una volta preparato il tutto vengono eettuate a schermo delle stampe e viene vericato il corretto funzionamento degli interrupt. Questi le sono stati molto utili per capire il funzionamento ed il dialogo con la CPU e la GPU di sistema. Facendo delle ricerche sono state scaricate e provate due versioni della toolchain (l'insieme degli strumenti per la compilazione e l'assemblaggio dei sorgenti) precompilate per ARM bare metal e quindi con target arm-none-eabi . Entrambe sono state scartate in quanto il più delle volte riuscivano a compilare gli esempi in modo corretto ma quando venivano inserite chiamate alle librerie standard venivano restituiti errori per cui si è 13
1.4 Strumenti deciso di scaricare i sorgenti di GCC e Newlib per compilare e congurare tutto in modo corretto. Basandosi su commenti, guide presenti in rete e facendo prove con parametri di congurazione diversi si è poi giunti al risultato ed inne, allo scopo di rendere il tutto il più ripetibile, si è creato uno script che in automatico scarica tutti i componenti necessari, provvede alle corrette congurazioni e compila il tutto ottenendo una toolchain completa e funzionante. Partendo da una installazione di Xubuntu 14.04 prima di eseguire lo script è necessario installare i pacchetti Texinfo, m4 e g++. I componenti utilizzati per l'installazione sono Gnu Mpc 1.0.1, GMP 5.1.1, GCC 4.8.0, Binutils 2.23.2, Gnu Mpfr 3.1.2 e Newlib 1.2.0. Sullo stesso sistema Linux possono coesistere più toolchain, è suciente che la cartella di installazione sia dierente; a questo punto occorre modicare il le bash.rc nella home dell'utente inserendo il percorso della cartella contenente gli eseguibili del compilatore e degli altri strumenti che si vuole utilizzare. Una volta ottenuta la toolchain funzionante, allo scopo eettuare più velocemente le prove ed evitare di scrivere continuamente la scheda SD, sono state eettuate delle ricerche per ottenere un emulatore di Raspberry Pi funzionante. Per tutte le prove fatte in questo senso sono stati utilizzati i le di esempio compilati con GCC. La prima prova è stata fatta utilizzando QEMU, in particolare qemu-system-arm presente tra i repository del sistema ospite. Come risultato l'emulazione si bloccava in modo anomalo; questo è dovuto al fatto che, come scoperto successivamente, QEMU non supporta ancora direttamente il Raspberry Pi. Per la prova successiva sono stati scaricati direttamente i sorgenti dell'emulatore e si è cercato di compilarli per l'architettura desiderata; in questo caso è emerso in modo ancora più evidente come QEMU non supporti l'hardware scelto in quanto l'architettura disponibile che si avvicina di più a quella desiderata è VersatilePCB, molto generica e non soddisfacente. Utilizzando la versione compilata in questo modo infatti l'emulatore rimane inattivo e la nestra principale risulta completamente nera. Scartate queste due opzioni sono state fatte ricerche ed è stato individuato un progetto interessante raggiungibile all'indirizzo: https://github.com/Torlus/qemu/tree/rpi. Questo progetto consiste in una versione di QEMU modicata che aggiunge il supporto al Raspberry Pi; il tutto è ancora sperimentale e non fa parte del codice sorgente uciale. Anche se è ancora in fase di sviluppo gestisce correttamente il framebuer e le parti principali dell'ARM tuttavia non è ancora completo il supporto per il controller USB. Per questo motivo questo strumento è stato utilizzato solamente nella prima parte dello 14
1.4 Strumenti sviluppo di piAlto; quando è stato invece necessario implementare la parte relativa al mouse e alla tastiera è stato utilizzato il dispositivo sico. Per la gestione del codice sorgente del progetto è stato usato un repository SVN privato in rete. Anche se piAlto è stato sviluppato da una singola persona l'utilizzo di questo strumento permette di avere una sola copia di riferimento ed evita che esistano più versioni con date dierenti che potrebbero portare confusione. Un altro motivo per cui si è scelto di utilizzare Subversion è la possibilità di tenere la storia dello sviluppo e di poter ottenere le versioni precedenti in modo facile e ripetibile. 15
Capitolo 2 Sviluppo di piAlto In questo capitolo verrà prima introdotta l'architettura del SoC Broadcom nel dettaglio e successivamente verranno descritte in modo più completo le due parti principali di piAlto: il kernel e le modiche fatte a Salto per ottenere un eseguibile funzionante. Verrà inoltre descritta l'integrazione del progetto con le librerie Newlib e il driver USB. 2.1 Bare metal su Raspberry Pi Studiando i sorgenti di esempio precedentemente citati e utilizzando la documentazione online (wiki, esempi e manuale dell'ARM) è stato possibile capire il funzionamento generale del Raspberry Pi. A dierenza di altre architetture ore ben 7 modalità di funzionamento: User, Fast Interrupt, Interrupt, Supervisor, Undened, Abort, e System Mode. Tutte le modalità tranne quella User sono privilegiate, il che signica che è possibile eseguire operazioni per la gestione degli interrupt, controllare la paginazione e operare su registri tipicamente non accessibili all'utente. Ogni modalità ha un suo stack pointer che all'avvio non è impostato automaticamente. Al boot la CPU si trova in Supervisor Mode per cui nel caso sia necessario lavorare sempre senza sistema operativo residente basta rimanere in questa modalità ed impostare lo stack pointer per poter eettuare chiamate a funzione. Per la gestione degli interrupt l'ARMv4+ prevede la presenza di 7 vettori in memoria a dierenza dell'architettura x86 che ne prevede 256. Non è inoltre possibile utilizzare l'istruzione int N ma si deve usare il comando SWI (SofWare Interrupt) in quanto è presente un solo vettore che identica il gestore per l'interrupt software. A dierenza dell'architettura x86 dove ogni vettore aveva un livello di privilegio associato è stata introdotta l'idea di dierenziare le modalità di funzionamento della CPU; quando l'istruzione SWI viene eseguita l'ARM passa automaticamente in Supervisor Mode e, utilizzando il vettore di 16
2.1 Bare metal su Raspberry Pi indirizzi fornito dal kernel, viene individuato ed eseguito il gestore corretto. Le rimanenti modalità sono utilizzate per gli scopi più specici: System Mode: è una via di mezzo tra la modalità Supervisor e la User in quanto permette il livello di privilegio massimo nonostante condivida il set di registri della modalità User. User Mode: è l'unica modalità non privilegiata ed è stata concepita per permettere l'esecuzione di codice utente senza il rischio di compromettere l'integrità del sistema operativo. Abort Mode: è utilizzata nel caso ci sia un fallimento nel caricamento di un istruzione (Prefetch Abort) oppure di dati (Data Abort). In quest'ultimo caso si verica l'abort prima che l'istruzione alteri lo stato del sistema. Fast Interrupt Mode (FIQ Mode): è usata per gestire gli interrupt che necessitano di velocità elevate e ha un set di registri diverso dalla IRQ Mode. Gli interrupt gestiti tramite FIQ hanno priorità maggiore rispetto a quelli più lenti e possono interrompere questi ultimi se necessario. Interrupt Mode (IRQ Mode): l'altra modalità lecita per la gestione degli interrupt, in particolare tutti quelli che non richiedono velocità elevate usano questa modalità. Undened Mode: è usata per gestire l'esecuzione di istruzioni non denite. Come già accennato il boot del Raspberry Pi è gestito dalla GPU che carica il rmware e successivamente il le kernel.img all'indirizzo 0x8000. A questo punto la GPU passa il controllo dell'esecuzione al le appena caricato che deve preoccuparsi di portare il sistema ad uno stato consistente prima di poter eettuare altre operazioni. La prima operazione da fare all'avvio è impostare quindi lo stack pointer; dato che lo stack cresce in memoria verso il basso è comodo tenerlo al di sotto del punto in cui è stato caricato il kernel; subito dopo si può procedere alla congurazione della memoria, del framebuer e degli interrupt. Gestione della memoria La gestione della memoria a un livello così basso di astrazione richiede di tenere in considerazione sia gli indirizzi sici che quelli virtuali. Sfogliando il manuale di riferimento del SoC utilizzato si può vedere come i primi siano organizzati in due parti: la memoria di sistema e quella dedicata alle periferiche. La prima parte è denita dall'indirizzo 0 no a 256MB o 512MB di RAM a seconda del modello del Raspberry Pi utilizzato. La 17
2.1 Bare metal su Raspberry Pi memoria dedicata alle periferiche invece è posizionata al di sopra in un'area non sicamente utilizzabile dal sistema operativo. In questa zona è possibile accedere ai registri di controllo per le periferiche ed quindi comunicare con queste. Per esempio si possono usare i registri per impostare un timer, controllare i LED presenti sul PC o altro ancora. Durante la prima parte dello sviluppo si è pensato di utilizzare un prolo della memoria di tipo at dove l'indirizzo virtuale è uguale a quello sico e senza utilizzare la paginazione, per fare questo semplicemente non veniva abilitata l'MMU di sistema. L'idea di fondo era che senza l'utilizzo della paginazione si avrebbe avuto un guadagno di prestazioni dovuto al fatto che non era necessario sprecare accessi alla memoria per via delle traduzioni necessarie. Probabilmente questa convinzione avrebbe portato al risultato sperato su architettura x86 ma non lavorando con ARM. Su questa architettura infatti il funzionamento dell'MMU è un requisito fondamentale per l'abilitazione della cache di sistema. L'utilizzo della cache può incidere in modo pesante sulle prestazioni complessive della macchina in modo particolare quando si eettuano numerosi accessi in modo continuo a dati globali o statici per i quali non vale il meccanismo dello stack. Una soluzione semplice a questo problema è abilitare la paginazione e applicare l'identity paging ovvero fare in modo che l'indirizzo sico e quello virtuale siano lo stesso; in questo modo l'inizializzazione è semplice e trasparente alle altre parti del programma. La paginazione può essere ad un livello o a due, in base alla nezza che si vuole ottenere nella mappatura. Possono per esempio essere utilizzate pagine rappresentanti 4096 byte ma anche di 1MB (dette sezioni ). Possono essere impostate pagine che rappresentano addirittura 16MB di RAM e vengono dette super section. Si è scelto di utilizzare pagine di 1MB perché dovendo semplicemente fare identity paging non era necessario utilizzarne di dimensioni più piccole. Per abilitare l'MMU è quindi necessaria una sola tabella con 4096 voci, questo perché lo spazio di indirizzamento su architettura a 32 bit è 4 GB (una voce per ogni MB di memoria). La tabella deve essere allineata alla parola cioè ad un multiplo di 4 byte e sono presenti diversi registri appositamente predisposti per congurare la paginazione. Nel caso si utilizzino pagine di 1MB la voce corrispondente in tabella è di 4 byte di cui i 20 bit più signicativi identicano l'inizio dell'area di memoria da associare all'indirizzo virtuale. Gli altri bit identicano le proprietà denite per quella zona di RAM. I bit particolarmente importanti sono 10:11 (AP), 2 (B) e 3 (C); i primi servono per impostare i permessi di lettura/scrittura per la sezione mentre i bit C e B indicano se per la pagina deve essere abilitata la cache o se questa debba essere messa in un buer. I bit di AP sono stati impostati a 1 entrambi per tutte le pagine per poterle sia leggere che scrivere. 18
2.1 Bare metal su Raspberry Pi La procedura di inizializzazione consiste quindi nel costruire la tabella di 4096 voci impostando le proprietà per tutte le sezioni. Si può decidere quindi la politica di caching per la sezione utilizzando i bit C e B oltre a 12:14 (TEX). Per esempio impostando TEX = b000 e CB = b10 la pagina è trattata come memoria normale con cache abilitata mentre impostando TEX = b000 e CB = b01 la zona di memoria associata viene considerata dedicata a periferiche condivise per la quale è quindi disabilitato il caching. Sfruttando questo meccanismo è quindi possibile ottenere aree di RAM disponibili per eventuali buer utili alla comunicazione con le periferiche (come il controller USB) le quali, anché il trasferimento di informazioni vada a buon ne, necessitano appunto che la cache non sia usata. Subito dopo aver creato e riempito la tabella vengono modicati due registri di controllo (System Control Register e Auxiliary Control Register ) per assicurare che i campi TEX e AP siano strutturati secondo quanto detto nora. Occorre poi impostare un registro di controllo che contiene l'indirizzo della tabella e invalidare il contenuto della cache perchè non possa essere utilizzata in seguito. Viene inne scritto nuovamente il System Control Register per abilitare la data cache, l'instruction cache e il program ow prediction oltre all'MMU stessa. Una volta terminata la procedura di inizializzazione sono quindi abilitate tutte le ottimizzazioni principali disponibili ed il tutto risulta trasparente alle altre parti del programma. Il framebuer Il framebuer può essere visto semplicemente come una matrice di pixel le cui dimensioni variano in base alla risoluzione selezionata, ogni pixel occupa una quantità di memoria che dipende dal bpp (bit per pixel ). Nel caso si utilizzino 32 bpp si ha che ogni pixel occupa una zona di memoria di 4 byte; per colorare il pixel sullo schermo basta scrivere il valore associato al colore utilizzando un opportuna codica dipendente dal bpp. Per congurare il framebuer occorre comunicare con la GPU tramite il meccanismo delle mailbox ([7]) il quale permette una comunicazione di tipo request-response tra il programma e il VideoCore (GPU di sistema). Utilizzando questa interfaccia è possibile non solo impostare il framebuer ma anche ottenere informazioni importanti come dimensione della memoria RAM usabile per il sistema o dedicata alla GPU, controllare l'overclocking e molto altro. La comunicazione avviene fornendo un buer che contiene le richieste (ognuna identicata da un tag) e gli argomenti specici per quella richiesta; è quindi possibile, tramite questo meccanismo, eettuare request multiple con un unico trasferimento di informazioni. É implementato il meccanismo dei canali: ne esistono 16 ed ognuno deve essere utilizzato per realizzare un tipo di comunicazione. In particolare i 19
2.1 Bare metal su Raspberry Pi canali 8 e 9 vengono usati rispettivamente per la comunicazione tra ARM e VideoCore e viceversa. Lo scambio di dati deve avvenire rispettando le seguenti regole: La risposta sovrascrive la richiesta. Il buer usato per la comunicazione deve essere allineato a 16 byte in quanto solo i 28 bit più signicativi sono trasmessi mentre i rimanenti 4 bit identicano il canale. I tag sconosciuti vengono ignorati. La risposta può contenere tag non richiesti. La lunghezza del tag di risposta può essere più lunga di quanto ci si aspetti in quanto informazioni aggiuntive potranno essere fornite in futuro. Tutti i valori senza segno a 16/32/64 bit sono ordinati in base all'architettura ospite (little o big endian). I tag devono essere processati in ordine ad eccezione del caso in cui l'interfaccia richieda tag multipli per una singola operazione. Per iniziare la comunicazione occorre quindi allocare il buer e formattare la richiesta al suo interno tenendo conto delle regole appena citate. Di seguito verrà presentato come il contenuto del buer deve essere organizzato, indicando con il presso u e s un intero rispettivamente senza segno e con segno. u32: dimensione del buer in byte includendo header, tag nale e padding u32: codice della richiesta/risposta Richiesta 0x00000000: richiesta da processare Tutti gli altri valori sono riservati Risposta 0x80000000: richiesta completata con successo 0x80000001: errore nel parsing della richiesta Tutti gli altri valori sono riservati u8: sequenza di tag concatenati 20
2.1 Bare metal su Raspberry Pi ... u32: 0 (tag nale) u8: padding ... Ogni tag è invece organizzato internamente in questo modo: u32: identicatore del tag u32: dimensione del buer del value buer in byte u32: 1 bit (MSB) che indica una richiesta (0) oppure una risposta (1) e i rimanenti 31 bit rappresentano la lunghezza del valore u8: sequenza di byte che rappresentano il value buer ... Il value buer deve essere allineato a 32 bit per cui viene aggiunto del padding alla ne quando necessario. Gestione degli interrupt All'avvio gli interrupt sono disabilitati e prima di poterli abilitare occorre fornire gli opportuni gestori. Questi non sono altro che funzioni le quali vengono chiamate quando l'interruzione è intercettata. Come già detto esistono 7 tipi di interrupt e sono: Reset, Data Abort, FIQ, IRQ, Prefetch Abort, SWI, Undened Istruction. Il primo avviene nel caso di reset del core ARM; Data Abort e Prefetch Abort servono per gestire le analoghe situazioni precedentemente descritte mentre FIQ e IRQ gestiscono gli interrupt hardware veloci e non rispettivamente. Inne SWI viene utilizzato per gestire le interruzioni software e Undened Instruction, come suggerisce il nome, tratta l'esecuzione di istruzioni non riconosciute. Per poter impostare i gestori richiesti esiste un apposito registro ed occorre utilizzare il coprocessore CP15 il quale si occupa di controllare e fornire informazioni di status per le funzioni del processore ARM. Il coprocessore dispone di diversi registri, in particolare il registro Vector Base Address Register permette di impostare l'indirizzo del vettore contenente gli indirizzi dei gestori. É possibile usare un'unica istruzione assembly per eettuare quanto detto: asm volatile ( " mcr p15 , 0 , %[ addr ] , c12 , c0 , 0 " : : [ addr ] " r " (& interrupt_vectors ) ) ; dove addr è l'indirizzo del vettore. Per abilitare gli interrupt invece basta utilizzare l'istruzione apposita cpsid: asm volatile ( " cpsid i " ) ; 21
2.2 Il kernel 2.2 Il kernel Il piccolo kernel sviluppato per supportare Salto è composto da solamente da 12 le: 1 le assembly e 10 le C, oltre ad un ulteriore le C che permette l'integrazione con le librerie Newlib. Sono stati creati poi un linker script e un Makele ; il primo è necessario per indicare al linker come deve essere assemblato l'eseguibile mentre il secondo è utile per rendere automatica e veloce la compilazione del progetto. Saranno in seguito descritti in dettaglio tutti questi le allo scopo di spiegarne lo scopo e come questi interagiscano. linkscript Un eseguibile è composto tipicamente da varie parti, in particolare le più comuni sono data, bss, rodata e text. La sezione data contiene tutti i dati inizializzati, rodata i dati costanti, text il codice eseguibile e bss tutti i dati non inizializzati. Questi ultimi sono tipicamente tutti i dati globali o statici che, in caso sia presente un sistema ospite, vengono sempre inizializzati in automatico a 0. Nel caso si programmi senza sistema operativo residente questo non avviene e deve essere fatto manualmente. Questo le serve per comunicare al linker come devono essere disposte le varie parti dell'eseguibile in memoria, in particolare kernel.elf (con cui vine creato kernel.img) deve essere caricato all'indirizzo 0x8000. Le sezioni vengono caricate in ordine ed allineate a 4096 byte (dimensione tipica di una pagina) e l'ordine scelto è text, data, rodata e bss. Nel le vengono dichiarati dei simboli preceduti da `_', questi sono visibili dai le sorgente e sono utili per ricavare gli indirizzi di inizio e ne delle varie sezioni. Per esempio se si vuole stabilire dove la memoria utilizzata nisce basta leggere il valore di _bssend che indica appunto la ne della sezione bss cioè l'ultima presente in RAM. Makele Per eettuare la compilazione e l'assemblaggio dell'eseguibile si è sin dall'inizio utilizzato il comando make ed un makele. Questo però era molto semplice ed ogni volta che veniva usato tutti i le oggetto venivano cancellati ed il progetto era ricompilato completamente. Con il crescere delle dimensioni del codice sorgente è diventato necessario riscrivere questo le da zero in modo da ottenere che solamente i le modicati vengano ricompilati accorciando i tempi necessari per lo sviluppo ed i test. 22
2.2 Il kernel start.s Questo le è l'unico scritto completamente in assembly e si occupa solamente di impostare lo stack pointer e abilitare la FPU per poi passare il controllo dell'esecuzione alle funzioni di inizializzazione scritte in C. Viene denito il simbolo globale _start che è utilizzato dal linker in fase di assemblaggio dell'eseguibile, in questo modo si indica che quel punto è l'inizio della sezione contenente il codice eseguibile e le istruzioni successive dovranno essere le prime ad essere eseguite una volta avviato il dispositivo. É stato necessario abilitare la FPU in quanto sono stati riscontrati problemi con la funzione printf(), questa al suo interno fa uso di tipi oating point per cui se la FPU è disabilitata e si cerca di utilizzare la funzione quello che si ottiene è il blocco del programma. libgcc.a Il SoC Broadcom utilizzato ha la particolarità di non implementare le operazioni di divisione e modulo tra interi in hardware, queste operazioni devono essere riprodotte tramite software. GGC fornisce la libreria di basso livello libgcc.a per alcune piattaforme e genera le chiamate alle funzioni in questa libreria durante la compilazione per sostituire quelle operazioni troppo complicate da implementare con funzioni inline. Oltre ad implementare divisione e modulo tra interi per alcune piattaforme vengono fornite tutte le operazioni tra oating point e xed point oltre a routine per la gestione delle eccezioni e molto altro. La maggior parte delle funzionalità sono implementate in codice C indipendente dalla macchina mentre una parte deve essere scritta in assembly quando l'architettura di destinazione lo richiede. divby0.c Il le contiene un'unica funzione chiamata raise() la quale viene chiamata ogni volta che viene eettuata una divisione per 0. Quello che si è scelto di fare per gestire questa situazione è semplicemente segnalare il problema tramite delle stampe opportune e bloccare la macchina tramite un ciclo. initsys.c Questo sorgente C contiene le prime due funzioni di inizializzazione del dispositivo: la prima, chiamata get_info(), utilizza il meccanismo delle mailbox per ottenere informazioni come la quantità di RAM destinata alla CPU e quella invece riservata al VideoCore. Queste informazioni sono utili per poter impostare la quantità massima di heap utilizzabile dal sistema. La parte di RAM compresa tra la ne della sezione bss dell'eseguibile e la 23
2.2 Il kernel ne della RAM riservata alla CPU ARM è infatti utilizzabile come stack oppure come heap e, dato che si è deciso di sistemare lo stack al di sotto del sistema in memoria, questa zona si può considerare totalmente utilizzabile per allocazione dinamica. L'altra funzione di inizializzazione è initsys() la quale viene chiamata alla ne del le start.s ; questa semplicemente si occupa di preservare gli argomenti passati dalla CPU nei registri r0, r1, r2 (utilizzati in seguito), inizializzare la sezione bss con 0 utilizzando i simboli preparati dal linker e chiamare get_info(). In seguito il controllo viene dato ad un'altra funzione di nome kmain(). led.c Contiene alcune funzioni per l'inizializzazione ed il controllo dei LED presenti su Raspberry Pi. Questo le originariamente faceva parte degli esempi C di partenza e non è stato modicato. Anche se non è utilizzato in piAlto è stato mantenuto nel caso in futuro sia necessario l'output tramite i LED presenti sul dispositivo. atags.c La CPU ARM all'avvio prepara una serie di informazioni in memoria per il sistema operativo se queste vengono richieste (per disabilitare questa funzionalità occorre modicare il le cong.txt ). Queste informazioni sono organizzate tramite tag e sono dette ATAGS (ARM TAGS). Il le atags.c contiene una serie di funzioni utilizzate allo scopo di stampare le informazioni a video; questo le faceva parte degli esempi di partenza e è stato mantenuto per permettere in caso di bisogno di ricavare le informazioni necessarie. Tramite queste funzioni è infatti possibile capire come sono strutturati gli ATAGS in memoria e come accedervi. textutils.c Contiene solamente due funzioni: tohex() e todec(). La prima converte un intero senza segno in una stringa che è la sua rappresentazione in esadecimale mentre la seconda eettua la stessa cosa con la dierenza che la stringa generata è la rappresentazione in decimale dell'intero. Queste due funzioni venivano utilizzate negli esempi per eettuare stampe di interi e sono state quasi ovunque sostituite tramite printf(). Vengono ancora usate solamente quando non è possibile utilizzare le funzioni delle librerie standard ovvero nelle system call. interrupts.c In questo le sono deniti i gestori per i 7 tipi di interrupt e una piccola funzione di inizializzazione. In particolare esistono i gestori per gli interrupt 24
2.2 Il kernel di Abort (Data e Prefetch) che visualizzano stampe di debug e non ripristinano la macchina. É presente un gestore per gli IRQ ma, non essedo questo utile per piAlto, non fa nulla di rilevante e uno simile per gli interrupt software (SWI). Tutti gli altri tipi di interrupt vengono gestiti come situazione anomala da un'unica funzione che, in caso venga chiamata, blocca il Raspberry Pi. mailbox.c Contiene due funzioni per leggere e scrivere le mailbox usando dei registri appositi mappati in RAM. Normalmente tali registri hanno indirizzi 0x2000B880, 0x2000B898 e 0x2000B8A0. Il primo viene utilizzato per leggere la mailbox, il secondo per scriverla ed il terzo per vericarne lo stato. É importante sottolineare che se viene abilitata la cache di sistema utilizzando questi registri possono sorgere problemi. Lo spazio di indirizzamento del Raspberry è stato diviso in 4 parti e in ogni zona è mappata la stessa memoria, cambia solo la modalità di accesso. É possibile quindi accedere agli stessi registri senza che la cache venga utilizzata semplicemente usando indirizzi diversi (utilizzando quindi i registri 0xE000B880, 0xE000B898 e 0xE000B8A0). framebuer.c Prima di poter visualizzare oggetti e testo sullo schermo occorre impostare la risoluzione e la nestra di lavoro. Tutto questo è gestito da due funzioni: fb_init() e fb_setup(). La prima si occupa di impostare la risoluzione ottimale che, in caso l'esecuzione sia eettuata per mezzo dell'emulatore è di 640x480 (il massimo possibile su QEMU al momento) mentre normalmente viene scelta 800x600 in quanto, abilitando la rotazione dello schermo a 90 gradi, è molto simile a quella originale dello Xerox Alto (606x808). Una volta impostata la risoluzione tramite mailbox si memorizzano tutte le informazioni relative allo schermo (pitch, bpp, indirizzo sico e dimensione del framebuer). La funzione fb_setup() si occupa di restringere lo spazio disponibile sullo schermo a dimensioni pressate in modo che siano gestiti correttamente i casi in cui la risoluzione dello Xerox Alto sia diversa da quella oerta dal monitor usato. Per esempio se si decide di usare un monitor con una risoluzione di 1024x768 viene creata una nestra di dimensioni 606x768 centrata in larghezza e solo all'interno di questa nestra sarà possibile la visualizzazione. Tutto il software di gestione del video è stato pensato in questo modo per cui usando qualsiasi delle funzioni grache presenti il comportamento deve essere coerente a quanto detto. Si è pensato di implementare anche la rotazione dello schermo tramite software; è possibile abilitarla prima della compilazione agendo su di un 25
2.2 Il kernel ag globale. Si è scoperto in seguito che questa funzionalità è implementata anche in hardware dal Raspberry Pi ed è attivabile modicando il le cong.txt ma, nonostante questo, si è deciso di lasciare il codice di gestione della rotazione per completezza. Una volta completata la congurazione dello schermo la nestra viene riempita con il colore di sfondo di default (bianco). graphics.c Il servizio principale che il kernel deve fornire a Salto oltre a inizializzare correttamente il Raspberry Pi è l'output a video. Sono state sviluppate diverse funzioni per implementare le operazioni più basilari anche se molte di queste non sono direttamente utilizzate da Salto ma sono state inserite per permettere la stampa di testo da parte del kernel o per rendere il lavoro più completo. La funzione di base è quella per colorare un pixel, usando questa è stata implementata la stampa di un carattere. Sfruttando quest'ultima sono state implementate due funzioni per la stampa di testo, una permette la visualizzazione in un punto qualsiasi della nestra mentre l'altra implementa il comportamento classico di una shell di sistema per cui è gestito il newline, lo scroll e la posizione del testo è calcolata in modo automatico. Sono state inoltre implementate diverse utilità per cambiare il colore di sfondo o del carattere, per riempire la nestra di un colore o per impostare la console in modalità debug. Questa è abilitata prima dell'avvio di Salto e utilizza solo l'ultima riga disponibile nella nestra per la stampa di informazioni di debug durante l'esecuzione dell'emulatore. In questo modo è possibile usare Salto e visualizzare anche informazioni utili che normalmente sarebbero mostrate sulla shell dal quale l'eseguibile è stato lanciato. La funzione maggiormente utilizzata da Salto durante l'esecuzione è fb_write(), la quale è implementata usando put_pixel() e non fa altro che visualizzare un array di 16 pixel contigui sulla nestra. Il fatto che molte funzioni siano state scritte usandone altre più semplici rende il tutto molto più essibile. main.c Questo è il le principale del kernel, al suo interno sono presenti 4 funzioni: print_info(), MMU_init(), test(), kmain(). Prima di lanciare Salto deve avvenire l'ultima parte di inizializzazione del Raspberry Pi. Questa è implementata da kmain() che si occupa di inizializzare interrupt e framebuer tramite le funzioni descritte precedentemente. Viene inoltre inizializzato il driver USB, abilitata e congurata la MMU (usando la funzione MMU_init()) e vengono stampate informazioni utili all'avvio. La routine kmain() utilizza i registri r0, r1 e r2 ; questi sono stati preservati 26
Puoi anche leggere