Merge pull request #10469 from ARMmbed/release-candidate

Release candidate for mbed-os-5.12.2
mbed-os-5.12 mbed-os-5.12.2
Anna Bridge 2019-04-26 11:49:10 +01:00 committed by GitHub
commit cfa7938a4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
419 changed files with 177807 additions and 151753 deletions

View File

@ -14,59 +14,103 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
language: python language: sh
python: 2.7 os: linux
dist: xenial
env: env:
global: global:
- > - deps_url="https://mbed-os-ci.s3-eu-west-1.amazonaws.com/jenkins-ci/deps"
STATUS=$'curl -so/dev/null --user "$MBED_BOT" --request POST - deps_dir="${HOME}/.cache/deps"
https://api.github.com/repos/$TRAVIS_REPO_SLUG/statuses/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT}
--data @- << DATA\n{
"state": "$0",
"description": "$1",
"context": "travis-ci/$NAME",
"target_url": "https://travis-ci.org/$TRAVIS_REPO_SLUG/jobs/$TRAVIS_JOB_ID"
}\nDATA'
cache: cache:
pip: true pip: true
directories: directories:
- $HOME/.cache/apt - ${HOME}/.cache/deps
- $HOME/gcc-arm-none-eabi-6-2017-q2-update
before_install: before_install:
- bash -c "$STATUS" pending "Local $NAME testing is in progress" - source tools/test/travis-ci/functions.sh
# Make sure pipefail - set_status "pending" "Test started."
- set -o pipefail
# Setup apt to cache
- mkdir -p $HOME/.cache/apt/partial
- sudo rm -rf /var/cache/apt/archives
- sudo ln -s $HOME/.cache/apt /var/cache/apt/archives
# Setup ppa to make sure arm-none-eabi-gcc is correct version
- sudo add-apt-repository -y ppa:team-gcc-arm-embedded/ppa
- sudo add-apt-repository -y ppa:deadsnakes/ppa
# workaround for https://travis-ci.community/t/then-sudo-apt-get-update-failed-public-key-is-not-available-no-pubkey-6b05f25d762e3157-in-ubuntu-xenial/1728
- sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 762E3157
# Loop until update succeeds (timeouts can occur)
- travis_retry $(! sudo apt-get update 2>&1 |grep Failed)
after_success: after_success:
- bash -c "$STATUS" success "Local $NAME testing has passed" - set_status "success" "Success!"
after_failure: after_failure:
- bash -c "$STATUS" failure "Local $NAME testing has failed" - set_status "failure" "Test failed."
matrix: matrix:
include: include:
- env:
- NAME=docs ### Basic Tests ###
- &basic-vm
stage: "Basic"
name: "file attributes"
env: NAME=gitattributestest
script:
- git diff --exit-code
- <<: *basic-vm
name: "license check"
env: NAME=licence_check
script:
- |
! grep --recursive --max-count=100 --ignore-case --exclude .travis.yml \
"gnu general\|gnu lesser\|lesser general\|public license"
- <<: *basic-vm
name: "include check"
env: NAME=include_check
script:
- |
! git grep '^#include\s["'"']mbed.h['"'"]$' -- '*.c' '*.h' '*.cpp' '*.hpp' \
':!*platform_mbed.h' ':!*TESTS/*' ':!TEST_APPS/' ':!UNITTESTS/' \
':!*tests/*' ':!*targets/*' ':!*TARGET_*' ':!*unsupported/*'
### Docs Tests ###
- &docs-vm
stage: "Docs"
name: "astyle"
env: NAME=astyle
install:
- >-
curl -L0 https://mbed-os.s3-eu-west-1.amazonaws.com/builds/deps/astyle_3.1_linux.tar.gz --output astyle.tar.gz;
mkdir -p BUILD && tar xf astyle.tar.gz -C BUILD;
cd BUILD/astyle/build/gcc;
make;
export PATH="${PWD}/bin:${PATH}";
cd -
- astyle --version
# Fetch remaining information needed for branch comparison
- git fetch --all --unshallow --tags
- git fetch origin "${TRAVIS_BRANCH}"
script:
- >-
git diff --name-only --diff-filter=d FETCH_HEAD..HEAD \
| ( grep '.\(c\|cpp\|h\|hpp\)$' || true ) \
| ( grep -v -f .astyleignore || true ) \
| while read file; do astyle -n --options=.astylerc "${file}"; done
- git diff --exit-code --diff-filter=d --color
- <<: *docs-vm
name: "spellcheck"
env: NAME=doxy-spellcheck
install:
- source_pkg aspell
script:
- ./tools/test/travis-ci/doxy-spellchecker/spell.sh drivers
- ./tools/test/travis-ci/doxy-spellchecker/spell.sh platform
- ./tools/test/travis-ci/doxy-spellchecker/spell.sh events
- ./tools/test/travis-ci/doxy-spellchecker/spell.sh rtos
- ./tools/test/travis-ci/doxy-spellchecker/spell.sh features/netsocket
- <<: *docs-vm
name: "doxygen"
env: NAME=docs
install: install:
# Install dependencies
- sudo apt-get install doxygen
# Print versions we use
- doxygen --version
before_script:
# Build doxygen # Build doxygen
- > - >
(git clone --depth=1 --single-branch --branch Release_1_8_14 https://github.com/doxygen/doxygen; (git clone --depth=1 --single-branch --branch Release_1_8_14 https://github.com/doxygen/doxygen;
@ -99,14 +143,19 @@ matrix:
find -name "*.s" | tee BUILD/badasm | find -name "*.s" | tee BUILD/badasm |
sed -e "s/^/Bad Assembler file name found: /" && [ ! -s BUILD/badasm ] sed -e "s/^/Bad Assembler file name found: /" && [ ! -s BUILD/badasm ]
- &tools-pytest
### Python Tests ###
- &pytools-vm
stage: "Pytest"
name: "tools-py27"
env: NAME=tools-py2.7 env: NAME=tools-py2.7
language: python
python: 2.7 python: 2.7
install: install:
# Install dependencies # Install gcc
- sudo apt-get install gcc-arm-embedded - source_pkg gcc
- arm-none-eabi-gcc --version - arm-none-eabi-gcc --version
# Add additional dependencies specific for testing # Install additional python modules
- python --version - python --version
- |- - |-
tr -d ' ' >> requirements.txt <<< " tr -d ' ' >> requirements.txt <<< "
@ -117,7 +166,8 @@ matrix:
coverage>=4.5,<5 coverage>=4.5,<5
coveralls>=1.5,<2 coveralls>=1.5,<2
" "
# ... and install. - python -m pip install --upgrade pip==18.1
- python -m pip install --upgrade setuptools==40.4.3
- pip install -r requirements.txt - pip install -r requirements.txt
- pip list --verbose - pip list --verbose
script: script:
@ -126,118 +176,92 @@ matrix:
- python tools/test/pylint.py - python tools/test/pylint.py
- coverage run -a tools/project.py -S | sed -n '/^Total/p' - coverage run -a tools/project.py -S | sed -n '/^Total/p'
- coverage html - coverage html
after_success:
# Coverage for tools
- coveralls - coveralls
# Report success since we have overridden default behavior
- bash -c "$STATUS" success "Local $NAME testing has passed"
- env: - <<: *pytools-vm
- NAME=doxy-spellcheck name: "tools-py35"
install:
- sudo apt-get install aspell
script:
# Run local testing on header file doxy
- ./tools/test/travis-ci/doxy-spellchecker/spell.sh drivers
- ./tools/test/travis-ci/doxy-spellchecker/spell.sh platform
- ./tools/test/travis-ci/doxy-spellchecker/spell.sh events
- ./tools/test/travis-ci/doxy-spellchecker/spell.sh rtos
- ./tools/test/travis-ci/doxy-spellchecker/spell.sh features/netsocket
after_success:
# Coverage for tools
- coveralls
# Report success since we have overridden default behavior
- bash -c "$STATUS" success "Local $NAME testing has passed"
- <<: *tools-pytest
env: NAME=tools-py3.5 env: NAME=tools-py3.5
python: 3.5 python: 3.5
- <<: *tools-pytest - <<: *pytools-vm
name: "tools-py36"
env: NAME=tools-py3.6 env: NAME=tools-py3.6
python: 3.6 python: 3.6
- <<: *tools-pytest - <<: *pytools-vm
name: "tools-py37"
env: NAME=tools-py3.7 env: NAME=tools-py3.7
python: 3.7 python: 3.7
dist: xenial
- env:
- NAME=astyle ### Extended Tests ###
- &extended-vm
stage: "Extended"
name: "psa autogen"
env: NAME=psa-autogen
language: python
python: 3.7
install: install:
- >- # Install gcc
curl -L0 https://mbed-os-ci.s3-eu-west-1.amazonaws.com/jenkins-ci/deps/astyle_3.1_linux.tar.gz --output astyle.tar.gz; - source_pkg gcc
mkdir -p BUILD && tar xf astyle.tar.gz -C BUILD;
cd BUILD/astyle/build/gcc;
make;
export PATH=$PWD/bin:$PATH;
cd -
- astyle --version
# Fetch remaining information needed for branch comparison
- git fetch --all --unshallow --tags
- git fetch origin "${TRAVIS_BRANCH}"
script:
- >-
git diff --name-only --diff-filter=d FETCH_HEAD..HEAD \
| ( grep '.\(c\|cpp\|h\|hpp\)$' || true ) \
| ( grep -v -f .astyleignore || true ) \
| while read file; do astyle -n --options=.astylerc "${file}"; done
- git diff --exit-code --diff-filter=d --color
- env:
- NAME=events
- EVENTS=events
install:
# Install dependencies
- sudo apt-get install gcc-arm-embedded
- pip install -r requirements.txt
# Print versions we use
- arm-none-eabi-gcc --version - arm-none-eabi-gcc --version
- gcc --version # Install python modules
- python --version - python -m pip install --upgrade pip==18.1
- python -m pip install --upgrade setuptools==40.4.3
- pip install -r requirements.txt
- pip list --verbose
script:
- python tools/psa/generate_partition_code.py
- git diff --exit-code
- <<: *extended-vm
name: "events"
env: NAME=events EVENTS=events
script: script:
# Check that example compiles # Check that example compiles
- sed -n '/``` cpp/,/```/{/```$/Q;/```/d;p;}' $EVENTS/README.md > main.cpp - sed -n '/``` cpp/,/```/{/```$/Q;/```/d;p;}' ${EVENTS}/README.md > main.cpp
- python tools/make.py -t GCC_ARM -m K64F --source=. --build=BUILD/K64F/GCC_ARM -j0 - python tools/make.py -t GCC_ARM -m K64F --source=. --build=BUILD/K64F/GCC_ARM -j0
# Check that example compiles without rtos # Check that example compiles without rtos
- sed -n '/``` cpp/,/```/{/```$/Q;/```/d;p;}' $EVENTS/README.md > main.cpp - sed -n '/``` cpp/,/```/{/```$/Q;/```/d;p;}' ${EVENTS}/README.md > main.cpp
- rm -r rtos usb features/cellular features/netsocket features/nanostack features/lwipstack features/frameworks/greentea-client features/frameworks/utest features/frameworks/unity components BUILD - |
rm -r rtos usb features/cellular features/netsocket features/nanostack \
features/lwipstack features/frameworks/greentea-client \
features/frameworks/utest features/frameworks/unity components BUILD
- python tools/make.py -t GCC_ARM -m DISCO_F401VC --source=. --build=BUILD/DISCO_F401VC/GCC_ARM -j0 - python tools/make.py -t GCC_ARM -m DISCO_F401VC --source=. --build=BUILD/DISCO_F401VC/GCC_ARM -j0
# Run local equeue tests # Run local equeue tests
- make -C $EVENTS/equeue test - make -C ${EVENTS}/equeue test
# Run profiling tests # Run profiling tests
- make -C $EVENTS/equeue prof | tee prof - make -C ${EVENTS}/equeue prof | tee prof
after_success: after_success:
# update status if we succeeded, compare with master if possible # Update status, comparing with master if possible.
- | - |
CURR=$(grep -o '[0-9]\+ cycles' prof | awk '{sum += $1} END {print sum}') CURR=$(grep -o '[0-9]\+ cycles' prof | awk '{sum += $1} END {print sum}')
PREV=$(curl -u "$MBED_BOT" https://api.github.com/repos/$TRAVIS_REPO_SLUG/status/master \ PREV=$(curl -u "${MBED_BOT}" https://api.github.com/repos/${TRAVIS_REPO_SLUG}/status/master \
| jq -re "select(.sha != \"$TRAVIS_COMMIT\") | jq -re "select(.sha != \"${TRAVIS_COMMIT}\")
| .statuses[] | select(.context == \"travis-ci/$NAME\").description | .statuses[] | select(.context == \"travis-ci/${NAME}\").description
| capture(\"runtime is (?<runtime>[0-9]+)\").runtime" \ | capture(\"runtime is (?<runtime>[0-9]+)\").runtime" \
|| echo 0) || echo 0)
STATUSM="Passed, runtime is ${CURR} cycles" delta=""
if [ "$PREV" -ne 0 ] [ "${PREV}" -ne 0 ] && delta="($(printf "%+d" "$(( ${CURR} - ${PREV} ))" cycles)"
then
STATUSM="$STATUSM ($(python -c "print '%+d' % ($CURR-$PREV)") cycles)"
fi
- bash -c "$STATUS" success "$STATUSM"
- env: set_status "success" "Success! Runtime is ${CURR} cycles. ${delta}"
- NAME=littlefs
- LITTLEFS=features/storage/filesystem/littlefs - <<: *extended-vm
name: "littlefs"
env: NAME=littlefs LITTLEFS=features/storage/filesystem/littlefs
install: install:
# Install dependencies # Install gcc
- sudo apt-get install gcc-arm-embedded fuse libfuse-dev - source_pkg gcc
- pip install -r requirements.txt
# Print versions
- arm-none-eabi-gcc --version - arm-none-eabi-gcc --version
- gcc --version # Install python modules
- python --version - python -m pip install --upgrade pip==18.1
- python -m pip install --upgrade setuptools==40.4.3
- pip install -r requirements.txt
- pip list --verbose
# Install test-specific packages
- source_pkg fuse
- source_pkg libfuse-dev
- fusermount --version - fusermount --version
before_script: before_script:
# Setup and patch littlefs-fuse # Setup and patch littlefs-fuse
@ -245,7 +269,7 @@ matrix:
- git -C littlefs_fuse checkout 3f1ed6e37799e49e3710830dc6abb926d5503cf2 - git -C littlefs_fuse checkout 3f1ed6e37799e49e3710830dc6abb926d5503cf2
- echo '*' > littlefs_fuse/.mbedignore - echo '*' > littlefs_fuse/.mbedignore
- rm -rf littlefs_fuse/littlefs/* - rm -rf littlefs_fuse/littlefs/*
- cp -r $(git ls-tree --name-only HEAD $LITTLEFS/littlefs/) littlefs_fuse/littlefs - cp -r $(git ls-tree --name-only HEAD ${LITTLEFS}/littlefs/) littlefs_fuse/littlefs
# Create file-backed disk # Create file-backed disk
- mkdir MOUNT - mkdir MOUNT
- sudo chmod a+rw /dev/loop0 - sudo chmod a+rw /dev/loop0
@ -255,74 +279,43 @@ matrix:
script: script:
# Check that example compiles # Check that example compiles
- export CFLAGS="-Werror -Wno-format" - export CFLAGS="-Werror -Wno-format"
- sed -n '/``` c++/,/```/{/```/d;p;}' $LITTLEFS/README.md > main.cpp - sed -n '/``` c++/,/```/{/```/d;p;}' ${LITTLEFS}/README.md > main.cpp
- python tools/make.py -t GCC_ARM -m K82F --source=. --build=BUILD/K82F/GCC_ARM -j0 - python tools/make.py -t GCC_ARM -m K82F --source=. --build=BUILD/K82F/GCC_ARM -j0
# Run local littlefs tests # Run local littlefs tests
- make -C$LITTLEFS/littlefs test QUIET=1 - make -C${LITTLEFS}/littlefs test QUIET=1
# Run local littlefs tests with set of variations # Run local littlefs tests with set of variations
- make -C$LITTLEFS/littlefs test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=64 -DLFS_PROG_SIZE=64" - make -C${LITTLEFS}/littlefs test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=64 -DLFS_PROG_SIZE=64"
- make -C$LITTLEFS/littlefs test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=1 -DLFS_PROG_SIZE=1" - make -C${LITTLEFS}/littlefs test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=1 -DLFS_PROG_SIZE=1"
- make -C$LITTLEFS/littlefs test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=512 -DLFS_PROG_SIZE=512" - make -C${LITTLEFS}/littlefs test QUIET=1 CFLAGS+="-DLFS_READ_SIZE=512 -DLFS_PROG_SIZE=512"
- make -C$LITTLEFS/littlefs test QUIET=1 CFLAGS+="-DLFS_BLOCK_COUNT=1023 -DLFS_LOOKAHEAD=2048" - make -C${LITTLEFS}/littlefs test QUIET=1 CFLAGS+="-DLFS_BLOCK_COUNT=1023 -DLFS_LOOKAHEAD=2048"
- make -C$LITTLEFS/littlefs clean test QUIET=1 CFLAGS+="-DLFS_NO_INTRINSICS" - make -C${LITTLEFS}/littlefs clean test QUIET=1 CFLAGS+="-DLFS_NO_INTRINSICS"
# Self-hosting littlefs fuzz test with littlefs-fuse # Self-hosting littlefs fuzz test with littlefs-fuse
- make -Clittlefs_fuse - make -Clittlefs_fuse
- littlefs_fuse/lfs --format /dev/loop0 - littlefs_fuse/lfs --format /dev/loop0
- littlefs_fuse/lfs /dev/loop0 MOUNT - littlefs_fuse/lfs /dev/loop0 MOUNT
- ls MOUNT - ls MOUNT
- mkdir MOUNT/littlefs - mkdir MOUNT/littlefs
- cp -r $(git ls-tree --name-only HEAD $LITTLEFS/littlefs/) MOUNT/littlefs - cp -r $(git ls-tree --name-only HEAD ${LITTLEFS}/littlefs/) MOUNT/littlefs
- ls MOUNT/littlefs - ls MOUNT/littlefs
- CFLAGS="-Wno-format" make -CMOUNT/littlefs -B test_dirs test_files QUIET=1 - CFLAGS="-Wno-format" make -CMOUNT/littlefs -B test_dirs test_files QUIET=1
# Compile and find the code size with smallest configuration # Compile and find the code size with smallest configuration
- cd $TRAVIS_BUILD_DIR/$LITTLEFS/littlefs - cd ${TRAVIS_BUILD_DIR}/${LITTLEFS}/littlefs
- make clean size - make clean size
CC='arm-none-eabi-gcc -mthumb' CC='arm-none-eabi-gcc -mthumb'
OBJ="$(ls lfs*.o | tr '\n' ' ')" OBJ="$(ls lfs*.o | tr '\n' ' ')"
CFLAGS+="-DLFS_NO_ASSERT -DLFS_NO_DEBUG -DLFS_NO_WARN -DLFS_NO_ERROR" CFLAGS+="-DLFS_NO_ASSERT -DLFS_NO_DEBUG -DLFS_NO_WARN -DLFS_NO_ERROR"
| tee sizes | tee sizes
after_success: after_success:
# update status if we succeeded, compare with master if possible # Update status, comparing with master if possible.
- | - |
CURR=$(tail -n1 sizes | awk '{print $1}') CURR=$(tail -n1 sizes | awk '{print $1}')
PREV=$(curl -u "$MBED_BOT" https://api.github.com/repos/$TRAVIS_REPO_SLUG/status/master \ PREV=$(curl -u "${MBED_BOT}" https://api.github.com/repos/${TRAVIS_REPO_SLUG}/status/master \
| jq -re "select(.sha != \"$TRAVIS_COMMIT\") | jq -re "select(.sha != \"${TRAVIS_COMMIT}\")
| .statuses[] | select(.context == \"travis-ci/$NAME\").description | .statuses[] | select(.context == \"travis-ci/${NAME}\").description
| capture(\"code size is (?<size>[0-9]+)\").size" \ | capture(\"code size is (?<size>[0-9]+)\").size" \
|| echo 0) || echo 0)
STATUSM="Passed, code size is ${CURR}B" delta=""
if [ "$PREV" -ne 0 ] [ "${PREV}" -ne 0 ] && delta="($(printf "%+0.2f%%" "$(<<< "100 * ((${CURR} - ${PREV})/${PREV})" bc -l)"))"
then
STATUSM="$STATUSM ($(python -c "print '%+.2f' % (100*($CURR-$PREV)/$PREV.0)")%)"
fi
- bash -c "$STATUS" success "$STATUSM"
- env: set_status "success" "Success! Code size is ${CURR}B. ${delta}"
- NAME=gitattributestest
script:
# Check that no changes after clone. This check that .gitattributes is used right way.
- git diff --exit-code
- env:
- NAME=licence_check
script:
- >-
! grep --recursive --max-count=100 --ignore-case --exclude .travis.yml \
"gnu general\|gnu lesser\|lesser general\|public license"
- env:
- NAME=include_check
script:
- echo 'Checking that there are no '#include "mbed.h"' in code where it should not be'
- |
! git grep '^#include\s["'"']mbed.h['"'"]$' -- '*.c' '*.h' '*.cpp' '*.hpp' \
':!*platform_mbed.h' ':!*TESTS/*' ':!TEST_APPS/' ':!UNITTESTS/' \
':!*tests/*' ':!*targets/*' ':!*TARGET_*' ':!*unsupported/*'
- env:
- NAME=psa-autogen
script:
# Run SPM code generators and check that changes are not needed
- python tools/psa/generate_partition_code.py
- git diff --exit-code

View File

@ -50,3 +50,4 @@ The Python modules used by Mbed tools are used under the following licenses:
- [pycryptodome](https://pypi.org/project/pycryptodome) - BSD-2-Clause - [pycryptodome](https://pypi.org/project/pycryptodome) - BSD-2-Clause
- [pyusb](https://pypi.org/project/pyusb/) - Apache-2.0 - [pyusb](https://pypi.org/project/pyusb/) - Apache-2.0
- [cmsis-pack-manager](https://pypi.org/project/cmsis-pack-manager) - Apache-2.0 - [cmsis-pack-manager](https://pypi.org/project/cmsis-pack-manager) - Apache-2.0
- [hidapi](https://pypi.org/project/hidapi/) - BSD-style

View File

@ -0,0 +1,566 @@
"""
mbed SDK
Copyright (c) 2019 ARM Limited
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
from __future__ import print_function
import functools
import time
import threading
import uuid
import mbed_host_tests
import usb.core
from usb.util import (
CTRL_IN,
CTRL_OUT,
CTRL_TYPE_STANDARD,
CTRL_TYPE_CLASS,
CTRL_RECIPIENT_DEVICE,
CTRL_RECIPIENT_INTERFACE,
DESC_TYPE_CONFIG,
build_request_type)
try:
import hid
except ImportError:
CYTHON_HIDAPI_PRESENT = False
else:
CYTHON_HIDAPI_PRESENT = True
# USB device -- device classes
USB_CLASS_HID = 0x03
# USB device -- standard requests
USB_REQUEST_GET_DESCRIPTOR = 0x06
# USB device -- HID class requests
HID_REQUEST_GET_REPORT = 0x01
HID_REQUEST_SET_REPORT = 0x09
HID_REQUEST_GET_IDLE = 0x02
HID_REQUEST_SET_IDLE = 0x0A
HID_REQUEST_GET_PROTOCOL = 0x03
HID_REQUEST_SET_PROTOCOL = 0x0B
# USB device -- HID class descriptors
DESC_TYPE_HID_HID = 0x21
DESC_TYPE_HID_REPORT = 0x22
DESC_TYPE_HID_PHYSICAL = 0x23
# USB device -- HID class descriptor lengths
DESC_LEN_HID_HID = 0x09
# USB device -- descriptor fields offsets
DESC_OFFSET_BLENGTH = 0
DESC_OFFSET_BDESCRIPTORTYPE = 1
# USB device -- HID subclasses
HID_SUBCLASS_NONE = 0
HID_SUBCLASS_BOOT = 1
# USB device -- HID protocols
HID_PROTOCOL_NONE = 0
HID_PROTOCOL_KEYBOARD = 1
HID_PROTOCOL_MOUSE = 2
# Greentea message keys used for callbacks
MSG_KEY_DEVICE_READY = 'dev_ready'
MSG_KEY_HOST_READY = 'host_ready'
MSG_KEY_SERIAL_NUMBER = 'usb_dev_sn'
MSG_KEY_TEST_GET_DESCRIPTOR_HID = 'test_get_desc_hid'
MSG_KEY_TEST_GET_DESCRIPTOR_CFG = 'test_get_desc_cfg'
MSG_KEY_TEST_REQUESTS = 'test_requests'
MSG_KEY_TEST_RAW_IO = 'test_raw_io'
# Greentea message keys used to notify DUT of test status
MSG_KEY_TEST_CASE_FAILED = 'fail'
MSG_KEY_TEST_CASE_PASSED = 'pass'
MSG_VALUE_DUMMY = '0'
MSG_VALUE_NOT_SUPPORTED = 'not_supported'
# Constants for the tests.
KEYBOARD_IDLE_RATE_TO_SET = 0x00 # Duration = 0 (indefinite)
HID_PROTOCOL_TO_SET = 0x01 # Protocol = 1 (Report Protocol)
RAW_IO_REPS = 16 # Number of loopback test reps.
def build_get_desc_value(desc_type, desc_index):
"""Build and return a wValue field for control requests."""
return (desc_type << 8) | desc_index
def usb_hid_path(serial_number):
"""Get a USB HID device system path based on the serial number."""
if not CYTHON_HIDAPI_PRESENT:
return None
for device_info in hid.enumerate(): # pylint: disable=no-member
if device_info.get('serial_number') == serial_number: # pylint: disable=not-callable
return device_info['path']
return None
def get_descriptor_types(desc):
"""Return a list of all bDescriptorType values found in desc.
desc is expected to be a sequence of bytes, i.e. array.array('B')
returned from usb.core.
From the USB 2.0 spec, paragraph 9.5:
Each descriptor begins with a byte-wide field that contains the total
number of bytes in the descriptor followed by a byte-wide field that
identifies the descriptor type.
"""
tmp_desc = desc[DESC_OFFSET_BLENGTH:]
desc_types = []
while True:
try:
bLength = tmp_desc[DESC_OFFSET_BLENGTH] # pylint: disable=invalid-name
bDescriptorType = tmp_desc[DESC_OFFSET_BDESCRIPTORTYPE] # pylint: disable=invalid-name
desc_types.append(int(bDescriptorType))
tmp_desc = tmp_desc[int(bLength):]
except IndexError:
break
return desc_types
def get_hid_descriptor_parts(hid_descriptor):
"""Return bNumDescriptors, bDescriptorType, wDescriptorLength from hid_descriptor."""
err_msg = 'Invalid HID class descriptor'
try:
if hid_descriptor[1] != DESC_TYPE_HID_HID:
raise TypeError(err_msg)
bNumDescriptors = int(hid_descriptor[5]) # pylint: disable=invalid-name
bDescriptorType = int(hid_descriptor[6]) # pylint: disable=invalid-name
wDescriptorLength = int((hid_descriptor[8] << 8) | hid_descriptor[7]) # pylint: disable=invalid-name
except (IndexError, ValueError):
raise TypeError(err_msg)
return bNumDescriptors, bDescriptorType, wDescriptorLength
def get_usbhid_dev_type(intf):
"""Return a name of the HID device class type for intf."""
if not isinstance(intf, usb.core.Interface):
return None
if intf.bInterfaceClass != USB_CLASS_HID:
# USB Device Class Definition for HID, v1.11, paragraphs 4.1, 4.2 & 4.3:
# the class is specified in the Interface descriptor
# and not the Device descriptor.
return None
if (intf.bInterfaceSubClass == HID_SUBCLASS_BOOT
and intf.bInterfaceProtocol == HID_PROTOCOL_KEYBOARD):
return 'boot_keyboard'
if (intf.bInterfaceSubClass == HID_SUBCLASS_BOOT
and intf.bInterfaceProtocol == HID_PROTOCOL_MOUSE):
return 'boot_mouse'
# Determining any other HID dev type, like a non-boot_keyboard or
# a non-boot_mouse requires getting and parsing a HID Report descriptor
# for intf.
# Only the boot_keyboard, boot_mouse and other_device are used for this
# greentea test suite.
return 'other_device'
class RetryError(Exception):
"""Exception raised by retry_fun_call()."""
def retry_fun_call(fun, num_retries=3, retry_delay=0.0):
"""Call fun and retry if any exception was raised.
fun is called at most num_retries with a retry_dalay in between calls.
Raises RetryError if the retry limit is exhausted.
"""
verbose = False
final_err = None
for retry in range(1, num_retries + 1):
try:
return fun() # pylint: disable=not-callable
except Exception as exc: # pylint: disable=broad-except
final_err = exc
if verbose:
print('Retry {}/{} failed ({})'
.format(retry, num_retries, str(fun)))
time.sleep(retry_delay)
err_msg = 'Failed with "{}". Tried {} times.'
raise RetryError(err_msg.format(final_err, num_retries))
def raise_if_different(expected, actual, text=''):
"""Raise a RuntimeError if actual is different than expected."""
if expected != actual:
raise RuntimeError('{}Got {!r}, expected {!r}.'.format(text, actual, expected))
def raise_if_false(expression, text):
"""Raise a RuntimeError if expression is False."""
if not expression:
raise RuntimeError(text)
class USBHIDTest(mbed_host_tests.BaseHostTest):
"""Host side test for USB device HID class."""
@staticmethod
def get_usb_hid_path(usb_id_str):
"""Get a USB HID device path as registered in the system.
Search is based on the unique USB SN generated by the host
during test suite setup.
Raises RuntimeError if the device is not found.
"""
hid_path = usb_hid_path(usb_id_str)
if hid_path is None:
err_msg = 'USB HID device (SN={}) not found.'
raise RuntimeError(err_msg.format(usb_id_str))
return hid_path
@staticmethod
def get_usb_dev(usb_id_str):
"""Get a usb.core.Device instance.
Search is based on the unique USB SN generated by the host
during test suite setup.
Raises RuntimeError if the device is not found.
"""
usb_dev = usb.core.find(custom_match=lambda d: d.serial_number == usb_id_str)
if usb_dev is None:
err_msg = 'USB device (SN={}) not found.'
raise RuntimeError(err_msg.format(usb_id_str))
return usb_dev
def __init__(self):
super(USBHIDTest, self).__init__()
self.__bg_task = None
self.dut_usb_dev_sn = uuid.uuid4().hex # 32 hex digit string
def notify_error(self, msg):
"""Terminate the test with an error msg."""
self.log('TEST ERROR: {}'.format(msg))
self.notify_complete(None)
def notify_failure(self, msg):
"""Report a host side test failure to the DUT."""
self.log('TEST FAILED: {}'.format(msg))
self.send_kv(MSG_KEY_TEST_CASE_FAILED, MSG_VALUE_DUMMY)
def notify_success(self, value=None, msg=''):
"""Report a host side test success to the DUT."""
if msg:
self.log('TEST PASSED: {}'.format(msg))
if value is None:
value = MSG_VALUE_DUMMY
self.send_kv(MSG_KEY_TEST_CASE_PASSED, value)
def cb_test_get_hid_desc(self, key, value, timestamp):
"""Verify the device handles Get_Descriptor request correctly.
Two requests are tested for every HID interface:
1. Get_Descriptor(HID),
2. Get_Descriptor(Report).
Details in USB Device Class Definition for HID, v1.11, paragraph 7.1.
"""
kwargs_hid_desc_req = {
'bmRequestType': build_request_type(
CTRL_IN, CTRL_TYPE_STANDARD, CTRL_RECIPIENT_INTERFACE),
'bRequest': USB_REQUEST_GET_DESCRIPTOR,
# Descriptor Index (part of wValue) is reset to zero for
# HID class descriptors other than Physical ones.
'wValue': build_get_desc_value(DESC_TYPE_HID_HID, 0x00),
# wIndex is replaced with the Interface Number in the loop.
'wIndex': None,
'data_or_wLength': DESC_LEN_HID_HID}
kwargs_report_desc_req = {
'bmRequestType': build_request_type(
CTRL_IN, CTRL_TYPE_STANDARD, CTRL_RECIPIENT_INTERFACE),
'bRequest': USB_REQUEST_GET_DESCRIPTOR,
# Descriptor Index (part of wValue) is reset to zero for
# HID class descriptors other than Physical ones.
'wValue': build_get_desc_value(DESC_TYPE_HID_REPORT, 0x00),
# wIndex is replaced with the Interface Number in the loop.
'wIndex': None,
# wLength is replaced with the Report Descriptor Length in the loop.
'data_or_wLength': None}
mbed_hid_dev = None
report_desc_lengths = []
try:
mbed_hid_dev = retry_fun_call(
fun=functools.partial(self.get_usb_dev, self.dut_usb_dev_sn), # pylint: disable=not-callable
num_retries=20,
retry_delay=0.05)
except RetryError as exc:
self.notify_error(exc)
return
try:
for intf in mbed_hid_dev.get_active_configuration(): # pylint: disable=not-callable
if intf.bInterfaceClass != USB_CLASS_HID:
continue
try:
if mbed_hid_dev.is_kernel_driver_active(intf.bInterfaceNumber):
mbed_hid_dev.detach_kernel_driver(intf.bInterfaceNumber) # pylint: disable=not-callable
except (NotImplementedError, AttributeError):
pass
# Request the HID descriptor.
kwargs_hid_desc_req['wIndex'] = intf.bInterfaceNumber
hid_desc = mbed_hid_dev.ctrl_transfer(**kwargs_hid_desc_req) # pylint: disable=not-callable
try:
bNumDescriptors, bDescriptorType, wDescriptorLength = get_hid_descriptor_parts(hid_desc) # pylint: disable=invalid-name
except TypeError as exc:
self.notify_error(exc)
return
raise_if_different(1, bNumDescriptors, 'Exactly one HID Report descriptor expected. ')
raise_if_different(DESC_TYPE_HID_REPORT, bDescriptorType, 'Invalid HID class descriptor type. ')
raise_if_false(wDescriptorLength > 0, 'Invalid HID Report descriptor length. ')
# Request the Report descriptor.
kwargs_report_desc_req['wIndex'] = intf.bInterfaceNumber
kwargs_report_desc_req['data_or_wLength'] = wDescriptorLength
report_desc = mbed_hid_dev.ctrl_transfer(**kwargs_report_desc_req) # pylint: disable=not-callable
raise_if_different(wDescriptorLength, len(report_desc),
'The size of data received does not match the HID Report descriptor length. ')
report_desc_lengths.append(len(report_desc))
except usb.core.USBError as exc:
self.notify_failure('Get_Descriptor request failed. {}'.format(exc))
except RuntimeError as exc:
self.notify_failure(exc)
else:
# Send the report desc len to the device.
# USBHID::report_desc_length() returns uint16_t
msg_value = '{0:04x}'.format(max(report_desc_lengths))
self.notify_success(msg_value)
def cb_test_get_cfg_desc(self, key, value, timestamp):
"""Verify the device provides required HID descriptors.
USB Device Class Definition for HID, v1.11, paragraph 7.1:
When a Get_Descriptor(Configuration) request is issued, it
returns (...), and the HID descriptor for each interface.
"""
kwargs_cfg_desc_req = {
'bmRequestType': build_request_type(
CTRL_IN, CTRL_TYPE_STANDARD, CTRL_RECIPIENT_DEVICE),
'bRequest': USB_REQUEST_GET_DESCRIPTOR,
# Descriptor Index (part of wValue) is reset to zero.
'wValue': build_get_desc_value(DESC_TYPE_CONFIG, 0x00),
# wIndex is reset to zero.
'wIndex': 0x00,
# wLength unknown, set to 1024.
'data_or_wLength': 1024}
mbed_hid_dev = None
try:
mbed_hid_dev = retry_fun_call(
fun=functools.partial(self.get_usb_dev, self.dut_usb_dev_sn), # pylint: disable=not-callable
num_retries=20,
retry_delay=0.05)
except RetryError as exc:
self.notify_error(exc)
return
try:
# Request the Configuration descriptor.
cfg_desc = mbed_hid_dev.ctrl_transfer(**kwargs_cfg_desc_req) # pylint: disable=not-callable
raise_if_false(DESC_TYPE_HID_HID in get_descriptor_types(cfg_desc),
'No HID class descriptor in the Configuration descriptor.')
except usb.core.USBError as exc:
self.notify_failure('Get_Descriptor request failed. {}'.format(exc))
except RuntimeError as exc:
self.notify_failure(exc)
else:
self.notify_success()
def cb_test_class_requests(self, key, value, timestamp):
"""Verify all required HID requests are supported.
USB Device Class Definition for HID, v1.11, Appendix G:
1. Get_Report -- required for all types,
2. Set_Report -- not required if dev doesn't declare an Output Report,
3. Get_Idle -- required for keyboards,
4. Set_Idle -- required for keyboards,
5. Get_Protocol -- required for boot_keyboard and boot_mouse,
6. Set_Protocol -- required for boot_keyboard and boot_mouse.
Details in USB Device Class Definition for HID, v1.11, paragraph 7.2.
"""
kwargs_get_report_request = {
'bmRequestType': build_request_type(
CTRL_IN, CTRL_TYPE_CLASS, CTRL_RECIPIENT_INTERFACE),
'bRequest': HID_REQUEST_GET_REPORT,
# wValue: ReportType = Input, ReportID = 0 (not used)
'wValue': (0x01 << 8) | 0x00,
# wIndex: InterfaceNumber (defined later)
'wIndex': None,
# wLength: unknown, set to 1024
'data_or_wLength': 1024}
kwargs_get_idle_request = {
'bmRequestType': build_request_type(
CTRL_IN, CTRL_TYPE_CLASS, CTRL_RECIPIENT_INTERFACE),
'bRequest': HID_REQUEST_GET_IDLE,
# wValue: 0, ReportID = 0 (not used)
'wValue': (0x00 << 8) | 0x00,
# wIndex: InterfaceNumber (defined later)
'wIndex': None,
'data_or_wLength': 1}
kwargs_set_idle_request = {
'bmRequestType': build_request_type(
CTRL_OUT, CTRL_TYPE_CLASS, CTRL_RECIPIENT_INTERFACE),
'bRequest': HID_REQUEST_SET_IDLE,
# wValue: Duration, ReportID = 0 (all input reports)
'wValue': (KEYBOARD_IDLE_RATE_TO_SET << 8) | 0x00,
# wIndex: InterfaceNumber (defined later)
'wIndex': None,
'data_or_wLength': 0}
kwargs_get_protocol_request = {
'bmRequestType': build_request_type(
CTRL_IN, CTRL_TYPE_CLASS, CTRL_RECIPIENT_INTERFACE),
'bRequest': HID_REQUEST_GET_PROTOCOL,
'wValue': 0x00,
# wIndex: InterfaceNumber (defined later)
'wIndex': None,
'data_or_wLength': 1}
kwargs_set_protocol_request = {
'bmRequestType': build_request_type(
CTRL_OUT, CTRL_TYPE_CLASS, CTRL_RECIPIENT_INTERFACE),
'bRequest': HID_REQUEST_SET_PROTOCOL,
'wValue': HID_PROTOCOL_TO_SET,
# wIndex: InterfaceNumber (defined later)
'wIndex': None,
'data_or_wLength': 0}
mbed_hid_dev = None
try:
mbed_hid_dev = retry_fun_call(
fun=functools.partial(self.get_usb_dev, self.dut_usb_dev_sn), # pylint: disable=not-callable
num_retries=20,
retry_delay=0.05)
except RetryError as exc:
self.notify_error(exc)
return
hid_dev_type = None
tested_request_name = None
try:
for intf in mbed_hid_dev.get_active_configuration(): # pylint: disable=not-callable
hid_dev_type = get_usbhid_dev_type(intf)
if hid_dev_type is None:
continue
try:
if mbed_hid_dev.is_kernel_driver_active(intf.bInterfaceNumber):
mbed_hid_dev.detach_kernel_driver(intf.bInterfaceNumber) # pylint: disable=not-callable
except (NotImplementedError, AttributeError):
pass
if hid_dev_type == 'boot_keyboard':
# 4. Set_Idle
tested_request_name = 'Set_Idle'
kwargs_set_idle_request['wIndex'] = intf.bInterfaceNumber
mbed_hid_dev.ctrl_transfer(**kwargs_set_idle_request) # pylint: disable=not-callable
# 3. Get_Idle
tested_request_name = 'Get_Idle'
kwargs_get_idle_request['wIndex'] = intf.bInterfaceNumber
idle_rate = mbed_hid_dev.ctrl_transfer(**kwargs_get_idle_request) # pylint: disable=not-callable
raise_if_different(KEYBOARD_IDLE_RATE_TO_SET, idle_rate, 'Invalid idle rate received. ')
if hid_dev_type in ('boot_keyboard', 'boot_mouse'):
# 6. Set_Protocol
tested_request_name = 'Set_Protocol'
kwargs_set_protocol_request['wIndex'] = intf.bInterfaceNumber
mbed_hid_dev.ctrl_transfer(**kwargs_set_protocol_request) # pylint: disable=not-callable
# 5. Get_Protocol
tested_request_name = 'Get_Protocol'
kwargs_get_protocol_request['wIndex'] = intf.bInterfaceNumber
protocol = mbed_hid_dev.ctrl_transfer(**kwargs_get_protocol_request) # pylint: disable=not-callable
raise_if_different(HID_PROTOCOL_TO_SET, protocol, 'Invalid protocol received. ')
# 1. Get_Report
tested_request_name = 'Get_Report'
kwargs_get_report_request['wIndex'] = intf.bInterfaceNumber
mbed_hid_dev.ctrl_transfer(**kwargs_get_report_request) # pylint: disable=not-callable
except usb.core.USBError as exc:
self.notify_failure('The {!r} does not support the {!r} HID class request ({}).'
.format(hid_dev_type, tested_request_name, exc))
except RuntimeError as exc:
self.notify_failure('Set/Get data mismatch for {!r} for the {!r} HID class request ({}).'
.format(hid_dev_type, tested_request_name, exc))
else:
self.notify_success()
def raw_loopback(self, report_size):
"""Send every input report back to the device."""
mbed_hid_path = None
mbed_hid = hid.device()
try:
mbed_hid_path = retry_fun_call(
fun=functools.partial(self.get_usb_hid_path, self.dut_usb_dev_sn), # pylint: disable=not-callable
num_retries=20,
retry_delay=0.05)
retry_fun_call(
fun=functools.partial(mbed_hid.open_path, mbed_hid_path), # pylint: disable=not-callable
num_retries=10,
retry_delay=0.05)
except RetryError as exc:
self.notify_error(exc)
return
# Notify the device it can send reports now.
self.send_kv(MSG_KEY_HOST_READY, MSG_VALUE_DUMMY)
try:
for _ in range(RAW_IO_REPS):
# There are no Report ID tags in the Report descriptor.
# Receiving only the Report Data, Report ID is omitted.
report_in = mbed_hid.read(report_size)
report_out = report_in[:]
# Set the Report ID to 0x00 (not used).
report_out.insert(0, 0x00)
mbed_hid.write(report_out)
except (ValueError, IOError) as exc:
self.notify_failure('HID Report transfer failed. {}'.format(exc))
finally:
mbed_hid.close()
def setup(self):
self.register_callback(MSG_KEY_DEVICE_READY, self.cb_device_ready)
self.register_callback(MSG_KEY_TEST_GET_DESCRIPTOR_HID, self.cb_test_get_hid_desc)
self.register_callback(MSG_KEY_TEST_GET_DESCRIPTOR_CFG, self.cb_test_get_cfg_desc)
self.register_callback(MSG_KEY_TEST_REQUESTS, self.cb_test_class_requests)
self.register_callback(MSG_KEY_TEST_RAW_IO, self.cb_test_raw_io)
def cb_device_ready(self, key, value, timestamp):
"""Send a unique USB SN to the device.
DUT uses this SN every time it connects to host as a USB device.
"""
self.send_kv(MSG_KEY_SERIAL_NUMBER, self.dut_usb_dev_sn)
def start_bg_task(self, **thread_kwargs):
"""Start a new daemon thread.
Some callbacks delegate HID dev handling to a background task to
prevent any delays in the device side assert handling. Only one
background task is kept running to prevent multiple access
to the HID device.
"""
try:
self.__bg_task.join()
except (AttributeError, RuntimeError):
pass
self.__bg_task = threading.Thread(**thread_kwargs)
self.__bg_task.daemon = True
self.__bg_task.start()
def cb_test_raw_io(self, key, value, timestamp):
"""Receive HID reports and send them back to the device."""
if not CYTHON_HIDAPI_PRESENT:
self.send_kv(MSG_KEY_HOST_READY, MSG_VALUE_NOT_SUPPORTED)
return
try:
# The size of input and output reports used in test.
report_size = int(value)
except ValueError as exc:
self.notify_error(exc)
return
self.start_bg_task(
target=self.raw_loopback,
args=(report_size, ))

View File

@ -74,9 +74,10 @@ Case cases[] = {
Case("1 s delay during deepsleep (attach_us)", test_deepsleep<AttachUSTester<LowPowerTimeout>, 1000000, LONG_DELTA_US>, Case("1 s delay during deepsleep (attach_us)", test_deepsleep<AttachUSTester<LowPowerTimeout>, 1000000, LONG_DELTA_US>,
greentea_failure_handler), greentea_failure_handler),
#endif #endif
#if !defined(__ARM_FM) //FastModels not support time drifting test
Case("Timing drift (attach)", test_drift<AttachTester<LowPowerTimeout> >), Case("Timing drift (attach)", test_drift<AttachTester<LowPowerTimeout> >),
Case("Timing drift (attach_us)", test_drift<AttachUSTester<LowPowerTimeout> >), Case("Timing drift (attach_us)", test_drift<AttachUSTester<LowPowerTimeout> >),
#endif
}; };
utest::v1::status_t greentea_test_setup(const size_t number_of_cases) utest::v1::status_t greentea_test_setup(const size_t number_of_cases)

View File

@ -859,9 +859,6 @@ Within each loop, one `recvfrom()` may return the received packet size
When `NSAPI_ERROR_WOULD_BLOCK` is received, check that time consumed is When `NSAPI_ERROR_WOULD_BLOCK` is received, check that time consumed is
more that 100 milliseconds but less than 200 milliseconds. more that 100 milliseconds but less than 200 milliseconds.
After repeating for 10 times, at least 5 packets must have been
received.
### UDPSOCKET_SENDTO_TIMEOUT ### UDPSOCKET_SENDTO_TIMEOUT

View File

@ -67,16 +67,16 @@ void TCPSOCKET_ECHOTEST()
int recvd; int recvd;
int sent; int sent;
int x = 0; for (int s_idx = 0; s_idx < sizeof(pkt_sizes) / sizeof(*pkt_sizes); s_idx++) {
for (int pkt_s = pkt_sizes[x]; x < PKTS; pkt_s = pkt_sizes[x++]) { int pkt_s = pkt_sizes[s_idx];
fill_tx_buffer_ascii(tcp_global::tx_buffer, BUFF_SIZE); fill_tx_buffer_ascii(tcp_global::tx_buffer, BUFF_SIZE);
sent = sock.send(tcp_global::tx_buffer, pkt_s); sent = sock.send(tcp_global::tx_buffer, pkt_s);
if (sent < 0) { if (sent < 0) {
printf("[Round#%02d] network error %d\n", x, sent); printf("[Round#%02d] network error %d\n", s_idx, sent);
TEST_FAIL(); TEST_FAIL();
break; break;
} else if (sent != pkt_s) { } else if (sent != pkt_s) {
printf("[%02d] sock.send return size %d does not match the expectation %d\n", x, sent, pkt_s); printf("[%02d] sock.send return size %d does not match the expectation %d\n", s_idx, sent, pkt_s);
TEST_FAIL(); TEST_FAIL();
break; break;
} }
@ -85,7 +85,7 @@ void TCPSOCKET_ECHOTEST()
while (bytes2recv) { while (bytes2recv) {
recvd = sock.recv(&(tcp_global::rx_buffer[sent - bytes2recv]), bytes2recv); recvd = sock.recv(&(tcp_global::rx_buffer[sent - bytes2recv]), bytes2recv);
if (recvd < 0) { if (recvd < 0) {
printf("[Round#%02d] network error %d\n", x, recvd); printf("[Round#%02d] network error %d\n", s_idx, recvd);
TEST_FAIL(); TEST_FAIL();
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.close()); TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.close());
return; return;
@ -143,7 +143,6 @@ void TCPSOCKET_ECHOTEST_NONBLOCK()
int bytes2send; int bytes2send;
int sent; int sent;
int s_idx = 0;
receive_error = false; receive_error = false;
unsigned char *stack_mem = (unsigned char *)malloc(tcp_global::TCP_OS_STACK_SIZE); unsigned char *stack_mem = (unsigned char *)malloc(tcp_global::TCP_OS_STACK_SIZE);
TEST_ASSERT_NOT_NULL(stack_mem); TEST_ASSERT_NOT_NULL(stack_mem);
@ -154,8 +153,8 @@ void TCPSOCKET_ECHOTEST_NONBLOCK()
TEST_ASSERT_EQUAL(osOK, receiver_thread->start(callback(&queue, &EventQueue::dispatch_forever))); TEST_ASSERT_EQUAL(osOK, receiver_thread->start(callback(&queue, &EventQueue::dispatch_forever)));
for (int pkt_s = pkt_sizes[s_idx]; s_idx < PKTS; ++s_idx) { for (int s_idx = 0; s_idx < sizeof(pkt_sizes) / sizeof(*pkt_sizes); ++s_idx) {
pkt_s = pkt_sizes[s_idx]; int pkt_s = pkt_sizes[s_idx];
bytes2recv = pkt_s; bytes2recv = pkt_s;
bytes2recv_total = pkt_s; bytes2recv_total = pkt_s;

View File

@ -70,17 +70,17 @@ void TLSSOCKET_ECHOTEST()
int recvd; int recvd;
int sent; int sent;
int x = 0; for (int s_idx = 0; s_idx < sizeof(pkt_sizes) / sizeof(*pkt_sizes); s_idx++) {
for (int pkt_s = pkt_sizes[x]; x < PKTS; pkt_s = pkt_sizes[x++]) { int pkt_s = pkt_sizes[s_idx];
fill_tx_buffer_ascii(tls_global::tx_buffer, BUFF_SIZE); fill_tx_buffer_ascii(tls_global::tx_buffer, BUFF_SIZE);
sent = sock->send(tls_global::tx_buffer, pkt_s); sent = sock->send(tls_global::tx_buffer, pkt_s);
if (sent < 0) { if (sent < 0) {
printf("[Round#%02d] network error %d\n", x, sent); printf("[Round#%02d] network error %d\n", s_idx, sent);
TEST_FAIL(); TEST_FAIL();
break; break;
} else if (sent != pkt_s) { } else if (sent != pkt_s) {
printf("[%02d] sock.send return size %d does not match the expectation %d\n", x, sent, pkt_s); printf("[%02d] sock.send return size %d does not match the expectation %d\n", s_idx, sent, pkt_s);
TEST_FAIL(); TEST_FAIL();
break; break;
} }
@ -89,7 +89,7 @@ void TLSSOCKET_ECHOTEST()
while (bytes2recv) { while (bytes2recv) {
recvd = sock->recv(&(tls_global::rx_buffer[sent - bytes2recv]), bytes2recv); recvd = sock->recv(&(tls_global::rx_buffer[sent - bytes2recv]), bytes2recv);
if (recvd < 0) { if (recvd < 0) {
printf("[Round#%02d] network error %d\n", x, recvd); printf("[Round#%02d] network error %d\n", s_idx, recvd);
TEST_FAIL(); TEST_FAIL();
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock->close()); TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock->close());
return; return;
@ -146,7 +146,7 @@ void TLSSOCKET_ECHOTEST_NONBLOCK()
int bytes2send; int bytes2send;
int sent; int sent;
int s_idx = 0; ;
receive_error = false; receive_error = false;
unsigned char *stack_mem = (unsigned char *)malloc(tls_global::TLS_OS_STACK_SIZE); unsigned char *stack_mem = (unsigned char *)malloc(tls_global::TLS_OS_STACK_SIZE);
TEST_ASSERT_NOT_NULL(stack_mem); TEST_ASSERT_NOT_NULL(stack_mem);
@ -158,8 +158,8 @@ void TLSSOCKET_ECHOTEST_NONBLOCK()
event_queue = &queue; event_queue = &queue;
TEST_ASSERT_EQUAL(osOK, receiver_thread->start(callback(&queue, &EventQueue::dispatch_forever))); TEST_ASSERT_EQUAL(osOK, receiver_thread->start(callback(&queue, &EventQueue::dispatch_forever)));
for (int pkt_s = pkt_sizes[s_idx]; s_idx < PKTS; ++s_idx) { for (int s_idx = 0; s_idx < sizeof(pkt_sizes) / sizeof(*pkt_sizes); ++s_idx) {
pkt_s = pkt_sizes[s_idx]; int pkt_s = pkt_sizes[s_idx];
bytes2recv = pkt_s; bytes2recv = pkt_s;
bytes2recv_total = pkt_s; bytes2recv_total = pkt_s;

View File

@ -28,7 +28,7 @@ using namespace utest::v1;
namespace { namespace {
static const int SIGNAL_SIGIO = 0x1; static const int SIGNAL_SIGIO = 0x1;
static const int SIGIO_TIMEOUT = 20000; //[ms] static const int SIGIO_TIMEOUT = 50000; //[ms]
} }
static void _sigio_handler(osThreadId id) static void _sigio_handler(osThreadId id)

View File

@ -68,11 +68,10 @@ void UDPSOCKET_ECHOTEST()
int recvd; int recvd;
int sent; int sent;
int s_idx = 0;
int packets_sent = 0; int packets_sent = 0;
int packets_recv = 0; int packets_recv = 0;
for (int pkt_s = pkt_sizes[s_idx]; s_idx < PKTS; pkt_s = ++s_idx) { for (int s_idx = 0; s_idx < sizeof(pkt_sizes) / sizeof(*pkt_sizes); ++s_idx) {
pkt_s = pkt_sizes[s_idx]; int pkt_s = pkt_sizes[s_idx];
fill_tx_buffer_ascii(tx_buffer, BUFF_SIZE); fill_tx_buffer_ascii(tx_buffer, BUFF_SIZE);
@ -147,15 +146,14 @@ void UDPSOCKET_ECHOTEST_NONBLOCK()
sock.sigio(callback(_sigio_handler)); sock.sigio(callback(_sigio_handler));
int sent; int sent;
int s_idx = 0;
int packets_sent = 0; int packets_sent = 0;
int packets_recv = 0; int packets_recv = 0;
Thread *thread; Thread *thread;
unsigned char *stack_mem = (unsigned char *)malloc(OS_STACK_SIZE); unsigned char *stack_mem = (unsigned char *)malloc(OS_STACK_SIZE);
TEST_ASSERT_NOT_NULL(stack_mem); TEST_ASSERT_NOT_NULL(stack_mem);
for (int pkt_s = pkt_sizes[s_idx]; s_idx < PKTS; ++s_idx) { for (int s_idx = 0; s_idx < sizeof(pkt_sizes) / sizeof(*pkt_sizes); ++s_idx) {
pkt_s = pkt_sizes[s_idx]; int pkt_s = pkt_sizes[s_idx];
thread = new Thread(osPriorityNormal, thread = new Thread(osPriorityNormal,
OS_STACK_SIZE, OS_STACK_SIZE,

View File

@ -27,6 +27,7 @@ using namespace utest::v1;
namespace { namespace {
static const int SIGNAL_SIGIO = 0x1; static const int SIGNAL_SIGIO = 0x1;
static const int SIGIO_TIMEOUT = 5000; //[ms] static const int SIGIO_TIMEOUT = 5000; //[ms]
static const int PKT_NUM = 2;
} }
static void _sigio_handler(osThreadId id) static void _sigio_handler(osThreadId id)
@ -52,7 +53,7 @@ void UDPSOCKET_RECV_TIMEOUT()
Timer timer; Timer timer;
SocketAddress temp_addr; SocketAddress temp_addr;
int pkt_success = 0; int pkt_success = 0;
for (int i = 0; i < 10; i++) { for (int i = 0; i < PKT_NUM; i++) {
TEST_ASSERT_EQUAL(DATA_LEN, sock.sendto(udp_addr, buff, DATA_LEN)); TEST_ASSERT_EQUAL(DATA_LEN, sock.sendto(udp_addr, buff, DATA_LEN));
timer.reset(); timer.reset();
timer.start(); timer.start();
@ -75,6 +76,6 @@ void UDPSOCKET_RECV_TIMEOUT()
pkt_success++; pkt_success++;
} }
TEST_ASSERT(pkt_success >= 5); printf("MBED: %d out of %d packets were received.\n", pkt_success, PKT_NUM);
TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.close()); TEST_ASSERT_EQUAL(NSAPI_ERROR_OK, sock.close());
} }

View File

@ -87,10 +87,12 @@ git checkout master
cd .. cd ..
``` ```
Prepare an `mbed_app.json` configuration file with all the required definitions provided. See [template_mbed_app.txt](template_mbed_app.txt) file for the full list of necessary definitions.
Now build test binaries: Now build test binaries:
```.sh ```.sh
mbed test --compile -t <toolchain> -m <target> -n mbed-os-tests-network-wifi mbed test --compile -t <toolchain> -m <target> --app-config TESTS/network/wifi/template_mbed_app.txt -n mbed-os-tests-network-wifi
``` ```
Running tests Running tests

View File

@ -27,8 +27,8 @@
#include "psa_manifest/sid.h" #include "psa_manifest/sid.h"
#if defined(TARGET_TFM) #if defined(TARGET_TFM)
#include "psa/service.h"
#define MBED_CONF_SPM_IPC_MAX_NUM_OF_CHANNELS TFM_CONN_HANDLE_MAX_NUM #define MBED_CONF_SPM_IPC_MAX_NUM_OF_CHANNELS TFM_CONN_HANDLE_MAX_NUM
#define PSA_MAX_IOVEC 4
#endif #endif
using namespace utest::v1; using namespace utest::v1;

View File

@ -28,7 +28,7 @@
#include "psa_manifest/sid.h" #include "psa_manifest/sid.h"
#if defined(TARGET_TFM) #if defined(TARGET_TFM)
#include "psa/service.h" #define PSA_MAX_IOVEC 4
#endif #endif
using namespace utest::v1; using namespace utest::v1;

View File

@ -29,7 +29,7 @@
#include "psa_manifest/sid.h" #include "psa_manifest/sid.h"
#if defined(TARGET_TFM) #if defined(TARGET_TFM)
#include "psa/service.h" #define PSA_MAX_IOVEC 4
#endif #endif
using namespace utest::v1; using namespace utest::v1;

View File

@ -0,0 +1,23 @@
# Testing the USB HID device with a Linux host
Before running `tests-usb_device-hid` test suite on a Linux machine, please
make sure to install the `hidapi` Python module first, otherwise some test
cases will be skipped. Due to external dependencies for Linux, this module
is not installed during the initial setup, to keep the process as simple
as possible.
For Debian-based Linux distros, the dependencies can be installed as follows
(based on module's [README][1]):
```bash
apt-get install python-dev libusb-1.0-0-dev libudev-dev
pip install --upgrade setuptools
```
To install the `hidapi` module itself, please use the attached
`TESTS/usb_device/hid/requirements.txt` file:
```bash
pip install -r requirements.txt
```
[1]: https://github.com/trezor/cython-hidapi/blob/master/README.rst#install

View File

@ -0,0 +1,384 @@
/*
* Copyright (c) 2018, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE
#error [NOT_SUPPORTED] USB Device not supported for this target
#endif
#include "greentea-client/test_env.h"
#include "utest/utest.h"
#include "unity/unity.h"
#include "mbed.h"
#include <stdlib.h>
#include "usb_phy_api.h"
#include "USBHID.h"
#include "USBMouse.h"
#include "USBKeyboard.h"
// Reuse the VID & PID from basic USB test.
#define USB_HID_VID 0x0d28
#define USB_HID_PID_GENERIC 0x0206
#define USB_HID_PID_KEYBOARD 0x0206
#define USB_HID_PID_MOUSE 0x0206
#define USB_HID_PID_GENERIC2 0x0007
#define MSG_VALUE_LEN 24
#define MSG_KEY_LEN 24
#define MSG_KEY_DEVICE_READY "ready"
#define MSG_KEY_DEVICE_READY "dev_ready"
#define MSG_KEY_HOST_READY "host_ready"
#define MSG_KEY_SERIAL_NUMBER "usb_dev_sn"
#define MSG_KEY_TEST_GET_DESCRIPTOR_HID "test_get_desc_hid"
#define MSG_KEY_TEST_GET_DESCRIPTOR_CFG "test_get_desc_cfg"
#define MSG_KEY_TEST_REQUESTS "test_requests"
#define MSG_KEY_TEST_RAW_IO "test_raw_io"
#define MSG_KEY_TEST_CASE_FAILED "fail"
#define MSG_KEY_TEST_CASE_PASSED "pass"
#define MSG_VALUE_DUMMY "0"
#define MSG_VALUE_NOT_SUPPORTED "not_supported"
#define RAW_IO_REPS 16
#define USB_DEV_SN_LEN (32) // 32 hex digit UUID
#define NONASCII_CHAR ('?')
#define USB_DEV_SN_DESC_SIZE (USB_DEV_SN_LEN * 2 + 2)
const char *default_serial_num = "0123456789";
char usb_dev_sn[USB_DEV_SN_LEN + 1];
using utest::v1::Case;
using utest::v1::Specification;
using utest::v1::Harness;
/**
* Convert a USB string descriptor to C style ASCII
*
* The string placed in str is always null-terminated which may cause the
* loss of data if n is to small. If the length of descriptor string is less
* than n, additional null bytes are written to str.
*
* @param str output buffer for the ASCII string
* @param usb_desc USB string descriptor
* @param n size of str buffer
* @returns number of non-null bytes returned in str or -1 on failure
*/
int usb_string_desc2ascii(char *str, const uint8_t *usb_desc, size_t n)
{
if (str == NULL || usb_desc == NULL || n < 1) {
return -1;
}
// bDescriptorType @ offset 1
if (usb_desc[1] != STRING_DESCRIPTOR) {
return -1;
}
// bLength @ offset 0
const size_t bLength = usb_desc[0];
if (bLength % 2 != 0) {
return -1;
}
size_t s, d;
for (s = 0, d = 2; s < n - 1 && d < bLength; s++, d += 2) {
// handle non-ASCII characters
if (usb_desc[d] > 0x7f || usb_desc[d + 1] != 0) {
str[s] = NONASCII_CHAR;
} else {
str[s] = usb_desc[d];
}
}
int str_len = s;
for (; s < n; s++) {
str[s] = '\0';
}
return str_len;
}
/**
* Convert a C style ASCII to a USB string descriptor
*
* @param usb_desc output buffer for the USB string descriptor
* @param str ASCII string
* @param n size of usb_desc buffer, even number
* @returns number of bytes returned in usb_desc or -1 on failure
*/
int ascii2usb_string_desc(uint8_t *usb_desc, const char *str, size_t n)
{
if (str == NULL || usb_desc == NULL || n < 4) {
return -1;
}
if (n % 2 != 0) {
return -1;
}
size_t s, d;
// set bString (@ offset 2 onwards) as a UNICODE UTF-16LE string
memset(usb_desc, 0, n);
for (s = 0, d = 2; str[s] != '\0' && d < n; s++, d += 2) {
usb_desc[d] = str[s];
}
// set bLength @ offset 0
usb_desc[0] = d;
// set bDescriptorType @ offset 1
usb_desc[1] = STRING_DESCRIPTOR;
return d;
}
class TestUSBHID: public USBHID {
private:
uint8_t _serial_num_descriptor[USB_DEV_SN_DESC_SIZE];
public:
TestUSBHID(uint16_t vendor_id, uint16_t product_id, const char *serial_number = default_serial_num, uint8_t output_report_length = 64, uint8_t input_report_length = 64) :
USBHID(get_usb_phy(), output_report_length, input_report_length, vendor_id, product_id, 0x01)
{
init();
int rc = ascii2usb_string_desc(_serial_num_descriptor, serial_number, USB_DEV_SN_DESC_SIZE);
if (rc < 0) {
ascii2usb_string_desc(_serial_num_descriptor, default_serial_num, USB_DEV_SN_DESC_SIZE);
}
}
virtual ~TestUSBHID()
{
deinit();
}
virtual const uint8_t *string_iserial_desc()
{
return (const uint8_t *) _serial_num_descriptor;
}
// Make this accessible for tests (public).
using USBHID::report_desc_length;
};
class TestUSBMouse: public USBMouse {
private:
uint8_t _serial_num_descriptor[USB_DEV_SN_DESC_SIZE];
public:
TestUSBMouse(uint16_t vendor_id, uint16_t product_id, const char *serial_number = default_serial_num) :
USBMouse(get_usb_phy(), REL_MOUSE, vendor_id, product_id, 0x01)
{
init();
int rc = ascii2usb_string_desc(_serial_num_descriptor, serial_number, USB_DEV_SN_DESC_SIZE);
if (rc < 0) {
ascii2usb_string_desc(_serial_num_descriptor, default_serial_num, USB_DEV_SN_DESC_SIZE);
}
}
virtual ~TestUSBMouse()
{
deinit();
}
virtual const uint8_t *string_iserial_desc()
{
return (const uint8_t *) _serial_num_descriptor;
}
// Make this accessible for tests (public).
using USBHID::report_desc_length;
};
class TestUSBKeyboard: public USBKeyboard {
private:
uint8_t _serial_num_descriptor[USB_DEV_SN_DESC_SIZE];
public:
TestUSBKeyboard(uint16_t vendor_id, uint16_t product_id, const char *serial_number = default_serial_num) :
USBKeyboard(get_usb_phy(), vendor_id, product_id, 0x01)
{
init();
int rc = ascii2usb_string_desc(_serial_num_descriptor, serial_number, USB_DEV_SN_DESC_SIZE);
if (rc < 0) {
ascii2usb_string_desc(_serial_num_descriptor, default_serial_num, USB_DEV_SN_DESC_SIZE);
}
}
virtual ~TestUSBKeyboard()
{
deinit();
}
virtual const uint8_t *string_iserial_desc()
{
return (const uint8_t *) _serial_num_descriptor;
}
// Make this accessible for tests (public).
using USBHID::report_desc_length;
};
/** Test Get_Descriptor request with the HID class descriptors
*
* Given a USB HID class device connected to a host,
* when the host issues the Get_Descriptor(HID) request,
* then the device returns the HID descriptor.
*
* When the host issues the Get_Descriptor(Report) request,
* then the device returns the Report descriptor
* and the size of the descriptor is equal to USBHID::report_desc_length().
*
* Details in USB Device Class Definition for HID, v1.11, paragraph 7.1.
*/
template<typename T, uint16_t PID>
void test_get_hid_class_desc()
{
T usb_hid(USB_HID_VID, PID, usb_dev_sn);
usb_hid.connect();
greentea_send_kv(MSG_KEY_TEST_GET_DESCRIPTOR_HID, MSG_VALUE_DUMMY);
char key[MSG_KEY_LEN + 1] = { };
char value[MSG_VALUE_LEN + 1] = { };
greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN);
TEST_ASSERT_EQUAL_STRING(MSG_KEY_TEST_CASE_PASSED, key);
uint16_t host_report_desc_len;
int num_args = sscanf(value, "%04hx", &host_report_desc_len);
TEST_ASSERT_MESSAGE(num_args != 0 && num_args != EOF, "Invalid data received from host.");
TEST_ASSERT_EQUAL_UINT16(usb_hid.report_desc_length(), host_report_desc_len);
}
/** Test Get_Descriptor request with the Configuration descriptor
*
* Given a USB HID class device connected to a host,
* when the host issues the Get_Descriptor(Configuration) request,
* then the device returns the Configuration descriptor and a HID
* descriptor for each HID interface.
*
* Details in USB Device Class Definition for HID, v1.11, paragraph 7.1.
*/
template<typename T, uint16_t PID>
void test_get_configuration_desc()
{
T usb_hid(USB_HID_VID, PID, usb_dev_sn);
usb_hid.connect();
greentea_send_kv(MSG_KEY_TEST_GET_DESCRIPTOR_CFG, MSG_VALUE_DUMMY);
char key[MSG_KEY_LEN + 1] = { };
char value[MSG_VALUE_LEN + 1] = { };
greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN);
TEST_ASSERT_EQUAL_STRING(MSG_KEY_TEST_CASE_PASSED, key);
}
/** Test HID class requests
*
* Given a USB HID class device connected to a host,
* when the host issues a request specific to the HID class device type,
* then the device returns valid data.
*
* Details in USB Device Class Definition for HID, v1.11,
* paragraph 7.2 and Appendix G.
*/
template<typename T, uint16_t PID>
void test_class_requests()
{
T usb_hid(USB_HID_VID, PID, usb_dev_sn);
usb_hid.connect();
greentea_send_kv(MSG_KEY_TEST_REQUESTS, MSG_VALUE_DUMMY);
char key[MSG_KEY_LEN + 1] = { };
char value[MSG_VALUE_LEN + 1] = { };
greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN);
TEST_ASSERT_EQUAL_STRING(MSG_KEY_TEST_CASE_PASSED, key);
}
/** Test send & read
*
* Given a USB HID class device connected to a host,
* when the device sends input reports with a random data to the host
* and the host sends them back to the device,
* then received output report data is equal to the input report data.
*/
template<uint8_t REPORT_SIZE> // Range [1, MAX_HID_REPORT_SIZE].
void test_generic_raw_io()
{
TestUSBHID usb_hid(USB_HID_VID, USB_HID_PID_GENERIC2, usb_dev_sn, REPORT_SIZE, REPORT_SIZE);
usb_hid.connect();
greentea_send_kv(MSG_KEY_TEST_RAW_IO, REPORT_SIZE);
// Wait for the host HID driver to complete setup.
char key[MSG_KEY_LEN + 1] = { };
char value[MSG_VALUE_LEN + 1] = { };
greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN);
TEST_ASSERT_EQUAL_STRING(MSG_KEY_HOST_READY, key);
if (strcmp(value, MSG_VALUE_NOT_SUPPORTED) == 0) {
TEST_IGNORE_MESSAGE("Test case not supported by host plarform.");
return;
}
// Report ID omitted here. There are no Report ID tags in the Report descriptor.
HID_REPORT input_report = {};
HID_REPORT output_report = {};
for (size_t r = 0; r < RAW_IO_REPS; r++) {
for (size_t i = 0; i < REPORT_SIZE; i++) {
input_report.data[i] = (uint8_t)(rand() % 0x100);
}
input_report.length = REPORT_SIZE;
output_report.length = 0;
TEST_ASSERT(usb_hid.send(&input_report));
TEST_ASSERT(usb_hid.read(&output_report));
TEST_ASSERT_EQUAL_UINT32(input_report.length, output_report.length);
TEST_ASSERT_EQUAL_UINT8_ARRAY(input_report.data, output_report.data, REPORT_SIZE);
}
}
utest::v1::status_t testsuite_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(45, "usb_device_hid");
srand((unsigned) ticker_read_us(get_us_ticker_data()));
utest::v1::status_t status = utest::v1::greentea_test_setup_handler(number_of_cases);
if (status != utest::v1::STATUS_CONTINUE) {
return status;
}
char key[MSG_KEY_LEN + 1] = { };
char usb_dev_uuid[USB_DEV_SN_LEN + 1] = { };
greentea_send_kv(MSG_KEY_DEVICE_READY, MSG_VALUE_DUMMY);
greentea_parse_kv(key, usb_dev_uuid, MSG_KEY_LEN, USB_DEV_SN_LEN + 1);
if (strcmp(key, MSG_KEY_SERIAL_NUMBER) != 0) {
utest_printf("Invalid message key.\n");
return utest::v1::STATUS_ABORT;
}
strncpy(usb_dev_sn, usb_dev_uuid, USB_DEV_SN_LEN + 1);
return status;
}
Case cases[] = {
Case("Configuration descriptor, generic", test_get_configuration_desc<TestUSBHID, USB_HID_PID_GENERIC>),
Case("Configuration descriptor, keyboard", test_get_configuration_desc<TestUSBKeyboard, USB_HID_PID_KEYBOARD>),
Case("Configuration descriptor, mouse", test_get_configuration_desc<TestUSBMouse, USB_HID_PID_MOUSE>),
Case("HID class descriptors, generic", test_get_hid_class_desc<TestUSBHID, USB_HID_PID_GENERIC>),
Case("HID class descriptors, keyboard", test_get_hid_class_desc<TestUSBKeyboard, USB_HID_PID_KEYBOARD>),
Case("HID class descriptors, mouse", test_get_hid_class_desc<TestUSBMouse, USB_HID_PID_MOUSE>),
// HID class requests not supported by Mbed
// Case("HID class requests, generic", test_class_requests<TestUSBHID, USB_HID_PID_GENERIC>),
// Case("HID class requests, keyboard", test_class_requests<TestUSBKeyboard, USB_HID_PID_KEYBOARD>),
// Case("HID class requests, mouse", test_class_requests<TestUSBMouse, USB_HID_PID_MOUSE>),
Case("Raw input/output, 1-byte reports", test_generic_raw_io<1>),
Case("Raw input/output, 20-byte reports", test_generic_raw_io<20>),
Case("Raw input/output, 64-byte reports", test_generic_raw_io<64>),
};
Specification specification((utest::v1::test_setup_handler_t) testsuite_setup, cases);
int main()
{
return !Harness::run(specification);
}

