aboutsummaryrefslogtreecommitdiff
path: root/raw-wiki-dump/GitRepositories%2Fcore%2Fcomm%2Fcoretest
diff options
context:
space:
mode:
Diffstat (limited to 'raw-wiki-dump/GitRepositories%2Fcore%2Fcomm%2Fcoretest')
-rw-r--r--raw-wiki-dump/GitRepositories%2Fcore%2Fcomm%2Fcoretest211
1 files changed, 0 insertions, 211 deletions
diff --git a/raw-wiki-dump/GitRepositories%2Fcore%2Fcomm%2Fcoretest b/raw-wiki-dump/GitRepositories%2Fcore%2Fcomm%2Fcoretest
deleted file mode 100644
index ad8749e..0000000
--- a/raw-wiki-dump/GitRepositories%2Fcore%2Fcomm%2Fcoretest
+++ /dev/null
@@ -1,211 +0,0 @@
-{{{
-#!htmlcomment
-
-This page is maintained automatically by a script. Don't modify this page by hand,
-your changes will just be overwritten the next time the script runs. Talk to your
-Friendly Neighborhood Repository Maintainer if you need to change something here.
-
-}}}
-
-{{{
-#!html
-<h1>coretest</h1>
-
-<p>Test platform for the <a href="https://cryptech.is/">Cryptech Open HSM project</a>.</p>
-
-<p>(_Note:The Cryptech certificate is by choice not from a CA and therefore
-not in your brower trust store._)</p>
-
-<h2>Description</h2>
-
-<p>This platform and hardware design is used to functionally verfiy cores
-developed in the Cryptech Open HSM project. The test core itself
-contains just enough functionality to be able to verify that the SW in
-the host computer can talk to the core in the FPGA by reading and
-writing 32 bit data words to given addresses.</p>
-
-<p>This project includes cores in Verilog, a testbench as well as host SW
-to talk to the core.</p>
-
-<h2>Architecture</h2>
-
-<p>The coretest consists of three state machines:</p>
-
-<h3>rx_engine</h3>
-
-<p>Handles receiving command messages from the host. Awaits SYN signals from
-the host interface and reads bytes when SYN is asserted. For each byte
-the rx_engine assetts and ACK and waits for the SYN to be asserted. When
-a EOC byte has been detected the rx_engine signals the test_engine that
-there is a new command available in the rx_buffer.</p>
-
-<h3>tx_engine</h3>
-
-<p>Handles transmitting response messages to the host. When the test_engine
-signals that there is a new response in the tx_buffer the tx_engine will
-start transmitting all bytes up to and including the EOR byte it is
-expecting in the tx_buffer. The transmission is done by asserting SYN
-awaiting ACK, deasserting SYN and moving to the next byte before
-asserting SYN again. This process is repeated until all bytes has been
-transmitted.</p>
-
-<h3>test_engine</h3>
-
-<p>Performs the parsing of commands from the host. Known read or write
-commands are used to test the core to be tested. The response from the
-core is collected and the appropriate response is stored in the
-tx_buffer. The test_engine then signals the tx_engine that there is a
-new response message to be transmitted.</p>
-
-<h2>Interfaces</h2>
-
-<p>The host communication interface is a byte wide data interface with
-SYN-ACK handshake for each byte.</p>
-
-<p>The interface to the core to be tested is a memory like
-interface with chip select and write enable. The data width is 32-bits
-and the address is 16-bits.</p>
-
-<h2>Core under test</h2>
-
-<p>The core under test is expected to have a simple memory like interface
-with chip select (cs), write enable (we) signal ports, 16-bit address
-port and separate 32-bit data ports for read and write data. The core is
-also expected to have an error signal port that informs the master if
-any read or write commands given cannot be performed by the core.</p>
-
-<p><strong><em>Note:</em></strong>
-The core reset signal is expected to by active high. The
-core reset signal should be connected to the coretest core_reset
-port, not to system reset.</p>
-
-<h2>Protocol</h2>
-
-<p>Coretest uses a simple command-response protocol to allow a host to
-control the test functionality.</p>
-
-<p>The command messages are sent as a sequence of bytes with a command byte
-followed by zero or more arguments. The response consists of a response
-code byte followed by zero or more data fields.</p>
-
-<p>The start of a command is signalled by a Start of Command (SOC)
-byte. The end of a command is signalled by a End of Command (EOC)
-byte. These bytes are:</p>
-
-<ul>
-<li>SOC: 0x55</li>
-<li>EOC: 0xaa</li>
-</ul>
-
-<p>The start of a response is signalled by a Start of Response (SOR)
-byte. The end of a response is signalled by a End of Respons (EOC)
-byte. These bytes are:</p>
-
-<ul>
-<li>SOR: 0xaa</li>
-<li>EOR: 0x55</li>
-</ul>
-
-<p><strong><em>The commands accepted are:</em></strong>
- - RESET_CMD. Reset the core being tested. Message length is 3 bytes
- including SOC and EOC.
- - SOC
- - 0x01 opcode
- - EOC</p>
-
-<ul>
-<li><p>READ_CMD. Read a 32-bit data word from a given address. Message
-length is 5 bytes including SOC and EOC.</p>
-
-<ul>
-<li>SOC</li>
-<li>0x10 opcode</li>
-<li>16-bit address in MSB format</li>
-<li>EOC</li>
-</ul></li>
-<li><p>WRITE_CMD. Write a given data word to a given address. Message
-length is 9 bytes including SOC and EOC.</p>
-
-<ul>
-<li>SOC</li>
-<li>0x11 opcode</li>
-<li>16-bit address in MSB format</li>
-<li>32-bit data in MSB format</li>
-<li>EOC</li>
-</ul></li>
-</ul>
-
-<p><strong><em>The possible responses are:</em></strong>
- - UNKNOWN. Unknown command received. Message length is 4 bytes
- including SOR and EOR.
- - SOR
- - 0xfe response code
- - Received command
- - EOR</p>
-
-<ul>
-<li><p>ERROR. Known but unsuccessful command as signalled by the
-core. Caused for example by a write command to read only
-register. Message length is 4 bytes including SOR and EOR.</p>
-
-<ul>
-<li>SOR</li>
-<li>0xfd response code</li>
-<li>command received</li>
-<li>EOR</li>
-</ul></li>
-<li><p>READ_OK. Sent after successful read operation. Message length is 9
-bytes including SOR and EOR .</p>
-
-<ul>
-<li>SOR</li>
-<li>0x7f response code</li>
-<li>16-bit address in MSB format</li>
-<li>32-bit data in MSB format</li>
-<li>EOR</li>
-</ul></li>
-<li><p>WRITE_OK. Sent after successful write operation. Message length is 5
-bytes including SOR and EOR </p>
-
-<ul>
-<li>SOR</li>
-<li>0x7e response code</li>
-<li>16-bit address in MSB format</li>
-<li>EOR</li>
-</ul></li>
-<li><p>RESET_OK. Sent after successful reset operation. Message length is 3
-bytes including SOR and EOR.</p>
-
-<ul>
-<li>SOR</li>
-<li>0x7d response code</li>
-<li>EOR</li>
-</ul></li>
-</ul>
-
-<h2>Status</h2>
-
-<p><strong><em>(2014-02-11):</em></strong></p>
-
-<p>Added information about the architecture and protocols. Updated the
-command and response with explicit read and write ok responses. Some
-cleanup of the description.</p>
-
-<p>Completed first draft of the RTL for coretest. The RTL is not debugged
-and has not been synthesized. We need to add a testbench and a simple
-test core.</p>
-
-<p>Added a simple test core.
-Adding initial version of UART core that will be used for the host
-interface.</p>
-
-<p><strong><em>(2014-02-10):</em></strong></p>
-
-<p>Initial version of the project. Based on previous cttest project but
-renamed and with new (ideas) about the test architecture. Specified
-command and response protocol.</p>
-}}}
-
-[[RepositoryIndex(format=table,glob=core/comm/coretest)]]
-
-|| Clone `https://git.cryptech.is/core/comm/coretest.git` ||
} /* Literal.String.Escape */ .highlight .sh { color: #D20; background-color: #FFF0F0 } /* Literal.String.Heredoc */ .highlight .si { color: #33B; background-color: #FFF0F0 } /* Literal.String.Interpol */ .highlight .sx { color: #2B2; background-color: #F0FFF0 } /* Literal.String.Other */ .highlight .sr { color: #080; background-color: #FFF0FF } /* Literal.String.Regex */ .highlight .s1 { color: #D20; background-color: #FFF0F0 } /* Literal.String.Single */ .highlight .ss { color: #A60; background-color: #FFF0F0 } /* Literal.String.Symbol */ .highlight .bp { color: #038 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #06B; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #369 } /* Name.Variable.Class */ .highlight .vg { color: #D70 } /* Name.Variable.Global */ .highlight .vi { color: #33B } /* Name.Variable.Instance */ .highlight .vm { color: #369 } /* Name.Variable.Magic */ .highlight .il { color: #00D; font-weight: bold } /* Literal.Number.Integer.Long */
#!/usr/bin/env python
#
# Copyright (c) 2016-2017, NORDUnet A/S All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# - Redistributions of source code must retain the above copyright notice,
#   this list of conditions and the following disclaimer.
#
# - Redistributions in binary form must reproduce the above copyright
#   notice, this list of conditions and the following disclaimer in the
#   documentation and/or other materials provided with the distribution.
#
# - Neither the name of the NORDUnet nor the names of its contributors may
#   be used to endorse or promote products derived from this software
#   without specific prior written permission.
#
# 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.

"""
Utility to upload a new firmware image or FPGA bitstream.
"""

import os
import sys
import time
import struct
import serial
import getpass
import os.path
import tarfile
import argparse
import platform

from binascii import crc32

FIRMWARE_CHUNK_SIZE = 4096
FPGA_CHUNK_SIZE = 4096


def parse_args():
    """
    Parse the command line arguments
    """

    share_directory = "/usr/share" if platform.system() == "Linux" else "/usr/local/share"

    default_tarball = os.path.join(share_directory, "cryptech-alpha-firmware.tar.gz")

    if not os.path.exists(default_tarball):
        default_tarball = None

    parser = argparse.ArgumentParser(description = __doc__,
                                     formatter_class = argparse.ArgumentDefaultsHelpFormatter,
                                     )

    parser.add_argument("-d", "--device",
                        default = os.getenv("CRYPTECH_CTY_CLIENT_SERIAL_DEVICE", "/dev/ttyUSB0"),
                        help = "Name of management port USB serial device",
                        )

    parser.add_argument("--firmware-tarball",
                        type = argparse.FileType("rb"),
                        default = default_tarball,
                        help = "Location of firmware tarball",
                        )

    parser.add_argument("--username",
                        choices = ("so", "wheel"),
                        default = "so",
                        help = "Username to use when logging into the HSM",
                        )

    parser.add_argument("--pin",
                        help = "PIN to use when logging into the HSM",
                        )

    parser.add_argument("--separate-pins",
                        action = "store_true",
                        help = "Prompt separately for each PIN required during firmware upload")

    actions = parser.add_mutually_exclusive_group(required = True)
    actions.add_argument("--fpga",
                         action = "store_true",
                         help = "Upload FPGA bitstream",
                         )
    actions.add_argument("--firmware", "--hsm",
                         action = "store_true",
                         help = "Upload HSM firmware image",
                         )
    actions.add_argument("--bootloader",
                         action = "store_true",
                         help = "Upload bootloader image (dangerous!)",
                         )

    parser.add_argument("--simon-says-whack-my-bootloader",
                        action = "store_true",
                        help = "Confirm that you really want to risk bricking the HSM",
                        )

    parser.add_argument("-i", "--explicit-image",
                        type = argparse.FileType("rb"),
                        help = "Explicit source image file for upload, overrides firmware tarball")

    parser.add_argument("--debug",
                        action = "store_true",
                        help = "Enable debugging of upload protocol",
                        )

    parser.add_argument("-q", "--quiet",
                        action = "store_true",
                        help = "Only report errors",
                        )

    return parser.parse_args()


def _write(dst, data):
    numeric = isinstance(data, (int, long))
    if numeric:
        data = struct.pack("<I", data)
    dst.write(data)
    dst.flush()
    if args.debug:
        if numeric:
            print("Wrote 0x{!s}".format(data.encode("hex")))
        else:
            print("Wrote {!r}".format(data))

def _read(dst):
    res = ""
    x = dst.read(1)
    while not x:
        x = dst.read(1)
    while x:
        res += x
        x = dst.read(1)
    if args.debug:
        print ("Read {!r}".format(res))
    return res

def _execute(dst, cmd):
    global args
    _write(dst, "\r")
    prompt = _read(dst)
    #if prompt.endswith("This is the bootloader speaking..."):
    #    prompt = _read(dst)
    if prompt.endswith("Username: "):
        _write(dst, args.username + "\r")
        prompt = _read(dst)
        if prompt.endswith("Password: "):
            if not args.pin or args.separate_pins:
                args.pin = getpass.getpass("{} PIN: ".format(args.username))
            _write(dst, args.pin + "\r")
            prompt = _read(dst)
    if not prompt.endswith(("> ", "# ")):
        print("Device does not seem to be ready for a file transfer (got {!r})".format(prompt))
        return prompt
    _write(dst, cmd + "\r")
    response = _read(dst)
    return response

def send_file(src, size, args, dst):
    if args.fpga:
        chunk_size = FPGA_CHUNK_SIZE
        response = _execute(dst, "fpga bitstream upload")
    elif args.firmware:
        chunk_size = FIRMWARE_CHUNK_SIZE
        response = _execute(dst, "firmware upload")
        if "Rebooting" in response:
            response = _execute(dst, "firmware upload")
    elif args.bootloader:
        chunk_size = FIRMWARE_CHUNK_SIZE
        response = _execute(dst, "bootloader upload")
    if "Access denied" in response:
        print "Access denied"
        return False
    if not "OK" in response:
        print("Device did not accept the upload command (got {!r})".format(response))
        return False

    dst.timeout = 0.001
    crc = 0
    counter = 0
    # 1. Write size of file (4 bytes)
    _write(dst, struct.pack("<I", size))
    response = _read(dst)
    if not response.startswith("Send "):
        print response
        return False

    # 2. Write file contents while calculating CRC-32
    chunks = int((size + chunk_size - 1) / chunk_size)
    for counter in xrange(chunks):
        data = src.read(chunk_size)
        dst.write(data)
        dst.flush()
        if not args.quiet:
            print("Wrote {!s} bytes (chunk {!s}/{!s})".format(len(data), counter + 1, chunks))
        # read ACK (a counter of number of 4k chunks received)
        ack_bytes = ""
        while len(ack_bytes) < 4:
            ack_bytes += _read(dst)
        ack = struct.unpack("<I", ack_bytes[:4])[0]
        if ack != counter + 1:
            print("ERROR: Did not receive the expected counter as ACK (got {!r}/{!r}, not {!r})".format(ack, ack_bytes, counter))
            return False
        counter += 1

        crc = crc32(data, crc) & 0xffffffff

    # 3. Write CRC-32 (4 bytes)
    _write(dst, struct.pack("<I", crc))
    response = _read(dst)
    if not args.quiet:
        print response

    src.close()

    if args.fpga:
        # tell the fpga to read its new configuration
        _execute(dst, "fpga reset")
        # log out of the CLI
        # (firmware/bootloader upgrades reboot, don't need an exit)
        _execute(dst, "exit")

    return True


dire_bootloader_warning = '''
                            WARNING

Updating the bootloader risks bricking your HSM!  If something goes
badly wrong here, or you upload a bad bootloader image, you will not
be able to recover without an ST-LINK programmer.

In most cases a normal "--firmware" upgrade should be all that is
necessary to bring your HSM up to date, there is seldom any real need
to update the bootloader.

Do not proceed with this unless you REALLY know what you are doing.

If you got here by accident, ^C now, without answering the PIN prompt.
'''


def main():
    global args
    args = parse_args()
    

    if args.bootloader:
        if not args.simon_says_whack_my_bootloader:
            sys.exit("You didn't say \"Simon says\"")
        print dire_bootloader_warning
        args.pin = None

    if args.explicit_image is None and args.firmware_tarball is None:
        sys.exit("No source file specified for upload and firmware tarball not found")

    if args.explicit_image:
        src = args.explicit_image       # file-like object, thanks to argparse
        size = os.fstat(src.fileno()).st_size
        if size == 0:                   # Flashing from stdin won't work, sorry
            sys.exit("Can't flash from a pipe or zero-length file")
        if not args.quiet:
            print "Uploading from explicitly-specified file {}".format(args.explicit_image.name)

    else:
        tar = tarfile.open(fileobj = args.firmware_tarball)
        if not args.quiet:
            print "Firmware tarball {} content:".format(args.firmware_tarball.name)
        tar.list(True)
        if args.fpga:
            name = "alpha_fmc.bit"
        elif args.firmware:
            name = "hsm.bin"
        elif args.bootloader:
            name = "bootloader.bin"
        else:
            # Somebody updated other part of this script without updating this part :(
            sys.exit("Don't know which component to select from firmware tarball, sorry")
        try:
            size = tar.getmember(name).size
        except KeyError:
            sys.exit("Expected component {} missing from firmware tarball {}".format(name, args.firmware_tarball.name))
        src = tar.extractfile(name)
        if not args.quiet:
            print "Uploading {} from {}".format(name, args.firmware_tarball.name)

    if not args.quiet:
        print "Initializing serial port and synchronizing with HSM, this may take a few seconds"
    dst  = serial.Serial(args.device, 921600, timeout = 1)
    send_file(src, size, args, dst)
    dst.close()


if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        pass