Flattened Device Tree generálás

Fórumok

Üdv,

Van itt olyanvalaki, aki jártas az FDT (.dtb) generálásban? Próbálom kihámozni a specifikációból, de egyre inkább az az érzésem, hogy ez egy förtelem.

Specifikáció itt (PDF), ez próbálom összevetni ezzel: linux dts. Ebben ilyent látok:

	soc {
		/*
		 * Defined ranges:
		 *   Common BCM283x peripherals
		 *   BCM2711-specific peripherals
		 *   ARM-local peripherals
		 */
		ranges = <0x7e000000  0x0 0xfe000000  0x01800000>,
			 <0x7c000000  0x0 0xfc000000  0x02000000>,
			 <0x40000000  0x0 0xff800000  0x00800000>;
		/* Emulate a contiguous 30-bit address range for DMA */
		dma-ranges = <0xc0000000  0x0 0x00000000  0x40000000>;

		/*
		 * This node is the provider for the enable-method for
		 * bringing up secondary cores.
		 */
		local_intc: interrupt-controller@40000000 {
			compatible = "brcm,bcm2836-l1-intc";
			reg = <0x40000000 0x100>;
		};

Első számú kérdés, a spec-ben az áll:

2.3.5 #address-cells and #size-cells
...
The #address-cells and #size-cells properties are not inherited from ancestors in the devicetree. They shall be explicitly defined.

Hát a fenti példában ez nyilvánvalóan nem így van. Nincs egyik "-cells" property se, szóval szinte biztos, hogy a szülőtől kellene örökíteni, szemben azzal, amit a spec állít, nem?

A másik kérdés a "ranges" és "dma-ranges" property-kre vonatkozik, na itt aztán végképp se füle, se farka az egésznek. A spec szerint:

2.3.8 ranges
...
The format of the value of the ranges property is an arbitrary number of triplets of (child-bus-address, parent-bus-address, length)

Illetve

2.3.9 dma-ranges
...
The format of the value of the dma-ranges property is an arbitrary number of triplets of (child-bus-address, parent-bus-address, length).
...
The child-bus-address is a physical address within the child bus’ address space. The number of cells to represent the address depends on the bus and can be determined from the #address-cells of this node (the node in which the dma-ranges property appears).

The parent-bus-address is a physical address within the parent bus’ address space. The number of cells to represent the parent address is bus dependent and can be determined from the #address-cells property of the node that defines the parent’s address space.

Namost a "ranges"-ben eleve négyesek vannak, ami sehogy sem jön ki, még akkor se, ha az "#address-cells" 2 és "#size-cells" 1 (és egyébként is, ha igaz, hogy öröklődnek ezek, akkor az "interrupt-controller@40000000" alnode-beli "reg"-nél hogy lehet az "#address-cells" és a "#size-cells" is egyértelműen 1?). Ugyancsak a "dma-ranges"-nél, ott sem hármasok vannak, hanem négyesek. Ez mégis hogy?

Nincs is külön "#address-cells" a node-on, mint ahogy a specifikáció állítása szerint kéne lennie. És különben is, ha az "#address-cells" 2 a "#size-cells" meg 0, akkor is hogy kezdődhetne a számsor a címmel, hisz ez esetben két u32 érték a cím (és a DTB big-endian), szóval az nem 0x0 0x7e000000 lenne? Ha pedig az "#address-cells" és a "#size-cells" is 1, akkor a méret hogy lehetne már 0 és hogy lesz a két cím egy hossz elemből négyes?

És végül az értékek, na ezek sem jönnek ki sehogy se. A specifikáció szerint az alnode-okban a "reg"-beli címek a "ranges"-hez képesti relatív címek.

The ranges property provides a means of defining a mapping or translation between the address space of the bus (the child address space) and the address space of the bus node’s parent (the parent address space).

Namost ez sem jön ki sehogy sem, mert a "interrupt-controller@40000000" node-ban a "reg" propertyben egyértelműen "parent address space"-ben van direktben a cím, és ha hozzáadnám a 0xff800000-át (ugye az utolsó nem-éppen-triplet vonatkozik erre a child címre), akkor hibás eredmény jönne ki.

Hozzászólások

Érdekesség: hogy mondják ezt FDT-ül? Diszasszembláltam egy dtb-t (dtc -I dtb -O dts -o test.dts bcm2711-rpi-4-b.dtb), és így már sokkal több értelme van az egésznek:

        soc {
                compatible = "simple-bus";
                #address-cells = <0x01>;
                #size-cells = <0x01>;
                ranges = <0x7e000000 0x00 0xfe000000 0x1800000 0x7c000000 0x00 0xfc000000 0x2000000 0x40000000 0x00 0xff800000 0x800000>;
                dma-ranges = <0xc0000000 0x00 0x00 0x40000000 0x7c000000 0x00 0xfc000000 0x3800000>;
                phandle = <0x5c>;

Amint látható, ide már bekerültek a "-cells" property-k, és így már van is értelme. Ha feltesszük, hogy a szülő node-nál a cím 2 darab u32, a "soc" esetén meg egyértelműen meg van adva, hogy 1, na így tök jól kijön: child-address (1), parent-address (2), size (1), azaz hármasonként összesen 4 darab u32.

Most már csak az a kérdés, hogy a címfordítások hogyan állnak össze. Feltételezésem az, hogy ha az address >= child-address && address < child-address + size, akkor a végső címnek address - child-address + parent-address-nek kéne lennie. Már ha jól értem a specifikációt. A gond az, hogy nem jön ki így a matek, nem az a cím lesz, ami a periféria doksijában szerepel (az ugyanis a 0x40000000). Erre valakinek tippje van esetleg?

Feltételezésem az, hogy ha az address >= child-address && address < child-address + size, akkor a végső címnek address - child-address + parent-address-nek kéne lennie. Már ha jól értem a specifikációt.

Szerintem jól érted.

A gond az, hogy nem jön ki így a matek, nem az a cím lesz, ami a periféria doksijában szerepel (az ugyanis a 0x40000000).

Az én értelmezésem:

A "soc" doksija a "soc" saját címterét tudja leírni; abban pedig a "local_intc" a 0x40000000 címen található.

A "/" felől címezve a "local_intc" bizonyára a 0x40000000-0x40000000+0xff800000=0xff800000 címen érhető el, erről viszont a "soc" (ill. a "soc" dokumentációja) nem tud (nem is tudhat) semmit. Nem tudhatja, hogy milyen buszra, milyen címtérbe lesz beültetve.

A "/" felől címezve a "local_intc" bizonyára a 0x40000000-0x40000000+0xff800000=0xff800000 címen érhető el,

Valószínű, de nem értem, ez hogy jön a Linux kernelhez, amikor az ARM-on fut, tehát neki a "soc"-os címek kellenek. Ez honnan derül ki? Mármint az a program, ami felhasználja a dtb-t, az honnan tudja, hogy ennek a fának melyik pontján is kell(ene) bekapcsolódnia a mókába? Merhogy ezek szerint nem a "/"-nál, mint ahogy eddig feltételeztem.

erről viszont a "soc" (ill. a "soc" dokumentációja) nem tud (nem is tudhat) semmit.

Ennek szerintem is így kéne lennie, és így van értelme, csakhogy a címfordítás leképezés a "soc" alatti paraméter (lásd fenti példámban). Szóval az nem egészen világos, hogy akkor mikor, a fa melyik pontján bekapcsolódva kell egy címteret fordítani vagy nem fordítani (és hát hogy honnan kéne tudni, hogy hol kell bekapcsolódni).

A specifikációban semmi ilyesmiről nem írnak, de feltehető, hogy a valódi gyökér node neve mindig "soc"? A "simple-bus" gondolom nem mérvadó, más megkülönböztetője meg nincs.

Mármint az a program, ami felhasználja a dtb-t, az honnan tudja, hogy ennek a fának melyik pontján is kell(ene) bekapcsolódnia a mókába?

Szerintem a gyökérnél. A CPU-nak azokat a címeket kell elérnie.

Azt el tudom képzelni, hogy a mélyebben lévő címfordítást be kell programozni a busz hierarchiába (vagy esetleg eleve a hardver adottsága), de a CPU felől a /-nél kell belépni szerintem.

Merhogy ezek szerint nem a "/"-nál, mint ahogy eddig feltételeztem.

Bocsánat, az nem világos számomra, hogy pontosan mi mond ellent annak, hogy a /-nél kellene belépni.

de a CPU felől a /-nél kell belépni szerintem.

Én is így gondoltam, de nem.

Bocsánat, az nem világos számomra, hogy pontosan mi mond ellent annak, hogy a /-nél kellene belépni.

Hát az, hogyha a fogom a "reg"-beli értéket, és végigmegyek a pathon visszafele, útközben konvertálva a "ranges" alapján, akkor ha ezt a "soc"-nál fejezem be, akkor pont azt a címet kapom, amin az ARM processzor eléri ezt a perifériát. Viszont ha teljesen végigmegyek a láncon egészen a "/"-ig, akkor történik mégegy "ranges" fordítás, aminek eredményeképpen egy olyan cím jön ki, 0xff800000, amin az ARM processzor nem tudja elérni ezt az eszközt (csak a VC processzor). Remélem érthető.

Szóval rosszul fogalmaztam korábban, nem itt lépek be, hanem itt lépek ki, ugyanis a címfordítás során visszafele kell menni a path-on, a "reg"-et tartalmazó child node-tól a parenteken át a gyökérig (vagy eggyel korábbig).

A specifikációban szereplő példa egyébként itt kifejezetten hibás szerintem (16. oldal, 2.3.8 ranges, Address Translation Example). Abban van egy "soc", aminek van egy "ranges" property-je, és van egy child node "serial@4600" néven egy "reg" property-vel. Azt írja This property value specifies that for a 1024 KB range of address space, a child node addressed at physical 0x0 maps to a parent address of physical 0xe0000000. With this mapping, the serial device node can be addressed by a load or store at address 0xe0004600, an offset of 0x4600 (specified in reg) plus the 0xe0000000 mapping specified in ranges. Ugye azt már megfejtettük, hogy ez nem így van, a szülő "soc" továbbra is a 0x0-ás címet használja, a "ranges" csak a "soc" szülőjére vonatkozik. Vagy mégsem? Szerintem de, különben mégkevésbé van értelme a rpi.dtb-nek.

Hát az, hogyha a fogom a "reg"-beli értéket, és végigmegyek a pathon visszafele, útközben konvertálva a "ranges" alapján, akkor ha ezt a "soc"-nál fejezem be, akkor pont azt a címet kapom, amin az ARM processzor eléri ezt a perifériát. Viszont ha teljesen végigmegyek a láncon egészen a "/"-ig, akkor történik mégegy "ranges" fordítás, aminek eredményeképpen egy olyan cím jön ki, 0xff800000, amin az ARM processzor nem tudja elérni ezt az eszközt (csak a VC processzor). Remélem érthető.

Igen, a leírásod most már tökéletesen érthető, köszönöm; ami nem érthető, az a rendszer viselkedése :)

Mit jelent az, hogy VC processzor? Azt el tudom képzelni, hogy a két processzor más címteret lát, csak azt nem tudom, hogy hogyan lehetne egyazon DT-vel leírni a rendszert mindkét (különböző) processzor számára.

Szerk.: vannak kollégáim, akik DT-vel közelebbről foglalkoznak, megkérdezem, mert most már fúrja az oldalam.

Mit jelent az, hogy VC processzor? Azt el tudom képzelni, hogy a két processzor más címteret lát

Az RPi furcsa egy szerzet, mert az ARM csak társprocesszor benne, a fő processzor egy BCM VideoCore GPU. Nem látnak más címteret, hanem a címbusz mérete más (egész pontosan a config.txt gpu_mem direktívájával adható meg, hogy mennyit csípjen le a GPU a címtér tetejéből), a felső részt csak a GPU érheti el, míg az alsót a GPU és az ARM is.

Úgy is fogalmazhatnék, hogyha ARM-on lekéred az ID_AA64MMFR0_EL1 rendszerregiszterből a memória címbuszának méretét bitekben, akkor azt fogod kapni, hogy a 0xff800000 nem egy címezhető memóriatartomány.

vannak kollégáim, akik DT-vel közelebbről foglalkoznak, megkérdezem, mert most már fúrja az oldalam.

Sikerült esetleg beszélni velük?

Sikerült; ma kaptam választ David Gibson-tól (történetesen a kollégám).

Röviden megpróbálom összefoglalni (bármilyen értelmetlenség vagy hiba az én saram, nem az övé).

  • A DTS (device tree source) formátumban az #include direktívák valójában overlay-ként működnek. Tehát ha van egy olyan /soc node-unk egy DTS-ben, amelyből szemre hiányzik az #address-cells=<1> tulajdonság, és van egy olyan /soc node-unk egy másik DTS-ben, amely ezt a tulajdonságot "szolgáltatja", akkor ha #include-dal ezt a két DTS-t egymásra tesszük, akkor a végső /soc node-ban a tulajdonságok össze lesznek fésülve. (Továbbá, ha jól értem, az egymást átfedő tulajdonságok közül az érvényesül, amelyiket a fordító később lát meg.) A konkrét "bcm2711.dtsi" példára David azt mondta, hogy a /soc node-nak az #address-cells=<1> tulajdonsága az #include-olt "bcm283x.dtsi" file-ból érkezik meg.
  • DTB (bináris) szinten (futási időben) szintén van overlay képesség, további kacifántokkal.
  • David azzal egyetértett, hogy ha egy adott, belső "reg"-től a "ranges"-eken keresztül kigyalogolunk a legkülső (/) node-ig, majd az így (többszörösen is) fordított címet nem sikerül elérni a main CPU-ról, akkor az bug. (És az, hogy a /soc-nál megállva a címfordítással, a cím elkezd működni, szintén bug.) Ezzel kapcsolatban három dolgot jegyzett meg:
    • alas some things seem to have become common practice in the ARM dt world that would not be considered at all correct in the traditional OF derived dt world (az OF-en OpenFirmware-t ért)
    • a VideoCore GPU és a main CPU kapcsán azt mondta, hogy különböző CPU-knak különböző nézetei (lényegében különböző root bus-ai) vannak, így különböző DTS-eket kell használni ahhoz, hogy a processzorok szemszögéből leírjuk a rendszert. Vagyis annak nincs értelme, hogy ugyanazt a DT-t nézik a két processzoron futó programok, csak máshol szállnak ki a címfordításból. Hozzátette ugyanakkor: That said, I can see why people might want to have a single dt, so I can see the impetus for abusing this.
    • Az, hogy konkrétan a 0xff800000 cím nem működik, lehet annak a következménye is, hogy mi még mindig csak egy DTSI (#include) file-t vizsgálgatunk, nem pedig a végleges DTS-t (mivel pl. az a végső "board" DTS, amely ezt a DTSI-t fogyasztja, felülbírálhatja a DTSI-ből behozott címfordítást). A végleges DTS vagy úgy nézhető meg, hogy a DTB-t visszafordítjuk DTS-re, vagy úgy, hogy csak a DTS szintű egyesítést kérjük: "dtc -I dts -O dts".

Nagyszerű, nagyon nagyon köszönöm!

A DTS (device tree source) formátumban az #include direktívák valójában overlay-ként működnek.

Igen, erre idöközben rájöttem, de nem is számít, mert én a .dtb-t használom.

DTB (bináris) szinten (futási időben) szintén van overlay képesség, további kacifántokkal.

Ebbe inkább nem is akarok belegondolni, egyelőre nem támogatom ezt.

a VideoCore GPU és a main CPU kapcsán azt mondta, hogy különböző CPU-knak különböző nézetei (lényegében különböző root bus-ai) vannak, így különböző DTS-eket kell használni ahhoz, hogy a processzorok szemszögéből leírjuk a rendszert.

Ez általánosságban igaz, de az RPi egy külön állatfaj, ott nem, mert a VC mappeli az ARM-nek a cuccokat. Egyébként megoldódott ez is, jól fordítottam a címeket, csak rossz doksival vetettem össze, és merő véletlenségből a régiben a buszcím pontosan ugyanaz, mint az újabban az ARM cím, ezért nem tűnt fel egyből.

Szóval nagyon szépen köszönöm a segítséget, úgy tűnik minden megoldódott!

A specifikáció nem hibás, csak hibásan értelmezed.

A példa azt jelenti, hogy: a soc lokális busza (azaz a soc-relatív címek) azok a soc parentjének a 0xe0000000 részén kezdődnek, és 0x00100000 hosszan tartanak.

A serialnál ott is van a reg, a soc-hoz képest 0x4600-on kezdődik, és 0x100 hosszú. Ez ugye globálisan nézve (nem a soc-hoz képest) valóban a 0xe0000000 + 0x4600-on van, hiszen a soc 0-ja az a szülő (a /)  fizikai 0xe0000000-n van.

Azaz /-ről nézve a serial címe 0xe0004600, azaz ezen a címen érhető el a serial.

És a CPU számára mindig ez számít, a globális címtér. Hiszen ő nem tud arról, hogy a címtér egyes elemei milyen perifériákat jelentenek, a CPU nem tudja, hol van a soc a címtéren belül (azt a firmware/memóriavezérlő tudja).

Az egész egy szép hierarchia, és hierarchikusan ki lehet számítani mindennek a címét. Minden cím a /-hez képest fontos, amikor címezni akarod.

amikor az ARM-on fut, tehát neki a "soc"-os címek kellenek.

Nem, ez nem így van. Nem a soc-on belüli címek kellenek neki (nem soc-relatív a címzése), hanem a globális címtér kell neki. A Linux kernelnek fogalma sincs a címtér struktúráról, abszolút címeket fog használni (/-hez képesti címeket), nem a soc mint hardverhez képest relatív címeket.

Most már csak az a kérdés, hogy a címfordítások hogyan állnak össze. Feltételezésem az, hogy ha az address >= child-address && address < child-address + size, akkor a végső címnek address - child-address + parent-address-nek kéne lennie. Már ha jól értem a specifikációt. A gond az, hogy nem jön ki így a matek, nem az a cím lesz, ami a periféria doksijában szerepel (az ugyanis a 0x40000000). Erre valakinek tippje van esetleg?

Alapvetően jól érted. Melyik device címe nem jön ki?

Azta betyár mindenit, most meg egy ilyenbe futottam bele:

                ethernet@7d580000 {
                        compatible = "brcm,bcm2711-genet-v5";
                        reg = <0x00 0x7d580000 0x00 0x10000>;
                        #address-cells = <0x01>;
                        #size-cells = <0x01>;
                        interrupts = <0x00 0x9d 0x04 0x00 0x9e 0x04>;
                        status = "okay";
                        phy-handle = <0x40>;
                        phy-mode = "rgmii-rxid";
                        phandle = <0xe2>;
                
                        mdio@e14 {
                                compatible = "brcm,genet-mdio-v5";
                                reg = <0xe14 0x08>;

Azaz nem elég, hogy az "#address-cells" és "#size-cells" node-onként változhat, de ráadásul még az is számít, hogy hol van a node-on belül definiálva! A fenti példában a "reg" előbb szerepel, ezért hiába van megadva ugyan a node-ban a méret property, arra mégsem a node property-je vonatkozik, hanem a szülő node property-je! Őszintén nem tudom eldönteni, hogy ez a David Gibson gyerek csak töketlen volt-e, vagy pedig látens mazochista...

Komolyan, nincs senki itt a HUP-on, aki foglalkozott volna FDT-vel? Haladok így egymagam is, de sokat segítene egy szakértővel beszélni.

Azaz nem elég, hogy az "#address-cells" és "#size-cells" node-onként változhat, de ráadásul még az is számít, hogy hol van a node-on belül definiálva! A fenti példában a "reg" előbb szerepel, ezért hiába van megadva ugyan a node-ban a méret property, arra mégsem a node property-je vonatkozik, hanem a szülő node property-je!

Ezt szerintem félreértetted. A sorrend nem számít; a scope számít. A specifikáció ezt írja: The #address-cells and #size-cells properties may be used in any device node that has children in the devicetree hierarchy and describes how child device nodes should be addressed.

(Kiemelés tőlem.)

Tehát az "ethernet@7d580000" saját "reg"-jére az az "#address-cells" és "#size-cells" tulajdonságok vonatkoznak, amelyek az "ethernet@7d580000" szülőjében láthatók. Ezt a szülő node-ot nem idézted be, de valószínűleg #address-cells = <2> és #size-cells = <2> szerepel benne. Az "ethernet@7d580000"-en belül látható "#address-cells" és "#size-cells" pedig az "mdio@e14" "reg"-jére vonatkozik. Ez akkor sem változna meg, ha az "ethernet@7d580000" node-on belül a "reg" property-t levinnéd a "#size-cells" alá.

A sorrend nem számít; a scope számít.

Na de hogy? Nem úgy, ahogy a spec írja.

A specifikáció ezt írja: The #address-cells and #size-cells properties may be used in any device node that has children in the devicetree hierarchy and describes how child device nodes should be addressed.

Na ja, meg azt is írja, hogy The #address-cells and #size-cells properties are not inherited from ancestors in the devicetree. E kettő idézet élesen ellentmond egymásnak, pont ebből ered az értetlenségem. Ugyanis ha a gyerekekre vonatkozik, az azt jelentené, hogy megöröklik a szülő node-októl. Vagy a "reg" speciális, ami mindig a közvetlen szülő méreteit használja? Akkor mi a helyzet ez esetben az "#interrupt-cells"-el? Ott hogy van ez (merthogy annak független szülője lehet). Elég kusza, bocs, ha hülyeséget kérdezek!

Ezt a szülő node-ot nem idézted be, de valószínűleg #address-cells = <2> és #size-cells = <2> szerepel benne.

Így van.

Az "ethernet@7d580000"-en belül látható "#address-cells" és "#size-cells" pedig az "mdio@e14" "reg"-jére vonatkozik.

Csakhogy ez nem teljesen igaz. Ellenpéldának ott a fentebb idézett "soc". Ha a "#address-cells" és "#size-cells" a gyerekekre vonatkozna csak, és a saját property-jeire nem, akkor "ranges"-ben hogyan jön ki, hogy már azt kell használni, ami ennél a node-nál van megadva? Elég kusza ez az egész, magyarázat a specben meg nuku, de eltökélt szándékom, hogy kibogozom.

Nagyon köszönöm a segítséget, pár dologra máris rávilágítottál! Egyébként ehhez a projektemhez kell, ACPI AML-t már tök jól feldolgozom benne, és hát álmomban sem gondoltam volna, hogy az FDT-vel több szívás lesz, mint egy bájtkód interpretáló virtuális gép lefejlesztésével...

Hosszas morfondírozás után az alábbit tudom mondani :)

  • Ezt a DT-t: https://github.com/torvalds/linux/blob/master/arch/arm/boot/dts/broadco… én sem értem.
  • A DT spec szerint az #address-cells tulajdonság hatóköre (scope-ja) eltér a "reg" és a "ranges" tulajdonságok között. Amikor a "reg"-et értelmezzük egy node-ban, akkor a szülőben szereplő #address-cells számít. Amikor a "ranges"-t értelmezzük, akkor mind a szülő node-ban, mind ebben a node-ban szereplő #address-cells számít.

"2.3.6 reg": The number of <u32> cells required to specify the address and length are bus-specific and are specified by the #address-cells and #size-cells properties in the parent of the device node.

"2.3.8 ranges": The number of cells to represent the [child-bus-address] is bus dependent and can be determined from the #address-cells of this node [...] The number of cells to represent [parent-bus-address] is bus dependent and can be determined from the #address-cells property of the node that defines the parent’s address space.

Tehát akkor nézzük meg a két "soc"-ot, amit eddig idéztél.

Az első innen van: https://github.com/torvalds/linux/blob/master/arch/arm/boot/dts/broadco…

Részletei:

  • A gyökérben #address-cells = <2> és #size-cells = <1> szerepel. Ennek megfelel az, hogy a "/soc"-ban a "ranges" property-ben a parent-bus-address-ek 64 bitesek.
  • A /soc-on belül a ranges-ben a child-bus-address-ek 32 bitesek. Ehhez az kellene, hogy a /soc node tartalmazzon egy #address-cells = <1> kifejezést. Ilyen kifejezés viszont nincs ott! És a spec szerint egyrészt az #address-cells nem öröklődik, másrészt ha hiányzik, akkor az alapértelmezés <2>. Ez itt nem teljesül.
  • A "/soc/local_intc" node-on belül a "reg" 32 bites. Ez szintén azzal (lenne!) konzisztens, ha a /soc node tartalmazna egy #address-cells = <1> kifejezét.

Tehát itt a DT forrás bugosnak tűnik. Minden arra utal, hogy a /soc-on belül van egy #address-cells = <1> (egy ilyen kifejezés konzisztens lenne DT többi részével), de ez a tulajdonság nincs kiírva.

A másik "soc" példa innen: https://hup.hu/comment/3018232#comment-3018232

Ha jól értem, ennél egyikünk sem lát ellentmondást.

Tehát itt a DT forrás bugosnak tűnik. Minden arra utal, hogy a /soc-on belül van egy #address-cells = <1> (egy ilyen kifejezés konzisztens lenne DT többi részével), de ez a tulajdonság nincs kiírva.

Ugye? Én is erre jutottam. Valahogy indirekt mégis beleértendő, merthogy a dtc által lefordított dtb-be konkrétan mégis valamiért bekerül az az #address-cells = <1>.

A másik "soc" példa innen: https://hup.hu/comment/3018232#comment-3018232

Ez elvileg pontosan ugyanaz fájl, csak a dts-ből csináltam dtb-t, majd visszafordítottam (diszasszembláltam) a dtb-ből dts-be. Eredetileg azért tettem ilyent, mert tele lett a hócipőm a sok include-al, egyetlen fájlban akartam látni az egészet. Nem számítottam rá, hogy megjelenik egy #address-cells = <1> benne, és ezáltal értelmet nyer minden... Egyébként mindegy is, mert én direktben a dtb-t olvasom be, szóval ami abban van, az a mérvadó.

Ez elvileg pontosan ugyanaz fájl, csak a dts-ből csináltam dtb-t, majd visszafordítottam (diszasszembláltam) a dtb-ből dts-be.

Ah. Esetleg a dtc-ben van valami shortcut, ami (a specifikációval ellentétben, vagy esetleg azt kiegészítve) beköti az #address-cells = <1> tulajdonságot.

Szerkesztve: 2024. 01. 30., k – 10:47

Hát a fenti példában ez nyilvánvalóan nem így van. Nincs egyik "-cells" property se, szóval szinte biztos, hogy a szülőtől kellene örökíteni, szemben azzal, amit a spec állít, nem?

Nem, nem ezt állítja a spec. Azt mondja, hogy ha szükséged van ezekre a -cells property-kre, akkor expliciten fel kell őket venni, nem öröklődnek. Ha egy eszköznél nincs ilyen property, az nem hiba, sőt. Az normális dolog. Arra vannak kitalálva ezek a propertyk, hogy ha az eszköznek vannak gyerek eszközei, akkor hogyan kell a gyerek node-okat címezni:
 

The #address-cells and #size-cells properties may be used in any device node that has children in the devicetree
hierarchy and describes how child device nodes should be addressed. The #address-cells property defines the
number of <u32> cells used to encode the address field in a child node’s reg property. The #size-cells property
defines the number of <u32> cells used to encode the size field in a child node’s reg property.

...

If missing, a client program should assume a default value of 2 for #address-cells, and a value of 1 for #size-
cells.

És nyilván pont azért kell excpliciten megadni, és nem öröklődik, mert ha öröklődne, akkor egy devicetree levél node-ban is létezne ez a property a szölőből örökölve, pedig ott nincs értelme, hiszen levélnek nincsenek gyerekei. Ha meg nincs megadva, vannak default értékek.

Szerkesztve: 2024. 01. 30., k – 12:59

ranges:

Value type: <empty> or <prop-encoded-array> encoded as an arbitrary number of (child-bus-address, parent-bus-
address, length) triplets.

A prop-encoded-array formátuma property specifikus: <prop-encoded-array> Format is specific to the property. See the property definition.
És itt az van, hogy:

The format of the value of the ranges property is an arbitrary number of triplets of (child-bus-address, parent-
bus-address, length)
• The child-bus-address is a physical address within the child bus’ address space. The number of cells to
represent the address is bus dependent and can be determined from the #address-cells of this node (the
node in which the ranges property appears).

• The parent-bus-address is a physical address within the parent bus’ address space. The number of cells
to represent the parent address is bus dependent and can be determined from the #address-cells property
of the node that defines the parent’s address space.

• The length specifies the size of the range in the child’s address space. The number of cells to represent
the size can be determined from the #size-cells of this node (the node in which the ranges property
appears).

Azaz a triplet az nem úgy kódolódik, hogy3 db hex konstans, hanem az van, hogy a cím leírására szükséges cellák száma buszfüggő, és a kurrens node és a szülő node address-cells és a kurrens node size-cells értékéből határozható meg (erre való az address-cells és a size-cells).

És láss csodát, nálad az van, hogy a linkelt fájlban a soc parent nodeja alatt:    

    #address-cells = <2>;
    #size-cells = <1>;

Azaz így jön ki a cellák száma egy range leírására: child-bus address az 2 hosszú (ez az address-cells default értéke, mivel expliciten nincs megadva), parent-bus-address az 2 hosszú (expliciten megadva), és length az 1 hosszú (ez a default érték). Ez ugye 5 cellaelemet jelent, viszont te csak 4 konstanst látsz egy-egy értéknél.

De itt van még egy indirekció: az include. A fájl elején láthatsz egy include bcm283x.dtsi sort.

Ebben is fájlban van egy /soc node, amiben ott van, hogy:

        #address-cells = <1>;
        #size-cells = <1>;

 

Azaz a saját node address-e az 1 cella, a parent node addresse az 2 cella (az előzőek alapján), és a size az 1 cella. Így jön ki a 4 cella.

Érthető ez?

Nem, nem ezt állítja a spec. Azt mondja, hogy ha szükséged van ezekre a -cells property-kre, akkor expliciten fel kell őket venni, nem öröklődnek. Ha egy eszköznél nincs ilyen property, az nem hiba, sőt. Az normális dolog.

Na de ha nem a saját property-jét használja, hanem a szülőjét, azt úgy mondják magyarul, hogy öröklődés...

ha az eszköznek vannak gyerek eszközei, akkor hogyan kell a gyerek node-okat címezni

És újfent, magyarán a gyerekek öröklik a szülőben definiált címzést...

nem öröklődik, mert ha öröklődne, akkor egy devicetree levél node-ban is létezne ez a property a szölőből örökölve, pedig ott nincs értelme, hiszen levélnek nincsenek gyerekei.

Elméletben. A gyakorlatban a levél node-ok esetében is szükséges, különben öröklődés hiányában nem tudnád pl ezt értelmezni:

		local_intc: interrupt-controller@40000000 {
			compatible = "brcm,bcm2836-l1-intc";
			reg = <0x40000000 0x100>;
		};

ráadásul egy csomószor explicit is meg van adva a levél node-okban, például:

		spi6: spi@7e204c00 {
			compatible = "brcm,bcm2835-spi";
			reg = <0x7e204c00 0x0200>;
			interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>;
			clocks = <&clocks BCM2835_CLOCK_VPU>;
			#address-cells = <1>;
			#size-cells = <0>;
			status = "disabled";
		};

Pedig ennek az "spi6"-nak nincs egy gyereke se...

Ha meg nincs megadva, vannak default értékek.

Hát sajnos nem. Tökéletes példa erre a fentebb idézett "local_intc" node, itt például nincs megadva, de ha a default-ot használnád erre, ami 2 + 1, nem tudnád értelmezni a "reg"-jét.

Azaz a saját node address-e az 1 cella, a parent node addresse az 2 cella (az előzőek alapján), és a size az 1 cella. Így jön ki a 4 cella.

Ezt eddig is tudtam, olvasd csak el a fentebbi posztjaimat. Ezzel semmi újat nem mondtál. A kérdés egyébként is az volt, mitől lesz a "soc" saját node address-e 1-es, ha egyszer hiányzik a property. Ez utóbbit megválaszolja az include.

Arra tudnál valamit mondani, hogyha használom a "ranges"-ben megadott címfordítást, akkor miért kapok érvénytelen címeket a végén?

Na de ha nem a saját property-jét használja, hanem a szülőjét, azt úgy mondják magyarul, hogy öröklődés...

Nem, az azt jelentené, hogy a #address-cells meg a #size-cells, ha nincs expliciten megadva, akkor a szülő értékét használja. De nem, ez nincs így, az adott node #address-cells és #size-cells értéke, ha nincs megadva, akkor NEM a szülőét kell érteni alatta, hanem a defaultot (2 és 1).

És újfent, magyarán a gyerekek öröklik a szülőben definiált címzést...

Nem, nem örüllik. Olyan van, hogy a gyerek node propertyje a property definíciója szerint használja a szülő node értékét, de ettől még a gyerek nem örökölte az értéket.

Elméletben. A gyakorlatban a levél node-ok esetében is szükséges, különben nem tudnád pl ezt értelmezni:

De tudod, hiszen #address-cells alapértelmezett értéke 2, #size-cells alapértelmezett értéke 1. Azaz ha nincs expliciten megadva, akkor ezt a két értéket KELL használnod. A #address-cells és #size-cells mindig értelmezve van, és sosem öröklődik. Vagy fel van tüntetve expliciten, és azt KELL használnod, vagy nincs feltüntetve, akkor pedig a default értéket.

Ha például a szülőben #address-cells értéke 3, a gyerekben meg nincs feltüntetve, akkor a gyerek #address-cells értéke 2.

ráadásul egy csomószor explicit is meg van adva a levél node-okban, például:

Igen, azért van megadva, mert az értéke nem a default 2 és 1.

Hát sajnos nem. Tökéletes példa erre a fentebb idézett "local_intc" node, itt például nincs megadva, de ha a default-ot használnád erre, ami 2 + 1, nem tudnád értelmezni a "reg"-jét.

A reg definíciója a szabvány szerint:

The value is a <prop-encoded-array>, composed of an arbitrary number of pairs of address and length, <ad-
dress length>. The number of <u32> cells required to specify the address and length are bus-specific and are
specified by the #address-cells and #size-cells properties in the parent of the device node.
If the parent node
specifies a value of 0 for #size-cells, the length field in the value of reg shall be omitted.

A reg tehát a szülő node értékeit használja. A soc-nál meg ezek az értékek 1 és 1, ezért a reg jól értelmezhető.

 

Ott keveredsz te meg, hogy kevered az örölődést, meg a szülő node értékére való hivatkozást.
Van, hogy egy gyerek node X propertyje felhasználja a szülő node Y propertyjének az értékét. Ez tök más, mint az öröklődés, az az lenne, hogy a gyerek node X propertyje a szülő node X propertyjének az értéke lenne.

Itt is, a  local_intc node reg propertyje felhasználja a szülő soc node #address-cells értékét, de ettől még nem lesz az igaz, hogy a local_intc node #address-cells értéke megegyezik a soc node #address-cells értékével. Érted a külnbséget?

így is lehetne kb. mondani pszeudokódban:
sizeof(local_intc.reg) = soc.#address-cells (hivatkozunk a parent property értékére)

de ettől még

local_intc.#address-cells != soc.#address-cells (öröklünk)

 

Arra tudnál valamit mondani, hogyha használom a "ranges"-ben megadott címfordítást, akkor miért kapok érvénytelen címeket a végén?

Rosszul értelmezed az örököl és a hivatkozik-a-szülő-értékére dolgokat.

Nem, az azt jelentené, hogy a #address-cells meg a #size-cells, ha nincs expliciten megadva, akkor a szülő értékét használja.

Dehát ez pontosan ÍGY működik!!! A spec szerint is, és a .dtb példában is!

Olyan van, hogy a gyerek node propertyje a property definíciója szerint használja a szülő node értékét, de ettől még a gyerek nem örökölte az értéket.

Szőrszálhasogatás. Nem a saját property-jét használja és nem is a default-ot, hanem a szülőét, ez, akárki akármit mond, property öröklődés a javából. De nem is az elnevezés a lényeg, megvannak a helyes címméretek a többit leszarom.

Arra tudnál valamit mondani, hogyha használom a "ranges"-ben megadott címfordítást, akkor miért kapok érvénytelen címeket a végén?

Rosszul értelmezed az örököl és a hivatkozik-a-szülő-értékére dolgokat.

Vagy sokkal inkább Te nem értetted a kérdésem. Fuss neki mégegyszer a kérdésemnek (Gyk. ennek semmi köze a méret property-khez sem az öröklődéshez, ez a kérdés a már dekódolt "ranges" értékekre vonatkozik.)

Szerkesztve: 2024. 01. 30., k – 19:08

Ez a "ranges" sehogy sem jön ki. Megpróbálom mégegyszer körbeírni alaposan, hogy mi a probléma.

Tehát, a "soc" node-ban van egy ilyen:

	soc {
		ranges = <0x7e000000  0x0 0xfe000000  0x01800000>,
			 <0x7c000000  0x0 0xfc000000  0x02000000>,
			 <0x40000000  0x0 0xff800000  0x00800000>;

Valamint van benne egy eszköz child node, mondjuk ez:

		timer@7e003000 {
			compatible = "brcm,bcm2835-system-timer";
			reg = <0x7e003000 0x1000>;
			interrupts = <0x00 0x40 0x04 0x00 0x41 0x04 0x00 0x42 0x04 0x00 0x43 0x04>;
			clock-frequency = <0xf4240>;
			status = "disabled";
			phandle = <0x5d>;
		};

Ez az eszköz, amint a neve is sugallja, a 0x7e003000 címen érhető el. Namost ha szó szerint veszem a specifikációt, és a gyökérig követem a "reg" property-ben megadott <0x7e003000 0x1000> tartományt, akkor az bizony a "soc" node "ranges" első hármasába esik, 0x7e000000 és 0x7e000000+0x01800000 közé, tehát ebből ki kell vonni a child címét és hozzáadni a parent címét, mielőtt továbbadom a címet a következő szintre. így ez jön ki a gyökérnél: 0x7e003000 - 0x7e000000 + 0xfe000000 = 0xfe003000, csakhogy ez rossz cím.

Egész pontosan a BCM ARM Peripherals, page 6, 1.2.3 ARM physical addresses:

The bus addresses for peripherals are set up to map onto the peripheral bus address range starting at 0x7E000000. Thus a peripheral advertised here at bus address 0x7Ennnnnn is available at physical address 0x20nnnnnn.

(kiemelés tőlem). Tehát ez az eszköz a 0x7e003000-es buszcímen érhető el, ezt teszi elérhetővé a VC az ARM számára elérhető fizikai címként, és nem pedig a 0xfe003000 címet (a VC csavar mégegyet a dolgon, de ez az utolsó része világos, azzal most nem kell törődni, itt most a buszcím a lényeg.)

Szóval, mikor és milyen feltételek mellett is kell a "ranges" címfordítást elvégezni?

Szar doksit nézel szerintem.

A BCM2711-nek a device tree-jét nézed, de a BCM2835 periféria dokumentációját.

Láss csodát: https://datasheets.raspberrypi.com/bcm2711/bcm2711-peripherals.pdf

1.2.3. ARM physical addresses

When running in 32-bit mode, the ARM uses LPAE mode to enable it to access the full 32GB address space.
Physical addresses range from 0x4_7C00_0000 to 0x4_7FFF_FFFF for Main peripherals, and from 0x4_C000_0000 to
0x4_FFFF_FFFF for ARM Local peripherals.

If the VPU enables "Low Peripheral" mode then the ARM (only) has Main peripherals available from 0x0_FC00_0000 to
0x0_FF7F_FFFF and ARM Local peripherals available from 0x0_FF80_0000 to 0x0_FFFF_FFFF

Ja és ugyanezen doksi elején:

The BCM2711 has two main addressing schemes: a "Full" 35-bit address bus and a 32-bit "Legacy Master" view as seen
by the peripherals (except for "large address" masters). There’s also a "Low Peripherals" mode which modifies the ARM’s
view of the peripheral addresses.

Szerkesztve: 2024. 01. 31., sze – 11:24

tehát tök jól számolsz, és a memóriatérképnek a jó címét számolod ki, csak utána rossz dokumentációt nézel.

ÁÁÁÁÁÁÁÁÁÁ és valóban, igazad van! Ezer hála!

Lásd amúgy ezt a szálat itt: https://github.com/raspberrypi/documentation/issues/2289#issuecomment-9

Nekem miért nem dobta ki a kereső ezt? Sokat segített volna, így már tényleg minden érthető!
Összedobtam gyorsba' egy vizualizáló proggit is hozzá. Z generációs kollégák kedvéért elég, ha csak be drag'n'drop-olsz egy .dtb fájlt az ablakába, és megjeleníti.

https://gitlab.com/bztsrc/gudt/-/raw/main/examples/gudtgui_dtb.png

(Ejnye, miért nem jeleníti meg sehogy sem a képet a Drupal? Mi a helyes szintaxis képbeszúráshoz??? Hát nem <img src="">?)

Szerkesztve: 2024. 01. 31., sze – 18:28

OFF: mihez kell mindez? Pusztán kíváncsi vagyok!

Szükségem van egy egységes hardverleíró adatbázisra, függetlenül attól, hogy ACPI AML-t vagy FDT-t szolgáltat-e a förmver. Ehhez készült a hwdet és a gudt projektem.

Mindkettő függőség- és memóriafoglalásmentes single header library, hogy bare metal-on freestanding módban is használhatóak legyenek. A különbség csak annyi, hogy míg a hwdet futásidőben parszeolja a blobokat, emiatt nagy vermet igényel, addig a gudt alkalmazás oldalon (hosted módban) fájlokból olvassa be és egy egységes formátumba menti le, amihez ablakos szerkesztőprogram is jár. Így a bare metal-on freestanding módban használható gudt.h függvénykönyvtára minimális csak, alig igényel pár bájt memóriát, szóval tisztább, szárazabb érzés.

Kösz! :-D

Ez viszont szoftver p0rn! Jöhetnek még!

Jó! :-)

Igaz nem sok mindent értek belőle.

Hát, megpróbálom összefoglalni úgy, hogy laikusok is értsék, de nem ígérem, hogy sikerül.

Arról van szó, hogy sokféle alaplap sokféle csippel jön, és sokféle lehetséges paraméterrel. Ezért szükséges egy leíró adatformátum, amiben a hardvergyártók egy adott alaplap paramétereit rögzítik és becsűrik a ROM-ba a förmver mellé, és amit aztán bootoláskor át lehet passzolni a kernel meghajtóprogramjainak megcsócsálásra. Konrét példa, IBM PC kompatíbilis gépeken szokott lenni két soros port. Mindkettőt ugyanaz a csip hajtja, de az egyik IO port címe 0x3F8, míg a másiké 0x2F8. A leíró adatformátumban ezek az adatok szerepelnek, mint paraméterek, innen tudja a kernel soros port meghajtóprogramja, hogy hogyan éri el ezeket a perifériákat (és hogy egyáltalán hány soros port is van az alaplapon, lehet nulla, egy, vagy akár négy is belőle).

Namost ilyen leíró adatformátumból jelenleg kettő féle van, de sajnos mindkettőnek súlyos hátrányai vannak.

- AML: ebben az a jó, hogy fixek és szabványosítottak a paraméterek, így egyértelműek. Minden más viszont rossz benne, egyrészről x86 centrikus, más architektúrákon nagyon nyögvenyelős. Másrészről annak ellenére, hogy csupán egy egyszerű leíróról van szó, ez igazából egy bájtkód, tehát egy komplett virtuális gépre van szükség az értelmezéséhez, ami meg egyszerűen agyfasz na. Sajnos a pénzzel kitömött Intel + Microsoft páros ráerőszakolta ezt minden hardvergyártóra az UEFI-vel egyetemben (az más kérdés, hogy a hardvergyártók nemigazán tudnak mit kezdeni egy ilyen elcseszett bájtkóddal, ezért hemzsegnek a bugos DSDT táblájú gépek, keress csak arra, hogy "Windows acpi.sys bsod", a felhasználók meg beszopják a malware-eket vele. Ez nem csupán csak elmélet, hanem aktívan kihasznált sebezhetőség.)

- FDT: aztán van a nem Intel-es világ (ARM, PPC, stb. kábé mindenki más), ők meg ezt az adatformátumot használják. Ez minden szempontból jobb, mint az AML, de nem lett megtolva lóvéval, ezért nem terjedt el PC-ken. Van viszont más baj vele, nevezetesen elkövettek egy oltári nagy tervezési hibát, ami miatt gyakorlatilag használhatatlan általános leírónak. Sajnos elfelejtették szabványosítani a paramétereket, emiatt nem is használható csak úgy, hanem van egy program, ami a megadott .dtb fájl alapján C nyelvű interfész forrásfájlt gyárt, és a meghajtóprogramok forrásában ezt kell használni. Az a lefordított meghajtóprogram aztán csak azt az egy fajta .dtb-t tudja csak beolvasni. Azt ugye különösebben nem kell magyarázni, hogy ez a körülmény miért vágja haza a portolhatóságot és hordozhatóságot. (Arról nem is beszélve, hogy minden csipgyártó saját paramétereket és értékformátumot talál ki magának, így a tökéletesen kompatíbilis eszközök leírása is tökéletesen inkompatíbilis ebben a formátumban...)

Na és itt jön a képbe a projektem, ami mindkét féle formátumból egy egységes, azonos formátumú adathalmazt gyárt. Ez még egyelőre csak PoC, hogy egy ilyen egységes formátum egyáltalán lehetséges, de idővel ha elég esetet megbízhatóan lekezelek benne, akkor simán production ready lehet.

Köszi a magyarázatot!

Egyszer találkoztam dtb fájlokkal, mikor a RPi2-t beüzemeltem. Azt láttam, hogy egy hardver leíró valami. Érdekel a low level hardver hacking, de nekem ez már olyan szint amit nem látok át. Még egy Raspberry is bonyolult darab egy mikrokontrollerhez vagy egy C64/Amigához képest. Most a RISC-V felé irányul az érdeklődésem, találtam pár kütyüt, amivel szeretnék közelebbről megismerkedni. https://wiki.pine64.org/wiki/Ox64 vagy a https://wiki.pine64.org/wiki/STAR64