1. EEPROM?
EEPROM (Electrically Erasable Programmable Read-Only Memory)一般用于存储程序和数据,包括但不限于以下内容:
1. 系统配置信息,如启动设置、时钟设置等;
2. BIOS和固件更新文件;
3. 用户数据,如文档、图像、音频、视频等;
4. 产品标识信息,如序列号、型号、制造日期等;
5. 加密密钥和证书等安全信息;
6. 其他设备和芯片的配置和参数信息,如调光器、温度控制器等。
EEPROM可以实现非易失性存储,即即使断电或重新启动系统,存储的信息也能够保留。因此,EEPROM通常用于需要长期存储的数据和程序,如嵌入式系统、电子设备和汽车电子等。
在服务器/交换机领域,EEPROM中一般存储的是FRU/TLV信息,这两种都是比较常见的。
FRU?
FRU是Field Replaceable Unit(现场可更换单元)的缩写。它是指计算机或其他电子设备中的可以被现场更换的模块、部件或组件。
例如,服务器中的硬盘、电源、风扇、网卡、内存插槽、处理器插槽等都属于FRU。当这些部件出现故障或需要升级时,可以在不影响整个系统的运行的同时,现场更换这些FRU以维护系统的可靠性和可用性。
FRU通常有标准的尺寸、接口和规格,以便于更换和兼容性。在一些计算机或设备中,FRU会赋予一个唯一的标识码或序列号,使得设备管理人员能够对其进行追踪、管理和维护。
TLV?
TLV是Type-Length-Value的缩写,是一种常用的数据结构格式。在这种格式中,数据被分为三个部分:
- Type:表示数据的类型,通常是一个数值或字母表示。
- Length:表示数据的长度,通常是一个数值,表示Value部分的字节长度。
- Value:表示实际的数据内容,可以是任意类型的数据,如字符串、整数、结构体等。
TLV格式常用于通信协议、配置文件、数据包等场景中。它的优点是灵活性高,可以描述各种不同类型的数据,同时也具有良好的可扩展性。
在实际应用中,TLV格式有时会被扩展为TLVs,也就是多个TLV结构的组合,以表示更加复杂的数据结构。此外,还有一些变种格式,如TVL和LTV等,它们的含义与TLV基本相同,只是顺序不同。
Loopback EEPROM
在交换机领域中,经常会使用到一种流量测试部件 -- Loopback,在Loopback中也有EEPROM,并存储了一些必要的信息:
出厂信息
主要包含了厂商,Serial Number,Part Number,生产日期,Loopback类型等等。
Sensor信息
主要包含Loopback的温度,电压,电流和功耗等信息。
低速信号
大部分的Loopback都支持4个低速信号的读取,包括present,interrupt,low power mode和reset。部分Loopback也支持设置一些信号,比如interrupt,present等等。
2. EEPROM的解析
这篇文章主要是以Loopback EEPROM为例,来讲解EEPROM信息如何解析。
1. 确定Loopback的类型
目前,基本所有的厂商的Loopback EEPROM协议都是基于标准协议进行微调,从而形成自己的协议,那如何确定当前的Loopback EEPROM是基于哪份标准协议进行修改的呢?
那就要引入一个EEPROM字段概念:Identifier Values,它定义了当前Loopback的类型,我们可以根据其类型确定所采用的EEPROM协议。
那这个Identifier Value如何读取?如何解析呢?
几乎所有的标准协议,都规定了统一的Identifier Value的对应关系,包括它的存储位置。
Identifier Value一般都是位于Loopback EEPROM的Page00的第128(0x80)位寄存器,如下图所示,这款Loopback的Identifier Value为0x11。
具体Identifier Value的对应关系如下图所示,上面Loopback的类型为QSFP28 or later:
2. 明确采用的EEPROM的协议
根据Identifier Value获取到Loopback的类型后,根据其类型确定所采用的EEPROM协议为SFF-8636/CMIS。
至此,我们就已经明确了这款Loopback EEPROM采用的标准协议。
3. EEPROM内容解析
确定了EEPROM协议后,我们可以根据其协议手册对EEPROM内容进行解析。
如上图,协议规定了128-255位寄存器存储的信息
通过文件节点获取EEPROM内容:
def read_eeprom_contents(self, file_path=None, size=256, offset=0):
"""Read the eeprom node contents.
Args:
@file_path: The path of the eeprom node.
@size: The size you want to read from this node.
@offset: The offset of the beginning of read.
Returns:
ret(int): The return value. 0 for success, greater than 0 otherwise.
contents(str): The contents of you read from this node.
"""
ret = PASS
contents = ""
retry_count = 0
if file_path is None:
logger_msg(
f"{sys._getframe().f_code.co_name} parmentation error.",
self.logger)
ret = FAIL
return ret, contents
while retry_count <= OPEN_NODE_RETRY:
try:
fd_eeprom = open(file_path,
mode='rb',
buffering=OPEN_NODE_BUFFER)
break
except Exception as e:
retry_count += 1
logger_msg("Open file {} Error: [{}].".format(
file_path, e), self.logger)
logger_msg("Retry to open file node [{}]:".format(
retry_count), self.logger)
else:
ret = FAIL
return ret, contents
try:
fd_eeprom.seek(offset)
contents = fd_eeprom.read(size)
except Exception as e:
logger_msg("Read file {} Error: [{}].".format(
file_path, e), self.logger)
ret = FAIL
return ret, contents
try:
fd_eeprom.close()
except Exception as e:
logger_msg("Close file {} Error: [{}].".format(
file_path, e), self.logger)
ret = FAIL
return ret, contents
return ret, contents
根据协议中对每个字段的定义去解析,最终将EEPROM的解析结果以键值对的形式返回:
def _reslove_eeprom(self, contents_list):
"""Get the type of the loopback.
Args:
@contents_list: The contents of hex strings.
Returns:
ret(int): The return value. 0 for success, greater than 0 otherwise.
type_value(str): The type of the loopback.
"""
ret = PASS
eeprom_dict = {}
if contents_list == None:
ret = FAIL
logger_msg("Parameters contents_list is None.", self.logger)
return ret, eeprom_dict
for eeprom_key in self.loopback_type.keys():
value = self.loopback_type[eeprom_key]["value"]
offset = self.loopback_type[eeprom_key]["offset"]
size = self.loopback_type[eeprom_key]["size"]
type = self.loopback_type[eeprom_key]["type"]
unit = self.loopback_type[eeprom_key]["unit"]
if eeprom_key == "Identifier":
eeprom_value = value
else:
start_addr = offset
end_addr = start_addr + size
hex_value = contents_list[start_addr:end_addr]
_ret, eeprom_value = self._data_format_coversion(
contents_list=hex_value,
type=type,
size=size)
if _ret != PASS:
ret = _ret
eeprom_dict[eeprom_key] = str(eeprom_value) + unit
return ret, eeprom_dict
4. 输出解析结果
def show_single_type_eeprom_dict(self,
loopback_type,
eeprom_dict_list=None):
"""Show single type eeprom dictionary by format table.
Args:
@loopback_type: The loopback type that you want to show.
@eprom_dict_list: The list of this type eeprom info dict list.
Returns:
ret(int): The return value. 0 for success, greater than 0 otherwise.
"""
ret = PASS
header = ["Port", "State", "Type"]
all_eeprom_list = []
if loopback_type != "NA":
ret, config = self._get_loopback_type_config(loopback_type)
if ret != PASS:
logger_msg(
"Get loopback type [{}] configuration failed".format(
loopback_type), self.logger)
for key in config.keys():
header.append(key)
try:
for eeprom_dict in eeprom_dict_list:
single_eeprom_list = []
for key in header:
single_eeprom_list.append(str_auto_line(
eeprom_dict[key], length_limit=KEY_LEN))
all_eeprom_list.append(single_eeprom_list)
logger_msg("\n===> [{} LOOPBACK TYPE EEPROM * {}]:".format(
loopback_type, len(all_eeprom_list)), LOG_INFO)
logger_msg(
tabulate(all_eeprom_list, header, tablefmt="grid"), LOG_INFO)
except Exception as e:
ret = FAIL
logger_msg("Show eeprom list error: [{}].".format(e), self.logger)
return ret
输出结果演示:
3. 总结
本文具体介绍了如何解析各类型的Loopback EEPROM,包括Loopback类型的确定,协议的确定,EEPROM的解析,结果的输出呈现。最终将一个个EEPROM文件节点转换为易读的列表信息。
后面有时间的话,可以将这个工具扩展,支持解析FRU和TLV信息等等。