Subversion Repositories freemyipod

Rev

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
64 benedikt93 30
 
176 farthen 31
from functools import wraps
32
 
64 benedikt93 33
import libembios
171 farthen 34
from libembios import Error
35
import libembiosdata
64 benedikt93 36
 
236 farthen 37
 
171 farthen 38
class NotImplementedError(Error):
39
    pass
64 benedikt93 40
 
171 farthen 41
class ArgumentError(Error):
42
    pass
64 benedikt93 43
 
171 farthen 44
class ArgumentTypeError(Error):
45
    def __init__(self, expected, seen=False):
46
        self.expected = expected
47
        self.seen = seen
48
    def __str__(self):
49
        if self.seen:
50
            return "Expected " + str(self.expected) + " but saw " + str(self.seen)
51
        else:
52
            return "Expected " + str(self.expected) + ", but saw something else"
64 benedikt93 53
 
54
 
171 farthen 55
def usage(errormsg=None, specific=False):
56
    """
57
        Prints the usage information.
58
        It is auto generated from various places.
59
    """
60
    logger = Logger()
61
    cmddict= Commandline.cmddict
62
    doc = {}
63
    # This sorts the output of various internal functions
64
    # and puts everything in easy readable form
65
    for function in cmddict:
66
        function = cmddict[function].func
67
        docinfo = {}
68
        name = function.__name__
69
        args = inspect.getargspec(function)[0]
70
        docinfo['varargs'] = False
71
        if inspect.getargspec(function)[1]:
72
            docinfo['varargs'] = True
73
        kwargvalues = inspect.getargspec(function)[3]
74
        kwargs = {}
75
        if args:
76
            if kwargvalues:
77
                argnum = len(args) - len(kwargvalues)
78
                kwargnum = len(kwargvalues)
79
                kwargs = dict(zip(args[argnum:], kwargvalues))
80
            else:
81
                argnum = len(args)
82
        else:
83
            argnum = 0
84
        docinfo['args'] = args[1:argnum]
85
        docinfo['kwargs'] = kwargs
86
        if function.__doc__:
87
            # strip unneccessary whitespace
88
            docinfo['documentation'] = re.sub(r'\n        ', '\n', function.__doc__)
89
        else:
90
            docinfo['documentation'] = None
91
        doc[name] = docinfo
64 benedikt93 92
 
171 farthen 93
    if not specific:
94
        logger.log("Please provide a command and (if needed) parameters as command line arguments\n\n")
95
        logger.log("Available commands:\n\n")
82 benedikt93 96
    else:
171 farthen 97
        logger.log("\n")
98
    for function in sorted(doc.items()):
99
        function = function[0]
100
        if specific == False or specific == function:
101
            logger.log("  " + function + " ")
102
            for arg in doc[function]['args']:
103
                logger.log("<" + arg + "> ")
104
            if doc[function]['kwargs']:
105
                for kwarg in doc[function]['kwargs']:
106
                    logger.log("[" + kwarg + "] ")
107
            if doc[function]['varargs']:
108
                logger.log("<db1> ... <dbN>")
109
            if doc[function]['documentation']:
110
                logger.log(doc[function]['documentation']+"\n")
64 benedikt93 111
 
171 farthen 112
    logger.log("\n")
113
 
114
    if errormsg:
115
        logger.error(str(errormsg)+"\n")
116
    exit(2)
117
 
118
 
119
class Logger(object):
120
    """
121
        Simple stdout logger.
122
        Loglevel 4 is most verbose, Loglevel 0 only say something if there is an error.
123
    """
124
    def __init__(self):
125
        # Possible values: 0 (only errors), 1 (warnings), 2 (info, recommended for production use), 3 and more (debug)
126
        self.loglevel = 3
127
 
128
    def log(self, text):
129
        sys.stdout.write(text)
67 benedikt93 130
 
171 farthen 131
    def debug(self, text):
132
        if self.loglevel >= 3:
133
            self.log(text)
119 benedikt93 134
 
171 farthen 135
    def info(self, text):
136
        if self.loglevel >= 2:
137
            self.log(text)
82 benedikt93 138
 
