Subversion Repositories freemyipod

Rev

Rev 922 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
811 theseven 1
#!/usr/bin/env python
2
#
3
#
4
#    Copyright 2011 TheSeven
5
#
6
#
7
#    This file is part of emCORE.
8
#
9
#    emCORE 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
#    emCORE 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 emCORE.  If not, see <http://www.gnu.org/licenses/>.
21
#
22
#
23
 
24
 
25
import sys
26
import struct
816 theseven 27
from misc import ExtendedCStruct, string_from_image
28
from libemcoredata import scheduler_thread
811 theseven 29
from ctypes import *
30
 
31
 
32
def usage():
33
  print("Usage: %s <image> [image_base] [tlsf_pool] [SL_INDEX_COUNT_LOG2] [FL_INDEX_MAX]" % (sys.argv[0]))
34
  exit(2)
35
 
36
 
37
image_base = 0x08000000
38
tlsf_pool = 0x08000000
39
SL_INDEX_COUNT_LOG2 = 5
40
FL_INDEX_MAX = 30
41
 
42
if len(sys.argv) < 2: usage()
43
filename = sys.argv[1]
44
if len(sys.argv) > 2: image_base = int(sys.argv[2])
45
if len(sys.argv) > 3: tlsf_pool = int(sys.argv[3])
46
if len(sys.argv) > 4: SL_INDEX_COUNT_LOG2 = int(sys.argv[4])
47
if len(sys.argv) > 5: FL_INDEX_MAX = int(sys.argv[5])
48
if len(sys.argv) > 6: usage()
49
 
50
file = open(filename, "rb")
51
data = file.read()
52
file.close()
53
 
54
SL_INDEX_COUNT = 1 << SL_INDEX_COUNT_LOG2
55
FL_INDEX_SHIFT = SL_INDEX_COUNT_LOG2 + 2
56
FL_INDEX_COUNT = FL_INDEX_MAX - FL_INDEX_SHIFT + 1
57
SMALL_BLOCK_SIZE = 1 << FL_INDEX_SHIFT
58
 
59
 
60
class block_header_t(ExtendedCStruct):
61
  _fields_ = [("prev_phys_block", c_uint32),
62
              ("size", c_uint32),
63
              ("next_free", c_uint32),
64
              ("prev_free", c_uint32)]
65
 
66
  def is_last(self):
67
    return self.get_size() == 0;
68
 
69
  def is_null(self):
70
    return self._address_ == self.next_free and self.size == 0 and self.prev_phys_block == 0
71
 
72
  def is_free(self):
73
    return (self.size & block_header_free_bit) != 0;
74
 
75
  def is_prev_free(self):
76
    return (self.size & block_header_prev_free_bit) != 0;
77
 
78
  def get_address(self):
79
    return self._address_
80
 
81
  def get_size(self):
82
    return self.size & 0xfffffffc
83
 
84
  def get_prev_free(self):
85
    if not self.is_free(): raise Exception("Trying to get previous free block of non-free block")
86
    return block_header_t(self._data_, self._base_, self.prev_free)
87
 
88
  def get_next_free(self):
89
    if not self.is_free(): raise Exception("Trying to get next free block of non-free block")
90
    return block_header_t(self._data_, self._base_, self.next_free)
91
 
92
  def get_prev_phys(self):
93
    if not self.is_prev_free(): raise Exception("Trying to get non-free previous physical block")
94
    return block_header_t(self._data_, self._base_, self.prev_phys_block)
95
 
96
  def get_next_phys(self):
97
    return self.get_next_by_offset(self.get_size() + 4)
98
 
99
  def get_next_by_offset(self, offset):
100
    return block_header_t(self._data_, self._base_, self._address_ + offset)
101
 
102
 
103
class pool_t(ExtendedCStruct):
104
  _fields_ = [("fl_bitmap", c_uint32),
105
              ("sl_bitmap", c_uint32 * FL_INDEX_COUNT),
106
              ("blocks", c_uint32 * SL_INDEX_COUNT * FL_INDEX_COUNT)]
107
 
108
 
109
block_header_free_bit = 1
110
block_header_prev_free_bit = 2
111
block_size_min = sizeof(block_header_t) - 4;
112
block_size_max = 1 << FL_INDEX_MAX
113
 
114
pool = pool_t(data, image_base, tlsf_pool)
115
block = block_header_t(data, image_base, tlsf_pool + sizeof(pool_t) - 4)
116
prev_free = False
117
prev_addr = 0
118
blocks = []
119
free_blocks = []
120
bytes_used = 0
121
bytes_free = 0
122
 
123
while True:
124
  if block.is_prev_free() != prev_free:
