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
        cpuload = 1
497
        threadload = 0
498
        idleload = 0
173 farthen 499
        for thread in threads:
382 farthen 500
            if thread.priority != 0:
501
                threadload += (thread.cpuload*100)/255
502
            else:
503
                idleload += (thread.cpuload*100)/255
504
        coreload = 1 - (threadload + idleload)
505
        cpuload = threadload + coreload
506
        self.logger.info("The device has " + str(len(threads)) + " running threads.\n" + \
507
                         "It is running at " + str(cpuload * 100) + "% cpu load with a core load of " + \
508
                         str(coreload * 100) + "%, a thread load of " + str(threadload * 100) + "% " + \
509
                         "and an idle load of " + str(idleload * 100) + "%\n\n")
510
        self.logger.info("Thread dump:\n")
511
        for thread in threads:
173 farthen 512
            self.logger.info("  "+thread.name+":\n")
382 farthen 513
            self.logger.info("    Thread id: "      + str(thread.id)+"\n")
514
            self.logger.info("    Thread type: "    + thread.type+"\n")
515
            self.logger.info("    Thread state: "   + thread.state+"\n")
516
            self.logger.info("    Block type: "     + thread.block_type+"\n")
517
            self.logger.info("    Blocked by: "     + self._hex(thread.blocked_by_ptr)+"\n")
518
            self.logger.info("    Priority: "       + str(thread.priority)+"/255\n")
324 theseven 519
            self.logger.info("    Current CPU load: "+str((thread.cpuload*100)/255)+"%\n")
173 farthen 520
            self.logger.info("    CPU time (total): "+str(datetime.timedelta(microseconds=thread.cputime_total))+"\n")
382 farthen 521
            self.logger.info("    Stack address: "  + self._hex(thread.stackaddr)+"\n")
173 farthen 522
            self.logger.info("    Registers:\n")
177 farthen 523
            for registerrange in range(4):
524
                self.logger.info("      ")
525
                for register in range(registerrange, 16, 4):
526
                    registerrepr = "r"+str(register)
318 theseven 527
                    self.logger.info("{0:3s}: 0x{1:08X}   ".format(registerrepr, thread.regs["r"+str(register)]))
177 farthen 528
                self.logger.info("\n")
318 theseven 529
            self.logger.info("     cpsr: 0x{0:08X}".format(thread.regs.cpsr))
173 farthen 530
            self.logger.info("\n")
531
 
171 farthen 532
    @command
533
    def lockscheduler(self):
534
        """
535
            Locks (freezes) the scheduler
536
        """
176 farthen 537
        self.logger.info("Will now lock scheduler\n")
173 farthen 538
        self.embios.lockscheduler()
178 farthen 539
 
171 farthen 540
    @command
541
    def unlockscheduler(self):
542
        """
543
            Unlocks (unfreezes) the scheduler
544
        """
176 farthen 545
        self.logger.info("Will now unlock scheduler\n")
173 farthen 546
        self.embios.unlockscheduler()
178 farthen 547
 
171 farthen 548
    @command
549
    def suspendthread(self, threadid):
550
        """
551
            Suspends/resumes the thread with thread ID <threadid>
552
        """
553
        threadid = self._hexint(threadid)
236 farthen 554
        self.logger.info("Suspending the thread with the threadid "+self._hex(threadid)+"\n")
555
        self.embios.suspendthread(threadid)
171 farthen 556
 
557
    @command
558
    def resumethread(self, threadid):
559
        """
560
            Resumes the thread with thread ID <threadid>
561
        """
562
        threadid = self._hexint(threadid)
236 farthen 563
        self.logger.info("Resuming the thread with the threadid "+self._hex(threadid)+"\n")
173 farthen 564
        self.embios.resumethread(threadid)
171 farthen 565
 
566
    @command
567
    def killthread(self, threadid):
568
        """
569
            Kills the thread with thread ID <threadid>
570
        """
571
        threadid = self._hexint(threadid)
236 farthen 572
        self.logger.info("Killing the thread with the threadid " + self._hex(threadid) + "\n")
173 farthen 573
        self.embios.killthread(threadid)
