Subversion Repositories freemyipod

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
82 benedikt93 1
#!/usr/bin/env python
64 benedikt93 2
#
3
#
171 farthen 4
#    Copyright 2010 TheSeven, benedikt93, Farthen
64 benedikt93 5
#
6
#
82 benedikt93 7
#    This file is part of emBIOS.
64 benedikt93 8
#
82 benedikt93 9
#    emBIOS is free software: you can redistribute it and/or
64 benedikt93 10
#    modify it under the terms of the GNU General Public License as
11
#    published by the Free Software Foundation, either version 2 of the
12
#    License, or (at your option) any later version.
13
#
82 benedikt93 14
#    emBIOS is distributed in the hope that it will be useful,
64 benedikt93 15
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
16
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
#    See the GNU General Public License for more details.
18
#
82 benedikt93 19
#    You should have received a copy of the GNU General Public License
20
#    along with emBIOS.  If not, see <http://www.gnu.org/licenses/>.
64 benedikt93 21
#
22
#
23
 
171 farthen 24
import sys
25
import os
26
import inspect
27
import re
176 farthen 28
import time
215 theseven 29
import struct
346 theseven 30
import locale
64 benedikt93 31
 
176 farthen 32
from functools import wraps
33
 
64 benedikt93 34
import libembios
171 farthen 35
from libembios import Error
36
import libembiosdata
64 benedikt93 37
 
236 farthen 38
 
171 farthen 39
class NotImplementedError(Error):
40
    pass
64 benedikt93 41
 
171 farthen 42
class ArgumentError(Error):
43
    pass
64 benedikt93 44
 
171 farthen 45
class ArgumentTypeError(Error):
46
    def __init__(self, expected, seen=False):
47
        self.expected = expected
48
        self.seen = seen
49
    def __str__(self):
50
        if self.seen:
51
            return "Expected " + str(self.expected) + " but saw " + str(self.seen)
52
        else:
53
            return "Expected " + str(self.expected) + ", but saw something else"
64 benedikt93 54
 
55
 
171 farthen 56
def usage(errormsg=None, specific=False):
57
    """
58
        Prints the usage information.
59
        It is auto generated from various places.
60
    """
61
    logger = Logger()
62
    cmddict= Commandline.cmddict
63
    doc = {}
64
    # This sorts the output of various internal functions
65
    # and puts everything in easy readable form
66
    for function in cmddict:
67
        function = cmddict[function].func
68
        docinfo = {}
69
        name = function.__name__
70
        args = inspect.getargspec(function)[0]
71
        docinfo['varargs'] = False
72
        if inspect.getargspec(function)[1]:
73
            docinfo['varargs'] = True
74
        kwargvalues = inspect.getargspec(function)[3]
75
        kwargs = {}
76
        if args:
77
            if kwargvalues:
78
                argnum = len(args) - len(kwargvalues)
79
                kwargnum = len(kwargvalues)
80
                kwargs = dict(zip(args[argnum:], kwargvalues))
81
            else:
82
                argnum = len(args)
83
        else:
84
            argnum = 0
85
        docinfo['args'] = args[1:argnum]
86
        docinfo['kwargs'] = kwargs
87
        if function.__doc__:
88
            # strip unneccessary whitespace
89
            docinfo['documentation'] = re.sub(r'\n        ', '\n', function.__doc__)
90
        else:
91
            docinfo['documentation'] = None
92
        doc[name] = docinfo
64 benedikt93 93
 
171 farthen 94
    if not specific:
95
        logger.log("Please provide a command and (if needed) parameters as command line arguments\n\n")
96
        logger.log("Available commands:\n\n")
82 benedikt93 97
    else:
171 farthen 98
        logger.log("\n")
99
    for function in sorted(doc.items()):
100
        function = function[0]
101
        if specific == False or specific == function:
102
            logger.log("  " + function + " ")
103
            for arg in doc[function]['args']:
104
                logger.log("<" + arg + "> ")
105
            if doc[function]['kwargs']:
106
                for kwarg in doc[function]['kwargs']:
107
                    logger.log("[" + kwarg + "] ")
108
            if doc[function]['varargs']:
109
                logger.log("<db1> ... <dbN>")
110
            if doc[function]['documentation']:
111
                logger.log(doc[function]['documentation']+"\n")
64 benedikt93 112
 
171 farthen 113
    logger.log("\n")
114
 
115
    if errormsg:
116
        logger.error(str(errormsg)+"\n")
117
    exit(2)
118
 
119
 
120
class Logger(object):
121
    """
122
        Simple stdout logger.
382 farthen 123
        Loglevel 4 is most verbose, Loglevel 0: Only say something if there is an error.
124
        The log function doesn't care about the loglevel and always logs to stdout.
171 farthen 125
    """
126
    def __init__(self):
127
        # Possible values: 0 (only errors), 1 (warnings), 2 (info, recommended for production use), 3 and more (debug)
128
        self.loglevel = 3
129
 
130
    def log(self, text):
131
        sys.stdout.write(text)
67 benedikt93 132
 
171 farthen 133
    def debug(self, text):
134
        if self.loglevel >= 3:
382 farthen 135
            self.log("DEBUG: " + text)
119 benedikt93 136
 
171 farthen 137
    def info(self, text):
138
        if self.loglevel >= 2:
139
            self.log(text)
82 benedikt93 140
 
382 farthen 141
    def warn(self, text):
171 farthen 142
        if self.loglevel >= 1:
143
            self.log("WARNING: " + text)
119 benedikt93 144
 
171 farthen 145
    def error(self, text):
146
        self.log("ERROR: " + text)
147
 
148
 
149
def command(func):
150
    """
151
        Decorator for all commands.
152
        The decorated function is called with (self, all, other, arguments, ...)
153
    """
176 farthen 154
    @wraps(func)
172 farthen 155
    def decorator(*args):
171 farthen 156
        return func(args[0], *args[1:])
157
    func._command = True
158
    decorator.func = func
159
    return decorator
160
 
161
 
162
def commandClass(cls):
163
    """
164
        Decorator for the class. Sets the self.cmddict of the class
165
        to all functions decorated with @command
166
    """