125
    print("Block %08X previous free indicator is %d, expected %d" % (block.get_address(), block.is_prev_free(), prev_free))
126
  if prev_free and prev_addr != block.prev_phys_block:
127
    print("Block %08X previous physical block address is wrong. Got %08X, should be %08X" % (block.get_address(), block.prev_phys_block, prev_addr))
128
  prev_free = block.is_free()
129
  prev_addr = block.get_address()
130
  if block.is_last(): break
131
  if blocks.count(prev_addr) > 0:
132
    print("Block loop detected at %08X" % (prev_addr))
133
    break
134
  blocks.append(prev_addr)
135
  if prev_free:
816 theseven 136
    try:
137
      nfa = block.get_next_free().get_address()
138
      if nfa >= prev_addr and nfa < prev_addr + block.get_size() + 4:
922 theseven 139
        print("%08X: Next free block (%08X) is within the block itself" % (prev_addr, nfa))
816 theseven 140
    except: print("%08X: Invalid next free block pointer: %08X" % (prev_addr, block.next_free))
141
    try:
142
      pfa = block.get_prev_free().get_address()
143
      if pfa >= prev_addr and pfa < prev_addr + block.get_size() + 4:
922 theseven 144
        print("%08X: Previous free block (%08X) is within the block itself" % (prev_addr, pfa))
816 theseven 145
    except:
146
      print("%08X: Invalid previous free block pointer: %08X" % (prev_addr, block.prev_free))
811 theseven 147
    print("%08X: %08X bytes free" % (prev_addr + 4, block.get_size() + 4))
148
    free_blocks.append(prev_addr)
149
    bytes_free = bytes_free + block.get_size() + 4
150
  else:
816 theseven 151
    owner_address = prev_addr - image_base + block.get_size() + 4
811 theseven 152
    owner = struct.unpack("<I", data[owner_address : owner_address + 4])[0]
816 theseven 153
    if (owner & 3) == 0:
154
      if (owner & 0xfffc0000) == 0x22000000: name = "<KERNEL_THREAD>"
155
      else:
156
        try:
157
          thread = scheduler_thread(data, image_base, owner & ~3)
158
          name = "Thread: " + string_from_image(data, image_base, thread.name, 128)
159
        except: name = "<BAD_THREAD>"
160
    elif (owner & 3) == 1:
161
      try:
162
        handle = struct.unpack("<I", data[(owner & ~3) - image_base + 4 : (owner & ~3) - image_base + 8])[0]
163
        lib = struct.unpack("<4sI", data[(handle & ~3) - image_base + 4 : (handle & ~3) - image_base + 12])
164
        name = "Library: " + lib[0].decode("latin_1") + "_v%d" % lib[1]
165
      except: name = "<BAD_LIBRARY>"
166
    elif (owner & 3) == 2:
167
      if (owner >> 2) == 0: name = "<KERNEL_UNKNOWN>";
168
      elif (owner >> 2) == 1: name = "<KERNEL_USB_MONITOR>";
169
      elif (owner >> 2) == 2: name = "<KERNEL_FILE_HANDLE>";
170
      elif (owner >> 2) == 3: name = "<KERNEL_DIR_HANDLE>";
171
      elif (owner >> 2) == 4: name = "<KERNEL_ATA_BBT>";
172
      else: name = "<KERNEL_UNKNOWN_TYPE>";
173
    else: name = "<UNKNOWN_TYPE>"
174
    print("%08X: %08X+8 bytes owned by %08X (%s)" % (prev_addr + 8, block.get_size() - 4, owner, name))
811 theseven 175
    bytes_used = bytes_used + block.get_size() + 4
176
  try: block = block.get_next_phys()
177
  except:
178
    print("Block %08X has invalid size: %08X" % (prev_addr, block.get_size()))
179
    print("Fatal error in block chain, continuing with map check")
180
    break
181
 
182
handled_blocks = []
183
for i in range(FL_INDEX_COUNT):
184
  fl_map = (pool.fl_bitmap >> i) & 1
185
  sl_list = pool.sl_bitmap[i]
186
  if fl_map == 0:
187
    if sl_list != 0:
188
      print("[%d:%d] Second-level map must be null, but isn't" % (i, j))
189
  elif sl_list == 0:
190
    print("[%d:%d] No free blocks in second-level map, but first-level map indicates there are some" % (i, j))
191
  for j in range(SL_INDEX_COUNT):
192
    sl_map = (sl_list >> j) & 1
193
    ba = pool.blocks[i][j]
194
    block = block_header_t(data, image_base, ba)
195
    if sl_map == 0:
196
      if not block.is_null():
197
        print("[%d:%d:%08X] Block list must be null, but isn't" % (i, j, ba))
198
      continue
