Benq S6 EC per tutti

Un veloce preambolo: mi trovo in possesso di un MID Benq S6.

I MID sono quegli oggetti tipo mezzo iPad, piu` o meno con lo stesso senso e un terzo del costo, ma che hanno provato a commercializzare prima che Steve Jobs ci spiegasse che ne avevamo bisogno. Ovviamente erano basati su atom, con linux e software opensource (ma volendo, proprio volendo, ci puoi buttare su anche il buon vecchio finestre). Ovviamente non hanno avuto successo.

Il Benq S6 arriva con un sistemino midinux moolto vecchio, basato su un ibrido tra varie fedora e le repository originali sono offline. Per poterlo usare nel mondo moderno bisogna necessariamente cambiare distro.

L'affarino ha un hardware non proprio dei migliori, con il famigerato poulsbo gma500 (per giunta nella versione pacco ul11l), ma si risolve piu` o meno tutto. E` piuttosto simile ad altri MID uguali e c'e` un sacco di gente che s'e` sbattuta per far funzionare tutto a modino, compresa la riproduzione di video con l'accelerazione grafica (tipo questo tipo qui con le sue distro).

C'e` un grosso problema pero` che distanzia il benq s6 da tutti gli altri MID: la wifi, il bluetooth e il modem 3G sembrano funzionare solo e solo unicamente con la distro originale.

Il mistero si risolve leggendo un post qui dove viene spiegato che wifi e compagnia cantante e` comandata da uno switch hardware. Lo switch hardware ha il suo driver apposito, ma il tipo del post ci dice candidamente di cercare i sorgenti nel CD allegato al Benq S6 e ricompilarselo.

Facile vero? No, perche` i sorgenti non ci sono. Nada. Niet.

Allora che si fa, ci si arrende? Pare che molti abbiano intrapreso questa strada, dopo tutto il MID e` un oggetto inutile la meta` di un iPad e si puo` vivere anche senza.

Io non mi sono arreso, quindi mi sono messo a guardare un po' quello che c'e` a disposizione: il modulo del driver.

Modinfo mi dice

filename:       benqecif.ko
version:        2.1.8
description:    BenQ EC Interface
author:         Weichen Lin
license:        GPL
srcversion:     B9AE214E3CD0D70F5025980
depends:        
vermagic:       2.6.22.18-2 SMP mod_unload 586 

GPL! Il nome dell'autore! Lo trovo, scrivo, aspetto.

Niente.

"Use the source, luke" ma come si fa senza sorgenti? Tsk, mi direbbe un vero hacker, hai il binario, hai tutto.

Ok, prima di tutto vediamo che fa il driver: crea un po' di file sotto /proc/acpi/benq_ec_if dai nomi tipo "sysled" "wireless" "wirelesslanled" e cose cosi`. Da questo impariamo che e` un driver ACPI EC (Embedded Controller).

Cerco un po' di documentazione a giro e scopro che gli embedded controller sono una cosa piuttosto standard: ci si parla con un protocollo banale attraverso due ioport e si possono scrivere 256 registri da 8 bit. Fine. Il significato di questi 256 registri e quello che fa l'embedded controller quando ci scrivi non dipende solo dal chip usato (in tal caso avevo gia` il cacciavite pronto), ma anche da come e` stato programmato.

Uff. Un primo tentativo e` stato guardando come cambiavano i registri leggendoli tutti prima e dopo aver abilitato/disabilitato/acceso/spento con questo utilissimo script qui. Cosi` ho scoperto che i led si possono comandare scrivendo il registro 0xb0, carino (e non la maniera che aveva in testa benq, scopriro` dopo), ma poco utile.

E` giunta l'ora di fare l'hax0r, quindi passiamo benqecif.ko a fil di nm.

Quasi tutte le funzioni hanno un nome che dice tutto, ma soprattutto scopro che usa l'interfaccia ACPI per comunicare con l'EC! Infatti le uniche funzioni esterne al modulo che possono leggere/scrivere le porte sono le ec_write ed ec_read standard del kernel.

Bene, mi dico, objdump tutta la vita, cerco di ricostruire da quello che ho capito fin ora (sul registro 0xb0 ci stanno i led) anche le altre funzioni.

Purtroppo non trovo 0xb0 da nessuna parte (che sia nascosto in .rodata ? da qualche altra parte?) e scopro anche che la maggior parte delle funzioni con nome banali tipo benq_set_wirelesslanled che da quello che ho capito fin ora dovrebbe scrivere |= 0x8 su 0xb0 fa una sequenza di ec_read, ec_write, ec_write, ec_read. Il primo read mi torna, il primo write ovviamente si, l'ultimo read potrebbe essere un controllo, ma il secondo write? Uhmm, il protocollo e` piu` complesso che modificare bitmask!

Что делать?

A questo punto non rimane che scendere negli inferi. Mi leggo phrack e mi leggo il testo sacro di Silvio Cesare e capisco. Code injection nel modulo.

Alla fine non uso nemmeno l'artiglieria pesante, basta scriversi due funzioni di hook per ec_read/ec_write che scrivano con printk quello che stanno facendo, modificare con un hex editor i nomi delle funzioni in benqecif.ko, relinkare il tutto (relocatable object ftw) e sostituire il modulo col nostro codice con quello vecchio.

Facile. Dopo *due giorni* di studio extra su come sono compilate le funzioni acpi nel kernel (fastcall questo sconosciuto) riesco ad ottenere il risultato voluto: ogni volta che il driver propietario, binario, di cui non si trovano i sorgenti, vuole fare qualcosa mi scrive gentilmente sul log di sistema cosa sta cercando di fare in modo che possa prendere appunti.

Il risultato di tutto questo e` la scoperta che la misteriosa interfaccia esegue ogni operazione con lo stesso protocollo.
Per scrivere: legge il registro 0x60, scrive in 0x61 il valore dell'operazione (0 == spento, 1 == acceso, 2 == per i led che lampeggiano), scrive in 0x60 l'opcode dell'operazione, poi legge 0x60.
Per leggere: legge il registro 0x60, scrive in 0x60 l'opcode, legge 0x60, legge in 0x61 il valore richiesto.
Le letture di 0x60 devono sempre risultare 0x0 (valori diversi sono un errore, suppongo).

Gli opcode sono:

  • 0x2a get_wireless
  • 0x29 set_wireless
  • 0x28 get_hdspa
  • 0x27 set_hdspa
  • 0x42 set_sysled
  • 0x40 set_wirelesslanled
  • 0x41 set_wwanled
  • 0x43 set_chargingled
  • 0x2b set_gps (non c'e` gps nel benq s6, ma tant'e`)
  • 0x18 set_sd_cd (non ho ancora capito che fa, credo cambi controller mmc)
  • 0x16 set_keylock

Diverso dagli altri e` get_powerbutton, fa due letture in 0x46 e 0x47, ma ancora non ne ho capito il significato.

Comunque tanto basta: con lo script in perl di cui ho dato il link un po' sopra sono riuscito a replicare la sequenza per attivare la wifi sotto nMind 0.5 (dal blog di mindbuntu) e si accende!

E` fatta. Viva l'iniezione del codice nei moduli! Viva la rivoluzione!