我的编程空间,编程开发者的网络收藏夹
学习永远不晚

硬件学习之通过树莓派操控 jtag

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

硬件学习之通过树莓派操控 jtag

作者:Hcamael@知道创宇404实验室
时间:2019年10月21日
原文链接:https://paper.seebug.org/1060/

最近在搞路由器的时候,不小心把CFE给刷挂了,然后发现能通过jtag进行救砖,所以就对jtag进行了一波研究。

最开始只是想救砖,并没有想深入研究的想法。

救砖尝试

变砖的路由器型号为:LinkSys wrt54g v8

CPU 型号为:BCM5354

Flash型号为:K8D6316UBM

首先通过jtagulator得到了设备上jtag接口的顺序。

正好公司有一个jlink,但是参试了一波失败,识别不了设备。

随后通过Google搜到发现了一个工具叫: tjtag-pi

可以通树莓派来控制jtag,随后学习了一波树莓派的操作。

树莓派Pins

我使用的是rpi3,其接口编号图如下:

硬件学习之通过树莓派操控 jtag

或者在树莓派3中可以使用gpio readall查看各个接口的状态:

硬件学习之通过树莓派操控 jtag

rpi3中的Python有一个RPi.GPIO模块,可以控制这些接口。

举个例子:

>>> from RPi import GPIO>>> GPIO.setmode(GPIO.BCM)>>> GPIO.setup(2, GPIO.OUT)>>> GPIO.setup(3, GPIO.IN)

首先是需要进行初始化GPIO的模式,BCM模式对应的针脚排序是上面图中橙色的部门。

然后可以对各个针脚进行单独设置,比如上图中,把2号针脚设置为输出,3号针脚设置为输入。

>>> GPIO.output(2, 1)>>> GPIO.output(2, 0)

使用output函数进行二进制输出

>>> GPIO.input(3)1

使用input函数获取针脚的输入。

我们可以用线把两个针脚连起来测试上面的代码。

将树莓派对应针脚和路由器的连起来以后,可以运行tjtag-pi程序。但是在运行的过程中却遇到了问题,经常会卡在写flash的时候。通过调整配置,有时是可以写成功的,但是CFE并没有被救回来,备份flash的数据,发现并没有成功写入数据。

因为使用轮子失败,所以我只能自己尝试研究和造轮子了。

jtag

首先是针脚,我见过的设备给jtag一般是提供了5 * 2以上的引脚。其中有一般都是接地引脚,另一半只要知道4个最重要的引脚。

这四个引脚一般情况下的排序是:

TDITDOTMSTCK

TDI表示输入,TDO表示输出,TMS控制位,TCK时钟输入。

硬件学习之通过树莓派操控 jtag

jtag大致架构如上图所示,其中TAP-Controller的架构如下图所示:

硬件学习之通过树莓派操控 jtag

根据上面这两个架构,对jtag的原理进行讲解。

jtag的核心是TAP-Controller,通过解析TMS数据,来决定输入和输出的关系。所以我们先来看看TAP-Controller的架构。

从上面的图中我们可以发现,在任何状态下,输出5次1,都会回到TEST LOGIC RESET状态下。所以在使用jtag前,我们先通过TMS端口,发送5次为1的数据,jtag的状态机将会进入到RESET的复原状态。

当TAP进入到SHIFT-IR的状态时,Instruction Register将会开始接收TDI传入的数据,当输入结束后,进入到UPDATE-IR状态时将会解析指令寄存器的值,随后决定输出什么数据。

SHIFT-DR则是控制数据寄存器,一般是在读写数据的时候需要使用。

讲到这里,就出现一个问题了,TMS就一个端口,jtag如何知道TMS每次输入的值是多少呢?这个时候就需要用到TCK端口了,该端口可以称为时钟指令。当TCK从低频变到高频时,获取一比特TMS/TDI输入,TDO输出1比特。

比如我们让TAP进行一次复位操作:

for x in range(5):    TCK 0    TMS 1    TCK 1

再比如,我们需要给指令寄存器传入0b10:

复位

进入RUN-TEST/IDLE状态

TCK 0TMS 0TCK 1

进入SELECT-DR-SCAN状态

TCK 0TMS 1TCK 1

进入SELECT-IR-SCAN状态

TCK 0TMS 1TCK 1

进入CAPTURE-IR状态

TCK 0TMS 0TCK 1

进入SHIFT-IR状态

TCK 0TMS 0 TCK 1

输入0b10

TCK 0TMS 0TDI 0TCK 1TCK 0TMS 1TDI 1TCK 0

随后就是进入EXIT-IR -> UPDATE-IR

根据上面的理论我们就可以通过写一个设置IR的函数:

def clock(tms, tdi):    tms = 1 if tms else 0    tdi = 1 if tdi else 0    GPIO.output(TCK, 0)    GPIO.output(TMS, tms)    GPIO.output(TDI, tdi)    GPIO.output(TCK, 1)    return GPIO.input(TDO)def reset():    clock(1, 0)    clock(1, 0)    clock(1, 0)    clock(1, 0)    clock(1, 0)    clock(0, 0)def set_instr(instr):    clock(1, 0)      clock(1, 0)    clock(0, 0)    clock(0, 0)    for i in range(INSTR_LENGTH):        clock(i==(INSTR_LENGTH - 1), (instr>>i)&1)    clock(1, 0)    clock(0, 0)

