Da hast Du etwas tierisch mißverstanden ... ich hatte das nur als Beispiel gedacht.
Das Vorgehen sähe so in etwa aus:
1. Ermitteln der Adresse von __avm_kernel_config_start
Code:
[B]$ grep __avm_kernel_config_start /proc/kallsyms[/B]
80848000 D __avm_kernel_config_start
An der Adresse hinter dem Symbol steht erst mal ein Pointer, also eine 32-Bit-Adresse, der auf eine weitere Liste verweist - nicht vergessen, die "kseg0"-Flags der Adresse zu entfernen, aus 8 wird 0 im höchsten Nibble.
Code:
$ devmem 0x00848000 32
0x80848010
Diese Liste beginnt also an der Adresse 0x80848010 (16 Byte hinter dem ersten Pointer), sie ist programmtechnisch aber ein Array und zwar aus "_avm_kernel_config"-Strukturen, die wir in der Datei include/uapi/linux/avm_kernel_config.h in den Kernel-Quellen finden:
Code:
struct _avm_kernel_config {
enum _avm_kernel_config_tags tag;
void *config;
};
Jedes Element besteht also aus einem "tag", mit dem der Typ des Eintrags angezeigt wird und einer weiteren Adresse, die - je nach Typ - auf weitere Daten verweist.
Die möglichen "tag"-Werte sind (aus derselben Datei wie oben):
Code:
enum _avm_kernel_config_tags {
avm_kernel_config_tags_undef,
avm_kernel_config_tags_modulememory,
avm_kernel_config_tags_version_info,
avm_kernel_config_tags_hw_config,
avm_kernel_config_tags_cache_config,
avm_kernel_config_tags_device_tree_subrev_0, /* subrev müssen aufeinander folgen */
avm_kernel_config_tags_device_tree_subrev_1,
avm_kernel_config_tags_device_tree_subrev_2,
avm_kernel_config_tags_device_tree_subrev_3,
avm_kernel_config_tags_device_tree_subrev_4,
avm_kernel_config_tags_device_tree_subrev_5,
avm_kernel_config_tags_device_tree_subrev_6,
avm_kernel_config_tags_device_tree_subrev_7,
avm_kernel_config_tags_device_tree_subrev_8,
avm_kernel_config_tags_device_tree_subrev_9, /* subrev müssen aufeinander folgen */
avm_kernel_config_tags_device_tree_subrev_last = avm_kernel_config_tags_device_tree_subrev_9,
avm_kernel_config_tags_avmnet,
avm_kernel_config_tags_last
};
So ein "enum"-Wert ist am Ende (sofern man nichts anderes festlegt) ein 32-Bit-Integer, der bei 0 für den ersten Wert losgeht und bis zum letzten "durchnummeriert" wird. Damit ist als "undef" der Wert 0, "modulememory" ist 1, usw. - brauchen wir gleich, um die Einträge zu erkennen.
Die Liste an der Adresse 0x80848010 sieht dann so aus:
Code:
$ devmem 0x00848010 32
0x00000001
$ devmem 0x00848014 32
0x80849528
$ devmem 0x00848018 32
0x00000002
$ devmem 0x0084801C 32
0x80849468
$ devmem 0x00848020 32
0x00000005
$ devmem 0x00848024 32
0x80848030
$ devmem 0x00848028 32
0x00000010
$ devmem 0x0084802C 32
0x00000000
Jeder Eintrag im Array besteht ja nun aus 8 Byte (4 für das "tag" und 4 für den Pointer) und irgendwie muß das Ende des Arrays ja angezeigt werden, solange nirgendwo die Anzahl der Einträge steht. Schaut man dann in die Datei init/avm_kernel_config.c, findet man da die folgende Funktion:
Code:
void init_avm_kernel_config(void) {
[...]
struct _avm_kernel_config *p;
[...]
p = *avm_kernel_config;
[COLOR="#FF0000"] if (p == NULL)
[/COLOR] return;
[COLOR="#00FF00"] while (p->tag <= avm_kernel_config_tags_last) {[/COLOR]
[COLOR="#008000"] if (p->config == NULL)
return;
[/COLOR]
[...]
p++;
}
[...]
}
Hier wird also in einer Schleife über das Array iteriert, wenn der Pointer auf den ersten Eintrag nicht von Beginn an NULL ist (der rote Test). Ansonsten wird die Schleife solange durchlaufen, bis entweder ein Eintrag mit dem höchsten Wert im "tag"-Feld (das ist 16, wie man durch Abzählen (ab 0 beginnend!) leicht feststellen kann in der Enumeration, also 0x00000010 in hex) gefunden wird (der hellgrüneTest) oder der Pointer auf die Daten in einem Eintrag auch NULL ist (der dunkelgrüne Test).
Schaut man sich dann die Daten aus dem Speicher an, haben wir da als vierten Eintrag genau einen solchen (ab 0x80848028) ... der erste Int32-Wert ist 0x10 und der nächste Pointer ist NULL. Damit besteht das Array aus "_avm_kernel_config"-Strukturen aus 4 Membern, drei davon zeigen auf irgendwelche Daten und der letzte markiert das Ende des Arrays. Die Pointer werden anhand des Tags im Code passend "casted", steht auch in der o.a. C-Datei.
In C sieht das als Definition der Liste dann etwa so aus:
Code:
static struct _arm_kernel_config[] = {
{ .tag = avm_kernel_config_tags_modulememory, .config = &module_list }, /* 1st entry is of type "modulememory" => 1, config is pointer to "struct _kernel_modulmemory_config[]" */
{ .tag = avm_kernel_config_tags_version_info, .config = &version_info }, /* 2nd entry is of type "version_info" => 2, config is pointer to "struct _avm_kernel_version_info" */
{ .tag = avm_kernel_config_tags_device_tree_subrev_0, .config = &device_tree }, /* 3rd entry is of type "device_tree_subrev_0" => 5, config is pointer to "struct _avm_kernel_config_device_tree" */
{ .tag = avm_kernel_config_tags_last, .config = NULL } /* final entry, end of array */
};
Aber das ist ja erst der Anfang, jetzt fehlen uns die Daten für "module_list", "version_info" und das Resultat der "Übersetzung" der Datei /arch/mips/boot/dts/Fritz_Box_HW185_310.dts in den "device tree" für die 7490 ... aber immer schön der Reihe nach, fangen wir mit der Module-Liste an. Die Definition dieser Einträge findet sich auch wieder in der include/uapi/linux/avm_kernel_config.h und sieht so aus:
Code:
struct _kernel_modulmemory_config {
char *name;
unsigned int size;
};
[...]
struct _avm_kernel_version_info {
char buildnumber[32];
char svnversion[32];
char firmwarestring[128];
};
und weil wir schon mal in der Datei waren, habe ich den nächsten Eintrag (das ist ja dann die Versionsinformationsstruktur) auch gleich noch mitkopiert, die merken wir uns für den zweiten Listeneintrag.
Die Module-Liste besteht also aus einem String-Pointer (eigentlich char, aber mehrere char sind nun mal als "string" bekannt) und wenn der "name" heißt, wird es wohl auch ein Name sein. Der zweite Wert heißt "size" und ist (vermutlich) die Größe des für dieses LKM vorzusehenden Speichers beim Laden. Das hängt mit etwas komischer Verwaltung des Speichers zusammen, damit da keine Interrupts an ungünstigen Stellen auftreten und ist die "Ablösung" der früheren "modulemem"-Mechanik, die auch gleichzeitig die Fallback-Lösung ist, wenn diese Liste im Kernel fehlt. Jedenfalls besteht also auch hier jeder Eintrag aus 8 Byte, wobei die ersten 4 wieder eine weitere Adresse darstellen und die folgenden 4 eine Zahl. Schauen wir uns das jetzt im Speicher an, sieht das so aus:
Code:
$ devmem 0x00849528 32
0x80849760
$ devmem 0x0084952C 32
0x00005300
$ devmem 0x00849530 32
0x8084976C
$ devmem 0x00849534 32
0x00007480
[...]
Ich habe nur die ersten zwei Einträge oben angeführt für das "händische Auslesen", die gesamte Liste hat einiges an Einträgen und wird wieder mit einem leeren Eintrag abgeschlossen. Wenn man das ein wenig automatisiert mit dem Auslesen, sieht das "in Rohform" erst einmal so aus:
Code:
$ name="-";ptr=849528;while [ $name != 0x00000000 ]; do name=$(devmem 0x$ptr 32);size=$(devmem 0x$(printf "%X" $(( 0x$ptr + 4 )) ) 32);echo "0x$ptr = $name,$size";ptr=$(printf "%X
" $(( 0x$ptr + 8 )) ); done
0x849528 = 0x80849760,0x00005300
0x849530 = 0x8084976C,0x00007480
0x849538 = 0x80849778,0x00008900
0x849540 = 0x8084977C,0x000414A4
0x849548 = 0x80849780,0x0003F760
0x849550 = 0x80849784,0x0003B600
0x849558 = 0x80849788,0x0010E570
0x849560 = 0x80849798,0x0006E480
0x849568 = 0x808497A4,0x0007D828
0x849570 = 0x808497B0,0x00004400
0x849578 = 0x808497B8,0x0031F505
0x849580 = 0x808497C4,0x00003000
0x849588 = 0x808497CC,0x0003BA70
0x849590 = 0x808497D4,0x0000D7EC
0x849598 = 0x808497DC,0x00003700
0x8495A0 = 0x808497F0,0x00029E48
0x8495A8 = 0x8084980C,0x00014970
0x8495B0 = 0x80849824,0x00038A40
0x8495B8 = 0x80849840,0x00014DC8
0x8495C0 = 0x80849858,0x00003600
0x8495C8 = 0x8084986C,0x00004080
0x8495D0 = 0x80849884,0x0000C100
0x8495D8 = 0x8084989C,0x0000E58D
0x8495E0 = 0x808498A4,0x00005780
0x8495E8 = 0x808498B4,0x00005600
0x8495F0 = 0x808498C0,0x00007080
0x8495F8 = 0x808498C8,0x00004C00
0x849600 = 0x808498D4,0x00010460
0x849608 = 0x808498DC,0x00010A50
0x849610 = 0x808498E8,0x00088F5C
0x849618 = 0x808498F0,0x00028D38
0x849620 = 0x808498FC,0x0001B9D0
0x849628 = 0x80849908,0x00005380
0x849630 = 0x80849914,0x00011CE9
0x849638 = 0x80849920,0x00003600
0x849640 = 0x80849928,0x00002600
0x849648 = 0x80849934,0x00066A9B
0x849650 = 0x8084993C,0x00006900
0x849658 = 0x80849944,0x0000BE00
0x849660 = 0x8084994C,0x0008C524
0x849668 = 0x80849954,0x0000D280
0x849670 = 0x80849964,0x0000E1B0
0x849678 = 0x80849974,0x00031530
0x849680 = 0x80849980,0x00004A80
0x849688 = 0x80849988,0x00055388
0x849690 = 0x80849990,0x0004943B
0x849698 = 0x8084999C,0x00010994
0x8496A0 = 0x808499A4,0x0002A314
0x8496A8 = 0x808499B0,0x000193F0
0x8496B0 = 0x808499B4,0x00004100
0x8496B8 = 0x808499BC,0x00003980
0x8496C0 = 0x808499C4,0x00002580
0x8496C8 = 0x808499D0,0x00002500
0x8496D0 = 0x808499E0,0x00002700
0x8496D8 = 0x808499EC,0x00002400
0x8496E0 = 0x808499FC,0x00002400
0x8496E8 = 0x80849A08,0x00002000
0x8496F0 = 0x80849A14,0x00002400
0x8496F8 = 0x80849A20,0x00002000
0x849700 = 0x80849A2C,0x00007000
0x849708 = 0x80849A38,0x00003800
0x849710 = 0x80849A40,0x00003F80
0x849718 = 0x80849A48,0x00003C00
0x849720 = 0x80849A54,0x00004B00
0x849728 = 0x80849A5C,0x00005C80
0x849730 = 0x80849A68,0x00004580
0x849738 = 0x80849A74,0x00003B80
0x849740 = 0x80849A7C,0x00005600
0x849748 = 0x80849A84,0x00003280
0x849750 = 0x80849A90,0x00006100
0x849758 = 0x00000000,0x00000000
Das ist eine ganz schön lange Liste, daher als Test nur die ersten zwei Einträge zur "Überprüfung". Da steht also an der Adresse 0x80849760 im Speicher der Name des LKM, welches 0x00005300 Byte Speicher belegt. Schaut man sich das dann an, sieht es so aus:
Code:
$ devmem 0x00849760 32
0x6174686C
$ devmem 0x00849764 32
0x6F676765
$ devmem 0x00849768 32
0x72000000
C-Strings enden bekanntermaßen mit einem NUL-Byte, daher hat der Name offenbar 9 Zeichen. Setzt man das hintereinander und macht ASCII daraus, ergibt sich:
Code:
6174686C6F67676572 => athlogger
und das gibt es als LKM tatsächlich:
Code:
$ modinfo $(find /lib -name athlogger.ko)
filename: /lib/modules/3.10.73/offload/athlogger.ko
license: Dual BSD/GPL
depends:
vermagic: 3.10.73 SMP mod_unload MIPS_34K 32BIT MT_SMTC
parm: mode:athlogger mode none/target/host
Der zweite Eintrag zeigt auf den Namen "hif_gmac" und auch das gibt es als LKM:
Code:
$ modinfo $(find /lib -name hif_gmac.ko)
filename: /lib/modules/3.10.73/offload/hif_gmac.ko
license: Dual BSD/GPL
author: Atheros Communications, Inc.
description: Atheros Device Module
depends:
vermagic: 3.10.73 SMP mod_unload MIPS_34K 32BIT MT_SMTC
parm: hif_tgt_pwrdown:If enabled, target reset command is not issued while unloading
parm: tgt_if:Interface used to connect target
parm: mac_id:MAC(mac0/mac1) Interface to which target's MDC/MDIO lines are connected
Das zieht sich dann ... aber die resultierenden C-Definitionen sähen in etwa so aus:
Code:
static struct _kernel_modulmemory_config[] = {
{ .name = "athlogger", .size = 0x00005300 },
{ .name = "hif_gmac", .size = 0x00007480 },
[...]
{ .name = NULL, .size = 0 }
}
Wobei das noch mit Vorsicht zu genießen ist, denn hier kann man den Ablageort der Namen ja noch nicht steuern, also wird das am Ende eher ein Array mit den Namen werden und als "name" ergibt sich dann die Adresse der passenden Zeichenkette in diesem Array. Diese ganzen Daten liegen im Kernel ja zwischen den Symbolen für "__avm_kernel_config_start" und "__avm_kernel_config_end" und nicht irgendwo bei anderen statischen Strings.
Wie Du jetzt die Namen in ein Array kriegst und wie dann die passenden C-Statements aussehen, überlasse ich Deiner Phantasie ... am Ende sollte eine Liste entstehen, deren Adresse als "config"-Pointer in der ganz oben erwähnten Liste auftauchen kann.
Der nächste Listeneintrag ist dann "version_info" und auch den habe ich oben ja schon gezeigt. Diese Struktur besteht aus drei Zeichenketten, wobei die ersten beiden max. 31 Zeichen lang sind (NUL-Byte am Ende nehmen wir mal an) und die dritte max. 127 Zeichen verkraftet. Sucht man die Daten jetzt mit "devmem" heraus, ergibt sich für den derzeit aktuellen Labor-Kernel allerdings nur, daß diese Struktur nicht wirklich benutzt wird ... ich kann gerade keinen 06.60-Kernel booten auf der 7490, da sollten aber sinnvolle Zeichenketten stehen (zumindest taten sie das bei der 06.51 noch).
Der dritte Eintrag zeigt dann auf einen "struct boot_param_header"-Eintrag, wie man in der Datei arch/mips/lantiq/common/ifxmips_setup.c sehen kann, denn hinter einem avm_kernel_config_device_tree-Eintrag verbirgt sich genau ein solcher:
Code:
[COLOR="#FF0000"]dtb = (struct boot_param_header *)avm_kernel_config_device_tree[subrev];[/COLOR]
if(!dtb) { /* fallback auf nächst kleine SubRev */
int i;
for(i = subrev - 1; i >= 0; i--){
if(avm_kernel_config_device_tree[i]){
dtb = (struct boot_param_header *) avm_kernel_config_device_tree[i];
prom_printf("%s: using Fallback device-tree of AVM hardware "
"subrevision %d \n",
__func__, i);
break;
}
}
}
if (!dtb) {
prom_printf("%s: Missing device-tree for AVM hardware "
"subrevision %d\n", __func__, subrev);
panic("%s: Missing device-tree for AVM hardware "
"subrevision %d\n", __func__, subrev);
} else {
extern struct boot_param_header *initial_boot_params;
initial_boot_params = dtb;
prom_printf("DT: %02x %02x %02x %02x %02x %02x %02x %02x\n",
((unsigned char *)dtb)[0],
((unsigned char *)dtb)[1],
((unsigned char *)dtb)[2],
((unsigned char *)dtb)[3],
((unsigned char *)dtb)[4],
((unsigned char *)dtb)[5],
((unsigned char *)dtb)[6],
((unsigned char *)dtb)[7]);
}
Ich habe das noch nicht weiter untersucht, aber das sollte sich aus der Ausgabe der Gerätedefinition in der arch/mips/boot/dts/Fritz_Box_HW185_310.dts irgendwie erzeugen lassen.
Es gibt bei der 7490 bisher auch nur einen Eintrag für die SubRevision 0, alle anderen fallen bis auf die 0 zurück bei der Suche oben.
Aus den ausgelesenen Informationen muß man also erst einmal passende Quellen bauen ... vielleicht am besten sogar direkt Assembler anstelle von C - man kann es einfach besser steuern, daß sich ein 1:1-Abbild des AVM-Bereichs ergibt.
Wenn die dann vorhanden sind (die müssen eben für jeden neuen AVM-Kernel auch wieder neu generiert werden, deshalb sollte das schon automatisiert werden), dann kann man sich einen Kopf machen, wie man die dazulinken kann bzw. wie man das in ein Freetz-Make integrieren könnte. Wobei ich mir wenige Hoffnungen mache, das auch aus einem "offline kernel" extrahieren zu können ... theoretisch sicherlich auch denkbar, aber noch viel aufwendiger als es nach allen Loader-Aktionen aus dem Hauptspeicher einer Box zu polken.
- - - Aktualisiert - - -
Eine 1:1-Kopie des
binären Inhalts bei einem Kernel kannst Du nicht verwenden ... durch andere Optionen beim Build ergeben sich auch andere Adressen und da die Daten ja auch jede Menge Pointer enthalten, müssen die schon beim Ladevorgang passend aufgelöst werden, falls sich da Adressen ggü. dem AVM-Kernel verschoben haben ... und das werden sie, sonst bräuchte es ja keinen eigenen Kernel, wenn der 1:1 mit dem von AVM übereinstimmt.
- - - Aktualisiert - - -
Kleines Versehen meinerseits: Bei der Versionsinformation handelt es sich ja direkt um die Zeichenketten und gar nicht um Pointer auf solche ... damit ist das ein Speicherbereich von 32 + 32 + 128 = 192 Byte und bei Offset 0 steht "buildnumber" (die ist tatsächlich leer), bei Offset 32 steht "svnversion" (die 34038 finde ich da, weil offenbar nicht jede Firmware-Version auch mit einem neuen Kernel aufwartet bzw. mit einem geänderten Stand der Quellen) und bei Offset 64 steht dann "firmwareversion", was bei der Labor-Version auf "06.69-40520" hinausläuft ... damit ist also der Kernel tatsächlich zumindest neu gelinkt, wenn da im "config"-Bereich die korrekte "subversion" auftaucht.