167
    cls.cmddict = {}
168
    for attr, value in cls.__dict__.iteritems():
169
        if getattr(value, 'func', False):
170
            if getattr(value.func, '_command', False):
171
                cls.cmddict[value.func.__name__] = value
172
    return cls
173
 
174
 
175
@commandClass
176
class Commandline(object):
177
    """
178
        If you want to create a new commandline function you just need to
179
        create a function with the name of it in this class and decorate
180
        it with the decorator @command. If you don't want to call the desired
181
        function (wrong arguments etc) just raise ArgumentError with or
341 farthen 182
        without an error message.
171 farthen 183
    """
184
    def __init__(self):
185
        self.logger = Logger()
186
        try:
187
            self.embios = libembios.Embios()
188
        except libembios.DeviceNotFoundError:
189
            self.logger.error("No emBIOS device found!")
176 farthen 190
            exit(1)
191
        self.getinfo("version")
171 farthen 192
 
193
    def _parsecommand(self, func, args):
194
        # adds self to the commandline args.
195
        # this is needed because the functions need access to their class.
196
        args.insert(0, self)
197
        if func in self.cmddict:
198
            try:
172 farthen 199
                self.cmddict[func](*args)
341 farthen 200
            except (ArgumentError, libembios.ArgumentError), e:
176 farthen 201
                usage(e, specific=func)
341 farthen 202
            except (ArgumentError, libembios.ArgumentError):
176 farthen 203
                usage("Syntax Error in function '" + func + "'", specific=func)
171 farthen 204
            except ArgumentTypeError, e:
176 farthen 205
                usage(e, specific=func)
171 farthen 206
            except NotImplementedError:
207
                self.logger.error("This function is not implemented yet!")
208
            except libembios.DeviceError, e:
209
                self.logger.error(str(e))
210
            except TypeError, e:
341 farthen 211
                # Only act on TypeErrors for the function we called, not on TypeErrors raised by another function.
171 farthen 212
                if str(e).split(" ", 1)[0] == func + "()":
213
                    self.logger.error(usage("Argument Error in '" + func + "': Wrong argument count", specific=func))
214
                else:
215
                    raise
176 farthen 216
            except libembios.usb.core.USBError:
341 farthen 217
                self.logger.error("There is a problem with the USB connection.")
171 farthen 218
        else:
219
            usage("No such command")
67 benedikt93 220
 
171 farthen 221
    @staticmethod
222
    def _bool(something):
223
        """
224
            Converts quite everything into bool.
225
        """
226
        if type(something) == bool:
227
            return something
228
        elif type(something) == int or type(something) == long:
229
            return bool(something)
230
        elif type(something == str):
231
            truelist = ['true', '1', 't', 'y', 'yes']
232
            falselist = ['false', '0', 'f', 'n', 'no']
233
            if something.lower() in truelist:
234
                return True
235
            elif something.lower() in falselist:
236
                return False
237
        raise ArgumentTypeError("bool", "'"+str(something)+"'")
238
 
239
    @staticmethod
240
    def _hexint(something):
241
        """
242
            Converts quite everything to a hexadecimal represented integer.
243
            This works for default arguments too, because it returns
244
            None when it found that it got a NoneType object.
245
        """
246
        if type(something) == int or type(something) == long:
247
            return something
248
        elif type(something) == str:
249
            try:
250
                return int(something, 16)
251
            except ValueError:
252
                raise ArgumentTypeError("hexadecimal coded integer", "'"+str(something)+"'")
253
        elif type(something) == NoneType:
254
            return None
255
        else:
256
            raise ArgumentTypeError("hexadecimal coded integer", "'"+str(something)+"'")
64 benedikt93 257
 
173 farthen 258
    @staticmethod
259
    def _hex(integer):
260
        return "0x%x" % integer
64 benedikt93 261
 
171 farthen 262
    @command
263
    def getinfo(self, infotype):
264
        """
265
            Get info on the running emBIOS.
266
            <infotype> may be either of 'version', 'packetsize', 'usermemrange'.
267
        """
268
        if infotype == "version":
271 farthen 269
            try:
343 farthen 270
                hwtype = libembiosdata.hwtypes[self.embios.lib.dev.hwtypeid]
271 farthen 271
            except KeyError:
343 farthen 272
                hwtype = "UNKNOWN (ID = " + self._hex(self.embios.lib.dev.hwtypeid) + ")"
382 farthen 273
            self.logger.info("Connected to " + \
274
                             libembiosdata.swtypes[self.embios.lib.dev.swtypeid] + \
275
                             " v" + str(self.embios.lib.dev.version.majorv) + \
276
                             "." + str(self.embios.lib.dev.version.minorv) + \
277
                             "." + str(self.embios.lib.dev.version.patchv) + \
278
                             " r" + str(self.embios.lib.dev.version.revision) + \
279
                             " running on " + hwtype + "\n")
343 farthen 280
 
171 farthen 281
        elif infotype == "packetsize":
382 farthen 282
            self.logger.info("Maximum packet sizes: \n command out: " + str(self.embios.lib.dev.packetsizelimit.cout) + \
283
                             "\n command in: " + str(self.embios.lib.dev.packetsizelimit.cin) + \
284
                             "\n data in: " + str(self.embios.lib.dev.packetsizelimit.din) + \
285
                             "\n data out: " + str(self.embios.lib.dev.packetsizelimit.dout))
343 farthen 286
 
171 farthen 287
        elif infotype == "usermemrange":
288
            resp = self.embios.getusermemrange()
382 farthen 289
            self.logger.info("The user memory range is " + \
290
                             self._hex(self.embios.lib.dev.usermem.lower) + \
291
                             " - " + \
292
                             self._hex(self.embios.lib.dev.usermem.upper - 1))
293
 
171 farthen 294
        else:
295
            raise ArgumentTypeError("one out of 'version', 'packetsize', 'usermemrange'", infotype)
64 benedikt93 296
 
171 farthen 297
    @command
298
    def reset(self, force=False):
299
        """
300
            Resets the device"
301
            If <force> is 1, the reset will be forced, otherwise it will be gracefully,
302
            which may take some time.
303
        """
