Difference between revisions of "BIOS"
(→Boot) |
(→Bootrom Main) |
||
Line 341: | Line 341: | ||
== Bootrom Main == | == Bootrom Main == | ||
+ | |||
+ | Процедура Main работает следующим образом : | ||
+ | * В память копируется резидентный образ ядра и запускается его процедура инициализации | ||
+ | * Устанавливаются Kernel Traps (обработчики исключений, прерываний и системных вызовов) | ||
+ | * Устанавливаются драйвера устройств (TTY, CDROM и MemCard) | ||
+ | * Инциализируется исполнительная система ядра (Kernel Executive) : Обработчики событий, потоки, события и системные счетчики | ||
+ | * Распаковывается и запускается SHELL, которая решает - запустить меню или выйти назад в ядро для загрузки диска | ||
+ | * Если SHELL выходит, то Main продолжает загрузку диска | ||
+ | * Считывает конфигурация SYSTEM.CNF и ядро производит реинциализацию системных таблиц в соответствии с настройками | ||
+ | * Загружается исполняемый файл | ||
+ | * Исполняемый файл запускается на исполнение | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c"> |
Revision as of 18:11, 5 June 2015
512 KB ROM содержит стартовый загрузчик BIOS, копию ядра (kernel) PlayStation OS, а также "оболочку" (shell), которая открывается, если в консоль не вставлен игровой диск и содержит менеджер карт памяти и CD-проигрыватель.
Типичный ROM BIOS выглядит примерно вот так :
- У BIOS PU-7 и старых PU-8 микросхема 40 выводов.
- Начиная с новых версий PU-8 (и далее) микросхема 32 вывода (в том числе и у PSOne)
Внутри скорее всего ROM с ионной имплантацией по маске.
Contents |
Тайминги
Версии BIOS
Вот это сложный вопрос, потому что версии BIOS во-первых отличаются от региона, во вторых они отличаются между моделями материнских плат. И даже внутри одной модели материнки могут быть разные версии BIOS, в зависимости от ревизии материнской платы одной модели.
Эталонной версией BIOS почти все эмуляторы считают SCPH1001.BIN. Этот BIOS был подробно дизассемблирован и считается "стабильным" для работы в эмуляторах.
Boot
Программа начальной загрузки (RESET)
Исполнение начинается с адреса 0xBFC00000
- Инициализирует недокументированные аппаратные регистры CPU (тайминг и пр.)
- Очищает память и регистры CPU
- Если в PSX присутствует устройство PIO - выполняет его программу инициализации (init)
- Переходит на загрузку ядра (процедура Main)
Some reversing of SCPH-1001 BIOS :
// // SCPH1001 Reset // Written likely on assembler // Reset () // 0xBFC00000 { dword.0x1F801010 = 0x13243F; dword.0x1F801060 = 0xB88; // ram_size? // // 20 nops (pipeline reset?) // nop x 20; goto Reset2; } Reset2 () // 0xBFC00150 { // // More registers // dword.0x1F801020 = 0x31125; dword.0x1F801000 = 0x1F000000; dword.0x1F801004 = 0x1F802000; dword.0x1F801008 = 0x13243F; dword.0x1F801014 = 0x200931E1; dword.0x1F801018 = 0x20843; dword.0x1F80100C = 0x3022; dword.0x1F80101C = 0x70777; // // Clear CPU registers // gpr[1...31] = 0; // // B/U Control (Lock ICache?) // dword.0xFFFE0130 = 0x804; // Enable I-Cache + Tag Test Mode // // COP0 // COP0.SR = 0x10000; // Isolate cache from bus // // Clear instruction cache Tag memory (Cache line = 16 bytes) // for ( int Addr=0; Addr<0x1000; Addr += 0x80 ) { dword.[Addr] = 0; dword.[Addr + 0x10] = 0; dword.[Addr + 0x20] = 0; dword.[Addr + 0x30] = 0; dword.[Addr + 0x40] = 0; dword.[Addr + 0x50] = 0; dword.[Addr + 0x60] = 0; dword.[Addr + 0x70] = 0; } // // COP0 // COP0.SR = 0; // // BIU/Cache configuration // dword.0xFFFE0130 = 0x800; // Enable I-Cache // // COP0 // COP0.SR = 0x10000; // Isolate cache from bus // // Clear instruction cache lines // for ( int Addr=0; Addr<0x1000; Addr += 0x80 ) { dword.[Addr] = 0; dword.[Addr + 0x4] = 0; dword.[Addr + 0x8] = 0; dword.[Addr + 0xC] = 0; dword.[Addr + 0x10] = 0; dword.[Addr + 0x14] = 0; dword.[Addr + 0x18] = 0; dword.[Addr + 0x1C] = 0; dword.[Addr + 0x20] = 0; dword.[Addr + 0x24] = 0; dword.[Addr + 0x28] = 0; dword.[Addr + 0x2C] = 0; dword.[Addr + 0x30] = 0; dword.[Addr + 0x34] = 0; dword.[Addr + 0x38] = 0; dword.[Addr + 0x3C] = 0; dword.[Addr + 0x40] = 0; dword.[Addr + 0x44] = 0; dword.[Addr + 0x48] = 0; dword.[Addr + 0x4C] = 0; dword.[Addr + 0x50] = 0; dword.[Addr + 0x54] = 0; dword.[Addr + 0x58] = 0; dword.[Addr + 0x5C] = 0; dword.[Addr + 0x60] = 0; dword.[Addr + 0x64] = 0; dword.[Addr + 0x68] = 0; dword.[Addr + 0x6C] = 0; dword.[Addr + 0x70] = 0; dword.[Addr + 0x74] = 0; dword.[Addr + 0x78] = 0; dword.[Addr + 0x7C] = 0; } // // COP0 // COP0.SR = 0; // // Read memory 8 times // t1 = dword.0xA0000000; t1 = dword.0xA0000000; t1 = dword.0xA0000000; t1 = dword.0xA0000000; t1 = dword.0xA0000000; t1 = dword.0xA0000000; t1 = dword.0xA0000000; t1 = dword.0xA0000000; // // BIU/Cache default configuration // dword.0xFFFE0130 = 0x1E988; // D-Cache as Scratchpad // Enable D-Cache // Enable I-Cache // Enable Bus Grant // No wait state // Enable Read Priority // Enable Load Scheduling // // Reset COP0 regs // COP0.Reg7 = 0; COP0.EntryLo1 = 0; COP0.PageMask = 0; COP0.Wired = 0; COP0.Count = 0; COP0.Compare = 0; COP0.SR = 0; COP0.Cause = 0; // // Clear 0xA0009000 // memset ( 0xA0009000, 0, 0x3160 ); // // Set initial context // CPU.SP = 0x801FFF00; CPU.GP = 0xA0010FF0; CPU.FP = CPU.SP; // // ram_size ? // dword.0x1F801060 = 0xB88; dword.0x60 = 2; dword.0x64 = 0; dword.0x68 = 0xff; // // Mute SPU // word.[0x1F801C00 + 0x180] = 0; // Mainvolume Left word.[0x1F801C00 + 0x182] = 0; // Mainvolume Right word.[0x1F801C00 + 0x184] = 0; // Reverb depth left word.[0x1F801C00 + 0x186] = 0; // Reverb depth right goto Reset3; } // // Following boot code written on C for sure (has C prolog/epilog in code) // Reset3 () // 0xBFC06EC4 { int Present; TraceStep (0xF); word.[0x1F801C00 + 0x186] = 0; word.[0x1F801C00 + 0x184] = 0; word.[0x1F801C00 + 0x182] = 0; word.[0x1F801C00 + 0x180] = 0; Present = CheckPIO (); if ( Present ) ResetPIO (); TraceStep (0xE); dword.0xA000B9B0 = 0; StartKernel (); } TraceStep (a0) // 0xBFC01A60 { Bogus1 (); byte.0x1F802041 = a0 & 0xFF; } Bogus1 () // 0xBFC03990 { dword.0xA000B068 = 0; dword.0xA000B068 = 0; dword.0xA000B068 = 0; dword.0xA000B068 = 0; } // // PIO Support // char Licensed[] = "Licensed by Sony Computer Entertainment Inc."; // 0xBFC0E288 int CheckPIO (void) // 0xBFC0703C { char * Source = Licensed; char * Dest = 0x1F000084; // PIO Header while ( *Source ) { if ( *Source != *Dest ) break; Source++; Dest++; } if ( *Source ) return 0; else return 1; } void ResetPIO (void) // 0xBFC0711C { // // Run init code in PIO Space // dword.0x1F000080 (); } void StartKernel () // 0xBFC06784 { char Config[0x50]; char Exec[0x50]; strcpy ( Config, "cdrom:" ); strcat ( Config, "SYSTEM.CNF;1" ); strcpy ( Exec, "cdrom:" ); strcat ( Exec, "PSX.EXE;1" ); Main ( Config, Exec ); }
Bootrom Main
Процедура Main работает следующим образом :
* В память копируется резидентный образ ядра и запускается его процедура инициализации * Устанавливаются Kernel Traps (обработчики исключений, прерываний и системных вызовов) * Устанавливаются драйвера устройств (TTY, CDROM и MemCard) * Инциализируется исполнительная система ядра (Kernel Executive) : Обработчики событий, потоки, события и системные счетчики * Распаковывается и запускается SHELL, которая решает - запустить меню или выйти назад в ядро для загрузки диска * Если SHELL выходит, то Main продолжает загрузку диска * Считывает конфигурация SYSTEM.CNF и ядро производит реинциализацию системных таблиц в соответствии с настройками * Загружается исполняемый файл * Исполняемый файл запускается на исполнение
// // Bootrom Main // // Sometimes we need to re-mute SPU (why?) #define MUTE_SPU() \ word.[0x1F801C00 + 0x186] = 0; \ word.[0x1F801C00 + 0x184] = 0; \ word.[0x1F801C00 + 0x182] = 0; \ word.[0x1F801C00 + 0x180] = 0; typedef struct _SYSTEM_CONFIG { SIZE_R Tcb; // Max number of threads SIZE_T Event; // Max number of events PVOID Stack; // Initial stack pointer } SYSTEM_CONFIG, *PSYSTEM_CONFIG; SYSTEM_CONFIG DefaultConfig = { // 0xBFC0E14C 4, 16, 0x801FFF00 }; void Main (char *Config, char *Exec ) // 0xBFC067E8 { int File; long Bytes; // // Disable all interupts, External (level 3) interrupts are disabled // TraceStep (1); SetSr ( GetSr () & 0xFFFFFBFE ); MUTE_SPU (); // // Copy kernel image from Bootrom and run its initialization code. // TraceStep (2); LoadInitKernel (); // // Initialize kernel traps // TraceStep (3); CopyBootapiTable (); // Copy BIOS shared procedure callbacks at 0x200 InitSyscall (); // Copy 0xA0, 0xB0 and 0xC0 syscall stubs from kernel image PatchA0Table (); InstallExceptionHandlers (); ResetEntryInt (); // // Init device drivers // TraceStep (4); MUTE_SPU (); dword.0x1F801074 = 0; // int_mask dword.0x1F801070 = 0; // int_reg InstallDevices ( dword_B9B0 ); // // Shout out first printf // TraceStep (5); printf ( "\n" "PS-X Realtime Kernel Ver.2.5\n" "Copyright 1993,1994 (C) Sony Computer Entertainment Inc. \n" ); // // Init Kernel executive // TraceStep (6); MUTE_SPU (); memcpy ( 0xA000B940, &DefaultConfig, sizeof(SYSTEM_CONFIG) ); printf ( "KERNEL SETUP!\n" ); SysInitMemory ( 0xA000E000, 0x2000 ); // Kernel heap InitEventHandlers (4); InitException (0) InitDefInt (3); InitEvents ( dword_B944 ); InitThreads ( 1, dword_B940 ); InitRCnt (1); MUTE_SPU (); if ( setjmp ( 0xA000B980 ) ) SystemHalt (901); // // Run Shell // TraceStep (7); LoadRunShell (a0); // // 8 // TraceStep (8); dword.0x1F801074 = 0; // int_mask dword.0x1F801070 = 0; // int_reg sub_BFC073A0 (); if ( setjmp ( 0xA000B980 ) ) SystemHalt (921); // // PIO Shell // if ( CheckPIO2 () == 1 ) BootPIO (); printf ( "\n" "BOOTSTRAP LOADER Type C Ver 2.1 03-JUL-1994\n" "Copyright 1993,1994 (C) Sony Computer Entertainment Inc.\n" ); if ( setjmp ( 0xA000B980 ) ) SystemHalt (902); // // Load executable from external media. // TraceStep (9); if ( setjmp ( 0xA000B980) ) SystemHalt (903); // // Try to open system config file. // If config file is missing fall back to plain EXE loading // File = open ( Config, O_RDONLY ); if ( File >= 0 ) { // // Read and parse config file parameters // printf ( "setup file : %s\n", Config ); if ( setjmp ( 0xA000B980 ) ) SystemHalt (911); Bytes = read ( File, 0xA000B070, 0x800 ); if ( Bytes ) { 0xA000B070[Bytes] = 0; // Terminate string close ( File ); if ( setjmp ( 0xA000B980 ) ) SystemHalt ( 912 ); ParseConfig ( 0xA000B070, 0xA000B940, 0xA000B8B0 ); } else { // // Set default config, fall back to plain EXE loading // memcpy ( 0xA000B940, &DefaultConfig, sizeof(SYSTEM_CONFIG) ); strcpy ( 0xA000B8B0, Exec ); } } else { // // Use plain executable // if ( setjmp ( 0xA000B980 ) ) SystemHalt (913); byte.0x180 = 0; // Version number memcpy ( 0xA000B940, &DefaultConfig, sizeof(SYSTEM_CONFIG) ); strcpy ( 0xA000B8B0, Exec ); } // // Load executable // if ( setjmp ( 0xA000B980 ) ) SystemHalt (904); ReinitKernel (); printf ( "boot file : %s\n", 0xA000B8B0 ); if ( setjmp ( 0xA000B980 ) ) SystemHalt (905); ClearStack (); if ( Load (0xA000B8B0, 0xA000B870 ) == 0 ) SystemHalt (906); printf ( "EXEC:PC0(%08x) T_ADDR(%08x) T_SIZE(%08x)\n", dword_B870, dword_B878, dword_B87C ); printf ( "boot address : %08x %08x\n" "Execute !\n\n", dword_B870, dword_B948 ); dword_B890 = dword_B948; dword_B894 = 0; printf ( " S_ADDR(%08x) S_SIZE(%08)\n", dword_B948, 0 ); EnterCriticalSection (); if ( setjmp ( 0xA000B980 ) ) SystemHalt (907); Exec (0xA000B870, 1, 0 ); printf ( "End of Main\n" ); SystemHalt (908); } // // Support // // Written in asm. int setjmp ( jmp_buf * Buffer ) // 0xBFC02240 { Buffer[JB_PC] = ra; Buffer[JB_GP] = gp; Buffer[JB_SP] = sp; Buffer[JB_FP] = fp; Buffer[JB_S0] = s0; Buffer[JB_S1] = s1; Buffer[JB_S2] = s2; Buffer[JB_S3] = s3; Buffer[JB_S4] = s4; Buffer[JB_S5] = s5; Buffer[JB_S6] = s6; Buffer[JB_S7] = s7; return 0; } void SystemHalt (int Code) // 0xBFC06FA4 { TraceStep (0xF); SystemError ( 'B', Code ); } ULONG GetSr (void) // 0xBFC03968 { return COP0.SR; } void SetSr (ULONG Value) // 0xBFC03978 { COP0.SR = Value; } void LoadInitKernel () // 0xBFC00420 { memcpy ( 0xA0000500, 0xBFC10000, 0x8BF0 ); 0xA0000500 (); // Run kernel initialization } CopyBootapiTable () // 0xBFC042D0 { memcpy ( 0x200, 0xBFC04300, 0x304 ); } InitSyscall () // 0xBFC042A0 { // // Copy 0xA0, 0xB0 and 0xC0 syscall stubs from kernel image // memcpy ( 0xA0, 0xA0000510, 0x30 ); } ClearStack () // 0xBFC0D850 { PVOID StartAddress; PVOID EndAddress; StartAddress = 0xA0010000; EndAddress = sp | 0xA0000000; memset ( StartAddress, 0, EndAddress - StartAddress ); } ReinitKernel () // 0xBFC06F28 { printf ( "KERNEL SETUP!\n" ); SysInitMemory ( 0xA000E000, 0x2000 ); // Kernel heap InitEventHandlers (4); InitException (0); InitDefInt (3); InitEvents ( dword_B944 ); InitThreads ( 1, dword_B940 ); InitRCnt (1); sub_BFC071A0 (); } char Licensed[] = "Licensed by Sony Computer Entertainment Inc."; // 0xBFC0E288 int CheckPIO2 () // 0xBFC070AC { char * Source = Licensed; char * Dest = 0x1F000004; // PIO Header 2 while ( *Source ) { if ( *Source != *Dest ) break; Source++; Dest++; } if ( *Source ) return 0; else return 1; } BootPIO () // 0xBFC07148 { printf ( "PIO SHELL for PlayStation(tm)\n" ); printf ( "%s\n", 0x1F000004 ); dword.0x1F000000 (); // Jump by pointer. } void ParseConfig (char *Text, PSYSTEM_CONFIG Config, char *Exec ) // 0xBFC008A0 { Config->Tcb = 0; Config->Event = 0; Config->Stack = 0; Exec[0] = '\0'; byte.0x180 = 0; // Version number ParseConfigInt ( Text, &Config->Tcb, "TCB" ); ParseConfigInt ( Text, &Config->Event, "EVENT" ); ParseConfigInt ( Text, &Config->Stack, "STACK" ); ParseConfigString ( Text, Exec, 0x180, "BOOT" ); } LoadRunShell (a0) // 0xBFC06FF0 { memcpy ( 0x80030000, 0xBFC18000, 0x67FF0 ); FlushCache (); 0x80030000 (a0); } // // Executive init // int InitEventHandlers (int Num) // 0xBFC04610 { int Bytes = Num * 8; Pointer = SysMalloc ( Bytes ); if ( Pointer ) { memclr ( Pointer, Bytes ); dword_100 = Pointer; dword_104 = Bytes; return Bytes; } else return 0; } PVOID InitEvents (int Num) // 0xBFC04678 { int Bytes; int n; PVOID Pointer; EvCB * Event; printf ( "\nConfiguration : EvCB\t0x%02x\t\t", Num ); Bytes = Num * sizeof (EvCB); Pointer = SysMalloc (Bytes); if ( Pointer ) { dword_124 = Pointer; dword_120 = Bytes; // // Clear "status" field for all event CBs. // for (n=0; n<Num; n++) { Event = &dword_124[n]; Event->status = 0; } return Pointer; } else return 0; } int InitThreads (int Tcbh, int Tcb) // 0xBFC0472C { TCBH * TCBH_Ptr; TCB * TCB_Ptr; TCBH * TcbhEntry; TCBH * TcbEntry; int n; printf ( "TCB\t0x%02x\n", Num ); dword_10C = Tcbh * 4; // BUGCHECK: Should be actually 8, since TCBH has additional "flag" field deinfed in KERNEL.H dword_114 = Tcb * 192; TCBH_Ptr = SysMalloc ( dword_10C ); if ( TCBH_Ptr == NULL ) return 0; TCB_Ptr = SysMalloc ( dword_114 ); if ( TCB_Ptr == NULL ) return 0; // // Clear TCBH // for (n=0; n<Tcbh; n++) { TcbhEntry = &TCBH_Ptr[n]; TcbhEntry->entry = NULL; } // // Clear TCB // for (n=0; n<Tcb; n++) { TcbEntry = &TCB_Ptr[n]; TcbEntry->status = TcbStUNUSED; } // // Set active first TCB entry // TCB_Ptr[0].status = TcbStACTIVE; TCBH_Ptr[0].entry = TCB_Ptr; // // Update ToT // dword_108 = TCBH_Ptr; dword_110 = TCB_Ptr; return dword_10C + dword_114; // Total size of TCBH and TCB tables }
Kernel (PlayStation OS)
Ядро PS OS резидентно находится в памяти. Доступ к процедурам ядра производится через специальные таблицы (которые находятся по адресам 0xA0, 0xB0, 0xC0).
Второй способ вызова некоторых механизмов ядра - это инструкция Syscall (но набор её функций ограничен, по сути используется только для EnterCriticalSection / ExitCriticalSection)
Также приложениям доступна специальная "Таблица Таблиц" ядра (ToT), через которую программа может получить различные системные описатели и пр.
Выполнение пользовательских программ происходит в режиме CPU Kernel Mode, поскольку одновременно может быть запущен только один "процесс" (исполняемый файл игры).
Shell
Оболочка BIOS - это специальным образом запакованный исполняемый файл формата PS-X EXE, который находится внутри ROM.
Программа начальной загрузки распаковывает его и загружает на лету в RAM перед запуском.
Оболочка запускается если в привод не вставлен игровой диск.