171 farthen 574
 
575
    @command
576
    def createthread(self, nameptr, entrypoint, stackptr, stacksize, threadtype, priority, state):
577
        """
578
            Creates a new thread and returns its thread ID
579
            <namepointer> a pointer to the thread's name
580
            <entrypoint> a pointer to the entrypoint of the thread
581
            <stackpointer> a pointer to the stack of the thread
582
            <stacksize> the size of the thread's stack
583
            <type> the thread type, vaild are: 0 => user thread, 1 => system thread
584
            <priority> the priority of the thread, from 1 to 255
585
            <state> the thread's initial state, valid are: 1 => ready, 0 => suspended
586
        """
587
        nameptr = self._hexint(nameptr)
588
        entrypoint = self._hexint(entrypoint)
589
        stackpointer = self._hexint(stackpointer)
590
        stacksize = self._hexint(stacksize)
591
        priority = self._hexint(priority)
236 farthen 592
        data = self.embios.createthread(nameptr, entrypoint, stackptr, stacksize, type, priority, state)
593
        name = self.embios.readstring(nameptr)
382 farthen 594
        self.logger.info("Created a thread with the threadid " + data.id + \
595
                         ", the name \"" + name + "\"" + \
596
                         ", the entrypoint at " + self._hex(entrypoint) + \
597
                         ", the stack at " + self._hex(stackpointer) + \
598
                         " with a size of " + self._hex(stacksize) + \
599
                         " and a priority of " + self._hex(priority) + "\n")
172 farthen 600
 
601
    @command
602
    def run(self, filename):
603
        """
173 farthen 604
            Uploads the emBIOS application <filename> to
236 farthen 605
            the memory and executes it
172 farthen 606
        """
236 farthen 607
        try:
608
            f = open(filename, 'rb')
609
        except IOError:
610
            raise ArgumentError("File not readable. Does it exist?")
238 farthen 611
        with f:
612
            data = self.embios.run(f.read())
613
        self.logger.info("Executed emBIOS application \"" + data.name + "\" at address " + self._hex(data.baseaddr))
171 farthen 614
 
615
    @command
173 farthen 616
    def execimage(self, addr):
171 farthen 617
        """
173 farthen 618
            Executes the emBIOS application at <addr>.
171 farthen 619
        """
172 farthen 620
        addr = self._hexint(addr)
173 farthen 621
        self.logger.info("Starting emBIOS app at "+self._hex(addr)+"\n")
172 farthen 622
        self.embios.execimage(addr)
176 farthen 623
 
171 farthen 624
    @command
176 farthen 625
    def flushcaches(self):
171 farthen 626
        """
176 farthen 627
            Flushes the CPUs data and instruction caches.
628
        """
629
        self.logger.info("Flushing CPU data and instruction caches...")
630
        self.embios.flushcaches()
631
        self.logger.info("done\n")
632
 
633
    @command
634
    def readbootflash(self, addr_flash, addr_mem, size):
635
        """
171 farthen 636
            Reads <size> bytes from bootflash to memory.
637
            <addr_bootflsh>: the address in bootflash to read from
638
            <addr_mem>: the address in memory to copy the data to
639
        """
640
        addr_flash = self._hexint(addr_flash)
641
        addr_mem = self._hexint(addr_mem)
642
        size = self._hexint(size)
174 farthen 643
        self.logger.info("Dumping boot flash addresses "+self._hex(addr_flash)+" - "+
644
                         hex(addr_flash+size)+" to "+self._hex(addr_mem)+" - "+self._hex(addr_mem+size)+"\n")
276 theseven 645
        self.embios.lib.dev.timeout = 5000
646
        self.embios.bootflashread(addr_mem, addr_flash, size)
176 farthen 647
 
171 farthen 648
    @command
176 farthen 649
    def writebootflash(self, addr_flash, addr_mem, size, force=False):
171 farthen 650
        """
651
            Writes <size> bytes from memory to bootflash.
652
            ATTENTION: Don't call this unless you really know what you're doing!
653
            This may BRICK your device (unless it has a good recovery option)
654
            <addr_mem>: the address in memory to copy the data from
655
            <addr_bootflsh>: the address in bootflash to write to
174 farthen 656
            <force>: Use this flag to suppress the 5 seconds delay
171 farthen 657
        """