304
        force = self._bool(force)
305
        if force: self.logger.info("Resetting forcefully...\n")
306
        else: self.logger.info("Resetting...\n")
307
        self.embios.reset(force)
64 benedikt93 308
 
171 farthen 309
    @command
310
    def poweroff(self, force=False):
311
        """
312
            Powers the device off
313
            If <force> is 1, the poweroff will be forced, otherwise it will be gracefully,
314
            which may take some time.
315
        """
316
        force = self._bool(force)
281 farthen 317
        if force: self.logger.info("Powering off forcefully...\n")
318
        else: self.logger.info("Powering off...\n")
319
        self.embios.poweroff(force)
64 benedikt93 320
 
171 farthen 321
    @command
322
    def uploadfile(self, addr, filename):
323
        """
324
            Uploads a file to the device
325
            <offset>: the address to upload the file to
326
            <filename>: the path to the file
327
        """
328
        addr = self._hexint(addr)
329
        try:
330
            f = open(filename, 'rb')
331
        except IOError:
332
            raise ArgumentError("File not readable. Does it exist?")
382 farthen 333
        self.logger.info("Writing file '" + filename + \
334
                         "' to memory at " + self._hex(addr) + "...")
171 farthen 335
        with f:
336
            self.embios.write(addr, f.read())
178 farthen 337
        f.close()
171 farthen 338
        self.logger.info("done\n")
176 farthen 339
 
171 farthen 340
    @command
341
    def downloadfile(self, addr, size, filename):
342
        """
343
            Uploads a file to the device
344
            <offset>: the address to upload the file to
345
            <size>: the number of bytes to be read
346
            <filename>: the path to the file
347
        """
348
        addr = self._hexint(addr)
349
        size = self._hexint(size)
350
        try:
351
            f = open(filename, 'wb')
352
        except IOError:
353
            raise ArgumentError("Can not open file for write!")
382 farthen 354
        self.logger.info("Reading data from address " + self._hex(addr) + \
355
                         " with the size " + self._hex(size) + \
356
                         " to '"+filename+"'...")
171 farthen 357
        with f:
358
            f.write(self.embios.read(addr, size))
178 farthen 359
        f.close()
171 farthen 360
        self.logger.info("done\n")
361
 
362
    @command
363
    def uploadint(self, addr, integer):
364
        """
365
            Uploads a single integer to the device
260 theseven 366
            <addr>: the address to upload the integer to
367
            <integer>: the integer to upload
171 farthen 368
        """
369
        addr = self._hexint(addr)
370
        integer = self._hexint(integer)
371
        if integer > 0xFFFFFFFF:
372
            raise ArgumentError("Specified integer too long")
260 theseven 373
        data = struct.pack("I", integer)
178 farthen 374
        self.embios.write(addr, data)
382 farthen 375
        self.logger.info("Integer '" + self._hex(integer) + \
376
                         "' written successfully to " + self._hex(addr) + "\n")
171 farthen 377
 
378
    @command
379
    def downloadint(self, addr):
380
        """
381
            Downloads a single integer from the device and prints it to the console window
236 farthen 382
            <addr>: the address to download the integer from
171 farthen 383
        """
384
        addr = self._hexint(addr)
260 theseven 385
        data = self.embios.read(addr, 4)
386
        integer = struct.unpack("I", data)[0]
382 farthen 387
        self.logger.info("Integer '" + self._hex(integer) + \
388
                         "' read from address " + self._hex(addr) + "\n")
171 farthen 389
 
390
    @command
176 farthen 391
    def i2cread(self, bus, slave, addr, size):
171 farthen 392
        """
393
            Reads data from an I2C device
394
            <bus> the bus index
395
            <slave> the slave address
396
            <addr> the start address on the I2C device
397
            <size> the number of bytes to read
398
        """
399
        bus = self._hexint(bus)
400
        slave = self._hexint(slave)
401
        addr = self._hexint(addr)
402
        size = self._hexint(size)
236 farthen 403
        data = self.embios.i2cread(bus, slave, addr, size)
404
        bytes = struct.unpack("%dB" % len(data), data)
405
        self.logger.info("Data read from I2C:\n")
406
        for index, byte in enumerate(bytes):
407
            self.logger.info("%02X: %02X\n" % (index, byte))
171 farthen 408
 
409
    @command
176 farthen 410
    def i2cwrite(self, bus, slave, addr, *args):
171 farthen 411
        """
412
            Writes data to an I2C device
413
            <bus> the bus index
414
            <slave> the slave address
415
            <addr> the start address on the I2C device
176 farthen 416
            <db1> ... <dbN> the data in single bytes, encoded in hex,
236 farthen 417
                seperated by whitespaces, eg. 37 5A 4F EB
171 farthen 418
        """
419
        bus = self._hexint(bus)
420
        slave = self._hexint(slave)
421
        addr = self._hexint(addr)
176 farthen 422
        data = ""
171 farthen 423
        for arg in args:
176 farthen 424
            data += chr(self._hexint(arg))
236 farthen 425
        self.logger.info("Writing data to I2C...\n")
176 farthen 426
        self.embios.i2cwrite(bus, slave, addr, data)
236 farthen 427
        self.logger.info("done\n")
171 farthen 428
 
429
    @command
176 farthen 430
    def console(self):
171 farthen 431
        """
176 farthen 432
            Reads data from the USB console continuously
171 farthen 433
        """
176 farthen 434
        while True:
435
            resp = self.embios.usbcread()
436
            self.logger.log(resp.data)
437
            time.sleep(0.1 / resp.maxsize * (resp.maxsize - len(resp.data)))
171 farthen 438
 
439
    @command
176 farthen 440
    def writeusbconsole(self, *args):
171 farthen 441
        """
176 farthen 442
            Writes the string <db1> ... <dbN> to the USB console.
171 farthen 443
        """
176 farthen 444
        text = ""
445
        for word in args:
446
            text += word + " "
447
        text = text[:-1]
236 farthen 448
        self.logger.info("Writing '"+ text +"' to the usb console\n")
176 farthen 449
        self.embios.usbcwrite(text)
