#
# Copyright 2008 Sony Corporation
# 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 names of the copyright holders nor the names of their
#     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 OWNER 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.
#
#
# This file is part of an OpenSSL ENGINE 'cell' which utilizes SPEs of
# Cell/B.E.
#
# Options:
#     DEBUG=1
#     RUNTIME_SYSTEM=<name>


##### BUILD PARAMETERS
# PPE toolchain
CC = $(CROSS)gcc
AR = $(CROSS)ar
RANLIB = $(CROSS)ranlib
EMBEDSPU = $(CROSS)embedspu
CFLAGS = $(ARCH_CFLAGS) $(WARN_CFLAGS) $(OPT_CFLAGS) -fPIC -D_FILE_OFFSET_BITS=64 \
	$(FEATURE_CFLAGS) $(EXTRA_CFLAGS)
LDFLAGS = $(ARCH_CFLAGS) \
	$(FEATURE_LDFLAGS) $(EXTRA_LDFLAGS) -lcrypto -lspe2 -lpthread
OPT_CFLAGS = -O3
WARN_CFLAGS = -Wall -Wmissing-declarations
#ARCH_CFLAGS = -m64

# SPE toolchain
SPU_CROSS = spu-
SPU_CC = $(SPU_CROSS)gcc
SPU_AR = $(SPU_CROSS)ar
SPU_RANLIB = $(SPU_CROSS)ranlib
SPU_CFLAGS = $(SPU_WARN_CFLAGS) $(SPU_OPT_CFLAGS) -DNO_STRINGS_H -DB_ENDIAN \
	$(FEATURE_SPU_CFLAGS) $(EXTRA_SPU_CFLAGS)
SPU_LDFLAGS = -Wl,--start-group -Wl,--gc-sections \
	$(FEATURE_SPU_LDFLAGS) $(EXTRA_SPU_LDFLAGS) $(spe_engine_lib) \
	-Wl,--end-group
SPU_OPT_CFLAGS = -O3 -funroll-all-loops -ftree-vectorize \
	-finline-limit=3000 -ffunction-sections -fdata-sections
SPU_WARN_CFLAGS = -Wall -Wmissing-declarations

# Feature flags
FEATURE_CFLAGS = \
	$(CFLAGS.$(RUNTIME_SYSTEM)) \
	$(DEBUG_CFLAGS.$(DEBUG))
FEATURE_LDFLAGS = \
	$(LDFLAGS.$(RUNTIME_SYSTEM))

FEATURE_SPU_CFLAGS = \
	$(SPU_CFLAGS.$(RUNTIME_SYSTEM)) \
	$(DEBUG_CFLAGS.$(DEBUG))
FEATURE_SPU_LDFLAGS = \
	$(SPU_LDFLAGS.$(RUNTIME_SYSTEM))

# SPU Runtime system support
RUNTIME_SYSTEM = mars

# libspe2 support
CFLAGS.libspe2 =
LDFLAGS.libspe2 =
SPU_CFLAGS.libspe2 =
SPU_LDFLAGS.libspe2 =
ppe_engine_lib_objs.libspe2 = \
	libspe2_task.o \
	libspe2_ppe_queue.o
spe_engine_lib_objs.libspe2 = \
	libspe2_spe_main.spu_o \
	libspe2_spe_queue.spu_o

# MARS support
CFLAGS.mars =
LDFLAGS.mars = -lmars_task
SPU_CFLAGS.mars =
SPU_LDFLAGS.mars = -lmars_task -lmars_base \
	-Wl,--section-start,.init=0x4000
ppe_engine_lib_objs.mars = \
	mars_task.o \
	mars_ppe_queue.o
spe_engine_lib_objs.mars = \
	mars_spe_main.spu_o \
	mars_spe_queue.spu_o

# Debugging support
DEBUG_CFLAGS.1 = -g -DCELL_DEBUG=1

# Other tools
PERL = perl

