[Gelöst] FW 6.50 für 7490 in Trunk rev. 13490 - keine Auswahlmöglichkeit

@opto:
Genau das Zurechtbasteln der init_avm_kernel_config() habe ich oben ja als "ultima ratio" bereits "angetextet" ... schneller als das Warten auf AVM ist das allemal. Ich habe noch eine winzige Hoffnung, daß es bei der 06.60 anders sein wird, weil AVM tatsächlich mal jemanden an das Problem setzt ... will man ab dem 01.08. seine doch eher hochpreisigen Modelle (für ein Consumer-Gerät wohlgemerkt) an den Mann bringen, wäre die Feststellung "Trotz angeblicher Veröffentlichung aller notwendigen Quelltexte nach GPL funktioniert es nicht." sicherlich nicht gerade "publikumswirksam" ... so platt so eine Fest- oder Vorstellung auch sein mag, denn auf der anderen Seite sind so viele entscheidende Komponenten "closed source", daß da ein unabhängiger Review ohnehin nicht möglich wäre - da sind dann andere Anbieter (die weniger Funktionen bieten, dafür aber alles auch auf FOSS-Basis) eben doch weiter.

Erscheinen die 06.60-Quellen auch in einer unbenutzbaren Form, mache ich mir tatsächlich mal den Spaß und lese die Daten aus einem funktionierenden System aus, woraus sich dann wieder die notwendigen Source-Files generieren lassen.

Und nein ... ich habe Oliver noch nicht darauf angesprochen. Aber ich bin überzeugt, daß er (zumindest sporadisch) auch im IPPF mitliest und wenn er zu derselben Feststellung gelangt wäre wie ich, hätte er sich meiner Überzeugung nach schon selbst zu Wort gemeldet. Entweder er kann also einen eigenen Kernel aus den 3.10.73-Quellen erstellen oder es interessiert ihn im Moment ohnehin nicht. Warum soll ich ihm dann auf die Nerven gehen?
 
Was wohl Druck machen würde wenn sich Heise.de damit beschäftigen und es im Zweifel veröffentlicht. Allerdings ist AVM auch ein grosser Anzeigenkunde :(

Aber Heise versucht es vielleicht freundlich hinter den Kulissen? Auf jden Fall hören sie Heise zu
 
@chilango79:
OT: Ich wage die Behauptung: "eher nicht" und hülle mich dann in Schweigen, woher ich das wissen will.
 
:)

Ja, da ist in erster Linie einiges zum Profiling von Prozessen hinzugekommen ... man kann ja mal mutmaßen, daß AVM bei entsprechender Auslastung der Boxen schon um jeden Takt bei der Abarbeitung von Instruktionen feilschen muß, so groß sind die Reserven (einer voll ausgelasteten Box) dann doch nicht.

Ansonsten wurde wohl wirklich ein Traffic-Shaping für Downstreams hinzugefügt zu avm_pa.c, wobei über das procfs wohl nur das generelle Ein- und Ausschalten eines solchen Shapings möglich ist (notfalls für ein einzelnes Interface), die genauen Einstellungen werden dann wohl irgendwo im dsld/kdsld oder auch multid erfolgen ... bestimmt kriegt man das auch irgendwann heraus.

Was mich wundert ... es gibt ja schon eine Weile in der Firmware die Möglichkeit, das Freischalten von Portweiterleitungen über UPnP und/oder PCP gezielt auf bestimmte Geräte (sicherlich z.B. Spielekonsolen o.ä., wo das sinnvoll sein mag) zu beschränken. Das wird aber im Moment auch nur dann "angeboten", wenn es sich nicht um eine Release-Version handelt (config.gu_type == "private").

Nun kann man ja mal spekulieren, ob das ein Feature ist, was die AVM-Mitarbeiter gerne selbst haben wollten (manchmal braucht man eben für Gaming solche Möglichkeiten und will doch nicht gleich allen Geräten im LAN das Durchbohren der Firewall erlauben) und was daher nie für die Veröffentlichung gedacht war oder ob es noch nicht "reif" ist, da noch weiterhin gebaut wird und es vielleicht sogar einen Zusammenhang zwischen diesen Features geben könnte.

Sicherlich ist es auch nicht allzu angenehm, wenn die Console des halbwüchsigen Sohnes mit dem Download eines 10 GB-Updates für irgendein ranziges Spiel den abendlichen Fernsehkonsum verhindert, weil man vielleicht doch kein VDSL-Vectoring hat und schon mit 16 MBit/s zufrieden sein muß (auch wenn man dann wohl selbst schuld ist, wenn man da auch noch TV-Streaming vom Provider gebucht hat).

Jedenfalls wurde dann wohl gleich noch im PA das Problem beseitigt, daß bei aktivem Capture trotzdem die Pakete "accelerated" wurden und damit im Mitschnitt nicht auftauchten.

Das waren dann aber auch schon fast die Änderungen, ein paar Zeilen für GRX500 (und damit in der Folge wohl für die 758x) sind noch dazugekommen.

Ansonsten sehe ich auch keine Änderungen an den von mir verdächtigten Stellen ... es wäre mir in der Tat sehr recht, wenn jemand anderes ebenfalls mal über die originalen Quellen schauen und sich eine Meinung bilden könnte. Manchmal ist man ja auch nur betriebsblind und ehe ich mich mit einer "offiziellen Beschwerde" zum Obst mache, hätte ich eben gerne eine zweite Meinung. Ob so ein "Maulen" in Richtung AVM dann Erfolg haben könnte/würde oder nicht, steht wieder auf einem ganz anderen Blatt.
 
