Subversion Repositories freemyipod

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
56 benedikt93 1
#!/usr/bin/env python
2
#
3
#
171 farthen 4
#    Copyright 2010 TheSeven, benedikt93, Farthen
56 benedikt93 5
#
6
#
7
#    This file is part of emBIOS.
8
#
9
#    emBIOS is free software: you can redistribute it and/or
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
#
14
#    emBIOS is distributed in the hope that it will be useful,
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
#
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/>.
21
#
22
#
23
 
24
import sys
25
import struct
171 farthen 26
import usb.core
27
import libembiosdata
56 benedikt93 28
 
171 farthen 29
class Error(Exception):
30
    def __init__(self, value=None):
31
        self.value = value
32
    def __str__(self):
33
        if self.value != None:
34
            return repr(self.value)
56 benedikt93 35
 
171 farthen 36
class ArgumentError(Error):
37
    pass
56 benedikt93 38
 
171 farthen 39
class DeviceNotFoundError(Error):
40
    pass
56 benedikt93 41
 
171 farthen 42
class DeviceError(Error):
43
    pass
56 benedikt93 44
 
171 farthen 45
class SendError(Error):
46
    pass
56 benedikt93 47
 
171 farthen 48
class ReceiveError(Error):
49
    pass
56 benedikt93 50
 
51
 
171 farthen 52
class Bunch(dict):
56 benedikt93 53
    """
171 farthen 54
        This is a dict whose items can also be accessed with
55
        bunchinstance.something.
56 benedikt93 56
    """
171 farthen 57
    def __init__(self, **kw):
58
        dict.__init__(self, kw)
59
        self.__dict__ = self
56 benedikt93 60
 
171 farthen 61
    def __getstate__(self):
62
        return self
56 benedikt93 63
 
171 farthen 64
    def __setstate__(self, state):
65
        self.update(state)
66
        self.__dict__ = self
56 benedikt93 67
 
68
 
171 farthen 69
class Embios(object):
70
    def __init__(self):
71
        self.lib = Lib(self)
56 benedikt93 72
 
171 farthen 73
    @staticmethod
74
    def _alignsplit(addr, size, blksize, align):
75
        end = addr + size
76
        if addr & (align - 1):
77
            bodyaddr = (addr + min(size, blksize)) & ~(align - 1)
78
        else: bodyaddr = addr
79
        headsize = bodyaddr - addr
80
        if (size - headsize) & (align - 1):
81
            tailaddr = (end - min(end - bodyaddr, blksize) + align - 1) & ~(align - 1)
82
        else: tailaddr = end
83
        tailsize = end - tailaddr
84
        return (headsize, tailaddr - bodyaddr, tailsize)
56 benedikt93 85
 
171 farthen 86
    def getversioninfo(self):
87
        """ This returns the emBIOS version and device information. """
88
        return self.lib.monitorcommand(struct.pack("IIII", 1, 0, 0, 0), "IBBBBI", ("revision", "majorv", "minorv", "patchv", "swtypeid", "hwtypeid"))
56 benedikt93 89
 
171 farthen 90
    def getpacketsizeinfo(self):
91
        """ This returns the emBIOS max packet size information.
92
            It also sets the properties of the device object accordingly.
93
        """
94
        resp = self.lib.monitorcommand(struct.pack("IIII", 1, 1, 0, 0), "HHII", ("coutmax", "cinmax", "doutmax", "dinmax"))
95
        self.lib.dev.packetsizelimit['cout'] = resp.coutmax
96
        self.lib.dev.packetsizelimit['cin'] = resp.cinmax
97
        self.lib.dev.packetsizelimit['din'] = resp.dinmax
98
        self.lib.dev.packetsizelimit['dout'] = resp.doutmax
99
        return resp
56 benedikt93 100
 
171 farthen 101
    def getusermemrange(self):
102
        """ This returns the memory range the user has access to. """
103
        return self.lib.monitorcommand(struct.pack("IIII", 1, 2, 0, 0), "III", ("lower", "upper", None))
56 benedikt93 104
 
171 farthen 105
    def reset(self, force=False):
106
        """ Reboot the device """
107
        if force:
108
            return self.lib.monitorcommand(struct.pack("IIII", 2, 0, 0, 0))
109
        else:
110
            return self.lib.monitorcommand(struct.pack("IIII", 2, 1, 0, 0), "III", (None, None, None))
56 benedikt93 111
 