# OpenSSL sources
OPENSSL_DIR = ../openssl
OPENSSL_INCLUDE_DIR = $(OPENSSL_DIR)/include

# Messages
VERBOSE = 0
Q = $(Q.$(VERBOSE))
Q.0 = -s
Q.1 =


##### TEST/BENCHMARK PARAMETERS
# test/benchmark common parameters
OPENSSL = openssl

TEST_ALL_DIGESTS = \
	md2 md4 md5 sha sha1 sha224 sha256 sha384 sha512 ripemd160
TEST_ALL_CIPHERS = \
	aes-128-cbc aes-128-ecb aes-128-ofb aes-128-cfb aes-128-cfb1 aes-128-cfb8 \
	aes-192-cbc aes-192-ecb aes-192-ofb aes-192-cfb aes-192-cfb1 aes-192-cfb8 \
	aes-256-cbc aes-256-ecb aes-256-ofb aes-256-cfb aes-256-cfb1 aes-256-cfb8

TEST_DIGEST = sha1
TEST_CIPHER = aes-256-cbc

TEST_KEY = $(TEST_CIPHER).key
TEST_IV = $(TEST_CIPHER).iv

TEST_DATA = $(engine)

TEST_ENGINES_DIR = $(PWD)

TEST_TMPDIR = $(PWD)
TEST_TMP_PREFIX = $(TEST_TMPDIR)/engine_cell-

# test parameters
TEST_DIGEST_COMMAND = dgst -$(TEST_DIGEST)
TEST_CIPHER_COMMAND = \
	enc -$(TEST_CIPHER) \
	-K `cat $(TEST_CIPHER).key` -iv `cat $(TEST_CIPHER).iv`

# benchmark parameters
BENCH_DATA_SIZE = 256M
BENCH_THREADS = 1
BENCH_OUTPUT = $(TEST_TMP_PREFIX)bench-%03u.tmp

BENCH_TYPE = 1
BENCH_DIGEST_COMMAND = \
	-dgst $(TEST_DIGEST) -threads $(BENCH_THREADS) \
	$(BENCH_DIGEST_PARAMS.$(BENCH_TYPE))
BENCH_CIPHER_COMMAND = \
	-cipher $(TEST_CIPHER) -threads $(BENCH_THREADS) \
	-key `cat $(TEST_CIPHER).key` -iv `cat $(TEST_CIPHER).iv` \
	$(BENCH_CIPHER_PARAMS.$(BENCH_TYPE))

# benchmark types
#   type 1: /dev/zero -> /dev/null
BENCH_DIGEST_PARAMS.1 = -in /dev/zero -size $(BENCH_DATA_SIZE)
BENCH_CIPHER_PARAMS.1 = -in /dev/zero -out /dev/null -size $(BENCH_DATA_SIZE)
#   type 2: file -> file
BENCH_DIGEST_PARAMS.2 = -in $(TEST_DATA)
BENCH_CIPHER_PARAMS.2 = -in $(TEST_DATA) -out $(BENCH_OUTPUT)


##### DISTRIBUTION PARAMETERS
DIST_BASE = openssl-cell-engine
DIST_VERSION = 1.1.5
DIST_NAME = $(DIST_BASE)-$(DIST_VERSION)
DIST_DIR = $(DIST_NAME)
DIST_TARBALL = $(DIST_NAME).tar.gz


##### LIBRARY/OBJECT/SOURCE FILES
engine = libcell.so
engine_soname = $(engine)

engine_objs = \
	e_cell.o \
	e_cell_err.o

ppe_engine_lib = libcell_ppe.a

ppe_engine_lib_objs = \
	ppe_malloc.o \
	ppe_ciphers.o \
	ppe_digests.o \
	$(ppe_engine_lib_objs.$(RUNTIME_SYSTEM))

ppe_engine_lib_srcs = $(ppe_engine_lib_objs:.o=.c)

spe_engine_lib = libcell_spe.a

spe_engine_lib_objs = \
	spe_dummy.spu_o \
	$(spe_engine_lib_objs.$(RUNTIME_SYSTEM))