Ich habe auch mal per Support Formular rumgestänkert und verlangt, dass sie die Sourcen endlich freigeben. Es wird so sein wie bei jeder Firma: begrenzte Ressourcen und auf Executive Ebene ein mangelndes Verständnis der GPL und ihrer Vorteile. Man muss den Schmerz für AVM so lange erhöhen, bis es für sie einfacher ist, GPL compliant zu sein als rumzutrödeln. Heißt: je mehr Leute sich regelmäßig über das Ausbleiben der Sourcen beschweren, desto besser.

- - - Aktualisiert - - -

Da sind sie auch schon, die Sourcen! :)
ftp://ftp.avm.de/fritz.box/fritzbox.7490/x_misc/opensrc/
 
@zyrill:
Hast Du da vielleicht etwas ein wenig falsch verstanden? Die waren doch vorgestern auch schon da. :gruebel:
 
Offensichtlich - ich hatte vor ein paar Tagen schonmal geschaut, da waren sie noch nicht da. Egal, Hauptsache, jetzt haben wir sie.
 
@zyrill:
Und nun? Wie geht es jetzt weiter?
 
Offensichtlich - ich hatte vor ein paar Tagen schonmal geschaut, da waren sie noch nicht da. Egal, Hauptsache, jetzt haben wir sie.
Bevor du noch weiter meinen Posteingang mit sinnlosen Nachrichten füllst, weil du kommentierst ohne die letzten Beiträge gelesen zu haben: Die Sourcen sind nicht vollständig und daher unbrauchbar.
 
Z.B. mit dem "devmem"-Applet der Busybox.

Die Startadresse steht irgendwo in den Kernel-Symbolen (/proc/kallsyms) und die dort liegenden Datenstrukturen sind in den C-Quellen des Kernels dokumentiert.

Das "devmem"-Applet kann zwar max. 32-Bit-Werte aus dem Speicher lesen (die verschiedenen Adressarten bei MIPS nicht vergessen, vermutlich müssen für das "devmem"-Applet die zwei höchstwertigen Bits gelöscht werden, weil das keine Kernel-Adressen sind, wenn das Applet sein "mmap" aufruft, sondern Offsets in /dev/mem), aber man kann auch Zeichenketten zusammenbasteln.

Wie das gehen könnte, findest Du irgendwo in meinem YourFritz-Repo in einem Beispiel, da wird in einem Skript mit dem "devmem"-Applet die TFFS-Name-Table aus dem Kernel einer laufenden Box gelesen.
 
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.
 
Zuletzt bearbeitet:
Nein, die Meldungen sind mir noch nicht aufgefallen ... und mein Syslog (das schreibt beim Start erst mal nur in den Speicher und erst später ins Netz) ist lange schon "umgebrochen", auch weil die 06.69 ständig irgendeinen voipd-Bug protokolliert beim Telefonieren und mir auf die Nerven geht.

Die Speicherangaben für die Module werden beim Initialisieren des Kernels berücksichtigt, in der arch/mips/avm_enh/kseg0_module.c:
Code:
/*--------------------------------------------------------------------------------*\
 * Initiale Vorbereitungen fuer kseg0-bootmem
 * es wird ein extra module_memory reserviert, so dass Treiber direkt in KSEG0
 * geladen werden koennen.
 * Dies wird z.B. unbedingt benoetigt, falls Module die Yield-Schnittstelle verwenden
 * will (es duerfen im Yield-Kontext keine TLB-Exception auftreten!)
 * Desweiteren
\*--------------------------------------------------------------------------------*/
void __init module_alloc_bootmem_init(struct resource *res) {
    unsigned int i;
        char *arg;

        if (kernel_modulmemory_config) {
                struct _kernel_modulmemory_config *CMod = kernel_modulmemory_config;

                module_alloc_size_list_size = 0;
                while (CMod->name) {
            if(!module_is_blacklisted(CMod->name)) {
                module_alloc_size_list_size += ((CMod->size + PAGE_SIZE) & ~(PAGE_SIZE - 1)) + PAGE_SIZE; /*--- inkl. Reserve-Page fuer Entwicklung -> groesseren Treiber  nachladen ---*/
                CMod++;
            }
                }
        } else if ((arg = prom_getenv("modulemem")) != NULL) {
                module_alloc_size_list_size = simple_strtoul(arg, NULL, 0) + (ARRAY_SIZE(module_alloc_size_list) * PAGE_SIZE);
                printk(KERN_ERR "[module-alloc-by-name] warning 'modulemem' used - depreciated\n");
        }
        if (module_alloc_size_list_size == 0UL) {
                printk(KERN_ERR "[module-alloc-by-name] 'modulemem' not set, "
                       "function disabled\n");
                return;
        }
        module_alloc_size_list_base = (unsigned long)alloc_bootmem_pages(module_alloc_size_list_size + (1 << PAGE_SHIFT));
        module_param.start = CPHYSADDR(module_alloc_size_list_base);
        module_param.end = CPHYSADDR(module_alloc_size_list_base +
                                     module_alloc_size_list_size);
        module_param.name  = "module memory";
        module_param.flags = IORESOURCE_MEM | IORESOURCE_BUSY;

        if (request_resource(res, &module_param)) {
                printk(KERN_ERR "[module-alloc] failed 0x%x bytes at 0x%lx\n",
                       module_alloc_size_list_size,
                       module_alloc_size_list_base);
        } else {
                printk(KERN_ERR "[module-alloc] use 0x%x bytes at 0x%lx\n",
                       module_alloc_size_list_size,
                       module_alloc_size_list_base);
        }
        if (reserve_bootmem(module_param.start,
                            module_param.end - module_param.start + 1,
                            BOOTMEM_DEFAULT)) {
                printk(KERN_ERR "[module-alloc] reserve memory for module-load failed (start 0x%x end 0x%x)\n",
               module_param.start, module_param.end);
        }
    for(i = 0 ; i < ARRAY_SIZE(module_alloc_size_list); i++) {
        atomic_set(&module_alloc_size_list[i].alloc, 0);
    }
}
Im Kommentar am Beginn des Blocks steht dann ja auch die Idee dahinter ... es wird dafür gesorgt, daß die Module in einen (zusätzlich reservierten) Speicherbereich geladen werden können, aus dem sie nicht ausgelagert werden (ansonsten wird ja beim Beenden der Kernelinitialisierung der nicht benötigte Speicher freigegeben, hier wird das "benötigt" wohl vorher etwas "aufgeblasen").

