Rbu_Linux.cpp

Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  * vim:expandtab:autoindent:tabstop=4:shiftwidth=4:filetype=c:cindent:textwidth=0:
00003  *
00004  * Copyright (C) 2005 Dell Inc.
00005  *  by Michael Brown <Michael_E_Brown@dell.com>
00006  * Licensed under the Open Software License version 2.1
00007  *
00008  * Alternatively, you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published
00010  * by the Free Software Foundation; either version 2 of the License,
00011  * or (at your option) any later version.
00012  
00013  * This program is distributed in the hope that it will be useful, but
00014  * WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00016  * See the GNU General Public License for more details.
00017  */
00018 
00019 // compat header should always be first header if including system headers
00020 #include "smbios/compat.h"
00021 
00022 #include <iostream>
00023 #include <sstream>
00024 #include <stdlib.h>
00025 #include <stdio.h>
00026 #include <time.h>
00027 #include <string.h>
00028 #include <errno.h>
00029 
00030 #include <sys/types.h>
00031 #include <unistd.h>
00032 
00033 #include "RbuImpl.h"
00034 #include "smbios/IToken.h"
00035 #include "smbios/SystemInfo.h"
00036 
00037 // always include last if included.
00038 #include "smbios/message.h"  // not needed outside of this lib. (mainly for gettext i18n)
00039 
00040 using namespace std;
00041 
00042 namespace rbu
00043 {
00044 const char *rbu_v1_mono_data_file = "/sys/firmware/rbu/rbudata";
00045 const char *rbu_v1_mono_size_file = "/sys/firmware/rbu/rbudatasize";
00046 const char *rbu_v1_pkt_data_file = "/sys/firmware/rbu/packetdata";
00047 const char *rbu_v1_pkt_size_file = "/sys/firmware/rbu/packetdatasize";
00048 
00049 const char *rbu_v2_fw_data_file = "/sys/class/firmware/dell_rbu/data";
00050 const char *rbu_v2_fw_load_file = "/sys/class/firmware/dell_rbu/loading";
00051 const char *rbu_v2_drv_data_file = "";
00052 const char *rbu_v2_img_type_file = "/sys/devices/platform/dell_rbu/image_type";
00053 const char *rbu_v2_pkt_size_file = "/sys/devices/platform/dell_rbu/packet_size";
00054 
00055     packet_type getSupportedPacketType(void)
00056     {
00057         packet_type pt = pt_mono;
00058         try
00059         {
00060             smbios::SmbiosFactory *smbiosFactory = smbios::SmbiosFactory::getFactory();
00061             smbios::ISmbiosTable *table = smbiosFactory->getSingleton();
00062             smbios::ISmbiosItem &rbuStructure = *((*table)[RBU_SMBIOS_STRUCT]);
00063     
00064             u8 byte = rbuStructure.getU8(0x0F); // Characteristics field
00065             if( byte & 0x01 ) 
00066                 pt = pt_packet;
00067         }
00068         catch(const smbios::DataOutOfBounds &)
00069         {
00070             // only happens when old-style struct present w/o characteristics field
00071             // this means only mono supported.
00072         }
00073         return pt;
00074     }
00075         
00076     void activateRbuToken()
00077     {
00078         smbios::TokenTableFactory *ttFactory = smbios::TokenTableFactory::getFactory() ;
00079         smbios::ITokenTable *tokenTable = ttFactory->getSingleton();
00080         (*tokenTable)[ RBU_ACTIVATE ]->activate();
00081     }
00082     
00083     void cancelRbuToken()
00084     {
00085         smbios::TokenTableFactory *ttFactory = smbios::TokenTableFactory::getFactory() ;
00086         smbios::ITokenTable *tokenTable = ttFactory->getSingleton();
00087         (*tokenTable)[ RBU_CANCEL ]->activate();
00088     }
00089 
00090     void checksumPacket(rbu_packet *pkt, size_t size)
00091     {
00092         u16 *buf = reinterpret_cast<u16 *>(pkt);
00093         pkt->pktChksum = 0;
00094     
00095         u16 csum = 0;
00096         for(size_t i=0; i<size/2; ++i)
00097             csum = csum + buf[i];
00098     
00099         pkt->pktChksum = -csum;
00100     }
00101 
00102     driver_type getDriverType()
00103     {
00104         if (!access(rbu_v1_mono_data_file, F_OK))
00105             return rbu_linux_v1;
00106         else if (!access(rbu_v2_fw_data_file, F_OK))
00107             return rbu_linux_v2;
00108         else
00109             return rbu_unsupported;
00110     }
00111 
00112     void createPacket(char *buffer, size_t bufSize, size_t imageSize)
00113     {
00114         // set up packet
00115         rbu_packet *pkt = reinterpret_cast<rbu_packet *>(buffer);
00116     
00117         pkt->pktId = 0x4B505224;  //2452504B;   // must be '$RPK'
00118         pkt->pktSize = bufSize / 1024;    // size of packet in KB
00119         pkt->reserved1 = 0;  //
00120         pkt->hdrSize = 2;    // size of packet header in paragraphs (16 byte chunks)
00121         pkt->reserved2 = 0;  //
00122         pkt->pktSetId = 0x12345678;   // unique id for packet set, can be anything
00123         pkt->pktNum = 0;     // sequential pkt number (only thing that changes)
00124         pkt->totPkts = (imageSize/bufSize) + ((imageSize % bufSize) ? 1:0) + 1;// total number of packets
00125         pkt->pktVer = 1;     // version == 1 for now
00126         pkt->pktChksum = 0;  // sum all bytes in pkt must be zero
00127     
00128         checksumPacket(pkt, bufSize);
00129     }
00130 
00131     static void writePacket(const char *fn, const char *buffer, size_t bufSize, bool openclose)
00132     {
00133         static FILE *data_fh = 0;
00134         if(!data_fh)
00135             data_fh = fopen(fn, "wb");
00136 
00137         if (!data_fh)
00138             throw RbuDriverIOErrorImpl(strerror(errno));
00139     
00140         try
00141         {
00142             fwrite(buffer, 1, bufSize, data_fh);
00143             if (ferror(data_fh))
00144                 throw RbuDriverIOErrorImpl(strerror(errno));
00145     
00146             if(openclose)
00147             {
00148                 fclose(data_fh);
00149                 data_fh = 0;
00150             }
00151         }
00152         catch(...)
00153         {
00154             fclose(data_fh);
00155             throw;
00156         }
00157 
00158         fflush(NULL);
00159     }
00160 
00161     static void pktUpdateLoop(FILE *hdr_fh, const char *packetFilename, char *buffer, size_t bufSize, bool openclose)
00162     {
00163         cout << "Writing RBU data (4096k/dot): ";
00164 
00165         fseek(hdr_fh, 0, SEEK_END);
00166         size_t totalSizeBytes = ftell(hdr_fh);
00167 
00168         fseek(hdr_fh, 0, 0);
00169         // set up packet
00170         rbu_packet *pkt = reinterpret_cast<rbu_packet *>(buffer);
00171         createPacket(buffer, bufSize, totalSizeBytes);
00172     
00173         // TODO: password support.
00174         writePacket(packetFilename, buffer, bufSize, openclose);
00175         cout << ".";
00176     
00177         while(!feof(hdr_fh))
00178         {
00179             ++pkt->pktNum;
00180             memset(pkt->pktData, 0, bufSize - sizeof(rbu_packet));
00181             fread(pkt->pktData, 1, bufSize - sizeof(rbu_packet), hdr_fh);
00182             if (ferror(hdr_fh))
00183                 throw HdrFileIOErrorImpl(strerror(errno));
00184     
00185             checksumPacket(pkt, bufSize);
00186             writePacket(packetFilename, buffer, bufSize, openclose);
00187     
00188             cout << ".";
00189         }
00190         cout << endl;
00191     
00192         cout << "Done writing packet data." << endl;
00193     }
00194 
00195     static void monoUpdateLoop(FILE *hdr_fh, FILE *data_fh)
00196     {
00197         cout << "Writing RBU data (4096k/dot): ";
00198         fseek(hdr_fh, 0, 0);
00199         const int bufSize = 4096;
00200         char *buffer[bufSize];
00201         while(!feof(hdr_fh))
00202         {
00203             memset(buffer, 0, bufSize);
00204             size_t readSz = fread(buffer, 1, bufSize, hdr_fh);
00205             if (ferror(hdr_fh))
00206                 throw HdrFileIOErrorImpl(strerror(errno));
00207     
00208             fwrite(buffer, 1, readSz, data_fh);
00209             if (ferror(data_fh))
00210                 throw RbuDriverIOErrorImpl(strerror(errno));
00211             cout << "." << flush;
00212         }
00213         cout << endl;
00214     }
00215 
00216 
00217 /****************************************
00218    RBU Linux v1 functions (older driver)
00219 ****************************************/
00220     static void setSize(const char *fn, size_t sz)
00221     {
00222         FILE *size_fh = fopen(fn, "wb");
00223         if (!size_fh)
00224             throw RbuDriverIOErrorImpl(strerror(errno));
00225 
00226         ostringstream ost("");
00227         ost << sz;
00228         cout << "writing (" << sz << ") to file: " << fn << endl;
00229         fwrite(ost.str().c_str(), 1, ost.str().length(), size_fh);
00230         if (ferror(size_fh))
00231             throw RbuDriverIOErrorImpl(strerror(errno));
00232         fclose(size_fh);
00233         size_fh = 0;
00234     }
00235 
00236     static void doPacketUpdate_v1(FILE *hdr_fh) 
00237     {
00238         const size_t bufSize = 4096;
00239         char buffer[bufSize] = {0};
00240     
00241         // set packet size, reset mono handler
00242         setSize(rbu_v1_mono_size_file, 0);
00243         setSize(rbu_v1_pkt_size_file, bufSize);
00244 
00245         pktUpdateLoop(hdr_fh, rbu_v1_pkt_data_file, buffer, bufSize, true);
00246     }
00247     
00248     static void doMonoUpdate_v1(FILE *hdr_fh) 
00249     {
00250         cout << "Prep driver for data load." << endl;
00251     
00252         FILE *data_fh = fopen(rbu_v1_mono_data_file, "wb");
00253         if (!data_fh)
00254             throw RbuDriverIOErrorImpl(strerror(errno));
00255     
00256         fseek(hdr_fh, 0, SEEK_END);
00257         size_t totalSizeBytes = ftell(hdr_fh);
00258 
00259         // set mono data size, reset pkt handler
00260         setSize(rbu_v1_pkt_size_file, 0);
00261         setSize(rbu_v1_mono_size_file, totalSizeBytes);
00262 
00263         monoUpdateLoop(hdr_fh, data_fh);
00264         
00265         fclose(data_fh);
00266         data_fh = 0;
00267         fflush(NULL);
00268     
00269         cout << "BIOS staging is complete." << endl;
00270     }
00271 
00272 
00273 /****************************************
00274    RBU Linux v2 functions (newer, included in 2.6.14+)
00275 ****************************************/
00276 
00277     static void setPacketType(packet_type type)
00278     {
00279         FILE *type_fh = 0;
00280         type_fh = fopen(rbu_v2_img_type_file, "wb");
00281         if (!type_fh)
00282             throw RbuDriverIOErrorImpl(strerror(errno));
00283     
00284         switch(type)
00285         {
00286         case pt_mono:
00287             fwrite("mono\0", 1, 5, type_fh);
00288             break;
00289         case pt_packet:
00290             fwrite("packet\0", 1, 7, type_fh);
00291             break;
00292         case pt_any:  /*fall thru*/
00293         case pt_init:  /*fall thru*/
00294         default:
00295             // not really a packet type, but causes driver to free its memory
00296             fwrite("init\0", 1, 7, type_fh);
00297             break;
00298         }
00299     
00300         if (ferror(type_fh))
00301             throw RbuDriverIOErrorImpl(strerror(errno));
00302 
00303         fclose(type_fh);
00304     }        // Step 5: set rbu cmos token
00305     
00306     
00307     static void waitForFile(const char *fn, time_t wait)
00308     {
00309         time_t start = time(NULL);
00310         while( access(fn, F_OK) && (time(NULL) - start < wait))
00311             /*nothing*/;
00312     }
00313     
00314     static void setLoadValue(char val)
00315     {
00316         FILE *load_fh = 0;
00317     
00318         waitForFile(rbu_v2_fw_load_file, 10);
00319     
00320         load_fh = fopen(rbu_v2_fw_load_file, "wb");
00321         if (!load_fh)
00322             throw RbuDriverIOErrorImpl(strerror(errno));
00323     
00324         fwrite(&val, 1, 1, load_fh);
00325         if (ferror(load_fh))
00326             throw RbuDriverIOErrorImpl(strerror(errno));
00327         fclose(load_fh);
00328         fflush(NULL);
00329     }
00330     
00331     static void doPacketUpdate_v2(FILE *hdr_fh) 
00332     {
00333         const size_t bufSize = 4096;
00334         char buffer[bufSize] = {0};
00335 
00336         setSize(rbu_v2_pkt_size_file, bufSize);
00337         setLoadValue('1');
00338         pktUpdateLoop(hdr_fh, rbu_v2_fw_data_file, buffer, bufSize, false);
00339         setLoadValue('0');
00340     }
00341     
00342     static void doMonoUpdate_v2(FILE *hdr_fh) 
00343     {
00344         cout << "Prep driver for data load." << endl;
00345         setLoadValue('1');
00346     
00347         FILE *data_fh = fopen(rbu_v2_fw_data_file, "wb");
00348         if (!data_fh)
00349             throw RbuDriverIOErrorImpl(strerror(errno));
00350     
00351         monoUpdateLoop(hdr_fh, data_fh);
00352        
00353         fclose(data_fh);
00354         data_fh = 0;
00355         fflush(NULL);
00356     
00357         cout << "Notify driver data is finished." << endl;
00358         setLoadValue('0');
00359     }
00360 
00361 
00362 
00363 /*****************************************************************************
00364 ******************************************************************************
00365 
00366 main entry points for this module.
00367 
00368 ******************************************************************************
00369 *****************************************************************************/
00370     
00371     void dellBiosUpdate(string fileName, packet_type force_type)
00372     {
00373         FILE *hdr_fh = fopen( fileName.c_str(), "rb" );
00374         if (!hdr_fh)
00375             throw InvalidHdrFileImpl(strerror(errno));
00376         
00377         // TODO: verify that it is a HDR file
00378         // TODO: checksum HDR file
00379 
00380         packet_type pt = getSupportedPacketType();
00381         cout << "Supported RBU type for this system: "
00382             << (pt == pt_packet ? "MONOLITHIC, PACKET" : "MONOLITHIC")
00383             << endl;
00384     
00385         if( force_type != pt_init || force_type != pt_any ) 
00386             pt = force_type;
00387     
00388         driver_type dt = getDriverType();
00389 
00390         if (pt == pt_packet)
00391         {
00392             cout << endl;
00393             cout << "WARNING:" << endl;
00394             cout << "        Packet support not yet complete/tested." << endl;
00395             cout << "        please report ID information for this system so" << endl;
00396             cout << "        that packet support can be tested." << endl;
00397             cout << endl;
00398         }
00399 
00400         if (dt == rbu_linux_v2)
00401         {
00402             // initialize RBU driver.
00403             cout << "Using RBU v2 driver. Initializing Driver. ";
00404             setPacketType(pt_init);
00405         
00406             // set packet/mono type
00407             cout << "Setting RBU type in v2 driver to: " 
00408                 << (pt == pt_packet ? "PACKET" : "MONOLITHIC")
00409                 << (pt == force_type ? " (FORCED) ": "" )
00410                 << endl;
00411             setPacketType(pt);
00412         
00413             if (pt == pt_packet)
00414                 doPacketUpdate_v2(hdr_fh);
00415             else
00416                 doMonoUpdate_v2(hdr_fh);
00417         } 
00418         else if(dt == rbu_linux_v1)
00419         {
00420             cout << "Using RBU v1 method: " 
00421                 << (pt == pt_packet ? "PACKET" : "MONOLITHIC")
00422                 << (pt == force_type ? " (FORCED) ": "" )
00423                 << endl;
00424             if (pt == pt_packet)
00425                 doPacketUpdate_v1(hdr_fh);
00426             else
00427                 doMonoUpdate_v1(hdr_fh);
00428         }
00429         else
00430         {
00431             throw RbuNotSupportedImpl("Could not open Dell RBU driver.");
00432         }
00433     
00434         fclose(hdr_fh);
00435     
00436         cout << "Activate CMOS bit to notify BIOS that update is ready on next boot." << endl;
00437         activateRbuToken();
00438     
00439         cout << "Update staged sucessfully. BIOS update will occur on next reboot." << endl;
00440     }
00441     
00442     void cancelDellBiosUpdate()
00443     {
00444         // FOR LOAD CANCEL:
00445         // Step 1: always unset CMOS first
00446         cout << "Cancel BIOS CMOS notification bit." << endl;
00447         cancelRbuToken();
00448 
00449         driver_type dt = getDriverType();
00450         switch(dt)
00451         {
00452         case rbu_linux_v2:
00453             // Step 2: make sure firmware class doesn't think we are loading
00454             cout << "Free kernel driver memory." << endl;
00455             setLoadValue('0');
00456 
00457             // Step 3: tell the dell_rbu driver to free its allocated memory
00458             cout << "Re-initialize driver for next user." << endl;
00459             setPacketType(pt_init);
00460             break;
00461 
00462         case rbu_linux_v1:
00463             // Step 2: Free monolithic, if present
00464             cout << "Re-initialize driver for next user." << endl;
00465             setSize(rbu_v1_mono_size_file, 0);
00466             setSize(rbu_v1_pkt_size_file, 0);
00467             fflush(NULL);
00468             break;
00469 
00470         default:
00471             cout << "Could not determine RBU driver present, skipping." << endl;
00472             break;
00473         }
00474     }
00475 }
00476 

Generated on Tue Jul 11 20:46:46 2006 for SMBIOS Library by  doxygen 1.4.7