171 farthen 112
    def poweroff(self, force=False):
113
        """ Powers the device off. """
114
        if force:
115
            return self.lib.monitorcommand(struct.pack("IIII", 3, 0, 0, 0))
116
        else:
117
            return self.lib.monitorcommand(struct.pack("IIII", 3, 1, 0, 0), "III", (None, None, None))
56 benedikt93 118
 
171 farthen 119
    def read(self, addr, size):
120
        """ Reads the memory from location 'addr' with size 'size'
121
            from the device. This cares about too long packages
122
            and decides whether to use DMA or not.
123
        """
124
        if not self.lib.connected:
125
            self.lib.connect()
126
        cin_maxsize = self.lib.dev.packetsizelimit["cin"] - 0x10
127
        din_maxsize = self.lib.dev.packetsizelimit["din"]
128
        data = ""
129
        (headsize, bodysize, tailsize) = self._alignsplit(addr, size, cin_maxsize, 16)
130
        if headsize != 0:
131
            data += self.readmem(addr, headsize)
132
            addr += headsize
133
        while bodysize > 0:
134
            if bodysize >= 2 * cin_maxsize:
135
                readsize = min(bodysize, din_maxsize)
136
                data += self.readdma(addr, readsize)
137
            else:
138
                readsize = min(bodysize, cin_maxsize)
139
                data += self.readmem(addr, readsize)
140
            addr += readsize
141
            bodysize -= readsize
142
        if tailsize != 0:
143
            data += self.readmem(addr, tailsize)
144
        return data
56 benedikt93 145
 
171 farthen 146
    def write(self, addr, data):
147
        """ Writes the data in 'data' to the location 'addr'
148
            in the memory of the device. This cares about too long packages
149
            and decides whether to use DMA or not.
150
        """
151
        if not self.lib.connected:
152
            self.lib.connect()
153
        cout_maxsize = self.lib.dev.packetsizelimit["cout"] - 0x10
154
        dout_maxsize = self.lib.dev.packetsizelimit["dout"]
155
        (headsize, bodysize, tailsize) = self._alignsplit(addr, len(data), cout_maxsize, 16)
156
        offset = 0
157
        if headsize != 0:
158
            self.writemem(addr, headsize)
159
            offset += headsize
160
            addr += headsize
161
        while bodysize > 0:
162
            if bodysize >= 2 * cout_maxsize:
163
                writesize = min(bodysize, dout_maxsize)
164
                self.writedma(addr, data[offset:offset+writesize])
165
            else:
166
                writesize = min(bodysize, cout_maxsize)
167
                self.writemem(addr, data[offset:offset+writesize])
168
            offset += writesize
169
            addr += writesize
170
            bodysize -= writesize
171
        if tailsize != 0:
172
            self.writemem(addr, data[offset:offset+tailsize])
173
        return data
56 benedikt93 174
 
171 farthen 175
    def readmem(self, addr, size):
176
        """ Reads the memory from location 'addr' with size 'size'
177
            from the device.
178
        """
179
        resp = self.lib.monitorcommand(struct.pack("IIII", 4, addr, size, 0), "III%ds" % size, (None, None, None, "data"))
180
        return resp.data
181
 
182
    def writemem(self, addr, data):
183
        """ Writes the data in 'data' to the location 'addr'
184
            in the memory of the device.
185
        """
186
        return self.lib.monitorcommand(struct.pack("IIII%ds" % len(data), 5, addr, len(data), 0, data), "III", (None, None, None))
56 benedikt93 187
 
171 farthen 188
    def readdma(self, addr, size):
189
        """ Reads the memory from location 'addr' with size 'size'
190
            from the device. This uses DMA and the data in endpoint.
191
        """
192
        self.lib.monitorcommand(struct.pack("IIII", 6, addr, size, 0), "III", (None, None, None))
193
        return struct.unpack("%ds" % size, self.lib.dev.din(size))[0]
56 benedikt93 194
 
171 farthen 195
    def writedma(self, addr, data):
196
        """ Writes the data in 'data' to the location 'addr'
197
            in the memory of the device. This uses DMA and the data out endpoint.
198
        """
199
        self.lib.monitorcommand(struct.pack("IIII", 7, addr, len(data), 0), "III", (None, None, None))
200
        return self.lib.dev.dout(data)
56 benedikt93 201
 
171 farthen 202
    def i2cread(self, index, slaveaddr, startaddr, size):