Beim erwähnten "Yield" handelt es sich vermutlich um eine AVM-eigene (oder auch eine von Lantiq initiierte, das kann ich nicht beurteilen) Variante des "Abgebens" von Ressourcen, wenn ein Thread "eingefroren" werden kann, bis er wieder etwas zu tun bekommt oder weil er für eine IRQ-Behandlung die Steuerung abgeben muß.

Mangels Dokumentation des VR9 (nur allgemeine MIPS-Dokus kenne ich) weiß man auch wenig über die Interna ... vielleicht ist das auch nur eine etwas effektivere (aber mit anderen Architekturen inkompatible) Art des Umgangs mit den "Virtual Processing Elements" (VPE) bei der MIPS-Architektur (erst ab 34K, ab da gibt es ja auch erst "Multicore Processing"), weil AVM bei Messungen (das "profiling" ist ja recht umfangreich nach den Kernel-Quellen) festgestellt hat, daß "große" Kontextwechsel viel Zeit fressen oder weil beim Kontextwechsel noch zusätzliche Daten gesichert werden müssen oder etwas in der Richtung (habe ich nicht versucht zu ergründen, steht irgendwo in arch/mips/kernel/yield_context.c - und unter der GPL(!) nach dem Kommentar am Beginn der Datei).

Am ehesten würde ich aber auf eine Art "scheduler" ("mighty massa"?) tippen, der diverse Kontextwechsel (innerhalb des Kernels) abfangen kann, weil er aus Systemsicht als ein einzelner Prozess erscheint und selbst dafür sorgt, daß andere (Kernel-)Prozesse entsprechend ihres Bedarfs Ressourcen zugeteilt bekommen oder auch eine besonders schnelle (oder konstante) IRQ-Behandlung sicherstellen kann (einiges in der Box ist ja schon kritisch beim korrekten Timing), was dann wieder in Richtung besserer "real-time capabilities" gehen könnte.

Offenbar darf für einen Thread in diesem Zustand (der sich auch für die "Teilnahme" registrieren muß) dann keine TLB-Exception (translation lookaside buffer) auftreten, das wäre z.B. der Fall, wenn ein Zugriff auf eine Adresse erfolgen soll, deren Inhalt/Seite sich gerade nicht im Speicher befindet und erst einzulagern wäre.

Damit ist aber auch (denke ich zumindest) klar, daß eigene Module in die Liste nicht aufgenommen werden müssen, es sein denn, sie verwenden ebenfalls die Funktionen aus arch/mips/kernel/yield_context.c (yield_to_...) - das ist zunächst nicht einmal in irgendwelchen OpenSource-Modulen der Fall, selbst wenn sie vielleicht bei AVM in der Liste stehen - also wird das wohl in diversen AVM-LKM seinen Niederschlag finden. Allerdings könnte ein LKM, das von einem Kernel-Thread benötigt wird, der auch AVM-Module benutzt, dann doch zu einem Problem werden ... aber ich tippe(! - mehr ist das erst einmal nicht) darauf, daß die TLB-Exceptions eher bei einem Kontextwechsel ein Problem sind (und dann sicherlich auch nur für den neuen aktiven Kontext) und da sich eigene Module wohl nicht als Ziel so eines Wechsels (bei einer IRQ-Behandlung) registrieren (es sei denn, sie unterstützen wirklich eigene Hardware und zwar "low level" - schon der USB-Stack dazwischen ist dann nicht mehr das, was ich da meine), wird das wohl weiterhin klappen. Allerdings berücksichtigte der alte Mechanismus (10 Minuten nach dem Start wurden alle Module zusammengerechnet) tatsächlich auch den Platzbedarf von Modulen, die über Freetz hinzukamen.

Lange Rede, kurzer Sinn: Probieren ... ich glaube nicht, daß es notwendig ist - bei unerklärlichen Neustarts (so eine "race condition" führt sicherlich zur "kernel panic") kann man dann immer noch über eine andere Lösung nachdenken.

