1##########################################################################
2# Copyright (c) 2014-2016 ETH Zurich.
3# All rights reserved.
4#
5# This file is distributed under the terms in the attached LICENSE file.
6# If you do not find this file, copies can be found by writing to:
7# ETH Zurich D-INFK, Universitaetstr 6, CH-8092 Zurich. Attn: Systems Group.
8##########################################################################
9
10import debug, eth_machinedata
11import subprocess, os, socket, sys, shutil, tempfile, pty
12from machines import ARMMachineBase, MachineFactory, MachineOperations
13from machines.eth import ETHBaseMachineOperations
14
15# XXX: the rack-mount pandaboard machines are ETH-specific
16PANDA_ROOT='/mnt/emmentaler1_nfs/pandaboot'
17PANDA_BOOT_HOST='masterpanda.in.barrelfish.org'
18PANDA_PORT=10000
19TOOLS_PATH='/home/netos/tools/bin'
20RACKBOOT=os.path.join(TOOLS_PATH, 'rackboot.sh')
21RACKPOWER=os.path.join(TOOLS_PATH, 'rackpower')
22
23
24class PandaboardMachine(ARMMachineBase):
25    '''Machine to run tests on locally attached pandaboard. Assumes your
26    pandaboard's serial port is attached to /dev/ttyUSB0'''
27    name = 'panda_local'
28    imagename = "armv7_omap44xx_image"
29
30    def __init__(self, options, **kwargs):
31        super(PandaboardMachine, self).__init__(options, PandaboardOperations(self), **kwargs)
32        self.menulst_template = "menu.lst.armv7_omap44xx"
33
34    def setup(self, builddir=None):
35        pass
36
37    def get_buildall_target(self):
38        return "PandaboardES"
39
40class PandaboardOperations(MachineOperations):
41
42    def __init__(self, machine):
43        super(PandaboardOperations, self).__init__(machine)
44        self.picocom = None
45        self.tftp_dir = None
46        self.masterfd = None
47
48    def get_tftp_dir(self):
49        if self.tftp_dir is None:
50            self.tftp_dir = tempfile.mkdtemp(prefix="panda_")
51        return self.tftp_dir
52
53    def set_bootmodules(self, modules):
54        menulst_fullpath = os.path.join(self._machine.options.builds[0].build_dir,
55                "platforms", "arm", self._machine.menulst_template)
56        self._machine._write_menu_lst(modules.get_menu_data("/"), menulst_fullpath)
57        debug.verbose("building proper pandaboard image")
58        debug.checkcmd(["make", self._machine.imagename],
59                cwd=self._machine.options.builds[0].build_dir)
60
61    def __usbboot(self):
62        debug.verbose("Usbbooting pandaboard; press reset")
63        debug.verbose("build dir: %s" % self._machine.options.builds[0].build_dir)
64        debug.checkcmd(["make", "usbboot_panda"],
65                cwd=self._machine.options.builds[0].build_dir)
66
67    def lock(self):
68        pass
69
70    def unlock(self):
71        pass
72
73    def reboot(self):
74        self.__usbboot()
75
76    def shutdown(self):
77        '''shutdown: close picocom'''
78        # FIXME: sending C-A C-X to close picocom does not seem to work
79        #if self.masterfd is not None:
80        #    debug.verbose("Sending C-A C-X to picocom")
81        #    os.write(self.masterfd, "\x01\x24")
82        if self.picocom is not None:
83            debug.verbose("Killing picocom")
84            self.picocom.kill()
85            try:
86                os.unlink("/var/lock/LCK..ttyUSB0")
87            except OSError:
88                pass
89        self.picocom = None
90        self.masterfd = None
91
92    def get_output(self):
93        '''Use picocom to get output. This replicates part of
94        ETHMachine.lock()'''
95        (self.masterfd, slavefd) = pty.openpty()
96        self.picocom = subprocess.Popen(
97                ["picocom", "-b", "115200", "/dev/ttyUSB0"],
98                close_fds=True, stdout=slavefd, stdin=slavefd)
99        os.close(slavefd)
100        self.console_out = os.fdopen(self.masterfd, 'rb', 0)
101        return self.console_out
102
103
104class ETHRackPandaboardMachine(ARMMachineBase):
105    _machines = eth_machinedata.pandaboards
106    imagename = "armv7_omap44xx_image"
107
108    def __init__(self, options, **kwargs):
109        super(ETHRackPandaboardMachine, self).__init__(options, ETHRackPandaboardMachineOperations(self), **kwargs)
110        self.menulst_template = "menu.lst.armv7_omap44xx"
111
112    def setup(self, builddir=None):
113        pass
114
115    # pandaboard specifics
116    def get_platform(self):
117        return 'omap44xx'
118
119    def get_buildall_target(self):
120        return "PandaboardES"
121
122class ETHRackPandaboardMachineOperations(ETHBaseMachineOperations):
123
124    def __init__(self, machine):
125        super(ETHRackPandaboardMachineOperations, self).__init__(machine)
126        self._tftp_dir = None
127        self.targe_name = None
128
129    def __chmod_ar(self, file):
130        '''make file/directory readable by all'''
131        import stat
132        extra = stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH
133        if os.path.isdir(file):
134            extra |= stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
135        os.chmod(file, os.stat(file).st_mode | extra)
136
137    def get_tftp_dir(self):
138        if self._tftp_dir is None:
139            self._tftp_dir = tempfile.mkdtemp(dir=PANDA_ROOT, prefix="%s_" % self._machine.getName())
140            self.__chmod_ar(self._tftp_dir)
141        return self._tftp_dir
142
143    def set_bootmodules(self, modules):
144        menulst_fullpath = os.path.join(self._machine.options.builds[0].build_dir,
145                "platforms", "arm", self._machine.menulst_template)
146        self._machine._write_menu_lst(modules.get_menu_data("/"), menulst_fullpath)
147        source_name = os.path.join(self._machine.options.builds[0].build_dir, self._machine.imagename)
148        self.target_name = os.path.join(self.get_tftp_dir(), self._machine.imagename)
149        debug.verbose("building proper pandaboard image")
150        debug.checkcmd(["make", self._machine.imagename],
151                cwd=self._machine.options.builds[0].build_dir)
152        debug.verbose("copying %s to %s" % (source_name, self.target_name))
153        shutil.copyfile(source_name, self.target_name)
154        self.__chmod_ar(self.target_name)
155
156    def __usbboot(self):
157        pandanum = self._machine.get_machine_name()[5:]
158        imagename = os.path.relpath(self.target_name, PANDA_ROOT)
159        # send "boot PANDANUM pandaboot/$tempdir/IMAGE_NAME" to
160        # masterpanda:10000
161        debug.verbose("sending boot command for pandaboard %s; pandaboard_image %s" % (pandanum, imagename))
162        masterpanda_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
163        masterpanda_sock.connect((PANDA_BOOT_HOST, PANDA_PORT))
164        masterpanda_sock.send('boot %s pandaboot/%s\n' %
165                (pandanum, imagename))
166        masterpanda_sock.shutdown(socket.SHUT_WR)
167        while True:
168            data = masterpanda_sock.recv(1024)
169            os.write(sys.stdout.fileno(), data)
170            if data == "":
171                break
172
173    def _get_console_status(self):
174        # for Pandaboards we cannot do console -i <machine> so we grab full -i
175        # output and find relevant line here
176        proc = subprocess.Popen(["console", "-i"], stdout=subprocess.PIPE)
177        output = proc.communicate()[0]
178        assert(proc.returncode == 0)
179        output = map(str.strip, output.split("\n"))
180        return filter(lambda l: l.startswith(self._machine.get_machine_name()), output)[0]
181
182    def __rackpower(self, arg):
183        try:
184            debug.checkcmd([RACKPOWER, arg, self._machine.get_machine_name()])
185        except subprocess.CalledProcessError:
186            debug.warning("rackpower %s %s failed" %
187                          (arg, self._machine.get_machine_name()))
188
189    def reboot(self):
190        self.__usbboot()
191
192    def shutdown(self):
193        self.__rackpower('-d')
194
195for pb in ETHRackPandaboardMachine._machines:
196    class TmpMachine(ETHRackPandaboardMachine):
197        name = pb
198    MachineFactory.addMachine(pb, TmpMachine, **ETHRackPandaboardMachine._machines[pb])
199
200MachineFactory.addMachine("panda_local", PandaboardMachine,
201                          bootarch='armv7',
202                          platform='omap44xx',
203                          ncores=2)
204