Subversion Repositories freemyipod

Rev

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

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