forked from leveldown-security/SVD-Loader-Ghidra
-
Notifications
You must be signed in to change notification settings - Fork 0
/
SVD-Loader.py
175 lines (139 loc) · 5.3 KB
/
SVD-Loader.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# Load specified SVD and generate peripheral memory maps & structures.
#@author Thomas Roth [email protected]
#@category leveldown security
#@keybinding
#@menupath
#@toolbar
# More information:
# https://leveldown.de/blog/svd-loader/
# License: GPLv3
from cmsis_svd.parser import SVDParser
from ghidra.program.model.data import Structure, StructureDataType, UnsignedIntegerDataType, DataTypeConflictHandler
from ghidra.program.model.data import UnsignedShortDataType, ByteDataType, UnsignedLongLongDataType
from ghidra.program.model.mem import MemoryBlockType
from ghidra.program.model.address import AddressFactory
from ghidra.program.model.symbol import SourceType
from ghidra.program.model.mem import MemoryConflictException
class MemoryRegion:
def __init__(self, name, start, end):
self.name = name
self.start = start
self.end = end
def length(self):
return self.end - self.start
def reduce_memory_regions(regions):
for i in range(len(regions)):
r1 = regions[i]
for j in range(len(regions)):
r2 = regions[j]
# Skip self
if i == j:
continue
if r1.end < r2.start:
continue
if r2.end < r1.start:
continue
# We are overlapping, generate larger area and call
# reduce_memory_regions again.
regions[i].start = min(r1.start, r2.start)
regions[i].end = max(r1.end, r2.end)
regions[i].name = r1.name + "_" + r2.name
regions.remove(regions[j])
return reduce_memory_regions(regions)
return regions
def calculate_peripheral_size(peripheral, default_register_size):
size = 0
for register in peripheral.registers:
register_size = default_register_size if not register._size else register._size
size = max(size, register.address_offset + register_size/8)
return size
svd_file = askFile("Choose SVD file", "Load SVD File")
print("Loading SVD file...")
parser = SVDParser.for_xml_file(str(svd_file))
print("\tDone!")
# CM0, CM4, etc
cpu_type = parser.get_device().cpu.name
# little/big
cpu_endian = parser.get_device().cpu.endian
default_register_size = parser.get_device().size
# Not all SVDs contain these fields
if cpu_type and not cpu_type.startswith("CM"):
print("Currently only Cortex-M CPUs are supported.")
print("Supplied CPU type was: " + cpu_type)
sys.exit(1)
if cpu_endian and cpu_endian != "little":
print("Currently only little endian CPUs are supported.")
print("Supplied CPU endian was: " + cpu_endian)
sys.exit(1)
# Get things we need
listing = currentProgram.getListing()
symtbl = currentProgram.getSymbolTable()
dtm = currentProgram.getDataTypeManager()
space = currentProgram.getAddressFactory().getDefaultAddressSpace()
namespace = symtbl.getNamespace("Peripherals", None)
if not namespace:
namespace = currentProgram.getSymbolTable().createNameSpace(None, "Peripherals", SourceType.ANALYSIS)
peripherals = parser.get_device().peripherals
print("Generating memory regions...")
# First, we need to generate a list of memory regions.
# This is because some SVD files have overlapping peripherals...
memory_regions = []
for peripheral in peripherals:
start = peripheral.base_address
length = peripheral.address_block.offset + peripheral.address_block.size
end = peripheral.base_address + length
memory_regions.append(MemoryRegion(peripheral.name, start, end))
memory_regions = reduce_memory_regions(memory_regions)
# Create memory blocks:
for r in memory_regions:
try:
addr = space.getAddress(r.start)
length = r.length()
t = currentProgram.memory.createUninitializedBlock(r.name, addr, length, False)
t.setRead(True)
t.setWrite(True)
t.setExecute(False)
t.setVolatile(True)
t.setComment("Generated by SVD-Loader.")
except Exception as e:
print("\tFailed to generate memory block for: " + r.name)
print("\t", e)
print("\tDone!")
print("Generating peripherals...")
for peripheral in peripherals:
print("\t" + peripheral.name)
if(len(peripheral.registers) == 0):
print("\t\tNo registers.")
continue
# try:
# Iterage registers to get size of peripheral
# Most SVDs have an address-block that specifies the size, but
# they are often far too large, leading to issues with overlaps.
length = calculate_peripheral_size(peripheral, default_register_size)
# Generate structure for the peripheral
peripheral_struct = StructureDataType(peripheral.name, length)
peripheral_start = peripheral.base_address
peripheral_end = peripheral_start + length
for register in peripheral.registers:
register_size = default_register_size if not register._size else register._size
r_type = UnsignedIntegerDataType()
rs = register_size / 8
if rs == 1:
r_type = ByteDataType()
elif rs == 2:
r_type = UnsignedShortDataType()
elif rs == 8:
r_type = UnsignedLongLongDataType()
peripheral_struct.replaceAtOffset(register.address_offset, r_type, register_size/8, register.name, register.description)
addr = space.getAddress(peripheral_start)
dtm.addDataType(peripheral_struct, DataTypeConflictHandler.REPLACE_HANDLER)
# Some SVDs, namely Nordic's, peripherals may have alternatives with the same base address
# In that case, we can't create a data listing at the same location multiple times
if not listing.getDataAt(addr).isDefined():
listing.createData(addr, peripheral_struct, False)
symtbl.createLabel(addr,
peripheral.name,
namespace,
SourceType.USER_DEFINED )
# except:
# print("\t\tFailed to generate peripheral " + peripheral.name)