diff options
Diffstat (limited to 'src/devices/machine/x76f041.cpp')
-rw-r--r-- | src/devices/machine/x76f041.cpp | 370 |
1 files changed, 318 insertions, 52 deletions
diff --git a/src/devices/machine/x76f041.cpp b/src/devices/machine/x76f041.cpp index 03fb5b65bf9..aa96ad90180 100644 --- a/src/devices/machine/x76f041.cpp +++ b/src/devices/machine/x76f041.cpp @@ -14,7 +14,11 @@ */ #include "emu.h" -#include "machine/x76f041.h" +#include "x76f041.h" + +#include <cstdarg> +#include <tuple> + #define VERBOSE_LEVEL ( 0 ) @@ -23,7 +27,7 @@ inline void ATTR_PRINTF( 3, 4 ) x76f041_device::verboselog( int n_level, const c if( VERBOSE_LEVEL >= n_level ) { va_list v; - char buf[ 32768 ]; + char buf[32768]; va_start( v, s_fmt ); vsprintf( buf, s_fmt, v ); va_end( v ); @@ -31,11 +35,12 @@ inline void ATTR_PRINTF( 3, 4 ) x76f041_device::verboselog( int n_level, const c } } + // device type definition DEFINE_DEVICE_TYPE(X76F041, x76f041_device, "x76f041", "X76F041 Secure SerialFlash") -x76f041_device::x76f041_device( const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock ) - : device_t( mconfig, X76F041, tag, owner, clock ), +x76f041_device::x76f041_device( const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock ) : + device_t( mconfig, X76F041, tag, owner, clock ), device_nvram_interface(mconfig, *this), m_region(*this, DEVICE_SELF), m_cs( 0 ), @@ -48,13 +53,15 @@ x76f041_device::x76f041_device( const machine_config &mconfig, const char *tag, m_bit( 0 ), m_byte( 0 ), m_command( 0 ), - m_address( 0 ) + m_address( 0 ), + m_is_password_accepted ( false ) { } void x76f041_device::device_start() { - memset( m_write_buffer, 0, sizeof( m_write_buffer ) ); + std::fill( std::begin( m_write_buffer ), std::end( m_write_buffer ), 0 ); + std::fill( std::begin( m_password_temp ), std::end( m_password_temp ), 0 ); save_item( NAME( m_cs ) ); save_item( NAME( m_rst ) ); @@ -67,6 +74,7 @@ void x76f041_device::device_start() save_item( NAME( m_byte ) ); save_item( NAME( m_command ) ); save_item( NAME( m_address ) ); + save_item( NAME( m_is_password_accepted ) ); save_item( NAME( m_write_buffer ) ); save_item( NAME( m_response_to_reset ) ); save_item( NAME( m_write_password ) ); @@ -74,9 +82,29 @@ void x76f041_device::device_start() save_item( NAME( m_configuration_password ) ); save_item( NAME( m_configuration_registers ) ); save_item( NAME( m_data ) ); + save_item( NAME( m_password_temp ) ); +} + +void x76f041_device::device_reset() +{ + std::fill( std::begin( m_write_buffer ), std::end( m_write_buffer ), 0 ); + std::fill( std::begin( m_password_temp ), std::end( m_password_temp ), 0 ); + + m_cs = 0; + m_rst = 0; + m_scl = 0; + m_sdaw = 0; + m_sdar = 0; + m_state = STATE_STOP; + m_shift = 0; + m_bit = 0; + m_byte = 0; + m_command = 0; + m_address = 0; + m_is_password_accepted = false; } -WRITE_LINE_MEMBER( x76f041_device::write_cs ) +void x76f041_device::write_cs(int state) { if( m_cs != state ) { @@ -100,7 +128,7 @@ WRITE_LINE_MEMBER( x76f041_device::write_cs ) m_cs = state; } -WRITE_LINE_MEMBER( x76f041_device::write_rst ) +void x76f041_device::write_rst(int state) { if( m_rst != state ) { @@ -128,6 +156,15 @@ uint8_t *x76f041_device::password() case COMMAND_READ: return m_read_password; + case COMMAND_CONFIGURATION: + if( m_address == CONFIGURATION_PROGRAM_WRITE_PASSWORD ) + return m_write_password; + + if( m_address == CONFIGURATION_PROGRAM_READ_PASSWORD ) + return m_read_password; + + return m_configuration_password; + default: return m_configuration_password; } @@ -135,6 +172,9 @@ uint8_t *x76f041_device::password() void x76f041_device::password_ok() { + if( m_configuration_registers[CONFIG_CR] & CR_RETRY_COUNTER_RESET_BIT ) + m_configuration_registers[CONFIG_RC] = 0; + switch( m_command & 0xe0 ) { case COMMAND_WRITE: @@ -144,7 +184,7 @@ void x76f041_device::password_ok() m_state = STATE_READ_DATA; break; case COMMAND_WRITE_USE_CONFIGURATION_PASSWORD: - m_state = STATE_WRITE_DATA; + m_state = STATE_CONFIGURATION_WRITE_DATA; break; case COMMAND_READ_USE_CONFIGURATION_PASSWORD: m_state = STATE_READ_DATA; @@ -153,14 +193,22 @@ void x76f041_device::password_ok() switch( m_address ) { case CONFIGURATION_PROGRAM_WRITE_PASSWORD: + m_state = STATE_PROGRAM_WRITE_PASSWORD; + m_byte = 0; break; case CONFIGURATION_PROGRAM_READ_PASSWORD: + m_state = STATE_PROGRAM_READ_PASSWORD; + m_byte = 0; break; case CONFIGURATION_PROGRAM_CONFIGURATION_PASSWORD: + m_state = STATE_PROGRAM_CONFIGURATION_PASSWORD; + m_byte = 0; break; case CONFIGURATION_RESET_WRITE_PASSWORD: + m_state = STATE_RESET_WRITE_PASSWORD; break; case CONFIGURATION_RESET_READ_PASSWORD: + m_state = STATE_RESET_READ_PASSWORD; break; case CONFIGURATION_PROGRAM_CONFIGURATION_REGISTERS: m_state = STATE_WRITE_CONFIGURATION_REGISTERS; @@ -171,8 +219,10 @@ void x76f041_device::password_ok() m_byte = 0; break; case CONFIGURATION_MASS_PROGRAM: + m_state = STATE_MASS_PROGRAM; break; case CONFIGURATION_MASS_ERASE: + m_state = STATE_MASS_ERASE; break; default: break; @@ -182,21 +232,56 @@ void x76f041_device::password_ok() void x76f041_device::load_address() { - /* todo: handle other bcr bits */ - int bcr; - m_address = m_shift; verboselog( 1, "-> address: %02x\n", m_address ); - if( ( m_command & 1 ) == 0 ) + if( ( m_configuration_registers[CONFIG_CR] & CR_RETRY_COUNTER_ENABLE_BIT ) != 0 && + m_configuration_registers[CONFIG_RR] == m_configuration_registers[CONFIG_RC] && + ( m_configuration_registers[CONFIG_CR] & CR_UNAUTHORIZED_ACCESS_BITS ) == 0x80 ) { - bcr = m_configuration_registers[ CONFIG_BCR1 ]; + // No commands are allowed + verboselog( 1, "unauthorized access rejected\n" ); + m_state = STATE_STOP; + m_sdar = 1; + m_byte = 0; + return; } - else + + if( ( m_command & 0xe0 ) == COMMAND_CONFIGURATION ) { - bcr = m_configuration_registers[ CONFIG_BCR2 ]; + // Configuration commands can be used regardless of array control register bits + if( m_address == CONFIGURATION_RESET_WRITE_PASSWORD || + m_address == CONFIGURATION_RESET_READ_PASSWORD || + m_address == CONFIGURATION_MASS_PROGRAM || + m_address == CONFIGURATION_MASS_ERASE ) + { + verboselog( 1, "password not required\n" ); + password_ok(); + } + else + { + verboselog( 1, "send password\n" ); + m_state = STATE_LOAD_PASSWORD; + m_byte = 0; + } + + return; + } + + if( ( m_configuration_registers[CONFIG_CR] & CR_RETRY_COUNTER_ENABLE_BIT ) != 0 && + m_configuration_registers[CONFIG_RR] == m_configuration_registers[CONFIG_RC] && + ( m_configuration_registers[CONFIG_CR] & CR_UNAUTHORIZED_ACCESS_BITS ) != 0x80 ) + { + // Only configuration commands are allowed + verboselog( 1, "unauthorized access rejected\n" ); + m_state = STATE_STOP; + m_sdar = 1; + m_byte = 0; + return; } + + int bcr = m_configuration_registers[( m_command & 1 ) ? CONFIG_BCR2 : CONFIG_BCR1]; if( ( m_address & 0x80 ) != 0 ) { bcr >>= 4; @@ -205,10 +290,11 @@ void x76f041_device::load_address() if( ( ( m_command & 0xe0 ) == COMMAND_READ && ( bcr & BCR_Z ) != 0 && ( bcr & BCR_T ) != 0 ) || ( ( m_command & 0xe0 ) == COMMAND_WRITE && ( bcr & BCR_Z ) != 0 ) ) { - /* todo: find out when this is really checked. */ + /* TODO: find out when this is really checked. */ verboselog( 1, "command not allowed\n" ); m_state = STATE_STOP; - m_sdar = 0; + m_sdar = 1; + m_byte = 0; } else if( ( ( m_command & 0xe0 ) == COMMAND_WRITE && ( bcr & BCR_X ) == 0 ) || ( ( m_command & 0xe0 ) == COMMAND_READ && ( bcr & BCR_Y ) == 0 ) ) @@ -233,7 +319,7 @@ int x76f041_device::data_offset() return ( block_offset & 0x180 ) | ( ( block_offset + m_byte ) & 0x7f ); } -WRITE_LINE_MEMBER( x76f041_device::write_scl ) +void x76f041_device::write_scl(int state) { if( m_scl != state ) { @@ -250,7 +336,7 @@ WRITE_LINE_MEMBER( x76f041_device::write_scl ) case STATE_RESPONSE_TO_RESET: if( m_scl != 0 && state == 0 ) { - m_sdar = ( m_response_to_reset[ m_byte ] >> m_bit ) & 1; + m_sdar = ( m_response_to_reset[m_byte] >> m_bit ) & 1; verboselog( 2, "in response to reset %d (%d/%d)\n", m_sdar, m_byte, m_bit ); m_bit++; @@ -272,7 +358,18 @@ WRITE_LINE_MEMBER( x76f041_device::write_scl ) case STATE_LOAD_PASSWORD: case STATE_VERIFY_PASSWORD: case STATE_WRITE_DATA: + case STATE_CONFIGURATION_WRITE_DATA: case STATE_WRITE_CONFIGURATION_REGISTERS: + case STATE_PROGRAM_WRITE_PASSWORD: + case STATE_PROGRAM_READ_PASSWORD: + case STATE_PROGRAM_CONFIGURATION_PASSWORD: + case STATE_RESET_WRITE_PASSWORD: + case STATE_RESET_READ_PASSWORD: + case STATE_MASS_PROGRAM: + case STATE_MASS_ERASE: + // FIXME: Processing on the rising edge of the clock causes sda to change state while clock is high + // which is not allowed. Also need to ensure that only valid device-id's and commands + // are acknowledged. if( m_scl == 0 && state != 0 ) { if( m_bit < 8 ) @@ -296,7 +393,7 @@ WRITE_LINE_MEMBER( x76f041_device::write_scl ) case STATE_LOAD_COMMAND: m_command = m_shift; verboselog( 1, "-> command: %02x\n", m_command ); - /* todo: verify command is valid? */ + /* TODO: verify command is valid? */ m_state = STATE_LOAD_ADDRESS; break; @@ -306,22 +403,32 @@ WRITE_LINE_MEMBER( x76f041_device::write_scl ) case STATE_LOAD_PASSWORD: verboselog( 1, "-> password: %02x\n", m_shift ); - m_write_buffer[ m_byte++ ] = m_shift; + m_write_buffer[m_byte++] = m_shift; if( m_byte == sizeof( m_write_buffer ) ) { m_state = STATE_VERIFY_PASSWORD; + + // Perform the password acceptance check before verify password because + // password verify ack is spammed and will quickly overflow the + // retry counter. + m_is_password_accepted = memcmp( password(), m_write_buffer, sizeof( m_write_buffer ) ) == 0; + if( !m_is_password_accepted ) + { + if( m_configuration_registers[CONFIG_CR] & CR_RETRY_COUNTER_ENABLE_BIT ) + m_configuration_registers[CONFIG_RC]++; + } } break; case STATE_VERIFY_PASSWORD: verboselog( 1, "-> verify password: %02x\n", m_shift ); - /* todo: this should probably be handled as a command */ + /* TODO: this should probably be handled as a command */ if( m_shift == 0xc0 ) { - /* todo: this should take 10ms before it returns ok. */ - if( memcmp( password(), m_write_buffer, sizeof( m_write_buffer ) ) == 0 ) + /* TODO: this should take 10ms before it returns ok. */ + if( m_is_password_accepted ) { password_ok(); } @@ -334,15 +441,45 @@ WRITE_LINE_MEMBER( x76f041_device::write_scl ) case STATE_WRITE_DATA: verboselog( 2, "-> data: %02x\n", m_shift ); - m_write_buffer[ m_byte++ ] = m_shift; + m_write_buffer[m_byte++] = m_shift; if( m_byte == sizeof( m_write_buffer ) ) { + int bcr = m_configuration_registers[( m_command & 1 ) ? CONFIG_BCR2 : CONFIG_BCR1]; + if( ( m_address & 0x80 ) != 0 ) + { + bcr >>= 4; + } + + if( ( bcr & ( BCR_Z | BCR_T ) ) == BCR_T ) + { + // Bits in the data can only be set, not cleared, when in program only mode + bool is_unauthorized_write = false; + + for( m_byte = 0; m_byte < sizeof( m_write_buffer ); m_byte++ ) + { + int offset = data_offset(); + if( m_write_buffer[m_byte] < m_data[offset] ) + { + verboselog( 1, "tried to unset bits while in program only mode\n" ); + is_unauthorized_write = true; + break; + } + } + + if( is_unauthorized_write ) + { + m_sdar = 1; + m_byte = 0; + break; + } + } + for( m_byte = 0; m_byte < sizeof( m_write_buffer ); m_byte++ ) { int offset = data_offset(); - verboselog( 1, "-> data[ %03x ]: %02x\n", offset, m_write_buffer[ m_byte ] ); - m_data[ offset ] = m_write_buffer[ m_byte ]; + verboselog( 1, "-> data[%03x]: %02x\n", offset, m_write_buffer[m_byte] ); + m_data[offset] = m_write_buffer[m_byte]; } m_byte = 0; @@ -350,16 +487,109 @@ WRITE_LINE_MEMBER( x76f041_device::write_scl ) } break; + + case STATE_CONFIGURATION_WRITE_DATA: + // Unlike normal writes, configuration writes aren't required to be exactly 8 bytes + // TODO: Store data in a temporary buffer until the proper end of the command before writing + verboselog( 2, "-> data: %02x\n", m_shift ); + m_data[data_offset()] = m_shift; + m_byte++; + break; + case STATE_WRITE_CONFIGURATION_REGISTERS: - verboselog( 1, "-> configuration register[ %d ]: %02x\n", m_byte, m_shift ); - /* todo: write after all bytes received? */ - m_configuration_registers[ m_byte++ ] = m_shift; + verboselog( 1, "-> configuration register[%d]: %02x\n", m_byte, m_shift ); + /* TODO: write after all bytes received? */ + m_configuration_registers[m_byte++] = m_shift; if( m_byte == sizeof( m_configuration_registers ) ) { m_byte = 0; } break; + + case STATE_PROGRAM_WRITE_PASSWORD: + verboselog( 1, "-> program write password[%d]: %02x\n", m_byte, m_shift ); + m_password_temp[m_byte++] = m_shift; + + if( m_byte == sizeof( m_password_temp ) ) + { + // Read in the password twice and if the two copies match then write it to the password field + if( memcmp( &m_password_temp[0], &m_password_temp[8], sizeof( m_write_password ) ) == 0 ) + { + std::copy_n( std::begin( m_password_temp ), sizeof( m_write_password ), std::begin ( m_write_password ) ); + } + else { + m_sdar = 1; + } + + std::fill( std::begin( m_password_temp ), std::end( m_password_temp ), 0 ); + + m_byte = 0; + } + break; + + case STATE_PROGRAM_READ_PASSWORD: + verboselog( 1, "-> program read password[%d]: %02x\n", m_byte, m_shift ); + m_password_temp[m_byte++] = m_shift; + + if( m_byte == sizeof( m_password_temp ) ) + { + if( memcmp( &m_password_temp[0], &m_password_temp[8], sizeof( m_read_password ) ) == 0 ) + { + std::copy_n( std::begin( m_password_temp ), sizeof( m_read_password ), std::begin ( m_read_password ) ); + } + else { + m_sdar = 1; + } + + std::fill( std::begin( m_password_temp ), std::end( m_password_temp ), 0 ); + + m_byte = 0; + } + break; + + case STATE_PROGRAM_CONFIGURATION_PASSWORD: + verboselog( 1, "-> program configuration password[%d]: %02x\n", m_byte, m_shift ); + m_password_temp[m_byte++] = m_shift; + + if( m_byte == sizeof( m_password_temp ) ) + { + if( memcmp( &m_password_temp[0], &m_password_temp[8], sizeof( m_configuration_password ) ) == 0 ) + { + std::copy_n( std::begin( m_password_temp ), sizeof( m_configuration_password ), std::begin ( m_configuration_password ) ); + } + else { + m_sdar = 1; + } + + std::fill( std::begin( m_password_temp ), std::end( m_password_temp ), 0 ); + + m_byte = 0; + } + break; + + case STATE_RESET_WRITE_PASSWORD: + verboselog( 1, "-> reset write password\n" ); + std::fill( std::begin( m_write_password ), std::end( m_write_password ), 0 ); + break; + + case STATE_RESET_READ_PASSWORD: + verboselog( 1, "-> reset read password\n" ); + std::fill( std::begin( m_read_password ), std::end( m_read_password ), 0 ); + break; + + case STATE_MASS_PROGRAM: + case STATE_MASS_ERASE: + { + const uint8_t fill = m_state == STATE_MASS_ERASE ? 0xff : 0; + verboselog( 1, "-> mass erase %02x\n", fill ); + std::fill( std::begin( m_data ), std::end( m_data ), fill ); + std::fill( std::begin( m_configuration_password ), std::end( m_configuration_password ), fill ); + std::fill( std::begin( m_configuration_registers ), std::end( m_configuration_registers ), fill ); + std::fill( std::begin( m_write_password ), std::end( m_write_password ), fill ); + std::fill( std::begin( m_read_password ), std::end( m_read_password ), fill ); + break; + } } m_bit = 0; @@ -370,6 +600,8 @@ WRITE_LINE_MEMBER( x76f041_device::write_scl ) case STATE_READ_DATA: case STATE_READ_CONFIGURATION_REGISTERS: + // FIXME: Processing on the rising edge of the clock causes sda to change state while clock is high + // which is not allowed. if( m_scl == 0 && state != 0 ) { if( m_bit < 8 ) @@ -382,14 +614,14 @@ WRITE_LINE_MEMBER( x76f041_device::write_scl ) { case STATE_READ_DATA: offset = data_offset(); - m_shift = m_data[ offset ]; - verboselog( 1, "<- data[ %03x ]: %02x\n", offset, m_shift ); + m_shift = m_data[offset]; + verboselog( 1, "<- data[%03x]: %02x\n", offset, m_shift ); break; case STATE_READ_CONFIGURATION_REGISTERS: offset = m_byte & 7; - m_shift = m_configuration_registers[ offset ]; - verboselog( 1, "<- configuration register[ %d ]: %02x\n", offset, m_shift ); + m_shift = m_configuration_registers[offset]; + verboselog( 1, "<- configuration register[%d]: %02x\n", offset, m_shift ); break; } } @@ -421,7 +653,7 @@ WRITE_LINE_MEMBER( x76f041_device::write_scl ) m_scl = state; } -WRITE_LINE_MEMBER( x76f041_device::write_sda ) +void x76f041_device::write_sda(int state) { if( m_sdaw != state ) { @@ -447,7 +679,7 @@ WRITE_LINE_MEMBER( x76f041_device::write_sda ) break; case STATE_LOAD_PASSWORD: - /* todo: this will be the 0xc0 command, but it's not handled as a command yet. */ + /* TODO: this will be the 0xc0 command, but it's not handled as a command yet. */ verboselog( 1, "goto start\n" ); break; @@ -471,7 +703,7 @@ WRITE_LINE_MEMBER( x76f041_device::write_sda ) m_sdaw = state; } -READ_LINE_MEMBER( x76f041_device::read_sda ) +int x76f041_device::read_sda() { if( m_cs != 0 ) { @@ -520,22 +752,56 @@ void x76f041_device::nvram_default() } } -void x76f041_device::nvram_read( emu_file &file ) +bool x76f041_device::nvram_read( util::read_stream &file ) { - file.read( m_response_to_reset, sizeof( m_response_to_reset ) ); - file.read( m_write_password, sizeof( m_write_password ) ); - file.read( m_read_password, sizeof( m_read_password ) ); - file.read( m_configuration_password, sizeof( m_configuration_password ) ); - file.read( m_configuration_registers, sizeof( m_configuration_registers ) ); - file.read( m_data, sizeof( m_data ) ); + std::error_condition err; + size_t actual; + + std::tie( err, actual ) = read( file, m_response_to_reset, sizeof( m_response_to_reset ) ); + if( err || ( sizeof( m_response_to_reset ) != actual ) ) + return false; + std::tie( err, actual ) = read( file, m_write_password, sizeof( m_write_password ) ); + if( err || ( sizeof( m_write_password ) != actual ) ) + return false; + std::tie( err, actual ) = read( file, m_read_password, sizeof( m_read_password ) ); + if( err || ( sizeof( m_read_password ) != actual ) ) + return false; + std::tie( err, actual ) = read( file, m_configuration_password, sizeof( m_configuration_password ) ); + if( err || ( sizeof( m_configuration_password ) != actual ) ) + return false; + std::tie( err, actual ) = read( file, m_configuration_registers, sizeof( m_configuration_registers ) ); + if( err || ( sizeof( m_configuration_registers ) != actual ) ) + return false; + std::tie( err, actual ) = read( file, m_data, sizeof( m_data ) ); + if( err || ( sizeof( m_data ) != actual ) ) + return false; + + return true; } -void x76f041_device::nvram_write( emu_file &file ) +bool x76f041_device::nvram_write( util::write_stream &file ) { - file.write( m_response_to_reset, sizeof( m_response_to_reset ) ); - file.write( m_write_password, sizeof( m_write_password ) ); - file.write( m_read_password, sizeof( m_read_password ) ); - file.write( m_configuration_password, sizeof( m_configuration_password ) ); - file.write( m_configuration_registers, sizeof( m_configuration_registers ) ); - file.write( m_data, sizeof( m_data ) ); + std::error_condition err; + size_t actual; + + std::tie( err, actual ) = write( file, m_response_to_reset, sizeof( m_response_to_reset ) ); + if (err) + return false; + std::tie( err, actual ) = write( file, m_write_password, sizeof( m_write_password ) ); + if (err) + return false; + std::tie( err, actual ) = write( file, m_read_password, sizeof( m_read_password ) ); + if (err) + return false; + std::tie( err, actual ) = write( file, m_configuration_password, sizeof( m_configuration_password ) ); + if (err) + return false; + std::tie( err, actual ) = write( file, m_configuration_registers, sizeof( m_configuration_registers ) ); + if (err) + return false; + std::tie( err, actual ) = write( file, m_data, sizeof( m_data ) ); + if (err) + return false; + + return true; } |