diff options
Diffstat (limited to 'roms/SLOF/board-qemu/slof/fdt.fs')
-rw-r--r-- | roms/SLOF/board-qemu/slof/fdt.fs | 673 |
1 files changed, 673 insertions, 0 deletions
diff --git a/roms/SLOF/board-qemu/slof/fdt.fs b/roms/SLOF/board-qemu/slof/fdt.fs new file mode 100644 index 000000000..78d2efe0c --- /dev/null +++ b/roms/SLOF/board-qemu/slof/fdt.fs @@ -0,0 +1,673 @@ +\ ***************************************************************************** +\ * Copyright (c) 2011 IBM Corporation +\ * All rights reserved. +\ * This program and the accompanying materials +\ * are made available under the terms of the BSD License +\ * which accompanies this distribution, and is available at +\ * http://www.opensource.org/licenses/bsd-license.php +\ * +\ * Contributors: +\ * IBM Corporation - initial implementation +\ ****************************************************************************/ + +0 VALUE fdt-debug +TRUE VALUE fdt-cas-fix? +0 VALUE fdt-cas-pass +0 VALUE fdt-generation# + +: fdt-update-from-fdt ( -- ) + fdt-generation# encode-int s" slof,from-fdt" property +; + +\ Bail out if no fdt +fdt-start 0 = IF -1 throw THEN + +struct + 4 field >fdth_magic + 4 field >fdth_tsize + 4 field >fdth_struct_off + 4 field >fdth_string_off + 4 field >fdth_rsvmap_off + 4 field >fdth_version + 4 field >fdth_compat_vers + 4 field >fdth_boot_cpu + 4 field >fdth_string_size + 4 field >fdth_struct_size +constant /fdth + +h# d00dfeed constant OF_DT_HEADER +h# 1 constant OF_DT_BEGIN_NODE +h# 2 constant OF_DT_END_NODE +h# 3 constant OF_DT_PROP +h# 4 constant OF_DT_NOP +h# 9 constant OF_DT_END + +\ Create some variables early +0 value fdt-start-addr +0 value fdt-struct +0 value fdt-strings + +: fdt-init ( fdt-start -- ) + dup to fdt-start-addr + dup dup >fdth_struct_off l@ + to fdt-struct + dup dup >fdth_string_off l@ + to fdt-strings + drop +; +fdt-start fdt-init + +\ Dump fdt header for all to see and check FDT validity +: fdt-check-header ( -- ) + fdt-start-addr dup 0 = IF + ." No flat device tree !" cr drop -1 throw EXIT THEN + hex + fdt-debug IF + ." Flat device tree header at 0x" dup . s" :" type cr + ." magic : 0x" dup >fdth_magic l@ . cr + ." total size : 0x" dup >fdth_tsize l@ . cr + ." offset to struct : 0x" dup >fdth_struct_off l@ . cr + ." offset to strings: 0x" dup >fdth_string_off l@ . cr + ." offset to rsvmap : 0x" dup >fdth_rsvmap_off l@ . cr + ." version : " dup >fdth_version l@ decimal . hex cr + ." last compat vers : " dup >fdth_compat_vers l@ decimal . hex cr + dup >fdth_version l@ 2 >= IF + ." boot CPU : 0x" dup >fdth_boot_cpu l@ . cr + THEN + dup >fdth_version l@ 3 >= IF + ." strings size : 0x" dup >fdth_string_size l@ . cr + THEN + dup >fdth_version l@ 11 >= IF + ." struct size : 0x" dup >fdth_struct_size l@ . cr + THEN + THEN + dup >fdth_magic l@ OF_DT_HEADER <> IF + ." Flat device tree has incorrect magic value !" cr + drop -1 throw EXIT + THEN + dup >fdth_version l@ 10 < IF + ." Flat device tree has usupported version !" cr + drop -1 throw EXIT + THEN + + drop +; +fdt-check-header + +\ Fetch next tag, skip nops and increment address +: fdt-next-tag ( addr -- nextaddr tag ) + 0 ( dummy tag on stack for loop ) + BEGIN + drop ( drop previous tag ) + dup l@ ( read new tag ) + swap 4 + swap ( increment addr ) + dup OF_DT_NOP <> UNTIL ( loop until not nop ) +; + +\ Parse unit name and advance addr +: fdt-fetch-unit ( addr -- addr $name ) + dup from-cstring \ get string size + 2dup + 1 + 3 + fffffffc and -rot +; + +\ Update unit with information from the reg property... +\ ... this is required for the PCI nodes for example. +: fdt-reg-unit ( prop-addr prop-len -- ) + decode-phys ( prop-addr' prop-len' phys.lo ... phys.hi ) + set-unit ( prop-addr' prop-len' ) + 2drop +; + +\ Lookup a string by index +: fdt-fetch-string ( index -- str-addr str-len ) + fdt-strings + dup from-cstring +; + +: fdt-create-dec s" decode-unit" $CREATE , DOES> @ hex64-decode-unit ; +: fdt-create-enc s" encode-unit" $CREATE , DOES> @ hex64-encode-unit ; + +\ Check whether array contains an zero-terminated ASCII string: +: fdt-prop-is-string? ( addr len -- string? ) + dup 1 < IF 2drop FALSE EXIT THEN \ Check for valid length + 1- + 2dup + c@ 0<> IF 2drop FALSE EXIT THEN \ Check zero-termination + test-string +; + +\ Encode fdt property to OF property +: fdt-encode-prop ( addr len -- pa ps ) + 2dup fdt-prop-is-string? IF + 1- encode-string + ELSE + encode-bytes + THEN +; + +\ Method to unflatten a node +: fdt-unflatten-node ( start -- end ) + \ this can and will recurse + recursive + + \ Get & check first tag of node ( addr -- addr) + fdt-next-tag dup OF_DT_BEGIN_NODE <> IF + s" Weird tag 0x" type . " at start of node" type cr + -1 throw + THEN drop + + new-device + + \ Parse name, split unit address + fdt-fetch-unit + dup 0 = IF drop drop " /" THEN + 40 left-parse-string + \ Set name + device-name + + \ Set preliminary unit address - might get overwritten by reg property + dup IF + " #address-cells" get-parent get-package-property IF + 2drop + ELSE + decode-int nip nip + hex-decode-unit + set-unit + THEN + ELSE 2drop THEN + + \ Iterate sub tags + BEGIN + fdt-next-tag dup OF_DT_END_NODE <> + WHILE + dup OF_DT_PROP = IF + \ Found property + drop dup ( drop tag, dup addr : a1 a1 ) + dup l@ dup rot 4 + ( fetch size, stack is : a1 s s a2) + dup l@ swap 4 + ( fetch nameid, stack is : a1 s s i a3 ) + rot ( we now have: a1 s i a3 s ) + fdt-encode-prop rot ( a1 s pa ps i) + fdt-fetch-string ( a1 s pa ps na ns ) + 2dup s" reg" str= IF + 2swap 2dup fdt-reg-unit 2swap + THEN + property + + 8 + 3 + fffffffc and + ELSE dup OF_DT_BEGIN_NODE = IF + drop ( drop tag ) + 4 - + fdt-unflatten-node + ELSE + drop -1 throw + THEN THEN + REPEAT drop \ drop tag + + \ Create encode/decode unit + " #address-cells" get-node get-package-property IF ELSE + decode-int dup fdt-create-dec fdt-create-enc 2drop + THEN + + fdt-update-from-fdt + + finish-device +; + +\ Start unflattening +: fdt-unflatten-tree + fdt-debug IF + ." Unflattening device tree..." cr THEN + fdt-struct fdt-unflatten-node drop + fdt-debug IF + ." Done !" cr THEN +; +fdt-unflatten-tree + +\ Find memory size +: fdt-parse-memory + \ XXX FIXME Handle more than one memory node, and deal + \ with RMA vs. full access + " /memory@0" find-device + " reg" get-node get-package-property IF throw -1 THEN + + \ XXX FIXME Assume one entry only in "reg" property for now + decode-phys 2drop decode-phys + my-#address-cells 1 > IF 20 << or THEN + + fdt-debug IF + dup ." Memory size: " . cr + THEN + \ claim.fs already released the memory between 0 and MIN-RAM-SIZE, + \ so we've got only to release the remaining memory now: + MIN-RAM-SIZE swap MIN-RAM-SIZE - release + 2drop device-end +; +fdt-parse-memory + + +\ Claim fdt memory and reserve map +: fdt-claim-reserve + fdt-start-addr + dup dup >fdth_tsize l@ 0 claim drop + dup >fdth_rsvmap_off l@ + + BEGIN + dup dup x@ swap 8 + x@ + dup 0 <> + WHILE + fdt-debug IF + 2dup swap ." Reserve map entry: " . ." : " . cr + THEN + 0 claim drop + 10 + + REPEAT drop drop drop +; +fdt-claim-reserve + + +\ The following functions are use to replace the FDT phandle and +\ linux,phandle properties with our own OF1275 phandles... + +\ This is used to check whether we successfully replaced a phandle value +0 VALUE (fdt-phandle-replaced) + +\ Replace phandle value in "interrupt-map" property +: fdt-replace-interrupt-map ( old new prop-addr prop-len -- old new ) + BEGIN + dup ( old new prop-addr prop-len prop-len ) + WHILE + \ This is a little bit ugly ... we're accessing the property at + \ hard-coded offsets instead of analyzing it completely... + swap dup 10 + ( old new prop-len prop-addr prop-addr+10 ) + dup l@ 5 pick = IF + \ it matches the old phandle value! + 3 pick swap l! + TRUE TO (fdt-phandle-replaced) + ELSE + drop + THEN + ( old new prop-len prop-addr ) + 1c + swap 1c - + ( old new new-prop-addr new-prop-len ) + REPEAT + 2drop +; + +: (fdt-replace-phandles) ( old new propname propnamelen node -- ) + get-property IF 2drop EXIT THEN + BEGIN + dup + WHILE ( old new prop-addr prop-len ) + over l@ + 4 pick = IF + 2 pick 2 pick l! \ replace old with new in place + TRUE TO (fdt-phandle-replaced) + THEN + 4 - swap 4 + swap + REPEAT + 2drop 2drop +; + +: (phandle>node) ( phandle current -- node|0 ) + dup s" phandle" rot get-property 0= IF + decode-int nip nip ( phandle current phandle-prop ) + 2 pick = IF + fdt-debug IF ." Found phandle; " dup . ." <= " over . cr THEN + nip ( current ) + EXIT + THEN + ELSE + dup s" linux-phandle" rot get-property 0= IF + decode-int nip nip ( phandle current phandle-prop ) + 2 pick = IF + fdt-debug IF ." Found linux-phandle; " dup . ." <= " over . cr THEN + nip ( current ) + EXIT + THEN + THEN + THEN + child BEGIN + dup + WHILE + 2dup + RECURSE + ?dup 0<> IF + nip nip + EXIT + THEN + PEER + REPEAT + 2drop 0 +; + +: phandle>node ( phandle -- node ) s" /" find-node (phandle>node) ; + +: (fdt-patch-phandles) ( prop-addr prop-len -- ) + BEGIN + dup + WHILE ( prop-addr prop-len ) + over l@ phandle>node + ?dup 0<> IF + fdt-debug IF ." ### Patching phandle=" 2 pick l@ . cr THEN + 2 pick l! + TRUE TO (fdt-phandle-replaced) + THEN + 4 - swap 4 + swap + REPEAT + 2drop +; + +: (fdt-patch-interrupt-map) ( prop-addr prop-len -- ) + \ interrupt-controller phandle is expected to be the same accross the map + over 10 + l@ phandle>node ?dup 0= IF 2drop EXIT THEN + -rot + fdt-debug IF ." ### Patching interrupt-map: " over 10 + l@ . ." => " 2 pick . cr THEN + + TRUE TO (fdt-phandle-replaced) + BEGIN + dup + WHILE ( newph prop-addr prop-len ) + 2 pick 2 pick 10 + l! + 1c - swap 1c + swap + REPEAT + 3drop +; + +: fdt-patch-phandles ( prop-addr prop-len nameadd namelen -- ) + 2dup s" interrupt-map" str= IF 2drop (fdt-patch-interrupt-map) EXIT THEN + 2dup s" interrupt-parent" str= IF 2drop (fdt-patch-phandles) EXIT THEN + 2dup s" ibm,gpu" str= IF 2drop (fdt-patch-phandles) EXIT THEN + 2dup s" ibm,npu" str= IF 2drop (fdt-patch-phandles) EXIT THEN + 2dup s" ibm,nvlink" str= IF 2drop (fdt-patch-phandles) EXIT THEN + 2dup s" memory-region" str= IF 2drop (fdt-patch-phandles) EXIT THEN + 4drop +; + +\ Replace one phandle "old" with a phandle "new" in "node" and recursively +\ in its child nodes: +: fdt-replace-all-phandles ( old new node -- ) + \ ." Replacing in " dup node>path type cr + >r + s" interrupt-map" r@ get-property 0= IF + ( old new prop-addr prop-len R: node ) + fdt-replace-interrupt-map + THEN + + 2dup s" interrupt-parent" r@ (fdt-replace-phandles) + 2dup s" ibm,gpu" r@ (fdt-replace-phandles) + 2dup s" ibm,npu" r@ (fdt-replace-phandles) + 2dup s" ibm,nvlink" r@ (fdt-replace-phandles) + 2dup s" memory-region" r@ (fdt-replace-phandles) + + \ ... add more properties that have to be fixed here ... + r> + \ Now recurse over all child nodes: ( old new node ) + child BEGIN + dup + WHILE + 3dup RECURSE + PEER + REPEAT + 3drop +; + +\ Replace one FDT phandle "val" with a OF1275 phandle "node" in the +\ whole tree: +: fdt-update-phandle ( val node -- ) + >r + FALSE TO (fdt-phandle-replaced) + r@ s" /" find-node ( val node root ) + fdt-replace-all-phandles + (fdt-phandle-replaced) IF + r@ set-node + s" phandle" delete-property + s" linux,phandle" delete-property + ELSE + diagnostic-mode? IF + cr ." Warning: Did not replace phandle in " r@ node>path type cr + THEN + THEN +r> drop +; + +\ Check whether a node has "phandle" or "linux,phandle" properties +\ and replace them: +: fdt-fix-node-phandle ( node -- ) + >r + s" phandle" r@ get-property 0= IF + decode-int nip nip + \ ." found phandle: " dup . cr + r@ fdt-update-phandle + THEN + r> drop +; + +\ Recursively walk through all nodes to fix their phandles: +: fdt-fix-phandles ( node -- ) + \ ." fixing phandles of " dup node>path type cr + dup fdt-fix-node-phandle + child BEGIN + dup + WHILE + dup RECURSE + PEER + REPEAT + drop + device-end +; + +: str=phandle? ( s len -- true|false ) + 2dup s" phandle" str= >r + s" linux,phandle" str= + r> or +; + +: fdt-cas-finish-device ( -- ) + " reg" get-node get-package-property IF ELSE fdt-reg-unit THEN + get-node finish-device set-node +; + +: (fdt-fix-cas-node) ( start -- end ) + recursive + fdt-next-tag dup OF_DT_BEGIN_NODE <> IF + ." Error " cr + false to fdt-cas-fix? + EXIT + THEN drop + fdt-fetch-unit ( a1 $name ) + dup 0 = IF drop drop " /" THEN + 40 left-parse-string + 2swap ?dup 0 <> IF + nip + 1 + + \ Add the string len +@ + ELSE + drop + THEN + + fdt-cas-pass 0= IF + \ The guest might have asked to change the interrupt controller + \ type. It doesn't make sense to merge the new node and the + \ existing "interrupt-controller" node in this case. Delete the + \ latter. A brand new one will be created with the appropriate + \ properties and unit name. + 2dup " interrupt-controller" find-substr 0= IF + " interrupt-controller" find-node ?dup 0 <> IF + fdt-debug IF ." Deleting existing node: " dup .node cr THEN + delete-node + THEN + THEN + THEN + 2dup find-node ?dup 0 <> IF + set-node + fdt-debug IF ." Setting node: " 2dup type cr THEN + 2drop + \ newnode?=0: updating the existing node, i.e. pass1 adds only phandles + 0 + ELSE + fdt-cas-pass 0 <> IF + \ We could not find the node added in the previous pass, + \ most likely because it is hotplug-under-hotplug case + \ (such as PCI brigde under bridge) when missing new node methods + \ such as "decode-unit" are critical. + \ Reboot when detect such case which is expected as it is a part of + \ ibm,client-architecture-support. + ." Cannot handle FDT update for the " 2dup type + ." node, rebooting" cr + reset-all + THEN + fdt-debug IF ." Creating node: " 2dup type cr THEN + new-device + 2dup " @" find-substr nip + device-name + \ newnode?=1: adding new node, i.e. pass1 adds all properties, + \ most importantly "reg". After reading properties, we call + \ "fdt-cas-finish-device" which sets the unit address from "reg". + 1 + THEN + swap ( newnode? a1 ) + + fdt-debug IF ." Current now: " pwd get-node ." = " . cr THEN + fdt-cas-pass 0= IF + fdt-update-from-fdt + THEN + BEGIN + fdt-next-tag dup OF_DT_END_NODE <> + WHILE + ( newnode? a1 tag ) + dup OF_DT_PROP = IF + drop dup ( newnode? a1 a1 ) + dup l@ dup rot 4 + ( newnode? a1 s s a2) + dup l@ swap 4 + ( newnode? a1 s s i a3 ) + rot ( newnode? a1 s i a3 s ) + fdt-encode-prop rot ( newnode? a1 s pa ps i) + fdt-fetch-string ( newnode? a1 s pa ps na ns ) + + fdt-cas-pass CASE + 0 OF + 2dup str=phandle? 7 pick or IF + fdt-debug IF 4dup ." Property: " type ." =" swap ." @" . ." " .d ." bytes" cr THEN + property + ELSE + 4drop + THEN + ENDOF + 1 OF + 2dup str=phandle? not IF + fdt-debug IF 4dup ." Property: " type ." =" swap ." @" . ." " .d ." bytes" cr THEN + 4dup fdt-patch-phandles + property + ELSE + 4drop + THEN + ENDOF + 2 OF + 2dup str=phandle? IF + fdt-debug IF 4dup ." Deleting: " type ." =" swap ." @" . ." " .d ." bytes" cr THEN + delete-property + 2drop + ELSE + 4drop + THEN + ENDOF + ENDCASE + + + 8 + 3 + fffffffc and + ELSE ( newnode? a1 tag ) + dup OF_DT_BEGIN_NODE = IF + 2 pick IF + rot drop 0 -rot + fdt-cas-finish-device + fdt-debug IF ." Finished node: " pwd get-node ." = " . cr THEN + THEN + drop ( a1 ) + 4 - + (fdt-fix-cas-node) + get-parent set-node + ELSE + ." Error " cr + drop + false to fdt-cas-fix? + EXIT + THEN + THEN + REPEAT + ( newnode? a1 tag ) + drop + swap ( a1 newnode? ) + IF + fdt-cas-finish-device + fdt-debug IF ." Finished subnode: " pwd get-node ." = " . cr THEN + THEN +; + +: alias-dev-path ( xt -- dev-path len ) + link> execute decode-string 2swap 2drop +; + +: alias-name ( xt -- alias-name len ) + link> >name name>string +; + +: fdt-cas-alias-obsolete? ( xt -- true|false ) + alias-dev-path find-node 0= +; + +: (fdt-cas-delete-obsolete-aliases) ( xt -- ) + dup IF + dup @ + recurse + dup alias-name s" name" str= IF ELSE + dup fdt-cas-alias-obsolete? IF + fdt-debug IF ." Deleting obsolete alias: " dup alias-name type ." -> " dup alias-dev-path type cr THEN + dup alias-name + delete-property + THEN + THEN + THEN + drop +; + +: fdt-cas-delete-obsolete-aliases ( -- ) + s" /aliases" find-device + get-node node>properties @ cell+ @ (fdt-cas-delete-obsolete-aliases) + device-end +; + +: fdt-cas-node-obsolete? ( node -- true|false) + s" slof,from-fdt" rot get-package-property IF + \ Not a QEMU originated node + false + ELSE + decode-int nip nip fdt-generation# < + THEN +; + +: (fdt-cas-search-obsolete-nodes) ( node -- ) + dup child + BEGIN + dup + WHILE + dup recurse + peer + REPEAT + drop + dup fdt-cas-node-obsolete? IF + fdt-debug IF dup ." Deleting obsolete node: " dup .node ." = " . cr THEN + dup delete-node + THEN + drop +; + +: fdt-cas-delete-obsolete-nodes ( -- ) + s" /" find-device get-node (fdt-cas-search-obsolete-nodes) + fdt-cas-delete-obsolete-aliases +; + +: fdt-fix-cas-node ( start -- ) + fdt-generation# 1+ to fdt-generation# + 0 to fdt-cas-pass dup (fdt-fix-cas-node) drop \ Add phandles + fdt-cas-delete-obsolete-nodes \ Delete removed devices + 1 to fdt-cas-pass dup (fdt-fix-cas-node) drop \ Patch+add other properties + 2 to fdt-cas-pass dup (fdt-fix-cas-node) drop \ Delete phandles from pass 0 + drop +; + +: fdt-fix-cas-success + fdt-cas-fix? +; + +s" /" find-node fdt-fix-phandles |