203
        """ Reads data from an i2c slave """
56 benedikt93 204
 
171 farthen 205
    def i2cwrite(self, index, slaveaddr, startaddr, data):
206
        """ Writes data to an i2c slave """
56 benedikt93 207
 
171 farthen 208
    def usbcread(self, size):
209
        """ Reads data with size 'size' from the USB console """
56 benedikt93 210
 
171 farthen 211
    def usbcwrite(self, data):
212
        """ Writes data to the USB console """
56 benedikt93 213
 
171 farthen 214
    def cread(self, size, bitmask):
215
        """ Reads data with the specified size from the device consoles
216
            identified with the specified bitmask
217
        """
56 benedikt93 218
 
171 farthen 219
    def cwrite(self, data):
220
        """ Writes data to the device consoles 
221
            identified with the specified bitmask.
222
        """
56 benedikt93 223
 
171 farthen 224
    def cflush(self, bitmask):
225
        """ Flushes the consoles specified with 'bitmask' """
226
        return self.lib.monitorcommand(struct.pack("IIII", 14, bitmask, 0, 0), "III", (None, None, None))
56 benedikt93 227
 
171 farthen 228
    def getprocinfo(self, offset, size):
229
        """ Gets current state of the scheduler """
56 benedikt93 230
 
171 farthen 231
    def freezescheduler(self, freeze=True):
232
        """ Freezes/Unfreezes the scheduler """
233
        return self.lib.monitorcommand(struct.pack("IIII", 16, 1 if freeze else 0, 0, 0), "III", ("before", None, None))
67 benedikt93 234
 
171 farthen 235
    def unfreezescheduler(self):
236
        """ Unfreezes the scheduler """
237
        return self.lib.monitorcommand(struct.pack("IIII", 16, 0, 0, 0), "III", ("before", None, None))
56 benedikt93 238
 
171 farthen 239
    def suspendthread(self, id, suspend=True):
240
        """ Suspends the thread with the specified id """
241
        return self.lib.monitorcommand(struct.pack("IIII", 17, 1 if suspend else 0, id, 0), "III", ("before", None, None))
56 benedikt93 242
 
171 farthen 243
    def unsuspendthread(self, id):
244
        """ Suspends the thread with the specified id """
245
        return self.lib.monitorcommand(struct.pack("IIII", 17, 0, id, 0), "III", ("before", None, None))
56 benedikt93 246
 
171 farthen 247
    def killthread(self, id):
248
        """ Kills the thread with the specified id """
249
        return self.lib.monitorcommand(struct.pack("IIII", 18, id, 0, 0), "III", ("before", None, None))
56 benedikt93 250
 
171 farthen 251
    def createthread(self, nameptr, entrypoint, stackptr, stacksize, threadtype, priority, state):
252
        """ Creates a thread with the specified attributes """
253
        if threadtype == "user":
254
            threadtype = 0
255
        elif threadtype == "system":
256
            threadtype = 1
257
        else:
258
            raise SyntaxError("Threadtype must be either 'system' or 'user'")
259
        if priority > 256 or priority < 0:
260
            raise SyntaxError("Priority must be a number between 0 and 256")
261
        if state == "ready":
262
            state = 0
263
        elif state == "suspended":
264
            state = 1
265
        else:
266
            raise SyntaxError("State must be either 'ready' or 'suspended'")
267
        resp = self.lib.monitorcommand(struct.pack("IIIIIIII", 19, nameptr, entrypoint, stackptr, stacksize, threadtype, priority, state), "III", (id, None, None))
268
        if resp.id < 0:
269
            raise DeviceError("The device returned the error code "+str(resp.id))
270
        return resp
56 benedikt93 271
 
171 farthen 272
    def flushcpucache(self):
273
        """ Flushes the CPU instruction and data cache """
274
        return self.lib.monitorcommand(struct.pack("IIII", 20, 0, 0, 0), "III", (None, None, None))
56 benedikt93 275
 
171 farthen 276
    def run(self, addr):
277
        """ Runs the emBIOS app at 'addr' """
278
        return self.lib.monitorcommand(struct.pack("IIII", 21, addr, 0, 0), "III", ("excecimage", None, None))
56 benedikt93 279
 
171 farthen 280
    def bootflashread(self, memaddr, flashaddr, size):
281
        """ Copies the data in the bootflash at 'flashaddr' of the specified size
282
            to the memory at addr 'memaddr'
283
        """