Ein Problem könnte allerdings auch ohne dedizierten Swap-Space auftreten (das ist also m.E. auch keine Gegenmaßnahme), denn eine "purged page" mit r/o-Daten könnte auch schon mal auftreten, wenn es enger im Speicher wird und eine lange nicht genutzte Seite Platz machen mußte, weil sie der Theorie nach ja einfach erneut aus dem Filesystem geladen werden kann, wenn sie benötigt wird (kann ja bei r/o nicht geändert sein) - keine Ahnung, was das Memory-Management da wirklich macht und wie weit man das abstellen/beeinflussen könnte über "sysctl" o.ä. - bei AVM laufen auch viele Dateizugriffe außerhalb des Ladens zusätzlicher Libraries über "memory-mapped files" und es wird schon seinen Grund haben, warum man diese Module lieber in den KSEG0-Bereich laden will. Das ist ja Speicher, der "unmapped" ist (also 1:1 dem physikalischen Speicher entspricht, damit auch ohne MMU nicht ausgelagert werden kann), aber trotzdem beim Caching berücksichtigt wird.

EDIT: Der Kommentar in der AVM-Meldung "printk(KERN_ERR "[module-alloc-by-name] warning 'modulemem' used - depreciated\n");" ist ja fast etwas für die "Schmunzelecke" ... was so ein einzelnes "i" doch für Schaden anrichten kann (auch wenn das kein "copied from" ist). :mrgreen:

- - - Aktualisiert - - -

Wenn ich das jetzt auf die Schnelle richtig gesehen habe, prüft der Loader-Code beim Laden eines LKM auch, ob das in der Liste für KSEG0 steht und lädt es nur dann dorthin ... auch ein paar AVM-Module werden explizit davon ausgenommen, siehe "blacklist" im Code.
 
Zuletzt bearbeitet:
Ich würde ohnehin schauen, ob nicht das Ergebnis der Übersetzung der Gerätebeschreibung schon mit dem Inhalt im Kernel übereinstimmt (die haben einen eigenen "Compiler") ... daß ich die dann doch noch irgendwo unterhalb von "arch" gefunden hatte, habe ich irgendwo geschrieben.

Der eigentliche BLOB mit dem FDT sollte auch unabhängig von einer Adresse im Speicher sein, der könnte dann tatsächlich wieder 1:1 binär übernommen werden - zumindest mal so aus dem Bauch heraus. Meines Erachtens fehlt eben im AVM-Code genau der Teil, der aus den fertig kompilierten Modulen die Liste erzeugt, die Versions-Info zusammenstellt und das ganze - zusammen mit den notwendigen FDTs, wenn das mehr als eine SubRevision ist - so an die Adresse __avm_kernel_config_start schafft, daß der fertige Kernel dort die notwendigen Pointer enthält.

Wenn man sich den Kernel an der Stelle anschaut in der arch/mips/kernel/vmlinux.lds.S, dann steht da folgendes:
Code:
        /* writeable */
        __data_start = .;
        .data : {       /* Data */
                . = . + DATAOFFSET;             /* for CONFIG_MAPPED_KERNEL */

                INIT_TASK_DATA(THREAD_SIZE)
                NOSAVE_DATA
                CACHELINE_ALIGNED_DATA(1 << CONFIG_MIPS_L1_CACHE_SHIFT)
                READ_MOSTLY_DATA(1 << CONFIG_MIPS_L1_CACHE_SHIFT)
                DATA_DATA
                CONSTRUCTORS
                . = ALIGN(4 * 1024);
[COLOR="#FF0000"]                __avm_kernel_config_start = .;
[COLOR="#008000"]                . += 64 * 1024;[/COLOR]
                __avm_kernel_config_end = .;
[/COLOR]        }
Zwischen diesen beiden Symbolen liegt ja nun der komplette Config-Bereich (hier halt lediglich als Reservierung von 64 KB) ... ich würde gar nicht viel Aufhebens machen und einfach (steht irgendwo oben) an die Stelle der Reservierung ein ".include" für das generierte File setzen ... das müßte natürlich dann dafür sorgen, daß am Ende des generierten Files das Alignment wieder so gesetzt ist, daß die Definition von "..._end" wieder genau 64KB hinter "..._start" liegt. Das würde jegliches Jonglieren mit irgendwelchen Linker-Definitionen vermeiden ... wenn ich nichts wesentliches übersehen habe.

Außerdem sehe ich ehrlich gesagt nur in Assembler auch wirklich die Chance, die erwähnte 1:1-Kopie des Inhalts zu erzeugen ... auch die LKM-Namen stehen ja bei AVM in einem geschlossenen Array von Strings, wobei wohl jede an einer 32-Bit-Boundary beginnt und die davor dann eben bis dorthin mit NUL(len) aufgefüllt wird. Den konkreten Namen muß man ja gar nicht wissen (kann man praktisch "durchnummerieren"), man muß halt nur jeweils die passende Adresse (bzw. den richtigen Namen) für die Pointer hernehmen.

Ich hatte zwar mal etwas von "C" geschrieben ... aber das dann auch wieder auf "Assembler" korrigiert. Daß ich das als C-Strukturen beschrieben habe im Text, war nur der Vermutung geschuldet, daß mehr Leute C verstehen als sich mit Assembler auskennen. Mir geht es ja ähnlich ... für konkrete Instruktionen oder Direktiven muß ich (beim Schreiben) auch immer erst eine Suchmaschine bemühen.
 
Hallo Peter,


nur um sicherzustellen, dass ich Dich richtig verstehe.