658
        addr_flash = self._hexint(addr_flash)
659
        addr_mem = self._hexint(addr_mem)
660
        size = self._hexint(size)
174 farthen 661
        force = self._bool(force)
382 farthen 662
        self.logger.warn("Writing boot flash from the memory in "+self._hex(addr_mem)+" - "+
174 farthen 663
                         hex(addr_mem+size)+" to "+self._hex(addr_flash)+" - "+self._hex(addr_flash+size)+"\n")
664
        if force == False:
382 farthen 665
            self.logger.warn("If this was not what you intended press Ctrl-C NOW")
176 farthen 666
            for i in range(10):
174 farthen 667
                self.logger.info(".")
668
                time.sleep(1)
669
            self.logger.info("\n")
276 theseven 670
        self.embios.lib.dev.timeout = 30000
671
        self.embios.bootflashwrite(addr_mem, addr_flash, size)
176 farthen 672
 
171 farthen 673
    @command
176 farthen 674
    def runfirmware(self, addr, filename):
171 farthen 675
        """
176 farthen 676
            Uploads the firmware in 'filename' to the beginning of the
677
            user memory and executes it
171 farthen 678
        """
176 farthen 679
        addr = self._hexint(addr)
680
        self.uploadfile(addr, filename)
681
        self.execfirmware(addr)
64 benedikt93 682
 
171 farthen 683
    @command
176 farthen 684
    def execfirmware(self, addr):
685
        """
686
            Executes the firmware at addr
687
        """
688
        addr = self._hexint(addr)
689
        self.logger.info("Running firmware at "+self._hex(addr)+". Bye.")
690
        self.embios.execfirmware(addr)
691
 
692
    @command
171 farthen 693
    def aesencrypt(self, addr, size, keyindex):
694
        """
172 farthen 695
            Encrypts a buffer using a hardware key
171 farthen 696
        """
697
        addr = self._hexint(addr)
698
        size = self._hexint(size)
699
        keyindex = self._hexint(keyindex)
381 theseven 700
        self.embios.lib.dev.timeout = 30000
171 farthen 701
        self.embios.aesencrypt(addr, size, keyindex)
82 benedikt93 702
 
171 farthen 703
    @command
704
    def aesdecrypt(self, addr, size, keyindex):
705
        """
172 farthen 706
            Decrypts a buffer using a hardware key
171 farthen 707
        """
708
        addr = self._hexint(addr)
709
        size = self._hexint(size)
710
        keyindex = self._hexint(keyindex)
381 theseven 711
        self.embios.lib.dev.timeout = 30000
171 farthen 712
        self.embios.aesdecrypt(addr, size, keyindex)
172 farthen 713
 
714
    @command
715
    def hmac_sha1(self, addr, size, destination):
716
        """
717
            Generates a HMAC-SHA1 hash of the buffer and saves it to 'destination'
718
        """
719
        addr = self._hexint(addr)
720
        size = self._hexint(size)
721
        destination = self._hexint(destination)
722
        sha1size = 0x14
382 farthen 723
        self.logger.info("Generating hmac-sha1 hash from the buffer at " + self._hex(addr) + \
724
                         " with the size " + self._hex(size) + " and saving it to " + \
725
                         self._hex(destination) + " - " + self._hex(destination+sha1size) + "...")
381 theseven 726
        self.embios.lib.dev.timeout = 30000
172 farthen 727
        self.embios.hmac_sha1(addr, size, destination)
728
        self.logger.info("done\n")
178 farthen 729
        data = self.embios.read(destination, sha1size)
172 farthen 730
        hash = ord(data)
173 farthen 731
        self.logger.info("The generated hash is "+self._hex(hash))
64 benedikt93 732
 
227 theseven 733
    @command
734
    def ipodnano2g_getnandinfo(self):
735
        """
736
            Target-specific function: ipodnano2g
737
            Gathers some information about the NAND chip used
738
        """
739
        data = self.embios.ipodnano2g_getnandinfo()
