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