171 farthen 450
 
451
    @command
176 farthen 452
    def readdevconsole(self, bitmask):
171 farthen 453
        """
176 farthen 454
            Reads data continuously from one or more of the device's consoles.
455
            <bitmask>: the bitmask of the consoles to read from.
171 farthen 456
        """
457
        bitmask = self._hexint(bitmask)
176 farthen 458
        while True:
459
            resp = self.embios.cread()
460
            self.logger.log(resp.data)
461
            time.sleep(0.1 / resp.maxsize * (resp.maxsize - len(resp.data)))
462
 
171 farthen 463
    @command
176 farthen 464
    def writedevconsole(self, bitmask, *args):
171 farthen 465
        """
176 farthen 466
            Writes the string <db1> ... <dbN> to one or more of the device's consoles.
467
            <bitmask>: the bitmask of the consoles to write to
171 farthen 468
        """
469
        bitmask = self._hexint(bitmask)
176 farthen 470
        text = ""
471
        for word in args:
472
            text += word + " "
473
        text = text[:-1]
382 farthen 474
        self.logger.info("Writing '" + text + \
475
                         "' to the device consoles identified with " + self._hex(bitmask) + "\n")
176 farthen 476
        self.embios.cwrite(text, bitmask)
171 farthen 477
 
478
    @command
479
    def flushconsolebuffers(self, bitmask):
480
        """
481
            flushes one or more of the device consoles' buffers.
482
            <bitmask>: the bitmask of the consoles to be flushed
483
        """
484
        bitmask = self._hexint(bitmask)
382 farthen 485
        self.logger.info("Flushing consoles identified with the bitmask " + \
486
                         self._hex(bitmask) + "\n")
236 farthen 487
        self.embios.cflush(bitmask)
171 farthen 488
 
489
    @command
490
    def getprocinfo(self):
491
        """
492
            Fetches data on the currently running processes
493
        """
173 farthen 494
        import datetime
495
        threads = self.embios.getprocinfo()
382 farthen 496
        threadload = 0
497
        idleload = 0
173 farthen 498
        for thread in threads:
392 theseven 499
            if thread.id != 0:
500
                threadload += thread.cpuload / 255.
382 farthen 501
            else:
392 theseven 502
                idleload += thread.cpuload / 255.
382 farthen 503
        coreload = 1 - (threadload + idleload)
504
        cpuload = threadload + coreload
392 theseven 505
        self.logger.info("Threads: %d, CPU load: %.1f%%, kernel load: %.1f%%, user load: %.1f%%\n\n"
506
                         % (len(threads), cpuload * 100, coreload * 100, threadload * 100))
382 farthen 507
        self.logger.info("Thread dump:\n")
508
        for thread in threads:
173 farthen 509
            self.logger.info("  "+thread.name+":\n")
382 farthen 510
            self.logger.info("    Thread id: "      + str(thread.id)+"\n")
511
            self.logger.info("    Thread type: "    + thread.type+"\n")
512
            self.logger.info("    Thread state: "   + thread.state+"\n")
513
            self.logger.info("    Block type: "     + thread.block_type+"\n")
514
            self.logger.info("    Blocked by: "     + self._hex(thread.blocked_by_ptr)+"\n")
515
            self.logger.info("    Priority: "       + str(thread.priority)+"/255\n")
392 theseven 516
            self.logger.info("    Current CPU load: %.1f%%\n" % ((thread.cpuload * 100) / 255.))
517
            self.logger.info("    CPU time (total): "+str(datetime.timedelta(microseconds = thread.cputime_total))+"\n")
382 farthen 518
            self.logger.info("    Stack address: "  + self._hex(thread.stackaddr)+"\n")
173 farthen 519
            self.logger.info("    Registers:\n")
177 farthen 520
            for registerrange in range(4):
521
                self.logger.info("      ")
522
                for register in range(registerrange, 16, 4):
523
                    registerrepr = "r"+str(register)
318 theseven 524
                    self.logger.info("{0:3s}: 0x{1:08X}   ".format(registerrepr, thread.regs["r"+str(register)]))
177 farthen 525
                self.logger.info("\n")
318 theseven 526
            self.logger.info("     cpsr: 0x{0:08X}".format(thread.regs.cpsr))
173 farthen 527
            self.logger.info("\n")
528
 
171 farthen 529
    @command
530
    def lockscheduler(self):
531
        """
532
            Locks (freezes) the scheduler
533
        """
176 farthen 534
        self.logger.info("Will now lock scheduler\n")
173 farthen 535
        self.embios.lockscheduler()
178 farthen 536
 
171 farthen 537
    @command
538
    def unlockscheduler(self):
539
        """
540
            Unlocks (unfreezes) the scheduler
541
        """
176 farthen 542
        self.logger.info("Will now unlock scheduler\n")
173 farthen 543
        self.embios.unlockscheduler()
178 farthen 544
 
171 farthen 545
    @command
546
    def suspendthread(self, threadid):
547
        """
548
            Suspends/resumes the thread with thread ID <threadid>
549
        """
550
        threadid = self._hexint(threadid)
236 farthen 551
        self.logger.info("Suspending the thread with the threadid "+self._hex(threadid)+"\n")
552
        self.embios.suspendthread(threadid)
171 farthen 553
 
554
    @command
555
    def resumethread(self, threadid):
556
        """
557
            Resumes the thread with thread ID <threadid>
558
        """
559
        threadid = self._hexint(threadid)
236 farthen 560
        self.logger.info("Resuming the thread with the threadid "+self._hex(threadid)+"\n")
173 farthen 561
        self.embios.resumethread(threadid)
171 farthen 562
 
563
    @command
564
    def killthread(self, threadid):
565
        """
566
            Kills the thread with thread ID <threadid>
567
        """
568
        threadid = self._hexint(threadid)
236 farthen 569
        self.logger.info("Killing the thread with the threadid " + self._hex(threadid) + "\n")
173 farthen 570
        self.embios.killthread(threadid)
171 farthen 571
 
572
    @command
573
    def createthread(self, nameptr, entrypoint, stackptr, stacksize, threadtype, priority, state):