spe_engine_progs = \
	$(spe_engine_digest_progs) \
	$(spe_engine_cipher_progs)

spe_engine_digest_progs = \
	spe_md2.spu \
	spe_md4.spu \
	spe_md5.spu \
	spe_sha.spu \
	spe_sha1.spu \
	spe_sha256.spu \
	spe_sha512.spu \
	spe_ripemd.spu

spe_engine_cipher_progs = \
	spe_aes.spu

spe_engine_embeded_progs = $(spe_engine_progs:.spu=.embed_o)

spe_engine_objs = \
	$(spe_engine_digest_objs) \
	$(spe_engine_cipher_objs)

spe_engine_digest_objs = $(spe_engine_digest_progs:.spu=.spu_o)

spe_engine_cipher_objs = $(spe_engine_cipher_progs:.spu=.spu_o)


##### SUFFIX RULES
#  .embed_o  embedded SPE ELF executable
#  .spu      SPE ELF executable
#  .spu_o    SPE ELF object file
.SUFFIXES: .embed_o .spu .spu_o

.c.spu_o:
	$(SPU_CC) -c $(SPU_CFLAGS) -o $@ $<

.spu_o.spu:
	$(SPU_CC) -o $@ $< $(SPU_LDFLAGS)

.spu.embed_o:
	$(EMBEDSPU) $(CFLAGS) $*_elf $< $@

##### DON'T REMOVE INTERMEDIATE FILES FOR DEBUGGING
.SECONDARY:

##### DEFAULT TARGET
all: engine


##### RULES FOR ENGINE
engine: $(engine)

# engine
$(engine): $(engine_objs) $(ppe_engine_lib)
	$(CC) -o $@ $(engine_objs) $(ppe_engine_lib) $(LDFLAGS) \
		-shared -Wl,-h$(engine_soname)

# PPE objects
$(ppe_engine_lib): $(ppe_engine_lib_objs) $(spe_engine_embeded_progs)
	-rm -f $@
	$(AR) cr $@ $(ppe_engine_lib_objs) $(spe_engine_embeded_progs)
	$(RANLIB) $@

$(engine_objs) $(ppe_engine_lib_objs): e_cell.h e_cell_err.h

e_cell_err.c e_cell_err.h: e_cell.ec e_cell.c $(ppe_engine_lib_srcs)
	$(PERL) $(OPENSSL_DIR)/util/mkerr.pl \
		-conf e_cell.ec -nostatic -write \
		e_cell.c $(ppe_engine_lib_srcs) 2>/dev/null

libspe2_task.o libspe2_ppe_queue.o: libspe2_runtime.h

mars_task.o mars_ppe_queue.o: mars_runtime.h

# SPE objects
$(spe_engine_progs): $(spe_engine_lib)

$(spe_engine_lib): $(spe_engine_lib_objs)
	-rm -f $@
	$(SPU_AR) cr $@ $(spe_engine_lib_objs)
	$(SPU_RANLIB) $@

$(spe_engine_objs) $(spe_engine_lib_objs): e_cell.h

libspe2_spe_main.o libspe2_spe_queue.spe_o: libspe2_runtime.h

$(spe_engine_digest_objs): spe_digest.c

$(spe_engine_cipher_objs): spe_cipher.c

spe_md2.spu_o: spe_md2.c
	$(SPU_CC) -c $(SPU_CFLAGS) \
		-I$(OPENSSL_DIR)/crypto/md2 \
		-I$(OPENSSL_DIR)/crypto \
		-I$(OPENSSL_INCLUDE_DIR) \
		-o $@ $<

spe_md4.spu_o: spe_md4.c
	$(SPU_CC) -c $(SPU_CFLAGS) \
		-I$(OPENSSL_DIR)/crypto/md4 \
		-I$(OPENSSL_DIR)/crypto \
		-I$(OPENSSL_INCLUDE_DIR) \
		-o $@ $<

