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