82 benedikt93 284
 
171 farthen 285
    def bootflashwrite(self, memaddr, flashaddr, size):
286
        """ Copies the data in the memory at 'memaddr' of the specified size
287
            to the boot flash at addr 'flashaddr'
288
        """
56 benedikt93 289
 
171 farthen 290
    def execfirmware(self, addr):
291
        """ Executes the firmware at 'addr' and passes all control to it. """
292
        return self.lib.monitorcommand(struct.pack("IIII", 24, addr, 0, 0), "III", (None, None, None))
56 benedikt93 293
 
171 farthen 294
    def aesencrypt(self, addr, size, keyindex):
295
        """ Encrypts the buffer at 'addr' with the specified size
296
            with the hardware AES key index 'keyindex'
297
        """
298
        return self.lib.monitorcommand(struct.pack("IBBHII", 25, 1, 0, keyindex, addr, size), "III", (None, None, None))
82 benedikt93 299
 
171 farthen 300
    def aesdecrypt(self, addr, size, keyindex):
301
        """ Decrypts the buffer at 'addr' with the specified size
302
            with the hardware AES key index 'keyindex'
303
        """
304
        return self.lib.monitorcommand(struct.pack("IBBHII", 25, 0, 0, keyindex, addr, size), "III", (None, None, None))
82 benedikt93 305
 
171 farthen 306
    def hmac_sha1(self, addr, size, destination):
307
        """ Generates a HMAC-SHA1 hash of the buffer and saves it to 'destination' """
308
        return self.lib.monitorcommand(struct.pack("IIII", 26, addr, size, destination), "III", (None, None, None))
56 benedikt93 309
 
310
 
171 farthen 311
class Lib(object):
312
    def __init__(self, embios):
313
        self.idVendor = 0xFFFF
314
        self.idProduct = 0xE000
315
 
316
        self.embios = embios
317
        self.connected = False
56 benedikt93 318
 
171 farthen 319
    def connect(self):
320
        self.dev = Dev(self.idVendor, self.idProduct)
321
        self.connected = True
322
        self.embios.getpacketsizeinfo()
56 benedikt93 323
 
171 farthen 324
    def monitorcommand(self, cmd, rcvdatatypes=None, rcvstruct=None):
325
        if not self.connected:
326
            self.connect()
327
        self.dev.cout(cmd)
328
        if rcvdatatypes:
329
            rcvdatatypes = "I" + rcvdatatypes # add the response
330
            data = self.dev.cin(struct.calcsize(rcvdatatypes))
331
            data = struct.unpack(rcvdatatypes, data)
332
            response = data[0]
333
            if libembiosdata.responsecodes[response] == "ok":
334
                if rcvstruct:
335
                    datadict = Bunch()
336
                    counter = 1 # start with 1, 0 is the id
337
                    for item in rcvstruct:
338
                        if item != None: # else the data is undefined
339
                            datadict[item] = data[counter]
340
                        counter += 1
341
                    return datadict
342
                else:
343
                    return data
344
            elif libembiosdata.responsecodes[response] == "unsupported":
345
                raise DeviceError("The device does not support this command.")
346
            elif libembiosdata.responsecodes[response] == "invalid":
347
                raise DeviceError("Invalid command! This should NOT happen!")
348
            elif libembiosdata.responsecodes[response] == "busy":
349
                raise DeviceError("Device busy")
56 benedikt93 350
 
351
 
171 farthen 352
class Dev(object):
353
    def __init__(self, idVendor, idProduct):
354
        self.idVendor = idVendor
355
        self.idProduct = idProduct
67 benedikt93 356
 
171 farthen 357
        self.interface = 0
358
        self.timeout = 100
119 benedikt93 359
 
171 farthen 360
        self.connect()
361
        self.findEndpoints()
362
 
363
        self.packetsizelimit = {}
364
        self.packetsizelimit['cout'] = None
365
        self.packetsizelimit['cin'] = None
366
        self.packetsizelimit['dout'] = None
367
        self.packetsizelimit['din'] = None
56 benedikt93 368
 
171 farthen 369
    def __del__(self):
370
        self.disconnect()
56 benedikt93 371
 
171 farthen 372
    def findEndpoints(self):
373
        epcounter = 0
374
        self.endpoint = {}
375
        for cfg in self.dev:
376
            for intf in cfg:
377
                for ep in intf:
378
                    if epcounter == 0:
379
                        self.endpoint['cout'] = ep.bEndpointAddress
380
                    elif epcounter == 1:
381
                        self.endpoint['cin'] = ep.bEndpointAddress
382
                    elif epcounter == 2:
383
                        self.endpoint['dout'] = ep.bEndpointAddress
384
                    elif epcounter == 3:
385
                        self.endpoint['din'] = ep.bEndpointAddress
386
                    epcounter += 1
387
        if epcounter <= 3:
388
            raise DeviceError("Not all endpoints found in the descriptor. Only "+str(epcounter)+" found, we need 4")
56 benedikt93 389
 
171 farthen 390
    def connect(self):
391
        self.dev = usb.core.find(idVendor=self.idVendor, idProduct=self.idProduct)
392
        if self.dev is None:
393
            raise DeviceNotFoundError()
394
        self.dev.set_configuration()
56 benedikt93 395
 
171 farthen 396
    def disconnect(self):
397
        pass
102 benedikt93 398
 
171 farthen 399
    def send(self, endpoint, data):
400
        size = self.dev.write(endpoint, data, self.interface, self.timeout)
401
        if size != len(data):
402
            raise SendError
403
        return len
102 benedikt93 404
 
171 farthen 405
    def receive(self, endpoint, size):
406
        read = self.dev.read(endpoint, size, self.interface, self.timeout)
407
        if len(read) != size:
408
            raise ReceiveError
409
        return read
56 benedikt93 410
 
171 farthen 411
    def cout(self, data):
412
        if self.packetsizelimit['cout'] and len(data) > self.packetsizelimit['cout']:
413
            raise SendError("Packet too big")
414
        return self.send(self.endpoint['cout'], data)
94 benedikt93 415
 
171 farthen 416
    def cin(self, size):
417
        if self.packetsizelimit['cin'] and size > self.packetsizelimit['cin']:
418
            raise ReceiveError("Packet too big")
419
        return self.receive(self.endpoint['cin'], size)
94 benedikt93 420
 
171 farthen 421
    def dout(self, data):
422
        if self.packetsizelimit['dout'] and len(data) > self.packetsizelimit['dout']:
423
            raise SendError("Packet too big")
424
        return self.send(self.endpoint['dout'], data)
94 benedikt93 425
 
171 farthen 426
    def din(self, size):
427
        if self.packetsizelimit['din'] and size > self.packetsizelimit['din']:
428
            raise ReceiveError("Packet too big")
429
        return self.receive(self.endpoint['din'], size)
56 benedikt93 430
 
96 benedikt93 431
 
171 farthen 432
if __name__ == "__main__":
433
    # Some tests
434
    import sys
435
    embios = Embios()
436
    resp = embios.getversioninfo()
437
    sys.stdout.write("Embios device version information: " + libembiosdata.swtypes[resp.swtypeid] + " v" + str(resp.majorv) + "." + str(resp.minorv) + 
438
                     "." + str(resp.patchv) + " r" + str(resp.revision) + " running on " + libembiosdata.hwtypes[resp.hwtypeid] + "\n")
439
    resp = embios.getusermemrange()
440
    sys.stdout.write("Usermemrange: "+hex(resp.lower)+" - "+hex(resp.upper)+"\n")
441
    memaddr = resp.lower
442
    maxlen = resp.upper - resp.lower
443
    f = open("./embios.py", "rb")
444
    sys.stdout.write("Loading test file (embios.py) to send over USB...\n")
445
    datastr = f.read()[:maxlen]
446
    sys.stdout.write("Sending data...\n")
447
    embios.write(memaddr, datastr)
448
    sys.stdout.write("Encrypting data with the hardware key...\n")
449
    embios.aesencrypt(memaddr, len(datastr), 0)
450
    sys.stdout.write("Reading data back and saving it to 'libembios-test-encrypted.bin'...\n")
451
    f = open("./libembios-test-encrypted.bin", "wb")
452
    f.write(embios.read(memaddr, len(datastr)))
453
    sys.stdout.write("Decrypting the data again...\n")
454
    embios.aesdecrypt(memaddr, len(datastr), 0)
455
    sys.stdout.write("Reading data back from device...\n")
456
    readdata = embios.read(memaddr, len(datastr))
457
    if readdata == datastr:
458
        sys.stdout.write("Data matches!")
459
    else:
460
        sys.stdout.write("Data does NOT match. Something got wrong")