a cura di Francesco Sileno
di Francesco Sileno
Introduzione
Salve smanettoni! Quest'oggi facciamo un viaggio nell'oscuro paese dei
Chipset, là dove il BIOS non conta e i Wait State vengono sterminati
a colpi di clock!
Però non intendo spiegarvi cosa significano tutte quelle buffe voci che
appaiono nel setup della vostra macchina, a far questo ci sta pensando
Fernando.
Ma come, sapete di cosa parlo? Bene, però forse non sapete che probabilmente numerose alternative, anche non indifferenti, vi sono state negate! Eh no, se non ci sono, non c'è mezzo di farle apparire magicamente... almeno finchè rimaniamo nel BIOS SETUP.
Il BIOS setup, in effetti, ha solo lo scopo di memorizzare le vostre scelte in una memoria tampone, la CMOS RAM, e si assume il compito di trasmetterle, in fase di POST, al Chipset presente sulla vostra Motherboard (d'ora in poi MB). Il chipset dispone a sua volta di un area di memoria, solitamente 256 byte, dove vengono copiate e diventano effettive. L'accesso a questa memoria non è diretto, ma avviene tramite un paio di registri di I/O. Se immaginiamo la memoria del chipset in questo modo
00 01 02 FD FE FF +----+----+----+---- ---+----+----+----+ | ?? | ?? | ?? | ... | ?? | ?? | ?? | +----+----+----+---- ---+----+----+----+allora, per manipolare il contenuto di un determinato byte (anche chiamato registro), si deve specificarne l'indice (indice del registro o offset). Per questo esistono nel chipset 2 porte di I/ O, una per specificare l'offset, l'altra per leggere/scrivere il registro. Ad ogni bit, o gruppo di bit, corrisponde poi un ben determinato significato (ROM Shadowing, Video Shadowing, DRAM Speed, etc...) Ovviamente esistono le solite invenzioni creative della IBM e INTEL che funzionano in un modo tutto loro...
Il bello sta nel fatto che il chipset è sempre accessibile, anche dopo che il BIOS lo ha programmato; i numerosi smanettoni possono modificare praticamente tutto quello che è modificabile sul loro PC. Il più famoso programma creato per questi loschi intrighi è sicuramente CtChipz, che può lavorare in interattivo o tramite macro. Ma... c'è un ma: di chipset ne esistono svariate marche e modelli, e i registri non sono mappati secondo uno standard. Perciò per ogni tipo di chipset si deve avere la documentazione (sheet) indicante come accedere all'area di memoria del chipset, quali registri sono disponibili e che significato hanno.
CtChipz fornisce il supporto per una certa gamma di prodotti, ma se il vostro non è presente, non potete fare nulla. Questa purtroppo è una limitazione pesante, anche per il Chip-O-Matic, almeno finchè le case produttrici non si decidono a rilasciare la tanto agognata documentazione.
Come fare
Ho già accennato come si accede ad un registro del chipset, facciamo ora qualche esempio pratico. Chiamiamo INDEX PORT la porta di I/O dove immettere l'offset del registro, DATA PORT quella in cui leggere/scrivere il valore del registro.
Ricordo che nei bus ISA/VLB ogni registro ha dimensione di un byte, mentre in un Chipset PCI può anche avere dimensione di WORD o DOUBLE WORD.
Da un estratto dal file sis471.cfg del CtChipz:
;******************************************************************** NAME=SiS471 ;SiS85C471 Single Chip in Datei SiS471.CFG ;******************************************************************** INDEXPORT=22h DATENPORT=23h [...] ;******************************************************************** INDEX=50h; Memory Configuration ;******************************************************************** BIT=7 ; Cache-Read Leading Cycle (if Reg5A Bit 7=0) 0 = 3 Takte 1 = 2 Takte BIT=76 ;DRAM Speed 00=Slowest (50MHz 4-3-4, CAS Puls= 2T) 01=Slower (40MHz 4-3-3 CAS Puls= 1T) 10=Faster (33MHz 3-2-2 CAS Puls= 2T) 11=Fastest (25MHz 3-2-2 CAS Puls= 1T) BIT=5 ;DRAM Write CAS Pulse Width 0=2T 1=1T BIT=4 ;L1-Write Strategy 0 = WT 1 = WB BIT=3 ;L2-Write Strategy 0 = WT 1 = WB BIT=2 ; PIN 138 Mulitiplex Output Control 0 = PIN 138 = RAS ( > 2 RAM Banks) 1 = PIN 138 = MA11 ( 16 M SIMM DRAMs) BIT=1 ;0/1 L1-Burst Write (only if Bit4 is set) BIT=0 ;Reset (INIT active enable) 0 = Normal Reset 1 = Warm ResetQundi ora sappiamo che la INDEX PORT è 22h, la DATA PORT è 23h, e sappiamo il significato dei vari bit del registro con offset 50h.
OUT 22h,50h
IN AL,23h
Supponiamo che dopo questa operazione il contenuto di AL sia 0. Quindi ne risulta che la velocità della DRAM è al minimo, che entrambe le cache sono in write trough, che il reset è un normale reset da tastiera, etc...
Noi però vogliamo mettere la cache L2 in write back, e aumentare la velocità delle DRAM a fastest. Quindi il nuovo valore del registro deve essere 0110-1000b = 68h.
OUT 22h,50h
OUT 23h,68h
E il gioco è fatto. In realtà non è così semplice, perchè quando si modificano le impostazioni della cache è necessario effettuarne un flush, altrimenti il suo contenuto viene reso inutilizzabile. Similmente, non è detto che la MB sia in grado di gestire correttamente un accesso troppo rapido alle DRAM, con lo stesso risultato.
In genere si devono fare delle prove fino a ottenere i migliori valori che il PC riesce a reggere.
PCI Bus
Quanto sopra vale per le MB ISA/VLB, nei bus PCI il discorso si complica un poco. In questo bus il chipset viene visto come un device PCI, esattamente come una qualsiasi altra scheda PCI, e il metodo di accesso cambia, pur usando la stessa logica. Fortunatamente per i pigri come me, viene in aiuto il PCI BIOS, che grazie all'Interrupt 1Ah, permette di accedere ad un qualsiasi registro, in lettura e scrittura, specificando il device da usare. Purtroppo ho da poco scoperto che il numero di device del chipset PCI cambia da MB a MB, quindi prima di procedere si deve sapere quale sia quello corrispondente sulla propria. Lo potete fare usando CtPci, un utility che mostra informazioni su tutte le periferiche PCI connesse, o rimediando la documentazione.
Da un estratto della Ralph Brown Interrupt List:
INT 1A - Intel PCI BIOS v2.0c - READ CONFIGURATION BYTE AX = B108h BL = device/function number (bits 7-3 device, bits 2-0 function) BH = bus number DI = register number (0000h-00FFh) Return: CF clear if successful CL = byte read CF set on error AH = status (00h,87h) (see #0548) EAX, EBX, ECX, and EDX may be modified all other flags (except IF) may be modified Notes: this function may require up to 1024 byte of stack; it will not enable interrupts if they were disabled before making the call the meanings of BL and BH on entry were exchanged between the initial drafts of the specification and final implementation [...] INT 1A - Intel PCI BIOS v2.0c - WRITE CONFIGURATION BYTE AX = B10Bh BH = bus number BL = device/function number (bits 7-3 device, bits 2-0 function) DI = register number (0000h-00FFh) CL = byte to write Return: CF clear if successful CF set on error AH = status (00h,87h) (see #0548) EAX, EBX, ECX, and EDX may be modified all other flags (except IF) may be modified Notes: this function may require up to 1024 byte of stack; it will not enable interrupts if they were disabled before making the call the meanings of BL and BH on entry were exchanged between the initial drafts of the specification and final implementationSe abbiamo determinato che il chipset come numero di device 5, e sapendo empiricamente che il numero di bus e di funzione sono 0 (se non vi risulta, fatemelo sapere!), per leggere il byte situato all'offset 34h, sarà:
MOV AX,0B108h MOV BX,28h ; 0000-0000 - 0010-1000 MOV DI,34h INT 1Ahe se non ci sono errori, avremo in CL il valore letto. Per scrivere invece:
MOV AX,0B10Bh MOV BX,28h ; 0000-0000 - 0010-1000 MOV DI,34h MOV CL,XX ; XX è il valore da immettere. INT 1AhA differenza dei chipset ISA/VLB, quelli PCI possono avere registri di dimensione pari ad una word o ad una double word, per i quali esistono appositi servizi dell'INT 1Ah.
Ancora di più
Nonostante CtChipz sia quindi uno strumento impareggiabile per gli smenettoni, ha delle pecche insite. Poichè la memoria del chipset è una miscela di RAM e ROM (infatti alcuni registri sono read only!) ad ogni spegnimento di macchina le vostre impostazioni sono perse. Questo vi obbliga ad immettere il CtChipz nell'AUTOEXEC.BAT, e poichè CtChipz funziona solo sotto DOS, ne consegue che in ogni altro SO non potete usufruire dei suoi vantaggi.
Quindi il problema è di fare in modo che le impostazioni vengano fatte prima ancora di caricare un qualiasi S.O... ma come è possibile fare un programma senza avere un S.O.? Ovvio, lo si fa in assembly. E come si fa a farlo partire prima di ogni altra cosa? Beh, se avete letto l'articolo "BOOTIAMO?" potete già rispondere. Ricorderete infatti che, subito dopo aver eseguito il POST, la macchina legge il primo settore dell'HD e lo manda in esecuzione. Questo settore contiene la tavola delle partizioni, e il codice necessario a stabilire da quale partizione effettuare il boot. Ma nessuno ci vieta di sostituire questo codice con un altro. Ed è nato così Chip-O-Matic.
Una prima idea potrebbe essere quella di fare un nuovo MBS che incorpori la capacità di modificare il chipset oltre alle solite funzioni, però dobbiamo pur rimanere nello spazio di 512 bytes (un settore), che si riduce a 446 byte in quanto la tavola delle partizioni non la possiamo eliminare. In questo modo rimarrebbe poco spazio per i dati.
L'ipotesi successiva prende in considerazione il fatto che la traccia 0, MBS a parte, rimane del tutto inutlizzata. Allora perchè non copiare il MBS originale su un altro settore, e poi installare al suo posto una routine che faccia le modifiche e richiami il vecchio MBS?
Questa è la situazione iniziale del vostro HD nel caso in cui non abbiate installato antivirus o programmi particolari:
Settore 1 Settore 2 Settore 3 +--------------+--------------+--------------+---- | MBS | Free | Free | ... +--------------+--------------+--------------+----E questo è quella che si ha dopo aver attuato la seconda ipotesi:
Settore 1 Settore 2 Settore 3 +--------------+--------------+--------------+---- | Chipset Code | Old MBS | Free | ... +--------------+--------------+--------------+---- | ^ +---------------+Dove per 'Chipset Code' si intende il codice atto a riprogrammare il chipset (non l'avreste mai detto, eh?).
Per i più smaliziati, faccio notare che il primo settore viene comunque caricato in memoria all'indirizzo 0000:7C00, il MBS originale invece lo potremmo caricare dove vogliamo noi. Invece non possiamo. Già, perchè tutti gli offset e le inizializzazioni di segmento sono state fatte in considerazione del fatto che normalmente questi viene caricato a un indirizzo prefissato. E non possiamo nemmeno metterlo lì, altrimenti sovrascriviamo il nostro codice. Sembrerebbe un bel problema... ma non dimentichiamo che abbiamo tutta la memoria a nostra disposizione! Possiamo tranquillamente spostare il codice del chipset altrove, per liberare così lo spazio al MBS. Se vi ricordate, è la stessa cosa che deve fare il MBS, in quanto anche il Boot Record della partizione attiva viene caricato in 0000:7C00.
Piccola Divagazione
Potrebbe sembrare strano il fatto che ogni cosa debba per forza essere
caricata sopra la precedente, mi sono costruito la mia teoria in proposito:
i primi PC avevano solo i floppy, e con la comparsa dei primi HD
probabilmente il concetto di partizione non esisteva neanche in sogno.
Quindi, c'era un solo settore da caricare, il boot sector, e tutti erano
felici.
Poi si sono accorti che la FAT più di 32Mb non reggeva, e hanno trovato la geniale idea dei mega cluster da 2^N byte. Qualcuno notò che in effetti si sprecacchiava un pò di spazio, e per farlo stare zitto pensarono di spezzettare l'HD in tanti HD logici di dimensioni tali da contenere il fenomeno cluster (eliminare la FAT? SCHERZATE? E poi la compatibilità col CP/M?).
Ora su un HD fisico vi erano più HD logici, però si poteva fare il boot solo dal primo logico, in quanto è da lì che il primo settore viene automaticamente caricato... e poi, dove mettere le informazioni su ogni partizione: grandezza, inizio, etc?
Crearono quindi il Master Boot Sector, (introducendo la limitazione a solo 4 partizioni, che poi verrà risolta con l'introduzione di partizioni primarie, estese e unità logiche, per la vostra semplicità), e l'hanno sostituito al vecchio boot record.
Più tardi, quando resettarono, si accorsero che il vecchio boot record doveva per forza venir caricato sopra il MBS, in quanto tutti gli offset e segmenti erano ormai stati impostati con i vecchi riferimenti. Eggià, pensate che ancora oggi nel MBS la prima cosa che viene fatta è qualcosa di simile a:
mov ax,0 push ax pop es push ax pop ds push ax pop ssin modo che tutti i segmenti siano PER FORZA a 0. Purtroppo, cambiare la prima istruzione con un
mov ax,csavrebbe portato ad un degrado prestazionale troppo elevato, e per di più si avrebbe avuto il vantaggio di poter caricare questo MBS virtualmente dove ci pare... ma era il periodo in cui Bill Gates affermava, con incredibile preveggenza, che 640Kb sarebbero stati più che sufficienti per tutti...
E noi, nell'anno 1995, abbiamo la confortante certezza che il MBS viene caricato nella stessa locazione di memoria in tutti i PC di questo mondo, esattamente come faceva nel 1975 il primo PC.
P.S.: non credo troverete conferma in un libro di storia, nè nella biblioteca della Microsoft(TM).
Let's do it
Adesso diamo un occhiata ad un possibile programma assembler da impiantare al posto del MBS, presentato in sorgente TASM-like:
; Inizializzazione segmenti. Non mi chiamo Billy, perciò in ax ci metto il ; CS attuale, quale che sia (per ora sicuramente 0). E a differenza di ; Billy, lo stack lo posiziono leggermente prima del codice, tanto per non ; far venire la claustrofobia. cli mov ax,cs mov ss,ax mov sp,7A00h push ax pop ds push ax pop es sti cld ; In questa sezione vengono messe le istruzioni necessarie ad impostare i ; valori desiderati sul proprio chipset. ; Eh, mica vi aspettavate il sorgente del Chip-O-Matic? ; Chipset Setup out 22h,50h out 23h,68h [ ... ] ; Quindi si provvede a spostare il codice attuale in un altra locazione, per ; poter caricare il vecchio MBS. Notate: la nuova locazione è in questo caso ; 0:600h, però noi non dobbiamo saltare a 600h, ma al corrispettivo traslato ; dell'etichetta /start_here/; altrimenti ricominciamo tutto da capo ! mov si,7c00h mov di,600h mov cx,100h repnz movsw db 0EAh ;jmp 0000:0xxx dw (offset start_here)-7c00h+600h dw 0000h ; Adesso che si siamo spostati, possiamo caricare il vecchio MBS. Fate ; attenzione ai parametri: BX e CX riguardano direttamente la procedura ; usata per leggere il settore, SI è usato in caso di errore direttamente da ; quest'ultima. Più avanti spiego il significato di questi parametri, per ; ora annotateli. start_here: mov bx,7c00h mov si,offset errore_MBS mov cx,3 call leggi_settore ; La procedure leggi_settore inchioda la macchina se trova un errore, ; altrimenti l'esecuzione prosegue e ci troviamo qui sotto. Provvediamo a ; copiare la nuova tavola delle partizioni (contenuta nel settore ora ; spostato in 0:600h), sul vecchio MBS appena caricato (in 0:7C00h). ; All'interno del MBS, la tavola comincia all'offset relativo 1BEh, da ; sommarsi rispettivamente a 600h e 7C00h, ed ha dimensione di 64 bytes. mov si,7BEh mov di,7DBEh mov cx,40h repnz movsb ; copy new partition table. ; Adesso c'è un trucchetto sporco. Possiamo passare l'esecuzione al MBS, ; però il mio compilatore assembler non accetta l'istruzione JMP 0000:7C00, ; o forse sono io un po' tonto. Rimediamo inserendo direttamente il codice ; operativo dell'istruzione in questione: EA7C000000 (EAh = JMP FAR, 7C00h = ; offset, 0000h = segmento). db 0EAh dw 7C00h dw 0000h ; jmp 0000:7C00 ; Da qui in poi ci sono le procedure e etichette usate dal "main": ; Questo è un loop eterno. Si salta qui dopo la visualizzazione di un ; messaggio di errore, per obbligare a resettare. eternal_loop: jmp eternal_loop ; Facciamo uso della funzione 0Eh dell'INT 10h (un interrupt bios, sempre ; disponibile) - Write character in TTY mode. Le stringhe sono ASCIIZ. In ; ingresso, SI contiene l'offset della stringa da stampare. stampa_msg proc near verifica: lodsb cmp al,0 jz fine_msg push si mov bx,7 mov ah,0Eh int 10h pop si jmp verifica fine_msg: ret stampa_msg endp ; Legge un determinato settore dell'HD, usando la funzione 02h dell'INT 13h. ; Il tentativo viene ripetuto 5 volte in caso di errore, forzando la ; ricalibratura delle testine (funzione 0 dell'INT 13h). Se dopo 5 tentativi ; l'interrupt restituisce ancora errore, stampiamo un messaggio di errore e ; mandiamo in loop il PC. ; In ingresso BX contiene l'offset del buffer di destinazione (per il ; segmento non c'è problema: sono tutti uguali!), CX il numero del primo ; settore da leggere (piatto e traccia sono sempre 0, l'hd e' sempre il ; primo), SI l'offset del messaggio da visualizzare in caso di errore. leggi_settore proc near mov di,5 riprova: mov dx,80h mov ax,0201h push di int 13h pop di jnb lettura_ok push bx xor ax,ax int 13h pop bx dec di jnz riprova call stampa_msg jmp eternal_loop lettura_ok: ret leggi_settore endp ; Il carattere con codice ascii 7 e' il bell, Errore: BEEP-BEEP. errore_MBS db "MBS read error.",07,07,00A differenza del MBS disassemblato nel precedente articolo, questo non comprime tutto nel minimo spazio indispensabile, anzi potrebbe apparire decisamente ridondante. Questo perchè sono solo pezzi di un codice più ampio.
Così com'è, non è possibile installare questo programma direttamente sul MBS: ricordiamoci che dobbiamo forzare gli offset in modo che la prima istruzione abbia IP pari a 7C00h, poi dobbiamo estrarre li codice dall'eseguibile generato e copiarlo sul HD al posto debito, ma non prima di aver spostato il MBS originale sul secondo settore dell'HD. Questo metodo funziona correttamente o senza particolari problemi con DOS, OS/2, Linux (LILO), Win95, e forse anche Windows NT se non si mette a fare il paranoico come suo solito.
Chip-O-Matic
Gli smanazzatori accaniti staranno già preparando un programma per fare tutto questo, i novizi si staranno domandando da che pianeta vengo, i curiosi si staranno chiedendo cosa c'entra Chip-O-Matic. Semplice, Chip-O-Matic è un programma che fa tutto questo da solo (potrei anche aggiungere che l'autore sono io, ma Luciano mi rimprovererebbe di strumentalizzazione, e la mia immensa modestia mi impedisce di vantarmi).
Tramite un file di configurazione, definite le informazioni elencate in precedenza riguardanti il proprio chipset e le nuove impostazioni. Quindi il programma provvede ad aggiornare correttamente il proprio HD, e a voi non resta che resettare. Comodo, no?
Prossimamente verrà messa in distribuzione la nuova versione, la potrete trovare in Fidonet (2:335/336), Caesarnet(175:391/1) e probabilmente anche Internet, con il nome di COM299B.*.
Qui finisce questo nostro viaggio... ma l'avventura continua...
visitatamente, Cthulhu