Difference between revisions of "BIOS"

From PSXDEV
Jump to: navigation, search
(Shell)
(Kernel (PlayStation OS))
Line 877: Line 877:
  
 
Выполнение пользовательских программ происходит в режиме CPU Kernel Mode, поскольку одновременно может быть запущен только один "процесс" (исполняемый файл игры).
 
Выполнение пользовательских программ происходит в режиме CPU Kernel Mode, поскольку одновременно может быть запущен только один "процесс" (исполняемый файл игры).
 +
 +
== Kernel memory map ==
  
 
== Shell ==
 
== Shell ==

Revision as of 17:31, 8 June 2015

512 KB ROM содержит стартовый загрузчик BIOS, копию ядра (kernel) PlayStation OS, а также "оболочку" (shell), которая открывается, если в консоль не вставлен игровой диск и содержит менеджер карт памяти и CD-проигрыватель.

Типичный ROM BIOS выглядит примерно вот так :

400px

  • У 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 продолжает загрузку диска
  • Запускается процедура main устройства PIO (PIO 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, поскольку одновременно может быть запущен только один "процесс" (исполняемый файл игры).

Kernel memory map

Shell

Оболочка BIOS - это специальным образом запакованный исполняемый файл формата PS-X EXE, который находится внутри ROM.

Программа начальной загрузки распаковывает его и загружает на лету в RAM перед запуском.

Оболочка запускается и перехватывает управление если в привод не вставлен игровой диск.