spe_md5.spu_o: spe_md5.c
	$(SPU_CC) -c $(SPU_CFLAGS) \
		-I$(OPENSSL_DIR)/crypto/md5 \
		-I$(OPENSSL_DIR)/crypto \
		-I$(OPENSSL_INCLUDE_DIR) \
		-I$(OPENSSL_DIR) \
		-o $@ $<

spe_sha.spu_o: spe_sha.c
	$(SPU_CC) -c $(SPU_CFLAGS) \
		-I$(OPENSSL_DIR)/crypto/sha \
		-I$(OPENSSL_DIR)/crypto \
		-I$(OPENSSL_INCLUDE_DIR) \
		-I$(OPENSSL_DIR) \
		-o $@ $<

spe_sha1.spu_o: spe_sha1.c
	$(SPU_CC) -c $(SPU_CFLAGS) \
		-I$(OPENSSL_DIR)/crypto/sha \
		-I$(OPENSSL_DIR)/crypto \
		-I$(OPENSSL_INCLUDE_DIR) \
		-I$(OPENSSL_DIR) \
		-o $@ $<

spe_sha256.spu_o: spe_sha256.c
	$(SPU_CC) -c $(SPU_CFLAGS) \
		-I$(OPENSSL_DIR)/crypto/sha \
		-I$(OPENSSL_DIR)/crypto \
		-I$(OPENSSL_INCLUDE_DIR) \
		-I$(OPENSSL_DIR) \
		-o $@ $<

spe_sha512.spu_o: spe_sha512.c
	$(SPU_CC) -c $(SPU_CFLAGS) \
		-I$(OPENSSL_DIR)/crypto/sha \
		-I$(OPENSSL_DIR)/crypto \
		-I$(OPENSSL_INCLUDE_DIR) \
		-I$(OPENSSL_DIR) \
		-o $@ $<

spe_ripemd.spu_o: spe_ripemd.c
	$(SPU_CC) -c $(SPU_CFLAGS) \
		-I$(OPENSSL_DIR)/crypto/ripemd \
		-I$(OPENSSL_DIR)/crypto \
		-I$(OPENSSL_INCLUDE_DIR) \
		-o $@ $<

spe_aes.spu_o: spe_aes.c
	$(SPU_CC) -c $(SPU_CFLAGS) \
		-I$(OPENSSL_DIR)/crypto/aes \
		-I$(OPENSSL_DIR)/crypto \
		-I$(OPENSSL_INCLUDE_DIR) \
		-I$(OPENSSL_DIR) \
		-o $@ $<


##### RULES FOR CLEAN UP
clean:
	-rm -f *.o *.spu_o *.embed_o *.spu *.a $(engine) *.tmp *.key *.iv
	-rm -f e_cell_err.c e_cell_err.h
	-rm -f bench_digest bench_cipher
	-rm -f $(TEST_TMP_PREFIX)*.tmp $(DIST_TARBALL)
	-rm -rf $(DIST_DIR)

distclean: clean
	-rm -f *~ *.log


##### RULES FOR DISTRIBUTION
dist: $(DIST_TARBALL)

$(DIST_TARBALL): distclean
	-rm -rf $(DIST_DIR)
	mkdir $(DIST_DIR)
	cp -a README LICENSE Makefile *.c *.h *.ec $(DIST_BASE).spec $(DIST_DIR)
	find $(DIST_DIR) -type f | sort | tar zcf $@ -T -
	-rm -rf $(DIST_DIR)


##### RULES FOR TESTS
check: check-all-digests check-all-ciphers

check-all-digests:
	@for a in $(TEST_ALL_DIGESTS); do \
		if ! $(MAKE) $(Q) check-digest TEST_DIGEST=$$a; then \
			__failed="$$__failed $$a"; \
		fi; \
	done; \
	[ -z "$$__failed" ] || ( echo "FAIL: $$__failed"; exit 1 )

