Beta [ Pagina Principale || Sommario || Redazione || Informazioni || Beta Browser ]

[Rubrica Programmazione]

a cura di Francesco Sileno



Nota: in questo articolo si fanno dei riferimenti a termini tecnici definiti nei precedenti Numeri (2 e 3). Nel collegare questi argomenti, si è assunto che la directory del Numero 2 sia "../0295", del Numero 3 sia "../0395" e che quella di questo Numero sia "../0495". Per esempio c:\beta\0295, c:\beta\0395, ecc. Questo è valido solo per installazione in locale.

Nel paese dei Chipset

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 Reset
Qundi ora sappiamo che la INDEX PORT è 22h, la DATA PORT è 23h, e sappiamo il significato dei vari bit del registro con offset 50h.
Vogliamo leggere il contenuto di quest'ultimo? Sara' sufficente un

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 implementation
Se 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 1Ah
e 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 1Ah
A 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     ss
in modo che tutti i segmenti siano PER FORZA a 0. Purtroppo, cambiare la prima istruzione con un

                mov     ax,cs
avrebbe 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,00
A 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



Beta [ Pagina Principale || Sommario || Redazione || Informazioni || Beta Browser ]
Beta - La rivista ipertestuale tecnica, copyright © 1994-95 Luciano Giustini e Fernando Carello. Tutti i diritti riservati.
0.05 07/11/95: installazione in rete