171 farthen 139
    def warning(self, text):
140
        if self.loglevel >= 1:
141
            self.log("WARNING: " + text)
119 benedikt93 142
 
171 farthen 143
    def error(self, text):
144
        self.log("ERROR: " + text)
145
 
146
 
147
def command(func):
148
    """
149
        Decorator for all commands.
150
        The decorated function is called with (self, all, other, arguments, ...)
151
    """
176 farthen 152
    @wraps(func)
172 farthen 153
    def decorator(*args):
171 farthen 154
        return func(args[0], *args[1:])
155
    func._command = True
156
    decorator.func = func
157
    return decorator
158
 
159
 
160
def commandClass(cls):
161
    """
162
        Decorator for the class. Sets the self.cmddict of the class
163
        to all functions decorated with @command
164
    """
165
    cls.cmddict = {}
166
    for attr, value in cls.__dict__.iteritems():
167
        if getattr(value, 'func', False):
168
            if getattr(value.func, '_command', False):
169
                cls.cmddict[value.func.__name__] = value
170
    return cls
171
 
172
 
173
@commandClass
174
class Commandline(object):
175
    """
176
        If you want to create a new commandline function you just need to
177
        create a function with the name of it in this class and decorate
178
        it with the decorator @command. If you don't want to call the desired
179
        function (wrong arguments etc) just raise ArgumentError with or
180
        without an error message or raise ArgumentCountError
181
    """
182
    def __init__(self):
183
        self.logger = Logger()
184
        try:
185
            self.embios = libembios.Embios()
186
        except libembios.DeviceNotFoundError:
187
            self.logger.error("No emBIOS device found!")
176 farthen 188
            exit(1)
189
        self.getinfo("version")
171 farthen 190
 
191
    def _parsecommand(self, func, args):
192
        # adds self to the commandline args.
193
        # this is needed because the functions need access to their class.
194
        args.insert(0, self)
195
        if func in self.cmddict:
196
            try:
172 farthen 197
                self.cmddict[func](*args)
171 farthen 198
            except ArgumentError, e:
176 farthen 199
                usage(e, specific=func)
171 farthen 200
            except ArgumentError:
176 farthen 201
                usage("Syntax Error in function '" + func + "'", specific=func)
171 farthen 202
            except ArgumentTypeError, e:
176 farthen 203
                usage(e, specific=func)
171 farthen 204
            except NotImplementedError:
205
                self.logger.error("This function is not implemented yet!")
206
            except libembios.DeviceError, e:
207
                self.logger.error(str(e))
176 farthen 208
            except ValueError:
209
                usage(specific=func)
