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
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
|
# IBM_PROLOG_BEGIN_TAG
# This is an automatically generated prolog.
#
# $Source: src/build/simics/ipmi_bt_responder.py $
#
# OpenPOWER HostBoot Project
#
# Contributors Listed Below - COPYRIGHT 2014
# [+] International Business Machines Corp.
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied. See the License for the specific language governing
# permissions and limitations under the License.
#
# IBM_PROLOG_END_TAG
#####################################################################33
#
# Provide behavior for the BMC IPMI interface.
# This code simulates the BMC firmware. The registers used for the
# bulk transfer commands work with this program to create response data
# as though the BMC firmware were operating.
#
#######################################################################
# To Add responses:
# 1) find the net function to respond to and its sub function in
# handle_ipmi_msg
# a) if not present then create handle_ipmi_XXX
# 2) Add if/else leg for command
# 3) Send response(s) with
# resp_buf
# msg.bld_response(resp_buf)
# bmc_rsp_q.append(msg)
#
#######################################################################
# To send SMS:
# 1) on Simics commandline:
# @ipmi_bt_responder_send_sms
# ("fpga0.ipmi_bmc_device", 0x18, 0xdf, (0xde, 0xad, 0xbe, 0xef))
# where first param is BMC device name, next is netfun, then command,
# then data
#
# The model calling into this python is the end-device-ipmi-bmc.dml
# The model call to this python under these circumstances
# 1) any BIT within BT_CTRL is written calls to ipmi_bt_responder_ctl
# 2) registers BT_CTRL, BT_INTMASK or RW Buffer are written, calls
# to ipmi_bt_responder_reg
#
# The end-device-ipmi-bmc.dml model maintains the buffer offset for
# reading and writing. The end-device-ipmi-bmc.dml model monitors
# the H_BUSY bit. Then this changes from set to cleared it is known
# the Host completed reading and buffer data. At that time the offset
# into the buffer is set to zero, allowing the next write operation
# to propley load the buffer data.
#
# The end-device-ipmi-bmc.dml model monitors the interrupt bit
# BMC_HWRST to reset the BMC registers
#
#
#####################################################################33
import cli
import simics
import pyobj
from configuration import *
from collections import deque
bt_debug = 1
bmc_rsp_q = deque([])
bmc_sms_q = deque([])
num_reads_for_processing = 5
cur_access_count = 0
processing_msg = 0
last_seq = 0
g_sel_time = []
##################################################################
# Entry point for all IPMI message handling
#########
def handle_ipmi_msg(bmc):
#####################
# NetFun/LUN definitions
NETFUN_CHASSIS = (0x00 << 2)
NETFUN_BRIDGE = (0x02 << 2)
NETFUN_SENSOR = (0x04 << 2)
NETFUN_APP = (0x06 << 2)
NETFUN_FIRMWARE = (0x08 << 2)
NETFUN_STORAGE = (0x0a << 2)
NETFUN_TRANPORT = (0x0c << 2)
global last_seq
bt_debug_print(3," handle_ipmi_msg")
msg = ipmiMessage(bmc.h2b_buffer)
last_seq = msg.getSeq()
msg.dump("Got MSG")
### Now decide how to respond
### break this up into a function for each Netfun/LUN
if(msg.getNetfLun() == NETFUN_APP):
handle_ipmi_APP(msg)
elif(msg.getNetfLun() == NETFUN_STORAGE):
handle_ipmi_STORAGE(msg)
else:
bt_debug_print(0,"ERROR> IPMI_RSP unrecognized message")
bt_debug_print(0,"Cowardly doing nothing")
msg.dump("UNRECOGNIZED IPMI MESSAGE")
pass
##################################################################
# Entry point for APP Net function/LUN
#########
def handle_ipmi_APP(msg):
global bmc_rsp_q
#####################
# Command IDs for NETFUN_APP
APP_GET_DEVID = 0x01
APP_GET_BT_CAP = 0x36
APP_GET_DROP_MAGIC = 0x3e
bt_debug_print(3," handle_ipmi_APP")
### Now decide how to respond
### Handle each command type inline
if(msg.getCmd() == APP_GET_DEVID):
bt_debug_print(2," bld response for DEVID")
### Send back "fake" dev id
### RC=0, FW level 1.0, IPMI 2.0, everything else 0s
resp_buf = [0x0, 0x0, 0x0, 0x81, 0x0, 0x02, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]
msg.bld_response(resp_buf)
msg.dump("DEVID RESP")
bmc_rsp_q.append(msg)
elif(msg.getCmd() == APP_GET_BT_CAP):
bt_debug_print(2," bld response for BT Capabilities")
### Send back "fake" dev id
### RC=0, # req, Input size, resp size, resp time(sec), retry
resp_buf = [0x0, 0x2, 0x40, 0x40, 0x1, 0x01]
msg.bld_response(resp_buf)
msg.dump("BTCAP RESP")
bmc_rsp_q.append(msg)
elif(msg.getCmd() == APP_GET_DROP_MAGIC):
bt_debug_print(1,"MAGIC MSG -- drop this message")
### Don't send back anything
else:
bt_debug_print(0,"ERROR> IPMI_APP invalid CMD")
bt_debug_print(0,"ERROR> Cowardly doing nothing")
msg.dump("UNRECOGNIZED APP CMD")
pass
##################################################################
# Entry point for STORAGE Net function/LUN
#########
def handle_ipmi_STORAGE(msg):
global bmc_rsp_q
global g_sel_time
#####################
# Command IDs for NETFUN_APP
APP_SET_SEL_TIME = 0x49
bt_debug_print(3," handle_ipmi_STORAGE")
### Now decide how to respond
### Handle each command type inline
if(msg.getCmd() == APP_SET_SEL_TIME):
bt_debug_print(2," bld response for set SEL Time")
### Send back good status and save away "time"
g_sel_time = msg.getData()
resp_buf = [0x0]
msg.bld_response(resp_buf)
msg.dump("SET_SEL RESP")
bmc_rsp_q.append(msg)
else:
bt_debug_print(0,"ERROR> IPMI_STORAGE invalid CMD")
bt_debug_print(0,"ERROR> Cowardly doing nothing")
msg.dump("UNRECOGNIZED STORAGE CMD")
pass
##############################################################################
##############################################################################
##############################################################################
##############################################################################
##############################################################################
##############################################################################
# END IPMI MESSAGE HANDLING FUNCTIONS #########
##############################################################################
##############################################################################
##############################################################################
##############################################################################
##############################################################################
##############################################################################
def bt_debug_print(level, str, parms=None):
global bt_debug
if (bt_debug >= level):
if parms:
print str % parms
else:
print str
class ipmiMessage:
#Instance constructor
def __init__(self, buffer=[]):
if(len(buffer)):
#The IPMI protocol says:
# byte 0 = len, byte 1 = NetFun/LUN, byte 2 = Seq, byte 4 = cmd
# byte 5:N = Data. Min size is 3
if(buffer[0] < 3):
bt_debug_print(0, "ERROR, malformed IPMI msg .Len %d", self.len)
pass
self.len = buffer[0] - 3
self.netf_lun = buffer[1]
self.seq = buffer[2]
self.cmd = buffer[3]
self.data = buffer[4:(buffer[0]+1)]
else:
self.len = 0
self.netf_lun = 0
self.seq = 0
self.cmd = 0
self.data = []
def getIPMIBuf(self, sendbuf):
sendbuf[0] = self.len
sendbuf[1] = self.netf_lun
sendbuf[2] = self.seq
sendbuf[3] = self.cmd
for x in xrange(4, 4+len(self.data)):
sendbuf[x] = self.data[x-4]
for x in xrange(4+len(self.data), len(sendbuf)):
sendbuf[x] = 0xFF
def getData(self):
return self.data
def setDataBuf(self, data):
self.data = data
self.len = 3+ len(data)
def getDataSize(self):
return self.len
def getNetfLun(self):
return self.netf_lun
def setNetfLun(self, netf_lun):
self.netf_lun = netf_lun
def createNetfResp(self):
self.netf_lun = self.netf_lun + 0x04
def getCmd(self):
return self.cmd
def setCmd(self, cmd):
self.cmd = cmd
def getSeq(self):
return self.seq
def setSeq(self, i_seq):
self.seq = i_seq
def bld_response(self, resp_buf):
#use existing message and tweak for the response
self.createNetfResp()
self.setDataBuf(resp_buf)
def dump(self, header="Debug Dump", lvl=1):
global bt_debug
if (bt_debug >= lvl):
print "======================================"
print "= %s " % header
print "================="
print "Net: %X cmd: %X seq: %X" % (self.netf_lun, self.cmd, self.seq)
print "Data: "
print " ".join(hex(n) for n in self.data)
print "======================================"
print " "
##################################################################
# send_messages
#########
def send_messages(bmc):
CTL_B2H_ATN = 0x08
CTL_EVT_ATN = 0x10
CTL_H_BUSY = 0x40
BUSY_MASK = CTL_B2H_ATN | CTL_EVT_ATN | CTL_H_BUSY
global processing_msg
global bmc_sms_q
global bmc_rsp_q
global debug
interrupt_bit = 0x0
msg = None
bt_debug_print(4,">>send_messages")
# Clear B_BUSY as we have a message to respond to
ctl_reg = bmc.BT_CTRL_base
ctl_reg = ctl_reg & 0x7F #clear B-BUSY
bmc.BT_CTRL_base = ctl_reg
#send message if we can (H_BUSY, B2H, EVT all have to be to clear)
if(not (bmc.BT_CTRL_base & BUSY_MASK)):
bt_debug_print(4," H_BUSY clear, OK to send")
#Always perfer SMS
if(len(bmc_sms_q) >0):
msg = bmc_sms_q.popleft()
interrupt_bit = CTL_EVT_ATN # SMS
elif(len(bmc_rsp_q) >0):
msg = bmc_rsp_q.popleft()
interrupt_bit = CTL_B2H_ATN # Response
#Have something to send
if msg:
msg.dump("BMC2Host message", 4)
sendbuf = bmc.b2h_buffer
msg.getIPMIBuf(sendbuf)
bmc.b2h_buffer = sendbuf
bt_debug_print(4,"about to set CTRL")
bmc.BT_CTRL_base = bmc.BT_CTRL_base | interrupt_bit
# If both SMS and rsp are empty we are not processing a message
if((len(bmc_rsp_q) == 0) and (len(bmc_sms_q) == 0)):
processing_msg = 0
bt_debug_print(4,"<<send_messages")
pass
################################################################################
##########################################
# Register update handler
# For the register passed in, access and update any BT registers and buffers
# to simulate the BT commands
# Called on a per register basis
################################################################################
def bt_handle_reg(bmc, reg_name, RW):
CTL_H2B_ATN = 0x04
global processing_msg
global cur_access_count
global num_reads_for_processing
cur_access_count = cur_access_count +1
bt_debug_print(9,"cnt[%d] proc[%d]", (cur_access_count, processing_msg))
#Always attempt to send response/sms messages
#function determines correct state
#delay X number of reads/writes by the host
#to simulate time
if((processing_msg == 1) and (cur_access_count > 1) and ((bmc.BT_CTRL_base & CTL_H2B_ATN))):
ctl_reg = bmc.BT_CTRL_base
ctl_reg = ctl_reg & 0xFB #clear H2B
bmc.BT_CTRL_base = ctl_reg
bt_debug_print(9,"prior to send messages check")
if((processing_msg == 1) and (cur_access_count > num_reads_for_processing)):
send_messages(bmc)
if RW == "READ":
# NOP
##bt_debug_print(2,"Process register %s for READ, NOP",(reg_name))
return
write_buff = bmc.BT_RW_BUFF_base # in case we need to modify
if (reg_name == "BT_RW_BUFF"):
bt_debug_print(4,"Process register %s",(reg_name))
elif (reg_name == "BT_CTRL"):
bt_debug_print(4,"Process register %s",(reg_name))
elif (reg_name == "BT_INTMASK"):
bt_debug_print(4,"Process register %s",(reg_name))
reg0 = bmc.BT_CTRL_base
bt_debug_print(4,"at present --- %X",(reg0))
# if REG0 equals 55, buffer set to AA
# Imaginary behavior as an example
if reg0 == 0x37:
write_buff = 0xaa
else:
bt_debug_print(0,"Unhandled register named %s", (reg_name))
bmc.BT_RW_BUFF_base = write_buff
pass
##########################################
# Register update handler based on control bits
# For the attention name passed in, access and update any BT
# registers and buffers to simulate the BT commands
# Called on a per register BIT
################################################################################
def bt_handle_ctl(bmc, bit_name, RW):
#####################
# CTL BIT Definitions
#######
CTL_CLR_WR_PTR = 0x01
CTL_CLR_RD_PTR = 0x02
CTL_H2B_ATN = 0x04
CTL_OEM0 = 0x20
CTL_B_BUSY = 0x80
global processing_msg
global cur_access_count
global num_reads_for_processing
bt_debug_print(4,"Process BIT %s RW %s",(bit_name, RW))
if RW == "READ":
# NOP
bt_debug_print(4,"Process BIT %s for READ, NOP",(bit_name))
return
if(bit_name == "CLR_WR_PTR"):
#handled by simics model proper
bt_debug_print(9,"CRL_WR_PTR asserted")
elif(bit_name == "CLR_RD_PTR"):
#handled by simics model proper
bt_debug_print(4,"CLR_RD_PTR asserted")
elif (bit_name == "H2B_ATN"):
bt_debug_print(1,"H2B --> Process process message")
# Since this happens in "Simics" time it is instantaneous
# however base model sets B_BUSY for us, so need to clear
# when ready
handle_ipmi_msg(bmc)
#the model proper sets B_BUSY immediately
cur_access_count = 0
processing_msg = 1
elif(bit_name == "B2H_ATN"):
bt_debug_print(4,"Process BIT %s for WRITE",(bit_name))
elif(bit_name == "SMS_ATN"):
bt_debug_print(4,"Process BIT %s for WRITE",(bit_name))
elif(bit_name == "OEM0"):
bt_debug_print(4,"Process BIT %s for WRITE",(bit_name))
elif(bit_name == "H_BUSY"):
bt_debug_print(4,"Process BIT %s for WRITE",(bit_name))
elif(bit_name == "B_BUSY"):
bt_debug_print(4,"Process BIT %s for WRITE",(bit_name))
elif(bit_name == "B2H_ATN"):
bt_debug_print(4,"Process BIT %s for WRITE",(bit_name))
else:
bt_debug_print(2,"Unhandled BIT named %s", (bit_name))
pass
##########################################
# Main entry point.
# Parms are the conf_t pointer to the BMC models
# and the name of the register and whether being R/W
#
# NOTE: Assumed called from the model registers after_write
# after_read method
#
#######
def ipmi_bt_responder_reg(bmc_name, reg_name, RW):
bmc_obj = SIM_get_object(bmc_name)
bt_handle_reg(bmc_obj, reg_name, RW)
pass
def ipmi_bt_responder_ctl(bmc_name, bit_name, RW):
bmc_obj = SIM_get_object(bmc_name)
bt_handle_ctl(bmc_obj, bit_name, RW)
pass
def ipmi_bt_responder_send_sms(bmc_name, i_netfunc, i_cmd, i_buf):
global last_seq
global bmc_sms_q
global processing_msg
bmc_obj = SIM_get_object(bmc_name)
msg = ipmiMessage()
msg.setNetfLun(i_netfunc)
last_seq = (last_seq + 1) % 256
msg.setSeq(last_seq)
msg.setCmd(i_cmd)
msg.setDataBuf(i_buf)
msg.dump("Sending SMS Event/Command")
bmc_sms_q.append(msg)
processing_msg = 1
pass
|