199
    elif block.is_null():
200
      print("[%d:%d:%08X] Block list is null, but second-level map indicates there are free blocks" % (i, j, ba))
201
    blocks = []
816 theseven 202
    prev = None
811 theseven 203
    while not block.is_null():
204
      fatal = False
205
      addr = block.get_address()
206
      if blocks.count(addr) > 0:
207
        print("[%d:%d:%08X] Detected block loop" % (i, j, addr))
208
        break
209
      blocks.append(addr)
816 theseven 210
      size = block.get_size()
211
      print("[%d:%d] Block at %08X (%08X bytes)" % (i, j, addr, size + 4))
811 theseven 212
      if not block.is_free():
213
        print("[%d:%d:%08X] Non-free block on free list" % (i, j, addr))
214
        fatal = True
215
      if block.is_prev_free():
216
        print("[%d:%d:%08X] Block should have coalesced with previous one" % (i, j, addr))
217
      try:
218
        if block.get_next_phys().is_free():
219
          print("[%d:%d:%08X] Block should have coalesced with next one" % (i, j, addr))
220
      except:
221
        print("Block %08X has invalid size: %08X" % (addr, block.get_size()))
222
      if size < block_size_min:
223
        print("[%d:%d:%08X] Block violates minimum size: %d (should be at least %d)" % (i, j, addr, size, block_size_min))
224
      if size > block_size_max:
225
        print("[%d:%d:%08X] Block violates maximum size: %d (should be at most %d)" % (i, j, addr, size, block_size_max))
226
      if size < SMALL_BLOCK_SIZE:
227
        fl = 0
228
        sl = size / (SMALL_BLOCK_SIZE / SL_INDEX_COUNT)
229
      else:
230
        fl = 32 - FL_INDEX_SHIFT;
231
        if (size & 0xffff0000) == 0:
232
          size = size << 16
233
          fl = fl - 16
234
        if (size & 0xff000000) == 0:
235
          size = size << 8
236
          fl = fl - 8
237
        if (size & 0xf0000000) == 0:
238
          size = size << 4
239
          fl = fl - 4
240
        if (size & 0xc0000000) == 0:
241
          size = size << 2
242
          fl = fl - 2
243
        if (size & 0x80000000) == 0:
244
          size = size << 1
245
          fl = fl - 1
816 theseven 246
        size = block.get_size()
247
        sl = (size >> (fl - SL_INDEX_COUNT_LOG2 + FL_INDEX_SHIFT - 1)) ^ (1 << SL_INDEX_COUNT_LOG2)
811 theseven 248
      if fl != i or sl != j:
249
        print("Block %08X is in wrong free list: [%d:%d] (should be [%d:%d])" % (addr, i, j, fl, sl))
250
      if free_blocks.count(addr) != 1:
251
        print("[%d:%d:%08X] Block is in free list, but was not found in pool" % (i, j, addr))
252
      if handled_blocks.count(addr) > 0:
253
        print("[%d:%d:%08X] Block appears in multiple free lists" % (i, j, addr))
254
      else: handled_blocks.append(addr)
816 theseven 255
      try:
256
        nfa = block.get_next_free().get_address()
257
        if nfa >= addr and nfa < addr + size + 4:
922 theseven 258
          print("[%d:%d:%08X] Next free block (%08X) is within the block itself" % (i, j, addr, nfa))
816 theseven 259
          fatal = True
260
      except:
261
        print("[%d:%d:%08X] Invalid next free block pointer: %08X" % (i, j, addr, block.next_free))
262
        fatal = True
263
      try:
264
        pfa = block.get_prev_free().get_address()
265
        if pfa >= addr and pfa < addr + size + 4:
922 theseven 266
          print("[%d:%d:%08X] Previous free block (%08X) is within the block itself" % (i, j, addr, pfa))
816 theseven 267
        if prev == None and not block.get_prev_free().is_null():
268
          print("[%d:%d:%08X] Previous free block pointer is broken: %08X (should be NULL)" % (i, j, addr, pfa))
269
        if prev != None and prev != pfa:
270
          print("[%d:%d:%08X] Previous free block pointer is broken: %08X (should be %08X)" % (i, j, addr, pfa, prev))
271
      except:
272
        print("[%d:%d:%08X] Invalid previous free block pointer: %08X" % (i, j, addr, block.prev_free))
811 theseven 273
      if fatal:
274
        print("Fatal error in block chain, continuing with next chain")
275
        break
816 theseven 276
      prev = addr
811 theseven 277
      block = block.get_next_free()
278
 
279
for addr in free_blocks:
280
  if handled_blocks.count(addr) != 1:
281
    print("Free block %08X does not appear in any free list" % (addr))