382 farthen 740
        self.logger.info("NAND chip type: "         + self._hex(data["type"])+"\n")
741
        self.logger.info("Number of banks: "        + str(data["banks"])+"\n")
742
        self.logger.info("Number of blocks: "       + str(data["blocks"])+"\n")
743
        self.logger.info("Number of user blocks: "  + str(data["userblocks"])+"\n")
744
        self.logger.info("Pages per block: "        + str(data["pagesperblock"]))
227 theseven 745
 
746
    @command
747
    def ipodnano2g_nandread(self, addr, start, count, doecc, checkempty):
748
        """
749
            Target-specific function: ipodnano2g
750
            Reads data from the NAND chip into memory
751
        """
752
        addr = self._hexint(addr)
753
        start = self._hexint(start)
754
        count = self._hexint(count)
755
        doecc = int(doecc)
756
        checkempty = int(checkempty)
382 farthen 757
        self.logger.info("Reading " + self._hex(count) + " NAND pages starting at " + \
758
                         self._hex(start) + " to " + self._hex(addr) + "...")
228 theseven 759
        self.embios.lib.dev.timeout = 30000
227 theseven 760
        self.embios.ipodnano2g_nandread(addr, start, count, doecc, checkempty)
761
        self.logger.info("done\n")
762
 
763
    @command
764
    def ipodnano2g_nandwrite(self, addr, start, count, doecc):
765
        """
766
            Target-specific function: ipodnano2g
767
            Writes data to the NAND chip
768
        """
769
        addr = self._hexint(addr)
770
        start = self._hexint(start)
771
        count = self._hexint(count)
772
        doecc = int(doecc)
382 farthen 773
        self.logger.info("Writing " + self._hex(count) + " NAND pages starting at " + \
774
                         self._hex(start) + " from " + self._hex(addr) + "...")
228 theseven 775
        self.embios.lib.dev.timeout = 30000
227 theseven 776
        self.embios.ipodnano2g_nandwrite(addr, start, count, doecc)
777
        self.logger.info("done\n")
778
 
779
    @command
780
    def ipodnano2g_nanderase(self, addr, start, count):
781
        """
782
            Target-specific function: ipodnano2g
783
            Erases blocks on the NAND chip and stores the results to memory
784
        """
785
        addr = self._hexint(addr)
786
        start = self._hexint(start)
787
        count = self._hexint(count)
382 farthen 788
        self.logger.info("Erasing " + self._hex(count) + " NAND blocks starting at " + \
789
                         self._hex(start) + " and logging to " + self._hex(addr) + "...")
228 theseven 790
        self.embios.lib.dev.timeout = 30000
227 theseven 791
        self.embios.ipodnano2g_nanderase(addr, start, count)
792
        self.logger.info("done\n")
793
 
228 theseven 794
    @command
795
    def ipodnano2g_dumpnand(self, filenameprefix):
796
        """
797
            Target-specific function: ipodnano2g
798
            Dumps the whole NAND chip to four files
799
        """
800
        info = self.embios.ipodnano2g_getnandinfo()
801
        self.logger.info("Dumping NAND contents...")
802
        try:
803
            infofile = open(filenameprefix+"_info.txt", 'wb')
804
            datafile = open(filenameprefix+"_data.bin", 'wb')
805
            sparefile = open(filenameprefix+"_spare.bin", 'wb')
806
            statusfile = open(filenameprefix+"_status.bin", 'wb')
807
        except IOError:
808
            raise ArgumentError("Can not open file for writing!")
382 farthen 809
        infofile.write("NAND chip type: "       + self._hex(info["type"]) + "\r\n")
810
        infofile.write("Number of banks: "      + str(info["banks"]) + "\r\n")
811
        infofile.write("Number of blocks: "     + str(info["blocks"]) + "\r\n")
812
        infofile.write("Number of user blocks: "+ str(info["userblocks"]) + "\r\n")
813
        infofile.write("Pages per block: "      + str(info["pagesperblock"]) + "\r\n")
228 theseven 814
        self.embios.lib.dev.timeout = 30000
815
        for i in range(info["banks"] * info["blocks"] * info["pagesperblock"] / 8192):
