[U-Boot] Sharing a hardware lab

Simon Glass sjg at chromium.org
Thu Mar 26 17:19:54 CET 2020


Hi Harald,

On Mon, 23 Mar 2020 at 04:30, Harald Seiler <hws at denx.de> wrote:
>
> Hello Simon,
>
> On Sun, 2020-03-22 at 12:42 -0600, Simon Glass wrote:
> > Hi Harald,
> >
> > On Sun, 22 Mar 2020 at 03:56, Harald Seiler <hws at denx.de> wrote:
> > > Hi Simon,
> > >
> > > On Sat, 2020-03-21 at 13:07 -0600, Simon Glass wrote:
> [...]
> > >
> > > Huh, actually I messed up in the patch I sent you.  Please apply the following
> > > diff:
> > >
> > > diff --git a/kea.py b/kea.py
> > > index a3ca968dc41e..f65d91b67f1d 100644
> > > --- a/kea.py
> > > +++ b/kea.py
> > > @@ -39,7 +39,7 @@ class KeaLab(
> > >          # toolchain is identified by a (unique) string.  For pcduino3 in this
> > >          # example, a toolchain named `armv7-a` is defined.
> > >          return {
> > > -            "armv7-a": ArmV7Toolchain,
> > > +            "armv7-a": ArmV7Toolchain(),
> > >          }
> > >
> > >      def build(self):
> > >
> > > This will make everything work as intended and the patch you needed in tbot is
> > > also no longer necessary (Passing a class instead of an instance will not
> > > bind the method's `self` argument so you got an error from a weird place.
> > > Maybe I should add a check against this kind of mistake ...).
> >
> > OK that fixes it, thanks.
> >
> > Actually I can call 'buildman -A <board>' to get the toolchain prefix
> > for a board, so perhaps I should figure out how to work that in
> > somehow, and just have a generic toolchain problem,
>
> That's not too difficult, actually.  I suggest creating a generic UBootBuilder
> class from which your individual board's builders inherit:
>
>     import abc
>     import contextlib
>
>     class BuildmanUBootBuilder(uboot.UBootBuilder):
>         @abc.abstractmethod
>         @property
>         def buildman_board(self) -> str:
>             raise Exception("abstract method")
>
>         # Overload `do_toolchain` to customize toolchain behavior
>         @contextlib.contextmanager
>         def do_toolchain(self, bh):
>             prefix = bh.exec0("buildman", "-A", self.buildman_board)
>
>             with bh.subshell():
>                 # Setup toolchain here
>                 bh.env("ARCH", "arm")
>
>                 yield None
>
>     # and for each board
>
>     class MyBoardUBootBuilder(BuildmanUBootBuilder):
>         buildman_board = "tegra"
>
> I have never used buildman myself so I am not really sure about the
> details of what needs to be done here ... But if this would be a useful
> feature I could look into supporting it directly in the UBootBuilder
> class.
>
> > > > So my next question is how to load U-Boot onto the board. I can see
> > > > methods for poweron/poweroff but not for actually writing to the
> > > > board. Is there a built-in structure for this? I cannot find it.
> > > >
> > > > I am hoping that the Pcduino3 class can define a method like
> > > > 'load_uboot()' to write U-Boot to the board?
> > >
> > > I added something similar to this in our DENX internal tbot configurations but
> > > did not yet publish it anywhere (maybe I should add it to tbot_contrib?).  Just
> > > for you, here is what I did:
> >
> > To me this should be a core feature, not contrib.
>
> I guess the split of what belongs where is not yet clearly defined and I still
> need to work on separating the different components better.  For me, core is
> just the code for interacting with machines; anything that looks like
> a testcase should be separate from that.  Right now I've got tbot_contrib for
> this but I guess the naming might not be optimal ...
>
> By the way, are you subscribed to tbot at lists.denx.de?  I'll be posting updates
> regarding this (and other things) over there.
>
> > I wonder if there is an upstream for tbot labs? I think it would be
> > useful to have a directory containing people's labs like we do for
> > pytest. Then we can end up with common functions for doing things.
>
> Maybe it makes sense to add a place in upstream tbot for these as well.
> Thanks for the idea!
>
> > > 1. In the board configs, each U-Boot class defines a `flash()` method:
> > >
> > >     from tbot.tc import shell, git
> > >
> > >     class P2020RdbUBoot(board.Connector, board.UBootShell):
> > >         name = "p2020rdb-uboot"
> > >         prompt = "=> "
> > >         build = P2020RdbUBootBuilder()
> > >
> > >         def flash(self, repo: git.GitRepository) -> None:
> > >             self.env("autoload", "no")
> > >             self.exec0("dhcp")
> > >             self.env("serverip", "192.168.1.1")
> > >
> > >             tftpdir = self.host.fsroot / "tftpboot" / "p2020rdb" / "tbot2"
> > >             if not tftpdir.is_dir():
> > >                 self.host.exec0("mkdir", "-p", tftpdir)
> > >
> > >             name = "u-boot-with-spl.bin"
> > >             tbot.log.message(f"Fetching {name} ...")
> > >             shell.copy(repo / name, tftpdir / name)
> > >
> > >             addr = 0x10000000
> > >             self.exec0("tftp", hex(addr), "p2020rdb/tbot2/" + name)
> > >             size = int(self.env("filesize"), 16)
> > >             tbot.log.message(f"Downoad succeeded:\n  U-Boot: {size} bytes")
> > >
> > >             tbot.log.message("Flashing ...")
> > >             self.exec0("nand", "device", "0")
> > >             self.exec0("nand", "erase.spread", "0", hex(size))
> > >             self.exec0("nand", "write", hex(addr), "0", hex(size))
> > >
> > >    As you can see, it get the repository (as a GitRepository [1]) where
> > >    U-Boot was built, passed as an argument, and copies the relevant
> > >    binaries to a tftp directory on the lab-host (using shell.copy() [2]).
> > >    Then it downloads and flashes U-Boot like you would manually.
> > >
> > >    Alternatively, I could imagine the implementation connecting to
> > >    a hardware debugger and uploading the binary to flash that way.
> >
> > Thanks very much. That explained it for me. I tried this out and ended
> > up moving it to the board class instead of the U-Boot class, since I
> > don't know what state the board is in. I just want to turn it off and
> > program it.
>
> During `flash()` (in my implementation), the board is powered on and waiting
> for commands at the U-Boot prompt.  I know this won't work for the case where
> U-Boot is failing to boot.  Alternatively, I could change it so the
> implementation decides on its own whether it needs U-Boot running.  E.g.:
>
>     class MyBoardUBoot(board.Connector, board.UBootShell):
>         @classmethod
>         def flash(cls, board: board.Board, repo: git.GitRepository):
>             # Board is powered on.  An implementation using a debugger could
>             # now flash U-Boot.
>
>             with board.host.run("arm-linux-gnu-gdb", elf) as gdb:
>                 gdb.read_until_prompt("(gdb) ")
>
>                 gdb.sendline("target remote localhost:3333")
>                 gdb.read_until_prompt("(gdb) ")
>
>                 gdb.sendline("load")
>                 gdb.read_until_prompt("(gdb) ")
>
>                 gdb.sendline("quit")
>                 gdb.terminate0()
>
>             # An implementation like mine above initializes the UBoot machine
>             # and then continues like before:
>
>             with cls(board) as ub:
>                 ub.exec0("dhcp")
>                 ub.exec0("nand", "update", ...)
>
> Hope this makes sense ...
>
> > I think a lot of my confusion is all the classes and inheritance and
> > things like selectable.py where objects are updated outside the
> > module. Plus all the decorators, getattr/setattr...
>
> Ideally I'd want all that to stay hidden from the user.  I know that right
> now I have not really archieved that and the code is really messy in some
> places because of it.
>
> I'd love some feedback on the things that aren't explained well in the
> documentation or places where features are lacking and you needed to dig
> into the internals.
>
> > > 2. I defined a few testcases for flashing.  Namely, `uboot_build_and_flash`
> > >    (build U-Boot and then flash the new version) and `uboot_bare_flash` (don't
> > >    rebuild, just flash the artifacts from the last build).  These then use the
> > >    board-specific U-Boot builder and flash method.
> > >
> > >    I also added a feature where you can specify `-fsafe` on the tbot
> > >    command-line.  This will, in case something goes wrong while flashing,
> > >    drop you onto the interactive U-Boot command-line instead of powering
> > >    off the board and reporting a failure.
> > >
> > >    I uploaded the testcases here:
> > >
> > >         https://gitlab.denx.de/snippets/10
> >
> > OK thanks. I am slowly getting the hang of it.
> >
> > My current problem is how to execute Python code on the host (the
> > machine that has the hardware attached). I end up having to call
> > host.exec() but it if I want to open a file and modify it, I cannot do
> > that easily remotely. Is it possible to run a tbot function on the lab
> > machine?
>
> The idea is that you shouldn't ever need to run Python code on the
> lab-host (so we don't have to make assumptions about the environment of
> the lab-host). What things do you need to do that can't be done right now?
> I'd argue that tbot should provide API to do them without manual (Python)
> scripting on the lab-host.
>
> For accessing files in particular, I just added some methods
> (`read_text()` [1], `write_text()` [2], `read_bytes()` [3], and
> `write_bytes` [4]) a few days ago.  Maybe they can help?

Thanks for all of this.

I will follow up on the tbot mailing list once I have figure out the
toolchain stuff.

Regards,
Simon


More information about the U-Boot mailing list