View File

@ -0,0 +1 @@
hidapi>=0.7.99,<0.8.0

View File

@ -104,75 +104,148 @@ You can also set custom compiler flags and other configurations supported by CMa
#### Example #### Example
With the following steps, you can write a simple unit test. In this example, `rtos/Semaphore.cpp` is a class under test. With the following steps, you can write a simple unit test. This example creates dummy classes to be tested, creates and configures unit tests for a class and stubs all external dependencies.
1. Create a directory for unit test files in `UNITTESTS/rtos/Semaphore`. 1. Create the following dummy classes in `mbed-os/example`:
1. Create a test configuration file `UNITTESTS/rtos/Semaphore/unittest.cmake` with the following content:
``` **MyClass.h**
set(unittest-sources
../rtos/Semaphore.cpp
)
set(unittest-test-sources
stubs/mbed_assert_stub.c
stubs/Kernel_stub.cpp
rtos/Semaphore/test_Semaphore.cpp
)
```
1. Stub all external dependencies. Create stubs `UNITTESTS/stubs/mbed_assert_stub.c` and `UNITTESTS/stubs/Kernel_stub.cpp` if they don't already exist.
1. Update header stubs with any missing type or function declarations.
1. Create a test source file `UNITTESTS/rtos/Semaphore/test_Semaphore.cpp` with the following content:
``` ```
#include "gtest/gtest.h" #ifndef MYCLASS_H_
#include "rtos/Semaphore.h" #define MYCLASS_H_
static osStatus_t retval = osOK; namespace example {
static uint32_t count = 0;
// Test stubs class MyClass {
osStatus_t osSemaphoreAcquire(osSemaphoreId_t semaphore_id, uint32_t timeout) public:
{ int myFunction();
return retval; };
}
osStatus_t osSemaphoreDelete(osSemaphoreId_t semaphore_id)
{
return retval;
}
osStatus_t osSemaphoreRelease(osSemaphoreId_t semaphore_id)
{
return retval;
}
uint32_t osSemaphoreGetCount(osSemaphoreId_t semaphore_id)
{
return count;
}
osSemaphoreId_t osSemaphoreNew(uint32_t max_count, uint32_t initial_count, const osSemaphoreAttr_t *attr)
{
return (void *)&count; // Just a dymmy reference
}
class TestSemaphore : public testing::Test {
protected:
rtos::Semaphore *sem;
virtual void SetUp()
{
sem = new rtos::Semaphore();
} }
virtual void TearDown() #endif
{ ```
delete sem;
}
};
TEST_F(TestSemaphore, constructor) **MyClass.cpp**
{
EXPECT_TRUE(sem); ```
} #include "MyClass.h"
``` #include "OtherClass.h"
namespace example {
int MyClass::myFunction() {
OtherClass o = OtherClass();
return o.otherFunction();
}
}
```
**OtherClass.h**
```
#ifndef OTHERCLASS_H_
#define OTHERCLASS_H_
namespace example {
class OtherClass {
public:
int otherFunction();
};
}
#endif
```
**OtherClass.cpp**
```
#include "OtherClass.h"
namespace example {
int OtherClass::otherFunction() {
return 1;
}
}
```
1. Create a directory for MyClass unit tests in `UNITTESTS/example/MyClass`.
1. Create a configuration file and a source file for MyClass unit tests in `UNITTESTS/example/MyClass`:
**unittest.cmake**
```
# Add here additional test specific include paths
set(unittest-includes ${unittest-includes}
../example
)
# Add here classes under test
set(unittest-sources
../example/MyClass.cpp
)
# Add here test classes and stubs
set(unittest-test-sources
example/MyClass/test_MyClass.cpp
stubs/OtherClass_stub.cpp
)
```
**test_MyClass.cpp**
```
#include "gtest/gtest.h"
#include "example/MyClass.h"
class TestMyClass : public testing::Test {
protected:
example::MyClass *obj;
virtual void SetUp()
{
obj = new example::MyClass();
}
virtual void TearDown()
{
delete obj;
}
};
TEST_F(TestMyClass, constructor)
{
EXPECT_TRUE(obj);
}
TEST_F(TestMyClass, myfunction)
{
EXPECT_EQ(obj->myFunction(), 0);
}
```
1. Stub all external dependencies. Create the following stub in `UNITTESTS/stubs`:
**OtherClass_stub.cpp**
```
#include "example/OtherClass.h"
namespace example {
int OtherClass::otherFunction() {
return 0;
}
}
```
This example does not use any Mbed OS code, but if your unit tests do, then remember to update header stubs in `UNITTESTS/target_h` and source stubs in `UNITTESTS/stubs` with any missing type or function declarations.
### Building and running unit tests ### Building and running unit tests
@ -187,6 +260,7 @@ Use Mbed CLI to build and run unit tests. For advanced use, you can run CMake an
* Add `-DCMAKE_MAKE_PROGRAM=<value>`, `-DCMAKE_CXX_COMPILER=<value>` and `-DCMAKE_C_COMPILER=<value>` to use a specific Make program and compilers. * Add `-DCMAKE_MAKE_PROGRAM=<value>`, `-DCMAKE_CXX_COMPILER=<value>` and `-DCMAKE_C_COMPILER=<value>` to use a specific Make program and compilers.
* Add `-DCMAKE_BUILD_TYPE=Debug` for a debug build. * Add `-DCMAKE_BUILD_TYPE=Debug` for a debug build.
* Add `-DCOVERAGE=True` to add coverage compiler flags. * Add `-DCOVERAGE=True` to add coverage compiler flags.
* Add `-Dgtest_disable_pthreads=ON` to run in a single thread.
* See the [CMake manual](https://cmake.org/cmake/help/v3.0/manual/cmake.1.html) for more information. * See the [CMake manual](https://cmake.org/cmake/help/v3.0/manual/cmake.1.html) for more information.
1. Run a Make program to build tests. 1. Run a Make program to build tests.

View File

@ -29,8 +29,11 @@
using namespace mbed; using namespace mbed;
using namespace events; using namespace events;
uint8_t urc_callback_count;
void urc_callback() void urc_callback()
{ {
urc_callback_count++;
} }
void urc2_callback() void urc2_callback()
@ -44,6 +47,7 @@ protected:
void SetUp() void SetUp()
{ {
urc_callback_count = 0;
} }
void TearDown() void TearDown()
@ -954,16 +958,19 @@ TEST_F(TestATHandler, test_ATHandler_resp_start)
filehandle_stub_table_pos = 0; filehandle_stub_table_pos = 0;
at.resp_start(); at.resp_start();
char table7[] = "urc: info\r\nresponseOK\r\n\0"; char table7[] = "urc: info\r\nresponse\r\nOK\r\n\0";
at.flush(); at.flush();
at.clear_error(); at.clear_error();
filehandle_stub_table = table7; filehandle_stub_table = table7;
filehandle_stub_table_pos = 0; filehandle_stub_table_pos = 0;
at.set_urc_handler("urc: ", NULL); mbed::Callback<void()> cb1(&urc_callback);
at.set_urc_handler("urc: ", cb1);
at.resp_start(); // recv_buff: "responseOK\r\n\0" at.resp_start(); // recv_buff: "responseOK\r\n\0"
at.resp_stop(); // consumes to OKCRLF -> OK at.resp_stop(); // consumes to OKCRLF -> OK
EXPECT_TRUE(at.get_last_error() == NSAPI_ERROR_OK); EXPECT_TRUE(at.get_last_error() == NSAPI_ERROR_OK);
EXPECT_TRUE(urc_callback_count == 1);
urc_callback_count = 0;
char table8[] = "urc: info\r\nresponse\0"; char table8[] = "urc: info\r\nresponse\0";
at.flush(); at.flush();
@ -1060,9 +1067,22 @@ TEST_F(TestATHandler, test_ATHandler_resp_stop)
filehandle_stub_table = table3; filehandle_stub_table = table3;
filehandle_stub_table_pos = 0; filehandle_stub_table_pos = 0;
at.resp_start(); at.resp_start();
at.resp_stop(); at.resp_stop();
// Set stop tag for response to CRLF -> resp stop should stop on first CRLF
char table6[] = "line1\r\nline2\r\nOK\r\n";
filehandle_stub_table = table6;
filehandle_stub_table_pos = 0;
at.flush();
at.clear_error();
filehandle_stub_table_pos = 0;
at.resp_start();
at.set_stop_tag("\r\n");
at.resp_stop();
EXPECT_TRUE(at.get_last_error() == NSAPI_ERROR_OK);
char table7[] = "ssssss\0"; char table7[] = "ssssss\0";
filehandle_stub_table = table7; filehandle_stub_table = table7;
filehandle_stub_table_pos = 0; filehandle_stub_table_pos = 0;
@ -1072,6 +1092,38 @@ TEST_F(TestATHandler, test_ATHandler_resp_stop)
filehandle_stub_table_pos = 0; filehandle_stub_table_pos = 0;
at.resp_start("ss", false); at.resp_start("ss", false);
at.resp_stop(); at.resp_stop();
// prefix + URC line + some other line + URC line + URC line + OKCRLF
char table4[] = "line1\r\nline2abcd\r\nline3abcd\r\nline4\r\n\r\nline3\r\nline3\r\nOK\r\n";
filehandle_stub_table = table4;
filehandle_stub_table_pos = 0;
at.flush();
at.clear_error();
filehandle_stub_table_pos = 0;
mbed::Callback<void()> cb1(&urc_callback);
at.set_urc_handler("line3", cb1);
at.resp_start("line2");
at.resp_stop();
EXPECT_TRUE(urc_callback_count == 3);
urc_callback_count = 0;
// URC line + prefix + URC line + some other line + URC line + URC line + some other line + OKCRLF
char table5[] = "line1\r\nline3\r\nline2abcd\r\nline3abcd\r\nline4\r\n\r\nline3\r\nline3\r\nline4\r\nOK\r\n";
filehandle_stub_table = table5;
filehandle_stub_table_pos = 0;
at.flush();
at.clear_error();
filehandle_stub_table_pos = 0;
mbed::Callback<void()> cb2(&urc_callback);
at.set_urc_handler("line3", cb2);
at.resp_start("line2");
at.resp_stop();
EXPECT_TRUE(urc_callback_count == 4);
urc_callback_count = 0;
} }
TEST_F(TestATHandler, test_ATHandler_info_resp) TEST_F(TestATHandler, test_ATHandler_info_resp)

