blob: 500b8fcb632b69dfffa752e8ebf53362cdcbc6fc [file] [log] [blame]
/*
* Copyright (C) 2015 NXP Semiconductors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <cutils/log.h>
#include <semaphore.h>
#include <AlaLib.h>
#include <JcopOsDownload.h>
#include <IChannel.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
JcopOsDwnld JcopOsDwnld::sJcopDwnld;
INT32 gTransceiveTimeout = 120000;
tJBL_STATUS (JcopOsDwnld::*JcopOs_dwnld_seqhandler[])(
JcopOs_ImageInfo_t* pContext, tJBL_STATUS status, JcopOs_TranscieveInfo_t* pInfo)={
&JcopOsDwnld::TriggerApdu,
&JcopOsDwnld::GetInfo,
&JcopOsDwnld::load_JcopOS_image,
&JcopOsDwnld::GetInfo,
&JcopOsDwnld::load_JcopOS_image,
&JcopOsDwnld::GetInfo,
&JcopOsDwnld::load_JcopOS_image,
NULL
};
pJcopOs_Dwnld_Context_t gpJcopOs_Dwnld_Context = NULL;
static const char *path[3] = {"/data/vendor/ese/JcopOs_Update1.apdu",
"/data/vendor/ese/JcopOs_Update2.apdu",
"/data/vendor/ese/JcopOs_Update3.apdu"};
/*******************************************************************************
**
** Function: getInstance
**
** Description: Get the JcopOsDwnld singleton object.
**
** Returns: JcopOsDwnld object.
**
*******************************************************************************/
JcopOsDwnld* JcopOsDwnld::getInstance()
{
JcopOsDwnld *jd = new JcopOsDwnld();
return jd;
}
/*******************************************************************************
**
** Function: getJcopOsFileInfo
**
** Description: Verify all the updater files required for download
** are present or not
**
** Returns: True if ok.
**
*******************************************************************************/
bool JcopOsDwnld::getJcopOsFileInfo()
{
static const char fn [] = "JcopOsDwnld::getJcopOsFileInfo";
bool status = true;
struct stat st;
for (int num = 0; num < 3; num++)
{
if (stat(path[num], &st))
{
status = false;
}
}
return status;
}
/*******************************************************************************
**
** Function: initialize
**
** Description: Initialize all member variables.
** native: Native data.
**
** Returns: True if ok.
**
*******************************************************************************/
bool JcopOsDwnld::initialize (IChannel_t *channel)
{
static const char fn [] = "JcopOsDwnld::initialize";
ALOGD ("%s: enter", fn);
if (!getJcopOsFileInfo())
{
ALOGD("%s: insufficient resources, file not present", fn);
return (false);
}
gpJcopOs_Dwnld_Context = (pJcopOs_Dwnld_Context_t)malloc(sizeof(JcopOs_Dwnld_Context_t));
if(gpJcopOs_Dwnld_Context != NULL)
{
memset((void *)gpJcopOs_Dwnld_Context, 0, (UINT32)sizeof(JcopOs_Dwnld_Context_t));
gpJcopOs_Dwnld_Context->channel = (IChannel_t*)malloc(sizeof(IChannel_t));
if(gpJcopOs_Dwnld_Context->channel != NULL)
{
memset(gpJcopOs_Dwnld_Context->channel, 0, sizeof(IChannel_t));
}
else
{
ALOGD("%s: Memory allocation for IChannel is failed", fn);
return (false);
}
gpJcopOs_Dwnld_Context->pJcopOs_TransInfo.sSendData = (UINT8*)malloc(sizeof(UINT8)*JCOP_MAX_BUF_SIZE);
if(gpJcopOs_Dwnld_Context->pJcopOs_TransInfo.sSendData != NULL)
{
memset(gpJcopOs_Dwnld_Context->pJcopOs_TransInfo.sSendData, 0, JCOP_MAX_BUF_SIZE);
}
else
{
ALOGD("%s: Memory allocation for SendBuf is failed", fn);
return (false);
}
}
else
{
ALOGD("%s: Memory allocation failed", fn);
return (false);
}
mIsInit = true;
memcpy(gpJcopOs_Dwnld_Context->channel, channel, sizeof(IChannel_t));
ALOGD ("%s: exit", fn);
return (true);
}
/*******************************************************************************
**
** Function: finalize
**
** Description: Release all resources.
**
** Returns: None
**
*******************************************************************************/
void JcopOsDwnld::finalize ()
{
static const char fn [] = "JcopOsDwnld::finalize";
ALOGD ("%s: enter", fn);
mIsInit = false;
if(gpJcopOs_Dwnld_Context != NULL)
{
if(gpJcopOs_Dwnld_Context->channel != NULL)
{
free(gpJcopOs_Dwnld_Context->channel);
gpJcopOs_Dwnld_Context->channel = NULL;
}
if(gpJcopOs_Dwnld_Context->pJcopOs_TransInfo.sSendData != NULL)
{
free(gpJcopOs_Dwnld_Context->pJcopOs_TransInfo.sSendData);
gpJcopOs_Dwnld_Context->pJcopOs_TransInfo.sSendData = NULL;
}
free(gpJcopOs_Dwnld_Context);
gpJcopOs_Dwnld_Context = NULL;
}
ALOGD ("%s: exit", fn);
}
/*******************************************************************************
**
** Function: JcopOs_Download
**
** Description: Starts the OS download sequence
**
** Returns: Success if ok.
**
*******************************************************************************/
tJBL_STATUS JcopOsDwnld::JcopOs_Download()
{
static const char fn [] = "JcopOsDwnld::JcopOs_Download";
tJBL_STATUS wstatus = STATUS_FAILED;
JcopOs_TranscieveInfo_t pTranscv_Info;
JcopOs_ImageInfo_t ImageInfo;
UINT8 retry_cnt = 0x00;
ALOGD("%s: enter:", fn);
if(mIsInit == false)
{
ALOGD ("%s: JcopOs Dwnld is not initialized", fn);
wstatus = STATUS_FAILED;
}
else
{
do
{
wstatus = JcopOsDwnld::JcopOs_update_seq_handler();
if(wstatus == STATUS_FAILED)
retry_cnt++;
else
break;
}while(retry_cnt < JCOP_MAX_RETRY_CNT);
}
ALOGD("%s: exit; status = 0x%x", fn, wstatus);
return wstatus;
}
/*******************************************************************************
**
** Function: JcopOs_update_seq_handler
**
** Description: Performs the JcopOS download sequence
**
** Returns: Success if ok.
**
*******************************************************************************/
tJBL_STATUS JcopOsDwnld::JcopOs_update_seq_handler()
{
static const char fn[] = "JcopOsDwnld::JcopOs_update_seq_handler";
UINT8 seq_counter = 0;
JcopOs_ImageInfo_t update_info = (JcopOs_ImageInfo_t )gpJcopOs_Dwnld_Context->Image_info;
JcopOs_TranscieveInfo_t trans_info = (JcopOs_TranscieveInfo_t )gpJcopOs_Dwnld_Context->pJcopOs_TransInfo;
update_info.index = 0x00;
update_info.cur_state = 0x00;
tJBL_STATUS status = STATUS_FAILED;
ALOGD("%s: enter", fn);
status = GetJcopOsState(&update_info, &seq_counter);
if(status != STATUS_SUCCESS)
{
ALOGE("Error in getting JcopOsState info");
}
else
{
ALOGE("seq_counter %d", seq_counter);
while((JcopOs_dwnld_seqhandler[seq_counter]) != NULL )
{
status = STATUS_FAILED;
status = (*this.*(JcopOs_dwnld_seqhandler[seq_counter]))(&update_info, status, &trans_info );
if(STATUS_SUCCESS != status)
{
ALOGE("%s: exiting; status=0x0%X", fn, status);
break;
}
seq_counter++;
}
}
return status;
}
/*******************************************************************************
**
** Function: TriggerApdu
**
** Description: Switch to updater OS
**
** Returns: Success if ok.
**
*******************************************************************************/
tJBL_STATUS JcopOsDwnld::TriggerApdu(JcopOs_ImageInfo_t* pVersionInfo, tJBL_STATUS status, JcopOs_TranscieveInfo_t* pTranscv_Info)
{
static const char fn [] = "JcopOsDwnld::TriggerApdu";
bool stat = false;
IChannel_t *mchannel = gpJcopOs_Dwnld_Context->channel;
INT32 recvBufferActualSize = 0;
ALOGD("%s: enter;", fn);
if(pTranscv_Info == NULL ||
pVersionInfo == NULL)
{
ALOGD("%s: Invalid parameter", fn);
status = STATUS_FAILED;
}
else
{
pTranscv_Info->timeout = gTransceiveTimeout;
pTranscv_Info->sSendlength = (INT32)sizeof(Trigger_APDU);
pTranscv_Info->sRecvlength = 1024;//(INT32)sizeof(INT32);
memcpy(pTranscv_Info->sSendData, Trigger_APDU, pTranscv_Info->sSendlength);
ALOGD("%s: Calling Secure Element Transceive", fn);
stat = mchannel->transceive (pTranscv_Info->sSendData,
pTranscv_Info->sSendlength,
pTranscv_Info->sRecvData,
pTranscv_Info->sRecvlength,
recvBufferActualSize,
pTranscv_Info->timeout);
if (stat != true)
{
status = STATUS_FAILED;
ALOGE("%s: SE transceive failed status = 0x%X", fn, status);//Stop JcopOs Update
}
else if(((pTranscv_Info->sRecvData[recvBufferActualSize-2] == 0x68) &&
(pTranscv_Info->sRecvData[recvBufferActualSize-1] == 0x81))||
((pTranscv_Info->sRecvData[recvBufferActualSize-2] == 0x90) &&
(pTranscv_Info->sRecvData[recvBufferActualSize-1] == 0x00))||
((pTranscv_Info->sRecvData[recvBufferActualSize-2] == 0x6F) &&
(pTranscv_Info->sRecvData[recvBufferActualSize-1] == 0x00)))
{
mchannel->doeSE_JcopDownLoadReset();
status = STATUS_OK;
ALOGD("%s: Trigger APDU Transceive status = 0x%X", fn, status);
}
else
{
/* status {90, 00} */
status = STATUS_OK;
}
}
ALOGD("%s: exit; status = 0x%X", fn, status);
return status;
}
/*******************************************************************************
**
** Function: GetInfo
**
** Description: Get the JCOP OS info
**
** Returns: Success if ok.
**
*******************************************************************************/
tJBL_STATUS JcopOsDwnld::GetInfo(JcopOs_ImageInfo_t* pImageInfo, tJBL_STATUS status, JcopOs_TranscieveInfo_t* pTranscv_Info)
{
static const char fn [] = "JcopOsDwnld::GetInfo";
bool stat = false;
IChannel_t *mchannel = gpJcopOs_Dwnld_Context->channel;
INT32 recvBufferActualSize = 0;
ALOGD("%s: enter;", fn);
if(pTranscv_Info == NULL ||
pImageInfo == NULL)
{
ALOGD("%s: Invalid parameter", fn);
status = STATUS_FAILED;
}
else
{
memcpy(pImageInfo->fls_path, (char *)path[pImageInfo->index], strlen(path[pImageInfo->index]));
memset(pTranscv_Info->sSendData, 0, JCOP_MAX_BUF_SIZE);
pTranscv_Info->timeout = gTransceiveTimeout;
pTranscv_Info->sSendlength = (UINT32)sizeof(GetInfo_APDU);
pTranscv_Info->sRecvlength = 1024;
memcpy(pTranscv_Info->sSendData, GetInfo_APDU, pTranscv_Info->sSendlength);
ALOGD("%s: Calling Secure Element Transceive", fn);
stat = mchannel->transceive (pTranscv_Info->sSendData,
pTranscv_Info->sSendlength,
pTranscv_Info->sRecvData,
pTranscv_Info->sRecvlength,
recvBufferActualSize,
pTranscv_Info->timeout);
if (stat != true)
{
status = STATUS_FAILED;
pImageInfo->index =0;
ALOGE("%s: SE transceive failed status = 0x%X", fn, status);//Stop JcopOs Update
}
else if((pTranscv_Info->sRecvData[recvBufferActualSize-2] == 0x90) &&
(pTranscv_Info->sRecvData[recvBufferActualSize-1] == 0x00))
{
pImageInfo->version_info.osid = pTranscv_Info->sRecvData[recvBufferActualSize-6];
pImageInfo->version_info.ver1 = pTranscv_Info->sRecvData[recvBufferActualSize-5];
pImageInfo->version_info.ver0 = pTranscv_Info->sRecvData[recvBufferActualSize-4];
pImageInfo->version_info.OtherValid = pTranscv_Info->sRecvData[recvBufferActualSize-3];
#if 0
if((pImageInfo->index != 0) &&
(pImageInfo->version_info.osid == 0x01) &&
(pImageInfo->version_info.OtherValid == 0x11))
{
ALOGE("3-Step update is not required");
memset(pImageInfo->fls_path,0,sizeof(pImageInfo->fls_path));
pImageInfo->index=0;
}
else
#endif
{
ALOGE("Starting 3-Step update");
memcpy(pImageInfo->fls_path, (char *)path[pImageInfo->index], sizeof(path[pImageInfo->index]));
pImageInfo->index++;
}
status = STATUS_OK;
ALOGD("%s: GetInfo Transceive status = 0x%X", fn, status);
}
else if((pTranscv_Info->sRecvData[recvBufferActualSize-2] == 0x6A) &&
(pTranscv_Info->sRecvData[recvBufferActualSize-1] == 0x82) &&
pImageInfo->version_info.ver_status == STATUS_UPTO_DATE)
{
status = STATUS_UPTO_DATE;
}
else
{
status = STATUS_FAILED;
ALOGD("%s; Invalid response for GetInfo", fn);
}
}
if (status == STATUS_FAILED)
{
ALOGD("%s; status failed, doing reset...", fn);
mchannel->doeSE_JcopDownLoadReset();
}
ALOGD("%s: exit; status = 0x%X", fn, status);
return status;
}
/*******************************************************************************
**
** Function: load_JcopOS_image
**
** Description: Used to update the JCOP OS
** Get Info function has to be called before this
**
** Returns: Success if ok.
**
*******************************************************************************/
tJBL_STATUS JcopOsDwnld::load_JcopOS_image(JcopOs_ImageInfo_t *Os_info, tJBL_STATUS status, JcopOs_TranscieveInfo_t *pTranscv_Info)
{
static const char fn [] = "JcopOsDwnld::load_JcopOS_image";
bool stat = false;
int wResult, size =0;
INT32 wIndex,wCount=0;
INT32 wLen;
IChannel_t *mchannel = gpJcopOs_Dwnld_Context->channel;
INT32 recvBufferActualSize = 0;
ALOGD("%s: enter", fn);
if(Os_info == NULL ||
pTranscv_Info == NULL)
{
ALOGE("%s: invalid parameter", fn);
return status;
}
Os_info->fp = fopen(Os_info->fls_path, "r+");
if (Os_info->fp == NULL) {
ALOGE("Error opening OS image file <%s> for reading: %s",
Os_info->fls_path, strerror(errno));
return STATUS_FILE_NOT_FOUND;
}
wResult = fseek(Os_info->fp, 0L, SEEK_END);
if (wResult) {
ALOGE("Error seeking end OS image file %s", strerror(errno));
goto exit;
}
Os_info->fls_size = ftell(Os_info->fp);
if (Os_info->fls_size < 0) {
ALOGE("Error ftelling file %s", strerror(errno));
goto exit;
}
wResult = fseek(Os_info->fp, 0L, SEEK_SET);
if (wResult) {
ALOGE("Error seeking start image file %s", strerror(errno));
goto exit;
}
while(!feof(Os_info->fp))
{
ALOGE("%s; Start of line processing", fn);
wIndex=0;
wLen=0;
wCount=0;
memset(pTranscv_Info->sSendData,0x00,JCOP_MAX_BUF_SIZE);
pTranscv_Info->sSendlength=0;
ALOGE("%s; wIndex = 0", fn);
for(wCount =0; (wCount < 5 && !feof(Os_info->fp)); wCount++, wIndex++)
{
wResult = FSCANF_BYTE(Os_info->fp,"%2X",&pTranscv_Info->sSendData[wIndex]);
}
if(wResult != 0)
{
wLen = pTranscv_Info->sSendData[4];
ALOGE("%s; Read 5byes success & len=%d", fn,wLen);
if(wLen == 0x00)
{
ALOGE("%s: Extended APDU", fn);
wResult = FSCANF_BYTE(Os_info->fp,"%2X",&pTranscv_Info->sSendData[wIndex++]);
wResult = FSCANF_BYTE(Os_info->fp,"%2X",&pTranscv_Info->sSendData[wIndex++]);
wLen = ((pTranscv_Info->sSendData[5] << 8) | (pTranscv_Info->sSendData[6]));
}
for(wCount =0; (wCount < wLen && !feof(Os_info->fp)); wCount++, wIndex++)
{
wResult = FSCANF_BYTE(Os_info->fp,"%2X",&pTranscv_Info->sSendData[wIndex]);
}
}
else
{
ALOGE("%s: JcopOs image Read failed", fn);
goto exit;
}
pTranscv_Info->sSendlength = wIndex;
ALOGE("%s: start transceive for length %d", fn, pTranscv_Info->sSendlength);
if((pTranscv_Info->sSendlength != 0x03) &&
(pTranscv_Info->sSendData[0] != 0x00) &&
(pTranscv_Info->sSendData[1] != 0x00))
{
stat = mchannel->transceive(pTranscv_Info->sSendData,
pTranscv_Info->sSendlength,
pTranscv_Info->sRecvData,
pTranscv_Info->sRecvlength,
recvBufferActualSize,
pTranscv_Info->timeout);
}
else
{
ALOGE("%s: Invalid packet", fn);
continue;
}
if(stat != true)
{
ALOGE("%s: Transceive failed; status=0x%X", fn, stat);
status = STATUS_FAILED;
goto exit;
}
else if(recvBufferActualSize != 0 &&
pTranscv_Info->sRecvData[recvBufferActualSize-2] == 0x90 &&
pTranscv_Info->sRecvData[recvBufferActualSize-1] == 0x00)
{
//ALOGE("%s: END transceive for length %d", fn, pTranscv_Info->sSendlength);
status = STATUS_SUCCESS;
}
else if(pTranscv_Info->sRecvData[recvBufferActualSize-2] == 0x6F &&
pTranscv_Info->sRecvData[recvBufferActualSize-1] == 0x00)
{
ALOGE("%s: JcopOs is already upto date-No update required exiting", fn);
Os_info->version_info.ver_status = STATUS_UPTO_DATE;
status = STATUS_FAILED;
break;
}
else
{
status = STATUS_FAILED;
ALOGE("%s: Invalid response", fn);
goto exit;
}
ALOGE("%s: Going for next line", fn);
}
if(status == STATUS_SUCCESS)
{
Os_info->cur_state++;
SetJcopOsState(Os_info, Os_info->cur_state);
}
exit:
mchannel->doeSE_JcopDownLoadReset();
ALOGE("%s close fp and exit; status= 0x%X", fn,status);
wResult = fclose(Os_info->fp);
return status;
}
/*******************************************************************************
**
** Function: GetJcopOsState
**
** Description: Used to update the JCOP OS state
**
** Returns: Success if ok.
**
*******************************************************************************/
tJBL_STATUS JcopOsDwnld::GetJcopOsState(JcopOs_ImageInfo_t *Os_info, UINT8 *counter)
{
static const char fn [] = "JcopOsDwnld::GetJcopOsState";
tJBL_STATUS status = STATUS_SUCCESS;
FILE *fp;
UINT8 xx=0;
ALOGD("%s: enter", fn);
if(Os_info == NULL)
{
ALOGE("%s: invalid parameter", fn);
return STATUS_FAILED;
}
fp = fopen(JCOP_INFO_PATH, "r");
if (fp == NULL) {
ALOGE("file <%s> not exits for reading- creating new file: %s",
JCOP_INFO_PATH, strerror(errno));
fp = fopen(JCOP_INFO_PATH, "w+");
if (fp == NULL)
{
ALOGE("Error opening OS image file <%s> for reading: %s",
JCOP_INFO_PATH, strerror(errno));
return STATUS_FAILED;
}
fprintf(fp, "%u", xx);
fclose(fp);
}
else
{
FSCANF_BYTE(fp, "%u", &xx);
ALOGE("JcopOsState %d", xx);
fclose(fp);
}
switch(xx)
{
case JCOP_UPDATE_STATE0:
case JCOP_UPDATE_STATE3:
ALOGE("Starting update from step1");
Os_info->index = JCOP_UPDATE_STATE0;
Os_info->cur_state = JCOP_UPDATE_STATE0;
*counter = 0;
break;
case JCOP_UPDATE_STATE1:
ALOGE("Starting update from step2");
Os_info->index = JCOP_UPDATE_STATE1;
Os_info->cur_state = JCOP_UPDATE_STATE1;
*counter = 3;
break;
case JCOP_UPDATE_STATE2:
ALOGE("Starting update from step3");
Os_info->index = JCOP_UPDATE_STATE2;
Os_info->cur_state = JCOP_UPDATE_STATE2;
*counter = 5;
break;
default:
ALOGE("invalid state");
status = STATUS_FAILED;
break;
}
return status;
}
/*******************************************************************************
**
** Function: SetJcopOsState
**
** Description: Used to set the JCOP OS state
**
** Returns: Success if ok.
**
*******************************************************************************/
tJBL_STATUS JcopOsDwnld::SetJcopOsState(JcopOs_ImageInfo_t *Os_info, UINT8 state)
{
static const char fn [] = "JcopOsDwnld::SetJcopOsState";
tJBL_STATUS status = STATUS_FAILED;
FILE *fp;
ALOGD("%s: enter", fn);
if(Os_info == NULL)
{
ALOGE("%s: invalid parameter", fn);
return status;
}
fp = fopen(JCOP_INFO_PATH, "w");
if (fp == NULL) {
ALOGE("Error opening OS image file <%s> for reading: %s",
JCOP_INFO_PATH, strerror(errno));
}
else
{
fprintf(fp, "%u", state);
fflush(fp);
ALOGE("Current JcopOsState: %d", state);
status = STATUS_SUCCESS;
int fd=fileno(fp);
int ret = fdatasync(fd);
ALOGE("ret value: %d", ret);
fclose(fp);
}
return status;
}
#if 0
void *JcopOsDwnld::GetMemory(UINT32 size)
{
void *pMem;
static const char fn [] = "JcopOsDwnld::GetMemory";
pMem = (void *)malloc(size);
if(pMem != NULL)
{
memset(pMem, 0, size);
}
else
{
ALOGD("%s: memory allocation failed", fn);
}
return pMem;
}
void JcopOsDwnld::FreeMemory(void *pMem)
{
if(pMem != NULL)
{
free(pMem);
pMem = NULL;
}
}
#endif