816
            self.logger.info(".")
817
            self.embios.ipodnano2g_nandread(0x08000000, i * 8192, 8192, 1, 1)
818
            datafile.write(self.embios.read(0x08000000, 0x01000000))
819
            sparefile.write(self.embios.read(0x09000000, 0x00080000))
820
            statusfile.write(self.embios.read(0x09080000, 0x00008000))
821
        infofile.close()
822
        datafile.close()
823
        sparefile.close()
824
        statusfile.close()
825
        self.logger.info("done\n")
826
 
827
    @command
828
    def ipodnano2g_wipenand(self, filename, force=False):
829
        """
830
            Target-specific function: ipodnano2g
831
            Wipes the whole NAND chip and logs the result to a file
832
            <force>: Use this flag to suppress the 5 seconds delay
833
        """
382 farthen 834
        self.logger.warn("Wiping the whole NAND chip!\n")
228 theseven 835
        if force == False:
382 farthen 836
            self.logger.warn("If this was not what you intended press Ctrl-C NOW")
228 theseven 837
            for i in range(10):
838
                self.logger.info(".")
839
                time.sleep(1)
840
            self.logger.info("\n")
841
        info = self.embios.ipodnano2g_getnandinfo()
842
        self.logger.info("Wiping NAND contents...")
843
        try:
844
            statusfile = open(filename, 'wb')
845
        except IOError:
846
            raise ArgumentError("Can not open file for writing!")
847
        self.embios.lib.dev.timeout = 30000
848
        for i in range(info["banks"] * info["blocks"] / 64):
849
            self.logger.info(".")
230 theseven 850
            self.embios.ipodnano2g_nanderase(0x08000000, i * 64, 64)
228 theseven 851
            statusfile.write(self.embios.read(0x08000000, 0x00000100))
852
        statusfile.close()
853
        self.logger.info("done\n")
854
 
346 theseven 855
    @command
856
    def ipodclassic_writebbt(self, tempaddr, filename):
857
        """
858
            Target-specific function: ipodclassic
859
            Uploads the bad block table <filename> to
860
            memory at <tempaddr> and writes it to the hard disk
861
        """
862
        tempaddr = self._hexint(tempaddr)
863
        try:
864
            f = open(filename, 'rb')
865
        except IOError:
866
            raise ArgumentError("File not readable. Does it exist?")
867
        self.embios.lib.dev.timeout = 30000
868
        self.logger.info("Writing bad block table to disk...")
869
        data = self.embios.ipodclassic_writebbt(f.read(), tempaddr)
870
        f.close()
871
        self.logger.info(" done\n")
872
 
873
    @command
379 theseven 874
    def getvolumeinfo(self, volume):
875
        """
876
            Gathers some information about a storage volume used
877
        """
878
        volume = self._hexint(volume)
879
        data = self.embios.storage_get_info(volume)
880
        self.logger.info("Sector size: "+str(data["sectorsize"])+"\n")
881
        self.logger.info("Number of sectors: "+str(data["numsectors"])+"\n")
882
        self.logger.info("Vendor: "+data["vendor"]+"\n")
883
        self.logger.info("Product: "+data["product"]+"\n")
884
        self.logger.info("Revision: "+data["revision"])
885
 
886
    @command
887
    def readrawstorage(self, volume, sector, count, addr):
888
        """
889
            Reads <count> sectors starting at <sector> from storage <volume> to memory at <addr>.
890
        """
891
        volume = self._hexint(volume)
892
        sector = self._hexint(sector)
893
        count = self._hexint(count)
894
        addr = self._hexint(addr)
895
        self.logger.info("Reading volume %s sectors %X - %X to %08X..." % (volume, sector, sector + count - 1, addr))
896
        self.embios.lib.dev.timeout = 50000
897
        self.embios.storage_read_sectors_md(volume, sector, count, addr)
898
        self.logger.info("done\n")
899
 
900
    @command
901
    def writerawstorage(self, volume, sector, count, addr):
902
        """
903
            Writes memory contents at <addr> to <count> sectors starting at <sector> on storage <volume>.
904
        """
905
        volume = self._hexint(volume)