View File

@ -73,9 +73,9 @@ public:
_state_machine = NULL; _state_machine = NULL;
} }
CellularStateMachine *create_state_machine(CellularDevice &device, events::EventQueue &queue) CellularStateMachine *create_state_machine(CellularDevice &device, events::EventQueue &queue, CellularNetwork &nw)
{ {
_state_machine = new CellularStateMachine(device, queue); _state_machine = new CellularStateMachine(device, queue, nw);
return _state_machine; return _state_machine;
} }
@ -171,7 +171,7 @@ TEST_F(TestCellularStateMachine, test_create_delete)
CellularDevice *dev = new myCellularDevice(&fh1); CellularDevice *dev = new myCellularDevice(&fh1);
EXPECT_TRUE(dev); EXPECT_TRUE(dev);
CellularStateMachine *stm = ut.create_state_machine(*dev, *dev->get_queue()); CellularStateMachine *stm = ut.create_state_machine(*dev, *dev->get_queue(), *dev->open_network());
EXPECT_TRUE(stm); EXPECT_TRUE(stm);
ut.delete_state_machine(); ut.delete_state_machine();
@ -187,7 +187,7 @@ TEST_F(TestCellularStateMachine, test_setters)
CellularDevice *dev = new myCellularDevice(&fh1); CellularDevice *dev = new myCellularDevice(&fh1);
EXPECT_TRUE(dev); EXPECT_TRUE(dev);
CellularStateMachine *stm = ut.create_state_machine(*dev, *dev->get_queue()); CellularStateMachine *stm = ut.create_state_machine(*dev, *dev->get_queue(), *dev->open_network());
EXPECT_TRUE(stm); EXPECT_TRUE(stm);
ut.set_cellular_callback(&cellular_callback); ut.set_cellular_callback(&cellular_callback);
@ -215,14 +215,14 @@ TEST_F(TestCellularStateMachine, test_start_dispatch)
CellularDevice *dev = new myCellularDevice(&fh1); CellularDevice *dev = new myCellularDevice(&fh1);
EXPECT_TRUE(dev); EXPECT_TRUE(dev);
CellularStateMachine *stm = ut.create_state_machine(*dev, *dev->get_queue()); CellularStateMachine *stm = ut.create_state_machine(*dev, *dev->get_queue(), *dev->open_network());
EXPECT_TRUE(stm); EXPECT_TRUE(stm);
nsapi_error_t err = ut.start_dispatch(); nsapi_error_t err = ut.start_dispatch();
ASSERT_EQ(NSAPI_ERROR_OK, err); ASSERT_EQ(NSAPI_ERROR_OK, err);
ut.delete_state_machine(); ut.delete_state_machine();
Thread_stub::osStatus_value = osErrorNoMemory; Thread_stub::osStatus_value = osErrorNoMemory;
stm = ut.create_state_machine(*dev, *dev->get_queue()); stm = ut.create_state_machine(*dev, *dev->get_queue(), *dev->open_network());
EXPECT_TRUE(stm); EXPECT_TRUE(stm);
err = ut.start_dispatch(); err = ut.start_dispatch();
ASSERT_EQ(NSAPI_ERROR_NO_MEMORY, err); ASSERT_EQ(NSAPI_ERROR_NO_MEMORY, err);
@ -240,13 +240,13 @@ TEST_F(TestCellularStateMachine, test_stop)
CellularDevice *dev = new AT_CellularDevice(&fh1); CellularDevice *dev = new AT_CellularDevice(&fh1);
EXPECT_TRUE(dev); EXPECT_TRUE(dev);
CellularStateMachine *stm = ut.create_state_machine(*dev, *dev->get_queue()); CellularStateMachine *stm = ut.create_state_machine(*dev, *dev->get_queue(), *dev->open_network());
EXPECT_TRUE(stm); EXPECT_TRUE(stm);
ut.stop(); // nothing created, run through ut.stop(); // nothing created, run through
ut.delete_state_machine(); ut.delete_state_machine();
stm = ut.create_state_machine(*dev, *dev->get_queue()); stm = ut.create_state_machine(*dev, *dev->get_queue(), *dev->open_network());
EXPECT_TRUE(stm); EXPECT_TRUE(stm);
nsapi_error_t err = ut.start_dispatch(); nsapi_error_t err = ut.start_dispatch();
ASSERT_EQ(NSAPI_ERROR_OK, err); ASSERT_EQ(NSAPI_ERROR_OK, err);
@ -254,7 +254,7 @@ TEST_F(TestCellularStateMachine, test_stop)
ut.stop(); // thread is created, now stop will delete it ut.stop(); // thread is created, now stop will delete it
ut.delete_state_machine(); ut.delete_state_machine();
stm = ut.create_state_machine(*dev, *dev->get_queue()); stm = ut.create_state_machine(*dev, *dev->get_queue(), *dev->open_network());
EXPECT_TRUE(stm); EXPECT_TRUE(stm);
err = ut.start_dispatch(); err = ut.start_dispatch();
ASSERT_EQ(NSAPI_ERROR_OK, err); ASSERT_EQ(NSAPI_ERROR_OK, err);
@ -270,7 +270,7 @@ TEST_F(TestCellularStateMachine, test_stop)
ut.stop(); // thread and power are created, now stop will delete them ut.stop(); // thread and power are created, now stop will delete them
ut.delete_state_machine(); ut.delete_state_machine();
stm = ut.create_state_machine(*dev, *dev->get_queue()); stm = ut.create_state_machine(*dev, *dev->get_queue(), *dev->open_network());
EXPECT_TRUE(stm); EXPECT_TRUE(stm);
err = ut.start_dispatch(); err = ut.start_dispatch();
ASSERT_EQ(NSAPI_ERROR_OK, err); ASSERT_EQ(NSAPI_ERROR_OK, err);
@ -294,7 +294,7 @@ TEST_F(TestCellularStateMachine, test_run_to_state)
CellularDevice *dev = new AT_CellularDevice(&fh1); CellularDevice *dev = new AT_CellularDevice(&fh1);
EXPECT_TRUE(dev); EXPECT_TRUE(dev);
CellularStateMachine *stm = ut.create_state_machine(*dev, *dev->get_queue()); CellularStateMachine *stm = ut.create_state_machine(*dev, *dev->get_queue(), *dev->open_network());
EXPECT_TRUE(stm); EXPECT_TRUE(stm);
nsapi_error_t err = ut.start_dispatch(); nsapi_error_t err = ut.start_dispatch();

View File

@ -25,8 +25,8 @@ CellularStubState CellularStateMachine_stub::get_current_target_state = STATE_IN
CellularStubState CellularStateMachine_stub::get_current_current_state = STATE_INIT; CellularStubState CellularStateMachine_stub::get_current_current_state = STATE_INIT;
bool CellularStateMachine_stub::bool_value = false; bool CellularStateMachine_stub::bool_value = false;
CellularStateMachine::CellularStateMachine(CellularDevice &device, events::EventQueue &queue) : CellularStateMachine::CellularStateMachine(CellularDevice &device, events::EventQueue &queue, CellularNetwork &nw) :
_cellularDevice(device), _queue(queue) _cellularDevice(device), _network(nw), _queue(queue)
{ {
} }

View File

@ -6,11 +6,9 @@
*/ */
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include "cmsis.h" #include "cmsis.h"
#include "rtx_os.h" #include "rtx_os.h"
#include "cmsis_os2.h" #include "cmsis_os2.h"
#include "tfm_api.h" #include "tfm_api.h"
#include "tfm_ns_lock.h" #include "tfm_ns_lock.h"

View File

