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