极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 30861|回复: 8

刷写AMI BIOS成功

[复制链接]
发表于 2017-1-7 15:08:50 | 显示全部楼层 |阅读模式
       最近休息,升级老电脑,换4核CPU,刷自己修改后的bios,结果刷出了一点小故障,主板默认写保护,用了N多种方法,花了几天时间,虽然搞好了,但是,还是想自己打造一个编程器,以后想刷就刷。
       查看主板bios芯片型号为25Q80P,上网查芯片资料发现与arduino的ISP编程接口功能相近,用arduino再配上相应的程序应该能做到;于是再次上网搜索看有没有制作成功的例子,找到了两遍博文,提供了方法,但都没有提到最后是否成功。其中一位博客离成功一步之遥,结果板子烧了。
       查看硬件配置,应该是5V直接3.3V,电流大,arduino板子承受不了,给烧了。
       于是我在信号间串联1个电阻,本来准备用510欧的电阻,但手头没有,找了4个1K的,用孔孔板焊好后,与arduino连接,成功的将文件写入spi_flash芯片。先上图:
      
      

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复

使用道具 举报

 楼主| 发表于 2017-1-7 15:44:21 | 显示全部楼层
下面是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[4] = {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[4]) {
    if (check[0] == END_CHARS[0] && check[1] == END_CHARS[1]
        && check[2] == END_CHARS[2] && check[3] == END_CHARS[3]){
        return true;
    }
    return false;
}

char buffer[64];            // 串口一次最多64字节
char page[256];             // 每页有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[currentPos + i] = buffer[i];
                    }
                    WritePage(currentPage, page, 256);
                    memset(page, 0, 256*sizeof(byte));
                    currentPos = 0;
                    currentPage++;

            // 这个时候要判断是不是末尾,是的话我们完成写入操作
                    char check[4] = {page[252], page[253], page[254], page[255]};
                    if(isEnd(check)) {
                        Serial.println("WRITE FINISH!!!");
                        writed = true;
                    }
                } else {
                    for(int i=0; i<64; i++) {
                        page[currentPos + i] = buffer[i];
                    }
                    currentPos += 64;
                }
            } else {        // 如果读取不足一页证明已经到文件末尾了,这个时候数据要全部写入flash
                for(int i=0; i<size; i++) {
                    page[currentPos + i] = buffer[i];
                }
                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[i]));
            }
            
            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[i] = 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[i]));
    }
    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();
}
回复 支持 反对

使用道具 举报

发表于 2017-1-7 17:13:25 | 显示全部楼层
整一个编程器啊。很强,想楼主学习。电脑上的软件和arduino的通信的具体协议资料是否也可以提供?
回复 支持 反对

使用道具 举报

发表于 2017-1-7 20:05:28 | 显示全部楼层
20年前就用WINFLASH刷BIOS了,大多可刷,楼主可以试试。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-1-7 20:21:15 | 显示全部楼层
catnull 发表于 2017-1-7 17:13
整一个编程器啊。很强,想楼主学习。电脑上的软件和arduino的通信的具体协议资料是否也可以提供?

刚试过,arduino IDE 1.0.5_r2就行,只要有SPI.h库文件就好,串口发送用的是sscom,网上很多。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-1-7 22:33:40 | 显示全部楼层
msold5 发表于 2017-1-7 20:05
20年前就用WINFLASH刷BIOS了,大多可刷,楼主可以试试。

我这是arduino组成的硬件编程器,与windows下的winflash刷新升级软件不是一个概念
回复 支持 反对

使用道具 举报

发表于 2017-1-12 08:50:01 | 显示全部楼层
hbyczcp 发表于 2017-1-7 20:21
刚试过,arduino IDE 1.0.5_r2就行,只要有SPI.h库文件就好,串口发送用的是sscom,网上很多。

这个就是一个定制能力非常强悍的编程器了。理论上,只有flash芯片手册可以搞到,读写控制器的协议可以知道,那么就能进行编程。感谢楼主的无私分享。
回复 支持 反对

使用道具 举报

发表于 2020-7-10 09:55:12 | 显示全部楼层
剛剛使用樓主程式碼的時候,我稍稍做了點修改讓程式能夠刷我的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格式

前面變數定義的部分:

  1. long currentPage = 0L;        // 当前页数(修正:使用long紀錄來寫入更大的ROM)
复制代码


後面讀出數據的函數也跟著改用Long格式:

  1.   // 写完而且未打印时候我们把flash里面数据按页读出来
  2.   // 我们可以复制粘贴串口调试助手里面的16进制数据到winhex
  3.   // 然后保存到一个文档和原bios作对比看看写入是否正确
  4.   // 修改:使用long格式
  5.   if (writed && !printed) {
  6.     for (long i = 0L; i < currentPage; i++) {
  7.       ReadPage(i, page, 256);
  8.       for (int j = 0; j < 256; j++)
  9.       {
  10.         Serial.print(char(page[j]));
  11.       }
复制代码


接下來就是我發現有一些BIOS最後四個字節會在不同頁出現(有些也似乎會出現在頁尾)
所以將樓主IF改寫成需要多檢查是否為最後一頁的版本(但是頁數計算要小心一點...)

  1.           // 这个时候要判断是不是末尾,是的话我们完成写入操作
  2.           //Add:確認是否到達最後一頁(公式:HXD或WINHEX以每行256 Bytes檢視,看OFFSET ex:00FFFF00 扣掉尾巴兩個00 => 00FFFF 換10進制=65535 最後加1=65536)
  3.           if(currentPage == 65536L){
  4.             char check[4] = {page[252], page[253], page[254], page[255]};
  5.             if (isEnd(check)) {
  6.               Serial.println("WRITE FINISH!!!");
  7.               writed = true;
  8.             }
复制代码


改完這幾個地方,重新讀入檔案刷寫ROM
提醒一下,我試了幾次才發現接上調適器要印出"inited"時(代表SPI已經清空完成),才可以進行下一步刷寫(尤其是像我這種16MB的大概要等30-60秒才會出現inited)
花了大概20幾分鐘寫完插回主機板上測試,成功點亮(用115200波特率換算下大概寫入速度是100多Kb/s)

我是用SSCOM 5.13.1的串口調試器,打開後取消DTR選項上的勾勾。



附上成功點亮的照片做個紀念

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊

GMT+8, 2024-4-20 22:28 , Processed in 0.040408 second(s), 22 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表