Die Idee/Hoffnung an der Stelle ist, dass man anhand der mittels devmem ausgelesenen Byte-Sequenzen das entsprechende BLOB-Fragment im (entpackten) kernel.image aus der originalen Firmware identifiziert (sfk hexfind oder whatever), dieses dann rausschneidet, in eine char-Array-Definition umwandelt und dann beim Übersetzen des eigenen Kernel einbindet. Richtig?


Ich habe nicht versucht, Dir bis ins letzte Detail bei der Analyse des AVM-Kernel-Quellcodes zu folgen. Wozu werden nochmal diese Strukturen benötigt? Kann man nicht die Funktionen, die auf diese zugreifen, irgendwie "umbiegen", i.e. immer true/false/null? Sollte diese Alternative schon mal in Betracht gezogen worden sein, so bitte ich um Entschuldigung.


Danke!


VG, Gene
 
Ich befürchte ein Mißverständnis.

Bei einem 3.10-Kernel kann man (oder sollte man meiner Meinung nach, das wäre die Antwort auf die Überlegung, die Module-Liste wegzulassen) den Teil mit der Reservierung von KSEG0-Speicher für einige Module nicht einfach ignorieren (aber ich habe mir in "module_alloc..." auch nicht bis ins letzte Detail angesehen, was beim Fehlen der Liste passiert) und somit müßte/sollte man sowohl die AVM-Strukturen für diese Module-Liste als auch den BLOB für den FDT (flattened device tree) so nachbilden, daß die originale AVM-Funktion "init_avm_kernel_config()" damit umgehen kann. Ob man dann noch die Versions-Info einbezieht oder nicht und welche Auswirkungen ein "oder nicht" hat, wäre eine weitere Überlegung wert, wobei das nun der simpelste Teil der Generierung wäre.

Das Ergebnis einer Übersetzung der Gerätebeschreibung (das sollte so ein FDT-BLOB sein) hatte ich ja dann noch gefunden (irgendwo weit vorne vor langer Zeit), nur dessen Einbindung fehlt m.E. in den AVM-Quellen - natürlich genauso wie jeglicher Code und jegliches Skript, der/das so eine "Module-Liste" wie im AVM-Kernel erzeugen würde.

Das ist ja am Ende auch der Punkt, wo ich den von mir behaupteten GPL-Verstoß von AVM "festmache" (es läßt sich kein (funktionell) identischer Kernel bauen) - auch wenn ich das immer wieder darauf einschränke, daß ich mich ja auch irren könnte. Wenn sich jemand (nachvollziehbar) meiner These anschließen sollte auf der Basis eigener Feststellungen, würde ich auch dieses "vermutlich" sicherlich irgendwann mal über Bord werfen ... ich mag es bloß nicht, erst groß aufzulaufen mit (falschen) Behauptungen und hinterher kleinlaut zuzugeben, daß ich mich geirrt habe. Das kommt auch so schon oft genug vor und insofern stelle ich meine eigenen Behauptungen/Schlußfolgerungen hier lieber immer wieder unter den Vorbehalt einer unabhängigen Prüfung durch jemand anderen.