把上面的代码理解清楚后,基本就理解了TAP的逻辑。接下来就是指令的问题了,指令寄存器的长度是多少?指令寄存器的值为多少时是有意义的?

不同的CPU对于上面的答案都不一样,通过我在网上搜索的结果,每个CPU应该都有一个bsd(boundary scan description)文件。本篇文章研究的CPU型号是BCM5354,但是我并没有在网上找到该型号CPU的bsd文件。我只能找了一个相同厂商不同型号的CPU的bsd文件进行参考。

bcm53101m.bsd

在该文件中我们能看到jtag端口在cpu端口的位置:

"tck              : B46  , " &"tdi              : A57  , " &"tdo              : B47  , " &"tms              : A58  , " &"trst_b           : A59  , " &attribute TAP_SCAN_RESET of trst_b                   : signal is true;attribute TAP_SCAN_IN    of tdi                      : signal is true;attribute TAP_SCAN_MODE  of tms                      : signal is true;attribute TAP_SCAN_OUT   of tdo                      : signal is true;attribute TAP_SCAN_CLOCK of tck                      : signal is (2.5000000000000000000e+07, BOTH);

能找到指令长度的定义:

attribute INSTRUCTION_LENGTH of top: entity is 32;

能找到指令寄存器的有效值:

attribute INSTRUCTION_OPCODE of top: entity is  "IDCODE       (11111111111111111111111111111110)," &  "BYPASS       (00000000000000000000000000000000, 11111111111111111111111111111111)," &  "EXTEST       (11111111111111111111111111101000)," &  "SAMPLE       (11111111111111111111111111111000)," &  "PRELOAD      (11111111111111111111111111111000)," &  "HIGHZ        (11111111111111111111111111001111)," &  "CLAMP        (11111111111111111111111111101111) " ;

当指令寄存器的值为IDCODE的时候,IDCODE寄存器的输出通道开启,我们来看看IDCODE寄存器:

attribute IDCODE_REGISTER of top: entity is  "0000"             & -- version  "0000000011011111" & -- part number  "00101111111"      & -- manufacturer's identity  "1";                   -- required by 1149.1

从这里我们能看出IDCODE寄存器的固定输出为: 0b00000000000011011111001011111111

那我们怎么获取TDO的输出呢?这个时候数据寄存器DR就发挥作用了。

  1. TAP状态机切换到SHIFT-IR
  2. 输出IDCODE到IR中
  3. 切换到SHIFT-DR
  4. 获取INSTRUCTION_LENGTH长度的TDO输出值
  5. 退出

用代码形式的表示如下:

def ReadWriteData(data):    out_data = 0    clock(1, 0)    clock(0, 0)    clock(0, 0)    for i in range(32):                    out_bit  = clock((i == 31), ((data >> i) &amp; 1))        out_data = out_data | (out_bit << i)    clock(1,0)    clock(0,0)    return out_datadef ReadData():    return ReadWriteData(0)def WriteData(data):    ReadWriteData(data)def idcode():    set_instr(INSTR_IDCODE)    print(hex(self.ReadData()))

因为我也是个初学者,边界扫描描述文件中的内容并不是都能看得懂,比如在边界扫描文件中并不能看出BYPASS指令是做什么的。但是在其他文档中,得知BYPASS寄存器一般是用来做测试的,在该寄存器中,输入和输出是直连,可以通过比较输入和输出的值,来判断端口是否连接正确。

另外还有边界扫描寄存器一大堆数据,也没完全研究透,相关的资料少的可怜。而且也找不到对应CPU的文档。

当研究到这里的时候,我只了解了jtag的基本原理,只会使用两个基本的指令(IDCODE, BYPASS)。但是对我修砖没任何帮助。

没办法,我又回头来看tjtag的源码,在tjtag中定义了几个指令寄存器的OPCODE:

INSTR_ADDRESS = 0x08INSTR_DATA    = 0x09INSTR_CONTROL = 0x0A

照抄着tjtag中flash AMD的操作,可以成功对flash进行擦除,写入操作读取操作。但是却不知其原理。

这里分享下我的脚本:jtag.py

flash 文档:https://www.dataman.com/media/datasheet/Samsung/K8D6x16UTM_K8D6x16UBM_rev16.pdf

接下来将会对该flash 文档进行研究,并在之后的文章中分享我后续的研究成果。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

硬件学习之通过树莓派操控 jtag

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

硬件学习之通过树莓派操控 jtag

作者:Hcamael@知道创宇404实验室时间:2019年10月21日原文链接:https://paper.seebug.org/1060/ 最近在搞路由器的时候,不小心把CFE给刷挂了,然后发现能通过jtag进行救砖,所以就对jtag进行
2023-06-04

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录