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