刷写AMI BIOS成功
最近休息,升级老电脑,换4核CPU,刷自己修改后的bios,结果刷出了一点小故障,主板默认写保护,用了N多种方法,花了几天时间,虽然搞好了,但是,还是想自己打造一个编程器,以后想刷就刷。查看主板bios芯片型号为25Q80P,上网查芯片资料发现与arduino的ISP编程接口功能相近,用arduino再配上相应的程序应该能做到;于是再次上网搜索看有没有制作成功的例子,找到了两遍博文,提供了方法,但都没有提到最后是否成功。其中一位博客离成功一步之遥,结果板子烧了。
查看硬件配置,应该是5V直接3.3V,电流大,arduino板子承受不了,给烧了。
于是我在信号间串联1个电阻,本来准备用510欧的电阻,但手头没有,找了4个1K的,用孔孔板焊好后,与arduino连接,成功的将文件写入spi_flash芯片。先上图:
下面是arduinoe原代码:
/*************** arduino SPI_flash编程器(烧录器)*******************
communication with W25Q80BV (1 MBYTE SPI FLASH) using Arduino Pro Mini 3.3V/8MHz
Reference: http://www.instructables.com/id/How-to-Design-with-Discrete-SPI-Flash-Memory/?ALLSTEPS
Reference2: http://www.cnblogs.com/zlbg/p/4246721.html
wuernuer:http://blog.csdn.net/wuernuer/article/details/50759523
*/
// arduino(与spi_falsh连接) pins 10 (CS), 11 (MOSI), 12 (MISO),13 (SCK)
/*
* 使用Arduino作为编程器将bios写入spi_flash
* 注意bios文件不要超过芯片存储容量
* 理论上支持所有spi_flash(EEPROM)
* 测试电脑:七彩虹C.G41H D3 V23主板,L5420至强四核CPU(771硬改),2*2G DDR3 1333内存
* 官方1003_bios,文件大小1M
*/
#include <SPI.h>
#define READ_JEDEC_ID 0x9F
#define READ_STATUS_1 0x05
#define READ_DATA 0x03
#define WRITE_ENABLE 0x06
#define PAGE_PROGRAM 0x02
#define CHIP_ERASE 0xC7
// bios最后四个字节,用于判断写入已经结束
// 我们可以使用winhex打开bios文件查看最后四个字节
static char END_CHARS = {0x3B, 0x54, 0x05, 0x10};
void setup()
{
SPI.begin();
SPI.setDataMode(SPI_MODE0);
SPI.setBitOrder(MSBFIRST);
Serial.begin(115200);
ReadID();
EraseChip(); // 擦除flash
Serial.println("inited");
}
// 检测是否写入完成,当最后四个字节和结束字节一样的时候写入结束
bool isEnd(char check) {
if (check == END_CHARS && check == END_CHARS
&& check == END_CHARS && check == END_CHARS){
return true;
}
return false;
}
char buffer; // 串口一次最多64字节
char page; // 每页有256字节
int currentPos = 0; // 当前页当前位置
int currentPage = 0; // 当前页数
bool writed = false; // 写入完成标记
bool start = false; // 开始写入标记
bool printed = false; // 写入完成之后打印完成标记
void loop() {
if(!writed && Serial.available() > 0) { // 如果写入未完成我们继续读取串口
if(!start) {
start = true;
Serial.println("start write");
}
int size = Serial.readBytes(buffer, 64);
if(size >= 0) {
if(size == 64) {
if(currentPos + size == 256) { // 当读取恰好满一页时候我们写入flash一页
for(int i=0; i<64; i++) {
page = buffer;
}
WritePage(currentPage, page, 256);
memset(page, 0, 256*sizeof(byte));
currentPos = 0;
currentPage++;
// 这个时候要判断是不是末尾,是的话我们完成写入操作
char check = {page, page, page, page};
if(isEnd(check)) {
Serial.println("WRITE FINISH!!!");
writed = true;
}
} else {
for(int i=0; i<64; i++) {
page = buffer;
}
currentPos += 64;
}
} else { // 如果读取不足一页证明已经到文件末尾了,这个时候数据要全部写入flash
for(int i=0; i<size; i++) {
page = buffer;
}
WritePage(currentPage, page, currentPos + size);
memset(page, 0, 256*sizeof(byte));
currentPos = 0;
currentPage++;
// 完成写入操作
Serial.println("WRITE FINISH!!!");
writed = true;
}
// 每次读完串口缓冲都要清零
memset(buffer, 0, 64*sizeof(byte));
}
}
// 写完而且未打印时候我们把flash里面数据按页读出来
// 我们可以复制粘贴串口调试助手里面的16进制数据到winhex
// 然后保存到一个文档和原bios作对比看看写入是否正确
if(writed && !printed) {
for(int i=0; i<currentPage; i++) {
ReadPage(i, page, 256);
for(int i = 0; i < 256; i++)
{
Serial.print(char(page));
}
memset(page, 0, 256*sizeof(byte));
delay(20);
}
printed = true;
}
}
void CheckBusy()
{
digitalWrite(SS, HIGH);
digitalWrite(SS, LOW);
SPI.transfer(READ_STATUS_1);
while(SPI.transfer(0) & 0x01);
digitalWrite(SS, HIGH);
}
void ReadID()
{
digitalWrite(SS, HIGH);
digitalWrite(SS, LOW);
SPI.transfer(READ_JEDEC_ID);
byte manuID = SPI.transfer(0);
byte memoType = SPI.transfer(0);
byte capa = SPI.transfer(0);
digitalWrite(SS, HIGH);
Serial.print("Manufacturer ID: "); Serial.println(manuID, HEX);
Serial.print("Memory Type: "); Serial.println(memoType, HEX);
Serial.print("Capacity : "); Serial.println(capa, HEX);
CheckBusy();
}
void ReadPage(word pageNumber, char pageBuffer[], int length)
{
// pageNumber: 16-bit data
digitalWrite(SS, HIGH);
digitalWrite(SS, LOW);
SPI.transfer(READ_DATA);
SPI.transfer((pageNumber >> 8) & 0xFF);
SPI.transfer(pageNumber & 0xFF);
SPI.transfer(0);
for(int i = 0; i < length; i++)
{
pageBuffer = SPI.transfer(0);
}
digitalWrite(SS, HIGH);
CheckBusy();
}
void WritePage(word pageNumber, char pageBuffer[], int length)
{
digitalWrite(SS, HIGH);
digitalWrite(SS, LOW);
SPI.transfer(WRITE_ENABLE);
digitalWrite(SS, HIGH);
digitalWrite(SS, LOW);
SPI.transfer(PAGE_PROGRAM);
SPI.transfer((pageNumber >>8) & 0xFF);
SPI.transfer(pageNumber & 0xFF);
SPI.transfer(0);
for(int i = 0; i < length; i++)
{
SPI.transfer(byte(pageBuffer));
}
digitalWrite(SS, HIGH);
CheckBusy();
}
void EraseChip()
{
digitalWrite(SS, HIGH);
digitalWrite(SS, LOW);
SPI.transfer(WRITE_ENABLE);
digitalWrite(SS, HIGH);
digitalWrite(SS, LOW);
SPI.transfer(CHIP_ERASE);
digitalWrite(SS, HIGH);
CheckBusy();
}
整一个编程器啊。很强,想楼主学习。电脑上的软件和arduino的通信的具体协议资料是否也可以提供? 20年前就用WINFLASH刷BIOS了,大多可刷,楼主可以试试。 catnull 发表于 2017-1-7 17:13
整一个编程器啊。很强,想楼主学习。电脑上的软件和arduino的通信的具体协议资料是否也可以提供?
刚试过,arduino IDE 1.0.5_r2就行,只要有SPI.h库文件就好,串口发送用的是sscom,网上很多。 msold5 发表于 2017-1-7 20:05
20年前就用WINFLASH刷BIOS了,大多可刷,楼主可以试试。
我这是arduino组成的硬件编程器,与windows下的winflash刷新升级软件不是一个概念 hbyczcp 发表于 2017-1-7 20:21
刚试过,arduino IDE 1.0.5_r2就行,只要有SPI.h库文件就好,串口发送用的是sscom,网上很多。
这个就是一个定制能力非常强悍的编程器了。理论上,只有flash芯片手册可以搞到,读写控制器的协议可以知道,那么就能进行编程。感谢楼主的无私分享。 剛剛使用樓主程式碼的時候,我稍稍做了點修改讓程式能夠刷我的BIOS
我主機板的好像是因為有些BIOS數據設錯,整個BIOS開機就完全卡住,清除也沒用只能重寫。
剛剛好不容易爬到這篇帖,研究一下接了線,連上電腦開刷,樓主的原始程式刷不起來...
我的ROM晶片是W25Q128FV,是UEFI BIOS的, 不開機的主機板是ASUS B75M-A
接線的部分,看到樓主有用電容,我從其他料板拔了一顆50V 0.47uF的,實測可以用,訊號照樓主串接一顆1K電阻才到芯片。
華碩板子的UEFI ROM需要從CAP轉檔成ROM格式才能用,我另外爬文找了UEFITOOl(github的專案)解決
最後是程式修改的部分,因為我的ROM是16MB的,比樓主的1MB大很多,用HXD(樓主是用WINHEX)以每行256 Byte顯示,總共會有65535行(也就是65535頁),會超出Arduino中Int格式的最大值32768,所以可能前期刷寫時,發生了ˊ整數溢位沒有寫到正確的頁上,所以沒有刷寫成功,因此將頁面計數的部分擴大成Long格式
前面變數定義的部分:
long currentPage = 0L; // 当前页数(修正:使用long紀錄來寫入更大的ROM)
後面讀出數據的函數也跟著改用Long格式:
// 写完而且未打印时候我们把flash里面数据按页读出来
// 我们可以复制粘贴串口调试助手里面的16进制数据到winhex
// 然后保存到一个文档和原bios作对比看看写入是否正确
// 修改:使用long格式
if (writed && !printed) {
for (long i = 0L; i < currentPage; i++) {
ReadPage(i, page, 256);
for (int j = 0; j < 256; j++)
{
Serial.print(char(page));
}
接下來就是我發現有一些BIOS最後四個字節會在不同頁出現(有些也似乎會出現在頁尾)
所以將樓主IF改寫成需要多檢查是否為最後一頁的版本(但是頁數計算要小心一點...)
// 这个时候要判断是不是末尾,是的话我们完成写入操作
//Add:確認是否到達最後一頁(公式:HXD或WINHEX以每行256 Bytes檢視,看OFFSET ex:00FFFF00 扣掉尾巴兩個00 => 00FFFF 換10進制=65535 最後加1=65536)
if(currentPage == 65536L){
char check = {page, page, page, page};
if (isEnd(check)) {
Serial.println("WRITE FINISH!!!");
writed = true;
}
改完這幾個地方,重新讀入檔案刷寫ROM
提醒一下,我試了幾次才發現接上調適器要印出"inited"時(代表SPI已經清空完成),才可以進行下一步刷寫(尤其是像我這種16MB的大概要等30-60秒才會出現inited)
花了大概20幾分鐘寫完插回主機板上測試,成功點亮(用115200波特率換算下大概寫入速度是100多Kb/s)
我是用SSCOM 5.13.1的串口調試器,打開後取消DTR選項上的勾勾。
附上成功點亮的照片做個紀念
页:
[1]