906
        sector = self._hexint(sector)
907
        count = self._hexint(count)
908
        addr = self._hexint(addr)
909
        self.logger.info("Writing %08X to volume %s sectors %X - %X..." % (addr, volume, sector, sector + count - 1))
910
        self.embios.lib.dev.timeout = 50000
911
        self.embios.storage_write_sectors_md(volume, sector, count, addr)
912
        self.logger.info("done\n")
913
 
914
    @command
915
    def readrawstoragefile(self, volume, sector, count, file, buffer = False, buffsize = "100000"):
916
        """
917
            Reads <count> sectors starting at <sector> from storage <volume> to file <file>,
918
            buffering them in memory at <buffer> in chunks of <buffsize> bytes (both optional).
919
        """
920
        volume = self._hexint(volume)
921
        sector = self._hexint(sector)
922
        count = self._hexint(count)
923
        if buffer == False: buffer = self.embios.lib.dev.usermem.lower
924
        else: buffer = self._hexint(buffer)
925
        buffsize = self._hexint(buffsize)
926
        try:
927
            f = open(file, 'wb')
928
        except IOError:
929
            raise ArgumentError("Could not open local file for writing.")
930
        self.logger.info("Reading volume %s sectors %X - %X to %s..." % (volume, sector, sector + count - 1, file))
931
        self.embios.lib.dev.timeout = 50000
932
        storageinfo = self.embios.storage_get_info(volume)
933
        while count > 0:
934
            sectors = min(count, int(buffsize / storageinfo.sectorsize))
935
            self.embios.storage_read_sectors_md(volume, sector, sectors, buffer)
936
            f.write(self.embios.read(buffer, storageinfo.sectorsize * sectors))
937
            sector = sector + sectors
938
            count = count - sectors
939
        f.close()
940
        self.logger.info("done\n")
941
 
942
    @command
943
    def writerawstoragefile(self, volume, sector, count, file, buffer = False, buffsize = "100000"):
944
        """
945
            Writes contents of <file> to <count> sectors starting at <sector> on storage <volume>,
946
            buffering them in memory at <buffer> in chunks of <buffsize> bytes (both optional).
947
        """
948
        volume = self._hexint(volume)
949
        sector = self._hexint(sector)
950
        count = self._hexint(count)
951
        if buffer == False: buffer = self.embios.lib.dev.usermem.lower
952
        else: buffer = self._hexint(buffer)
953
        buffsize = self._hexint(buffsize)
954
        try:
955
            f = open(file, 'rb')
956
        except IOError:
957
            raise ArgumentError("Could not open local file for reading.")
958
        self.logger.info("Writing %s to volume %s sectors %X - %X..." % (file, volume, sector, sector + count - 1))
959
        self.embios.lib.dev.timeout = 50000
960
        storageinfo = self.embios.storage_get_info(volume)
961
        while count > 0:
962
            sectors = min(count, int(buffsize / storageinfo.sectorsize))
963
            bytes = storageinfo.sectorsize * sectors
964
            data = f.read(bytes)
965
            if len(data) == 0: break
966
            while len(data) < bytes: data = data + f.read(bytes - len(data))
967
            self.embios.write(buffer, data)
968
            self.embios.storage_write_sectors_md(volume, sector, sectors, buffer)
969
            sector = sector + sectors
970
            count = count - sectors
971
        f.close()
972
        self.logger.info("done\n")
973
 
974
    @command
346 theseven 975
    def mkdir(self, dirname):
976
        """
977
            Creates a directory
978
        """
979
        self.embios.lib.dev.timeout = 30000
980
        self.logger.info("Creating directory " + dirname + "...")
981
        self.embios.dir_create(dirname)
982
        self.logger.info(" done\n")
983
 
984
    @command
985
    def rmdir(self, dirname):
986
        """
987
            Removes an empty directory
988
        """
989
        self.embios.lib.dev.timeout = 30000
990
        self.logger.info("Removing directory " + dirname + "...")
991
        self.embios.dir_remove(dirname)
992
        self.logger.info(" done\n")
993
 
994
    @command
349 theseven 995
    def rm(self, filename):
