1 /******************************************************************************/
2 /* src/tools/makedisk/makedisk.c */
4 /* Copyright (C) 2017 Mochi. */
5 /******************************************************************************/
6 /******************************************************************************/
8 /******************************************************************************/
19 #include <sys/types.h>
22 /******************************************************************************/
24 /******************************************************************************/
26 #define PT_STATUS_BOOT ( 0x80 ) /** ブート可能 */
27 #define PT_TYPE_MOCHI_BOOTER ( 0x30 ) /** MochiBooter用パーティション */
28 #define PT_TYPE_MOCHI_KERNEL ( 0x31 ) /** MochiKernel用パーティション */
31 #define OPTION_CYLINDER_DEFAULT ( 10 ) /** 引数シリンダ数デフォルト値 */
32 #define OPTION_HEAD_DEFAULT ( 16 ) /** 引数ヘッド数デフォルト値 */
33 #define OPTION_SECTOR_DEFAULT ( 63 ) /** 引数セクタ数デフォルト値 */
36 #define CYLINDER_MIN ( 0 ) /** シリンダ最小値 */
37 #define CYLINDER_MAX ( 1023 ) /** シリンダ最大値 */
40 #define HEAD_MIN ( 0 ) /** セクタ最小値 */
41 #define HEAD_MAX ( 254 ) /** セクタ最大値 */
44 #define SECTOR_MIN ( 1 ) /** セクタ最小値 */
45 #define SECTOR_MAX ( 63 ) /** セクタ最大値 */
48 #define BUFFER_SIZE ( 512 ) /** 読込みバッファサイズ */
51 #define ABORT( ... ) \
54 fprintf( stderr, __VA_ARGS__ ); \
55 fprintf( stderr, "\n" ); \
58 printUsage( EXIT_FAILURE ); \
62 #define SET_CYL_SEC( _CYLINDER, _SECTOR ) \
63 ( ( ( _CYLINDER & 0x0300 ) << 6 ) | \
64 ( ( _SECTOR & 0x003F ) << 8 ) | \
65 ( _CYLINDER & 0x00FF ) )
68 #define GET_CYLINDER( _CYLSEC ) \
69 ( ( ( _CYLSEC >> 6 ) & 0x0300 ) | ( _CYLSEC & 0x00FF ) )
72 #define GET_SECTOR( _CYLSEC ) ( ( _CYLSEC >> 8 ) & 0x3F )
76 uint16_t cylSec; /**< シリンダ&セクタ */
77 uint8_t head; /**< ヘッド */
78 } __attribute__( ( packed ) ) chs_t;
82 uint8_t status; /**< ステータス */
83 chs_t chsFirstAddr; /**< CHS先頭アドレス */
84 uint8_t type; /**< パーティションタイプ */
85 chs_t chsLastAddr; /**< CHS最後尾アドレス */
86 uint32_t lbaFirstAddr; /**< LBA先頭アドレス */
87 uint32_t lbaSize; /**< LBAサイズ */
88 } __attribute__( ( packed ) ) pt_t;
92 uint8_t code[ 446 ]; /**< ブートストラップコード */
93 pt_t partitionTbl[ 4 ]; /**< パーティションテーブル */
94 uint8_t signature[ 2 ]; /**< ブートシグネチャ */
95 } __attribute__( ( packed ) ) mbr_t;
98 /******************************************************************************/
100 /******************************************************************************/
102 static void checkOptions( int32_t argNum,
107 char **ppKernelPath );
109 static chs_t getChs( uint32_t lba );
112 static void printUsage( int status );
115 static void writeIpl( int diskFd,
119 static chs_t writeBoot( int diskFd,
121 chs_t chsFirstAddr );
124 static chs_t writeKernel( int diskFd,
126 chs_t chsFirstAddr );
129 static void writePartitionEntry( int diskFd,
134 /******************************************************************************/
136 /******************************************************************************/
137 uint32_t cylinder; /* 仮想ディスクのシリンダ数 */
138 uint32_t head; /* 仮想ディスクのヘッド数 */
139 uint32_t sector; /* 仮想ディスクのセクタ数 */
142 /******************************************************************************/
144 /******************************************************************************/
145 /******************************************************************************/
148 * @details 仮想マシン用ディスクイメージを作成する。
150 * param[in] argNum 引数の数
151 * param[in] *pArg[] 引数
153 * retval EXIT_SUCCESS 正常終了
154 * retval EXIT_FAILURE 異常終了
156 /******************************************************************************/
157 int main( int argNum,
160 int diskFd; /* 仮想ディスクファイルディスクリプタ */
161 char *pDiskPath; /* 仮想ディスクパス */
162 char *pIplPath; /* IPLバイナリパス */
163 char *pBootPath; /* ブートローダバイナリパス */
164 char *pKernelPath; /* カーネルバイナリパス */
165 char end; /* 仮想ディスク最終バイト */
166 chs_t chs; /* CHSアドレス */
167 off_t offset; /* オフセット */
168 int32_t size; /* 書込みサイズ */
172 memset( &chs, 0, sizeof ( chs_t ) );
175 checkOptions( argNum,
183 diskFd = open( pDiskPath,
184 O_RDWR | O_CREAT | O_TRUNC,
185 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH );
188 if ( diskFd == -1 ) {
192 ABORT( "ERROR(%04u): Can't open %s. errno=%d.\n",
199 writeIpl( diskFd, pIplPath );
202 chs.cylSec = SET_CYL_SEC( 0, 1 );
204 chs = writeBoot( diskFd, pBootPath, chs );
207 chs.cylSec = SET_CYL_SEC( GET_CYLINDER( chs.cylSec ) + 1, 1 );
209 chs = writeKernel( diskFd, pKernelPath, chs );
211 /* 仮想ディスクファイルサイズチェック */
212 if ( GET_CYLINDER( chs.cylSec ) < cylinder ) {
216 offset = lseek( diskFd, cylinder * head * sector * 512 - 1, SEEK_SET );
219 if ( ( int32_t ) offset != ( cylinder * head * sector * 512 - 1 ) ) {
223 ABORT( "ERROR(%04u): Can't seek at the disk image. ret=%d, errno=%d.\n",
230 size = ( int32_t ) write( diskFd, &end, 1 );
237 ABORT( "ERROR(%04u): Can't write %s. ret=%d, errno=%d.\n",
248 ABORT( "ERROR(%04u): Overwrite.CHS=%u/%u/%u > %u/%u/%u.\n",
250 GET_CYLINDER( chs.cylSec ),
252 GET_SECTOR( chs.cylSec ),
266 /******************************************************************************/
268 /******************************************************************************/
269 /******************************************************************************/
272 * @details オプションが許容可能な値かチェックする。
274 * @param[in] argNum 引数の数
275 * @param[in] *pArg[] 引数
276 * @param[out] *ppDiskPath 仮想ディスクのファイルパス
277 * @param[out] *ppIplPath IPLバイナリのファイルパス
278 * @param[out] *ppBootPath ブートローダバイナリのファイルパス
279 * @param[out] *ppKernelPath カーネルバイナリのファイルパス
281 /******************************************************************************/
282 static void checkOptions( int32_t argNum,
287 char **ppKernelPath )
289 char opt; /* オプション文字 */
292 *ppDiskPath = NULL; /* 仮想ディスクパス */
293 *ppIplPath = NULL; /* IPLバイナリパス */
294 *ppBootPath = NULL; /* ブートローダバイナリパス */
295 *ppKernelPath = NULL; /* カーネルバイナルパス */
296 cylinder = OPTION_CYLINDER_DEFAULT;/* デフォルトシリンダ */
297 head = OPTION_HEAD_DEFAULT; /* デフォルトヘッド */
298 sector = OPTION_SECTOR_DEFAULT; /* デフォルトセクタ */
300 /* オプションが無くなるまで繰り返し */
303 opt = getopt( argNum, pArg, "o:i:b:k:C:H:S:h" );
310 } else if ( opt == 'o' ) {
312 *ppDiskPath = optarg;
314 } else if ( opt == 'i' ) {
318 } else if ( opt == 'b' ) {
320 *ppBootPath = optarg;
322 } else if ( opt == 'k' ) {
324 *ppKernelPath = optarg;
326 } else if ( opt == 'C' ) {
328 cylinder = ( uint32_t ) atoi( optarg );
330 } else if ( opt == 'H' ) {
332 head = ( uint32_t ) atoi( optarg );
334 } else if ( opt == 'S' ) {
336 sector = ( uint32_t ) atoi( optarg );
338 } else if ( opt == 'h' ) {
342 printUsage( EXIT_SUCCESS );
348 ABORT( "ERROR(%04u): Unknown option!\n", __LINE__ );
353 if ( *ppDiskPath == NULL ) {
357 ABORT( "ERROR(%04u): No '-o' option!\n", __LINE__ );
360 /* IPLバイナリパス設定チェック */
361 if ( *ppIplPath == NULL ) {
365 ABORT( "ERROR(%04u): No '-i' option!\n", __LINE__ );
368 /* ブートローダバイナリパス設定チェック */
369 if ( *ppBootPath == NULL ) {
373 ABORT( "ERROR(%04u): No '-b' option!\n", __LINE__ );
376 /* カーネルバイナリパス設定チェック */
377 if ( *ppKernelPath == NULL ) {
381 ABORT( "ERROR(%04u): No '-k' option!\n", __LINE__ );
385 if ( !( ( CYLINDER_MIN <= cylinder ) &&
386 ( cylinder <= CYLINDER_MAX ) ) ) {
390 ABORT( "ERROR(%04u): cylinder(%u) is out of range(%u-%u).\n",
398 if ( !( ( HEAD_MIN <= head ) &&
399 ( head <= HEAD_MAX ) ) ) {
403 ABORT( "ERROR(%04u): Head(%u) is out of range(%u-%u).\n",
411 if ( !( ( SECTOR_MIN <= sector ) &&
412 ( sector <= SECTOR_MAX ) ) ) {
416 ABORT( "ERROR(%04u): Sector(%u) is out of range(%u-%u).\n",
427 /******************************************************************************/
430 * @details LBAアドレスをCHSアドレスに変換して取得する。
432 * @param[in] lba LBAアドレス
436 /******************************************************************************/
437 static chs_t getChs( uint32_t lba )
439 chs_t chs; /* CHSアドレス */
442 memset( &chs, 0, sizeof ( chs_t ) );
445 chs.cylSec = SET_CYL_SEC( ( lba / sector ) / head,
447 chs.head = ( lba / sector ) % head;
453 /******************************************************************************/
456 * @details USAGEを出力しプログラムを終了する。
458 * @param[in] status 終了ステータス
460 /******************************************************************************/
461 static void printUsage( int status )
464 fprintf( stderr, "USAGE: makedisk -o [FILE] -b [FILE] -k [FILE] [OPTION]...\n" );
465 fprintf( stderr, "\n" );
466 fprintf( stderr, "Option:\n" );
467 fprintf( stderr, " -o FILE specify the output FILE that is a disk image.\n" );
468 fprintf( stderr, " -i FILE specify the input FILE that is a IPL binary.\n" );
469 fprintf( stderr, " -b FILE specify the input FILE that is a boot loader binary.\n" );
470 fprintf( stderr, " -k FILE specify the input FILE that is a kernel binary.\n" );
471 fprintf( stderr, " -C NUMBER specify the NUMBER of cylinders.(default:%u)\n", OPTION_CYLINDER_DEFAULT );
472 fprintf( stderr, " -H NUMBER specify the NUMBER of heads.(default:%u)\n", OPTION_HEAD_DEFAULT );
473 fprintf( stderr, " -S NUMBER specify the NUMBER of sectors per track.(default:%u)\n", OPTION_SECTOR_DEFAULT );
474 fprintf( stderr, " -h print help.\n" );
481 /******************************************************************************/
484 * @details IPLバイナリを仮想ディスクに書き込む。
486 * @param[in] diskFd 仮想ディスクファイルディスクリプタ
487 * @param[in] *pIplPath IPLバイナリのファイルパス
489 /******************************************************************************/
490 static void writeIpl( int diskFd,
493 int iplFd; /* IPLバイナリファイルディスクリプタ */
494 mbr_t mbr; /* マスタブートレコード */
495 int32_t size; /* サイズ */
497 /* IPLバイナリファイルオープン */
498 iplFd = open( pIplPath, O_RDONLY );
505 ABORT( "ERROR(%04u): Can't open %s. errno=%d.\n",
512 size = ( int32_t ) read( iplFd, &mbr, sizeof ( mbr_t ) );
515 if ( size != ( sizeof ( mbr_t ) ) ) {
519 ABORT( "ERROR(%04u): Can't read from %s. ret=%d, errno=%d.\n",
526 /* IPLバイナリを仮想ディスクに書込み */
527 size = ( int32_t ) write( diskFd, &mbr, sizeof ( mbr_t ) );
530 if ( size != ( sizeof ( mbr_t ) ) ) {
534 ABORT( "ERROR(%04u): Can't write %s. ret=%d, errno=%d.\n",
548 /******************************************************************************/
550 * @brief ブートローダバイナリ書込み
551 * @details ブートローダバイナリを仮想ディスクのパーティション1番に書込む。
553 * @param[in] diskFd 仮想ディスクファイルディスクリプタ
554 * @param[in] *pBootPath ブートローダバイナリのファイルパス
555 * @param[in] chsFirstAddr 書込み先先頭CHSアドレス
557 * @return 書込み先最後尾CHSアドレス
559 /******************************************************************************/
560 static chs_t writeBoot( int diskFd,
564 int bootFd; /* ブートローダファイルディスクリプタ */
565 pt_t pe; /* パーティションエントリ */
566 char buffer[ BUFFER_SIZE ]; /* 読込みバッファ */
567 off_t offset; /* ファイルオフセット */
568 int32_t readSize; /* 読込サイズ */
569 int32_t writeSize; /* 書込みサイズ */
570 uint32_t fileSize; /* ファイルサイズ */
571 uint32_t lbaSize; /* ファイルサイズ(セクタ数) */
572 uint32_t lbaFirstAddr; /* 書込み先先頭LBAアドレス */
577 lbaFirstAddr = GET_CYLINDER( chsFirstAddr.cylSec ) * head * sector +
578 chsFirstAddr.head * sector +
579 GET_SECTOR( chsFirstAddr.cylSec ) - 1;
580 memset( &pe, 0, sizeof ( pt_t ) );
583 offset = lseek( diskFd, lbaFirstAddr * 512, SEEK_SET );
586 if ( ( int32_t ) offset != ( lbaFirstAddr * 512 ) ) {
590 ABORT( "ERROR(%04u): Can't seek at the disk image. ret=%d, errno=%d.\n",
596 /* ブートローダバイナリファイルオープン */
597 bootFd = open( pBootPath, O_RDONLY );
600 if ( bootFd == -1 ) {
604 ABORT( "ERROR(%04u): Can't open %s. errno=%d.\n",
610 /* 読み書きバッファサイズ毎に繰り返し */
613 memset( buffer, 0, BUFFER_SIZE );
616 readSize = read( bootFd, buffer, BUFFER_SIZE );
619 if ( readSize == -1 ) {
623 ABORT( "ERROR(%04u): Can't read from %s. errno=%d.\n",
628 } else if ( readSize == 0 ) {
635 writeSize = write( diskFd, buffer, readSize );
638 if ( writeSize != readSize ) {
642 ABORT( "ERROR(%04u): Can't write %s. errno=%d.\n",
649 fileSize += writeSize;
651 } while ( writeSize == BUFFER_SIZE );
654 pe.status = PT_STATUS_BOOT;
655 pe.chsFirstAddr = chsFirstAddr;
656 pe.type = PT_TYPE_MOCHI_BOOTER;
657 pe.chsLastAddr = getChs( lbaFirstAddr + lbaSize - 1 );
658 pe.lbaFirstAddr = lbaFirstAddr;
659 pe.lbaSize = lbaSize;
660 writePartitionEntry( diskFd, 1, &pe );
665 return pe.chsLastAddr;
669 /******************************************************************************/
672 * @details カーネルバイナリを仮想ディスクのパーティション2番に書込む。
674 * @param[in] diskFd 仮想ディスクファイルディスクリプタ
675 * @param[in] *pKernelPath カーネルバイナリのファイルパス
676 * @param[in] chsFirstAddr 書込み先先頭CHSアドレス
678 * @return 書込み先最後尾CHSアドレス
680 /******************************************************************************/
681 static chs_t writeKernel( int diskFd,
685 int kernelFd; /* カーネルファイルディスクリプタ */
686 pt_t pe; /* パーティションエントリ */
687 char buffer[ BUFFER_SIZE ]; /* 読込みバッファ */
688 off_t offset; /* ファイルオフセット */
689 int32_t readSize; /* 読込サイズ */
690 int32_t writeSize; /* 書込みサイズ */
691 uint32_t fileSize; /* ファイルサイズ */
692 uint32_t lbaSize; /* ファイルサイズ(セクタ数) */
693 uint32_t lbaFirstAddr; /* 書込み先先頭LBAアドレス */
698 lbaFirstAddr = GET_CYLINDER( chsFirstAddr.cylSec ) * head * sector +
699 chsFirstAddr.head * sector +
700 GET_SECTOR( chsFirstAddr.cylSec ) - 1;
701 memset( &pe, 0, sizeof ( pt_t ) );
704 offset = lseek( diskFd, lbaFirstAddr * 512, SEEK_SET );
707 if ( ( int32_t ) offset != ( lbaFirstAddr * 512 ) ) {
711 ABORT( "ERROR(%04u): Can't seek at the disk image. ret=%d, errno=%d.\n",
717 /* カーネルバイナリファイルオープン */
718 kernelFd = open( pKernelPath, O_RDONLY );
721 if ( kernelFd == -1 ) {
725 ABORT( "ERROR(%04u): Can't open %s. errno=%d.\n",
731 /* 読み書きバッファサイズ毎に繰り返し */
734 memset( buffer, 0, BUFFER_SIZE );
737 readSize = read( kernelFd, buffer, BUFFER_SIZE );
740 if ( readSize == -1 ) {
744 ABORT( "ERROR(%04u): Can't read from %s. errno=%d.\n",
749 } else if ( readSize == 0 ) {
756 writeSize = write( diskFd, buffer, readSize );
759 if ( writeSize != readSize ) {
763 ABORT( "ERROR(%04u): Can't write %s. errno=%d.\n",
770 fileSize += writeSize;
772 } while ( writeSize == BUFFER_SIZE );
776 pe.chsFirstAddr = chsFirstAddr;
777 pe.type = PT_TYPE_MOCHI_KERNEL;
778 pe.chsLastAddr = getChs( lbaFirstAddr + lbaSize - 1 );
779 pe.lbaFirstAddr = lbaFirstAddr;
780 pe.lbaSize = lbaSize;
781 writePartitionEntry( diskFd, 2, &pe );
786 return pe.chsLastAddr;
790 /******************************************************************************/
792 * @brief パーティションエントリ書込み
793 * @details パーティションテーブルのエントリを仮想ディスクに書き込む。
795 * @param[in] diskFd 仮想ディスクファイルディスクリプタ
796 * @param[in] no パーティション番号
797 * @param[in] *pPe パーティションエントリ
799 /******************************************************************************/
800 static void writePartitionEntry( int diskFd,
804 int iplFd; /* IPLバイナリファイルディスクリプタ */
805 mbr_t mbr; /* マスタブートレコード */
806 off_t offset; /* ファイルオフセット */
807 int32_t size; /* サイズ */
810 offset = lseek( diskFd, 0, SEEK_SET );
813 if ( ( int32_t ) offset != 0 ) {
817 ABORT( "ERROR(%04u): Can't seek at the disk image. ret=%d, errno=%d.\n",
824 size = ( int32_t ) read( diskFd, &mbr, sizeof ( mbr_t ) );
827 if ( size != ( sizeof ( mbr_t ) ) ) {
831 ABORT( "ERROR(%04u): Can't read from MBR. ret=%d, errno=%d.\n",
838 mbr.partitionTbl[ no - 1 ] = *pPe;
841 offset = lseek( diskFd, 0, SEEK_SET );
844 if ( ( int32_t ) offset != 0 ) {
848 ABORT( "ERROR(%04u): Can't seek at the disk image. ret=%d, errno=%d.\n",
855 size = ( int32_t ) write( diskFd, &mbr, sizeof ( mbr_t ) );
858 if ( size != ( sizeof ( mbr_t ) ) ) {
862 ABORT( "ERROR(%04u): Can't write MBR. ret=%d, errno=%d.\n",
872 /******************************************************************************/