@ -1,54 +0,0 @@
/*
* Copyright (c) 2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __BOOT_RECORD_H__
#define __BOOT_RECORD_H__
#include <stdint.h>
#include <stddef.h>
#include <limits.h>
#ifdef __cplusplus
extern "C" {
#endif
/*!
* \enum shared_data_err_t
*
* \brief Return values for adding data entry to shared memory area
*/
enum shared_memory_err_t {
SHARED_MEMORY_OK = 0,
SHARED_MEMORY_OVERFLOW = 1,
SHARED_MEMORY_OVERWRITE = 2,
/* This is used to force the maximum size */
TLV_TYPE_MAX = INT_MAX
};
/*!
* \brief Add a data item to the shared data area between bootloader and
* runtime SW
*
* \param[in] major_type TLV major type, identify consumer
* \param[in] minor_type TLV minor type, identify TLV type
* \param[in] size length of added data
* \param[in] data pointer to data
*
* \return Returns error code as specified in \ref shared_memory_err_t
*/
enum shared_memory_err_t
boot_add_data_to_shared_area(uint8_t major_type,
uint8_t minor_type,
size_t size,
const uint8_t *data);
#ifdef __cplusplus
}
#endif
#endif /* __BOOT_RECORD_H__ */

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2018, Arm Limited. All rights reserved. * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
* *
* SPDX-License-Identifier: BSD-3-Clause * SPDX-License-Identifier: BSD-3-Clause
* *
@ -16,43 +16,165 @@
extern "C" { extern "C" {
#endif #endif
/* Major numbers to identify the consumer of shared data in runtime SW */ /* Major numbers (4 bit) to identify
#define TLV_MAJOR_CORE 0x0 * the consumer of shared data in runtime SW
#define TLV_MAJOR_IAS 0x1 */
#define TLV_MAJOR_CORE 0x0
#define TLV_MAJOR_IAS 0x1
/* PSA Root of Trust */ /**
#define TLV_MINOR_IAS_PRoT_SHA256 0x00 * The shared data between boot loader and runtime SW is TLV encoded. The
#define TLV_MINOR_IAS_PRoT_SW_VERSION 0x01 * shared data is stored in a well known location in secure memory and this is
#define TLV_MINOR_IAS_PRoT_EPOCH 0x02 * a contract between boot loader and runtime SW.
*
* The structure of shared data must be the following:
* - At the beginning there must be a header: struct shared_data_tlv_header
* This contains a magic number and a size field which covers the entire
* size of the shared data area including this header.
* - After the header there come the entries which are composed from an entry
* header structure: struct shared_data_tlv_entry and the data. In the entry
* header is a type field (tly_type) which identify the consumer of the
* entry in the runtime SW and specify the subtype of that data item. There
* is a size field (tlv_len) which covers the size of the entry header and
* the data. After this structure comes the actual data.
* - Arbitrary number and size of data entry can be in the shared memory area.
*
* This table gives of overview about the tlv_type field in the entry header.
* The tlv_type always composed from a major and minor number. Major number
* identifies the addressee in runtime SW, who should process the data entry.
* Minor number used to encode more info about the data entry. The actual
* definition of minor number could change per major number. In case of boot
* status data, which is going to be processed by initial attestation service
* the minor number is split further to two part: sw_module and claim. The
* sw_module identifies the SW component in the system which the data item
* belongs to and the claim part identifies the exact type of the data.
*
* |---------------------------------------|
* | tlv_type (16) |
* |---------------------------------------|
* | tlv_major(4)| tlv_minor(12) |
* |---------------------------------------|
* | MAJOR_IAS | sw_module(6) | claim(6) |
* |---------------------------------------|
* | MAJOR_CORE | TBD |
* |---------------------------------------|
*/
/* Application Root of Trust */ /* Initial attestation: SW components / SW modules
#define TLV_MINOR_IAS_ARoT_SHA256 0x03 * This list is intended to be adjusted per device. It contains more SW
#define TLV_MINOR_IAS_ARoT_SW_VERSION 0x04 * components than currently available in TF-M project. It serves as an example,
#define TLV_MINOR_IAS_ARoT_EPOCH 0x05 * what kind of SW components might be available.
*/
#define SW_GENERAL 0x00
#define SW_BL2 0x01
#define SW_PROT 0x02
#define SW_AROT 0x03
#define SW_SPE 0x04
#define SW_NSPE 0x05
#define SW_S_NS 0x06
#define SW_MAX 0x07
/* Non-secure processing environment: single non-secure image */ /* Initial attestation: Claim per SW components / SW modules */
#define TLV_MINOR_IAS_NSPE_SHA256 0x06 /* Bits: 0-2 */
#define TLV_MINOR_IAS_NSPE_SW_VERSION 0x07 #define SW_VERSION 0x00
#define TLV_MINOR_IAS_NSPE_EPOCH 0x08 #define SW_SIGNER_ID 0x01
#define SW_EPOCH 0x02
#define SW_TYPE 0x03
/* Bits: 3-5 */
#define SW_MEASURE_VALUE 0x08
#define SW_MEASURE_TYPE 0x09
/* ARoT + PRoT: single secure image */ /* Initial attestation: General claim does not belong any particular SW
#define TLV_MINOR_IAS_S_SHA256 0x09 * component. But they might be part of the boot status.
#define TLV_MINOR_IAS_S_SW_VERSION 0x0a */
#define TLV_MINOR_IAS_S_EPOCH 0x0b #define BOOT_SEED 0x00
#define HW_VERSION 0x01
#define SECURITY_LIFECYCLE 0x02
/* S + NS: combined secure and non-secure image */ /* Minor numbers (12 bit) to identify attestation service related data */
#define TLV_MINOR_IAS_S_NS_SHA256 0x0c #define TLV_MINOR_IAS_BOOT_SEED ((SW_GENERAL << 6) | BOOT_SEED)
#define TLV_MINOR_IAS_S_NS_SW_VERSION 0x0d #define TLV_MINOR_IAS_HW_VERSION ((SW_GENERAL << 6) | HW_VERSION)
#define TLV_MINOR_IAS_S_NS_EPOCH 0x0e #define TLV_MINOR_IAS_SLC ((SW_GENERAL << 6) | SECURITY_LIFECYCLE)
/* Bootloader - It can be more stage */
#define TLV_MINOR_IAS_BL2_MEASURE_VALUE ((SW_BL2 << 6) | SW_MEASURE_VALUE)
#define TLV_MINOR_IAS_BL2_MEASURE_TYPE ((SW_BL2 << 6) | SW_MEASURE_TYPE)
#define TLV_MINOR_IAS_BL2_VERSION ((SW_BL2 << 6) | SW_VERSION)
#define TLV_MINOR_IAS_BL2_SIGNER_ID ((SW_BL2 << 6) | SW_SIGNER_ID)
#define TLV_MINOR_IAS_BL2_EPOCH ((SW_BL2 << 6) | SW_EPOCH)
#define TLV_MINOR_IAS_BL2_TYPE ((SW_BL2 << 6) | SW_TYPE)
/* PROT: PSA Root of Trust */
#define TLV_MINOR_IAS_PROT_MEASURE_VALUE ((SW_PROT << 6) | SW_MEASURE_VALUE)
#define TLV_MINOR_IAS_PROT_MEASURE_TYPE ((SW_PROT << 6) | SW_MEASURE_TYPE)
#define TLV_MINOR_IAS_PROT_VERSION ((SW_PROT << 6) | SW_VERSION)
#define TLV_MINOR_IAS_PROT_SIGNER_ID ((SW_PROT << 6) | SW_SIGNER_ID)
#define TLV_MINOR_IAS_PROT_EPOCH ((SW_PROT << 6) | SW_EPOCH)
#define TLV_MINOR_IAS_PROT_TYPE ((SW_PROT << 6) | SW_TYPE)
/* AROT: Application Root of Trust */
#define TLV_MINOR_IAS_AROT_MEASURE_VALUE ((SW_AROT << 6) | SW_MEASURE_VALUE)
#define TLV_MINOR_IAS_AROT_MEASURE_TYPE ((SW_AROT << 6) | SW_MEASURE_TYPE)
#define TLV_MINOR_IAS_AROT_VERSION ((SW_AROT << 6) | SW_VERSION)
#define TLV_MINOR_IAS_AROT_SIGNER_ID ((SW_AROT << 6) | SW_SIGNER_ID)
#define TLV_MINOR_IAS_AROT_EPOCH ((SW_AROT << 6) | SW_EPOCH)
#define TLV_MINOR_IAS_AROT_TYPE ((SW_AROT << 6) | SW_TYPE)
/* Non-secure processing environment - single non-secure image */
#define TLV_MINOR_IAS_NSPE_MEASURE_VALUE ((SW_NSPE << 6) | SW_MEASURE_VALUE)
#define TLV_MINOR_IAS_NSPE_MEASURE_TYPE ((SW_NSPE << 6) | SW_MEASURE_TYPE)
#define TLV_MINOR_IAS_NSPE_VERSION ((SW_NSPE << 6) | SW_VERSION)
#define TLV_MINOR_IAS_NSPE_SIGNER_ID ((SW_NSPE << 6) | SW_SIGNER_ID)
#define TLV_MINOR_IAS_NSPE_EPOCH ((SW_NSPE << 6) | SW_EPOCH)
#define TLV_MINOR_IAS_NSPE_TYPE ((SW_NSPE << 6) | SW_TYPE)
/* Secure processing environment (ARoT + PRoT) - single secure image */
#define TLV_MINOR_IAS_SPE_MEASURE_VALUE ((SW_SPE << 6) | SW_MEASURE_VALUE)
#define TLV_MINOR_IAS_SPE_MEASURE_TYPE ((SW_SPE << 6) | SW_MEASURE_TYPE)
#define TLV_MINOR_IAS_SPE_VERSION ((SW_SPE << 6) | SW_VERSION)
#define TLV_MINOR_IAS_SPE_SIGNER_ID ((SW_SPE << 6) | SW_SIGNER_ID)
#define TLV_MINOR_IAS_SPE_EPOCH ((SW_SPE << 6) | SW_EPOCH)
#define TLV_MINOR_IAS_SPE_TYPE ((SW_SPE << 6) | SW_TYPE)
/* SPE + NSPE - combined secure and non-secure image */
#define TLV_MINOR_IAS_S_NS_MEASURE_VALUE ((SW_S_NS << 6) | SW_MEASURE_VALUE)
#define TLV_MINOR_IAS_S_NS_MEASURE_TYPE ((SW_S_NS << 6) | SW_MEASURE_TYPE)
#define TLV_MINOR_IAS_S_NS_VERSION ((SW_S_NS << 6) | SW_VERSION)
#define TLV_MINOR_IAS_S_NS_SIGNER_ID ((SW_S_NS << 6) | SW_SIGNER_ID)
#define TLV_MINOR_IAS_S_NS_EPOCH ((SW_S_NS << 6) | SW_EPOCH)
#define TLV_MINOR_IAS_S_NS_TYPE ((SW_S_NS << 6) | SW_TYPE)
/* General macros to handle TLV type */
#define MAJOR_MASK 0xF /* 4 bit */
#define MAJOR_POS 12 /* 12 bit */
#define MINOR_MASK 0xFFF /* 12 bit */
#define SET_TLV_TYPE(major, minor) \
((((major) & MAJOR_MASK) << MAJOR_POS) | ((minor) & MINOR_MASK))
#define GET_MAJOR(tlv_type) ((tlv_type) >> MAJOR_POS)
#define GET_MINOR(tlv_type) ((tlv_type) & MINOR_MASK)
/* Initial attestation specific macros */
#define MODULE_POS 6 /* 6 bit */
#define CLAIM_MASK 0x3F /* 6 bit */
#define MEASUREMENT_CLAIM_POS 3 /* 3 bit */
#define GET_IAS_MODULE(tlv_type) (GET_MINOR(tlv_type) >> MODULE_POS)
#define GET_IAS_CLAIM(tlv_type) (GET_MINOR(tlv_type) & CLAIM_MASK)
#define SET_IAS_MINOR(sw_module, claim) (((sw_module) << 6) | (claim))
#define GET_IAS_MEASUREMENT_CLAIM(ias_claim) ((ias_claim) >> \
MEASUREMENT_CLAIM_POS)
/* Magic value which marks the beginning of shared data area in memory */
#define SHARED_DATA_TLV_INFO_MAGIC 0x2016 #define SHARED_DATA_TLV_INFO_MAGIC 0x2016
/** /**
* Shared data TLV header. All fields in little endian. * Shared data TLV header. All fields in little endian.
* *
* --------------------------- * -----------------------------------
* | tlv_magic | tlv_tot_len | * | tlv_magic(16) | tlv_tot_len(16) |
* --------------------------- * -----------------------------------
*/ */
struct shared_data_tlv_header { struct shared_data_tlv_header {
uint16_t tlv_magic; uint16_t tlv_magic;
@ -64,15 +186,14 @@ struct shared_data_tlv_header {
/** /**
* Shared data TLV entry header format. All fields in little endian. * Shared data TLV entry header format. All fields in little endian.
* *
* --------------------------------------------- * -------------------------------
* | tlv_major_type | tlv_minor_type | tlv_len | * | tlv_type(16) | tlv_len(16) |
* --------------------------------------------- * -------------------------------
* | Raw data | * | Raw data |
* --------------------------------------------- * -------------------------------
*/ */
struct shared_data_tlv_entry { struct shared_data_tlv_entry {
uint8_t tlv_major_type; uint16_t tlv_type;
uint8_t tlv_minor_type;
uint16_t tlv_len; /* size of single TLV entry (including this header). */ uint16_t tlv_len; /* size of single TLV entry (including this header). */
}; };

View File

@ -0,0 +1,16 @@
/*
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
//This file holds description for the current directory. This documentation
//will be included in the Doxygen output.
/*!
\dir
\brief Source code for the TF-M core.
\details This directory holds the source code of the "TF-M core" module.
*/

View File

@ -16,7 +16,7 @@ struct tfm_msg_body_t {
int32_t magic; int32_t magic;
struct tfm_spm_service_t *service; /* RoT service pointer */ struct tfm_spm_service_t *service; /* RoT service pointer */
psa_handle_t handle; /* Connected Service handle */ psa_handle_t handle; /* Connected Service handle */
struct tfm_event_ctx ack_mtx; /* Event for ack reponse */ struct tfm_event_t ack_evnt; /* Event for ack reponse */
psa_msg_t msg; /* PSA message body */ psa_msg_t msg; /* PSA message body */
psa_invec invec[PSA_MAX_IOVEC]; /* Put in/out vectors in msg body */ psa_invec invec[PSA_MAX_IOVEC]; /* Put in/out vectors in msg body */
psa_outvec outvec[PSA_MAX_IOVEC]; psa_outvec outvec[PSA_MAX_IOVEC];

View File

@ -59,7 +59,7 @@ struct tfm_spm_service_t {
struct tfm_spm_ipc_partition_t { struct tfm_spm_ipc_partition_t {
int32_t index; /* Partition index */ int32_t index; /* Partition index */
int32_t id; /* Secure partition ID */ int32_t id; /* Secure partition ID */
struct tfm_event_ctx signal_event; /* Event signal */ struct tfm_event_t signal_evnt; /* Event signal */
uint32_t signals; /* Service signals had been triggered*/ uint32_t signals; /* Service signals had been triggered*/
uint32_t signal_mask; /* Service signal mask passed by psa_wait() */ uint32_t signal_mask; /* Service signal mask passed by psa_wait() */
struct tfm_list_node_t service_list;/* Service list */ struct tfm_list_node_t service_list;/* Service list */

View File

@ -58,7 +58,7 @@ psa_handle_t tfm_svcall_psa_connect(uint32_t *args, int32_t ns_caller);
* handle, in_vec, in_len, out_vec, out_len. * handle, in_vec, in_len, out_vec, out_len.
* \param[in] ns_caller If 'non-zero', call from non-secure client. * \param[in] ns_caller If 'non-zero', call from non-secure client.
* Or from secure client. * Or from secure client.
* \param[in] lr Link register to be stored * \param[in] lr EXC_RETURN value of the SVC.
* *
* \retval >=0 RoT Service-specific status value. * \retval >=0 RoT Service-specific status value.
* \retval <0 RoT Service-specific error code. * \retval <0 RoT Service-specific error code.
@ -74,7 +74,8 @@ psa_handle_t tfm_svcall_psa_connect(uint32_t *args, int32_t ns_caller);
* \arg The message is unrecognized by the RoT * \arg The message is unrecognized by the RoT
* Service or incorrectly formatted. * Service or incorrectly formatted.
*/ */
psa_status_t tfm_svcall_psa_call(uint32_t *args, int32_t ns_caller, uint32_t lr); psa_status_t tfm_svcall_psa_call(uint32_t *args, int32_t ns_caller,
uint32_t lr);
/** /**
* \brief SVC handler for \ref psa_close. * \brief SVC handler for \ref psa_close.
@ -97,7 +98,7 @@ void tfm_svcall_psa_close(uint32_t *args, int32_t ns_caller);
* *
* \param[in] svc_num SVC number * \param[in] svc_num SVC number
* \param[in] ctx Argument context * \param[in] ctx Argument context
* \param[in] lr Link register to be stored * \param[in] lr EXC_RETURN value of the SVC.
* *
* \returns Return values from those who has, * \returns Return values from those who has,
* or PSA_SUCCESS. * or PSA_SUCCESS.

View File

@ -9,78 +9,47 @@
#include "cmsis_compiler.h" #include "cmsis_compiler.h"
#define EVENT_MAGIC 0x65766e74 /* The magic number has two purposes: corruption detection and debug */
#define EVENT_STAT_WAITED 0x0 #define TFM_EVENT_MAGIC 0x65766e74
#define EVENT_STAT_SIGNALED 0x1
struct tfm_event_ctx { struct tfm_event_t {
uint32_t magic; /* 'evnt' */ uint32_t magic; /* 'evnt' */
struct tfm_thrd_ctx *owner; /* waiting thread */ struct tfm_thrd_ctx *owner; /* Event blocked thread */
uint32_t status; /* status */
uint32_t retval; /* return value */
}; };
/* /*
* Initialize an event context. * Initialize an event object.
* *
* Parameters : * Parameters:
* pevt - pointer of event context caller provided * pevnt - The pointer of event object allocated by the caller
* stat - initial status (EVENT_STAT_WAITED or EVENT_STAT_SIGNALED)
*/ */
void __STATIC_INLINE tfm_event_init(struct tfm_event_ctx *pevt, uint32_t stat) void __STATIC_INLINE tfm_event_init(struct tfm_event_t *pevnt)
{ {
pevt->magic = EVENT_MAGIC; pevnt->magic = TFM_EVENT_MAGIC;
pevt->status = stat; pevnt->owner = NULL;
pevt->owner = NULL;
pevt->retval = 0;
} }
/* /*
* Wait on an event. * Wait on an event object.
* *
* Parameters : * Parameters:
* pevt - pointer of event context * pevnt - The pointer of event object allocated by the caller
* *
* Notes : * Notes:
* Thread is blocked if event is not signaled. * Block caller thread by calling this function.
*/ */
void tfm_event_wait(struct tfm_event_ctx *pevt); void tfm_event_wait(struct tfm_event_t *pevnt);
/* /*
* Signal an event. * Wake up an event object.
* *
* Parameters : * Parameters :
* pevt - pointer of event context * pevnt - The pointer of event object allocated by the caller
* retval - Value to be returned to owner
* *
* Notes : * Notes:
* Waiting thread on this event will be running. * Wake up the blocked thread and set parameter 'retval' as the return value.
*/ */
void tfm_event_signal(struct tfm_event_ctx *pevt); void tfm_event_wake(struct tfm_event_t *pevnt, uint32_t retval);
/*
* Peek an event status.
*
* Parameters :
* pevt - pointer of event context
*
* Return :
* Status of event.
*
* Notes :
* This function is used for getting event status without blocking thread.
*/
uint32_t tfm_event_peek(struct tfm_event_ctx *pevt);
/*
* Set event owner return value.
*
* Parameters :
* pevt - pointer of event context
* retval - return value of blocked owner thread
*
* Notes :
* Thread return value is set while thread is to be running.
*/
void tfm_event_owner_retval(struct tfm_event_ctx *pevt, uint32_t retval);
#endif #endif

View File

@ -11,9 +11,9 @@
#include "cmsis.h" #include "cmsis.h"
#include "psa_client.h" #include "psa_client.h"
#include "psa_service.h" #include "psa_service.h"
#include "secure_utilities.h"
#include "tfm_utils.h" #include "tfm_utils.h"
#include "tfm_thread.h" #include "tfm_thread.h"
#include "tfm_memory_utils.h"
/* This file contains the ARCH code for ARM V8M */ /* This file contains the ARCH code for ARM V8M */
@ -91,6 +91,7 @@ void tfm_initialize_context(struct tfm_state_context *ctx,
* thread SP/SP_LIMIT. R2 holds dummy data due to stack operation is 8 bytes * thread SP/SP_LIMIT. R2 holds dummy data due to stack operation is 8 bytes
* aligned. * aligned.
*/ */
#if defined(__ARM_ARCH_8M_MAIN__)
__attribute__((naked)) void PendSV_Handler(void) __attribute__((naked)) void PendSV_Handler(void)
{ {
__ASM( __ASM(
@ -107,6 +108,37 @@ __attribute__((naked)) void PendSV_Handler(void)
"bx lr \n" "bx lr \n"
); );
} }
#elif defined(__ARM_ARCH_8M_BASE__)
__attribute__((naked)) void PendSV_Handler(void)
{
__ASM(
"mrs r0, psp \n"
"mrs r1, psplim \n"
"push {r0, r1, r2, lr} \n"
"push {r4-r7} \n"
"mov r4, r8 \n"
"mov r5, r9 \n"
"mov r6, r10 \n"
"mov r7, r11 \n"
"push {r4-r7} \n"
"mov r0, sp \n"
"bl tfm_pendsv_do_schedule \n"
"pop {r4-r7} \n"
"mov r8, r4 \n"
"mov r9, r5 \n"
"mov r10, r6 \n"
"mov r11, r7 \n"
"pop {r4-r7} \n"
"pop {r0-r3} \n"
"mov lr, r3 \n"
"msr psp, r0 \n"
"msr psplim, r1 \n"
"bx lr \n"
);
}
#else
#error "Unsupported ARM Architecture."
#endif
/* Reserved for future usage */ /* Reserved for future usage */
__attribute__((naked)) void MemManage_Handler(void) __attribute__((naked)) void MemManage_Handler(void)

View File

@ -17,7 +17,7 @@
#include "tfm_utils.h" #include "tfm_utils.h"
#include "tfm_list.h" #include "tfm_list.h"
#include "tfm_pools.h" #include "tfm_pools.h"
#include "secure_utilities.h" #include "tfm_memory_utils.h"
int32_t tfm_pool_init(struct tfm_pool_instance_t *pool, size_t poolsz, int32_t tfm_pool_init(struct tfm_pool_instance_t *pool, size_t poolsz,
size_t chunksz, size_t num) size_t chunksz, size_t num)

View File

@ -25,6 +25,7 @@
#include "tfm_thread.h" #include "tfm_thread.h"
#include "region_defs.h" #include "region_defs.h"
#include "tfm_nspm.h" #include "tfm_nspm.h"
#include "tfm_memory_utils.h"
/* /*
* IPC partitions. * IPC partitions.
@ -348,7 +349,7 @@ struct tfm_msg_body_t *tfm_spm_create_msg(struct tfm_spm_service_t *service,
/* Clear message buffer before using it */ /* Clear message buffer before using it */
tfm_memset(msg, 0, sizeof(struct tfm_msg_body_t)); tfm_memset(msg, 0, sizeof(struct tfm_msg_body_t));
tfm_event_init(&msg->ack_mtx, EVENT_STAT_WAITED); tfm_event_init(&msg->ack_evnt);
msg->magic = TFM_MSG_MAGIC; msg->magic = TFM_MSG_MAGIC;
msg->service = service; msg->service = service;
msg->handle = handle; msg->handle = handle;
@ -405,15 +406,11 @@ int32_t tfm_spm_send_event(struct tfm_spm_service_t *service,
/* Messages put. Update signals */ /* Messages put. Update signals */
service->partition->signals |= service->service_db->signal; service->partition->signals |= service->service_db->signal;
/* Save return value for blocked threads */ tfm_event_wake(&service->partition->signal_evnt,
tfm_event_owner_retval(&service->partition->signal_event, (service->partition->signals &
service->partition->signals & service->partition->signal_mask));
service->partition->signal_mask);
/* Wake waiting thread up */ tfm_event_wait(&msg->ack_evnt);
tfm_event_signal(&service->partition->signal_event);
tfm_event_wait(&msg->ack_mtx);
return IPC_SUCCESS; return IPC_SUCCESS;
} }
@ -598,7 +595,8 @@ void tfm_spm_init(void)
} }
g_spm_ipc_partition[i].index = i; g_spm_ipc_partition[i].index = i;
g_spm_ipc_partition[i].id = tfm_spm_partition_get_partition_id(i); g_spm_ipc_partition[i].id = tfm_spm_partition_get_partition_id(i);
tfm_event_init(&g_spm_ipc_partition[i].signal_event, EVENT_STAT_WAITED);
tfm_event_init(&g_spm_ipc_partition[i].signal_evnt);
tfm_list_init(&g_spm_ipc_partition[i].service_list); tfm_list_init(&g_spm_ipc_partition[i].service_list);
pth = tfm_spm_partition_get_thread_info_ext(i); pth = tfm_spm_partition_get_thread_info_ext(i);

View File

@ -18,9 +18,9 @@
#include "tfm_internal_defines.h" #include "tfm_internal_defines.h"
#include "tfm_message_queue.h" #include "tfm_message_queue.h"
#include "tfm_spm.h" #include "tfm_spm.h"
#include "secure_utilities.h"
#include "tfm_api.h" #include "tfm_api.h"
#include "tfm_secure_api.h" #include "tfm_secure_api.h"
#include "tfm_memory_utils.h"
#define PSA_TIMEOUT_MASK PSA_BLOCK #define PSA_TIMEOUT_MASK PSA_BLOCK
@ -124,17 +124,25 @@ psa_status_t tfm_svcall_psa_call(uint32_t *args, int32_t ns_caller, uint32_t lr)
in_num = (size_t)args[2]; in_num = (size_t)args[2];
outptr = (psa_outvec *)args[3]; outptr = (psa_outvec *)args[3];
/* /*
* 5th parameter is pushed at stack top before SVC; plus exception stacked contents, * 5th parameter is pushed at stack top before SVC, then PE hardware
* 5th parameter is now at 8th position in SVC handler. * stacks the execution context. The size of the context depends on
* However, if thread mode applies FloatPoint, then FloatPoint context is pushed into * various settings:
* stack and then 5th parameter will be args[26]. * - if FP is not used, 5th parameter is at 8th position counting
* from SP;
* - if FP is used and FPCCR_S.TS is 0, 5th parameter is at 26th
* position counting from SP;
* - if FP is used and FPCCR_S.TS is 1, 5th parameter is at 42th
* position counting from SP.
*/ */
if (lr & EXC_RETURN_FPU_FRAME_BASIC) { if (lr & EXC_RETURN_FPU_FRAME_BASIC) {
out_num = (size_t)args[8]; out_num = (size_t)args[8];
} #if defined (__FPU_USED) && (__FPU_USED == 1U)
else { } else if (FPU->FPCCR & FPU_FPCCR_TS_Msk) {
out_num = (size_t)args[42];
#endif
} else {
out_num = (size_t)args[26]; out_num = (size_t)args[26];
} }
} else { } else {
/* /*
* FixMe: From non-secure caller, vec and len are composed into a new * FixMe: From non-secure caller, vec and len are composed into a new
@ -307,8 +315,8 @@ static psa_signal_t tfm_svcall_psa_wait(uint32_t *args)
* runtime context. After new signal(s) are available, the return value * runtime context. After new signal(s) are available, the return value
* is updated with the available signal(s) and blocked thread gets to run. * is updated with the available signal(s) and blocked thread gets to run.
*/ */
if ((timeout == PSA_BLOCK) && ((partition->signals & signal_mask) == 0)) { if (timeout == PSA_BLOCK && (partition->signals & signal_mask) == 0) {
tfm_event_wait(&partition->signal_event); tfm_event_wait(&partition->signal_evnt);
} }
return partition->signals & signal_mask; return partition->signals & signal_mask;
@ -683,7 +691,7 @@ static void update_caller_outvec_len(struct tfm_msg_body_t *msg)
* FixeMe: abstract these part into dedicated functions to avoid * FixeMe: abstract these part into dedicated functions to avoid
* accessing thread context in psa layer * accessing thread context in psa layer
*/ */
TFM_ASSERT(msg->ack_mtx.owner->status == THRD_STAT_BLOCK); TFM_ASSERT(msg->ack_evnt.owner->status == THRD_STAT_BLOCK);
while (msg->msg.out_size[i] != 0) { while (msg->msg.out_size[i] != 0) {
TFM_ASSERT(msg->caller_outvec[i].base == msg->outvec[i].base); TFM_ASSERT(msg->caller_outvec[i].base == msg->outvec[i].base);
@ -800,11 +808,7 @@ static void tfm_svcall_psa_reply(uint32_t *args)
tfm_panic(); tfm_panic();
} }
/* Save return value for blocked threads */ tfm_event_wake(&msg->ack_evnt, ret);
tfm_event_owner_retval(&msg->ack_mtx, ret);
/* Wake waiting thread up */
tfm_event_signal(&msg->ack_mtx);
/* Message should not be unsed anymore */ /* Message should not be unsed anymore */
tfm_spm_free_msg(msg); tfm_spm_free_msg(msg);
@ -852,11 +856,8 @@ static void tfm_svcall_psa_notify(uint32_t *args)
* called psa_wait(). Set the return value with the available signals * called psa_wait(). Set the return value with the available signals
* before wake it up with tfm_event_signal(). * before wake it up with tfm_event_signal().
*/ */
tfm_event_owner_retval(&partition->signal_event, tfm_event_wake(&partition->signal_evnt,
partition->signals & partition->signal_mask); partition->signals & partition->signal_mask);
/* Wake waiting thread up */
tfm_event_signal(&partition->signal_event);
} }
/** /**

View File

@ -9,7 +9,7 @@
#include "tfm_arch_v8m.h" #include "tfm_arch_v8m.h"
#include "tfm_thread.h" #include "tfm_thread.h"
#include "tfm_utils.h" #include "tfm_utils.h"
#include "secure_utilities.h" #include "tfm_memory_utils.h"
/* Force ZERO in case ZI(bss) clear is missing */ /* Force ZERO in case ZI(bss) clear is missing */
static struct tfm_thrd_ctx *p_thrd_head = NULL; static struct tfm_thrd_ctx *p_thrd_head = NULL;

View File

@ -4,59 +4,27 @@
* SPDX-License-Identifier: BSD-3-Clause * SPDX-License-Identifier: BSD-3-Clause
* *
*/ */
#include <inttypes.h>
#include <stdio.h> #include <stdio.h>
#include "tfm_arch_v8m.h"
#include "tfm_thread.h" #include "tfm_thread.h"
#include "tfm_utils.h" #include "tfm_utils.h"
#include "tfm_wait.h" #include "tfm_wait.h"
void tfm_event_wait(struct tfm_event_ctx *pevt) void tfm_event_wait(struct tfm_event_t *pevnt)
{ {
struct tfm_thrd_ctx *curr_thrd = tfm_thrd_curr_thread(); TFM_ASSERT(pevnt && pevnt->magic == TFM_EVENT_MAGIC);
TFM_ASSERT(pevt && pevt->magic == EVENT_MAGIC); pevnt->owner = tfm_thrd_curr_thread();
tfm_thrd_set_status(pevnt->owner, THRD_STAT_BLOCK);
if (pevt->status == EVENT_STAT_WAITED) { tfm_thrd_activate_schedule();
pevt->owner = curr_thrd;
pevt->retval = TFM_STATE_1ST_ARG(&pevt->owner->state_ctx);
tfm_thrd_set_status(pevt->owner, THRD_STAT_BLOCK);
tfm_thrd_activate_schedule();
}
pevt->status = EVENT_STAT_WAITED;
} }
/* Peek the status to see if caller would block. */ void tfm_event_wake(struct tfm_event_t *pevnt, uint32_t retval)
uint32_t tfm_event_peek(struct tfm_event_ctx *pevt)
{ {
TFM_ASSERT(pevt && pevt->magic == EVENT_MAGIC); TFM_ASSERT(pevnt && pevnt->magic == TFM_EVENT_MAGIC);
return pevt->status; if (pevnt->owner && pevnt->owner->status == THRD_STAT_BLOCK) {
} tfm_thrd_set_status(pevnt->owner, THRD_STAT_RUNNING);
tfm_thrd_set_retval(pevnt->owner, retval);
void tfm_event_signal(struct tfm_event_ctx *pevt)
{
TFM_ASSERT(pevt && pevt->magic == EVENT_MAGIC);
pevt->status = EVENT_STAT_SIGNALED;
/*
* Wake the blocked owner up and keep the status as EVENT_STAT_WAITED
* if there is an owner. Or the second event wait caller will return
* without block since status is EVENT_STAT_SIGNALED.
*/
if (pevt->owner && pevt->owner->status == THRD_STAT_BLOCK) {
tfm_thrd_set_status(pevt->owner, THRD_STAT_RUNNING);
tfm_thrd_set_retval(pevt->owner, pevt->retval);
pevt->status = EVENT_STAT_WAITED;
tfm_thrd_activate_schedule(); tfm_thrd_activate_schedule();
} }
} }
void tfm_event_owner_retval(struct tfm_event_ctx *pmtx, uint32_t retval)
{
TFM_ASSERT(pmtx && pmtx->magic == EVENT_MAGIC);
pmtx->retval = retval;
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2017-2018, Arm Limited. All rights reserved. * Copyright (c) 2017-2019, Arm Limited. All rights reserved.
* *
* SPDX-License-Identifier: BSD-3-Clause * SPDX-License-Identifier: BSD-3-Clause
* *
@ -8,9 +8,9 @@
#ifndef __SECURE_UTILITIES_H__ #ifndef __SECURE_UTILITIES_H__
#define __SECURE_UTILITIES_H__ #define __SECURE_UTILITIES_H__
#include <stdio.h>
#include "cmsis_compiler.h" #include "cmsis_compiler.h"
#include "tfm_svc.h" #include "tfm_svc.h"
#include "string.h"
#define EXC_RETURN_INDICATOR (0xF << 28) #define EXC_RETURN_INDICATOR (0xF << 28)
#define EXC_RETURN_SECURITY_STACK_STATUS_MASK (0x3 << 5) #define EXC_RETURN_SECURITY_STACK_STATUS_MASK (0x3 << 5)
@ -106,27 +106,4 @@ __STATIC_INLINE void __set_CONTROL_SPSEL(int32_t SPSEL)
__asm("ISB"); __asm("ISB");
} }
/* FIXME: The following functions are wrappers around standard C library
* functions: memcpy, memcmp, memset
* In long term standard C library might be removed from TF-M project or
* replaced with a secure implementation due to security concerns.
*/
__attribute__ ((always_inline)) __STATIC_INLINE
void tfm_memcpy(void *dest, const void *src, uint32_t size)
{
memcpy(dest, src, size);
}
__attribute__ ((always_inline)) __STATIC_INLINE
int32_t tfm_memcmp(const void * ptr1, const void * ptr2, size_t num)
{
return (memcmp(ptr1, ptr2, num));
}
__attribute__ ((always_inline)) __STATIC_INLINE
void * tfm_memset(void * ptr, int value, size_t num)
{
return (memset(ptr, value, num));
}
#endif /* __SECURE_UTILITIES_H__ */ #endif /* __SECURE_UTILITIES_H__ */

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2018, Arm Limited. All rights reserved. * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
* *
* SPDX-License-Identifier: BSD-3-Clause * SPDX-License-Identifier: BSD-3-Clause
* *
@ -7,7 +7,7 @@
#include <stdint.h> #include <stdint.h>
#include "bl2/include/tfm_boot_status.h" #include "bl2/include/tfm_boot_status.h"
#include "secure_utilities.h" #include "tfm_memory_utils.h"
#include "tfm_internal.h" #include "tfm_internal.h"
#include "tfm_api.h" #include "tfm_api.h"
#include "flash_layout.h" #include "flash_layout.h"
@ -57,13 +57,13 @@ void tfm_core_validate_boot_data(void)
void tfm_core_get_boot_data_handler(uint32_t args[]) void tfm_core_get_boot_data_handler(uint32_t args[])
{ {
uint8_t tlv_major = (uint8_t)args[0]; uint8_t tlv_major = (uint8_t)args[0];
uint8_t *ptr = (uint8_t *)args[1]; uint8_t *buf_start = (uint8_t *)args[1];
uint16_t buf_size = (uint16_t)args[2]; uint16_t buf_size = (uint16_t)args[2];
uint8_t *buf_start = ptr; uint8_t *ptr;
uint32_t running_partition_idx = uint32_t running_partition_idx =
tfm_spm_partition_get_running_partition_idx(); tfm_spm_partition_get_running_partition_idx();
struct shared_data_tlv_header *tlv_header; struct shared_data_tlv_header *tlv_header;
struct shared_data_tlv_entry *tlv_entry; struct shared_data_tlv_entry tlv_entry;
uintptr_t tlv_end, offset; uintptr_t tlv_end, offset;
uint32_t res; uint32_t res;
@ -71,9 +71,9 @@ void tfm_core_get_boot_data_handler(uint32_t args[])
* by the partition * by the partition
*/ */
res = tfm_core_check_buffer_access(running_partition_idx, res = tfm_core_check_buffer_access(running_partition_idx,
(void*)buf_start, (void *)buf_start,
buf_size, buf_size,
2); 2); /* Check 4 bytes alignment */
if (!res) { if (!res) {
/* Not in accessible range, return error */ /* Not in accessible range, return error */
args[0] = TFM_ERROR_INVALID_PARAMETER; args[0] = TFM_ERROR_INVALID_PARAMETER;
@ -97,26 +97,31 @@ void tfm_core_get_boot_data_handler(uint32_t args[])
args[0] = TFM_ERROR_INVALID_PARAMETER; args[0] = TFM_ERROR_INVALID_PARAMETER;
return; return;
} else { } else {
tfm_memcpy(ptr, tlv_header, SHARED_DATA_HEADER_SIZE); tlv_header = (struct shared_data_tlv_header *)buf_start;
ptr += SHARED_DATA_HEADER_SIZE; tlv_header->tlv_magic = SHARED_DATA_TLV_INFO_MAGIC;
tlv_header->tlv_tot_len = SHARED_DATA_HEADER_SIZE;
ptr = (uint8_t *)tlv_header + SHARED_DATA_HEADER_SIZE;
} }
/* Iterates over the TLV section and copy TLVs with requested major /* Iterates over the TLV section and copy TLVs with requested major
* type to the provided buffer. * type to the provided buffer.
*/ */
for(; offset < tlv_end; offset += tlv_entry->tlv_len) { for (; offset < tlv_end; offset += tlv_entry.tlv_len) {
tlv_entry = (struct shared_data_tlv_entry *)offset; /* Create local copy to avoid unaligned access */
if (tlv_entry->tlv_major_type == tlv_major) { tfm_memcpy(&tlv_entry,
(const void *)offset,
SHARED_DATA_ENTRY_HEADER_SIZE);
if (GET_MAJOR(tlv_entry.tlv_type) == tlv_major) {
/* Check buffer overflow */ /* Check buffer overflow */
if ((ptr - buf_start + tlv_entry->tlv_len) > buf_size) { if ((ptr - buf_start + tlv_entry.tlv_len) > buf_size) {
args[0] = TFM_ERROR_INVALID_PARAMETER; args[0] = TFM_ERROR_INVALID_PARAMETER;
return; return;
} }
tfm_memcpy(ptr, (const void *)tlv_entry, tlv_entry->tlv_len); tfm_memcpy(ptr, (const void *)offset, tlv_entry.tlv_len);
ptr += tlv_entry->tlv_len; ptr += tlv_entry.tlv_len;
tlv_header->tlv_tot_len += tlv_entry->tlv_len; tlv_header->tlv_tot_len += tlv_entry.tlv_len;
} }
} }
args[0] = TFM_SUCCESS; args[0] = TFM_SUCCESS;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2017-2018, Arm Limited. All rights reserved. * Copyright (c) 2017-2019, Arm Limited. All rights reserved.
* *
* SPDX-License-Identifier: BSD-3-Clause * SPDX-License-Identifier: BSD-3-Clause
* *
@ -8,7 +8,6 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "cmsis.h"
#include "secure_utilities.h" #include "secure_utilities.h"
#include "arm_acle.h" #include "arm_acle.h"
#include "tfm_svc.h" #include "tfm_svc.h"
@ -16,6 +15,7 @@
#include "region_defs.h" #include "region_defs.h"
#include "tfm_api.h" #include "tfm_api.h"
#include "tfm_internal.h" #include "tfm_internal.h"
#include "tfm_memory_utils.h"
#ifdef TFM_PSA_API #ifdef TFM_PSA_API
#include <stdbool.h> #include <stdbool.h>
#include "tfm_svcalls.h" #include "tfm_svcalls.h"

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#ifndef __TFM_MEMORY_UTILS_H__
#define __TFM_MEMORY_UTILS_H__
#include <string.h>
#include "cmsis_compiler.h"
#ifdef __cplusplus
extern "C" {
#endif
/* FIXME: The following functions are wrappers around standard C library
* functions: memcpy, memcmp, memset
* In long term standard C library might be removed from TF-M project or
* replaced with a secure implementation due to security concerns.
*/
__attribute__ ((always_inline)) __STATIC_INLINE
void *tfm_memcpy(void *dest, const void *src, size_t num)
{
return (memcpy(dest, src, num));
}
__attribute__ ((always_inline)) __STATIC_INLINE
int tfm_memcmp(const void *ptr1, const void *ptr2, size_t num)
{
return (memcmp(ptr1, ptr2, num));
}
__attribute__ ((always_inline)) __STATIC_INLINE
void *tfm_memset(void *ptr, int value, size_t num)
{
return (memset(ptr, value, num));
}
#ifdef __cplusplus
}
#endif
#endif /* __TFM_MEMORY_UTILS_H__ */

View File

@ -8,7 +8,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdbool.h> #include <stdbool.h>
#include "cmsis.h" #include <arm_cmse.h>
#include "tfm_secure_api.h" #include "tfm_secure_api.h"
#include "tfm_nspm.h" #include "tfm_nspm.h"
#include "secure_utilities.h" #include "secure_utilities.h"
@ -22,12 +22,15 @@
#error TFM_LVL is not defined! #error TFM_LVL is not defined!
#endif #endif
#if TFM_LVL == 1
/* Macros to pick linker symbols and allow references to sections */ /* Macros to pick linker symbols and allow references to sections */
#define REGION(a, b, c) a##b##c #define REGION(a, b, c) a##b##c
#define REGION_NAME(a, b, c) REGION(a, b, c) #define REGION_NAME(a, b, c) REGION(a, b, c)
#define REGION_DECLARE(a, b, c) extern uint32_t REGION_NAME(a, b, c) #define REGION_DECLARE(a, b, c) extern uint32_t REGION_NAME(a, b, c)
REGION_DECLARE(Image$$, TFM_UNPRIV_SCRATCH, $$ZI$$Base);
REGION_DECLARE(Image$$, TFM_UNPRIV_SCRATCH, $$ZI$$Limit);
#if TFM_LVL == 1
REGION_DECLARE(Image$$, TFM_SECURE_STACK, $$ZI$$Base); REGION_DECLARE(Image$$, TFM_SECURE_STACK, $$ZI$$Base);
REGION_DECLARE(Image$$, TFM_SECURE_STACK, $$ZI$$Limit); REGION_DECLARE(Image$$, TFM_SECURE_STACK, $$ZI$$Limit);
#endif #endif
@ -38,6 +41,15 @@ REGION_DECLARE(Image$$, TFM_SECURE_STACK, $$ZI$$Limit);
int32_t tfm_secure_lock; int32_t tfm_secure_lock;
static int32_t tfm_secure_api_initializing = 1; static int32_t tfm_secure_api_initializing = 1;
static int32_t is_iovec_api_call(void)
{
uint32_t current_partition_idx =
tfm_spm_partition_get_running_partition_idx();
const struct spm_partition_runtime_data_t *curr_part_data =
tfm_spm_partition_get_runtime_data(current_partition_idx);
return curr_part_data->iovec_api;
}
static int32_t *prepare_partition_ctx( static int32_t *prepare_partition_ctx(
struct tfm_exc_stack_t *svc_ctx, struct tfm_exc_stack_t *svc_ctx,
struct tfm_sfn_req_s *desc_ptr, struct tfm_sfn_req_s *desc_ptr,
@ -62,6 +74,30 @@ static int32_t *prepare_partition_ctx(
return dst; return dst;
} }
static int32_t *prepare_partition_iovec_ctx(
struct tfm_exc_stack_t *svc_ctx,
struct tfm_sfn_req_s *desc_ptr,
struct iovec_args_t *iovec_args,
int32_t *dst)
{
/* XPSR = as was when called, but make sure it's thread mode */
*(--dst) = svc_ctx->XPSR & 0xFFFFFE00;
/* ReturnAddress = resume veneer in new context */
*(--dst) = svc_ctx->RetAddr;
/* LR = sfn address */
*(--dst) = (int32_t)desc_ptr->sfn;
/* R12 = don't care */
*(--dst) = 0;
/* R0-R3 = sfn arguments */
*(--dst) = iovec_args->out_len;
*(--dst) = (uint32_t)iovec_args->out_vec;
*(--dst) = iovec_args->in_len;
*(--dst) = (uint32_t)iovec_args->in_vec;
return dst;
}
static void restore_caller_ctx( static void restore_caller_ctx(
struct tfm_exc_stack_t *svc_ctx, struct tfm_exc_stack_t *svc_ctx,
struct tfm_exc_stack_t *target_ctx) struct tfm_exc_stack_t *target_ctx)
@ -75,6 +111,241 @@ static void restore_caller_ctx(
return; return;
} }
/**
* \brief Check whether a memory range is inside a memory region.
*
* \param[in] p The start address of the range to check
* \param[in] s The size of the range to check
* \param[in] region_start The start address of the region, which should
* contain the range
* \param[in] region_len The size of the region, which should contain the
* range
*
* \return 1 if the region contains the range, 0 otherwise.
*/
static int32_t check_address_range(const void *p, size_t s,
uintptr_t region_start, uint32_t region_len)
{
int32_t range_in_region = 0;
/* Check for overflow in the range parameters */
if ((uintptr_t)p > UINTPTR_MAX-s) {
return 0;
}
/* We trust the region parameters, and don't check for overflow */
/* Calculate the result */
range_in_region = ((uintptr_t)p >= region_start) &&
((uintptr_t)p+s <= region_start+region_len);
return range_in_region;
}
/**
* \brief Check whether the current partition has access to a memory range
*
* This function assumes, that the current MPU configuration is set for the
* partition to be checked. The flags should contain information of the
* execution mode of the partition code (priv/unpriv), and access type
* (read/write) as specified in "ARMv8-M Security Extensions: Requirements on
* Development Tools" chapter "Address range check intrinsic"
*
* \param[in] p The start address of the range to check
* \param[in] s The size of the range to check
* \param[in] flags The flags to pass to the cmse_check_address_range func
*
* \return 1 if the partition has access to the memory range, 0 otherwise.
*/
static int32_t has_access_to_region(const void *p, size_t s, uint32_t flags)
{
int32_t range_access_allowed_by_mpu;
uint32_t scratch_base =
(uint32_t)&REGION_NAME(Image$$, TFM_UNPRIV_SCRATCH, $$ZI$$Base);
uint32_t scratch_limit =
(uint32_t)&REGION_NAME(Image$$, TFM_UNPRIV_SCRATCH, $$ZI$$Limit);
/* Use the TT instruction to check access to the partition's regions*/
range_access_allowed_by_mpu =
cmse_check_address_range((void *)p, s, flags) != NULL;
if (range_access_allowed_by_mpu) {
return 1;
}
/* If the check for the current MPU settings fails, check for the share
* region, only if the partition is secure
*/
if ((flags & CMSE_NONSECURE) == 0) {
if (check_address_range(p, s, scratch_base,
scratch_limit+1-scratch_base)) {
return 1;
}
}
/* If all else fails, check whether the region is in the non-secure
* memory
*/
return
check_address_range(p, s, NS_CODE_START, NS_CODE_LIMIT+1-NS_CODE_START) ||
check_address_range(p, s, NS_DATA_START, NS_DATA_LIMIT+1-NS_DATA_START);
}
/**
* \brief Check whether the current partition has read access to a memory range
*
* This function assumes, that the current MPU configuration is set for the
* partition to be checked.
*
* \param[in] p The start address of the range to check
* \param[in] s The size of the range to check
* \param[in] ns_caller Whether the current partition is a non-secure one
*
* \return 1 if the partition has access to the memory range, 0 otherwise.
*/
static int32_t has_read_access_to_region(const void *p, size_t s,
int32_t ns_caller)
{
uint32_t flags = CMSE_MPU_UNPRIV|CMSE_MPU_READ;
if (ns_caller) {
flags |= CMSE_NONSECURE;
}
return has_access_to_region(p, s, flags);
}
/**
* \brief Check whether the current partition has write access to a memory range
*
* This function assumes, that the current MPU configuration is set for the
* partition to be checked.
*
* \param[in] p The start address of the range to check
* \param[in] s The size of the range to check
* \param[in] ns_caller Whether the current partition is a non-secure one
*
* \return 1 if the partition has access to the memory range, 0 otherwise.
*/
static int32_t has_write_access_to_region(void *p, size_t s, int32_t ns_caller)
{
uint32_t flags = CMSE_MPU_UNPRIV|CMSE_MPU_READWRITE;
if (ns_caller) {
flags |= CMSE_NONSECURE;
}
return has_access_to_region(p, s, flags);
}
/** \brief Check whether the iovec parameters are valid, and the memory ranges
* are in the posession of the calling partition
*
* \param[in] desc_ptr The secure function request descriptor
*
* \return Return /ref TFM_SUCCESS if the iovec parameters are valid, error code
* otherwise as in /ref tfm_status_e
*/
static int32_t tfm_core_check_sfn_parameters(struct tfm_sfn_req_s *desc_ptr)
{
struct psa_invec *in_vec = (psa_invec *)desc_ptr->args[0];
size_t in_len = desc_ptr->args[1];
struct psa_outvec *out_vec = (psa_outvec *)desc_ptr->args[2];
size_t out_len = desc_ptr->args[3];
uint32_t i;
/* The number of vectors are within range. Extra checks to avoid overflow */
if ((in_len > PSA_MAX_IOVEC) || (out_len > PSA_MAX_IOVEC) ||
(in_len + out_len > PSA_MAX_IOVEC)) {
return TFM_ERROR_INVALID_PARAMETER;
}
/* Check whether the caller partition has at write access to the iovec
* structures themselves. Use the TT instruction for this.
*/
if (in_len > 0) {
if ((in_vec == NULL) ||
(has_write_access_to_region(in_vec, sizeof(psa_invec)*in_len,
desc_ptr->ns_caller) != 1)) {
return TFM_ERROR_INVALID_PARAMETER;
}
} else {
if (in_vec != NULL) {
return TFM_ERROR_INVALID_PARAMETER;
}
}
if (out_len > 0) {
if ((out_vec == NULL) ||
(has_write_access_to_region(out_vec, sizeof(psa_outvec)*out_len,
desc_ptr->ns_caller) != 1)) {
return TFM_ERROR_INVALID_PARAMETER;
}
} else {
if (out_vec != NULL) {
return TFM_ERROR_INVALID_PARAMETER;
}
}
/* Check whether the caller partition has access to the data inside the
* iovecs
*/
for (i = 0; i < in_len; ++i) {
if (in_vec[i].len > 0) {
if ((in_vec[i].base == NULL) ||
(has_read_access_to_region(in_vec[i].base, in_vec[i].len,
desc_ptr->ns_caller) != 1)) {
return TFM_ERROR_INVALID_PARAMETER;
}
}
}
for (i = 0; i < out_len; ++i) {
if (out_vec[i].len > 0) {
if ((out_vec[i].base == NULL) ||
(has_write_access_to_region(out_vec[i].base, out_vec[i].len,
desc_ptr->ns_caller) != 1)) {
return TFM_ERROR_INVALID_PARAMETER;
}
}
}
return TFM_SUCCESS;
}
static void tfm_copy_iovec_parameters(struct iovec_args_t *target,
const struct iovec_args_t *source)
{
int i;
target->in_len = source->in_len;
for (i = 0; i < source->in_len; ++i) {
target->in_vec[i].base = source->in_vec[i].base;
target->in_vec[i].len = source->in_vec[i].len;
}
target->out_len = source->out_len;
for (i = 0; i < source->out_len; ++i) {
target->out_vec[i].base = source->out_vec[i].base;
target->out_vec[i].len = source->out_vec[i].len;
}
}
static void tfm_clear_iovec_parameters(struct iovec_args_t *args)
{
int i;
args->in_len = 0;
for (i = 0; i < PSA_MAX_IOVEC; ++i) {
args->in_vec[i].base = NULL;
args->in_vec[i].len = 0;
}
args->out_len = 0;
for (i = 0; i < PSA_MAX_IOVEC; ++i) {
args->out_vec[i].base = NULL;
args->out_vec[i].len = 0;
}
}
static int32_t tfm_start_partition(struct tfm_sfn_req_s *desc_ptr, static int32_t tfm_start_partition(struct tfm_sfn_req_s *desc_ptr,
uint32_t excReturn) uint32_t excReturn)
{ {
@ -89,6 +360,7 @@ static int32_t tfm_start_partition(struct tfm_sfn_req_s *desc_ptr,
struct tfm_exc_stack_t *svc_ctx = (struct tfm_exc_stack_t *)psp; struct tfm_exc_stack_t *svc_ctx = (struct tfm_exc_stack_t *)psp;
uint32_t caller_partition_id; uint32_t caller_partition_id;
int32_t client_id; int32_t client_id;
struct iovec_args_t *iovec_args;
caller_flags = tfm_spm_partition_get_flags(caller_partition_idx); caller_flags = tfm_spm_partition_get_flags(caller_partition_idx);
@ -145,8 +417,13 @@ static int32_t tfm_start_partition(struct tfm_sfn_req_s *desc_ptr,
#if TFM_LVL == 1 #if TFM_LVL == 1
/* Prepare switch to shared secure partition stack */ /* Prepare switch to shared secure partition stack */
/* In case the call is coming from the non-secure world, we save the iovecs
* on the stop of the stack. So the memory area, that can actually be used
* as stack by the partitions starts at a lower address
*/
partition_psp = partition_psp =
(uint32_t)&REGION_NAME(Image$$, TFM_SECURE_STACK, $$ZI$$Limit); (uint32_t)&REGION_NAME(Image$$, TFM_SECURE_STACK, $$ZI$$Limit)-
sizeof(struct iovec_args_t);
partition_psplim = partition_psplim =
(uint32_t)&REGION_NAME(Image$$, TFM_SECURE_STACK, $$ZI$$Base); (uint32_t)&REGION_NAME(Image$$, TFM_SECURE_STACK, $$ZI$$Base);
#else #else
@ -193,16 +470,53 @@ static int32_t tfm_start_partition(struct tfm_sfn_req_s *desc_ptr,
* handler mode * handler mode
*/ */
if ((desc_ptr->ns_caller) || (tfm_secure_api_initializing)) { if ((desc_ptr->ns_caller) || (tfm_secure_api_initializing)) {
/* Prepare the partition context, update stack ptr */ if (desc_ptr->iovec_api == TFM_SFN_API_IOVEC) {
psp = (uint32_t)prepare_partition_ctx( /* Save the iovecs on the common stack. The vectors had been sanity
svc_ctx, desc_ptr, (int32_t *)partition_psp); * checked already, and since then the interrupts have been kept
* disabled. So we can be sure that the vectors haven't been
* tampered with since the check.
*/
iovec_args = (struct iovec_args_t *)
((uint32_t)&REGION_NAME(Image$$, TFM_SECURE_STACK, $$ZI$$Limit)-
sizeof(struct iovec_args_t));
tfm_spm_partition_set_iovec(partition_idx, desc_ptr->args);
tfm_copy_iovec_parameters(iovec_args,
&(curr_part_data->iovec_args));
/* Prepare the partition context, update stack ptr */
psp = (uint32_t)prepare_partition_iovec_ctx(svc_ctx, desc_ptr,
iovec_args,
(int32_t *)partition_psp);
} else {
/* Prepare the partition context, update stack ptr */
psp = (uint32_t)prepare_partition_ctx(svc_ctx, desc_ptr,
(int32_t *)partition_psp);
}
__set_PSP(psp); __set_PSP(psp);
__set_PSPLIM(partition_psplim); __set_PSPLIM(partition_psplim);
} }
#else #else
/* Prepare the partition context, update stack ptr */ if (desc_ptr->iovec_api == TFM_SFN_API_IOVEC) {
psp = (uint32_t)prepare_partition_ctx(svc_ctx, desc_ptr, /* Save the iovecs on the stack of the partition. The vectors had been
(int32_t *)partition_psp); * sanity checked already, and since then the interrupts have been kept
* disabled. So we can be sure that the vectors haven't been tampered
* with since the check.
*/
iovec_args =
(struct iovec_args_t *)(tfm_spm_partition_get_stack_top(partition_idx) -
sizeof(struct iovec_args_t));
tfm_spm_partition_set_iovec(partition_idx, desc_ptr->args);
tfm_copy_iovec_parameters(iovec_args, &(curr_part_data->iovec_args));
/* Prepare the partition context, update stack ptr */
psp = (uint32_t)prepare_partition_iovec_ctx(svc_ctx, desc_ptr,
iovec_args,
(int32_t *)partition_psp);
} else {
/* Prepare the partition context, update stack ptr */
psp = (uint32_t)prepare_partition_ctx(svc_ctx, desc_ptr,
(int32_t *)partition_psp);
}
__set_PSP(psp); __set_PSP(psp);
__set_PSPLIM(partition_psplim); __set_PSPLIM(partition_psplim);
#endif #endif
@ -224,7 +538,9 @@ static int32_t tfm_return_from_partition(uint32_t *excReturn)
uint32_t return_partition_idx; uint32_t return_partition_idx;
uint32_t return_partition_flags; uint32_t return_partition_flags;
uint32_t psp = __get_PSP(); uint32_t psp = __get_PSP();
int i;
struct tfm_exc_stack_t *svc_ctx = (struct tfm_exc_stack_t *)psp; struct tfm_exc_stack_t *svc_ctx = (struct tfm_exc_stack_t *)psp;
struct iovec_args_t *iovec_args;
if (current_partition_idx == SPM_INVALID_PARTITION_IDX) { if (current_partition_idx == SPM_INVALID_PARTITION_IDX) {
return TFM_SECURE_UNLOCK_FAILED; return TFM_SECURE_UNLOCK_FAILED;
@ -304,6 +620,18 @@ static int32_t tfm_return_from_partition(uint32_t *excReturn)
uint32_t psp_stack_bottom = (uint32_t)Image$$ARM_LIB_STACK$$ZI$$Base; uint32_t psp_stack_bottom = (uint32_t)Image$$ARM_LIB_STACK$$ZI$$Base;
__set_PSPLIM(psp_stack_bottom); __set_PSPLIM(psp_stack_bottom);
/* FIXME: The condition should be removed once all the secure service
* calls are done via the iovec veneers */
if (curr_part_data->iovec_api) {
iovec_args = (struct iovec_args_t *)
((uint32_t)&REGION_NAME(Image$$, TFM_SECURE_STACK, $$ZI$$Limit)-
sizeof(struct iovec_args_t));
for (i = 0; i < curr_part_data->iovec_args.out_len; ++i) {
curr_part_data->orig_outvec[i].len = iovec_args->out_vec[i].len;
}
tfm_clear_iovec_parameters(iovec_args);
}
} }
#else #else
/* Restore caller context */ /* Restore caller context */
@ -314,7 +642,20 @@ static int32_t tfm_return_from_partition(uint32_t *excReturn)
__set_PSPLIM(tfm_spm_partition_get_stack_bottom(return_partition_idx)); __set_PSPLIM(tfm_spm_partition_get_stack_bottom(return_partition_idx));
/* Clear the context entry before returning */ /* Clear the context entry before returning */
tfm_spm_partition_set_stack( tfm_spm_partition_set_stack(
current_partition_idx, psp - sizeof(struct tfm_exc_stack_t)); current_partition_idx, psp + sizeof(struct tfm_exc_stack_t));
/* FIXME: The condition should be removed once all the secure service
* calls are done via the iovec veneers */
if (curr_part_data->iovec_api) {
iovec_args = (struct iovec_args_t *)
(tfm_spm_partition_get_stack_top(current_partition_idx) -
sizeof(struct iovec_args_t));
for (i = 0; i < curr_part_data->iovec_args.out_len; ++i) {
curr_part_data->orig_outvec[i].len = iovec_args->out_vec[i].len;
}
tfm_clear_iovec_parameters(iovec_args);
}
#endif #endif
tfm_spm_partition_cleanup_context(current_partition_idx); tfm_spm_partition_cleanup_context(current_partition_idx);
@ -396,12 +737,22 @@ int32_t tfm_core_sfn_request_handler(
res = tfm_check_sfn_req_integrity(desc_ptr); res = tfm_check_sfn_req_integrity(desc_ptr);
if (res != TFM_SUCCESS) { if (res != TFM_SUCCESS) {
ERROR_MSG("Invalid service request!"); ERROR_MSG("Invalid service request!");
return TFM_ERROR_STATUS(res); tfm_secure_api_error_handler();
} }
__disable_irq(); __disable_irq();
desc_ptr->caller_part_idx = tfm_spm_partition_get_running_partition_idx(); desc_ptr->caller_part_idx = tfm_spm_partition_get_running_partition_idx();
if (desc_ptr->iovec_api == TFM_SFN_API_IOVEC) {
res = tfm_core_check_sfn_parameters(desc_ptr);
if (res != TFM_SUCCESS) {
/* The sanity check of iovecs failed. */
__enable_irq();
tfm_secure_api_error_handler();
}
}
res = tfm_core_check_sfn_req_rules(desc_ptr); res = tfm_core_check_sfn_req_rules(desc_ptr);
if (res != TFM_SUCCESS) { if (res != TFM_SUCCESS) {
/* FixMe: error compartmentalization TBD */ /* FixMe: error compartmentalization TBD */
@ -409,7 +760,7 @@ int32_t tfm_core_sfn_request_handler(
desc_ptr->caller_part_idx, SPM_PARTITION_STATE_CLOSED); desc_ptr->caller_part_idx, SPM_PARTITION_STATE_CLOSED);
__enable_irq(); __enable_irq();
ERROR_MSG("Unauthorized service request!"); ERROR_MSG("Unauthorized service request!");
return TFM_ERROR_STATUS(res); tfm_secure_api_error_handler();
} }
res = tfm_start_partition(desc_ptr, excReturn); res = tfm_start_partition(desc_ptr, excReturn);
@ -417,7 +768,7 @@ int32_t tfm_core_sfn_request_handler(
/* FixMe: consider possible fault scenarios */ /* FixMe: consider possible fault scenarios */
__enable_irq(); __enable_irq();
ERROR_MSG("Failed to process service request!"); ERROR_MSG("Failed to process service request!");
return TFM_ERROR_STATUS(res); tfm_secure_api_error_handler();
} }
__enable_irq(); __enable_irq();
@ -432,6 +783,14 @@ int32_t tfm_core_sfn_request_thread_mode(struct tfm_sfn_req_s *desc_ptr)
int32_t *args; int32_t *args;
int32_t retVal; int32_t retVal;
if (desc_ptr->iovec_api == TFM_SFN_API_IOVEC) {
res = tfm_core_check_sfn_parameters(desc_ptr);
if (res != TFM_SUCCESS) {
/* The sanity check of iovecs failed. */
return res;
}
}
/* No excReturn value is needed as no exception handling is used */ /* No excReturn value is needed as no exception handling is used */
res = tfm_core_sfn_request_handler(desc_ptr, 0); res = tfm_core_sfn_request_handler(desc_ptr, 0);
@ -689,6 +1048,8 @@ void tfm_core_memory_permission_check_handler(uint32_t *svc_args)
uint32_t tfm_core_partition_request_svc_handler( uint32_t tfm_core_partition_request_svc_handler(
struct tfm_exc_stack_t *svc_ctx, uint32_t excReturn) struct tfm_exc_stack_t *svc_ctx, uint32_t excReturn)
{ {
struct tfm_sfn_req_s *desc_ptr;
if (!(excReturn & EXC_RETURN_STACK_PROCESS)) { if (!(excReturn & EXC_RETURN_STACK_PROCESS)) {
/* Service request SVC called with MSP active. /* Service request SVC called with MSP active.
* Either invalid configuration for Thread mode or SVC called * Either invalid configuration for Thread mode or SVC called
@ -699,7 +1060,7 @@ uint32_t tfm_core_partition_request_svc_handler(
tfm_secure_api_error_handler(); tfm_secure_api_error_handler();
} }
struct tfm_sfn_req_s *desc_ptr = (struct tfm_sfn_req_s *)svc_ctx->R0; desc_ptr = (struct tfm_sfn_req_s *)svc_ctx->R0;
if (tfm_core_sfn_request_handler(desc_ptr, excReturn) != TFM_SUCCESS) { if (tfm_core_sfn_request_handler(desc_ptr, excReturn) != TFM_SUCCESS) {
tfm_secure_api_error_handler(); tfm_secure_api_error_handler();
@ -724,17 +1085,19 @@ uint32_t tfm_core_partition_return_handler(uint32_t lr)
/* Store return value from secure partition */ /* Store return value from secure partition */
int32_t retVal = *(int32_t *)__get_PSP(); int32_t retVal = *(int32_t *)__get_PSP();
if ((retVal > TFM_SUCCESS) && if (!is_iovec_api_call()) {
(retVal < TFM_PARTITION_SPECIFIC_ERROR_MIN)) { if ((retVal > TFM_SUCCESS) &&
/* Secure function returned a reserved value */ (retVal < TFM_PARTITION_SPECIFIC_ERROR_MIN)) {
/* Secure function returned a reserved value */
#ifdef TFM_CORE_DEBUG #ifdef TFM_CORE_DEBUG
LOG_MSG("Invalid return value from secure partition!"); LOG_MSG("Invalid return value from secure partition!");
#endif #endif
/* FixMe: error can be traced to specific secure partition /* FixMe: error can be traced to specific secure partition
* and Core is not compromised. Error handling flow can be * and Core is not compromised. Error handling flow can be
* refined * refined
*/ */
tfm_secure_api_error_handler(); tfm_secure_api_error_handler();
}
} }
res = tfm_return_from_partition(&lr); res = tfm_return_from_partition(&lr);

View File

@ -29,6 +29,9 @@
#define TFM_ERROR_STATUS(status) (TFM_PARTITION_BUSY) #define TFM_ERROR_STATUS(status) (TFM_PARTITION_BUSY)
#endif #endif
#define TFM_SFN_API_LEGACY 0
#define TFM_SFN_API_IOVEC 1
#ifndef TFM_LVL #ifndef TFM_LVL
#error TFM_LVL is not defined! #error TFM_LVL is not defined!
#endif #endif
@ -42,6 +45,7 @@ struct tfm_sfn_req_s {
sfn_t sfn; sfn_t sfn;
int32_t *args; int32_t *args;
uint32_t caller_part_idx; uint32_t caller_part_idx;
int32_t iovec_api;
int32_t ns_caller : 1; int32_t ns_caller : 1;
}; };
@ -73,8 +77,9 @@ extern int32_t tfm_core_validate_secure_caller(void);
extern int32_t tfm_core_get_caller_client_id(int32_t *caller_client_id); extern int32_t tfm_core_get_caller_client_id(int32_t *caller_client_id);
extern int32_t tfm_core_memory_permission_check( extern int32_t tfm_core_memory_permission_check(const void *ptr,
void *ptr, uint32_t size, int32_t access); uint32_t size,
int32_t access);
extern int32_t tfm_core_get_boot_data(uint8_t major_type, void *ptr, extern int32_t tfm_core_get_boot_data(uint8_t major_type, void *ptr,
uint32_t len); uint32_t len);
@ -83,12 +88,16 @@ int32_t tfm_core_sfn_request(struct tfm_sfn_req_s *desc_ptr);
int32_t tfm_core_sfn_request_thread_mode(struct tfm_sfn_req_s *desc_ptr); int32_t tfm_core_sfn_request_thread_mode(struct tfm_sfn_req_s *desc_ptr);
#define TFM_CORE_IOVEC_SFN_REQUEST(id, fn, a, b, c, d) \
return tfm_core_partition_request(id, fn, TFM_SFN_API_IOVEC, \
(int32_t)a, (int32_t)b, (int32_t)c, (int32_t)d)
#define TFM_CORE_SFN_REQUEST(id, fn, a, b, c, d) \ #define TFM_CORE_SFN_REQUEST(id, fn, a, b, c, d) \
return tfm_core_partition_request(id, fn, (int32_t)a, (int32_t)b, \ return tfm_core_partition_request(id, fn, TFM_SFN_API_LEGACY, \
(int32_t)c, (int32_t)d) (int32_t)a, (int32_t)b, (int32_t)c, (int32_t)d)
__attribute__ ((always_inline)) __STATIC_INLINE __attribute__ ((always_inline)) __STATIC_INLINE
int32_t tfm_core_partition_request(uint32_t id, void *fn, int32_t tfm_core_partition_request(uint32_t id, void *fn, int32_t iovec_api,
int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4) int32_t arg1, int32_t arg2, int32_t arg3, int32_t arg4)
{ {
int32_t args[4] = {arg1, arg2, arg3, arg4}; int32_t args[4] = {arg1, arg2, arg3, arg4};
@ -98,6 +107,7 @@ int32_t tfm_core_partition_request(uint32_t id, void *fn,
desc.sfn = fn; desc.sfn = fn;
desc.args = args; desc.args = args;
desc.ns_caller = cmse_nonsecure_caller(); desc.ns_caller = cmse_nonsecure_caller();
desc.iovec_api = iovec_api;
if (__get_active_exc_num() != EXC_NUM_THREAD_MODE) { if (__get_active_exc_num() != EXC_NUM_THREAD_MODE) {
/* FixMe: Error severity TBD */ /* FixMe: Error severity TBD */
return TFM_ERROR_GENERIC; return TFM_ERROR_GENERIC;

View File

@ -100,8 +100,9 @@ __attribute__((naked)) int32_t tfm_core_sfn_request(
#endif #endif
__attribute__((naked)) __attribute__((naked))
int32_t tfm_core_memory_permission_check( int32_t tfm_core_memory_permission_check(const void *ptr,
void *ptr, uint32_t len, int32_t access) uint32_t len,
int32_t access)
{ {
__ASM( __ASM(
"SVC %0\n" "SVC %0\n"

View File

@ -0,0 +1,17 @@
/*
* Copyright (c) 2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
//This file holds description for the current directory. This documentation
//will be included in the Doxygen output.
/*!
\dir
\brief Include files for the TF-M.
\details This directory currently only holds the include file for the SPM
module.
*/

View File

@ -0,0 +1,16 @@
/*
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
//This file holds description for the current directory. This documentation
//will be included in the Doxygen output.
/*!
\dir
\brief Source code for the Secure Partition Manager.
\details This directory holds the source code of the "TF-M SPM" module.
*/

View File

@ -11,13 +11,12 @@
#include <string.h> #include <string.h>
#include "spm_api.h" #include "spm_api.h"
#include "platform/include/tfm_spm_hal.h" #include "platform/include/tfm_spm_hal.h"
#include "secure_utilities.h" #include "tfm_memory_utils.h"
#include "spm_db_setup.h" #include "spm_db_setup.h"
#include "tfm_internal.h" #include "tfm_internal.h"
#include "tfm_api.h" #include "tfm_api.h"
#include "tfm_nspm.h" #include "tfm_nspm.h"
#include "secure_fw/core/tfm_core.h" #include "secure_fw/core/tfm_core.h"
#include "platform_retarget.h"
#include "tfm_peripherals_def.h" #include "tfm_peripherals_def.h"
#include "spm_partition_defs.h" #include "spm_partition_defs.h"
@ -156,6 +155,7 @@ enum spm_err_t tfm_spm_partition_init(void)
desc.args = args; desc.args = args;
desc.ns_caller = 0; desc.ns_caller = 0;
desc.iovec_api = TFM_SFN_API_IOVEC;
desc.sfn = (sfn_t)part->static_data.partition_init; desc.sfn = (sfn_t)part->static_data.partition_init;
desc.sp_id = part->static_data.partition_id; desc.sp_id = part->static_data.partition_id;
res = tfm_core_sfn_request(&desc); res = tfm_core_sfn_request(&desc);
@ -314,6 +314,29 @@ enum spm_err_t tfm_spm_partition_set_share(uint32_t partition_idx,
return ret; return ret;
} }
void tfm_spm_partition_set_iovec(uint32_t partition_idx, int32_t *args)
{
struct spm_partition_runtime_data_t *runtime_data =
&g_spm_partition_db.partitions[partition_idx].runtime_data;
int32_t i;
runtime_data->iovec_args.in_len = args[1];
for (i = 0; i < runtime_data->iovec_args.in_len; ++i) {
runtime_data->iovec_args.in_vec[i].base =
((psa_invec *)args[0])[i].base;
runtime_data->iovec_args.in_vec[i].len = ((psa_invec *)args[0])[i].len;
}
runtime_data->iovec_args.out_len = args[3];
for (i = 0; i < runtime_data->iovec_args.out_len; ++i) {
runtime_data->iovec_args.out_vec[i].base =
((psa_outvec *)args[2])[i].base;
runtime_data->iovec_args.out_vec[i].len =
((psa_outvec *)args[2])[i].len;
}
runtime_data->orig_outvec = (psa_outvec *)args[2];
runtime_data->iovec_api = 1;
}
uint32_t tfm_spm_partition_get_running_partition_idx(void) uint32_t tfm_spm_partition_get_running_partition_idx(void)
{ {
return g_spm_partition_db.running_partition_idx; return g_spm_partition_db.running_partition_idx;
@ -323,6 +346,20 @@ void tfm_spm_partition_cleanup_context(uint32_t partition_idx)
{ {
struct spm_partition_desc_t *partition = struct spm_partition_desc_t *partition =
&(g_spm_partition_db.partitions[partition_idx]); &(g_spm_partition_db.partitions[partition_idx]);
int32_t i;
partition->runtime_data.caller_partition_idx = SPM_INVALID_PARTITION_IDX; partition->runtime_data.caller_partition_idx = SPM_INVALID_PARTITION_IDX;
partition->runtime_data.share = 0; partition->runtime_data.share = 0;
partition->runtime_data.iovec_args.in_len = 0;
for (i = 0; i < PSA_MAX_IOVEC; ++i) {
partition->runtime_data.iovec_args.in_vec[i].base = 0;
partition->runtime_data.iovec_args.in_vec[i].len = 0;
}
partition->runtime_data.iovec_args.out_len = 0;
for (i = 0; i < PSA_MAX_IOVEC; ++i) {
partition->runtime_data.iovec_args.out_vec[i].base = 0;
partition->runtime_data.iovec_args.out_vec[i].len = 0;
}
partition->runtime_data.orig_outvec = 0;
partition->runtime_data.iovec_api = 0;
} }

View File

@ -9,6 +9,7 @@
#define __SPM_API_H__ #define __SPM_API_H__
/* This file contains the apis exported by the SPM to tfm core */ /* This file contains the apis exported by the SPM to tfm core */
#include "tfm_api.h"
#include "spm_partition_defs.h" #include "spm_partition_defs.h"
#include "secure_fw/core/tfm_secure_api.h" #include "secure_fw/core/tfm_secure_api.h"
@ -37,6 +38,20 @@ enum spm_part_flag_mask_t {
SPM_PART_FLAG_IPC = 0x04 SPM_PART_FLAG_IPC = 0x04
}; };
/**
* \brief Holds the iovec parameters that are passed to a service
*
* \note The size of the structure is (and have to be) multiple of 8 bytes
*/
struct iovec_args_t {
psa_invec in_vec[PSA_MAX_IOVEC]; /*!< Array of psa_invec objects */
size_t in_len; /*!< Number psa_invec objects in in_vec
*/
psa_outvec out_vec[PSA_MAX_IOVEC]; /*!< Array of psa_outvec objects */
size_t out_len; /*!< Number psa_outvec objects in out_vec
*/
};
/** /**
* \brief Runtime context information of a partition * \brief Runtime context information of a partition
*/ */
@ -47,6 +62,13 @@ struct spm_partition_runtime_data_t {
uint32_t share; uint32_t share;
uint32_t stack_ptr; uint32_t stack_ptr;
uint32_t lr; uint32_t lr;
int32_t iovec_api; /*!< Whether the function in the partition
* had been called using the iovec API.
* FIXME: Remove the field once this is the
* only option
*/
struct iovec_args_t iovec_args;
psa_outvec *orig_outvec;
}; };
@ -60,6 +82,7 @@ struct spm_partition_runtime_data_t {
*/ */
uint32_t get_partition_idx(uint32_t partition_id); uint32_t get_partition_idx(uint32_t partition_id);
#if TFM_LVL != 1
/** /**
* \brief Configure isolated sandbox for a partition * \brief Configure isolated sandbox for a partition
* *
@ -104,28 +127,6 @@ uint32_t tfm_spm_partition_get_stack_bottom(uint32_t partition_idx);
*/ */
uint32_t tfm_spm_partition_get_stack_top(uint32_t partition_idx); uint32_t tfm_spm_partition_get_stack_top(uint32_t partition_idx);
/**
* \brief Get the id of the partition for its index from the db
*
* \param[in] partition_idx Partition index
*
* \return Partition ID for that partition
*
* \note This function doesn't check if partition_idx is valid.
*/
uint32_t tfm_spm_partition_get_partition_id(uint32_t partition_idx);
/**
* \brief Get the flags associated with a partition
*
* \param[in] partition_idx Partition index
*
* \return Flags associated with the partition
*
* \note This function doesn't check if partition_idx is valid.
*/
uint32_t tfm_spm_partition_get_flags(uint32_t partition_idx);
/** /**
* \brief Get the start of the zero-initialised region for a partition * \brief Get the start of the zero-initialised region for a partition
* *
@ -172,6 +173,28 @@ uint32_t tfm_spm_partition_get_rw_start(uint32_t partition_idx);
*/ */
uint32_t tfm_spm_partition_get_rw_limit(uint32_t partition_idx); uint32_t tfm_spm_partition_get_rw_limit(uint32_t partition_idx);
/**
* \brief Save stack pointer for partition in database
*
* \param[in] partition_idx Partition index
* \param[in] stack_ptr Stack pointer to be stored
*
* \note This function doesn't check if partition_idx is valid.
*/
void tfm_spm_partition_set_stack(uint32_t partition_idx, uint32_t stack_ptr);
#endif
/**
* \brief Get the flags associated with a partition
*
* \param[in] partition_idx Partition index
*
* \return Flags associated with the partition
*
* \note This function doesn't check if partition_idx is valid.
*/
uint32_t tfm_spm_partition_get_flags(uint32_t partition_idx);
/** /**
* \brief Get the current runtime data of a partition * \brief Get the current runtime data of a partition
* *
@ -192,16 +215,6 @@ const struct spm_partition_runtime_data_t *
*/ */
uint32_t tfm_spm_partition_get_running_partition_idx(void); uint32_t tfm_spm_partition_get_running_partition_idx(void);
/**
* \brief Save stack pointer for partition in database
*
* \param[in] partition_idx Partition index
* \param[in] stack_ptr Stack pointer to be stored
*
* \note This function doesn't check if partition_idx is valid.
*/
void tfm_spm_partition_set_stack(uint32_t partition_id, uint32_t stack_ptr);
/** /**
* \brief Save stack pointer and link register for partition in database * \brief Save stack pointer and link register for partition in database
* *
@ -214,6 +227,17 @@ void tfm_spm_partition_set_stack(uint32_t partition_id, uint32_t stack_ptr);
void tfm_spm_partition_store_context(uint32_t partition_idx, void tfm_spm_partition_store_context(uint32_t partition_idx,
uint32_t stack_ptr, uint32_t lr); uint32_t stack_ptr, uint32_t lr);
/**
* \brief Get the id of the partition for its index from the db
*
* \param[in] partition_idx Partition index
*
* \return Partition ID for that partition
*
* \note This function doesn't check if partition_idx is valid.
*/
uint32_t tfm_spm_partition_get_partition_id(uint32_t partition_idx);
/** /**
* \brief Set the current state of a partition * \brief Set the current state of a partition
* *
@ -221,7 +245,7 @@ void tfm_spm_partition_store_context(uint32_t partition_idx,
* \param[in] state The state to be set * \param[in] state The state to be set
* *
* \note This function doesn't check if partition_idx is valid. * \note This function doesn't check if partition_idx is valid.
* \note The \ref state has to have the value set of \ref spm_part_state_t. * \note The state has to have the value set of \ref spm_part_state_t.
*/ */
void tfm_spm_partition_set_state(uint32_t partition_idx, uint32_t state); void tfm_spm_partition_set_state(uint32_t partition_idx, uint32_t state);
@ -261,6 +285,24 @@ void tfm_spm_partition_set_caller_client_id(uint32_t partition_idx,
enum spm_err_t tfm_spm_partition_set_share(uint32_t partition_idx, enum spm_err_t tfm_spm_partition_set_share(uint32_t partition_idx,
uint32_t share); uint32_t share);
/**
* \brief Set the iovec parameters for the partition
*
* \param[in] partition_idx Partition index
* \param[in] args The arguments of the secure function
*
* args is expected to be of type int32_t[4] where:
* args[0] is in_vec
* args[1] is in_len
* args[2] is out_vec
* args[3] is out_len
*
* \note This function doesn't check if partition_idx is valid.
* \note This function assumes that the iovecs that are passed in args are
* valid, and does no sanity check on them at all.
*/
void tfm_spm_partition_set_iovec(uint32_t partition_idx, int32_t *args);
/** /**
* \brief Initialize partition database * \brief Initialize partition database
* *

View File

@ -8,6 +8,7 @@
#ifndef __SPM_DB_H__ #ifndef __SPM_DB_H__
#define __SPM_DB_H__ #define __SPM_DB_H__
#ifdef TFM_PSA_API #ifdef TFM_PSA_API
#include "tfm_thread.h" #include "tfm_thread.h"
#endif #endif
@ -15,9 +16,7 @@
struct spm_partition_desc_t; struct spm_partition_desc_t;
struct spm_partition_db_t; struct spm_partition_db_t;
uint32_t get_partition_idx(uint32_t partition_id); typedef psa_status_t(*sp_init_function)(void);
typedef int32_t(*sp_init_function)(void);
#define TFM_PARTITION_TYPE_APP "APPLICATION-ROT" #define TFM_PARTITION_TYPE_APP "APPLICATION-ROT"
#define TFM_PARTITION_TYPE_PSA "PSA-ROT" #define TFM_PARTITION_TYPE_PSA "PSA-ROT"

View File

@ -17,10 +17,10 @@
* Gets the index of a partition in the partition db based on the partition ID * Gets the index of a partition in the partition db based on the partition ID
* provided as a parameter. * provided as a parameter.
* *
* \param[in] partition_id The ID of the partition * \param[in] partition_idx The index of the partition
* *
* \return \ref INVALID_PARTITION_IDX if the provided ID is invalid. The index * \return \ref INVALID_PARTITION_IDX if the provided index is invalid. The
* of the partition otherwise. * index of the partition otherwise.
*/ */
uint32_t get_partition_idx(uint32_t partition_id); uint32_t get_partition_idx(uint32_t partition_id);
@ -66,8 +66,13 @@ struct spm_partition_db_t {
#define PARTITION_INIT_RUNTIME_DATA(data, partition) \ #define PARTITION_INIT_RUNTIME_DATA(data, partition) \
do { \ do { \
data.partition_state = SPM_PARTITION_STATE_UNINIT; \ data.partition_state = SPM_PARTITION_STATE_UNINIT; \
/* The top of the stack is reserved for the iovec */ \
/* parameters of the service called. That's why in */ \
/* data.stack_ptr we extract sizeof(struct iovec_args_t) */ \
/* from the limit. */ \
data.stack_ptr = \ data.stack_ptr = \
PART_REGION_ADDR(partition, _STACK$$ZI$$Limit); \ PART_REGION_ADDR(partition, _STACK$$ZI$$Limit - \
sizeof(struct iovec_args_t)); \
} while (0) } while (0)
#endif #endif

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2018, Arm Limited. All rights reserved. * Copyright (c) 2018-2019, Arm Limited. All rights reserved.
* *
* SPDX-License-Identifier: BSD-3-Clause * SPDX-License-Identifier: BSD-3-Clause
* *

View File

@ -223,8 +223,6 @@ void psa_notify(int32_t partition_id);
/** /**
* \brief Clear the PSA_DOORBELL signal. * \brief Clear the PSA_DOORBELL signal.
* *
* \param[in] void
*
* \retval void Success. * \retval void Success.
* \retval "Does not return" The Secure Partition's doorbell signal is not * \retval "Does not return" The Secure Partition's doorbell signal is not
* currently asserted. * currently asserted.

View File

@ -13,7 +13,7 @@ extern "C" {
#endif #endif
#include <stdint.h> #include <stdint.h>
#include "interface/include/psa_client.h" #include "psa_client.h"
#define TFM_INVALID_CLIENT_ID 0 #define TFM_INVALID_CLIENT_ID 0
@ -35,13 +35,18 @@ extern "C" {
*/ */
#define TFM_CLIENT_ID_IS_NS(client_id) ((client_id)<0) #define TFM_CLIENT_ID_IS_NS(client_id) ((client_id)<0)
/* Maximum number of input and output vectors */
#define PSA_MAX_IOVEC (4)
/* FixMe: sort out DEBUG compile option and limit return value options /* FixMe: sort out DEBUG compile option and limit return value options
* on external interfaces */ * on external interfaces */
/* Note: /* For secure functions using prorietary signatures
* TFM will only return values recognized and parsed by TFM core. * TFM will only return values recognized and parsed by TFM core.
* Service return codes are not automatically passed on to REE. * Service return codes are not automatically passed on to REE.
* Any non-zero return value is interpreted as an error that may trigger * Any non-zero return value is interpreted as an error that may trigger
* TEE error handling flow. * TEE error handling flow.
* For secure functions using the veneers in secure_fw/ns_callable/tfm_veneers.c
* (iovec API) this limitation does not apply.
*/ */
enum tfm_status_e enum tfm_status_e
{ {

View File

@ -18,6 +18,7 @@
#if defined(TARGET_TFM) #if defined(TARGET_TFM)
#include "interface/include/psa_service.h" #include "interface/include/psa_service.h"
#include "secure_fw/core/ipc/include/tfm_utils.h"
#define SPM_PANIC(format, ...) tfm_panic() #define SPM_PANIC(format, ...) tfm_panic()
#elif defined(TARGET_MBED_SPM) #elif defined(TARGET_MBED_SPM)

View File

@ -17,6 +17,7 @@
*/ */
#include <stdint.h> #include <stdint.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <stddef.h> #include <stddef.h>
#include "tfm_plat_boot_seed.h" #include "tfm_plat_boot_seed.h"
@ -154,7 +155,7 @@ enum psa_attest_err_t attest_get_caller_client_id(int32_t *caller_id)
/* Boot seed data is part of bootloader status*/ /* Boot seed data is part of bootloader status*/
enum tfm_plat_err_t tfm_plat_get_boot_seed(uint32_t size, uint8_t *buf) enum tfm_plat_err_t tfm_plat_get_boot_seed(uint32_t size, uint8_t *buf)
{ {
return PSA_ATTEST_ERR_CLAIM_UNAVAILABLE; return (enum tfm_plat_err_t)PSA_ATTEST_ERR_CLAIM_UNAVAILABLE;
} }
/** /**
@ -181,13 +182,13 @@ enum tfm_plat_err_t tfm_plat_get_instance_id(uint32_t *size, uint8_t *buf)
/* HW version data is part of bootloader status*/ /* HW version data is part of bootloader status*/
enum tfm_plat_err_t tfm_plat_get_hw_version(uint32_t *size, uint8_t *buf) enum tfm_plat_err_t tfm_plat_get_hw_version(uint32_t *size, uint8_t *buf)
{ {
return PSA_ATTEST_ERR_CLAIM_UNAVAILABLE; return (enum tfm_plat_err_t)PSA_ATTEST_ERR_CLAIM_UNAVAILABLE;
} }
enum tfm_plat_err_t tfm_plat_get_implementation_id(uint32_t *size, uint8_t *buf) enum tfm_plat_err_t tfm_plat_get_implementation_id(uint32_t *size, uint8_t *buf)
{ {
memcpy(buf, impl_id_data, *size); memcpy(buf, impl_id_data, *size);
return PSA_ATTEST_ERR_SUCCESS; return (enum tfm_plat_err_t)PSA_ATTEST_ERR_SUCCESS;
} }
/* Temporary Implementation of security lifecycle data: mandatory claim. /* Temporary Implementation of security lifecycle data: mandatory claim.

View File

@ -25,6 +25,7 @@
#include "psa_attest_inject_key.h" #include "psa_attest_inject_key.h"
#include "psa_inject_attestation_key_impl.h" #include "psa_inject_attestation_key_impl.h"
#include "attestation.h" #include "attestation.h"
#include <stdlib.h>
#include <string.h> #include <string.h>
#include "psa/crypto.h" #include "psa/crypto.h"

View File

@ -120,6 +120,10 @@ static psa_status_t psa_mac_setup(psa_mac_operation_t *operation,
psa_algorithm_t alg, psa_algorithm_t alg,
psa_sec_function_t func) psa_sec_function_t func)
{ {
if (operation->handle != PSA_NULL_HANDLE) {
return (PSA_ERROR_BAD_STATE);
}
psa_crypto_ipc_t psa_crypto_ipc = { psa_crypto_ipc_t psa_crypto_ipc = {
.func = func, .func = func,
.handle = key_handle, .handle = key_handle,
@ -133,6 +137,9 @@ static psa_status_t psa_mac_setup(psa_mac_operation_t *operation,
return (status); return (status);
} }
status = ipc_call(&operation->handle, &in_vec, 1, NULL, 0, false); status = ipc_call(&operation->handle, &in_vec, 1, NULL, 0, false);
if (status != PSA_SUCCESS) {
ipc_close(&operation->handle);
}
return (status); return (status);
} }
@ -168,6 +175,9 @@ psa_status_t psa_mac_update(psa_mac_operation_t *operation,
}; };
psa_status_t status = ipc_call(&operation->handle, in_vec, 2, NULL, 0, false); psa_status_t status = ipc_call(&operation->handle, in_vec, 2, NULL, 0, false);
if (status != PSA_SUCCESS) {
ipc_close(&operation->handle);
}
return (status); return (status);
} }
@ -240,6 +250,10 @@ psa_status_t psa_hash_abort(psa_hash_operation_t *operation)
psa_status_t psa_hash_setup(psa_hash_operation_t *operation, psa_status_t psa_hash_setup(psa_hash_operation_t *operation,
psa_algorithm_t alg) psa_algorithm_t alg)
{ {
if (operation->handle != PSA_NULL_HANDLE) {
return (PSA_ERROR_BAD_STATE);
}
psa_crypto_ipc_t psa_crypto_ipc = { psa_crypto_ipc_t psa_crypto_ipc = {
.func = PSA_HASH_SETUP, .func = PSA_HASH_SETUP,
.handle = 0, .handle = 0,
@ -253,6 +267,9 @@ psa_status_t psa_hash_setup(psa_hash_operation_t *operation,
return (status); return (status);
} }
status = ipc_call(&operation->handle, &in_vec, 1, NULL, 0, false); status = ipc_call(&operation->handle, &in_vec, 1, NULL, 0, false);
if (status != PSA_SUCCESS) {
ipc_close(&operation->handle);
}
return (status); return (status);
} }
@ -272,6 +289,9 @@ psa_status_t psa_hash_update(psa_hash_operation_t *operation,
}; };
psa_status_t status = ipc_call(&operation->handle, in_vec, 2, NULL, 0, false); psa_status_t status = ipc_call(&operation->handle, in_vec, 2, NULL, 0, false);
if (status != PSA_SUCCESS) {
ipc_close(&operation->handle);
}
return (status); return (status);
} }
@ -986,6 +1006,10 @@ psa_status_t psa_key_derivation(psa_crypto_generator_t *generator,
size_t label_length, size_t label_length,
size_t capacity) size_t capacity)
{ {
if (generator->handle != PSA_NULL_HANDLE) {
return (PSA_ERROR_BAD_STATE);
}
psa_crypto_derivation_ipc_t psa_crypto_ipc = { psa_crypto_derivation_ipc_t psa_crypto_ipc = {
.func = PSA_KEY_DERIVATION, .func = PSA_KEY_DERIVATION,
.handle = key_handle, .handle = key_handle,
@ -1004,6 +1028,9 @@ psa_status_t psa_key_derivation(psa_crypto_generator_t *generator,
return (status); return (status);
} }
status = ipc_call(&generator->handle, in_vec, 3, NULL, 0, false); status = ipc_call(&generator->handle, in_vec, 3, NULL, 0, false);
if (status != PSA_SUCCESS) {
ipc_close(&generator->handle);
}
return (status); return (status);
} }
@ -1013,6 +1040,10 @@ psa_status_t psa_key_agreement(psa_crypto_generator_t *generator,
size_t peer_key_length, size_t peer_key_length,
psa_algorithm_t alg) psa_algorithm_t alg)
{ {
if (generator->handle != PSA_NULL_HANDLE) {
return (PSA_ERROR_BAD_STATE);
}
psa_crypto_derivation_ipc_t psa_crypto_ipc = { psa_crypto_derivation_ipc_t psa_crypto_ipc = {
.func = PSA_KEY_AGREEMENT, .func = PSA_KEY_AGREEMENT,
.handle = private_key_handle, .handle = private_key_handle,
@ -1030,6 +1061,9 @@ psa_status_t psa_key_agreement(psa_crypto_generator_t *generator,
return (status); return (status);
} }
status = ipc_call(&generator->handle, in_vec, 2, NULL, 0, false); status = ipc_call(&generator->handle, in_vec, 2, NULL, 0, false);
if (status != PSA_SUCCESS) {
ipc_close(&generator->handle);
}
return (status); return (status);
} }
@ -1055,12 +1089,17 @@ psa_status_t psa_generator_abort(psa_crypto_generator_t *generator)
/****************************************************************/ /****************************************************************/
/* SYMMETRIC */ /* SYMMETRIC */
/****************************************************************/ /****************************************************************/
psa_status_t psa_cipher_encrypt_setup(psa_cipher_operation_t *operation, static psa_status_t psa_cipher_setup(psa_cipher_operation_t *operation,
psa_key_handle_t key_handle, psa_key_handle_t key_handle,
psa_algorithm_t alg) psa_algorithm_t alg,
psa_sec_function_t func)
{ {
if (operation->handle != PSA_NULL_HANDLE) {
return (PSA_ERROR_BAD_STATE);
}
psa_crypto_ipc_t psa_crypto_ipc = { psa_crypto_ipc_t psa_crypto_ipc = {
.func = PSA_CIPHER_ENCRYPT_SETUP, .func = func,
.handle = key_handle, .handle = key_handle,
.alg = alg .alg = alg
}; };
@ -1072,6 +1111,17 @@ psa_status_t psa_cipher_encrypt_setup(psa_cipher_operation_t *operation,
return (status); return (status);
} }
status = ipc_call(&operation->handle, &in_vec, 1, NULL, 0, false); status = ipc_call(&operation->handle, &in_vec, 1, NULL, 0, false);
if (status != PSA_SUCCESS) {
ipc_close(&operation->handle);
}
return (status);
}
psa_status_t psa_cipher_encrypt_setup(psa_cipher_operation_t *operation,
psa_key_handle_t key_handle,
psa_algorithm_t alg)
{
psa_status_t status = psa_cipher_setup(operation, key_handle, alg, PSA_CIPHER_ENCRYPT_SETUP);
return (status); return (status);
} }
@ -1079,19 +1129,7 @@ psa_status_t psa_cipher_decrypt_setup(psa_cipher_operation_t *operation,
psa_key_handle_t key_handle, psa_key_handle_t key_handle,
psa_algorithm_t alg) psa_algorithm_t alg)
{ {
psa_crypto_ipc_t psa_crypto_ipc = { psa_status_t status = psa_cipher_setup(operation, key_handle, alg, PSA_CIPHER_DECRYPT_SETUP);
.func = PSA_CIPHER_DECRYPT_SETUP,
.handle = key_handle,
.alg = alg
};
psa_invec in_vec = { &psa_crypto_ipc, sizeof(psa_crypto_ipc) };
psa_status_t status = ipc_connect(PSA_SYMMETRIC_ID, &operation->handle);
if (status != PSA_SUCCESS) {
return (status);
}
status = ipc_call(&operation->handle, &in_vec, 1, NULL, 0, false);
return (status); return (status);
} }
@ -1114,6 +1152,9 @@ psa_status_t psa_cipher_generate_iv(psa_cipher_operation_t *operation,
}; };
psa_status_t status = ipc_call(&operation->handle, &in_vec, 1, out_vec, 2, false); psa_status_t status = ipc_call(&operation->handle, &in_vec, 1, out_vec, 2, false);
if (status != PSA_SUCCESS) {
ipc_close(&operation->handle);
}
return (status); return (status);
} }
@ -1133,6 +1174,9 @@ psa_status_t psa_cipher_set_iv(psa_cipher_operation_t *operation,
}; };
psa_status_t status = ipc_call(&operation->handle, in_vec, 2, NULL, 0, false); psa_status_t status = ipc_call(&operation->handle, in_vec, 2, NULL, 0, false);
if (status != PSA_SUCCESS) {
ipc_close(&operation->handle);
}
return (status); return (status);
} }
@ -1160,6 +1204,9 @@ psa_status_t psa_cipher_update(psa_cipher_operation_t *operation,
}; };
psa_status_t status = ipc_call(&operation->handle, in_vec, 2, out_vec, 2, false); psa_status_t status = ipc_call(&operation->handle, in_vec, 2, out_vec, 2, false);
if (status != PSA_SUCCESS) {
ipc_close(&operation->handle);
}
return (status); return (status);
} }

View File

@ -15,6 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
#include <stdlib.h>
#include <string.h> #include <string.h>
#include "psa/client.h" #include "psa/client.h"
#include "psa/service.h" #include "psa/service.h"
@ -25,6 +26,8 @@
#if defined(TARGET_MBED_SPM) #if defined(TARGET_MBED_SPM)
#include "kv_config.h" #include "kv_config.h"
#else
int kv_init_storage_config();
#endif #endif
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -15,8 +15,12 @@
*/ */
#if DEVICE_SERIAL && DEVICE_INTERRUPTIN && defined(MBED_CONF_EVENTS_PRESENT) && defined(MBED_CONF_NSAPI_PRESENT) && defined(MBED_CONF_RTOS_PRESENT) #if DEVICE_SERIAL && DEVICE_INTERRUPTIN && defined(MBED_CONF_EVENTS_PRESENT) && defined(MBED_CONF_NSAPI_PRESENT) && defined(MBED_CONF_RTOS_PRESENT)
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
#include <inttypes.h>
#include <string.h> #include <string.h>
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include "ESP8266.h" #include "ESP8266.h"
@ -601,7 +605,7 @@ nsapi_error_t ESP8266::send(int id, const void *data, uint32_t amount)
set_timeout(ESP8266_SEND_TIMEOUT); set_timeout(ESP8266_SEND_TIMEOUT);
_busy = false; _busy = false;
_error = false; _error = false;
if (!_parser.send("AT+CIPSEND=%d,%lu", id, amount)) { if (!_parser.send("AT+CIPSEND=%d,%" PRIu32, id, amount)) {
tr_debug("ESP8266::send(): AT+CIPSEND failed"); tr_debug("ESP8266::send(): AT+CIPSEND failed");
goto END; goto END;
} }
@ -734,7 +738,7 @@ int32_t ESP8266::_recv_tcp_passive(int id, void *data, uint32_t amount, uint32_t
amount = amount > 2048 ? 2048 : amount; amount = amount > 2048 ? 2048 : amount;
// NOTE: documentation v3.0 says '+CIPRECVDATA:<data_len>,' but it's not how the FW responds... // NOTE: documentation v3.0 says '+CIPRECVDATA:<data_len>,' but it's not how the FW responds...
bool done = _parser.send("AT+CIPRECVDATA=%d,%lu", id, amount) bool done = _parser.send("AT+CIPRECVDATA=%d,%" PRIu32, id, amount)
&& _parser.recv("OK\n"); && _parser.recv("OK\n");
_sock_i[id].tcp_data = NULL; _sock_i[id].tcp_data = NULL;
@ -1052,7 +1056,7 @@ void ESP8266::_oob_tcp_data_hdlr()
MBED_ASSERT(_sock_active_id >= 0 && _sock_active_id < 5); MBED_ASSERT(_sock_active_id >= 0 && _sock_active_id < 5);
if (!_parser.recv("%ld:", &len)) { if (!_parser.recv("%" SCNd32 ":", &len)) {
return; return;
} }

View File

@ -174,13 +174,20 @@ void ESP8266Interface::_connect_async()
return; return;
} }
_connect_retval = _esp.connect(ap_ssid, ap_pass); _connect_retval = _esp.connect(ap_ssid, ap_pass);
int timeleft_ms = ESP8266_INTERFACE_CONNECT_TIMEOUT_MS - _conn_timer.read_ms();
if (_connect_retval == NSAPI_ERROR_OK || _connect_retval == NSAPI_ERROR_AUTH_FAILURE if (_connect_retval == NSAPI_ERROR_OK || _connect_retval == NSAPI_ERROR_AUTH_FAILURE
|| _connect_retval == NSAPI_ERROR_NO_SSID) { || _connect_retval == NSAPI_ERROR_NO_SSID
|| ((_if_blocking == true) && (timeleft_ms <= 0))) {
_connect_event_id = 0; _connect_event_id = 0;
_conn_timer.stop();
if (timeleft_ms <= 0) {
_connect_retval = NSAPI_ERROR_CONNECTION_TIMEOUT;
}
_if_connected.notify_all(); _if_connected.notify_all();
} else { } else {
// Postpone to give other stuff time to run // Postpone to give other stuff time to run
_connect_event_id = _global_event_queue->call_in(ESP8266_CONNECT_TIMEOUT, callback(this, &ESP8266Interface::_connect_async)); _connect_event_id = _global_event_queue->call_in(ESP8266_INTERFACE_CONNECT_INTERVAL_MS,
callback(this, &ESP8266Interface::_connect_async));
if (!_connect_event_id) { if (!_connect_event_id) {
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER, MBED_ERROR_CODE_ENOMEM), \ MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER, MBED_ERROR_CODE_ENOMEM), \
"ESP8266Interface::_connect_async(): unable to add event to queue. Increase \"events.shared-eventsize\"\n"); "ESP8266Interface::_connect_async(): unable to add event to queue. Increase \"events.shared-eventsize\"\n");
@ -223,6 +230,9 @@ int ESP8266Interface::connect()
_connect_retval = NSAPI_ERROR_NO_CONNECTION; _connect_retval = NSAPI_ERROR_NO_CONNECTION;
MBED_ASSERT(!_connect_event_id); MBED_ASSERT(!_connect_event_id);
_conn_timer.stop();
_conn_timer.reset();
_conn_timer.start();
_connect_event_id = _global_event_queue->call(callback(this, &ESP8266Interface::_connect_async)); _connect_event_id = _global_event_queue->call(callback(this, &ESP8266Interface::_connect_async));
if (!_connect_event_id) { if (!_connect_event_id) {
@ -305,7 +315,7 @@ int ESP8266Interface::disconnect()
_initialized = false; _initialized = false;
nsapi_error_t status = _conn_status_to_error(); nsapi_error_t status = _conn_status_to_error();
if (status == NSAPI_ERROR_NO_CONNECTION || !get_ip_address()) { if (status == NSAPI_ERROR_NO_CONNECTION) {
return NSAPI_ERROR_NO_CONNECTION; return NSAPI_ERROR_NO_CONNECTION;
} }

View File

@ -19,6 +19,7 @@
#if DEVICE_SERIAL && DEVICE_INTERRUPTIN && defined(MBED_CONF_EVENTS_PRESENT) && defined(MBED_CONF_NSAPI_PRESENT) && defined(MBED_CONF_RTOS_PRESENT) #if DEVICE_SERIAL && DEVICE_INTERRUPTIN && defined(MBED_CONF_EVENTS_PRESENT) && defined(MBED_CONF_NSAPI_PRESENT) && defined(MBED_CONF_RTOS_PRESENT)
#include "drivers/DigitalOut.h" #include "drivers/DigitalOut.h"
#include "drivers/Timer.h"
#include "ESP8266/ESP8266.h" #include "ESP8266/ESP8266.h"
#include "events/EventQueue.h" #include "events/EventQueue.h"
#include "events/mbed_shared_queues.h" #include "events/mbed_shared_queues.h"
@ -34,6 +35,9 @@
#define ESP8266_SOCKET_COUNT 5 #define ESP8266_SOCKET_COUNT 5
#define ESP8266_INTERFACE_CONNECT_INTERVAL_MS (5000)
#define ESP8266_INTERFACE_CONNECT_TIMEOUT_MS (2 * ESP8266_CONNECT_TIMEOUT + ESP8266_INTERFACE_CONNECT_INTERVAL_MS)
#ifdef TARGET_FF_ARDUINO #ifdef TARGET_FF_ARDUINO
#ifndef MBED_CONF_ESP8266_TX #ifndef MBED_CONF_ESP8266_TX
#define MBED_CONF_ESP8266_TX D1 #define MBED_CONF_ESP8266_TX D1
@ -82,6 +86,9 @@ public:
* *
* Attempts to connect to a WiFi network. * Attempts to connect to a WiFi network.
* *
* If interface is configured blocking it will timeout after up to
* ESP8266_INTERFACE_CONNECT_TIMEOUT_MS + ESP8266_CONNECT_TIMEOUT ms.
*
* @param ssid Name of the network to connect to * @param ssid Name of the network to connect to
* @param pass Security passphrase to connect to the network * @param pass Security passphrase to connect to the network
* @param security Type of encryption for connection (Default: NSAPI_SECURITY_NONE) * @param security Type of encryption for connection (Default: NSAPI_SECURITY_NONE)
@ -355,6 +362,7 @@ private:
// connect status reporting // connect status reporting
nsapi_error_t _conn_status_to_error(); nsapi_error_t _conn_status_to_error();
mbed::Timer _conn_timer;
// Drivers's socket info // Drivers's socket info
struct _sock_info { struct _sock_info {

View File

@ -55,7 +55,7 @@ TDBStore includes the following design basics:
All writes occur sequentially on the physical storage as records, superseding the previous ones for the same key. Each data record is written right after the last written one. If a key is updated, a new record with this key is written, overriding the previous value of this key. If a key is deleted, a new record with a "deleted" flag is added. All writes occur sequentially on the physical storage as records, superseding the previous ones for the same key. Each data record is written right after the last written one. If a key is updated, a new record with this key is written, overriding the previous value of this key. If a key is deleted, a new record with a "deleted" flag is added.
Writes expect the storage to be erased. However, TDBStore takes the "erase as you go" approcah, meaning that when it crosses a sector boundary, it checks whether the next sector is erased. If not, it erases the next sector. This saves time during initialization and garbage collection. Writes expect the storage to be erased. However, TDBStore takes the "erase as you go" approach, meaning that when it crosses a sector boundary, it checks whether the next sector is erased. If not, it erases the next sector. This saves time during initialization and garbage collection.
### Memory layout and areas ### Memory layout and areas

View File

@ -24,7 +24,10 @@
namespace mbed { namespace mbed {
/** \addtogroup drivers */ /** \addtogroup drivers */
/** A digital input output bus, used for setting the state of a collection of pins /** A digital input output bus, used for setting the state of a collection of pins.
* Implemented as an array of DigitalInOut pins, the bus can be constructed by any
* pins without restriction other than being capable of digital input or output
* capabilities
* *
* @note Synchronization level: Thread safe * @note Synchronization level: Thread safe
* @ingroup drivers * @ingroup drivers
@ -33,7 +36,7 @@ class BusInOut : private NonCopyable<BusInOut> {
public: public:
/** Create an BusInOut, connected to the specified pins /** Create a BusInOut, connected to the specified pins
* *
* @param p0 DigitalInOut pin to connect to bus bit * @param p0 DigitalInOut pin to connect to bus bit
* @param p1 DigitalInOut pin to connect to bus bit * @param p1 DigitalInOut pin to connect to bus bit
@ -61,9 +64,11 @@ public:
PinName p8 = NC, PinName p9 = NC, PinName p10 = NC, PinName p11 = NC, PinName p8 = NC, PinName p9 = NC, PinName p10 = NC, PinName p11 = NC,
PinName p12 = NC, PinName p13 = NC, PinName p14 = NC, PinName p15 = NC); PinName p12 = NC, PinName p13 = NC, PinName p14 = NC, PinName p15 = NC);
/** Create an BusInOut, connected to the specified pins /** Create a BusInOut, connected to the specified pins
* *
* @param pins An array of pins to construct a BusInOut from * @param pins An array of pins (PinName) to construct a BusInOut from. The maximum
* number of pins in the array is 16 and any pins that are unspecified or are not to be
* connected must be specified as NC in the array that is passed in
*/ */
BusInOut(PinName pins[16]); BusInOut(PinName pins[16]);
@ -111,7 +116,7 @@ public:
} }
/** A shorthand for write() /** A shorthand for write()
* \sa BusInOut::write() * \sa BusInOut::write()
*/ */
BusInOut &operator= (int v); BusInOut &operator= (int v);
BusInOut &operator= (BusInOut &rhs); BusInOut &operator= (BusInOut &rhs);
@ -125,8 +130,9 @@ public:
* \sa BusInOut::read() * \sa BusInOut::read()
*/ */
operator int(); operator int();
#if !defined(DOXYGEN_ONLY)
protected: protected:
#if !defined(DOXYGEN_ONLY)
virtual void lock(); virtual void lock();
virtual void unlock(); virtual void unlock();
DigitalInOut *_pin[16]; DigitalInOut *_pin[16];
@ -138,7 +144,7 @@ protected:
int _nc_mask; int _nc_mask;
PlatformMutex _mutex; PlatformMutex _mutex;
#endif #endif //!defined(DOXYGEN_ONLY)
}; };
} // namespace mbed } // namespace mbed

21
drivers/LICENSE-mit.txt Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Arm Limited and affiliates.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

7
drivers/LICENSE.md Normal file
View File

@ -0,0 +1,7 @@
Unless specifically indicated otherwise in a file or in the list below, files are licensed under the Apache 2.0 license,
as can be found: LICENSE-apache-2.0.txt.
Files licensed under MIT:
- FlashIAP.cpp
- FlashIAP.h

View File

@ -147,7 +147,6 @@ ssize_t UARTSerial::write_unbuffered(const char *buf_ptr, size_t length)
for (size_t data_written = 0; data_written < length; data_written++) { for (size_t data_written = 0; data_written < length; data_written++) {
SerialBase::_base_putc(*buf_ptr++); SerialBase::_base_putc(*buf_ptr++);
data_written++;
} }
return length; return length;

View File

@ -124,6 +124,13 @@ void GattClient<Impl>::terminateCharacteristicDescriptorDiscovery(
return impl()->terminateCharacteristicDescriptorDiscovery_(characteristic); return impl()->terminateCharacteristicDescriptorDiscovery_(characteristic);
} }
template<class Impl>
ble_error_t GattClient<Impl>::negotiateAttMtu(
ble::connection_handle_t connHandle
) {
return impl()->negotiateAttMtu_(connHandle);
}
template<class Impl> template<class Impl>
ble_error_t GattClient<Impl>::reset(void) ble_error_t GattClient<Impl>::reset(void)
{ {
@ -179,6 +186,13 @@ void GattClient<Impl>::terminateServiceDiscovery_(void)
{ {
} }
template<class Impl>
ble_error_t GattClient<Impl>::negotiateAttMtu_(
ble::connection_handle_t connHandle
) {
return BLE_ERROR_NOT_IMPLEMENTED;
}
template<class Impl> template<class Impl>
ble_error_t GattClient<Impl>::read_( ble_error_t GattClient<Impl>::read_(
ble::connection_handle_t connHandle, ble::connection_handle_t connHandle,

View File

@ -321,8 +321,10 @@ ble_error_t GattServer::insert_characteristic_value_attribute(
memset(attribute_it->pValue + *attribute_it->pLen, 0, attribute_it->maxLen - *attribute_it->pLen); memset(attribute_it->pValue + *attribute_it->pLen, 0, attribute_it->maxLen - *attribute_it->pLen);
// Set value attribute settings // Set value attribute settings
attribute_it->settings = 0;
if (properties & READ_PROPERTY) { if (properties & READ_PROPERTY) {
attribute_it->settings = ATTS_SET_READ_CBACK; attribute_it->settings |= ATTS_SET_READ_CBACK;
} }
if (properties & WRITABLE_PROPERTIES) { if (properties & WRITABLE_PROPERTIES) {
attribute_it->settings |= ATTS_SET_WRITE_CBACK; attribute_it->settings |= ATTS_SET_WRITE_CBACK;

View File

@ -121,7 +121,7 @@ void WsfTaskUnlock(void)
void WsfSetEvent(wsfHandlerId_t handlerId, wsfEventMask_t event) void WsfSetEvent(wsfHandlerId_t handlerId, wsfEventMask_t event)
{ {
WSF_CS_INIT(cs); WSF_CS_INIT(cs);
// coverity[CONSTANT_EXPRESSION_RESULT]
WSF_ASSERT(WSF_HANDLER_FROM_ID(handlerId) < WSF_MAX_HANDLERS); WSF_ASSERT(WSF_HANDLER_FROM_ID(handlerId) < WSF_MAX_HANDLERS);
WSF_TRACE_INFO2("WsfSetEvent handlerId:%u event:%u", handlerId, event); WSF_TRACE_INFO2("WsfSetEvent handlerId:%u event:%u", handlerId, event);

View File

@ -1058,21 +1058,52 @@ bool ATHandler::consume_to_stop_tag()
void ATHandler::resp_stop() void ATHandler::resp_stop()
{ {
// Do not return on error so that we can consume whatever there is in the buffer if (_is_fh_usable) {
// Do not return on error so that we can consume whatever there is in the buffer
if (_current_scope == ElemType) { if (_current_scope == ElemType) {
information_response_element_stop(); information_response_element_stop();
set_scope(InfoType); set_scope(InfoType);
}
if (_current_scope == InfoType) {
information_response_stop();
}
// Go for response stop_tag
if (_stop_tag && !_stop_tag->found && !_error_found) {
// Check for URC for every new line
while (!get_last_error()) {
if (match(_stop_tag->tag, _stop_tag->len)) {
break;
}
if (match_urc()) {
continue;
}
// If no URC nor stop_tag found, look for CRLF and consume everything up to and including CRLF
if (mem_str(_recv_buff, _recv_len, CRLF, CRLF_LENGTH)) {
consume_to_tag(CRLF, true);
// If stop tag is CRLF we have to stop reading/consuming the buffer
if (!strncmp(CRLF, _stop_tag->tag, _stop_tag->len)) {
break;
}
// If no URC nor CRLF nor stop_tag -> fill buffer
} else {
if (!fill_buffer()) {
// if we don't get any match and no data within timeout, set an error to indicate need for recovery
set_error(NSAPI_ERROR_DEVICE_ERROR);
}
}
}
}
} else {
_last_err = NSAPI_ERROR_BUSY;
} }
if (_current_scope == InfoType) { set_scope(NotSet);
information_response_stop();
}
// Go for response stop_tag
if (consume_to_stop_tag()) {
set_scope(NotSet);
}
// Restore stop tag to OK // Restore stop tag to OK
set_tag(&_resp_stop, OK); set_tag(&_resp_stop, OK);
@ -1289,7 +1320,7 @@ void ATHandler::debug_print(const char *p, int len, ATType type)
#if MBED_CONF_CELLULAR_DEBUG_AT #if MBED_CONF_CELLULAR_DEBUG_AT
if (_debug_on) { if (_debug_on) {
const int buf_size = len * 4 + 1; // x4 -> reserve space for extra characters, +1 -> terminating null const int buf_size = len * 4 + 1; // x4 -> reserve space for extra characters, +1 -> terminating null
char *buffer = (char *)malloc(buf_size); char *buffer = new char [buf_size];
if (buffer) { if (buffer) {
memset(buffer, 0, buf_size); memset(buffer, 0, buf_size);
@ -1319,7 +1350,7 @@ void ATHandler::debug_print(const char *p, int len, ATType type)
tr_info("AT ERR (%2d): %s", len, buffer); tr_info("AT ERR (%2d): %s", len, buffer);
} }
free(buffer); delete [] buffer;
} else { } else {
tr_error("AT trace unable to allocate buffer!"); tr_error("AT trace unable to allocate buffer!");
} }

View File

@ -381,6 +381,10 @@ public:
/** Ends all scopes starting from current scope. /** Ends all scopes starting from current scope.
* Consumes everything until the scope's stop tag is found, then * Consumes everything until the scope's stop tag is found, then
* goes to next scope until response scope is ending. * goes to next scope until response scope is ending.
* URC match is checked during response scope ending,
* for every new line / CRLF.
*
*
* Possible sequence: * Possible sequence:
* element scope -> information response scope -> response scope * element scope -> information response scope -> response scope
*/ */

View File

@ -672,6 +672,10 @@ nsapi_error_t AT_CellularContext::disconnect()
{ {
tr_info("CellularContext disconnect()"); tr_info("CellularContext disconnect()");
if (!_nw || !_is_connected) { if (!_nw || !_is_connected) {
if (_new_context_set) {
delete_current_context();
}
_cid = -1;
return NSAPI_ERROR_NO_CONNECTION; return NSAPI_ERROR_NO_CONNECTION;
} }
@ -701,6 +705,11 @@ nsapi_error_t AT_CellularContext::disconnect()
// call device's callback, it will broadcast this to here (cellular_callback) // call device's callback, it will broadcast this to here (cellular_callback)
_device->cellular_callback(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, NSAPI_STATUS_DISCONNECTED, this); _device->cellular_callback(NSAPI_EVENT_CONNECTION_STATUS_CHANGE, NSAPI_STATUS_DISCONNECTED, this);
if (_new_context_set) {
delete_current_context();
}
_cid = -1;
return _at.unlock_return_error(); return _at.unlock_return_error();
} }

View File

@ -47,8 +47,6 @@ AT_CellularDevice::AT_CellularDevice(FileHandle *fh) : CellularDevice(fh), _netw
AT_CellularDevice::~AT_CellularDevice() AT_CellularDevice::~AT_CellularDevice()
{ {
delete _state_machine;
// make sure that all is deleted even if somewhere close was not called and reference counting is messed up. // make sure that all is deleted even if somewhere close was not called and reference counting is messed up.
_network_ref_count = 1; _network_ref_count = 1;
_sms_ref_count = 1; _sms_ref_count = 1;

View File

@ -44,6 +44,7 @@ CellularDevice::CellularDevice(FileHandle *fh) : _network_ref_count(0), _sms_ref
CellularDevice::~CellularDevice() CellularDevice::~CellularDevice()
{ {
tr_debug("CellularDevice destruct"); tr_debug("CellularDevice destruct");
delete _state_machine;
} }
void CellularDevice::stop() void CellularDevice::stop()
@ -111,7 +112,10 @@ nsapi_error_t CellularDevice::create_state_machine()
{ {
nsapi_error_t err = NSAPI_ERROR_OK; nsapi_error_t err = NSAPI_ERROR_OK;
if (!_state_machine) { if (!_state_machine) {
_state_machine = new CellularStateMachine(*this, *get_queue()); _nw = open_network(_fh);
// Attach to network so we can get update status from the network
_nw->attach(callback(this, &CellularDevice::stm_callback));
_state_machine = new CellularStateMachine(*this, *get_queue(), *_nw);
_state_machine->set_cellular_callback(callback(this, &CellularDevice::stm_callback)); _state_machine->set_cellular_callback(callback(this, &CellularDevice::stm_callback));
err = _state_machine->start_dispatch(); err = _state_machine->start_dispatch();
if (err) { if (err) {
@ -177,13 +181,6 @@ void CellularDevice::cellular_callback(nsapi_event_t ev, intptr_t ptr, CellularC
// broadcast only network registration changes to state machine // broadcast only network registration changes to state machine
_state_machine->cellular_event_changed(ev, ptr); _state_machine->cellular_event_changed(ev, ptr);
} }
if (cell_ev == CellularDeviceReady && ptr_data->error == NSAPI_ERROR_OK) {
// Here we can create mux and give new filehandles as mux reserves the one what was in use.
// if mux we would need to set new filehandle:_state_machine->set_filehandle( get fh from mux);
_nw = open_network(_fh);
// Attach to network so we can get update status from the network
_nw->attach(callback(this, &CellularDevice::stm_callback));
}
} else { } else {
tr_debug("callback: %d, ptr: %d", ev, ptr); tr_debug("callback: %d, ptr: %d", ev, ptr);
if (ev == NSAPI_EVENT_CONNECTION_STATUS_CHANGE && ptr == NSAPI_STATUS_DISCONNECTED) { if (ev == NSAPI_EVENT_CONNECTION_STATUS_CHANGE && ptr == NSAPI_STATUS_DISCONNECTED) {

View File

@ -43,9 +43,9 @@ const int DEVICE_READY = 0x04;
namespace mbed { namespace mbed {
CellularStateMachine::CellularStateMachine(CellularDevice &device, events::EventQueue &queue) : CellularStateMachine::CellularStateMachine(CellularDevice &device, events::EventQueue &queue, CellularNetwork &nw) :
_cellularDevice(device), _state(STATE_INIT), _next_state(_state), _target_state(_state), _cellularDevice(device), _state(STATE_INIT), _next_state(_state), _target_state(_state),
_event_status_cb(0), _network(0), _queue(queue), _queue_thread(0), _sim_pin(0), _event_status_cb(0), _network(nw), _queue(queue), _queue_thread(0), _sim_pin(0),
_retry_count(0), _event_timeout(-1), _event_id(-1), _plmn(0), _command_success(false), _retry_count(0), _event_timeout(-1), _event_id(-1), _plmn(0), _command_success(false),
_is_retry(false), _cb_data(), _current_event(NSAPI_EVENT_CONNECTION_STATUS_CHANGE), _status(0) _is_retry(false), _cb_data(), _current_event(NSAPI_EVENT_CONNECTION_STATUS_CHANGE), _status(0)
{ {
@ -99,11 +99,6 @@ void CellularStateMachine::stop()
reset(); reset();
_event_id = STM_STOPPED; _event_id = STM_STOPPED;
if (_network) {
_cellularDevice.close_network();
_network = NULL;
}
} }
bool CellularStateMachine::power_on() bool CellularStateMachine::power_on()
@ -163,7 +158,7 @@ bool CellularStateMachine::open_sim()
if (sim_ready) { if (sim_ready) {
// If plmn is set, we should it right after sim is opened so that registration is forced to correct network. // If plmn is set, we should it right after sim is opened so that registration is forced to correct network.
if (_plmn && strlen(_plmn)) { if (_plmn && strlen(_plmn)) {
_cb_data.error = _network->set_registration(_plmn); _cb_data.error = _network.set_registration(_plmn);
tr_debug("STM: manual set_registration: %d, plmn: %s", _cb_data.error, _plmn); tr_debug("STM: manual set_registration: %d, plmn: %s", _cb_data.error, _plmn);
if (_cb_data.error) { if (_cb_data.error) {
return false; return false;
@ -204,7 +199,7 @@ bool CellularStateMachine::get_network_registration(CellularNetwork::Registratio
is_registered = false; is_registered = false;
bool is_roaming = false; bool is_roaming = false;
CellularNetwork::registration_params_t reg_params; CellularNetwork::registration_params_t reg_params;
_cb_data.error = _network->get_registration_params(type, reg_params); _cb_data.error = _network.get_registration_params(type, reg_params);
if (_cb_data.error != NSAPI_ERROR_OK) { if (_cb_data.error != NSAPI_ERROR_OK) {
if (_cb_data.error != NSAPI_ERROR_UNSUPPORTED) { if (_cb_data.error != NSAPI_ERROR_UNSUPPORTED) {
@ -329,14 +324,10 @@ bool CellularStateMachine::device_ready()
{ {
tr_info("Modem ready"); tr_info("Modem ready");
if (!_network) {
_network = _cellularDevice.open_network();
}
#ifdef MBED_CONF_CELLULAR_RADIO_ACCESS_TECHNOLOGY #ifdef MBED_CONF_CELLULAR_RADIO_ACCESS_TECHNOLOGY
MBED_ASSERT(MBED_CONF_CELLULAR_RADIO_ACCESS_TECHNOLOGY >= CellularNetwork::RAT_GSM && MBED_ASSERT(MBED_CONF_CELLULAR_RADIO_ACCESS_TECHNOLOGY >= CellularNetwork::RAT_GSM &&
MBED_CONF_CELLULAR_RADIO_ACCESS_TECHNOLOGY < CellularNetwork::RAT_UNKNOWN); MBED_CONF_CELLULAR_RADIO_ACCESS_TECHNOLOGY < CellularNetwork::RAT_UNKNOWN);
nsapi_error_t err = _network->set_access_technology((CellularNetwork::RadioAccessTechnology)MBED_CONF_CELLULAR_RADIO_ACCESS_TECHNOLOGY); nsapi_error_t err = _network.set_access_technology((CellularNetwork::RadioAccessTechnology)MBED_CONF_CELLULAR_RADIO_ACCESS_TECHNOLOGY);
if (err != NSAPI_ERROR_OK && err != NSAPI_ERROR_UNSUPPORTED) { if (err != NSAPI_ERROR_OK && err != NSAPI_ERROR_UNSUPPORTED) {
tr_warning("Failed to set access technology to %d", MBED_CONF_CELLULAR_RADIO_ACCESS_TECHNOLOGY); tr_warning("Failed to set access technology to %d", MBED_CONF_CELLULAR_RADIO_ACCESS_TECHNOLOGY);
return false; return false;
@ -382,7 +373,7 @@ void CellularStateMachine::state_sim_pin()
if (open_sim()) { if (open_sim()) {
bool success = false; bool success = false;
for (int type = 0; type < CellularNetwork::C_MAX; type++) { for (int type = 0; type < CellularNetwork::C_MAX; type++) {
_cb_data.error = _network->set_registration_urc((CellularNetwork::RegistrationType)type, true); _cb_data.error = _network.set_registration_urc((CellularNetwork::RegistrationType)type, true);
if (!_cb_data.error && (type == CellularNetwork::C_EREG || type == CellularNetwork::C_GREG)) { if (!_cb_data.error && (type == CellularNetwork::C_EREG || type == CellularNetwork::C_GREG)) {
success = true; success = true;
} }
@ -393,19 +384,19 @@ void CellularStateMachine::state_sim_pin()
return; return;
} }
if (_network->is_active_context()) { // check if context was already activated if (_network.is_active_context()) { // check if context was already activated
tr_debug("Active context found."); tr_debug("Active context found.");
_status |= ACTIVE_PDP_CONTEXT; _status |= ACTIVE_PDP_CONTEXT;
} }
CellularNetwork::AttachStatus status = CellularNetwork::Detached; // check if modem is already attached to a network CellularNetwork::AttachStatus status = CellularNetwork::Detached; // check if modem is already attached to a network
if (_network->get_attach(status) == NSAPI_ERROR_OK && status == CellularNetwork::Attached) { if (_network.get_attach(status) == NSAPI_ERROR_OK && status == CellularNetwork::Attached) {
_status |= ATTACHED_TO_NETWORK; _status |= ATTACHED_TO_NETWORK;
tr_debug("Cellular already attached."); tr_debug("Cellular already attached.");
} }
// if packet domain event reporting is not set it's not a stopper. We might lack some events when we are // if packet domain event reporting is not set it's not a stopper. We might lack some events when we are
// dropped from the network. // dropped from the network.
_cb_data.error = _network->set_packet_domain_event_reporting(true); _cb_data.error = _network.set_packet_domain_event_reporting(true);
if (_cb_data.error == NSAPI_STATUS_ERROR_UNSUPPORTED) { if (_cb_data.error == NSAPI_STATUS_ERROR_UNSUPPORTED) {
tr_warning("Packet domain event reporting not supported!"); tr_warning("Packet domain event reporting not supported!");
} else if (_cb_data.error) { } else if (_cb_data.error) {
@ -434,7 +425,7 @@ void CellularStateMachine::state_registering()
} else { } else {
_cellularDevice.set_timeout(TIMEOUT_REGISTRATION); _cellularDevice.set_timeout(TIMEOUT_REGISTRATION);
if (!_command_success && !_plmn) { // don't call set_registration twice for manual registration if (!_command_success && !_plmn) { // don't call set_registration twice for manual registration
_cb_data.error = _network->set_registration(_plmn); _cb_data.error = _network.set_registration(_plmn);
_command_success = (_cb_data.error == NSAPI_ERROR_OK); _command_success = (_cb_data.error == NSAPI_ERROR_OK);
} }
retry_state_or_fail(); retry_state_or_fail();
@ -446,7 +437,7 @@ void CellularStateMachine::state_attaching()
_cellularDevice.set_timeout(TIMEOUT_CONNECT); _cellularDevice.set_timeout(TIMEOUT_CONNECT);
tr_info("Attaching network (timeout %d s)", TIMEOUT_CONNECT / 1000); tr_info("Attaching network (timeout %d s)", TIMEOUT_CONNECT / 1000);
if (_status != ATTACHED_TO_NETWORK) { if (_status != ATTACHED_TO_NETWORK) {
_cb_data.error = _network->set_attach(); _cb_data.error = _network.set_attach();
} }
if (_cb_data.error == NSAPI_ERROR_OK) { if (_cb_data.error == NSAPI_ERROR_OK) {
if (_event_status_cb) { if (_event_status_cb) {
@ -534,14 +525,12 @@ bool CellularStateMachine::get_current_status(CellularStateMachine::CellularStat
void CellularStateMachine::event() void CellularStateMachine::event()
{ {
#if MBED_CONF_MBED_TRACE_ENABLE #if MBED_CONF_MBED_TRACE_ENABLE
if (_network) { int rssi;
int rssi; if (_network.get_signal_quality(rssi) == NSAPI_ERROR_OK) {
if (_network->get_signal_quality(rssi) == NSAPI_ERROR_OK) { if (rssi == CellularNetwork::SignalQualityUnknown) {
if (rssi == CellularNetwork::SignalQualityUnknown) { tr_info("RSSI unknown");
tr_info("RSSI unknown"); } else {
} else { tr_info("RSSI %d dBm", rssi);
tr_info("RSSI %d dBm", rssi);
}
} }
} }
#endif #endif
@ -643,7 +632,7 @@ void CellularStateMachine::cellular_event_changed(nsapi_event_t ev, intptr_t ptr
if ((cellular_connection_status_t)ev == CellularRegistrationStatusChanged && _state == STATE_REGISTERING_NETWORK) { if ((cellular_connection_status_t)ev == CellularRegistrationStatusChanged && _state == STATE_REGISTERING_NETWORK) {
// expect packet data so only these states are valid // expect packet data so only these states are valid
CellularNetwork::registration_params_t reg_params; CellularNetwork::registration_params_t reg_params;
nsapi_error_t err = _network->get_registration_params(reg_params); nsapi_error_t err = _network.get_registration_params(reg_params);
if (err == NSAPI_ERROR_OK && (reg_params._type == CellularNetwork::C_EREG || reg_params._type == CellularNetwork::C_GREG)) { if (err == NSAPI_ERROR_OK && (reg_params._type == CellularNetwork::C_EREG || reg_params._type == CellularNetwork::C_GREG)) {
if ((data->status_data == CellularNetwork::RegisteredHomeNetwork || if ((data->status_data == CellularNetwork::RegisteredHomeNetwork ||

View File

@ -46,8 +46,9 @@ private:
* *
* @param device reference to CellularDevice * @param device reference to CellularDevice
* @param queue reference to queue used in state transitions * @param queue reference to queue used in state transitions
* @param nw reference to CellularNetwork
*/ */
CellularStateMachine(CellularDevice &device, events::EventQueue &queue); CellularStateMachine(CellularDevice &device, events::EventQueue &queue, CellularNetwork &nw);
~CellularStateMachine(); ~CellularStateMachine();
/** Cellular connection states /** Cellular connection states
@ -162,7 +163,7 @@ private:
Callback<void(nsapi_event_t, intptr_t)> _event_status_cb; Callback<void(nsapi_event_t, intptr_t)> _event_status_cb;
CellularNetwork *_network; CellularNetwork &_network;
events::EventQueue &_queue; events::EventQueue &_queue;
rtos::Thread *_queue_thread; rtos::Thread *_queue_thread;

View File

@ -20,6 +20,12 @@
using namespace mbed; using namespace mbed;
const char *QIURC_RECV = "recv";
const uint8_t QIURC_RECV_LENGTH = 4;
const char *QIURC_CLOSED = "closed";
const uint8_t QIURC_CLOSED_LENGTH = 6;
const uint8_t MAX_QIURC_LENGTH = QIURC_CLOSED_LENGTH;
QUECTEL_BG96_CellularStack::QUECTEL_BG96_CellularStack(ATHandler &atHandler, int cid, nsapi_ip_stack_t stack_type) : AT_CellularStack(atHandler, cid, stack_type) QUECTEL_BG96_CellularStack::QUECTEL_BG96_CellularStack(ATHandler &atHandler, int cid, nsapi_ip_stack_t stack_type) : AT_CellularStack(atHandler, cid, stack_type)
{ {
_at.set_urc_handler("+QIURC:", mbed::Callback<void()>(this, &QUECTEL_BG96_CellularStack::urc_qiurc)); _at.set_urc_handler("+QIURC:", mbed::Callback<void()>(this, &QUECTEL_BG96_CellularStack::urc_qiurc));
@ -110,20 +116,30 @@ nsapi_error_t QUECTEL_BG96_CellularStack::socket_connect(nsapi_socket_t handle,
void QUECTEL_BG96_CellularStack::urc_qiurc() void QUECTEL_BG96_CellularStack::urc_qiurc()
{ {
int sock_id = 0; char urc_string[MAX_QIURC_LENGTH + 1];
_at.lock(); _at.lock();
(void) _at.skip_param(); const int urc_string_length = _at.read_string(urc_string, sizeof(urc_string));
sock_id = _at.read_int(); const int sock_id = _at.read_int();
_at.unlock(); const nsapi_error_t err = _at.unlock_return_error();
for (int i = 0; i < get_max_socket_count(); i++) { if (err != NSAPI_ERROR_OK) {
CellularSocket *sock = _socket[i]; return;
if (sock && sock->id == sock_id) { }
bool recv = strcmp(urc_string, "recv") == 0;
bool closed = strcmp(urc_string, "closed") == 0;
CellularSocket *sock = find_socket(sock_id);
if (sock) {
if (closed) {
tr_error("Socket closed %d", sock_id);
sock->closed = true;
}
if (recv || closed) {
if (sock->_cb) { if (sock->_cb) {
sock->_cb(sock->_data); sock->_cb(sock->_data);
} }
break;
} }
} }
} }

View File

@ -0,0 +1,143 @@
/*
* Copyright (c) 2019, Arm Limited and affiliates.
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "QUECTEL_EC2X.h"
#include "PinNames.h"
#include "AT_CellularNetwork.h"
#include "rtos/ThisThread.h"
#include "UARTSerial.h"
using namespace mbed;
using namespace rtos;
using namespace events;
#if !defined(MBED_CONF_QUECTEL_EC2X_PWR)
#define MBED_CONF_QUECTEL_EC2X_PWR NC
#endif
#if !defined(MBED_CONF_QUECTEL_EC2X_RST)
#define MBED_CONF_QUECTEL_EC2X_RST NC
#endif
#if !defined(MBED_CONF_QUECTEL_EC2X_TX)
#define MBED_CONF_QUECTEL_EC2X_TX NC
#endif
#if !defined(MBED_CONF_QUECTEL_EC2X_RX)
#define MBED_CONF_QUECTEL_EC2X_RX NC
#endif
#if !defined(MBED_CONF_QUECTEL_EC2X_POLARITY)
#define MBED_CONF_QUECTEL_EC2X_POLARITY 1 // active high
#endif
static const intptr_t cellular_properties[AT_CellularBase::PROPERTY_MAX] = {
AT_CellularNetwork::RegistrationModeLAC, // C_EREG
AT_CellularNetwork::RegistrationModeLAC, // C_GREG
AT_CellularNetwork::RegistrationModeLAC, // C_REG
0, // AT_CGSN_WITH_TYPE
1, // AT_CGDATA
0, // AT_CGAUTH
1, // AT_CNMI
1, // AT_CSMP
1, // AT_CMGF
1, // AT_CSDH
1, // PROPERTY_IPV4_STACK
1, // PROPERTY_IPV6_STACK
1, // PROPERTY_IPV4V6_STACK
0, // PROPERTY_NON_IP_PDP_TYPE
1, // PROPERTY_AT_CGEREP
};
QUECTEL_EC2X::QUECTEL_EC2X(FileHandle *fh, PinName pwr, bool active_high, PinName rst)
: AT_CellularDevice(fh),
_active_high(active_high),
_pwr_key(pwr, !_active_high),
_rst(rst, !_active_high)
{
AT_CellularBase::set_cellular_properties(cellular_properties);
}
#if MBED_CONF_QUECTEL_EC2X_PROVIDE_DEFAULT
CellularDevice *CellularDevice::get_default_instance()
{
static UARTSerial serial(MBED_CONF_QUECTEL_EC2X_TX,
MBED_CONF_QUECTEL_EC2X_RX,
MBED_CONF_QUECTEL_EC2X_BAUDRATE);
#if defined(MBED_CONF_QUECTEL_EC2X_RTS) && defined(MBED_CONF_QUECTEL_EC2X_CTS)
serial.set_flow_control(SerialBase::RTSCTS, MBED_CONF_QUECTEL_EC2X_RTS, MBED_CONF_QUECTEL_EC2X_CTS);
#endif
static QUECTEL_EC2X device(&serial,
MBED_CONF_QUECTEL_EC2X_PWR,
MBED_CONF_QUECTEL_EC2X_POLARITY,
MBED_CONF_QUECTEL_EC2X_RST);
return &device;
}
#endif
nsapi_error_t QUECTEL_EC2X::press_power_button(uint32_t timeout)
{
_pwr_key = _active_high;
ThisThread::sleep_for(timeout);
_pwr_key = !_active_high;
ThisThread::sleep_for(100);
return NSAPI_ERROR_OK;
}
nsapi_error_t QUECTEL_EC2X::hard_power_on()
{
return press_power_button(600);
}
nsapi_error_t QUECTEL_EC2X::hard_power_off()
{
return press_power_button(750);
}
nsapi_error_t QUECTEL_EC2X::soft_power_on()
{
if (_rst.is_connected()) {
_rst = _active_high;
ThisThread::sleep_for(460);
_rst = !_active_high;
ThisThread::sleep_for(100);
_at->lock();
_at->set_at_timeout(5000);
_at->resp_start();
_at->set_stop_tag("RDY");
bool rdy = _at->consume_to_stop_tag();
_at->set_stop_tag(OK);
_at->unlock();
if (!rdy) {
return NSAPI_ERROR_DEVICE_ERROR;
}
}
return NSAPI_ERROR_OK;
}
nsapi_error_t QUECTEL_EC2X::soft_power_off()
{
return hard_power_off();
}

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2019, Arm Limited and affiliates.
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef QUECTEL_EC2X_H
#define QUECTEL_EC2X_H
#include "DigitalOut.h"
#include "AT_CellularDevice.h"
namespace mbed {
class QUECTEL_EC2X : public AT_CellularDevice {
public:
/**
* Constructs the Quectel EC2X series driver. It is mandatory to provide
* a FileHandle object, the power pin and the polarity of the pin.
* Providing reset pin is optional.
*/
QUECTEL_EC2X(FileHandle *fh, PinName pwr, bool active_high, PinName rst = NC);
virtual nsapi_error_t hard_power_on();
virtual nsapi_error_t hard_power_off();
virtual nsapi_error_t soft_power_on();
virtual nsapi_error_t soft_power_off();
private:
nsapi_error_t press_power_button(uint32_t timeout);
bool _active_high;
DigitalOut _pwr_key;
DigitalOut _rst;
};
} // namespace mbed
#endif // QUECTEL_EC2X_H

View File

@ -0,0 +1,41 @@
{
"name": "QUECTEL_EC2X",
"config": {
"tx": {
"help": "TX pin for serial connection. D1 assumed if Arduino Form Factor, needs to be set/overwritten otherwise.",
"value": null
},
"rx": {
"help": "RX pin for serial connection. D0 assumed if Arduino Form Factor, needs to be set/overwritten otherwise.",
"value": null
},
"rts": {
"help": "RTS pin for serial connection",
"value": null
},
"cts": {
"help": "CTS pin for serial connection",
"value": null
},
"pwr": {
"help": "Power control pin",
"value": null
},
"rst": {
"help": "Reset control pin",
"value": null
},
"polarity": {
"help": "Pin polarity, 1 = Active high, 0 = Active low",
"value": null
},
"baudrate" : {
"help": "Serial connection baud rate",
"value": 115200
},
"provide-default": {
"help": "Provide as default CellularDevice [true/false]",
"value": false
}
}
}

View File

@ -467,7 +467,7 @@ nsapi_error_t LWIP::add_l3ip_interface(L3IP &l3ip, bool default_if, OnboardNetwo
#if LWIP_IPV4 #if LWIP_IPV4
0, 0, 0, 0, 0, 0,
#endif #endif
interface, &LWIP::Interface::l3ip_if_init, ip_input)) { interface, &LWIP::Interface::l3ip_if_init, tcpip_input)) {
return NSAPI_ERROR_DEVICE_ERROR; return NSAPI_ERROR_DEVICE_ERROR;
} }

View File

@ -126,7 +126,7 @@ public:
virtual char *get_gateway(char *buf, nsapi_size_t buflen); virtual char *get_gateway(char *buf, nsapi_size_t buflen);
private: private:
friend LWIP; friend class LWIP;
Interface(); Interface();

View File

@ -403,6 +403,7 @@ bool test_socket_api_callbacks()
coap_conn_handler_t *handler = connection_handler_create(&receive_from_sock_cb, &send_to_sock_cb, NULL, NULL); coap_conn_handler_t *handler = connection_handler_create(&receive_from_sock_cb, &send_to_sock_cb, NULL, NULL);
nsdynmemlib_stub.returnCounter = 2; nsdynmemlib_stub.returnCounter = 2;
if (0 != coap_connection_handler_open_connection(handler, 22, false, false, true, false)) { if (0 != coap_connection_handler_open_connection(handler, 22, false, false, true, false)) {
free(sckt_data);
return false; return false;
} }
@ -483,6 +484,7 @@ bool test_security_callbacks()
coap_conn_handler_t *handler = connection_handler_create(&receive_from_sock_cb, &send_to_sock_cb, NULL, NULL); coap_conn_handler_t *handler = connection_handler_create(&receive_from_sock_cb, &send_to_sock_cb, NULL, NULL);
nsdynmemlib_stub.returnCounter = 2; nsdynmemlib_stub.returnCounter = 2;
if (0 != coap_connection_handler_open_connection(handler, 22, false, true, true, false)) { if (0 != coap_connection_handler_open_connection(handler, 22, false, true, true, false)) {
free(sckt_data);
return false; return false;
} }

View File

@ -30,7 +30,7 @@ public:
virtual nsapi_error_t bringdown(); virtual nsapi_error_t bringdown();
private: private:
friend Nanostack; friend class Nanostack;
friend class NanostackEthernetInterface; friend class NanostackEthernetInterface;
EthernetInterface(NanostackEthernetPhy &phy) : Interface(phy) {} EthernetInterface(NanostackEthernetPhy &phy) : Interface(phy) {}
nsapi_error_t initialize(); nsapi_error_t initialize();

View File

@ -33,7 +33,7 @@ public:
virtual nsapi_error_t bringdown(); virtual nsapi_error_t bringdown();
virtual char *get_gateway(char *buf, nsapi_size_t buflen); virtual char *get_gateway(char *buf, nsapi_size_t buflen);
friend Nanostack; friend class Nanostack;
friend class ::LoWPANNDInterface; friend class ::LoWPANNDInterface;
private: private:
LoWPANNDInterface(NanostackRfPhy &phy) : MeshInterface(phy) { } LoWPANNDInterface(NanostackRfPhy &phy) : MeshInterface(phy) { }

View File

@ -30,7 +30,7 @@ public:
nsapi_ip_stack_t stack = IPV6_STACK, nsapi_ip_stack_t stack = IPV6_STACK,
bool blocking = true); bool blocking = true);
virtual nsapi_error_t bringdown(); virtual nsapi_error_t bringdown();
friend Nanostack; friend class Nanostack;
friend class ::ThreadInterface; friend class ::ThreadInterface;
private: private:
ThreadInterface(NanostackRfPhy &phy) : MeshInterface(phy), eui64_set(false) { } ThreadInterface(NanostackRfPhy &phy) : MeshInterface(phy), eui64_set(false) { }

View File

@ -33,7 +33,7 @@ public:
virtual nsapi_error_t bringdown(); virtual nsapi_error_t bringdown();
virtual char *get_gateway(char *buf, nsapi_size_t buflen); virtual char *get_gateway(char *buf, nsapi_size_t buflen);
friend Nanostack; friend class Nanostack;
friend class ::WisunInterface; friend class ::WisunInterface;
private: private:
WisunInterface(NanostackRfPhy &phy) : MeshInterface(phy) { } WisunInterface(NanostackRfPhy &phy) : MeshInterface(phy) { }

View File

@ -103,9 +103,9 @@ static void initialize_channel_list(void)
arm_nwk_set_channel_list(tasklet_data_ptr->network_interface_id, &tasklet_data_ptr->channel_list); arm_nwk_set_channel_list(tasklet_data_ptr->network_interface_id, &tasklet_data_ptr->channel_list);
tr_debug("Channel: %ld", channel); tr_debug("Channel: %" PRIu32, channel);
tr_debug("Channel page: %d", tasklet_data_ptr->channel_list.channel_page); tr_debug("Channel page: %d", tasklet_data_ptr->channel_list.channel_page);
tr_debug("Channel mask: 0x%.8lx", tasklet_data_ptr->channel_list.channel_mask[word_index]); tr_debug("Channel mask: 0x%.8" PRIx32, tasklet_data_ptr->channel_list.channel_mask[word_index]);
} }
/* /*

View File

@ -319,7 +319,7 @@ void thread_tasklet_configure_and_connect_to_network(void)
thread_tasklet_data_ptr->channel_list.channel_mask[0] = MBED_CONF_MBED_MESH_API_THREAD_CONFIG_CHANNEL_MASK; thread_tasklet_data_ptr->channel_list.channel_mask[0] = MBED_CONF_MBED_MESH_API_THREAD_CONFIG_CHANNEL_MASK;
TRACE_DETAIL("channel page: %d", thread_tasklet_data_ptr->channel_list.channel_page); TRACE_DETAIL("channel page: %d", thread_tasklet_data_ptr->channel_list.channel_page);
TRACE_DETAIL("channel mask: 0x%.8lx", thread_tasklet_data_ptr->channel_list.channel_mask[0]); TRACE_DETAIL("channel mask: 0x%.8" PRIx32, thread_tasklet_data_ptr->channel_list.channel_mask[0]);
// PSKd // PSKd
const char PSKd[] = MBED_CONF_MBED_MESH_API_THREAD_PSKD; const char PSKd[] = MBED_CONF_MBED_MESH_API_THREAD_PSKD;

View File

@ -99,6 +99,9 @@ static void update_read_buffer(uint8_t *buf)
g_handle.rxBdCurrent[0]->buffer = buf; g_handle.rxBdCurrent[0]->buffer = buf;
} }
/* Ensures buffer pointer is written before control. */
__DMB();
/* Clears status. */ /* Clears status. */
g_handle.rxBdCurrent[0]->control &= ENET_BUFFDESCRIPTOR_RX_WRAP_MASK; g_handle.rxBdCurrent[0]->control &= ENET_BUFFDESCRIPTOR_RX_WRAP_MASK;
@ -112,6 +115,9 @@ static void update_read_buffer(uint8_t *buf)
g_handle.rxBdCurrent[0]++; g_handle.rxBdCurrent[0]++;
} }
/* Ensures descriptor is written before kicking hardware. */
__DSB();
/* Actives the receive buffer descriptor. */ /* Actives the receive buffer descriptor. */
ENET->RDAR = ENET_RDAR_RDAR_MASK; ENET->RDAR = ENET_RDAR_RDAR_MASK;
} }
@ -195,6 +201,7 @@ bool Kinetis_EMAC::low_level_init_successful()
return false; return false;
rx_ptr[i] = (uint32_t*)memory_manager->get_ptr(rx_buff[i]); rx_ptr[i] = (uint32_t*)memory_manager->get_ptr(rx_buff[i]);
SCB_InvalidateDCache_by_Addr(rx_ptr[i], ENET_ALIGN(ENET_ETH_MAX_FLEN, ENET_BUFF_ALIGNMENT));
} }
tx_consume_index = tx_produce_index = 0; tx_consume_index = tx_produce_index = 0;
@ -277,6 +284,7 @@ emac_mem_buf_t *Kinetis_EMAC::low_level_input(int idx)
/* Zero-copy */ /* Zero-copy */
p = rx_buff[idx]; p = rx_buff[idx];
SCB_InvalidateDCache_by_Addr(rx_ptr[idx], length);
memory_manager->set_len(p, length); memory_manager->set_len(p, length);
/* Attempt to queue new buffer */ /* Attempt to queue new buffer */
@ -295,6 +303,7 @@ emac_mem_buf_t *Kinetis_EMAC::low_level_input(int idx)
rx_buff[idx] = temp_rxbuf; rx_buff[idx] = temp_rxbuf;
rx_ptr[idx] = (uint32_t*)memory_manager->get_ptr(rx_buff[idx]); rx_ptr[idx] = (uint32_t*)memory_manager->get_ptr(rx_buff[idx]);
SCB_InvalidateDCache_by_Addr(rx_ptr[idx], ENET_ALIGN(ENET_ETH_MAX_FLEN, ENET_BUFF_ALIGNMENT));
update_read_buffer((uint8_t*)rx_ptr[idx]); update_read_buffer((uint8_t*)rx_ptr[idx]);
} }
@ -399,6 +408,8 @@ bool Kinetis_EMAC::link_out(emac_mem_buf_t *buf)
buf = copy_buf; buf = copy_buf;
} }
SCB_CleanDCache_by_Addr(static_cast<uint32_t *>(memory_manager->get_ptr(buf)), memory_manager->get_len(buf));
/* Check if a descriptor is available for the transfer (wait 10ms before dropping the buffer) */ /* Check if a descriptor is available for the transfer (wait 10ms before dropping the buffer) */
if (xTXDCountSem.wait(10) == 0) { if (xTXDCountSem.wait(10) == 0) {
memory_manager->free(buf); memory_manager->free(buf);
@ -415,6 +426,8 @@ bool Kinetis_EMAC::link_out(emac_mem_buf_t *buf)
/* Setup transfers */ /* Setup transfers */
g_handle.txBdCurrent[0]->buffer = static_cast<uint8_t *>(memory_manager->get_ptr(buf)); g_handle.txBdCurrent[0]->buffer = static_cast<uint8_t *>(memory_manager->get_ptr(buf));
g_handle.txBdCurrent[0]->length = memory_manager->get_len(buf); g_handle.txBdCurrent[0]->length = memory_manager->get_len(buf);
/* Ensures buffer and length is written before control. */
__DMB();
g_handle.txBdCurrent[0]->control |= (ENET_BUFFDESCRIPTOR_TX_READY_MASK | ENET_BUFFDESCRIPTOR_TX_LAST_MASK); g_handle.txBdCurrent[0]->control |= (ENET_BUFFDESCRIPTOR_TX_READY_MASK | ENET_BUFFDESCRIPTOR_TX_LAST_MASK);
/* Increase the buffer descriptor address. */ /* Increase the buffer descriptor address. */
@ -424,6 +437,9 @@ bool Kinetis_EMAC::link_out(emac_mem_buf_t *buf)
g_handle.txBdCurrent[0]++; g_handle.txBdCurrent[0]++;
} }
/* Ensures descriptor is written before kicking hardware. */
__DSB();
/* Active the transmit buffer descriptor. */ /* Active the transmit buffer descriptor. */
ENET->TDAR = ENET_TDAR_TDAR_MASK; ENET->TDAR = ENET_TDAR_TDAR_MASK;

View File

@ -1,23 +1,18 @@
/* mbed Microcontroller Library /*
* Copyright (c) 2017 ARM Limited * Copyright (c) 2018-2019, Arm Limited and affiliates.
* SPDX-License-Identifier: Apache-2.0
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Licensed under the Apache License, Version 2.0 (the "License");
* of this software and associated documentation files (the "Software"), to deal * you may not use this file except in compliance with the License.
* in the Software without restriction, including without limitation the rights * You may obtain a copy of the License at
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in * http://www.apache.org/licenses/LICENSE-2.0
* all copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * Unless required by applicable law or agreed to in writing, software
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * distributed under the License is distributed on an "AS IS" BASIS,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * See the License for the specific language governing permissions and
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * limitations under the License.
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/ */
/** \addtogroup storage */ /** \addtogroup storage */

View File

@ -7,24 +7,24 @@ import os
import re import re
def main(): def main():
with open('blocks/config') as file: with open('blocks/config', 'rb') as file:
s = struct.unpack('<LLLL', file.read()) s = struct.unpack('<LLLL', file.read())
print 'read_size: %d' % s[0] print('read_size: %d' % s[0])
print 'prog_size: %d' % s[1] print('prog_size: %d' % s[1])
print 'block_size: %d' % s[2] print('block_size: %d' % s[2])
print 'block_size: %d' % s[3] print('block_size: %d' % s[3])
print 'real_size: %d' % sum( print('real_size: %d' % sum(
os.path.getsize(os.path.join('blocks', f)) os.path.getsize(os.path.join('blocks', f))
for f in os.listdir('blocks') if re.match('\d+', f)) for f in os.listdir('blocks') if re.match('\d+', f)))
with open('blocks/stats') as file: with open('blocks/stats', 'rb') as file:
s = struct.unpack('<QQQ', file.read()) s = struct.unpack('<QQQ', file.read())
print 'read_count: %d' % s[0] print('read_count: %d' % s[0])
print 'prog_count: %d' % s[1] print('prog_count: %d' % s[1])
print 'erase_count: %d' % s[2] print('erase_count: %d' % s[2])
print 'runtime: %.3f' % (time.time() - os.stat('blocks').st_ctime) print('runtime: %.3f' % (time.time() - os.stat('blocks').st_ctime))
if __name__ == "__main__": if __name__ == "__main__":
main(*sys.argv[1:]) main(*sys.argv[1:])

View File

@ -14,6 +14,9 @@
"FUTURE_SEQUANA_M0_PSA": { "FUTURE_SEQUANA_M0_PSA": {
"storage_type": "TDB_INTERNAL" "storage_type": "TDB_INTERNAL"
}, },
"FUTURE_SEQUANA_PSA": {
"storage_type": "TDB_INTERNAL"
},
"CY8CKIT_062_WIFI_BT_M0_PSA": { "CY8CKIT_062_WIFI_BT_M0_PSA": {
"storage_type": "TDB_INTERNAL" "storage_type": "TDB_INTERNAL"
}, },

View File

@ -15,6 +15,10 @@
"internal_size": "0x8000", "internal_size": "0x8000",
"internal_base_address": "0x10078000" "internal_base_address": "0x10078000"
}, },
"FUTURE_SEQUANA_PSA": {
"internal_size": "0x8000",
"internal_base_address": "0x100F8000"
},
"CY8CKIT_062_WIFI_BT_M0_PSA": { "CY8CKIT_062_WIFI_BT_M0_PSA": {
"internal_size": "0x8000", "internal_size": "0x8000",
"internal_base_address": "0x10038000" "internal_base_address": "0x10038000"

View File

@ -23,7 +23,6 @@
MBED_NORETURN void mbed_assert_internal(const char *expr, const char *file, int line) MBED_NORETURN void mbed_assert_internal(const char *expr, const char *file, int line)
{ {
core_util_critical_section_enter();
mbed_error(MBED_ERROR_ASSERTION_FAILED, expr, 0, file, line); mbed_error(MBED_ERROR_ASSERTION_FAILED, expr, 0, file, line);
} }

View File

@ -74,6 +74,28 @@ void mbed_error_vprintf(const char *format, va_list arg)
void mbed_error_puts(const char *str) void mbed_error_puts(const char *str)
{ {
// Writing the string to the console in a critical section is
// potentially beneficial - for example in UARTSerial it
// forces the "unbuffered" mode that makes sure all characters
// go out now. If we made the call not in a critical section,
// it would go to the software buffer and we would be reliant
// on platform.stdio-flush-at-exit forcing a fsync before
// entering mbed_die().
//
// But this may be the very first write to the console, and hence
// require it to be initialized - doing this in a critical
// section could be problematic. So we prime it outside the
// critical section with a zero-length write - this forces
// the initialization.
//
// It's still possible that we were in a critical section
// or interrupt on entry anyway (eg if this is an error coming
// from inside RTX), so in other areas of the system we suppress
// things like mutex creation asserts and RTX traps while
// an error is in progress, so that console initialization
// may work.
write(STDERR_FILENO, str, 0);
core_util_critical_section_enter(); core_util_critical_section_enter();
#if MBED_CONF_PLATFORM_STDIO_CONVERT_NEWLINES || MBED_CONF_PLATFORM_STDIO_CONVERT_TTY_NEWLINES #if MBED_CONF_PLATFORM_STDIO_CONVERT_NEWLINES || MBED_CONF_PLATFORM_STDIO_CONVERT_TTY_NEWLINES
char stdio_out_prev = '\0'; char stdio_out_prev = '\0';

View File

@ -28,15 +28,12 @@ extern "C" {
extern uint32_t Image$$RW_m_crash_data$$ZI$$Base[]; extern uint32_t Image$$RW_m_crash_data$$ZI$$Base[];
extern uint32_t Image$$RW_m_crash_data$$ZI$$Size; extern uint32_t Image$$RW_m_crash_data$$ZI$$Size;
#define __CRASH_DATA_RAM_START__ Image$$RW_m_crash_data$$ZI$$Base #define __CRASH_DATA_RAM_START__ Image$$RW_m_crash_data$$ZI$$Base
#define __CRASH_DATA_RAM_SIZE__ Image$$RW_m_crash_data$$ZI$$Size
#elif defined(__ICCARM__) #elif defined(__ICCARM__)
extern uint32_t __CRASH_DATA_RAM_START__[]; extern uint32_t __CRASH_DATA_RAM_START__[];
extern uint32_t __CRASH_DATA_RAM_END__[]; extern uint32_t __CRASH_DATA_RAM_END__[];
#define __CRASH_DATA_RAM_SIZE__ (__CRASH_DATA_RAM_END__ - __CRASH_DATA_RAM_START__)
#elif defined(__GNUC__) #elif defined(__GNUC__)
extern uint32_t __CRASH_DATA_RAM_START__[]; extern uint32_t __CRASH_DATA_RAM_START__[];
extern uint32_t __CRASH_DATA_RAM_END__[]; extern uint32_t __CRASH_DATA_RAM_END__[];
#define __CRASH_DATA_RAM_SIZE__ (__CRASH_DATA_RAM_END__ - __CRASH_DATA_RAM_START__)
#endif /* defined(__CC_ARM) */ #endif /* defined(__CC_ARM) */
/* Offset definitions for context capture */ /* Offset definitions for context capture */

View File

@ -45,7 +45,7 @@ static void print_error_report(const mbed_error_ctx *ctx, const char *, const ch
#define ERROR_REPORT(ctx, error_msg, error_filename, error_line) ((void) 0) #define ERROR_REPORT(ctx, error_msg, error_filename, error_line) ((void) 0)
#endif #endif
static core_util_atomic_flag error_in_progress = CORE_UTIL_ATOMIC_FLAG_INIT; static bool error_in_progress;
static core_util_atomic_flag halt_in_progress = CORE_UTIL_ATOMIC_FLAG_INIT; static core_util_atomic_flag halt_in_progress = CORE_UTIL_ATOMIC_FLAG_INIT;
static int error_count = 0; static int error_count = 0;
static mbed_error_ctx first_error_ctx = {0}; static mbed_error_ctx first_error_ctx = {0};
@ -115,7 +115,7 @@ static MBED_NORETURN void mbed_halt_system(void)
WEAK MBED_NORETURN void error(const char *format, ...) WEAK MBED_NORETURN void error(const char *format, ...)
{ {
// Prevent recursion if error is called again during store+print attempt // Prevent recursion if error is called again during store+print attempt
if (!core_util_atomic_flag_test_and_set(&error_in_progress)) { if (!core_util_atomic_exchange_bool(&error_in_progress, true)) {
handle_error(MBED_ERROR_UNKNOWN, 0, NULL, 0, MBED_CALLER_ADDR()); handle_error(MBED_ERROR_UNKNOWN, 0, NULL, 0, MBED_CALLER_ADDR());
ERROR_REPORT(&last_error_ctx, "Fatal Run-time error", NULL, 0); ERROR_REPORT(&last_error_ctx, "Fatal Run-time error", NULL, 0);
@ -256,6 +256,12 @@ int mbed_get_error_count(void)
return error_count; return error_count;
} }
//Reads the fatal error occurred" flag
bool mbed_get_error_in_progress(void)
{
return core_util_atomic_load_bool(&error_in_progress);
}
//Sets a non-fatal error //Sets a non-fatal error
mbed_error_status_t mbed_warning(mbed_error_status_t error_status, const char *error_msg, unsigned int error_value, const char *filename, int line_number) mbed_error_status_t mbed_warning(mbed_error_status_t error_status, const char *error_msg, unsigned int error_value, const char *filename, int line_number)
{ {
@ -266,7 +272,7 @@ mbed_error_status_t mbed_warning(mbed_error_status_t error_status, const char *e
WEAK MBED_NORETURN mbed_error_status_t mbed_error(mbed_error_status_t error_status, const char *error_msg, unsigned int error_value, const char *filename, int line_number) WEAK MBED_NORETURN mbed_error_status_t mbed_error(mbed_error_status_t error_status, const char *error_msg, unsigned int error_value, const char *filename, int line_number)
{ {
// Prevent recursion if error is called again during store+print attempt // Prevent recursion if error is called again during store+print attempt
if (!core_util_atomic_flag_test_and_set(&error_in_progress)) { if (!core_util_atomic_exchange_bool(&error_in_progress, true)) {
//set the error reported //set the error reported
(void) handle_error(error_status, error_value, filename, line_number, MBED_CALLER_ADDR()); (void) handle_error(error_status, error_value, filename, line_number, MBED_CALLER_ADDR());

View File

@ -23,6 +23,7 @@
#ifndef MBED_ERROR_H #ifndef MBED_ERROR_H
#define MBED_ERROR_H #define MBED_ERROR_H
#include <stdbool.h>
#include "platform/mbed_retarget.h" #include "platform/mbed_retarget.h"
#include "platform/mbed_toolchain.h" #include "platform/mbed_toolchain.h"
@ -50,14 +51,17 @@ extern "C" {
#endif #endif
#define MBED_ERROR_STATUS_CODE_MASK (0x0000FFFF) #define MBED_ERROR_STATUS_CODE_MASK (0x0000FFFF)
#define MBED_ERROR_STATUS_CODE_UNSHIFTED_MASK (0x0000FFFF)
#define MBED_ERROR_STATUS_CODE_POS (0) #define MBED_ERROR_STATUS_CODE_POS (0)
#define MBED_ERROR_STATUS_CODE_FIELD_SIZE (16) #define MBED_ERROR_STATUS_CODE_FIELD_SIZE (16)
#define MBED_ERROR_STATUS_MODULE_MASK (0x00FF0000) #define MBED_ERROR_STATUS_MODULE_MASK (0x00FF0000)
#define MBED_ERROR_STATUS_MODULE_UNSHIFTED_MASK (0x000000FF)
#define MBED_ERROR_STATUS_MODULE_POS (16) #define MBED_ERROR_STATUS_MODULE_POS (16)
#define MBED_ERROR_STATUS_MODULE_FIELD_SIZE (8) #define MBED_ERROR_STATUS_MODULE_FIELD_SIZE (8)
#define MBED_ERROR_STATUS_TYPE_MASK (0x60000000) #define MBED_ERROR_STATUS_TYPE_MASK (0x60000000)
#define MBED_ERROR_STATUS_TYPE_UNSHIFTED_MASK (0x00000003)
#define MBED_ERROR_STATUS_TYPE_POS (29) #define MBED_ERROR_STATUS_TYPE_POS (29)
#define MBED_ERROR_STATUS_TYPE_FIELD_SIZE (2) #define MBED_ERROR_STATUS_TYPE_FIELD_SIZE (2)
@ -65,11 +69,11 @@ extern "C" {
//|31(1 bit) Always Negative|30-29(2 bits) |28-24 | 23-16(8 bits) | 15-0(16 bits) | //|31(1 bit) Always Negative|30-29(2 bits) |28-24 | 23-16(8 bits) | 15-0(16 bits) |
//|-1 |TYPE |(unused/reserved) | MODULE TYPE | ERROR CODE | //|-1 |TYPE |(unused/reserved) | MODULE TYPE | ERROR CODE |
#define MAKE_MBED_ERROR(type, module, error_code) (mbed_error_status_t) \ #define MAKE_MBED_ERROR(type, module, error_code) (mbed_error_status_t) \
((0x80000000) | \ ((0x80000000) | \
(MBED_ERROR_STATUS_CODE_MASK & (error_code << MBED_ERROR_STATUS_CODE_POS)) | \ ((mbed_error_status_t) (error_code & MBED_ERROR_STATUS_CODE_UNSHIFTED_MASK) << MBED_ERROR_STATUS_CODE_POS) | \
(MBED_ERROR_STATUS_MODULE_MASK & (module << MBED_ERROR_STATUS_MODULE_POS)) | \ ((mbed_error_status_t) (module & MBED_ERROR_STATUS_MODULE_UNSHIFTED_MASK) << MBED_ERROR_STATUS_MODULE_POS) | \
(MBED_ERROR_STATUS_TYPE_MASK & (type << MBED_ERROR_STATUS_TYPE_POS))) ((mbed_error_status_t) (type & MBED_ERROR_STATUS_TYPE_UNSHIFTED_MASK) << MBED_ERROR_STATUS_TYPE_POS))
#define MBED_GET_ERROR_TYPE( error_status ) ((error_status & MBED_ERROR_STATUS_TYPE_MASK) >> MBED_ERROR_STATUS_TYPE_POS) #define MBED_GET_ERROR_TYPE( error_status ) ((error_status & MBED_ERROR_STATUS_TYPE_MASK) >> MBED_ERROR_STATUS_TYPE_POS)
#define MBED_GET_ERROR_MODULE( error_status ) ((error_status & MBED_ERROR_STATUS_MODULE_MASK) >> MBED_ERROR_STATUS_MODULE_POS) #define MBED_GET_ERROR_MODULE( error_status ) ((error_status & MBED_ERROR_STATUS_MODULE_MASK) >> MBED_ERROR_STATUS_MODULE_POS)
@ -83,7 +87,7 @@ extern "C" {
* *
\verbatim \verbatim
| 31 Always Negative | 30-29(2 bits) | 28-24 | 23-16(8 bits) | 15-0(16 bits) | | 31 Always Negative | 30-29(2 bits) | 28-24 | 23-16(8 bits) | 15-0(16 bits) |
| -1 | TYPE | (unused/reserved) | MODULE TYPE | ERROR CODE | | -1 | TYPE | (unused/reserved) | MODULE TYPE | ERROR CODE |
\endverbatim \endverbatim
* *
* The error status value range for each error type is as follows:\n * The error status value range for each error type is as follows:\n
@ -1036,6 +1040,13 @@ mbed_error_status_t mbed_get_last_error(void);
*/ */
int mbed_get_error_count(void); int mbed_get_error_count(void);
/**
* Returns whether we are processing a fatal mbed error.
* @return bool Whether a fatal error has occurred.
*
*/
bool mbed_get_error_in_progress(void);
/** /**
* Call this function to set a fatal system error and halt the system. This function will log the fatal error with the context info and prints the error report and halts the system. * Call this function to set a fatal system error and halt the system. This function will log the fatal error with the context info and prints the error report and halts the system.
* *

View File

@ -127,7 +127,9 @@ MBED_NORETURN void mbed_die(void);
/** Print out an error message. This is typically called when /** Print out an error message. This is typically called when
* handling a crash. * handling a crash.
* *
* @note Synchronization level: Interrupt safe * @note Synchronization level: Interrupt safe, as long as the
* FileHandle::write of the stderr device is. See mbed_error_puts
* for more information.
* @note This uses an internal 128-byte buffer to format the string, * @note This uses an internal 128-byte buffer to format the string,
* so the output may be truncated. If you need to write a potentially * so the output may be truncated. If you need to write a potentially
* long string, use mbed_error_puts. * long string, use mbed_error_puts.
@ -145,7 +147,9 @@ void mbed_error_printf(const char *format, ...) MBED_PRINTF(1, 2);
/** Print out an error message. Similar to mbed_error_printf /** Print out an error message. Similar to mbed_error_printf
* but uses a va_list. * but uses a va_list.
* *
* @note Synchronization level: Interrupt safe * @note Synchronization level: Interrupt safe, as long as the
* FileHandle::write of the stderr device is. See mbed_error_puts
* for more information.
* *
* @param format C string that contains data stream to be printed. * @param format C string that contains data stream to be printed.
* @param arg Variable arguments list * @param arg Variable arguments list
@ -160,7 +164,13 @@ void mbed_error_vprintf(const char *format, va_list arg) MBED_PRINTF(1, 0);
* length. Unlike standard puts, but like standard fputs, this does not * length. Unlike standard puts, but like standard fputs, this does not
* append a '\n' character. * append a '\n' character.
* *
* @note Synchronization level: Interrupt safe * @note Synchronization level: Interrupt safe, as long as the
* FileHandle::write of the stderr device is. The default
* serial console is safe, either buffered or not. If the
* console has not previously been initialized, an attempt
* to use this from interrupt may during console initialization.
* Special handling of `mbed_error` relaxes various system traps
* to increase the chance of initialization working.
* *
* @param str C string that contains data stream to be printed. * @param str C string that contains data stream to be printed.
* *

View File

@ -183,6 +183,10 @@
"crash-capture-enabled": true, "crash-capture-enabled": true,
"fatal-error-auto-reboot-enabled": true "fatal-error-auto-reboot-enabled": true
}, },
"NUMAKER_PFM_NUC472": {
"crash-capture-enabled": true,
"fatal-error-auto-reboot-enabled": true
},
"NRF52840_DK": { "NRF52840_DK": {
"crash-capture-enabled": true, "crash-capture-enabled": true,
"fatal-error-auto-reboot-enabled": true "fatal-error-auto-reboot-enabled": true

Some files were not shown because too many files have changed in this diff Show More