static int mmc_read_mbr (const char *device, MmcPartition *mbr) { FILE *fd; unsigned char buffer[512]; int idx, i; unsigned mmc_partition_count = 0; unsigned int dtype; unsigned int dfirstsec; unsigned int EBR_first_sec; unsigned int EBR_current_sec; int ret = -1; fd = fopen(device, "r"); if(fd == NULL) { printf("Can't open device: \"%s\"\n", device); goto ERROR2; } if ((fread(buffer, 512, 1, fd)) != 1) { printf("Can't read device: \"%s\"\n", device); goto ERROR1; } /* Check to see if signature exists */ if ((buffer[TABLE_SIGNATURE] != 0x55) || \ (buffer[TABLE_SIGNATURE + 1] != 0xAA)) { printf("Incorrect mbr signatures!\n"); goto ERROR1; } idx = TABLE_ENTRY_0; for (i = 0; i < 4; i++) { char device_index[128]; mbr[mmc_partition_count].dstatus = \ buffer[idx + i * TABLE_ENTRY_SIZE + OFFSET_STATUS]; mbr[mmc_partition_count].dtype = \ buffer[idx + i * TABLE_ENTRY_SIZE + OFFSET_TYPE]; mbr[mmc_partition_count].dfirstsec = \ GET_LWORD_FROM_BYTE(&buffer[idx + \ i * TABLE_ENTRY_SIZE + \ OFFSET_FIRST_SEC]); mbr[mmc_partition_count].dsize = \ GET_LWORD_FROM_BYTE(&buffer[idx + \ i * TABLE_ENTRY_SIZE + \ OFFSET_SIZE]); dtype = mbr[mmc_partition_count].dtype; dfirstsec = mbr[mmc_partition_count].dfirstsec; mmc_partition_name(&mbr[mmc_partition_count], \ mbr[mmc_partition_count].dtype); sprintf(device_index, "%sp%d", device, (mmc_partition_count+1)); mbr[mmc_partition_count].device_index = strdup(device_index); mmc_partition_count++; if (mmc_partition_count == MAX_PARTITIONS) goto SUCCESS; } /* See if the last partition is EBR, if not, parsing is done */ if (dtype != 0x05) { goto SUCCESS; } EBR_first_sec = dfirstsec; EBR_current_sec = dfirstsec; fseek (fd, (EBR_first_sec * 512), SEEK_SET); if ((fread(buffer, 512, 1, fd)) != 1) goto ERROR1; /* Loop to parse the EBR */ for (i = 0;; i++) { char device_index[128]; if ((buffer[TABLE_SIGNATURE] != 0x55) || (buffer[TABLE_SIGNATURE + 1] != 0xAA)) { break; } mbr[mmc_partition_count].dstatus = \ buffer[TABLE_ENTRY_0 + OFFSET_STATUS]; mbr[mmc_partition_count].dtype = \ buffer[TABLE_ENTRY_0 + OFFSET_TYPE]; mbr[mmc_partition_count].dfirstsec = \ GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_0 + \ OFFSET_FIRST_SEC]) + \ EBR_current_sec; mbr[mmc_partition_count].dsize = \ GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_0 + \ OFFSET_SIZE]); mmc_partition_name(&mbr[mmc_partition_count], \ mbr[mmc_partition_count].dtype); sprintf(device_index, "%sp%d", device, (mmc_partition_count+1)); mbr[mmc_partition_count].device_index = strdup(device_index); mmc_partition_count++; if (mmc_partition_count == MAX_PARTITIONS) goto SUCCESS; dfirstsec = GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_1 + OFFSET_FIRST_SEC]); if(dfirstsec == 0) { /* Getting to the end of the EBR tables */ break; } /* More EBR to follow - read in the next EBR sector */ fseek (fd, ((EBR_first_sec + dfirstsec) * 512), SEEK_SET); if ((fread(buffer, 512, 1, fd)) != 1) goto ERROR1; EBR_current_sec = EBR_first_sec + dfirstsec; } SUCCESS: ret = mmc_partition_count; ERROR1: fclose(fd); ERROR2: return ret; }
static unsigned int emmc_boot_read_MBR() { unsigned char buffer[512]; unsigned int dtype; unsigned int dfirstsec; unsigned int EBR_first_sec; unsigned int EBR_current_sec; int ret = 0; int idx, i; /* Read Master Boot Record from eMMC */ ret = BOOTSD_Read(SD_BUS_GetBootSlotIndex() , 0 , 1, buffer); if(ret) { dprintf(INFO , "Master Boot Record Read Fail on eMMC\n"); return ret; } /* check to see if signature exists */ if((buffer[TABLE_SIGNATURE] != 0x55) || (buffer[TABLE_SIGNATURE+1] !=0xAA)) { dprintf(CRITICAL , "MBR Signature does not match \n"); return ret; } /* Search 4 Primary Partitions */ idx = TABLE_ENTRY_0; for(i=0; i<4; i++) { mbr[mmc_partition_count].dstatus = buffer[idx+i*TABLE_ENTRY_SIZE + OFFSET_STATUS]; mbr[mmc_partition_count].dtype = buffer[idx+i*TABLE_ENTRY_SIZE + OFFSET_TYPE]; mbr[mmc_partition_count].dfirstsec = GET_LWORD_FROM_BYTE(&buffer[idx+i*TABLE_ENTRY_SIZE + OFFSET_FIRST_SEC]); mbr[mmc_partition_count].dsize = GET_LWORD_FROM_BYTE(&buffer[idx+i*TABLE_ENTRY_SIZE + OFFSET_SIZE]); dtype = mbr[mmc_partition_count].dtype; dfirstsec = mbr[mmc_partition_count].dfirstsec; mbr_set_partition_name(&mbr[mmc_partition_count], mbr[mmc_partition_count].dtype); mmc_partition_count++; if (mmc_partition_count == MAX_PARTITIONS) return ret; } EBR_first_sec = dfirstsec; EBR_current_sec = dfirstsec; ret = BOOTSD_Read(SD_BUS_GetBootSlotIndex(), EBR_first_sec , 1, buffer); if(ret) return ret; /* Search for Extended partion Extended Boot Record*/ for (i = 0;; i++) { /* check to see if signature exists */ if((buffer[TABLE_SIGNATURE] != 0x55) || (buffer[TABLE_SIGNATURE+1] !=0xAA)) { dprintf(CRITICAL , "Extended Partition MBR Signature does not match \n"); break; } mbr[mmc_partition_count].dstatus = buffer[TABLE_ENTRY_0 + OFFSET_STATUS]; mbr[mmc_partition_count].dtype = buffer[TABLE_ENTRY_0 + OFFSET_TYPE]; mbr[mmc_partition_count].dfirstsec = GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_0 + OFFSET_FIRST_SEC])+EBR_current_sec; mbr[mmc_partition_count].dsize = GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_0 + OFFSET_SIZE]); mbr_set_partition_name(&mbr[mmc_partition_count], mbr[mmc_partition_count].dtype); mmc_partition_count++; if (mmc_partition_count == MAX_PARTITIONS) return ret; dfirstsec = GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_1 + OFFSET_FIRST_SEC]); if(dfirstsec == 0) { /* Getting to the end of the EBR tables */ break; } /* More EBR to follow - read in the next EBR sector */ ret = BOOTSD_Read(SD_BUS_GetBootSlotIndex(), EBR_first_sec+dfirstsec , 1 , buffer); if (ret) { return ret; } EBR_current_sec = EBR_first_sec + dfirstsec; } return ret; }
/* MOD 0014570: [FACTORY RESET] change system call to filp function for handling the flag */ int lge_mmc_read_mbr (MmcPartition *mbr) { //int fd; unsigned char *buffer = NULL; char *device_index = NULL; int idx, i; unsigned mmc_partition_count = 0; unsigned int dtype; unsigned int dfirstsec; unsigned int EBR_first_sec; unsigned int EBR_current_sec; int ret = -1; struct file *phMscd_Filp = NULL; mm_segment_t old_fs; old_fs=get_fs(); set_fs(get_ds()); buffer = kmalloc(512, GFP_KERNEL); device_index = kmalloc(128, GFP_KERNEL); if((buffer == NULL) || (device_index == NULL)) { printk("%s, allocation failed\n", __func__); goto ERROR2; } // change from sys operation to flip operation, do not use system call since this routine is also system call service. phMscd_Filp = filp_open(MMC_DEVICENAME, O_RDONLY, 0); if (IS_ERR(phMscd_Filp)) { printk(KERN_ERR "%s, Can't open %s\n", __func__, MMC_DEVICENAME); goto ERROR2; } if( !phMscd_Filp ) { printk(KERN_ERR "%s, Can't open device\n", __func__ ); goto ERROR2; } phMscd_Filp->f_pos = (loff_t)0; if (phMscd_Filp->f_op->read(phMscd_Filp, buffer, 512, &phMscd_Filp->f_pos) != 512) { printk(KERN_ERR "%s, Can't read device: \"%s\"\n", __func__, MMC_DEVICENAME); goto ERROR1; } /* Check to see if signature exists */ if ((buffer[TABLE_SIGNATURE] != 0x55) || \ (buffer[TABLE_SIGNATURE + 1] != 0xAA)) { printk(KERN_ERR "Incorrect mbr signatures!\n"); goto ERROR1; } idx = TABLE_ENTRY_0; for (i = 0; i < 4; i++) { //char device_index[128]; mbr[mmc_partition_count].dstatus = \ buffer[idx + i * TABLE_ENTRY_SIZE + OFFSET_STATUS]; mbr[mmc_partition_count].dtype = \ buffer[idx + i * TABLE_ENTRY_SIZE + OFFSET_TYPE]; mbr[mmc_partition_count].dfirstsec = \ GET_LWORD_FROM_BYTE(&buffer[idx + \ i * TABLE_ENTRY_SIZE + \ OFFSET_FIRST_SEC]); mbr[mmc_partition_count].dsize = \ GET_LWORD_FROM_BYTE(&buffer[idx + \ i * TABLE_ENTRY_SIZE + \ OFFSET_SIZE]); dtype = mbr[mmc_partition_count].dtype; dfirstsec = mbr[mmc_partition_count].dfirstsec; lge_mmc_partition_name(&mbr[mmc_partition_count], \ mbr[mmc_partition_count].dtype); sprintf(device_index, "%sp%d", MMC_DEVICENAME, (mmc_partition_count+1)); mbr[mmc_partition_count].device_index = lge_strdup(device_index); mmc_partition_count++; if (mmc_partition_count == MAX_PARTITIONS) goto SUCCESS; } /* See if the last partition is EBR, if not, parsing is done */ if (dtype != 0x05) { goto SUCCESS; } EBR_first_sec = dfirstsec; EBR_current_sec = dfirstsec; phMscd_Filp->f_pos = (loff_t)(EBR_first_sec * 512); if (phMscd_Filp->f_op->read(phMscd_Filp, buffer, 512, &phMscd_Filp->f_pos) != 512) { printk(KERN_ERR "%s, Can't read device: \"%s\"\n", __func__, MMC_DEVICENAME); goto ERROR1; } /* Loop to parse the EBR */ for (i = 0;; i++) { if ((buffer[TABLE_SIGNATURE] != 0x55) || (buffer[TABLE_SIGNATURE + 1] != 0xAA)) { break; } mbr[mmc_partition_count].dstatus = \ buffer[TABLE_ENTRY_0 + OFFSET_STATUS]; mbr[mmc_partition_count].dtype = \ buffer[TABLE_ENTRY_0 + OFFSET_TYPE]; mbr[mmc_partition_count].dfirstsec = \ GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_0 + \ OFFSET_FIRST_SEC]) + \ EBR_current_sec; mbr[mmc_partition_count].dsize = \ GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_0 + \ OFFSET_SIZE]); lge_mmc_partition_name(&mbr[mmc_partition_count], \ mbr[mmc_partition_count].dtype); sprintf(device_index, "%sp%d", MMC_DEVICENAME, (mmc_partition_count+1)); mbr[mmc_partition_count].device_index = lge_strdup(device_index); mmc_partition_count++; if (mmc_partition_count == MAX_PARTITIONS) goto SUCCESS; dfirstsec = GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_1 + OFFSET_FIRST_SEC]); if(dfirstsec == 0) { /* Getting to the end of the EBR tables */ break; } /* More EBR to follow - read in the next EBR sector */ phMscd_Filp->f_pos = (loff_t)((EBR_first_sec + dfirstsec) * 512); if (phMscd_Filp->f_op->read(phMscd_Filp, buffer, 512, &phMscd_Filp->f_pos) != 512) { printk(KERN_ERR "%s, Can't read device: \"%s\"\n", __func__, MMC_DEVICENAME); goto ERROR1; } EBR_current_sec = EBR_first_sec + dfirstsec; } SUCCESS: ret = mmc_partition_count; ERROR1: if(phMscd_Filp != NULL) filp_close(phMscd_Filp,NULL); ERROR2: set_fs(old_fs); if(buffer != NULL) kfree(buffer); if(device_index != NULL) kfree(device_index); return ret; }
int lge_mmc_read_gpt (MmcPartition *mbr) { //int fd; unsigned char *buffer = NULL; char *device_index = NULL; int idx, i; unsigned mmc_partition_count = 0; unsigned int dtype; unsigned int dfirstsec; // unsigned int EBR_first_sec; //unsigned int EBR_current_sec; int ret = -1; struct file *phMscd_Filp = NULL; mm_segment_t old_fs; old_fs=get_fs(); set_fs(get_ds()); buffer = kmalloc(1024, GFP_KERNEL); device_index = kmalloc(128, GFP_KERNEL); if((buffer == NULL) || (device_index == NULL)) { printk("%s, allocation failed\n", __func__); goto ERROR2; } // change from sys operation to flip operation, do not use system call since this routine is also system call service. phMscd_Filp = filp_open(MMC_DEVICENAME, O_RDONLY, 0); if( !phMscd_Filp) { printk(KERN_ERR "%s, Can't open device\n", __func__ ); goto ERROR2; } phMscd_Filp->f_pos = (loff_t)0; if (phMscd_Filp->f_op->read(phMscd_Filp, buffer, 1024, &phMscd_Filp->f_pos) != 1024) { printk(KERN_ERR "%s, Can't read device: \"%s\"\n", __func__, MMC_DEVICENAME); goto ERROR1; } /* Check to see if signature exists */ if ((buffer[TABLE_SIGNATURE] != 0x55) || \ (buffer[TABLE_SIGNATURE + 1] != 0xAA)) { printk(KERN_ERR "Incorrect mbr signatures!\n"); goto ERROR1; } idx = TABLE_ENTRY_0; // for (i = 0; i < 4; i++) for (i = 0; i < MAX_PARTITIONS; i++) { //char device_index[128]; mbr[mmc_partition_count].dstatus = \ buffer[idx + i * GPT_ENTRY_SIZE+ OFFSET_STATUS]; mbr[mmc_partition_count].dtype = \ buffer[idx + i * GPT_ENTRY_SIZE + OFFSET_TYPE]; mbr[mmc_partition_count].dfirstsec = \ GET_LWORD_FROM_BYTE(&buffer[idx + \ i * GPT_ENTRY_SIZE + \ GPT_OFF_FIRST_SEC]); mbr[mmc_partition_count].dsize = \ GET_LWORD_FROM_BYTE(&buffer[idx + \ i * GPT_ENTRY_SIZE + \ OFFSET_SIZE]); dtype = mbr[mmc_partition_count].dtype; dfirstsec = mbr[mmc_partition_count].dfirstsec; sprintf(device_index, "%sp%d", MMC_DEVICENAME, (mmc_partition_count+1)); mbr[mmc_partition_count].device_index = lge_strdup(device_index); mmc_partition_count++; if (mmc_partition_count == MAX_PARTITIONS) goto SUCCESS; } /* See if the last partition is EBR, if not, parsing is done */ if (dtype != 0x05) { goto SUCCESS; } SUCCESS: ret = mmc_partition_count; printk(KERN_INFO "mmc_partition succes!!"); ERROR1: if(phMscd_Filp != NULL) filp_close(phMscd_Filp,NULL); ERROR2: set_fs(old_fs); if(buffer != NULL) kfree(buffer); if(device_index != NULL) kfree(device_index); return ret; }
/* * Read MBR from MMC card and fill partition table. */ unsigned int mmc_boot_read_mbr( struct mmc_boot_host * mmc_host, struct mmc_boot_card * mmc_card) { unsigned char buffer[MMC_BOOT_RD_BLOCK_LEN]; unsigned int dtype; unsigned int dfirstsec; unsigned int EBR_first_sec; unsigned int EBR_current_sec; int ret = MMC_BOOT_E_SUCCESS; int idx, i; /* Print out the MBR first */ ret = mmc_boot_read_from_card( mmc_host, mmc_card, 0, \ MMC_BOOT_RD_BLOCK_LEN, \ (unsigned int *)buffer); if (ret) { dprintf(CRITICAL, "Could not read partition from mmc"); return ret; } /* Check to see if signature exists */ ret = partition_verify_mbr_signature(MMC_BOOT_RD_BLOCK_LEN, buffer); if (ret) { return ret; } /* * Process each of the four partitions in the MBR by reading the table * information into our mbr table. */ partition_count = 0; idx = TABLE_ENTRY_0; for (i = 0; i < 4; i++) { /* Type 0xEE indicates end of MBR and GPT partitions exist */ dtype = buffer[idx + i * TABLE_ENTRY_SIZE + OFFSET_TYPE]; if (dtype == MBR_PROTECTED_TYPE){ gpt_partitions_exist = 1; return ret; } partition_entries[partition_count].dtype = dtype; partition_entries[partition_count].attribute_flag = \ buffer[idx + i * TABLE_ENTRY_SIZE + OFFSET_STATUS]; partition_entries[partition_count].first_lba = \ GET_LWORD_FROM_BYTE(&buffer[idx + \ i * TABLE_ENTRY_SIZE + \ OFFSET_FIRST_SEC]); partition_entries[partition_count].size = \ GET_LWORD_FROM_BYTE(&buffer[idx + \ i * TABLE_ENTRY_SIZE + \ OFFSET_SIZE]); dfirstsec = partition_entries[partition_count].first_lba; mbr_fill_name(&partition_entries[partition_count], \ partition_entries[partition_count].dtype); partition_count++; if (partition_count == NUM_PARTITIONS) return ret; } /* See if the last partition is EBR, if not, parsing is done */ if (dtype != MBR_EBR_TYPE) { return ret; } EBR_first_sec = dfirstsec; EBR_current_sec = dfirstsec; ret = mmc_boot_read_from_card( mmc_host, mmc_card, \ (EBR_first_sec * 512), \ MMC_BOOT_RD_BLOCK_LEN, \ (unsigned int *)buffer); if (ret) { return ret; } /* Loop to parse the EBR */ for (i = 0;; i++) { ret = partition_verify_mbr_signature(MMC_BOOT_RD_BLOCK_LEN, buffer); if (ret) { ret = MMC_BOOT_E_SUCCESS; break; } partition_entries[partition_count].attribute_flag = \ buffer[TABLE_ENTRY_0 + OFFSET_STATUS]; partition_entries[partition_count].dtype = \ buffer[TABLE_ENTRY_0 + OFFSET_TYPE]; partition_entries[partition_count].first_lba = \ GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_0 + \ OFFSET_FIRST_SEC]) + \ EBR_current_sec; partition_entries[partition_count].size = \ GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_0 + \ OFFSET_SIZE]); mbr_fill_name(&(partition_entries[partition_count]), \ partition_entries[partition_count].dtype); partition_count++; if (partition_count == NUM_PARTITIONS) return ret; dfirstsec = GET_LWORD_FROM_BYTE(&buffer[TABLE_ENTRY_1 + OFFSET_FIRST_SEC]); if(dfirstsec == 0) { /* Getting to the end of the EBR tables */ break; } /* More EBR to follow - read in the next EBR sector */ #ifndef DISABLE_MMC_DEBUG_SPEW dprintf(SPEW, "Reading EBR block from 0x%X\n", EBR_first_sec + dfirstsec); #endif ret = mmc_boot_read_from_card( mmc_host, mmc_card, \ ((EBR_first_sec + dfirstsec) * 512), \ MMC_BOOT_RD_BLOCK_LEN, \ (unsigned int *)buffer); if (ret) { return ret; } EBR_current_sec = EBR_first_sec + dfirstsec; } return ret; }
/* * Read GPT from MMC and fill partition table */ unsigned int mmc_boot_read_gpt( struct mmc_boot_host * mmc_host, struct mmc_boot_card * mmc_card) { int ret = MMC_BOOT_E_SUCCESS; unsigned int header_size; unsigned long long first_usable_lba; unsigned int max_partition_count; unsigned int partition_entry_size; unsigned char data[MMC_BOOT_RD_BLOCK_LEN]; unsigned int i = 0; /* Counter for each 512 block */ unsigned int j = 0; /* Counter for each 128 entry in the 512 block */ unsigned int n = 0; /* Counter for UTF-16 -> 8 conversion */ unsigned char UTF16_name[MAX_GPT_NAME_SIZE]; /* Print out the GPT first */ ret = mmc_boot_read_from_card( mmc_host, mmc_card, \ PROTECTIVE_MBR_SIZE, \ MMC_BOOT_RD_BLOCK_LEN, \ (unsigned int *)data); /* Check GPT Signature */ if( ((uint32_t *)data)[0] != GPT_SIGNATURE_2 || ((uint32_t *)data)[1] != GPT_SIGNATURE_1 ) { dprintf(CRITICAL, "GPT: signature does not match.\n" ); return MMC_BOOT_E_FAILURE; } header_size = GET_LWORD_FROM_BYTE(&data[HEADER_SIZE_OFFSET]); first_usable_lba = GET_LLWORD_FROM_BYTE(&data[FIRST_USABLE_LBA_OFFSET]); max_partition_count = GET_LWORD_FROM_BYTE(&data[PARTITION_COUNT_OFFSET]); partition_entry_size = GET_LWORD_FROM_BYTE(&data[PENTRY_SIZE_OFFSET]); /* Read GPT Entries */ for(i = 0; i < (max_partition_count/4); i++) { ret = mmc_boot_read_from_card( mmc_host, mmc_card, PROTECTIVE_MBR_SIZE + PARTITION_TABLE_SIZE + (i * MMC_BOOT_RD_BLOCK_LEN), MMC_BOOT_RD_BLOCK_LEN, (uint32_t *)data); if (ret) { dprintf(CRITICAL, "GPT: mmc read card failed reading partition entries.\n" ); return ret; } for(j=0; j < 4; j++) { memcpy(&(partition_entries[partition_count].type_guid), &data[(j * partition_entry_size)], PARTITION_TYPE_GUID_SIZE); if (partition_entries[partition_count].type_guid[0] == 0x00 && partition_entries[partition_count].type_guid[1] == 0x00) { i = max_partition_count; break; } memcpy(&(partition_entries[partition_count].unique_partition_guid), &data[(j * partition_entry_size) + UNIQUE_GUID_OFFSET], UNIQUE_PARTITION_GUID_SIZE); partition_entries[partition_count].first_lba = GET_LLWORD_FROM_BYTE(&data[(j * partition_entry_size) + FIRST_LBA_OFFSET]); partition_entries[partition_count].last_lba = GET_LLWORD_FROM_BYTE(&data[(j * partition_entry_size) + LAST_LBA_OFFSET]); partition_entries[partition_count].size = partition_entries[partition_count].last_lba - partition_entries[partition_count].first_lba; partition_entries[partition_count].attribute_flag = GET_LLWORD_FROM_BYTE(&data[(j * partition_entry_size) + ATTRIBUTE_FLAG_OFFSET]); memset(&UTF16_name, 0x00, MAX_GPT_NAME_SIZE); memcpy(UTF16_name, &data[(j * partition_entry_size) + PARTITION_NAME_OFFSET], MAX_GPT_NAME_SIZE); /* * Currently partition names in *.xml are UTF-8 and lowercase * Only supporting english for now so removing 2nd byte of UTF-16 */ for(n = 0; n < MAX_GPT_NAME_SIZE/2; n++){ partition_entries[partition_count].name[n] = UTF16_name[n*2]; } partition_count++; } } return ret; }