346 theseven 996
        """
997
            Removes a file
998
        """
999
        self.embios.lib.dev.timeout = 30000
1000
        self.logger.info("Removing file " + filename + "...")
1001
        self.embios.file_unlink(filename)
1002
        self.logger.info(" done\n")
1003
 
1004
    @command
352 theseven 1005
    def mv(self, oldname, newname):
350 theseven 1006
        """
352 theseven 1007
            Renames or moves a file or directory
350 theseven 1008
        """
1009
        self.embios.lib.dev.timeout = 30000
1010
        self.logger.info("Renaming " + oldname + " to " + newname + "...")
1011
        self.embios.file_rename(oldname, newname)
1012
        self.logger.info(" done\n")
1013
 
1014
    @command
351 theseven 1015
    def get(self, remotename, localname, buffer = False, buffsize = "10000"):
346 theseven 1016
        """
1017
            Downloads a file
1018
        """
351 theseven 1019
        if buffer == False: buffer = self.embios.lib.dev.usermem.lower
1020
        else: buffer = self._hexint(buffer)
346 theseven 1021
        buffsize = self._hexint(buffsize)
1022
        try:
1023
            f = open(localname, 'wb')
1024
        except IOError:
1025
            raise ArgumentError("Could not open local file for writing.")
1026
        self.embios.lib.dev.timeout = 30000
1027
        self.logger.info("Downloading file " + remotename + " to " + localname + "...")
1028
        fd = self.embios.file_open(remotename, 0)
1029
        size = self.embios.file_size(fd)
1030
        while size > 0:
370 theseven 1031
            bytes = self.embios.file_read(fd, buffer, buffsize)
346 theseven 1032
            f.write(self.embios.read(buffer, bytes))
1033
            size = size - bytes
1034
        self.embios.file_close(fd)
1035
        f.close()
1036
        self.logger.info(" done\n")
1037
 
1038
    @command
351 theseven 1039
    def put(self, localname, remotename, buffer = False, buffsize = "10000"):
346 theseven 1040
        """
1041
            Uploads a file
1042
        """
351 theseven 1043
        if buffer == False: buffer = self.embios.lib.dev.usermem.lower
1044
        else: buffer = self._hexint(buffer)
346 theseven 1045
        buffsize = self._hexint(buffsize)
1046
        try:
1047
            f = open(localname, 'rb')
1048
        except IOError:
1049
            raise ArgumentError("Could not open local file for reading.")
1050
        self.embios.lib.dev.timeout = 30000
1051
        self.logger.info("Uploading file " + localname + " to " + remotename + "...")
1052
        fd = self.embios.file_open(remotename, 0x15)
1053
        while True:
1054
            data = f.read(buffsize)
1055
            if len(data) == 0: break
1056
            self.embios.write(buffer, data)
1057
            bytes = 0
1058
            while bytes < len(data):
1059
                bytes = bytes + self.embios.file_write(fd, buffer + bytes, len(data) - bytes)
1060
        self.embios.file_close(fd)
1061
        f.close()
1062
        self.logger.info(" done\n")
1063
 
1064
    @command
351 theseven 1065
    def ls(self, path = "/"):
346 theseven 1066
        """
1067
            Lists all files in the specified path
1068
        """
1069
        self.embios.lib.dev.timeout = 30000
1070
        handle = self.embios.dir_open(path)
1071
        self.logger.info("Directory listing of " + path + ":\n")
1072
        while True:
1073
            try:
1074
                entry = self.embios.dir_read(handle)
1075
                if entry.attributes & 0x10: size = "DIR"
1076
                else: size = locale.format("%d", entry.size, True).rjust(13)
1077
                self.logger.info(entry.name.ljust(50) + " - " + size + "\n")
1078
            except: break
1079
        self.embios.dir_close(handle)
1080
 
171 farthen 1081
if __name__ == "__main__":
1082
    if len(sys.argv) < 2:
1083
        usage("No command specified")
382 farthen 1084
    try:
1085
        interface = Commandline()
1086
        interface._parsecommand(sys.argv[1], sys.argv[2:])
1087
    except KeyboardInterrupt:
1088
        sys.exit()