574
        """
575
            Creates a new thread and returns its thread ID
576
            <namepointer> a pointer to the thread's name
577
            <entrypoint> a pointer to the entrypoint of the thread
578
            <stackpointer> a pointer to the stack of the thread
579
            <stacksize> the size of the thread's stack
580
            <type> the thread type, vaild are: 0 => user thread, 1 => system thread
581
            <priority> the priority of the thread, from 1 to 255
582
            <state> the thread's initial state, valid are: 1 => ready, 0 => suspended
583
        """
584
        nameptr = self._hexint(nameptr)
585
        entrypoint = self._hexint(entrypoint)
586
        stackpointer = self._hexint(stackpointer)
587
        stacksize = self._hexint(stacksize)
588
        priority = self._hexint(priority)
236 farthen 589
        data = self.embios.createthread(nameptr, entrypoint, stackptr, stacksize, type, priority, state)
590
        name = self.embios.readstring(nameptr)
382 farthen 591
        self.logger.info("Created a thread with the threadid " + data.id + \
592
                         ", the name \"" + name + "\"" + \
593
                         ", the entrypoint at " + self._hex(entrypoint) + \
594
                         ", the stack at " + self._hex(stackpointer) + \
595
                         " with a size of " + self._hex(stacksize) + \
596
                         " and a priority of " + self._hex(priority) + "\n")
172 farthen 597
 
598
    @command
599
    def run(self, filename):
600
        """
173 farthen 601
            Uploads the emBIOS application <filename> to
236 farthen 602
            the memory and executes it
172 farthen 603
        """
236 farthen 604
        try:
605
            f = open(filename, 'rb')
606
        except IOError:
607
            raise ArgumentError("File not readable. Does it exist?")
238 farthen 608
        with f:
609
            data = self.embios.run(f.read())
610
        self.logger.info("Executed emBIOS application \"" + data.name + "\" at address " + self._hex(data.baseaddr))
171 farthen 611
 
612
    @command
173 farthen 613
    def execimage(self, addr):
171 farthen 614
        """
173 farthen 615
            Executes the emBIOS application at <addr>.
171 farthen 616
        """
172 farthen 617
        addr = self._hexint(addr)
173 farthen 618
        self.logger.info("Starting emBIOS app at "+self._hex(addr)+"\n")
172 farthen 619
        self.embios.execimage(addr)
176 farthen 620
 
171 farthen 621
    @command
176 farthen 622
    def flushcaches(self):
171 farthen 623
        """
176 farthen 624
            Flushes the CPUs data and instruction caches.
625
        """
626
        self.logger.info("Flushing CPU data and instruction caches...")
627
        self.embios.flushcaches()
628
        self.logger.info("done\n")
629
 
630
    @command
631
    def readbootflash(self, addr_flash, addr_mem, size):
632
        """
171 farthen 633
            Reads <size> bytes from bootflash to memory.
634
            <addr_bootflsh>: the address in bootflash to read from
635
            <addr_mem>: the address in memory to copy the data to
636
        """
637
        addr_flash = self._hexint(addr_flash)
638
        addr_mem = self._hexint(addr_mem)
639
        size = self._hexint(size)
174 farthen 640
        self.logger.info("Dumping boot flash addresses "+self._hex(addr_flash)+" - "+
641
                         hex(addr_flash+size)+" to "+self._hex(addr_mem)+" - "+self._hex(addr_mem+size)+"\n")
276 theseven 642
        self.embios.lib.dev.timeout = 5000
643
        self.embios.bootflashread(addr_mem, addr_flash, size)
176 farthen 644
 
171 farthen 645
    @command
176 farthen 646
    def writebootflash(self, addr_flash, addr_mem, size, force=False):
171 farthen 647
        """
648
            Writes <size> bytes from memory to bootflash.
649
            ATTENTION: Don't call this unless you really know what you're doing!
650
            This may BRICK your device (unless it has a good recovery option)
651
            <addr_mem>: the address in memory to copy the data from
652
            <addr_bootflsh>: the address in bootflash to write to
174 farthen 653
            <force>: Use this flag to suppress the 5 seconds delay
171 farthen 654
        """
655
        addr_flash = self._hexint(addr_flash)
656
        addr_mem = self._hexint(addr_mem)
657
        size = self._hexint(size)
174 farthen 658
        force = self._bool(force)
382 farthen 659
        self.logger.warn("Writing boot flash from the memory in "+self._hex(addr_mem)+" - "+
174 farthen 660
                         hex(addr_mem+size)+" to "+self._hex(addr_flash)+" - "+self._hex(addr_flash+size)+"\n")
661
        if force == False:
382 farthen 662
            self.logger.warn("If this was not what you intended press Ctrl-C NOW")
176 farthen 663
            for i in range(10):
174 farthen 664
                self.logger.info(".")
665
                time.sleep(1)
666
            self.logger.info("\n")
276 theseven 667
        self.embios.lib.dev.timeout = 30000
668
        self.embios.bootflashwrite(addr_mem, addr_flash, size)
176 farthen 669
 
171 farthen 670
    @command
176 farthen 671
    def runfirmware(self, addr, filename):
171 farthen 672
        """
176 farthen 673
            Uploads the firmware in 'filename' to the beginning of the
674
            user memory and executes it
171 farthen 675
        """
176 farthen 676
        addr = self._hexint(addr)
677
        self.uploadfile(addr, filename)
678
        self.execfirmware(addr)
64 benedikt93 679
 
171 farthen 680
    @command
176 farthen 681
    def execfirmware(self, addr):
682
        """
683
            Executes the firmware at addr
684
        """
685
        addr = self._hexint(addr)
686
        self.logger.info("Running firmware at "+self._hex(addr)+". Bye.")
687
        self.embios.execfirmware(addr)
688
 
689
    @command
171 farthen 690
    def aesencrypt(self, addr, size, keyindex):
691
        """
172 farthen 692
            Encrypts a buffer using a hardware key
171 farthen 693
        """
694
        addr = self._hexint(addr)
695
        size = self._hexint(size)
696
        keyindex = self._hexint(keyindex)