171 farthen 210
            except TypeError, e:
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:
216
                self.logger.error("Problem with 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":
268
            resp = self.embios.getversioninfo()
172 farthen 269
            self.logger.info("Connected to "+libembiosdata.swtypes[resp.swtypeid] + " v" + str(resp.majorv) + "." + str(resp.minorv) +
171 farthen 270
                             "." + str(resp.patchv) + " r" + str(resp.revision) + " running on " + libembiosdata.hwtypes[resp.hwtypeid] + "\n")
271
        elif infotype == "packetsize":
272
            resp = self.embios.getpacketsizeinfo()
273
            self.logger.info("Maximum packet sizes: "+str(resp))
274
        elif infotype == "usermemrange":
275
            resp = self.embios.getusermemrange()
173 farthen 276
            self.logger.info("The user memory range is "+self._hex(resp.lower)+" - "+self._hex(resp.upper-1))
171 farthen 277
        else:
278
            raise ArgumentTypeError("one out of 'version', 'packetsize', 'usermemrange'", infotype)
64 benedikt93 279
 
171 farthen 280
    @command
281
    def reset(self, force=False):
282
        """
283
            Resets the device"
284
            If <force> is 1, the reset will be forced, otherwise it will be gracefully,
285
            which may take some time.
286
        """
287
        force = self._bool(force)
288
        if force: self.logger.info("Resetting forcefully...\n")
289
        else: self.logger.info("Resetting...\n")
290
        self.embios.reset(force)
64 benedikt93 291
 
171 farthen 292
    @command
293
    def poweroff(self, force=False):
294
        """
295
            Powers the device off
296
            If <force> is 1, the poweroff will be forced, otherwise it will be gracefully,
297
            which may take some time.
298
        """
299
        force = self._bool(force)
300
        if force: self.logger.info("Resetting forcefully...\n")
301
        else: self.logger.info("Resetting...\n")
302
        self.embios.reset(force)
64 benedikt93 303
 
171 farthen 304
    @command
305
    def uploadfile(self, addr, filename):
306
        """
307
            Uploads a file to the device
308
            <offset>: the address to upload the file to
309
            <filename>: the path to the file
310
        """
311
        addr = self._hexint(addr)
312
        try:
313
            f = open(filename, 'rb')
314
        except IOError:
315
            raise ArgumentError("File not readable. Does it exist?")
173 farthen 316
        self.logger.info("Writing file '"+filename+"' to memory at "+self._hex(addr)+"...")
171 farthen 317
        with f:
318
            self.embios.write(addr, f.read())
178 farthen 319
        f.close()
171 farthen 320
        self.logger.info("done\n")
176 farthen 321
 
171 farthen 322
    @command
323
    def downloadfile(self, addr, size, filename):
324
        """
325
            Uploads a file to the device
326
            <offset>: the address to upload the file to
327
            <size>: the number of bytes to be read
328
            <filename>: the path to the file
329
        """
330
        addr = self._hexint(addr)
331
        size = self._hexint(size)
332
        try:
333
            f = open(filename, 'wb')
334
        except IOError:
335
            raise ArgumentError("Can not open file for write!")
173 farthen 336
        self.logger.info("Reading data from address "+self._hex(addr)+" with the size "+self._hex(size)+" to '"+filename+"'...")
171 farthen 337
        with f:
338
            f.write(self.embios.read(addr, size))
178 farthen 339
        f.close()
171 farthen 340
        self.logger.info("done\n")
341
 
342
    @command
343
    def uploadint(self, addr, integer):
344
        """
345
            Uploads a single integer to the device
346
            <offset>: the address to upload the integer to
347
            <data>: the integer to upload
348
        """
349
        addr = self._hexint(addr)
350
        integer = self._hexint(integer)
351
        if integer > 0xFFFFFFFF:
352
            raise ArgumentError("Specified integer too long")
353
        data = chr(integer)
178 farthen 354
        self.embios.write(addr, data)
236 farthen 355
        self.logger.info("Integer '"+self._hex(integer)+"' written successfully to "+self._hex(addr)+"\n")
171 farthen 356
 
357
    @command
358
    def downloadint(self, addr):
359
        """
360
            Downloads a single integer from the device and prints it to the console window
236 farthen 361
            <addr>: the address to download the integer from
171 farthen 362
        """
363
        addr = self._hexint(addr)
178 farthen 364
        data = self.embios.read(addr, 1)
171 farthen 365
        integer = ord(data)
236 farthen 366
        self.logger.info("Integer '"+self._hex(integer)+"' read from address "+self._hex(addr)+"\n")
171 farthen 367
 
368
    @command
176 farthen 369
    def i2cread(self, bus, slave, addr, size):
171 farthen 370
        """
371
            Reads data from an I2C device
372
            <bus> the bus index
373
            <slave> the slave address
374
            <addr> the start address on the I2C device
375
            <size> the number of bytes to read
376
        """
377
        bus = self._hexint(bus)
378
        slave = self._hexint(slave)
379
        addr = self._hexint(addr)
380
        size = self._hexint(size)
236 farthen 381
        data = self.embios.i2cread(bus, slave, addr, size)
382
        bytes = struct.unpack("%dB" % len(data), data)
383
        self.logger.info("Data read from I2C:\n")
384
        for index, byte in enumerate(bytes):
385
            self.logger.info("%02X: %02X\n" % (index, byte))
171 farthen 386
 
387
    @command
176 farthen 388
    def i2cwrite(self, bus, slave, addr, *args):
171 farthen 389
        """
390
            Writes data to an I2C device
391
            <bus> the bus index
392
            <slave> the slave address
393
            <addr> the start address on the I2C device
176 farthen 394
            <db1> ... <dbN> the data in single bytes, encoded in hex,
236 farthen 395
                seperated by whitespaces, eg. 37 5A 4F EB
171 farthen 396
        """
397
        bus = self._hexint(bus)
398
        slave = self._hexint(slave)
399
        addr = self._hexint(addr)
176 farthen 400
        data = ""
171 farthen 401
        for arg in args:
176 farthen 402
            data += chr(self._hexint(arg))
236 farthen 403
        self.logger.info("Writing data to I2C...\n")
176 farthen 404
        self.embios.i2cwrite(bus, slave, addr, data)
236 farthen 405
        self.logger.info("done\n")
171 farthen 406
 
407
    @command
176 farthen 408
    def console(self):
171 farthen 409
        """
176 farthen 410
            Reads data from the USB console continuously
171 farthen 411
        """
176 farthen 412
        while True:
413
            resp = self.embios.usbcread()
414
            self.logger.log(resp.data)
415
            time.sleep(0.1 / resp.maxsize * (resp.maxsize - len(resp.data)))
171 farthen 416
 
417
    @command
176 farthen 418
    def writeusbconsole(self, *args):
171 farthen 419
        """
176 farthen 420
            Writes the string <db1> ... <dbN> to the USB console.
171 farthen 421
        """
176 farthen 422
        text = ""
423
        for word in args:
424
            text += word + " "
425
        text = text[:-1]
236 farthen 426
        self.logger.info("Writing '"+ text +"' to the usb console\n")
176 farthen 427
        self.embios.usbcwrite(text)
171 farthen 428
 
429
    @command
176 farthen 430
    def readdevconsole(self, bitmask):
171 farthen 431
        """
176 farthen 432
            Reads data continuously from one or more of the device's consoles.
433
            <bitmask>: the bitmask of the consoles to read from.
171 farthen 434
        """
435
        bitmask = self._hexint(bitmask)
176 farthen 436
        while True:
437
            resp = self.embios.cread()
438
            self.logger.log(resp.data)
439
            time.sleep(0.1 / resp.maxsize * (resp.maxsize - len(resp.data)))
440
 
171 farthen 441
    @command
176 farthen 442
    def writedevconsole(self, bitmask, *args):
171 farthen 443
        """
176 farthen 444
            Writes the string <db1> ... <dbN> to one or more of the device's consoles.
445
            <bitmask>: the bitmask of the consoles to write to
171 farthen 446
        """
447
        bitmask = self._hexint(bitmask)
176 farthen 448
        text = ""
449
        for word in args:
450
            text += word + " "
451
        text = text[:-1]
452
        self.logger.info("Writing '"+text+"' to the device consoles identified with "+self._hex(bitmask)+"\n")
453
        self.embios.cwrite(text, bitmask)
171 farthen 454
 
455
    @command
456
    def flushconsolebuffers(self, bitmask):
457
        """
458
            flushes one or more of the device consoles' buffers.
459
            <bitmask>: the bitmask of the consoles to be flushed
460
        """
461
        bitmask = self._hexint(bitmask)
236 farthen 462
        self.logger.info("Flushing consoles identified with the bitmask "+self._hex(bitmask)+"\n")
463
        self.embios.cflush(bitmask)
171 farthen 464
 
465
    @command
466
    def getprocinfo(self):
467
        """
468
            Fetches data on the currently running processes
469
        """
173 farthen 470
        import datetime
471
        threads = self.embios.getprocinfo()
472
        self.logger.info("The device has "+str(len(threads))+" running threads:\n\n")
473
        for thread in threads:
474
            self.logger.info("  "+thread.name+":\n")
475
            self.logger.info("    Thread id: "+str(thread.id)+"\n")
476
            self.logger.info("    Thread type: "+thread.type+"\n")
477
            self.logger.info("    Thread state: "+thread.state+"\n")
478
            self.logger.info("    Priority: "+str(thread.priority)+"/256\n")
479
            self.logger.info("    CPU time (total): "+str(datetime.timedelta(microseconds=thread.cputime_total))+"\n")
480
            self.logger.info("    Stack address: "+self._hex(thread.stackaddr)+"\n")
481
            self.logger.info("    Registers:\n")
177 farthen 482
            for registerrange in range(4):
483
                self.logger.info("      ")
484
                for register in range(registerrange, 16, 4):
485
                    registerrepr = "r"+str(register)
486
                    self.logger.info("{:3s}: 0x{:08X}    ".format(registerrepr, thread.regs["r"+str(register)]))
487
                self.logger.info("\n")
488
            self.logger.info("      cpsr: 0x{:08X}".format(thread.regs.cpsr))
173 farthen 489
            self.logger.info("\n")
490
 
171 farthen 491
    @command
492
    def lockscheduler(self):
493
        """
494
            Locks (freezes) the scheduler
495
        """
176 farthen 496
        self.logger.info("Will now lock scheduler\n")
173 farthen 497
        self.embios.lockscheduler()
178 farthen 498
 
171 farthen 499
    @command
500
    def unlockscheduler(self):
501
        """
502
            Unlocks (unfreezes) the scheduler
503
        """
176 farthen 504
        self.logger.info("Will now unlock scheduler\n")
173 farthen 505
        self.embios.unlockscheduler()
178 farthen 506
 
171 farthen 507
    @command
508
    def suspendthread(self, threadid):
509
        """
510
            Suspends/resumes the thread with thread ID <threadid>
511
        """
512
        threadid = self._hexint(threadid)
236 farthen 513
        self.logger.info("Suspending the thread with the threadid "+self._hex(threadid)+"\n")
514
        self.embios.suspendthread(threadid)
171 farthen 515
 
516
    @command
517
    def resumethread(self, threadid):
518
        """
519
            Resumes the thread with thread ID <threadid>
520
        """
521
        threadid = self._hexint(threadid)
236 farthen 522
        self.logger.info("Resuming the thread with the threadid "+self._hex(threadid)+"\n")
173 farthen 523
        self.embios.resumethread(threadid)
171 farthen 524
 
525
    @command
526
    def killthread(self, threadid):
527
        """
528
            Kills the thread with thread ID <threadid>
529
        """
530
        threadid = self._hexint(threadid)
236 farthen 531
        self.logger.info("Killing the thread with the threadid " + self._hex(threadid) + "\n")
173 farthen 532
        self.embios.killthread(threadid)
171 farthen 533
 
534
    @command
535
    def createthread(self, nameptr, entrypoint, stackptr, stacksize, threadtype, priority, state):
536
        """
537
            Creates a new thread and returns its thread ID
538
            <namepointer> a pointer to the thread's name
539
            <entrypoint> a pointer to the entrypoint of the thread
540
            <stackpointer> a pointer to the stack of the thread
541
            <stacksize> the size of the thread's stack
542
            <type> the thread type, vaild are: 0 => user thread, 1 => system thread
543
            <priority> the priority of the thread, from 1 to 255
544
            <state> the thread's initial state, valid are: 1 => ready, 0 => suspended
545
        """
546
        nameptr = self._hexint(nameptr)
547
        entrypoint = self._hexint(entrypoint)
548
        stackpointer = self._hexint(stackpointer)
549
        stacksize = self._hexint(stacksize)
550
        priority = self._hexint(priority)
236 farthen 551
        data = self.embios.createthread(nameptr, entrypoint, stackptr, stacksize, type, priority, state)
552
        name = self.embios.readstring(nameptr)
553
        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 554
 
555
    @command
556
    def run(self, filename):
557
        """
173 farthen 558
            Uploads the emBIOS application <filename> to
236 farthen 559
            the memory and executes it
172 farthen 560
        """
236 farthen 561
        try:
562
            f = open(filename, 'rb')
563
        except IOError:
564
            raise ArgumentError("File not readable. Does it exist?")
565
        try:
566
            appheader = struct.unpack("<8sIIIIIIIIII", f.read(48))
567
            header = appheader[0]
568
            if header != "emBIexec":
569
                raise ArgumentError("The specified file is not an emBIOS application")
570
            baseaddr = appheader[2]
571
            threadnameptr = appheader[8]
572
            f.seek(threadnameptr - baseaddr)
573
            name = ""
574
            while True:
575
                char = f.read(1)
576
                if ord(char) == 0:
577
                    break
578
                name += char
579
            self.logger.info("Writing emBIOS application \"" + name + "\" to the memory at " + self._hex(baseaddr) + "...")
580
            f.seek(0)
581
            self.embios.write(baseaddr, f.read())
582
        except IOError:
583
            raise ArgumentError("The specified file is not an emBIOS application")
584
        except struct.error:
585
            raise ArgumentError("The specified file is not an emBIOS application")
586
        finally:
587
            f.close()
588
        self.logger.info("done\n")
589
        self.execimage(baseaddr)
171 farthen 590
 
591
    @command
173 farthen 592
    def execimage(self, addr):
171 farthen 593
        """
173 farthen 594
            Executes the emBIOS application at <addr>.
171 farthen 595
        """
172 farthen 596
        addr = self._hexint(addr)
173 farthen 597
        self.logger.info("Starting emBIOS app at "+self._hex(addr)+"\n")
172 farthen 598
        self.embios.execimage(addr)
176 farthen 599
 
171 farthen 600
    @command
176 farthen 601
    def flushcaches(self):
171 farthen 602
        """
176 farthen 603
            Flushes the CPUs data and instruction caches.
604
        """
605
        self.logger.info("Flushing CPU data and instruction caches...")
606
        self.embios.flushcaches()
607
        self.logger.info("done\n")
608
 
609
    @command
610
    def readbootflash(self, addr_flash, addr_mem, size):
611
        """
171 farthen 612
            Reads <size> bytes from bootflash to memory.
613
            <addr_bootflsh>: the address in bootflash to read from
614
            <addr_mem>: the address in memory to copy the data to
615
        """
616
        addr_flash = self._hexint(addr_flash)
617
        addr_mem = self._hexint(addr_mem)
618
        size = self._hexint(size)
174 farthen 619
        self.logger.info("Dumping boot flash addresses "+self._hex(addr_flash)+" - "+
620
                         hex(addr_flash+size)+" to "+self._hex(addr_mem)+" - "+self._hex(addr_mem+size)+"\n")
621
        self.embios.bootflashread(addr_flash, addr_mem, size)
176 farthen 622
 
171 farthen 623
    @command
176 farthen 624
    def writebootflash(self, addr_flash, addr_mem, size, force=False):
171 farthen 625
        """
626
            Writes <size> bytes from memory to bootflash.
627
            ATTENTION: Don't call this unless you really know what you're doing!
628
            This may BRICK your device (unless it has a good recovery option)
629
            <addr_mem>: the address in memory to copy the data from
630
            <addr_bootflsh>: the address in bootflash to write to
174 farthen 631
            <force>: Use this flag to suppress the 5 seconds delay
171 farthen 632
        """
633
        addr_flash = self._hexint(addr_flash)
634
        addr_mem = self._hexint(addr_mem)
635
        size = self._hexint(size)
174 farthen 636
        force = self._bool(force)
637
        self.logger.info("Writing boot flash from the memory in "+self._hex(addr_mem)+" - "+
638
                         hex(addr_mem+size)+" to "+self._hex(addr_flash)+" - "+self._hex(addr_flash+size)+"\n")
639
        if force == False:
640
            self.logger.info("If this was not what you intended press Ctrl-C NOW")
176 farthen 641
            for i in range(10):
174 farthen 642
                self.logger.info(".")
643
                time.sleep(1)
644
            self.logger.info("\n")
645
        self.embios.bootflashwrite(addr_flash, addr_mem, size)
176 farthen 646
 
171 farthen 647
    @command
176 farthen 648
    def runfirmware(self, addr, filename):
171 farthen 649
        """
176 farthen 650
            Uploads the firmware in 'filename' to the beginning of the
651
            user memory and executes it
171 farthen 652
        """
176 farthen 653
        addr = self._hexint(addr)
654
        self.uploadfile(addr, filename)
655
        self.execfirmware(addr)
64 benedikt93 656
 
171 farthen 657
    @command
176 farthen 658
    def execfirmware(self, addr):
659
        """
660
            Executes the firmware at addr
661
        """
662
        addr = self._hexint(addr)
663
        self.logger.info("Running firmware at "+self._hex(addr)+". Bye.")
664
        self.embios.execfirmware(addr)
665
 
666
    @command
171 farthen 667
    def aesencrypt(self, addr, size, keyindex):
668
        """
172 farthen 669
            Encrypts a buffer using a hardware key
171 farthen 670
        """
671
        addr = self._hexint(addr)
672
        size = self._hexint(size)
673
        keyindex = self._hexint(keyindex)
674
        self.embios.aesencrypt(addr, size, keyindex)
82 benedikt93 675
 
171 farthen 676
    @command
677
    def aesdecrypt(self, addr, size, keyindex):
678
        """
172 farthen 679
            Decrypts a buffer using a hardware key
171 farthen 680
        """
681
        addr = self._hexint(addr)
682
        size = self._hexint(size)
683
        keyindex = self._hexint(keyindex)
684
        self.embios.aesdecrypt(addr, size, keyindex)
172 farthen 685
 
686
    @command
687
    def hmac_sha1(self, addr, size, destination):
688
        """
689
            Generates a HMAC-SHA1 hash of the buffer and saves it to 'destination'
690
        """
691
        addr = self._hexint(addr)
692
        size = self._hexint(size)
693
        destination = self._hexint(destination)
694
        sha1size = 0x14
173 farthen 695
        self.logger.info("Generating hmac-sha1 hash from the buffer at "+self._hex(addr)+" with the size "+self._hex(size)+
696
                         " and saving it to "+self._hex(destination)+" - "+self._hex(destination+sha1size)+"...")
172 farthen 697
        self.embios.hmac_sha1(addr, size, destination)
698
        self.logger.info("done\n")
178 farthen 699
        data = self.embios.read(destination, sha1size)
172 farthen 700
        hash = ord(data)
173 farthen 701
        self.logger.info("The generated hash is "+self._hex(hash))
64 benedikt93 702
 
227 theseven 703
    @command
704
    def ipodnano2g_getnandinfo(self):
705
        """
706
            Target-specific function: ipodnano2g
707
            Gathers some information about the NAND chip used
708
        """
709
        data = self.embios.ipodnano2g_getnandinfo()
710
        self.logger.info("NAND chip type: "+self._hex(data["type"])+"\n")
711
        self.logger.info("Number of banks: "+str(data["banks"])+"\n")
712
        self.logger.info("Number of blocks: "+str(data["blocks"])+"\n")
713
        self.logger.info("Number of user blocks: "+str(data["userblocks"])+"\n")
714
        self.logger.info("Pages per block: "+str(data["pagesperblock"]))
715
 
716
    @command
717
    def ipodnano2g_nandread(self, addr, start, count, doecc, checkempty):
718
        """
719
            Target-specific function: ipodnano2g
720
            Reads data from the NAND chip into memory
721
        """
722
        addr = self._hexint(addr)
723
        start = self._hexint(start)
724
        count = self._hexint(count)
725
        doecc = int(doecc)
726
        checkempty = int(checkempty)
727
        self.logger.info("Reading "+self._hex(count)+" NAND pages starting at "+self._hex(start)+" to "+self._hex(addr)+"...")
228 theseven 728
        self.embios.lib.dev.timeout = 30000
227 theseven 729
        self.embios.ipodnano2g_nandread(addr, start, count, doecc, checkempty)
730
        self.logger.info("done\n")
731
 
732
    @command
733
    def ipodnano2g_nandwrite(self, addr, start, count, doecc):
734
        """
735
            Target-specific function: ipodnano2g
736
            Writes data to the NAND chip
737
        """
738
        addr = self._hexint(addr)
739
        start = self._hexint(start)
740
        count = self._hexint(count)
741
        doecc = int(doecc)
742
        self.logger.info("Writing "+self._hex(count)+" NAND pages starting at "+self._hex(start)+" from "+self._hex(addr)+"...")
228 theseven 743
        self.embios.lib.dev.timeout = 30000
227 theseven 744
        self.embios.ipodnano2g_nandwrite(addr, start, count, doecc)
745
        self.logger.info("done\n")
746
 
747
    @command
748
    def ipodnano2g_nanderase(self, addr, start, count):
749
        """
750
            Target-specific function: ipodnano2g
751
            Erases blocks on the NAND chip and stores the results to memory
752
        """
753
        addr = self._hexint(addr)
754
        start = self._hexint(start)
755
        count = self._hexint(count)
756
        self.logger.info("Erasing "+self._hex(count)+" NAND blocks starting at "+self._hex(start)+" and logging to "+self._hex(addr)+"...")
228 theseven 757
        self.embios.lib.dev.timeout = 30000
227 theseven 758
        self.embios.ipodnano2g_nanderase(addr, start, count)
759
        self.logger.info("done\n")
760
 
228 theseven 761
    @command
762
    def ipodnano2g_dumpnand(self, filenameprefix):
763
        """
764
            Target-specific function: ipodnano2g
765
            Dumps the whole NAND chip to four files
766
        """
767
        info = self.embios.ipodnano2g_getnandinfo()
768
        self.logger.info("Dumping NAND contents...")
769
        try:
770
            infofile = open(filenameprefix+"_info.txt", 'wb')
771
            datafile = open(filenameprefix+"_data.bin", 'wb')
772
            sparefile = open(filenameprefix+"_spare.bin", 'wb')
773
            statusfile = open(filenameprefix+"_status.bin", 'wb')
774
        except IOError:
775
            raise ArgumentError("Can not open file for writing!")
776
        infofile.write("NAND chip type: "+self._hex(info["type"])+"\r\n")
777
        infofile.write("Number of banks: "+str(info["banks"])+"\r\n")
778
        infofile.write("Number of blocks: "+str(info["blocks"])+"\r\n")
779
        infofile.write("Number of user blocks: "+str(info["userblocks"])+"\r\n")
780
        infofile.write("Pages per block: "+str(info["pagesperblock"])+"\r\n")
781
        self.embios.lib.dev.timeout = 30000
782
        for i in range(info["banks"] * info["blocks"] * info["pagesperblock"] / 8192):
783
            self.logger.info(".")
784
            self.embios.ipodnano2g_nandread(0x08000000, i * 8192, 8192, 1, 1)
785
            datafile.write(self.embios.read(0x08000000, 0x01000000))
786
            sparefile.write(self.embios.read(0x09000000, 0x00080000))
787
            statusfile.write(self.embios.read(0x09080000, 0x00008000))
788
        infofile.close()
789
        datafile.close()
790
        sparefile.close()
791
        statusfile.close()
792
        self.logger.info("done\n")
793
 
794
    @command
795
    def ipodnano2g_wipenand(self, filename, force=False):
796
        """
797
            Target-specific function: ipodnano2g
798
            Wipes the whole NAND chip and logs the result to a file
799
            <force>: Use this flag to suppress the 5 seconds delay
800
        """
801
        self.logger.info("Wiping the whole NAND chip!\n")
802
        if force == False:
803
            self.logger.info("If this was not what you intended press Ctrl-C NOW")
804
            for i in range(10):
805
                self.logger.info(".")
806
                time.sleep(1)
807
            self.logger.info("\n")
808
        info = self.embios.ipodnano2g_getnandinfo()
809
        self.logger.info("Wiping NAND contents...")
810
        try:
811
            statusfile = open(filename, 'wb')
812
        except IOError:
813
            raise ArgumentError("Can not open file for writing!")
814
        self.embios.lib.dev.timeout = 30000
815
        for i in range(info["banks"] * info["blocks"] / 64):
816
            self.logger.info(".")
230 theseven 817
            self.embios.ipodnano2g_nanderase(0x08000000, i * 64, 64)
228 theseven 818
            statusfile.write(self.embios.read(0x08000000, 0x00000100))
819
        statusfile.close()
820
        self.logger.info("done\n")
821
 
171 farthen 822
if __name__ == "__main__":
823
    if len(sys.argv) < 2:
824
        usage("No command specified")
825
    interface = Commandline()
826
    interface._parsecommand(sys.argv[1], sys.argv[2:])