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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
|
\ *****************************************************************************
\ * Copyright (c) 2004, 2011 IBM Corporation
\ * All rights reserved.
\ * This program and the accompanying materials
\ * are made available under the terms of the BSD License
\ * which accompanies this distribution, and is available at
\ * http://www.opensource.org/licenses/bsd-license.php
\ *
\ * Contributors:
\ * IBM Corporation - initial implementation
\ ****************************************************************************/
\ PAPR PCI host bridge.
0 VALUE phb-debug?
1000 CONSTANT tce-ps \ Default TCE page size is 4K
tce-ps 1- CONSTANT tce-mask
." Populating " pwd cr
\ needed to find the right path in the device tree
: decode-unit ( addr len -- phys.lo ... phys.hi )
2 hex-decode-unit \ decode string
b lshift swap \ shift the devicenumber to the right spot
8 lshift or \ add the functionnumber
\ my-bus 10 lshift or \ add the busnumber (assume always bus 0)
0 0 rot \ make phys.lo = 0 = phys.mid
;
\ needed to have the right unit address in the device tree listing
\ phys.lo=phys.mid=0 , phys.hi=config-address
: encode-unit ( phys.lo phys-mid phys.hi -- unit-str unit-len )
nip nip \ forget the phys.lo and phys.mid
dup 8 rshift 7 and swap \ calculate function number
B rshift 1F and \ calculate device number
over IF 2 ELSE nip 1 THEN \ create string with dev#,fn# or dev# only?
hex-encode-unit
;
0 VALUE my-puid
: setup-puid
s" reg" get-node get-property 0= IF
decode-64 to my-puid 2drop
THEN
;
setup-puid
: config-b@ puid >r my-puid TO puid rtas-config-b@ r> TO puid ;
: config-w@ puid >r my-puid TO puid rtas-config-w@ r> TO puid ;
: config-l@ puid >r my-puid TO puid rtas-config-l@ r> TO puid ;
\ define the config writes
: config-b! puid >r my-puid TO puid rtas-config-b! r> TO puid ;
: config-w! puid >r my-puid TO puid rtas-config-w! r> TO puid ;
: config-l! puid >r my-puid TO puid rtas-config-l! r> TO puid ;
: map-in ( phys.lo phys.mid phys.hi size -- virt )
phb-debug? IF cr ." map-in called: " .s cr THEN
\ Ignore the size, phys.lo and phys.mid, get BAR from config space
drop nip nip ( phys.hi )
\ Sanity check whether config address is in expected range:
dup FF AND dup 10 28 WITHIN NOT swap 30 <> AND IF
cr ." phys.hi = " . cr
ABORT" map-in with illegal config space address"
THEN
00FFFFFF AND \ Need only bus-dev-fn+register bits
dup config-l@ ( phys.hi' bar.lo )
dup 7 AND 4 = IF \ Is it a 64-bit BAR?
swap 4 + config-l@ lxjoin \ Add upper part of 64-bit BAR
ELSE
nip
THEN
F NOT AND \ Clear indicator bits
translate-my-address
phb-debug? IF ." map-in done: " .s cr THEN
;
: map-out ( virt size -- )
phb-debug? IF ." map-out called: " .s cr THEN
2drop
;
: dma-alloc ( size -- virt )
phb-debug? IF cr ." dma-alloc called: " .s cr THEN
tce-ps #aligned
alloc-mem
\ alloc-mem always returns aligned memory - double check just to be sure
dup tce-mask and IF
." Warning: dma-alloc got unaligned memory!" cr
THEN
;
: dma-free ( virt size -- )
phb-debug? IF cr ." dma-free called: " .s cr THEN
tce-ps #aligned
free-mem
;
\ Helper variables for dma-map-in and dma-map-out
0 VALUE dma-window-liobn \ Logical I/O bus number
0 VALUE dma-window-base \ Start address of window
0 VALUE dma-window-size \ Size of the window
0 VALUE bm-handle \ Bitmap allocator handle
\ Read helper variables (LIOBN, DMA window base and size) from the
\ "ibm,dma-window" property. This property can be either located
\ in the PCI device node or in the bus node, so we've got to use the
\ "calling-child" variable here to get to the node that initiated the call.
\ XXX We should search all the way up the tree to the PHB ...
: (init-dma-window-vars) ( -- )
\ ." Foo called in " pwd cr
\ ." calling child is " calling-child .node cr
\ ." parent is " calling-child parent .node cr
s" ibm,dma-window" calling-child get-property IF
s" ibm,dma-window" calling-child parent get-property
ABORT" no dma-window property available"
THEN
decode-int TO dma-window-liobn
decode-64 TO dma-window-base
decode-64 TO dma-window-size
2drop
bm-handle 0= IF
dma-window-base dma-window-size tce-ps bm-allocator-init to bm-handle
\ Sometimes the window-base appears as zero, that does not
\ go well with NULL pointers. So block this address
dma-window-base 0= IF
bm-handle tce-ps bm-alloc drop
THEN
THEN
;
: (clear-dma-window-vars) ( -- )
0 TO dma-window-liobn
0 TO dma-window-base
0 TO dma-window-size
;
\ grub does not align allocated addresses to the size so when mapping,
\ we might need to ask bm-alloc for an extra IOMMU page
: dma-align ( size virt -- aligned-size ) tce-mask and + tce-ps #aligned ;
: dma-trunc ( addr -- addr&~fff ) tce-mask not and ;
: dma-map-in ( virt size cachable? -- devaddr )
phb-debug? IF cr ." dma-map-in called: " .s cr THEN
(init-dma-window-vars)
drop
over dma-align ( virt size ) \ size is aligned now
tuck ( size virt size )
bm-handle swap bm-alloc ( size virt dev-addr ) \ dev-addr is aligned
dup 0 < IF
." Bitmap allocation Failed "
3drop
0 EXIT
THEN
swap ( size dev-addr virt )
2dup tce-mask and or >r \ add page offset to the return value
dma-trunc 3 OR \ Truncate and add read and write perm
rot ( dev-addr virt size r: dev-addr )
0
?DO
2dup dma-window-liobn -rot ( dev-addr virt liobn dev-addr virt r: dev-addr )
hv-put-tce ABORT" H_PUT_TCE failed"
tce-ps + swap tce-ps + swap ( dev-addr' virt' r: dev-addr )
tce-ps +LOOP
(clear-dma-window-vars)
2drop
r>
;
: dma-map-out ( virt devaddr size -- )
phb-debug? IF cr ." dma-map-out called: " .s cr THEN
(init-dma-window-vars)
rot drop ( devaddr size )
over dma-align
swap dma-trunc swap ( devaddr-trunc size-extended )
2dup bm-handle -rot bm-free
0
?DO
dup 0 dma-window-liobn -rot
hv-put-tce ABORT" H_PUT_TCE failed"
tce-ps +
tce-ps +LOOP
drop
(clear-dma-window-vars)
;
: dma-sync ( virt devaddr size -- )
phb-debug? IF cr ." dma-sync called: " .s cr THEN
\ TODO: Call flush-cache or sync here?
3drop
;
: open true ;
: close ;
\ Parse the "ranges" property of the root pci node to decode the available
\ memory ranges. See "PCI Bus Binding to IEEE Std 1275-1994" for details.
\ The memory ranges are then used for setting up the device bars (if necessary)
: phb-parse-ranges ( -- )
\ First clear everything, in case there is something missing in the ranges
0 pci-next-io !
0 pci-max-io !
0 pci-next-mem !
0 pci-max-mem !
0 pci-next-mmio !
0 pci-max-mmio !
0 pci-next-mem64 !
0 pci-max-mem64 !
\ Now get the "ranges" property
s" ranges" get-node get-property 0<> ABORT" ranges property not found"
( prop-addr prop-len )
BEGIN
dup
WHILE
decode-int \ Decode phys.hi
3000000 AND \ Filter out address space in phys.hi
CASE
1000000 OF \ I/O space?
decode-64 dup >r pci-next-io ! \ Decode PCI base address
decode-64 drop \ Forget the parent address
decode-64 r> + pci-max-io ! \ Decode size & calc max address
pci-next-io @ 0= IF
pci-next-io @ 10 + pci-next-io ! \ BARs must not be set to zero
THEN
ENDOF
2000000 OF \ 32-bit memory space?
decode-64 dup >r pci-next-mmio ! \ Decode base address
decode-64 drop \ Forget the parent address
decode-64 r> + pci-max-mmio ! \ calc max MMIO address
ENDOF
3000000 OF \ 64-bit memory space?
decode-64 dup >r pci-next-mem64 !
decode-64 drop \ Forget the parent address
decode-64 r> + pci-max-mem64 !
ENDOF
ENDCASE
REPEAT
( prop-addr prop-len )
2drop
\ If we do not have 64-bit prefetchable memory, split the 32-bit space:
pci-next-mem64 @ 0= IF
pci-next-mmio @ pci-next-mem ! \ Start of 32-bit prefetchable
pci-max-mmio @ pci-next-mmio @ - 2 / \ Calculate new size
pci-next-mmio @ + \ The middle of the area
dup pci-max-mem !
pci-next-mmio !
THEN
phb-debug? IF
pci-var-out
THEN
;
: phb-pci-walk-bridge ( -- )
phb-debug? IF ." Calling pci-walk-bridge " pwd cr THEN
get-node child ?dup 0= IF EXIT THEN \ get and check if we have children
0 to pci-device-slots \ reset slot array to unpoppulated
BEGIN
dup \ Continue as long as there are children
WHILE
dup set-node \ Set child node as current node
my-space pci-set-slot \ set the slot bit
my-space pci-htype@ \ read HEADER-Type
7f and \ Mask bit 7 - multifunction device
CASE
0 OF my-space pci-device-setup ENDOF \ | set up the device
1 OF my-space pci-bridge-setup ENDOF \ | set up the bridge
dup OF my-space [char] ? pci-out ENDOF
ENDCASE
peer
REPEAT drop
get-parent set-node
;
\ Similar to pci-bridge-probe, but without setting the secondary and
\ subordinate bus numbers (since this has been done by QEMU already)
: phb-pci-bridge-probe ( addr -- )
dup pci-bridge-set-bases \ Set up all Base Registers
dup func-pci-bridge-range-props \ Set up temporary "range"
my-space pci-bus-scnd@ TO pci-bus-number \ Set correct current bus number
pci-device-vec-len 1+ TO pci-device-vec-len \ increase the device-slot vector depth
pci-enable \ enable mem/IO transactions
phb-pci-walk-bridge \ and walk the secondary bus
pci-device-vec-len 1- TO pci-device-vec-len \ decrease the device-slot vector depth
pci-bridge-set-limits \ Set up all Limit Registers
;
\ Stub routine, as qemu has enumerated, we already have the device
\ properties set.
: phb-pci-device-props ( addr -- )
dup pci-class-name device-name
dup pci-device-assigned-addresses-prop
drop
;
\ Scan the child nodes of the pci root node to assign bars, fixup
\ properties etc.
: phb-setup-children
puid >r \ Save old value of puid
my-puid TO puid \ Set current puid
phb-parse-ranges
1 TO pci-hotplug-enabled
s" qemu,mem-bar-min-align" get-node get-property 0= IF
decode-int TO pci-mem-bar-min-align
2drop
ELSE
10000 TO pci-mem-bar-min-align
THEN
s" qemu,phb-enumerated" get-node get-property 0<> IF
1 0 (probe-pci-host-bridge)
ELSE
2drop
['] phb-pci-bridge-probe TO func-pci-bridge-probe
['] phb-pci-device-props TO func-pci-device-props
phb-pci-walk-bridge \ PHB device tree is already populated.
THEN
r> TO puid \ Restore previous puid
;
phb-setup-children
|