381 theseven 697
        self.embios.lib.dev.timeout = 30000
171 farthen 698
        self.embios.aesencrypt(addr, size, keyindex)
82 benedikt93 699
 
171 farthen 700
    @command
701
    def aesdecrypt(self, addr, size, keyindex):
702
        """
172 farthen 703
            Decrypts a buffer using a hardware key
171 farthen 704
        """
705
        addr = self._hexint(addr)
706
        size = self._hexint(size)
707
        keyindex = self._hexint(keyindex)
381 theseven 708
        self.embios.lib.dev.timeout = 30000
171 farthen 709
        self.embios.aesdecrypt(addr, size, keyindex)
172 farthen 710
 
711
    @command
712
    def hmac_sha1(self, addr, size, destination):
713
        """
714
            Generates a HMAC-SHA1 hash of the buffer and saves it to 'destination'
715
        """
716
        addr = self._hexint(addr)
717
        size = self._hexint(size)
718
        destination = self._hexint(destination)
719
        sha1size = 0x14
382 farthen 720
        self.logger.info("Generating hmac-sha1 hash from the buffer at " + self._hex(addr) + \
721
                         " with the size " + self._hex(size) + " and saving it to " + \
722
                         self._hex(destination) + " - " + self._hex(destination+sha1size) + "...")
381 theseven 723
        self.embios.lib.dev.timeout = 30000
172 farthen 724
        self.embios.hmac_sha1(addr, size, destination)
725
        self.logger.info("done\n")
178 farthen 726
        data = self.embios.read(destination, sha1size)
172 farthen 727
        hash = ord(data)
173 farthen 728
        self.logger.info("The generated hash is "+self._hex(hash))
64 benedikt93 729
 
227 theseven 730
    @command
731
    def ipodnano2g_getnandinfo(self):
732
        """
733
            Target-specific function: ipodnano2g
734
            Gathers some information about the NAND chip used
735
        """
736
        data = self.embios.ipodnano2g_getnandinfo()
382 farthen 737
        self.logger.info("NAND chip type: "         + self._hex(data["type"])+"\n")
738
        self.logger.info("Number of banks: "        + str(data["banks"])+"\n")
739
        self.logger.info("Number of blocks: "       + str(data["blocks"])+"\n")
740
        self.logger.info("Number of user blocks: "  + str(data["userblocks"])+"\n")
741
        self.logger.info("Pages per block: "        + str(data["pagesperblock"]))
227 theseven 742
 
743
    @command
744
    def ipodnano2g_nandread(self, addr, start, count, doecc, checkempty):
745
        """
746
            Target-specific function: ipodnano2g
747
            Reads data from the NAND chip into memory
748
        """
749
        addr = self._hexint(addr)
750
        start = self._hexint(start)
751
        count = self._hexint(count)
752
        doecc = int(doecc)
753
        checkempty = int(checkempty)
382 farthen 754
        self.logger.info("Reading " + self._hex(count) + " NAND pages starting at " + \
755
                         self._hex(start) + " to " + self._hex(addr) + "...")
228 theseven 756
        self.embios.lib.dev.timeout = 30000
227 theseven 757
        self.embios.ipodnano2g_nandread(addr, start, count, doecc, checkempty)
758
        self.logger.info("done\n")
759
 
760
    @command
761
    def ipodnano2g_nandwrite(self, addr, start, count, doecc):
762
        """
763
            Target-specific function: ipodnano2g
764
            Writes data to the NAND chip
765
        """
766
        addr = self._hexint(addr)
767
        start = self._hexint(start)
768
        count = self._hexint(count)
769
        doecc = int(doecc)
382 farthen 770
        self.logger.info("Writing " + self._hex(count) + " NAND pages starting at " + \
771
                         self._hex(start) + " from " + self._hex(addr) + "...")
228 theseven 772
        self.embios.lib.dev.timeout = 30000
227 theseven 773
        self.embios.ipodnano2g_nandwrite(addr, start, count, doecc)
774
        self.logger.info("done\n")
775
 
776
    @command
777
    def ipodnano2g_nanderase(self, addr, start, count):
778
        """
779
            Target-specific function: ipodnano2g
780
            Erases blocks on the NAND chip and stores the results to memory
781
        """
782
        addr = self._hexint(addr)
783
        start = self._hexint(start)
784
        count = self._hexint(count)
382 farthen 785
        self.logger.info("Erasing " + self._hex(count) + " NAND blocks starting at " + \
786
                         self._hex(start) + " and logging to " + self._hex(addr) + "...")
228 theseven 787
        self.embios.lib.dev.timeout = 30000
227 theseven 788
        self.embios.ipodnano2g_nanderase(addr, start, count)
789
        self.logger.info("done\n")
790
 
228 theseven 791
    @command
792
    def ipodnano2g_dumpnand(self, filenameprefix):
793
        """
794
            Target-specific function: ipodnano2g
795
            Dumps the whole NAND chip to four files
796
        """
797
        info = self.embios.ipodnano2g_getnandinfo()
798
        self.logger.info("Dumping NAND contents...")
799
        try:
800
            infofile = open(filenameprefix+"_info.txt", 'wb')
801
            datafile = open(filenameprefix+"_data.bin", 'wb')
802
            sparefile = open(filenameprefix+"_spare.bin", 'wb')
803
            statusfile = open(filenameprefix+"_status.bin", 'wb')
804
        except IOError:
805
            raise ArgumentError("Can not open file for writing!")
382 farthen 806
        infofile.write("NAND chip type: "       + self._hex(info["type"]) + "\r\n")
807
        infofile.write("Number of banks: "      + str(info["banks"]) + "\r\n")
808
        infofile.write("Number of blocks: "     + str(info["blocks"]) + "\r\n")
809
        infofile.write("Number of user blocks: "+ str(info["userblocks"]) + "\r\n")
810
        infofile.write("Pages per block: "      + str(info["pagesperblock"]) + "\r\n")
228 theseven 811
        self.embios.lib.dev.timeout = 30000
812
        for i in range(info["banks"] * info["blocks"] * info["pagesperblock"] / 8192):
813
            self.logger.info(".")