Die Hoffnung, die Daten auch in einem "offline kernel" zu finden, hege ich (zumindest erst einmal) jedenfalls nicht (#284, drittletzter Absatz) ... die theoretische Überlegung, daß da anstelle der Pointer (im Hauptspeicher) dann in einem Offline-Kernel (entpackt) eben entsprechende Relocation-Offsets stehen, ist zwar auch nicht von der Hand zu weisen, aber ich würde erst einmal den ersten Schritt gehen und das Erstellen einer passenden Datei für ein Modell und eine OS-Version funktionsfähig umsetzen (und testen), bevor man sich einen Plan zurechtlegt, wie man das im Build-Prozess dann automatisieren könnte.

- - - Aktualisiert - - -

Wobei ich gerade noch einmal (durch die Frage, was man weglassen oder anders machen könnte, inspiriert) überlegt habe, ob man nicht auch einfach die AVM-Funktion so ändern könnte, daß nur der BLOB für HWSubRevision 0 mit seiner Adresse an der richtigen Stelle (also als erster Eintrag in avm_kernel_config_device_tree) liegt (und den Rest einfach - zumindest testweise - ausläßt).

Das geht aber spätestens dann schief, wenn AVM mal eine wirklich abweichende Hardware-Konfiguration (mit anderer Gerätebeschreibung für eine neue SubRevision) hat ... wie das bei anderen Modellen als der 7490 aussieht, weiß ich nicht - genauso wenig, ob es Modelle gibt, wo die anderen Typen in "tags" (in erster Linie die avmnet-Konfiguration) dann wirklich Verwendung finden.

- - - Aktualisiert - - -

Es muß ja bei AVM auch irgendeinen Punkt geben, der tatsächlich die richtige dtb-Datei für das passende Gerät auswählt und einbindet.

In arch/mips/boot/dts/Makefile werden ja erst einmal alle dts-Dateien für VR9 (anhand von "CONFIG_VR9") übersetzt in die dtb-Dateien.

Da fehlt in Freetz vermutlich auch das Traversieren der Directories (oder zumindest dieses einen) beim "make", wenigstens solange man ohne "replace kernel"-Option übersetzt, da werden die dtb-Files jedenfalls nicht erstellt.

Auf der Basis irgendeiner (m.E. fehlenden) Definitionsdatei beim Build wird dann sicherlich das richtige dtb-File ausgewählt (das können auch mehrere sein, wenn es unterschiedliche HWSubRevisionen gibt) und zusammen mit der Module-Liste und der Versionsinfo so in ein Object-File übersetzt, daß der Linker es als Overlay ab der Adresse __avm_kernel_config_start einbindet.

Anders kann ich mir das Vorhandensein eines Pointers direkt an der Adresse des Symbols (der ja auch im Code erst einmal in eine definierte statische Variable "avm_kernel_config" kopiert wird - in include/linux/avm_kernel_config.h als inline-Funktion nachzulesen) nicht wirklich erklären ... vielleicht gibt es ja einen passenden Guru hier, der mir auf die Sprünge hilft, welche Alternativen da noch in Frage kämen, wenn man mal unterstellt, daß das Assembler-File (arch/mips/kernel/vmlinux.lds.S) dasselbe ist, welches AVM auch verwendet.

Es kann aber auch sein, daß AVM das beim (internen) Build mit einem überschreibt, welches ähnlich arbeitet, wie ich das für das Generieren der Datei oben vorgeschlagen habe ... so eine String-Tabelle, wo jeder Eintrag an einer 32-Bit-Boundary "aligned" ist, sieht schon verdammt mehr nach Assembler aus (".asciz <string>" gefolgt von einem ".align 2") als nach C, jedenfalls für mein Empfinden.

Bei der 7580 würde ich ja dann sogar wieder darauf tippen, daß tatsächlich der Bootloader schon den passenden Pointer auf einen FDT an den Kernel übergibt ... da wird es vermutlich in "init_avm_kernel_config()" gar keine Pointer für "device trees" mehr geben zum Kopieren.

EDIT: Ich habe gerade noch einmal nachgesehen ... es kann ja sogar sein, daß das Alignment an einer 8-Byte-Grenze erfolgt (also .align 3), ist mir vorher gar nicht so aufgefallen (nur daß es nach dem ersten NUL-Byte noch weitere gab und damit irgendein Alignment). Auf den Unterschied ".align 3" und ".align 8" in Abhängigkeit von Plattform und Format pfeife ich hier mal, ich meine mit ".align 3" die nächste Adresse, deren niederwertigste drei Bits Null sind.
 
Zuletzt bearbeitet:
Das dachte ich bereits geschrieben zu haben ... wenn Du die Daten 1:1 ausliest und wegen einer abweichenden Kernel-Konfiguration ändert sich die Adresse, an der __avm_kernel_config_start liegt (die ist ja relativ und nicht absolut), stimmt natürlich auch kein einziger Pointer innerhalb dieses Bereiches.

Das Alignment habe ich daraus abgeleitet, daß bei mir in #284 (10. Code-Kasten) die Pointer auf die Module-Namen alle an einer durch 4 teilbaren Adresse stehen ... und da hast Du dann insofern recht, als der Blick auf Deinen zweiten Code-Kasten in #285 (wo offenbar in der ersten Spalte dann nicht die String-Pointer stehen, sondern die Adresse des Listeneintrags - ich hatte keine Lust, mich durch das Skript danach zu kämpfen um es zu verstehen) mich zu der falschen(!) Annahme verleitete, das wären 8-Byte-Boundaries ... man sollte sich eben doch besser auf die eigenen Ergebnisse stützen, wo man dann auch weiß, was sie bedeuten.

Wenn ein Module-Name 8 oder 9 Zeichen lang ist und der nächste erst 12 Byte später in der Liste steht, dann nenne ich das ein Alignment - und genau das war in meinem Beitrag #284 bei den drei "devmem"-Aufrufen für den Namen "athlogger" zu sehen (11. Kasten). Wenn der nächste Name direkt nach der einzelnen NUL beginnen würde, hätte der letzte gelesene Wert in den niederwertigen Bytes keine Nullen - nur das zweite Byte (Bits 16-23 "von hinten") wäre dann das "String-Ende" mit einer einzelnen NUL (das mit dem einen "L" ist Absicht, so heißt das Zeichen in der ASCII-Tabelle ... nur falls sich jemand wundern sollte; das ist etwas anderes als NULL - zumindest unter Programmieraspekten).
 
Und wieso liegen diese Strings jetzt Deiner Meinung nach in dem Bereich zwischen __avm_kernel_config_start und __avm_kernel_config_end?
Code:
$ grep kernel_config /proc/kallsyms
8000f640 T init_avm_kernel_config
80848000 D __avm_kernel_config_start
80858000 D __avm_kernel_config_end
808a2080 B avm_kernel_config_device_tree
808a20b0 B avm_kernel_config
Der ist bei mir (113.06.69-40766) bei 0x80858000 beendet und 0x08758xxx liegt irgendwie dahinter. Daß sich normaler C-Code ja genau nicht um Alignments kümmern würde für seine statischen Strings und ich deshalb bei diesem Bereich eher auf Assembler tippe, habe ich ja gerade in #290 im drittletzten Absatz geschrieben.

Ich verstehe gerade nicht so richtig, was Du mir sagen willst ... :gruebel:
 
Zuletzt bearbeitet:
opto schrieb:
8758316 dec = 85A449 hex
passt also so wie ich schrieb.
Ich weiß nicht, wer bei Dir die Umrechnungen macht ... bei mir ist 8758316 zur Basis 10 gleich 85A42C zur Basis 16 - sagt zumindest mein "Calculator" unter Windows und bei derart großen Zahlen benutze ich den anstelle des "Kopfrechners". Schon die einfachen Regeln für das Kopfrechnen sagen aber, daß eine ungerade Zahl (die in Hex auf 9 endet) nicht in dezimaler Darstellung auf 6 enden kann. Das "2C" im letzten Byte bei korrekter Umrechnung ist dann auch wieder eine integrale Grenze.

Ach so ... bitte tu mir einen Gefallen und mache künfig deutlich, ob Du Dich auf dezimale oder hexadezimale Zahlen beziehst. Ich habe auf Anhieb nicht erkennen können, daß es sich bei dem "@ irgendwas" um dezimale Werte handeln sollte ... das ist beim Hantieren mit Speicheradressen schon recht ungewöhnlich (und gewöhnungsbedürftig).

Wenn bei Dir der Bereich schon an einer anderen Adresse liegt (0x80859000 vs. 0x80848000), dann illustriert das doch gerade das, was ich mit dem "relative vs. absolute Adresse" sagen wollte ... bei Dir ist dann sicherlich das erste 32-Bit-Wort in diesem Bereich auch 0x80859010 - das ist doch kein Pointer auf die Liste mehr, wenn diese (wie bei mir) an 0x80848010 liegt.

Wie soll das also beim 1:1-Kopieren (binär) funktionieren ... es spielt doch keine Rolle, ob es Verweise nach außerhalb gibt, wenn schon die Pointer innerhalb nicht mehr stimmen, sowie der Block an einer anderen Adresse beginnt.

Meine Ratlosigkeit nimmt eher zu ...

- - - Aktualisiert - - -

Und nochmal ... ich weiß auch nicht, was ansonsten noch so in dem Block landet (außerhalb von AVM wahrscheinlich auch ansonsten niemand) - meine Bemerkung mit dem "alignment" für die String-Tabelle bezog sich deutlich nur auf die Namen der Module ... andere Strings (zumindest als Pointer) gibt es in den drei Typen, die ich untersucht habe für meinen Beitrag oben, erst einmal nicht ... in der Versions-Info sind das ja keine Pointer (auch wenn ich mich zuerst auch irritieren ließ) und selbst in den dort definierten 192 Byte beginnt jeder (Teil-)String wieder an einer integralen Grenze (bei mir sogar an einer 8-Byte-Grenze).

Es muß auch noch irgendwelche anderen Daten in diesem Bereich geben, deshalb ist das mit dem Erstellen einer eigenen Kopie (auch mit relativer Adressierung) noch nicht zwingend automatisch auch ein funktionierendes System ... eigentlich müßte man mal mit einem passenden Skript einen (Hex-)Dump des gesamten Bereiches machen, damit man mal eine Vorstellung kriegt, was AVM da noch so alles untergebracht hat (und welche Daten ansonsten noch fehlen könnten). Wenn da noch weitere Daten aus der Initialisierung liegen, für die ebenfalls die Basis fehlt, dann wird das sicherlich das nächste Problem ... aber dazu braucht man erst einmal das passende "mapping", wie weit die bisher bekannten Daten diesen Bereich schon ausfüllen würden.

Ich habe noch nicht probiert, bei einer FRITZ!Box den gesamten Inhalt von /dev/mem in eine Datei zu kopieren ... das wäre aber vermutlich einfacher (vielleicht klappt das sogar mit "dd") als es Byte für Byte mit "devmem" auszulesen. Das Problem ist in aller Regel die Notwendigkeit, den richtigen Bereich zu mappen. Normalerweise arbeiten solche Programme so (weil sie ja außerhalb von KSEG0 laufen, wenn sie im Userspace unterwegs sind), daß sie /dev/mem ganz normal als Datei öffnen und dann den fraglichen Speicherbereich über "mmap()" in ihren Userspace mappen lassen. Das sähe dann in etwa so aus:
Code:
// map kernel memory page(s) to user space memory
void * mapKernelMemory(int fileDescriptor, void * realAddr, size_t size, size_t * mappedSize)
{
        void *          virtualAddr;
        unsigned int    pageSize;
        off_t           pageStart;
        unsigned int    pageCount = 1;
        int             mapSize = (int) size;
        off_t           offsetInPage;
        unsigned int    offsetMask;

        DBG2("Map %d bytes at real address %8p\n", size, realAddr)
        pageSize = getpagesize();
        offsetMask = pageSize - 1;
        offsetInPage = (off_t) ((int) realAddr & offsetMask);
        pageStart = (off_t) ((int) realAddr - offsetInPage);
        if (offsetInPage + size > pageSize) {
                while (mapSize > 0)
                {
                        pageCount++;
                        mapSize -= pageSize;
                }
        }
        *mappedSize = pageCount * pageSize;
        virtualAddr = mmap(NULL, *mappedSize, PROT_READ, MAP_SHARED, fileDescriptor, pageStart);
        DBG3("Mapped %d bytes from 0x%08X to address %8p\n", (int)mappedSize, (unsigned int)pageStart, virtualAddr)
        return virtualAddr;
}

// unmap mapped region
int unmapKernelMemory(void * virtualAddr, size_t size)
{
        DBG2("Unmap %d bytes from %8p\n", (int)size, virtualAddr)
        return munmap(virtualAddr, size);
}
Auch für das Auslesen der notwendigen Start-Adresse aus den Symbolen habe ich in einem "ollen" Quelltext die passende Funktion bei mir gefunden:
Code:
// convert kernel symbol address from text
void * parseHexPointer(char * addr)
{
        void *          ptr = 0;
        int             i = 0;
        char            c;
        unsigned        val;

        DBG2("Parse pointer at %8p, value 0x%08X\n", addr, (unsigned int)*addr)
        if (*addr) {
                c = *addr;
                addr++;
                while (c && i < 8) {
                        if (c >= '0' && c <= '9') {
                                val = (int) (c - '0');
                        }
                        else {
                                if (c >= 'a' && c <= 'f') {
                                        val = (unsigned) (c - 'a') + 10;
                                }
                                else {
                                        if (c >= 'A' && c <= 'F') {
                                                val = (unsigned) (c - 'A') + 10;
                                        }
                                        else {
                                                return INVADDR;
                                        }
                                }
                        }
                        ptr = (void *)(((int) ptr << 4) | val);
                        i++;
                        c = *addr;
                        addr++;
                }
                if (c) {
                        return INVADDR;
                }
                DBG1("Returned value %8p\n", ptr)
                return ptr;
        }
        else {
                DBG0("Returned INVADDR\n")
                return INVADDR;
        }

}

char * nextToken(char ** nextStart)
{
        char *          start = *nextStart;
        char *          token;

        if (*start != 0) {
                while (*start != 0 && (*start == ' ' || *start == '\t' || *start == '\n')) {
                        start++;
                }
                if (*start != 0) {
                        token = start;
                        while (*start != 0 && *start != ' ' && *start != '\t' && *start != '\n') {
                                start++;
                        }
                        if (*start != 0) {
                                *start++ = 0;
                                *nextStart = start;
                        }
                        return token;
                }
                else {
                        *nextStart = start;
                        return NULL;
                }
        }
        else {
                return NULL;
        }
}

void * readKernelSymbol(const char * symName)
{
        void *          symAddr = INVADDR;
        FILE *          symFile;
        char *          line = NULL;
        char *          ptr;
        size_t          bufSize = 0;
        ssize_t         read;
        char *          symbolAddr;
        char *          symbolType;
        char *          symbolName;

        symFile = fopen(symbolProcFS, "r");
        if (symFile != NULL)
        {
                bufSize = 256;
                line = malloc(bufSize);
                while ((read = getline(&line, &bufSize, symFile)) != -1) {
                        symbolAddr = line;
                        ptr = line;
                        symbolAddr = nextToken(&ptr);
                        symbolType = nextToken(&ptr);
                        symbolName = nextToken(&ptr);
                        if (symbolName != NULL) {
                                if (strcmp(symbolName, symName) == 0) {
                                        if (*symbolType == 'B') {
                                                symAddr = parseHexPointer(symbolAddr);
                                                DBG1("avm_current_hw_config found at %8p\n", symAddr)
                                                break;
                                        }
                                }
                        }
                }
                fclose(symFile);
        }
        return symAddr;
}
Das könnte man dann in etwa so zusammenfügen:
Code:
[...]
        kernelAddr = readKernelSymbol(currentConfig);
        if (kernelAddr != INVADDR) {
                realAddr = KSEG0(kernelAddr);
                mfd = open(memoryDevice, O_RDONLY | O_SYNC);
                if (mfd != -1) {
                        pageSize = getpagesize();
[ ... hier kommt ein bißchen Code, der sich darum kümmert, daß die gesuchten Daten ggf. über mehr als eine Speicherseite gehen könnten - mein originaler Code sucht einzelne (kürzere) Strukturen in einem Array ... ]
                                entriesAddr = mapKernelMemory(mfd, realAddr, entries * sizeof(entry_t), &entriesMappedSize);
                                if (entriesAddr != MAP_FAILED) {
                                        entry = (entry_t *) (KSEG0(entriesAddr) + offsetStart);
[...]
                if (entriesAddr != MAP_FAILED) {
                        unmapKernelMemory(entriesAddr, entriesMappedSize);
                }
                close(mfd);
[...]

Warum schreibe ich das eigentlich jetzt, wenn ich doch zuerst das "devmem"-Applet angeführt habe? Weil das nur eine Möglichkeit war und man in meinen Augen mit dem Klammerbeutel gepudert sein müßte, wenn man das komplette Auslesen der fraglichen 64 KB aus einem laufenden Kernel immer schön in 4-Byte-Schritten mit diesem Applet machen würde. Da ist dann ein "richtiges" Programm doch wieder die bessere Lösung.

Wenn es mit "dd" klappen sollte (vielleicht sogar mit entsprechender Positionierung mit "skip"-Parameter), dann braucht es das eigene Programm natürlich auch nicht - einen Versuch ist es vermutlich wert.
 
Holen Sie sich 3CX - völlig kostenlos!
Verbinden Sie Ihr Team und Ihre Kunden Telefonie Livechat Videokonferenzen

Gehostet oder selbst-verwaltet. Für bis zu 10 Nutzer dauerhaft kostenlos. Keine Kreditkartendetails erforderlich. Ohne Risiko testen.

3CX
Für diese E-Mail-Adresse besteht bereits ein 3CX-Konto. Sie werden zum Kundenportal weitergeleitet, wo Sie sich anmelden oder Ihr Passwort zurücksetzen können, falls Sie dieses vergessen haben.