check-digest: all
	@echo '==========================================================='
	@echo 'Algorithm: $(TEST_DIGEST)'
	@echo '-----------------------------------------------------------'
	@echo "No SPE engine:"
	@$(MAKE) $(Q) TEST_COMMAND="$(TEST_DIGEST_COMMAND) \
			< $(TEST_DATA)" check-command | \
		tee $(TEST_TMP_PREFIX)no-spe.tmp
	@echo '-----------------------------------------------------------'
	@echo "SPE engine:"
	@$(MAKE) $(Q) TEST_COMMAND="$(TEST_DIGEST_COMMAND) -engine cell \
			< $(TEST_DATA)" check-command | \
		tee $(TEST_TMP_PREFIX)spe.tmp
	@diff $(TEST_TMP_PREFIX)no-spe.tmp \
			$(TEST_TMP_PREFIX)spe.tmp > /dev/null || \
		( echo 'Result mismatch.'; exit 1 )

check-all-ciphers:
	@for a in $(TEST_ALL_CIPHERS); do \
		if ! $(MAKE) $(Q) check-cipher TEST_CIPHER=$$a; then \
			__failed="$$__failed $$a"; \
		fi; \
	done; \
	[ -z "$$__failed" ] || ( echo "FAIL: $$__failed"; exit 1 )

check-cipher: all $(TEST_KEY) $(TEST_IV)
	@echo '==========================================================='
	@echo 'Algorithm: $(TEST_CIPHER)'
	@echo '-----------------------------------------------------------'
	@echo "No Cell/B.E. engine (encryption):"
	@$(MAKE) $(Q) TEST_COMMAND="$(TEST_CIPHER_COMMAND) -e \
			< $(TEST_DATA) \
			> $(TEST_TMP_PREFIX)no-cell-e.tmp" check-command
	@echo '-----------------------------------------------------------'
	@echo "No Cell/B.E. engine (decryption):"
	@$(MAKE) $(Q) TEST_COMMAND="$(TEST_CIPHER_COMMAND) -d \
			< $(TEST_TMP_PREFIX)no-cell-e.tmp \
			> $(TEST_TMP_PREFIX)no-cell-d.tmp" check-command
	@echo '-----------------------------------------------------------'
	@echo "Cell/B.E. engine (encryption):"
	@$(MAKE) $(Q) TEST_COMMAND="$(TEST_CIPHER_COMMAND) -e -engine cell \
			< $(TEST_DATA) \
			> $(TEST_TMP_PREFIX)cell-e.tmp" check-command
	@diff $(TEST_TMP_PREFIX)no-cell-e.tmp \
			$(TEST_TMP_PREFIX)cell-e.tmp >/dev/null || \
		( echo 'Encrypted result mismatch.'; exit 1 )
	@echo '-----------------------------------------------------------'
	@echo "Cell/B.E. engine (decryption):"
	@$(MAKE) $(Q) TEST_COMMAND="$(TEST_CIPHER_COMMAND) -d -engine cell \
			< $(TEST_TMP_PREFIX)cell-e.tmp \
			> $(TEST_TMP_PREFIX)cell-d.tmp" check-command
	@diff $(TEST_DATA) $(TEST_TMP_PREFIX)cell-d.tmp >/dev/null || \
		( echo 'Decrypted result mismatch.'; exit 1 )

check-command:
	OPENSSL_ENGINES=$(TEST_ENGINES_DIR) time -p $(OPENSSL) $(TEST_COMMAND)


##### RULES FOR BENCHMARK
bench: bench-all-digests bench-all-ciphers

bench-all-digests:
	@for a in $(TEST_ALL_DIGESTS); do \
		$(MAKE) $(Q) bench-digest TEST_DIGEST=$$a; \
	done

bench-digest: bench-no-engine-digest bench-engine-digest

bench-no-engine-digest: all
	@echo '==========================================================='
	@echo 'Algorithm: $(TEST_DIGEST)'
	@echo '-----------------------------------------------------------'
	@echo "No Cell/B.E. engine:"
	@$(MAKE) $(Q) BENCH_DIGEST_COMMAND="$(BENCH_DIGEST_COMMAND)" \
			bench-digest-command