814
            self.embios.ipodnano2g_nandread(0x08000000, i * 8192, 8192, 1, 1)
815
            datafile.write(self.embios.read(0x08000000, 0x01000000))
816
            sparefile.write(self.embios.read(0x09000000, 0x00080000))
817
            statusfile.write(self.embios.read(0x09080000, 0x00008000))
818
        infofile.close()
819
        datafile.close()
820
        sparefile.close()
821
        statusfile.close()
822
        self.logger.info("done\n")
823
 
824
    @command
825
    def ipodnano2g_wipenand(self, filename, force=False):
826
        """
827
            Target-specific function: ipodnano2g
828
            Wipes the whole NAND chip and logs the result to a file
829
            <force>: Use this flag to suppress the 5 seconds delay
830
        """
382 farthen 831
        self.logger.warn("Wiping the whole NAND chip!\n")
228 theseven 832
        if force == False:
382 farthen 833
            self.logger.warn("If this was not what you intended press Ctrl-C NOW")
228 theseven 834
            for i in range(10):
835
                self.logger.info(".")
836
                time.sleep(1)
837
            self.logger.info("\n")
838
        info = self.embios.ipodnano2g_getnandinfo()
839
        self.logger.info("Wiping NAND contents...")
840
        try:
841
            statusfile = open(filename, 'wb')
842
        except IOError:
843
            raise ArgumentError("Can not open file for writing!")
844
        self.embios.lib.dev.timeout = 30000
845
        for i in range(info["banks"] * info["blocks"] / 64):
846
            self.logger.info(".")
230 theseven 847
            self.embios.ipodnano2g_nanderase(0x08000000, i * 64, 64)
228 theseven 848
            statusfile.write(self.embios.read(0x08000000, 0x00000100))
849
        statusfile.close()
850
        self.logger.info("done\n")
851
 
346 theseven 852
    @command
853
    def ipodclassic_writebbt(self, tempaddr, filename):
854
        """
855
            Target-specific function: ipodclassic
856
            Uploads the bad block table <filename> to
857
            memory at <tempaddr> and writes it to the hard disk
858
        """
859
        tempaddr = self._hexint(tempaddr)
860
        try:
861
            f = open(filename, 'rb')
862
        except IOError:
863
            raise ArgumentError("File not readable. Does it exist?")
864
        self.embios.lib.dev.timeout = 30000
865
        self.logger.info("Writing bad block table to disk...")
866
        data = self.embios.ipodclassic_writebbt(f.read(), tempaddr)
867
        f.close()
868
        self.logger.info(" done\n")
869
 
870
    @command
379 theseven 871
    def getvolumeinfo(self, volume):
872
        """
873
            Gathers some information about a storage volume used
874
        """
875
        volume = self._hexint(volume)
876
        data = self.embios.storage_get_info(volume)
877
        self.logger.info("Sector size: "+str(data["sectorsize"])+"\n")
878
        self.logger.info("Number of sectors: "+str(data["numsectors"])+"\n")
879
        self.logger.info("Vendor: "+data["vendor"]+"\n")
880
        self.logger.info("Product: "+data["product"]+"\n")
881
        self.logger.info("Revision: "+data["revision"])
882
 
883
    @command
884
    def readrawstorage(self, volume, sector, count, addr):
885
        """
886
            Reads <count> sectors starting at <sector> from storage <volume> to memory at <addr>.
887
        """
888
        volume = self._hexint(volume)
889
        sector = self._hexint(sector)
890
        count = self._hexint(count)
891
        addr = self._hexint(addr)
892
        self.logger.info("Reading volume %s sectors %X - %X to %08X..." % (volume, sector, sector + count - 1, addr))
893
        self.embios.lib.dev.timeout = 50000
894
        self.embios.storage_read_sectors_md(volume, sector, count, addr)
895
        self.logger.info("done\n")
896
 
897
    @command
898
    def writerawstorage(self, volume, sector, count, addr):
899
        """
900
            Writes memory contents at <addr> to <count> sectors starting at <sector> on storage <volume>.
901
        """
902
        volume = self._hexint(volume)
903
        sector = self._hexint(sector)
904
        count = self._hexint(count)
905
        addr = self._hexint(addr)
906
        self.logger.info("Writing %08X to volume %s sectors %X - %X..." % (addr, volume, sector, sector + count - 1))
907
        self.embios.lib.dev.timeout = 50000
908
        self.embios.storage_write_sectors_md(volume, sector, count, addr)
909
        self.logger.info("done\n")
910
 
911
    @command
912
    def readrawstoragefile(self, volume, sector, count, file, buffer = False, buffsize = "100000"):
913
        """
914
            Reads <count> sectors starting at <sector> from storage <volume> to file <file>,
915
            buffering them in memory at <buffer> in chunks of <buffsize> bytes (both optional).
916
        """
917
        volume = self._hexint(volume)
918
        sector = self._hexint(sector)
919
        count = self._hexint(count)
920
        if buffer == False: buffer = self.embios.lib.dev.usermem.lower
921
        else: buffer = self._hexint(buffer)
922
        buffsize = self._hexint(buffsize)
923
        try:
924
            f = open(file, 'wb')
925
        except IOError:
926
            raise ArgumentError("Could not open local file for writing.")
927
        self.logger.info("Reading volume %s sectors %X - %X to %s..." % (volume, sector, sector + count - 1, file))
928
        self.embios.lib.dev.timeout = 50000
929
        storageinfo = self.embios.storage_get_info(volume)
930
        while count > 0:
931
            sectors = min(count, int(buffsize / storageinfo.sectorsize))
932
            self.embios.storage_read_sectors_md(volume, sector, sectors, buffer)
933
            f.write(self.embios.read(buffer, storageinfo.sectorsize * sectors))
934
            sector = sector + sectors
935
            count = count - sectors
936
        f.close()
937
        self.logger.info("done\n")
938
 
939
    @command
940
    def writerawstoragefile(self, volume, sector, count, file, buffer = False, buffsize = "100000"):
941
        """
942
            Writes contents of <file> to <count> sectors starting at <sector> on storage <volume>,
943
            buffering them in memory at <buffer> in chunks of <buffsize> bytes (both optional).
944
        """
