# Copyright (c) 2020 P&E Microcomputer Systems, Inc
# All rights reserved.
# Visit us at www.pemicro.com
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# o Redistributions of source code must retain the above copyright notice, this list
#   of conditions and the following disclaimer.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

# Version 1.00

'''
 Authenticate device secured in Password mode
 This script requires two parameters:
 -hardwareid: the debug hardware IP address, name, serial number, or port name (e.g. -hardwareid=10.0.4.17)
 -password: the 16 byte hexadecimal preconfigured password needed to authenticate the device (e.g. -password=0123456789ABCDEF0123456789ABCDEF)
'''

from pemicroprobewrapper import pemicroProbeWrapper
from pemicrounitacmp import pemicroUnitAcmp
import sys


''' Get parameters (e.g. -hardwareid=10.0.4.17 -password=0123456789ABCDEF0123456789ABCDEF) '''

print('')
print('Authenticating S32K3xx in Password Mode.')
print('')

hardwareid=''
password=''
for i in range(1, len(sys.argv)):
    if sys.argv[i].split('=')[0] == "-hardwareid":
        if len(sys.argv[i].split('=')) > 1: # if hardware ID is given
            hardwareid = sys.argv[i].split('=')[1]

    elif sys.argv[i].split('=')[0] == "-password":
        if len(sys.argv[i].split('=')) > 1: # if password is given
            password = sys.argv[i].split('=')[1]
            if len(password) != 32:
                sys.exit('Error - Password must be 16 bytes (32 characters) long, in hexadecimal format (e.g. -password=0123456789ABCDEF0123456789ABCDEF).')
            
if (hardwareid == '') or (password == ''):
    if hardwareid == '':
        print('Error - Please specify the debug hardware IP address, name, serial number, or generic port name (such as USB1).')
        print('Examples of specifying the HW Identifier :')
        print('')
        print('             -hardwareid=USB1                ; Opens First PEmicro HW detected on USB bus')
        print('             -hardwareid=PEM592731           ; Opens PEmicro HW with Serial Number PEM592731')
        print('             -hardwareid=10.0.4.17           ; Opens PEmicro HW with IP 10.0.4.17')
        print('             -hardwareid=MyCyclone           ; Opens PEmicro HW with assigned Name MyCyclone')
        print('             -hardwareid=ETHERNET1           ; Opens First PEmicro HW detected on Ethernet')
        print('')
        print('Current List of autodetected PEmicro Hardware :')
        print('')
        pemicroUnitAcmp().listPortsDescription()
    if password == '':
        print('Error - Please specify the 16-byte hexadecimal password needed to authenticate the device.')
        print('Example of specifying the Password :')
        print('')
        print('             -password=0123456789ABCDEF0123456789ABCDEF')
        print('')
    sys.exit(1)


''' Power up DAP main system domain and debug domain. '''

myprobe = pemicroProbeWrapper(0x07,hardwareid)

if not myprobe.connectedToDebugHardware:
    print ('Error ... Unable to connect to PEMicro HW',hardwareid)
    sys.exit(1)
else:
    print ('Connecting to debug hardware ... ok.')    

if not myprobe.connectedToTarget:
    print ('Error ... Unable to establish communications with target MCU')
    sys.exit(1)
else:
    print ('Requesting Debug Module Power On via SWD ... ok.')


''' Write the password to the KEYRESPn registers. The offset for KEYRESPn register is 0x40 + n*0x4. '''

# Test that we can talk to the SDA AP by reading its ID value (should read 0x001C0040)
targetRead = myprobe.read_reg(0xFC)
if not ((targetRead==0x001C0040) and (myprobe.pemicroProbeLibrary.lastSwdStatus()==0x04)):
    print ('Reading SDA AP ID Register ... ERROR !!!!!!!!!!!!!!!!!!!!! . (', hex(targetRead),')')
    sys.exit(1)
else:
    print ('Reading SDA AP ID Register ... ok.')

    # Adjust endianness of password
for n in range(4):
    index = n*8
    keystr = password[index+6:index+8] + password[index+4:index+6] + password[index+2:index+4] + password[index+0:index+2]
    keyval = int(keystr, 16)
    
    # Write to KEYRESPn register
    myprobe.write_reg(0x40+n*4,keyval)
    targetRead = myprobe.read_reg(0x40+n*4)
    if (not ((targetRead==keyval) and (myprobe.pemicroProbeLibrary.lastSwdStatus()==0x04))):
        print ('Reading/Writing SDA AP KEYRESP'+str(n)+' ... ERROR !!!!!!!!!!!!!!!!!!!!! . (', hex(targetRead),')')
        sys.exit(1)
    else:
        print ('Reading/Writing SDA AP KEYRESP'+str(n)+' ... ok.')
    
    
''' Trigger the authentication by setting HSEAUTHREQ (bit 0) in AUTHCTL register (offset 0x4) '''

myprobe.write_reg(0x04,0x00000001) #set HSEAUTHREQ (0)
print ('Writing SDA AP AUTHCTL.HSEAUTHREQ ... ok.')


''' Set GDBGEN (bit 4) and CDBGEN (bit 28) in DBGENCTRL register (offset 0x80 in SDA memory map). Setting these bits ensure that the cores can be debugged. '''

myprobe.write_reg(0x80,0x10000010) #set GDBGEN (4), CDBGEN (28)
print ('Writing SDA AP DBGENCTRL.GDBGEN and DBGENCTRL.CDBGEN ... ok.')

# Test that we can talk to the SDA AP again by reading its ID value (should read 0x001C0040). If a wrong password is provided, this test will fail. 
targetRead = myprobe.read_reg(0xFC)
if not ((targetRead==0x001C0040) and (myprobe.pemicroProbeLibrary.lastSwdStatus()==0x04)):
    print ('Reading SDA AP ID Register ... ERROR !!!!!!!!!!!!!!!!!!!!! . (', hex(targetRead),')')
    sys.exit(1)
else:
    print ('Reading SDA AP ID Register ... ok.')

''' AUTHSTTS SDA register (offset 0) shows the authentication status. If the authentication was successful, APPDBGEN (bit 30) will be set. Otherwise, it remains cleared. On successful authentication, the debugger can access the application cores. '''
AUTHSTTS = myprobe.read_reg(0x0)
if ((AUTHSTTS & 0x60000000) != 0x60000000):
    print ('Checking Assertion of SDA AP AUTHSTTS.APPDBGEN ... ERROR !!!!!!!!!!!!!!!!!!!!! . (', hex(AUTHSTTS),')')
    sys.exit(1)
else:
    print ('Checking Assertion of SDA AP AUTHSTTS.APPDBGEN ... ok.')

myprobe.close()
del myprobe
print ('Success.') 
sys.exit(0)