bench-engine-digest: all
	@echo '==========================================================='
	@echo 'Algorithm: $(TEST_DIGEST)'
	@echo '-----------------------------------------------------------'
	@echo "Cell/B.E. engine:"
	@$(MAKE) $(Q) BENCH_DIGEST_COMMAND="$(BENCH_DIGEST_COMMAND) -engine cell" \
			bench-digest-command

bench-digest-command: bench_digest all
	OPENSSL_ENGINES=$(TEST_ENGINES_DIR) \
		time -p ./bench_digest $(BENCH_DIGEST_COMMAND)

bench_digest: bench_digest.o
	$(CC) -o $@ $< $(ppe_engine_lib) $(LDFLAGS)

bench_digest.o: e_cell.h bench_digest.c bench_utils.c

bench-all-ciphers:
	@for a in $(TEST_ALL_CIPHERS); do \
		$(MAKE) $(Q) bench-cipher TEST_CIPHER=$$a; \
	done

bench-cipher: bench-no-engine-cipher bench-engine-cipher

bench-no-engine-cipher: all $(TEST_KEY) $(TEST_IV)
	@echo '==========================================================='
	@echo 'Algorithm: $(TEST_CIPHER)'
	@echo '-----------------------------------------------------------'
	@echo "No Cell/B.E. engine (encryption):"
	@$(MAKE) $(Q) BENCH_CIPHER_COMMAND="$(BENCH_CIPHER_COMMAND)" \
			bench-cipher-command

bench-engine-cipher: all $(TEST_KEY) $(TEST_IV)
	@echo '==========================================================='
	@echo 'Algorithm: $(TEST_CIPHER)'
	@echo '-----------------------------------------------------------'
	@echo "Cell/B.E. engine (encryption):"
	@$(MAKE) $(Q) BENCH_CIPHER_COMMAND="$(BENCH_CIPHER_COMMAND) -engine cell" \
		bench-cipher-command

bench-cipher-command: bench_cipher all $(TEST_KEY) $(TEST_IV)
	OPENSSL_ENGINES=$(TEST_ENGINES_DIR) \
		time -p ./bench_cipher $(BENCH_CIPHER_COMMAND)

bench_cipher: bench_cipher.o
	$(CC) -o $@ $< $(ppe_engine_lib) $(LDFLAGS)

bench_cipher.o: e_cell.h bench_cipher.c bench_utils.c


##### RULES FOR TEST/BENCHMARK DATA
.SUFFIXES: .key .iv

128.key 128.iv 192.key 192.iv 256.key 256.iv:
	$(OPENSSL) rand `expr $* / 8` | hexdump -v -e '/1 "%02x"' > $@

aes-128-cbc.key aes-128-ecb.key aes-128-ofb.key \
	aes-128-cfb.key aes-128-cfb1.key aes-128-cfb8.key: 128.key
	ln -fs $< $@

aes-192-cbc.key aes-192-ecb.key aes-192-ofb.key \
	aes-192-cfb.key aes-192-cfb1.key aes-192-cfb8.key: 192.key
	ln -fs $< $@

aes-256-cbc.key aes-256-ecb.key aes-256-ofb.key \
	aes-256-cfb.key aes-256-cfb1.key aes-256-cfb8.key: 256.key
	ln -fs $< $@

aes-128-cbc.iv aes-128-ecb.iv aes-128-ofb.iv \
	aes-128-cfb.iv aes-128-cfb1.iv aes-128-cfb8.iv \
	aes-192-cbc.iv aes-192-ecb.iv aes-192-ofb.iv \
	aes-192-cfb.iv aes-192-cfb1.iv aes-192-cfb8.iv \
	aes-256-cbc.iv aes-256-ecb.iv aes-256-ofb.iv \
	aes-256-cfb.iv aes-256-cfb1.iv aes-256-cfb8.iv: 128.iv
	ln -fs $< $@

##### EOF