945
        volume = self._hexint(volume)
946
        sector = self._hexint(sector)
947
        count = self._hexint(count)
948
        if buffer == False: buffer = self.embios.lib.dev.usermem.lower
949
        else: buffer = self._hexint(buffer)
950
        buffsize = self._hexint(buffsize)
951
        try:
952
            f = open(file, 'rb')
953
        except IOError:
954
            raise ArgumentError("Could not open local file for reading.")
955
        self.logger.info("Writing %s to volume %s sectors %X - %X..." % (file, volume, sector, sector + count - 1))
956
        self.embios.lib.dev.timeout = 50000
957
        storageinfo = self.embios.storage_get_info(volume)
958
        while count > 0:
959
            sectors = min(count, int(buffsize / storageinfo.sectorsize))
960
            bytes = storageinfo.sectorsize * sectors
961
            data = f.read(bytes)
962
            if len(data) == 0: break
963
            while len(data) < bytes: data = data + f.read(bytes - len(data))
964
            self.embios.write(buffer, data)
965
            self.embios.storage_write_sectors_md(volume, sector, sectors, buffer)
966
            sector = sector + sectors
967
            count = count - sectors
968
        f.close()
969
        self.logger.info("done\n")
970
 
971
    @command
346 theseven 972
    def mkdir(self, dirname):
973
        """
974
            Creates a directory
975
        """
976
        self.embios.lib.dev.timeout = 30000
977
        self.logger.info("Creating directory " + dirname + "...")
978
        self.embios.dir_create(dirname)
979
        self.logger.info(" done\n")
980
 
981
    @command
982
    def rmdir(self, dirname):
983
        """
984
            Removes an empty directory
985
        """
986
        self.embios.lib.dev.timeout = 30000
987
        self.logger.info("Removing directory " + dirname + "...")
988
        self.embios.dir_remove(dirname)
989
        self.logger.info(" done\n")
990
 
991
    @command
349 theseven 992
    def rm(self, filename):
346 theseven 993
        """
994
            Removes a file
995
        """
996
        self.embios.lib.dev.timeout = 30000
997
        self.logger.info("Removing file " + filename + "...")
998
        self.embios.file_unlink(filename)
999
        self.logger.info(" done\n")
1000
 
1001
    @command
352 theseven 1002
    def mv(self, oldname, newname):
350 theseven 1003
        """
352 theseven 1004
            Renames or moves a file or directory
350 theseven 1005
        """
1006
        self.embios.lib.dev.timeout = 30000
1007
        self.logger.info("Renaming " + oldname + " to " + newname + "...")
1008
        self.embios.file_rename(oldname, newname)
1009
        self.logger.info(" done\n")
1010
 
1011
    @command
351 theseven 1012
    def get(self, remotename, localname, buffer = False, buffsize = "10000"):
346 theseven 1013
        """
1014
            Downloads a file
1015
        """
351 theseven 1016
        if buffer == False: buffer = self.embios.lib.dev.usermem.lower
1017
        else: buffer = self._hexint(buffer)
346 theseven 1018
        buffsize = self._hexint(buffsize)
1019
        try:
1020
            f = open(localname, 'wb')
1021
        except IOError:
1022
            raise ArgumentError("Could not open local file for writing.")
1023
        self.embios.lib.dev.timeout = 30000
1024
        self.logger.info("Downloading file " + remotename + " to " + localname + "...")
1025
        fd = self.embios.file_open(remotename, 0)
1026
        size = self.embios.file_size(fd)
1027
        while size > 0:
370 theseven 1028
            bytes = self.embios.file_read(fd, buffer, buffsize)
346 theseven 1029
            f.write(self.embios.read(buffer, bytes))
1030
            size = size - bytes
1031
        self.embios.file_close(fd)
1032
        f.close()
1033
        self.logger.info(" done\n")
1034
 
1035
    @command
351 theseven 1036
    def put(self, localname, remotename, buffer = False, buffsize = "10000"):
346 theseven 1037
        """
1038
            Uploads a file
1039
        """
351 theseven 1040
        if buffer == False: buffer = self.embios.lib.dev.usermem.lower
1041
        else: buffer = self._hexint(buffer)
346 theseven 1042
        buffsize = self._hexint(buffsize)
1043
        try:
1044
            f = open(localname, 'rb')
1045
        except IOError:
1046
            raise ArgumentError("Could not open local file for reading.")
1047
        self.embios.lib.dev.timeout = 30000
1048
        self.logger.info("Uploading file " + localname + " to " + remotename + "...")
1049
        fd = self.embios.file_open(remotename, 0x15)
1050
        while True:
1051
            data = f.read(buffsize)
1052
            if len(data) == 0: break
1053
            self.embios.write(buffer, data)
1054
            bytes = 0
1055
            while bytes < len(data):
1056
                bytes = bytes + self.embios.file_write(fd, buffer + bytes, len(data) - bytes)
1057
        self.embios.file_close(fd)
1058
        f.close()
1059
        self.logger.info(" done\n")
1060
 
1061
    @command
351 theseven 1062
    def ls(self, path = "/"):
346 theseven 1063
        """
1064
            Lists all files in the specified path
1065
        """
1066
        self.embios.lib.dev.timeout = 30000
1067
        handle = self.embios.dir_open(path)
1068
        self.logger.info("Directory listing of " + path + ":\n")
1069
        while True:
1070
            try:
1071
                entry = self.embios.dir_read(handle)
1072
                if entry.attributes & 0x10: size = "DIR"
1073
                else: size = locale.format("%d", entry.size, True).rjust(13)
1074
                self.logger.info(entry.name.ljust(50) + " - " + size + "\n")
1075
            except: break
1076
        self.embios.dir_close(handle)
1077
 
171 farthen 1078
if __name__ == "__main__":
1079
    if len(sys.argv) < 2:
1080
        usage("No command specified")
382 farthen 1081
    try:
1082
        interface = Commandline()
1083
        interface._parsecommand(sys.argv[1], sys.argv[2:])
1084
    except KeyboardInterrupt:
1085
        sys.exit()