Import Mbed OS hard-float snapshot
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2015 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.
|
||||
*/
|
||||
#include "stdint.h"
|
||||
#include "ip_fsc.h"
|
||||
|
||||
/** \brief Compute IP checksum for arbitary data
|
||||
*
|
||||
* Compute an IP checksum, given a arbitrary gather list.
|
||||
*
|
||||
* See ipv6_fcf for discussion of use.
|
||||
*
|
||||
* This will work for any arbitrary gather list - it can handle odd
|
||||
* alignments. The one limitation is that the 32-bit accumulator limits
|
||||
* it to basically 64K of total data.
|
||||
*/
|
||||
uint16_t ip_fcf_v(uint_fast8_t count, const ns_iovec_t vec[static count])
|
||||
{
|
||||
uint_fast32_t acc32 = 0;
|
||||
bool odd = false;
|
||||
while (count) {
|
||||
const uint8_t *data_ptr = vec->iov_base;
|
||||
uint_fast16_t data_length = vec->iov_len;
|
||||
if (odd && data_length > 0) {
|
||||
acc32 += *data_ptr++;
|
||||
data_length--;
|
||||
odd = false;
|
||||
}
|
||||
while (data_length >= 2) {
|
||||
acc32 += (uint_fast16_t) data_ptr[0] << 8 | data_ptr[1];
|
||||
data_ptr += 2;
|
||||
data_length -= 2;
|
||||
}
|
||||
if (data_length) {
|
||||
acc32 += (uint_fast16_t) data_ptr[0] << 8;
|
||||
odd = true;
|
||||
}
|
||||
vec++;
|
||||
count--;
|
||||
}
|
||||
|
||||
// Fold down up to 0xffff carries in the 32-bit accumulator
|
||||
acc32 = (acc32 >> 16) + (acc32 & 0xffff);
|
||||
|
||||
// Could be one more carry from the previous addition (result <= 0x1fffe)
|
||||
uint16_t sum16 = (uint16_t)((acc32 >> 16) + (acc32 & 0xffff));
|
||||
return ~sum16;
|
||||
}
|
||||
|
||||
/** \brief Compute IPv6 checksum
|
||||
*
|
||||
* Compute an IPv6 checksum, given fields of an IPv6 pseudoheader and payload.
|
||||
*
|
||||
* This returns the 1's-complement of the checksum, as required when
|
||||
* generating the checksum for transmission. The result can be 0x0000;
|
||||
* for UDP (only) this must be transformed to 0xFFFF to distinguish from
|
||||
* a packet with no checksum.
|
||||
*
|
||||
* To check a packet, this function will return 0 when run on a
|
||||
* packet with a valid checksum. Checksums should be checked like this rather
|
||||
* than setting the checksum field to zero and comparing generated checksum with
|
||||
* the original value - this would fail in the case the received packet had
|
||||
* checksum 0xFFFF.
|
||||
*/
|
||||
uint16_t ipv6_fcf(const uint8_t src_address[static 16], const uint8_t dest_address[static 16],
|
||||
uint16_t data_length, const uint8_t data_ptr[static data_length], uint8_t next_protocol)
|
||||
{
|
||||
// Use gather vector to lay out IPv6 pseudo-header (RFC 2460) and data
|
||||
uint8_t hdr_data[] = { data_length >> 8, data_length, 0, next_protocol };
|
||||
ns_iovec_t vec[4] = {
|
||||
{ (void *) src_address, 16 },
|
||||
{ (void *) dest_address, 16 },
|
||||
{ hdr_data, 4 },
|
||||
{ (void *) data_ptr, data_length }
|
||||
};
|
||||
|
||||
return ip_fcf_v(4, vec);
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2015 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Most functions can be inlined, and definitions are in common_functions.h.
|
||||
* Define COMMON_FUNCTIONS_FN before including it to generate external definitions.
|
||||
*/
|
||||
#define COMMON_FUNCTIONS_FN extern
|
||||
|
||||
#include "common_functions.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* Returns mask for <split_value> (0-8) most-significant bits of a byte */
|
||||
static inline uint8_t context_split_mask(uint_fast8_t split_value)
|
||||
{
|
||||
return (uint8_t) - (0x100u >> split_value);
|
||||
}
|
||||
|
||||
bool bitsequal(const uint8_t *a, const uint8_t *b, uint_fast8_t bits)
|
||||
{
|
||||
uint_fast8_t bytes = bits / 8;
|
||||
bits %= 8;
|
||||
|
||||
if (bytes && memcmp(a, b, bytes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bits) {
|
||||
uint_fast8_t split_bit = context_split_mask(bits);
|
||||
if ((a[bytes] & split_bit) != (b[bytes] & split_bit)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t *bitcopy(uint8_t *restrict dst, const uint8_t *restrict src, uint_fast8_t bits)
|
||||
{
|
||||
uint_fast8_t bytes = bits / 8;
|
||||
bits %= 8;
|
||||
|
||||
if (bytes) {
|
||||
dst = (uint8_t *) memcpy(dst, src, bytes) + bytes;
|
||||
src += bytes;
|
||||
}
|
||||
|
||||
if (bits) {
|
||||
uint_fast8_t split_bit = context_split_mask(bits);
|
||||
*dst = (*src & split_bit) | (*dst & ~ split_bit);
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
uint8_t *bitcopy0(uint8_t *restrict dst, const uint8_t *restrict src, uint_fast8_t bits)
|
||||
{
|
||||
uint_fast8_t bytes = bits / 8;
|
||||
bits %= 8;
|
||||
|
||||
if (bytes) {
|
||||
dst = (uint8_t *) memcpy(dst, src, bytes) + bytes;
|
||||
src += bytes;
|
||||
}
|
||||
|
||||
if (bits) {
|
||||
uint_fast8_t split_bit = context_split_mask(bits);
|
||||
*dst = (*src & split_bit);
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2015 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* All functions can be inlined, and definitions are in ns_list.h.
|
||||
* Define NS_LIST_FN before including it to generate external definitions.
|
||||
*/
|
||||
#define NS_LIST_FN extern
|
||||
|
||||
#include "ns_list.h"
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
egrep -hr '^#define' ../../../../ |sed -n 's/\#define\s*TRACE_GROUP_[^\s]*\s*\"\([^\"]*\)\"/\1/p' > groups.txt
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2014-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.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "common_functions.h"
|
||||
#include "ip4string.h"
|
||||
|
||||
static void ipv4_itoa(char *string, uint8_t byte);
|
||||
|
||||
/**
|
||||
* Print binary IPv4 address to a string.
|
||||
* String must contain enough room for full address, 16 bytes exact.
|
||||
* \param addr IPv4 address.
|
||||
* \p buffer to write string to.
|
||||
*/
|
||||
uint_fast8_t ip4tos(const void *ip4addr, char *p)
|
||||
{
|
||||
uint_fast8_t outputPos = 0;
|
||||
const uint8_t *byteArray = ip4addr;
|
||||
|
||||
for (uint_fast8_t component = 0; component < 4; ++component) {
|
||||
//Convert the byte to string
|
||||
ipv4_itoa(&p[outputPos], byteArray[component]);
|
||||
|
||||
//Move outputPos to the end of the string
|
||||
while (p[outputPos] != '\0') {
|
||||
outputPos += 1;
|
||||
}
|
||||
|
||||
//Append a dot if this is not the last digit
|
||||
if (component < 3) {
|
||||
p[outputPos++] = '.';
|
||||
}
|
||||
}
|
||||
|
||||
// Return length of generated string, excluding the terminating null character
|
||||
return outputPos;
|
||||
}
|
||||
|
||||
static void ipv4_itoa(char *string, uint8_t byte)
|
||||
{
|
||||
char *baseString = string;
|
||||
|
||||
//Write the digits to the buffer from the least significant to the most
|
||||
// This is the incorrect order but we will swap later
|
||||
do {
|
||||
*string++ = '0' + byte % 10;
|
||||
byte /= 10;
|
||||
} while (byte);
|
||||
|
||||
//We put the final \0, then go back one step on the last digit for the swap
|
||||
*string-- = '\0';
|
||||
|
||||
//We now swap the digits
|
||||
while (baseString < string) {
|
||||
uint8_t tmp = *string;
|
||||
*string-- = *baseString;
|
||||
*baseString++ = tmp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (c) 2014-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.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include "common_functions.h"
|
||||
#include "ip4string.h"
|
||||
|
||||
/**
|
||||
* Convert numeric IPv4 address string to a binary.
|
||||
* \param ip4addr IPv4 address in string format.
|
||||
* \param len Length of IPv4 string, maximum of 16..
|
||||
* \param dest buffer for address. MUST be 4 bytes.
|
||||
* \return boolean set to true if conversion succeded, false if it didn't
|
||||
*/
|
||||
bool stoip4(const char *ip4addr, size_t len, void *dest)
|
||||
{
|
||||
uint8_t *addr = dest;
|
||||
|
||||
if (len > 16) { // Too long, not possible
|
||||
return false;
|
||||
}
|
||||
|
||||
uint_fast8_t stringLength = 0, byteCount = 0;
|
||||
|
||||
//Iterate over each component of the IP. The exit condition is in the middle of the loop
|
||||
while (true) {
|
||||
|
||||
//No valid character (IPv4 addresses don't have implicit 0, that is x.y..z being read as x.y.0.z)
|
||||
if (stringLength == len || ip4addr[stringLength] < '0' || ip4addr[stringLength] > '9') {
|
||||
return false;
|
||||
}
|
||||
|
||||
//For each component, we convert it to the raw value
|
||||
uint_fast16_t byte = 0;
|
||||
while (stringLength < len && ip4addr[stringLength] >= '0' && ip4addr[stringLength] <= '9') {
|
||||
byte *= 10;
|
||||
byte += ip4addr[stringLength++] - '0';
|
||||
|
||||
//We go over the maximum value for an IPv4 component
|
||||
if (byte > 0xff) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//Append the component
|
||||
addr[byteCount++] = (uint8_t) byte;
|
||||
|
||||
//If we're at the end, we leave the loop. It's the only way to reach the `true` output
|
||||
if (byteCount == 4) {
|
||||
break;
|
||||
}
|
||||
|
||||
//If the next character is invalid, we return false
|
||||
if (stringLength == len || ip4addr[stringLength++] != '.') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return stringLength == len || ip4addr[stringLength] == '\0';
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2015 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.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "common_functions.h"
|
||||
#include "ip6string.h"
|
||||
|
||||
/**
|
||||
* Print binary IPv6 address to a string.
|
||||
* String must contain enough room for full address, 40 bytes exact.
|
||||
* IPv4 tunneling addresses are not covered.
|
||||
* \param addr IPv6 address.
|
||||
* \p buffer to write string to.
|
||||
*/
|
||||
uint_fast8_t ip6tos(const void *ip6addr, char *p)
|
||||
{
|
||||
char *p_orig = p;
|
||||
uint_fast8_t zero_start = 255, zero_len = 1;
|
||||
const uint8_t *addr = ip6addr;
|
||||
uint_fast16_t part;
|
||||
|
||||
/* Follow RFC 5952 - pre-scan for longest run of zeros */
|
||||
for (uint_fast8_t n = 0; n < 8; n++) {
|
||||
part = *addr++;
|
||||
part = (part << 8) | *addr++;
|
||||
if (part != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We're at the start of a run of zeros - scan to non-zero (or end) */
|
||||
uint_fast8_t n0 = n;
|
||||
for (n = n0 + 1; n < 8; n++) {
|
||||
part = *addr++;
|
||||
part = (part << 8) | *addr++;
|
||||
if (part != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now n0->initial zero of run, n->after final zero in run. Is this the
|
||||
* longest run yet? If equal, we stick with the previous one - RFC 5952
|
||||
* S4.2.3. Note that zero_len being initialised to 1 stops us
|
||||
* shortening a 1-part run (S4.2.2.)
|
||||
*/
|
||||
if (n - n0 > zero_len) {
|
||||
zero_start = n0;
|
||||
zero_len = n - n0;
|
||||
}
|
||||
|
||||
/* Continue scan for initial zeros from part n+1 - we've already
|
||||
* consumed part n, and know it's non-zero. */
|
||||
}
|
||||
|
||||
/* Now go back and print, jumping over any zero run */
|
||||
addr = ip6addr;
|
||||
for (uint_fast8_t n = 0; n < 8;) {
|
||||
if (n == zero_start) {
|
||||
if (n == 0) {
|
||||
*p++ = ':';
|
||||
}
|
||||
*p++ = ':';
|
||||
addr += 2 * zero_len;
|
||||
n += zero_len;
|
||||
continue;
|
||||
}
|
||||
|
||||
part = *addr++;
|
||||
part = (part << 8) | *addr++;
|
||||
n++;
|
||||
|
||||
p += sprintf(p, "%"PRIxFAST16, part);
|
||||
|
||||
/* One iteration writes "part:" rather than ":part", and has the
|
||||
* explicit check for n == 8 below, to allow easy extension for
|
||||
* IPv4-in-IPv6-type addresses ("xxxx::xxxx:a.b.c.d"): we'd just
|
||||
* run the same loop for 6 parts, and output would then finish with the
|
||||
* required : or ::, ready for "a.b.c.d" to be tacked on.
|
||||
*/
|
||||
if (n != 8) {
|
||||
*p++ = ':';
|
||||
}
|
||||
}
|
||||
*p = '\0';
|
||||
|
||||
// Return length of generated string, excluding the terminating null character
|
||||
return p - p_orig;
|
||||
}
|
||||
|
||||
uint_fast8_t ip6_prefix_tos(const void *prefix, uint_fast8_t prefix_len, char *p)
|
||||
{
|
||||
char *wptr = p;
|
||||
uint8_t addr[16] = {0};
|
||||
|
||||
if (prefix_len > 128) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Generate prefix part of the string
|
||||
bitcopy(addr, prefix, prefix_len);
|
||||
wptr += ip6tos(addr, wptr);
|
||||
// Add the prefix length part of the string
|
||||
wptr += sprintf(wptr, "/%"PRIuFAST8, prefix_len);
|
||||
|
||||
// Return total length of generated string
|
||||
return wptr - p;
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2015 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.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include "common_functions.h"
|
||||
#include "ip6string.h"
|
||||
|
||||
static uint16_t hex(const char *p);
|
||||
static bool is_hex(char c);
|
||||
|
||||
/**
|
||||
* Convert numeric IPv6 address string to a binary.
|
||||
* IPv4 tunnelling addresses are not covered.
|
||||
* \param ip6addr IPv6 address in string format.
|
||||
* \param len Length of ipv6 string.
|
||||
* \param dest buffer for address. MUST be 16 bytes.
|
||||
* \return boolean set to true if conversion succeed, false if it didn't
|
||||
*/
|
||||
bool stoip6(const char *ip6addr, size_t len, void *dest)
|
||||
{
|
||||
uint8_t *addr;
|
||||
const char *p, *q;
|
||||
int_fast8_t field_no, coloncolon = -1;
|
||||
|
||||
addr = dest;
|
||||
|
||||
if (len > 39) { // Too long, not possible. We do not support IPv4-mapped IPv6 addresses
|
||||
goto error;
|
||||
}
|
||||
|
||||
// First go forward the string, until end, noting :: position if any
|
||||
// We're decrementing `len` as we go forward, and stop when it reaches 0
|
||||
for (field_no = 0, p = ip6addr; len && *p; p = q + 1) {
|
||||
|
||||
for (q = p; len && *q && (*q != ':'); len -= 1) { // Seek for ':' or end
|
||||
if (!is_hex(*q++)) { // There must only be hex characters besides ':'
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if ((q - p) > 4) { // We can't have more than 4 hex digits per segment
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (field_no == 8) { // If the address goes farther than 8 segments
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Convert and write this part, (high-endian AKA network byte order)
|
||||
addr = common_write_16_bit(hex(p), addr);
|
||||
field_no++;
|
||||
|
||||
// We handle the colons
|
||||
if (len) {
|
||||
// Check if we reached "::"
|
||||
if (q[0] == ':' && q[1] == ':') {
|
||||
if (coloncolon != -1) { // We are not supposed to see "::" more than once per address
|
||||
goto error;
|
||||
}
|
||||
coloncolon = field_no;
|
||||
q++;
|
||||
len -= 2;
|
||||
} else {
|
||||
len -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (coloncolon != -1) {
|
||||
/* Insert zeros in the appropriate place */
|
||||
uint_fast8_t head_size = 2 * coloncolon;
|
||||
uint_fast8_t inserted_size = 2 * (8 - field_no);
|
||||
uint_fast8_t tail_size = 16 - head_size - inserted_size;
|
||||
addr = dest;
|
||||
memmove(addr + head_size + inserted_size, addr + head_size, tail_size);
|
||||
memset(addr + head_size, 0, inserted_size);
|
||||
} else if (field_no != 8) { // Report an error if we didn't get 8 fields
|
||||
goto error;
|
||||
}
|
||||
return true;
|
||||
|
||||
error:
|
||||
// Fill the output buffer with 0 so we stick to the old failure behavior.
|
||||
// We are however more agressive and wipe the entire address, and do so more often.
|
||||
memset(dest, 0, 16);
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char sipv6_prefixlength(const char *ip6addr)
|
||||
{
|
||||
char *ptr = strchr(ip6addr, '/');
|
||||
if (ptr) {
|
||||
return (unsigned char)strtoul(ptr + 1, 0, 10);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int stoip6_prefix(const char *ip6addr, void *dest, int_fast16_t *prefix_len_out)
|
||||
{
|
||||
size_t addr_len, total_len;
|
||||
int_fast16_t prefix_length;
|
||||
|
||||
if (prefix_len_out) {
|
||||
*prefix_len_out = -1;
|
||||
}
|
||||
|
||||
total_len = addr_len = strlen(ip6addr);
|
||||
const char *ptr = strchr(ip6addr, '/');
|
||||
if (ptr) {
|
||||
addr_len = ptr - ip6addr;
|
||||
if (prefix_len_out) {
|
||||
if (total_len - addr_len > 3) {
|
||||
/* too many digits in prefix */
|
||||
return -1;
|
||||
}
|
||||
|
||||
prefix_length = strtoul(ptr + 1, 0, 10);
|
||||
if (prefix_length < 0 || prefix_length > 128) {
|
||||
/* prefix value illegal */
|
||||
return -1;
|
||||
}
|
||||
|
||||
*prefix_len_out = prefix_length;
|
||||
}
|
||||
}
|
||||
|
||||
if (!stoip6(ip6addr, addr_len, dest)) {
|
||||
/* parser failure */
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool is_hex(char c)
|
||||
{
|
||||
// 'A' (0x41) and 'a' (0x61) are mapped in the ASCII table in such a way that masking the 0x20 bit turn 'a' in 'A'
|
||||
if ((c & ~0x20) >= 'A' && (c & ~0x20) <= 'F') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (c >= '0' && c <= '9') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint16_t hex(const char *p)
|
||||
{
|
||||
uint16_t val = 0;
|
||||
|
||||
for (;;) {
|
||||
char c = *p++;
|
||||
if ((c >= '0') && (c <= '9')) {
|
||||
val = (val << 4) | (c - '0');
|
||||
} else if ((c >= 'A') && (c <= 'F')) {
|
||||
val = (val << 4) | (10 + (c - 'A'));
|
||||
} else if ((c >= 'a') && (c <= 'f')) {
|
||||
val = (val << 4) | (10 + (c - 'a'));
|
||||
} else {
|
||||
break; // Non hex character
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
@@ -0,0 +1,640 @@
|
||||
/*
|
||||
* Copyright (c) 2014-2019 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.
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "nsdynmemLIB.h"
|
||||
#include "platform/arm_hal_interrupt.h"
|
||||
#include <stdlib.h>
|
||||
#include "ns_list.h"
|
||||
|
||||
#ifndef STANDARD_MALLOC
|
||||
typedef enum mem_stat_update_t {
|
||||
DEV_HEAP_ALLOC_OK,
|
||||
DEV_HEAP_ALLOC_FAIL,
|
||||
DEV_HEAP_FREE,
|
||||
} mem_stat_update_t;
|
||||
|
||||
typedef struct {
|
||||
ns_list_link_t link;
|
||||
} hole_t;
|
||||
|
||||
typedef int ns_mem_word_size_t; // internal signed heap block size type
|
||||
|
||||
// Amount of memory regions
|
||||
#define REGION_COUNT 3
|
||||
|
||||
/* struct for book keeping variables */
|
||||
struct ns_mem_book {
|
||||
ns_mem_word_size_t *heap_main[REGION_COUNT];
|
||||
ns_mem_word_size_t *heap_main_end[REGION_COUNT];
|
||||
mem_stat_t *mem_stat_info_ptr;
|
||||
void (*heap_failure_callback)(heap_fail_t);
|
||||
NS_LIST_HEAD(hole_t, link) holes_list;
|
||||
ns_mem_heap_size_t heap_size;
|
||||
ns_mem_heap_size_t temporary_alloc_heap_limit; /* Amount of reserved heap temporary alloc can't exceed */
|
||||
};
|
||||
|
||||
static ns_mem_book_t *default_book; // heap pointer for original "ns_" API use
|
||||
|
||||
// size of a hole_t in our word units
|
||||
#define HOLE_T_SIZE ((ns_mem_word_size_t) ((sizeof(hole_t) + sizeof(ns_mem_word_size_t) - 1) / sizeof(ns_mem_word_size_t)))
|
||||
|
||||
#define TEMPORARY_ALLOC_FREE_HEAP_THRESHOLD 5 /* temporary allocations must leave 5% of the heap free */
|
||||
|
||||
static NS_INLINE hole_t *hole_from_block_start(ns_mem_word_size_t *start)
|
||||
{
|
||||
return (hole_t *)(start + 1);
|
||||
}
|
||||
|
||||
static NS_INLINE ns_mem_word_size_t *block_start_from_hole(hole_t *start)
|
||||
{
|
||||
return ((ns_mem_word_size_t *)start) - 1;
|
||||
}
|
||||
|
||||
static void heap_failure(ns_mem_book_t *book, heap_fail_t reason)
|
||||
{
|
||||
if (book->heap_failure_callback) {
|
||||
book->heap_failure_callback(reason);
|
||||
}
|
||||
}
|
||||
|
||||
static int ns_dyn_mem_region_find(ns_mem_book_t *book, ns_mem_word_size_t *block_ptr, ns_mem_word_size_t size)
|
||||
{
|
||||
int index;
|
||||
for (index = 0; index < REGION_COUNT; index++) {
|
||||
if (book->heap_main[index] != 0) {
|
||||
if ((block_ptr >= book->heap_main[index]) &&
|
||||
(block_ptr < book->heap_main_end[index]) &&
|
||||
((block_ptr + size) < book->heap_main_end[index])) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ns_dyn_mem_region_save(ns_mem_book_t *book, ns_mem_word_size_t *region_start_ptr, ns_mem_word_size_t region_size)
|
||||
{
|
||||
for (int i = 1; i < REGION_COUNT; i++) {
|
||||
if (book->heap_main[i] == 0) {
|
||||
book->heap_main[i] = region_start_ptr;
|
||||
book->heap_main_end[i] = book->heap_main[i] + region_size;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
#endif //STANDARD_MALLOC
|
||||
|
||||
void ns_dyn_mem_init(void *heap, ns_mem_heap_size_t h_size,
|
||||
void (*passed_fptr)(heap_fail_t), mem_stat_t *info_ptr)
|
||||
{
|
||||
default_book = ns_mem_init(heap, h_size, passed_fptr, info_ptr);
|
||||
}
|
||||
|
||||
int ns_dyn_mem_region_add(void *region_ptr, ns_mem_heap_size_t region_size)
|
||||
{
|
||||
return ns_mem_region_add(default_book, region_ptr, region_size);
|
||||
}
|
||||
|
||||
const mem_stat_t *ns_dyn_mem_get_mem_stat(void)
|
||||
{
|
||||
#ifndef STANDARD_MALLOC
|
||||
return ns_mem_get_mem_stat(default_book);
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
ns_mem_book_t *ns_mem_init(void *heap, ns_mem_heap_size_t h_size,
|
||||
void (*passed_fptr)(heap_fail_t),
|
||||
mem_stat_t *info_ptr)
|
||||
{
|
||||
#ifndef STANDARD_MALLOC
|
||||
ns_mem_book_t *book;
|
||||
|
||||
ns_mem_word_size_t *ptr;
|
||||
ns_mem_word_size_t temp_int;
|
||||
/* Do memory alignment */
|
||||
temp_int = ((uintptr_t)heap % sizeof(ns_mem_word_size_t));
|
||||
if (temp_int) {
|
||||
heap = (uint8_t *) heap + (sizeof(ns_mem_word_size_t) - temp_int);
|
||||
h_size -= (sizeof(ns_mem_word_size_t) - temp_int);
|
||||
}
|
||||
|
||||
/* Make correction for total length also */
|
||||
temp_int = (h_size % sizeof(ns_mem_word_size_t));
|
||||
if (temp_int) {
|
||||
h_size -= (sizeof(ns_mem_word_size_t) - temp_int);
|
||||
}
|
||||
|
||||
book = heap;
|
||||
memset(book->heap_main, 0, REGION_COUNT * sizeof(ns_mem_word_size_t *));
|
||||
memset(book->heap_main_end, 0, REGION_COUNT * sizeof(ns_mem_word_size_t *));
|
||||
|
||||
book->heap_main[0] = (ns_mem_word_size_t *) & (book[1]); // SET Heap Pointer
|
||||
book->heap_size = h_size - sizeof(ns_mem_book_t); //Set Heap Size
|
||||
temp_int = (book->heap_size / sizeof(ns_mem_word_size_t));
|
||||
temp_int -= 2;
|
||||
ptr = book->heap_main[0];
|
||||
*ptr = -(temp_int);
|
||||
ptr += (temp_int + 1);
|
||||
*ptr = -(temp_int);
|
||||
book->heap_main_end[0] = ptr;
|
||||
|
||||
ns_list_init(&book->holes_list);
|
||||
ns_list_add_to_start(&book->holes_list, hole_from_block_start(book->heap_main[0]));
|
||||
|
||||
book->mem_stat_info_ptr = info_ptr;
|
||||
//RESET Memory by Hea Len
|
||||
if (info_ptr) {
|
||||
memset(book->mem_stat_info_ptr, 0, sizeof(mem_stat_t));
|
||||
book->mem_stat_info_ptr->heap_sector_size = book->heap_size;
|
||||
}
|
||||
book->temporary_alloc_heap_limit = book->heap_size / 100 * (100 - TEMPORARY_ALLOC_FREE_HEAP_THRESHOLD);
|
||||
#endif
|
||||
//There really is no support to standard malloc in this library anymore
|
||||
book->heap_failure_callback = passed_fptr;
|
||||
|
||||
return book;
|
||||
}
|
||||
|
||||
int ns_mem_region_add(ns_mem_book_t *book, void *region_ptr, ns_mem_heap_size_t region_size)
|
||||
{
|
||||
#ifndef STANDARD_MALLOC
|
||||
if (!book || !region_ptr || region_size < 3 * sizeof(ns_mem_word_size_t)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ns_mem_word_size_t *block_ptr;
|
||||
ns_mem_word_size_t temp_int;
|
||||
|
||||
/* Do memory alignment */
|
||||
temp_int = ((uintptr_t)region_ptr % sizeof(ns_mem_word_size_t));
|
||||
if (temp_int) {
|
||||
region_ptr = (uint8_t *) region_ptr + (sizeof(ns_mem_word_size_t) - temp_int);
|
||||
region_size -= (sizeof(ns_mem_word_size_t) - temp_int);
|
||||
}
|
||||
|
||||
/* Make correction for total length */
|
||||
temp_int = (region_size % sizeof(ns_mem_word_size_t));
|
||||
if (temp_int) {
|
||||
region_size -= (sizeof(ns_mem_word_size_t) - temp_int);
|
||||
}
|
||||
|
||||
// Create hole from new heap memory
|
||||
temp_int = (region_size / sizeof(ns_mem_word_size_t));
|
||||
temp_int -= 2;
|
||||
block_ptr = region_ptr;
|
||||
*block_ptr = -(temp_int);
|
||||
block_ptr += (temp_int + 1); // now block_ptr points to end of block
|
||||
*block_ptr = -(temp_int);
|
||||
|
||||
// find place for the new hole from the holes list
|
||||
hole_t *hole_to_add = hole_from_block_start(region_ptr);
|
||||
hole_t *previous_hole = NULL;
|
||||
ns_list_foreach(hole_t, hole_in_list_ptr, &book->holes_list) {
|
||||
if (hole_in_list_ptr < hole_to_add) {
|
||||
previous_hole = hole_in_list_ptr;
|
||||
} else if (hole_in_list_ptr == hole_to_add) {
|
||||
// trying to add memory block that is already in the list!
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
// save region
|
||||
if (ns_dyn_mem_region_save(book, region_ptr, (region_size / (sizeof(ns_mem_word_size_t))) - 1) != 0) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
// Add new hole to the list
|
||||
if (previous_hole) {
|
||||
ns_list_add_after(&book->holes_list, previous_hole, hole_to_add);
|
||||
} else {
|
||||
ns_list_add_to_start(&book->holes_list, hole_to_add);
|
||||
}
|
||||
|
||||
// adjust total heap size with new hole
|
||||
book->heap_size += region_size;
|
||||
|
||||
if (book->mem_stat_info_ptr) {
|
||||
book->mem_stat_info_ptr->heap_sector_size = book->heap_size;
|
||||
}
|
||||
|
||||
// adjust temporary allocation limits to match new heap
|
||||
book->temporary_alloc_heap_limit = book->heap_size / 100 * (100 - TEMPORARY_ALLOC_FREE_HEAP_THRESHOLD);
|
||||
|
||||
return 0;
|
||||
#else
|
||||
(void) book;
|
||||
(void) region_ptr;
|
||||
(void) region_size;
|
||||
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
const mem_stat_t *ns_mem_get_mem_stat(ns_mem_book_t *heap)
|
||||
{
|
||||
#ifndef STANDARD_MALLOC
|
||||
return heap->mem_stat_info_ptr;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
int ns_mem_set_temporary_alloc_free_heap_threshold(ns_mem_book_t *book, uint8_t free_heap_percentage, ns_mem_heap_size_t free_heap_amount)
|
||||
{
|
||||
#ifndef STANDARD_MALLOC
|
||||
ns_mem_heap_size_t heap_limit = 0;
|
||||
|
||||
if (!book || !book->mem_stat_info_ptr) {
|
||||
// no book or mem_stats
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (free_heap_amount && free_heap_amount < book->heap_size / 2) {
|
||||
heap_limit = book->heap_size - free_heap_amount;
|
||||
}
|
||||
|
||||
if (!free_heap_amount && free_heap_percentage && free_heap_percentage < 50) {
|
||||
heap_limit = book->heap_size / 100 * (100 - free_heap_percentage);
|
||||
}
|
||||
|
||||
if (free_heap_amount == 0 && free_heap_percentage == 0) {
|
||||
// feature disabled, allow whole heap to be reserved by temporary allo
|
||||
heap_limit = book->heap_size;
|
||||
}
|
||||
|
||||
if (heap_limit == 0) {
|
||||
// illegal heap parameters
|
||||
return -2;
|
||||
}
|
||||
|
||||
book->temporary_alloc_heap_limit = heap_limit;
|
||||
|
||||
return 0;
|
||||
#else
|
||||
return -3;
|
||||
#endif
|
||||
}
|
||||
|
||||
extern int ns_dyn_mem_set_temporary_alloc_free_heap_threshold(uint8_t free_heap_percentage, ns_mem_heap_size_t free_heap_amount)
|
||||
{
|
||||
return ns_mem_set_temporary_alloc_free_heap_threshold(default_book, free_heap_percentage, free_heap_amount);
|
||||
}
|
||||
|
||||
#ifndef STANDARD_MALLOC
|
||||
static void dev_stat_update(mem_stat_t *mem_stat_info_ptr, mem_stat_update_t type, ns_mem_block_size_t size)
|
||||
{
|
||||
if (mem_stat_info_ptr) {
|
||||
switch (type) {
|
||||
case DEV_HEAP_ALLOC_OK:
|
||||
mem_stat_info_ptr->heap_sector_alloc_cnt++;
|
||||
mem_stat_info_ptr->heap_sector_allocated_bytes += size;
|
||||
if (mem_stat_info_ptr->heap_sector_allocated_bytes_max < mem_stat_info_ptr->heap_sector_allocated_bytes) {
|
||||
mem_stat_info_ptr->heap_sector_allocated_bytes_max = mem_stat_info_ptr->heap_sector_allocated_bytes;
|
||||
}
|
||||
mem_stat_info_ptr->heap_alloc_total_bytes += size;
|
||||
break;
|
||||
case DEV_HEAP_ALLOC_FAIL:
|
||||
mem_stat_info_ptr->heap_alloc_fail_cnt++;
|
||||
break;
|
||||
case DEV_HEAP_FREE:
|
||||
mem_stat_info_ptr->heap_sector_alloc_cnt--;
|
||||
mem_stat_info_ptr->heap_sector_allocated_bytes -= size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ns_mem_word_size_t convert_allocation_size(ns_mem_book_t *book, ns_mem_block_size_t requested_bytes)
|
||||
{
|
||||
if (book->heap_main[0] == 0) {
|
||||
heap_failure(book, NS_DYN_MEM_HEAP_SECTOR_UNITIALIZED);
|
||||
} else if (requested_bytes < 1) {
|
||||
heap_failure(book, NS_DYN_MEM_ALLOCATE_SIZE_NOT_VALID);
|
||||
} else if (requested_bytes > (book->heap_size - 2 * sizeof(ns_mem_word_size_t))) {
|
||||
heap_failure(book, NS_DYN_MEM_ALLOCATE_SIZE_NOT_VALID);
|
||||
}
|
||||
return (requested_bytes + sizeof(ns_mem_word_size_t) - 1) / sizeof(ns_mem_word_size_t);
|
||||
}
|
||||
|
||||
// Checks that block length indicators are valid
|
||||
// Block has format: Size of data area [1 word] | data area [abs(size) words]| Size of data area [1 word]
|
||||
// If Size is negative it means area is unallocated
|
||||
static int8_t ns_mem_block_validate(ns_mem_word_size_t *block_start)
|
||||
{
|
||||
int8_t ret_val = -1;
|
||||
ns_mem_word_size_t *end = block_start;
|
||||
ns_mem_word_size_t size_start = *end;
|
||||
end += (1 + abs(size_start));
|
||||
if (size_start != 0 && size_start == *end) {
|
||||
ret_val = 0;
|
||||
}
|
||||
return ret_val;
|
||||
}
|
||||
#endif
|
||||
|
||||
// For direction, use 1 for direction up and -1 for down
|
||||
static void *ns_mem_internal_alloc(ns_mem_book_t *book, const ns_mem_block_size_t alloc_size, int direction)
|
||||
{
|
||||
#ifndef STANDARD_MALLOC
|
||||
if (!book) {
|
||||
/* We can not do anything except return NULL because we can't find book
|
||||
keeping block */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (book->mem_stat_info_ptr && direction == 1) {
|
||||
if (book->mem_stat_info_ptr->heap_sector_allocated_bytes > book->temporary_alloc_heap_limit) {
|
||||
/* Not enough heap for temporary memory allocation */
|
||||
dev_stat_update(book->mem_stat_info_ptr, DEV_HEAP_ALLOC_FAIL, 0);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ns_mem_word_size_t *block_ptr = NULL;
|
||||
|
||||
platform_enter_critical();
|
||||
|
||||
ns_mem_word_size_t data_size = convert_allocation_size(book, alloc_size);
|
||||
if (!data_size) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
// ns_list_foreach, either forwards or backwards, result to ptr
|
||||
for (hole_t *cur_hole = direction > 0 ? ns_list_get_first(&book->holes_list)
|
||||
: ns_list_get_last(&book->holes_list);
|
||||
cur_hole;
|
||||
cur_hole = direction > 0 ? ns_list_get_next(&book->holes_list, cur_hole)
|
||||
: ns_list_get_previous(&book->holes_list, cur_hole)
|
||||
) {
|
||||
ns_mem_word_size_t *p = block_start_from_hole(cur_hole);
|
||||
if (ns_mem_block_validate(p) != 0 || *p >= 0) {
|
||||
//Validation failed, or this supposed hole has positive (allocated) size
|
||||
heap_failure(book, NS_DYN_MEM_HEAP_SECTOR_CORRUPTED);
|
||||
break;
|
||||
}
|
||||
if (-*p >= data_size) {
|
||||
// Found a big enough block
|
||||
block_ptr = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!block_ptr) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
// Separate declaration from initialization to keep IAR happy as the gotos skip this block.
|
||||
ns_mem_word_size_t block_data_size;
|
||||
block_data_size = -*block_ptr;
|
||||
if (block_data_size >= (data_size + 2 + HOLE_T_SIZE)) {
|
||||
ns_mem_word_size_t hole_size = block_data_size - data_size - 2;
|
||||
ns_mem_word_size_t *hole_ptr;
|
||||
//There is enough room for a new hole so create it first
|
||||
if (direction > 0) {
|
||||
hole_ptr = block_ptr + 1 + data_size + 1;
|
||||
// Hole will be left at end of area.
|
||||
// Would like to just replace this block_ptr with new descriptor, but
|
||||
// they could overlap, so ns_list_replace might fail
|
||||
//ns_list_replace(&holes_list, block_ptr, hole_from_block_start(hole_ptr));
|
||||
hole_t *before = ns_list_get_previous(&book->holes_list, hole_from_block_start(block_ptr));
|
||||
ns_list_remove(&book->holes_list, hole_from_block_start(block_ptr));
|
||||
if (before) {
|
||||
ns_list_add_after(&book->holes_list, before, hole_from_block_start(hole_ptr));
|
||||
} else {
|
||||
ns_list_add_to_start(&book->holes_list, hole_from_block_start(hole_ptr));
|
||||
}
|
||||
} else {
|
||||
hole_ptr = block_ptr;
|
||||
// Hole remains at start of area - keep existing descriptor in place.
|
||||
block_ptr += 1 + hole_size + 1;
|
||||
}
|
||||
|
||||
hole_ptr[0] = -hole_size;
|
||||
hole_ptr[1 + hole_size] = -hole_size;
|
||||
} else {
|
||||
// Not enough room for a left-over hole, so use the whole block
|
||||
data_size = block_data_size;
|
||||
ns_list_remove(&book->holes_list, hole_from_block_start(block_ptr));
|
||||
}
|
||||
block_ptr[0] = data_size;
|
||||
block_ptr[1 + data_size] = data_size;
|
||||
|
||||
done:
|
||||
if (book->mem_stat_info_ptr) {
|
||||
if (block_ptr) {
|
||||
//Update Allocate OK
|
||||
dev_stat_update(book->mem_stat_info_ptr, DEV_HEAP_ALLOC_OK, (data_size + 2) * sizeof(ns_mem_word_size_t));
|
||||
} else {
|
||||
//Update Allocate Fail, second parameter is used for stats
|
||||
dev_stat_update(book->mem_stat_info_ptr, DEV_HEAP_ALLOC_FAIL, 0);
|
||||
}
|
||||
}
|
||||
platform_exit_critical();
|
||||
|
||||
return block_ptr ? block_ptr + 1 : NULL;
|
||||
#else
|
||||
void *retval = NULL;
|
||||
if (alloc_size) {
|
||||
platform_enter_critical();
|
||||
retval = malloc(alloc_size);
|
||||
platform_exit_critical();
|
||||
}
|
||||
return retval;
|
||||
#endif
|
||||
}
|
||||
|
||||
void *ns_mem_alloc(ns_mem_book_t *heap, ns_mem_block_size_t alloc_size)
|
||||
{
|
||||
return ns_mem_internal_alloc(heap, alloc_size, -1);
|
||||
}
|
||||
|
||||
void *ns_mem_temporary_alloc(ns_mem_book_t *heap, ns_mem_block_size_t alloc_size)
|
||||
{
|
||||
return ns_mem_internal_alloc(heap, alloc_size, 1);
|
||||
}
|
||||
|
||||
void *ns_dyn_mem_alloc(ns_mem_block_size_t alloc_size)
|
||||
{
|
||||
return ns_mem_alloc(default_book, alloc_size);
|
||||
}
|
||||
|
||||
void *ns_dyn_mem_temporary_alloc(ns_mem_block_size_t alloc_size)
|
||||
{
|
||||
return ns_mem_temporary_alloc(default_book, alloc_size);
|
||||
}
|
||||
|
||||
#ifndef STANDARD_MALLOC
|
||||
static void ns_mem_free_and_merge_with_adjacent_blocks(ns_mem_book_t *book, ns_mem_word_size_t *cur_block, ns_mem_word_size_t data_size)
|
||||
{
|
||||
// Theory of operation: Block is always in form | Len | Data | Len |
|
||||
// So we need to check length of previous (if current not heap start)
|
||||
// and next (if current not heap end) blocks. Negative length means
|
||||
// free memory so we can merge freed block with those.
|
||||
|
||||
hole_t *existing_start = NULL;
|
||||
hole_t *existing_end = NULL;
|
||||
ns_mem_word_size_t *start = cur_block;
|
||||
ns_mem_word_size_t *end = cur_block + data_size + 1;
|
||||
ns_mem_word_size_t *region_start;
|
||||
ns_mem_word_size_t *region_end;
|
||||
|
||||
int region_index = ns_dyn_mem_region_find(book, cur_block, data_size);
|
||||
if (region_index >= 0) {
|
||||
region_start = book->heap_main[region_index];
|
||||
region_end = book->heap_main_end[region_index];
|
||||
} else {
|
||||
heap_failure(book, NS_DYN_MEM_HEAP_SECTOR_CORRUPTED);
|
||||
// can't find region for the block, return
|
||||
return;
|
||||
}
|
||||
|
||||
//invalidate current block
|
||||
*start = -data_size;
|
||||
*end = -data_size;
|
||||
ns_mem_word_size_t merged_data_size = data_size;
|
||||
|
||||
if (start != region_start) {
|
||||
if (*(start - 1) < 0) {
|
||||
ns_mem_word_size_t *block_end = start - 1;
|
||||
ns_mem_word_size_t block_size = 1 + (-*block_end) + 1;
|
||||
merged_data_size += block_size;
|
||||
start -= block_size;
|
||||
if (*start != *block_end) {
|
||||
heap_failure(book, NS_DYN_MEM_HEAP_SECTOR_CORRUPTED);
|
||||
}
|
||||
if (block_size >= 1 + HOLE_T_SIZE + 1) {
|
||||
existing_start = hole_from_block_start(start);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (end != region_end) {
|
||||
if (*(end + 1) < 0) {
|
||||
ns_mem_word_size_t *block_start = end + 1;
|
||||
ns_mem_word_size_t block_size = 1 + (-*block_start) + 1;
|
||||
merged_data_size += block_size;
|
||||
end += block_size;
|
||||
if (*end != *block_start) {
|
||||
heap_failure(book, NS_DYN_MEM_HEAP_SECTOR_CORRUPTED);
|
||||
}
|
||||
if (block_size >= 1 + HOLE_T_SIZE + 1) {
|
||||
existing_end = hole_from_block_start(block_start);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hole_t *to_add = hole_from_block_start(start);
|
||||
hole_t *before = NULL;
|
||||
if (existing_end) {
|
||||
// Extending hole described by "existing_end" downwards.
|
||||
// Will replace with descriptor at bottom of merged block.
|
||||
// (Can't use ns_list_replace, because of danger of overlap)
|
||||
// Optimisation - note our position for insertion below.
|
||||
before = ns_list_get_next(&book->holes_list, existing_end);
|
||||
ns_list_remove(&book->holes_list, existing_end);
|
||||
}
|
||||
if (existing_start) {
|
||||
// Extending hole described by "existing_start" upwards.
|
||||
// No need to modify that descriptor - it remains at the bottom
|
||||
// of the merged block to describe it.
|
||||
} else {
|
||||
// Didn't find adjacent descriptors, but may still
|
||||
// be merging with small blocks without descriptors.
|
||||
if (merged_data_size >= HOLE_T_SIZE) {
|
||||
// Locate hole position in list, if we don't already know
|
||||
// from merging with the block above.
|
||||
if (!existing_end) {
|
||||
ns_list_foreach(hole_t, ptr, &book->holes_list) {
|
||||
if (ptr > to_add) {
|
||||
before = ptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (before) {
|
||||
ns_list_add_before(&book->holes_list, before, to_add);
|
||||
} else {
|
||||
ns_list_add_to_end(&book->holes_list, to_add);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
*start = -merged_data_size;
|
||||
*end = -merged_data_size;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool pointer_address_validate(ns_mem_book_t *book, ns_mem_word_size_t *ptr, ns_mem_word_size_t size)
|
||||
{
|
||||
|
||||
if (ns_dyn_mem_region_find(book, ptr, size) >= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ns_mem_free(ns_mem_book_t *book, void *block)
|
||||
{
|
||||
#ifndef STANDARD_MALLOC
|
||||
|
||||
if (!block) {
|
||||
return;
|
||||
}
|
||||
|
||||
ns_mem_word_size_t *ptr = block;
|
||||
ns_mem_word_size_t size;
|
||||
|
||||
platform_enter_critical();
|
||||
ptr --;
|
||||
//Read Current Size
|
||||
size = *ptr;
|
||||
if (!pointer_address_validate(book, ptr, size)) {
|
||||
heap_failure(book, NS_DYN_MEM_POINTER_NOT_VALID);
|
||||
} else if (size < 0) {
|
||||
heap_failure(book, NS_DYN_MEM_DOUBLE_FREE);
|
||||
} else {
|
||||
if (ns_mem_block_validate(ptr) != 0) {
|
||||
heap_failure(book, NS_DYN_MEM_HEAP_SECTOR_CORRUPTED);
|
||||
} else {
|
||||
ns_mem_free_and_merge_with_adjacent_blocks(book, ptr, size);
|
||||
if (book->mem_stat_info_ptr) {
|
||||
//Update Free Counter
|
||||
dev_stat_update(book->mem_stat_info_ptr, DEV_HEAP_FREE, (size + 2) * sizeof(ns_mem_word_size_t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
platform_exit_critical();
|
||||
#else
|
||||
platform_enter_critical();
|
||||
free(block);
|
||||
platform_exit_critical();
|
||||
#endif
|
||||
}
|
||||
|
||||
void ns_dyn_mem_free(void *block)
|
||||
{
|
||||
ns_mem_free(default_book, block);
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright 2016-2017 ARM Ltd.
|
||||
//
|
||||
// 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 <string.h>
|
||||
#include <ns_types.h>
|
||||
#include <nsdynmemLIB.h>
|
||||
#include "ns_list.h"
|
||||
#include "platform/arm_hal_nvm.h"
|
||||
#include "ns_nvm_helper.h"
|
||||
|
||||
#define TRACE_GROUP "nnvm"
|
||||
|
||||
/* NVM operations */
|
||||
#define NS_NVM_NONE 0x00
|
||||
#define NS_NVM_INIT 0x01
|
||||
#define NS_NVM_KEY_CREATE 0x02
|
||||
#define NS_NVM_KEY_READ 0x03
|
||||
#define NS_NVM_KEY_WRITE 0x04
|
||||
#define NS_NVM_FLUSH 0x05
|
||||
#define NS_NVM_KEY_DELETE 0x06
|
||||
|
||||
typedef struct {
|
||||
ns_nvm_callback *callback;
|
||||
const char *client_key_name;
|
||||
void *client_context;
|
||||
int operation;
|
||||
uint8_t *buffer;
|
||||
uint16_t *buffer_len;
|
||||
void *original_request;
|
||||
ns_list_link_t link;
|
||||
} ns_nvm_request_t;
|
||||
|
||||
static bool ns_nvm_initialized = false;
|
||||
static bool ns_nvm_operation_in_progress = false;
|
||||
|
||||
static ns_nvm_request_t *ns_nvm_create_request(ns_nvm_callback *callback, void *context, const char *key_name, uint8_t *buf, uint16_t *buf_len, uint8_t operation);
|
||||
static int ns_nvm_operation_start(ns_nvm_request_t *request);
|
||||
static int ns_nvm_operation_continue(ns_nvm_request_t *request, bool free_request);
|
||||
static void ns_nvm_operation_end(ns_nvm_request_t *ns_nvm_request_ptr, int client_retval);
|
||||
|
||||
static NS_LIST_DEFINE(ns_nvm_request_list, ns_nvm_request_t, link);
|
||||
|
||||
/*
|
||||
* Callback from platform NVM adaptation
|
||||
*/
|
||||
void ns_nvm_callback_func(platform_nvm_status status, void *args)
|
||||
{
|
||||
ns_nvm_request_t *ns_nvm_request_ptr = (ns_nvm_request_t *)args;
|
||||
int client_retval = NS_NVM_OK;
|
||||
|
||||
if (status == PLATFORM_NVM_ERROR) {
|
||||
client_retval = NS_NVM_ERROR;
|
||||
} else if (status == PLATFORM_NVM_KEY_NOT_FOUND) {
|
||||
client_retval = NS_NVM_DATA_NOT_FOUND;
|
||||
}
|
||||
|
||||
switch (ns_nvm_request_ptr->operation) {
|
||||
case NS_NVM_INIT:
|
||||
ns_nvm_operation_continue(ns_nvm_request_ptr->original_request, true);
|
||||
ns_dyn_mem_free(ns_nvm_request_ptr);
|
||||
break;
|
||||
case NS_NVM_FLUSH:
|
||||
case NS_NVM_KEY_READ:
|
||||
ns_nvm_operation_end(ns_nvm_request_ptr, client_retval);
|
||||
break;
|
||||
case NS_NVM_KEY_CREATE:
|
||||
if (status == PLATFORM_NVM_OK) {
|
||||
ns_nvm_request_ptr->operation = NS_NVM_KEY_WRITE;
|
||||
platform_nvm_write(ns_nvm_callback_func, ns_nvm_request_ptr->client_key_name, ns_nvm_request_ptr->buffer, ns_nvm_request_ptr->buffer_len, ns_nvm_request_ptr);
|
||||
} else {
|
||||
ns_nvm_operation_end(ns_nvm_request_ptr, client_retval);
|
||||
}
|
||||
break;
|
||||
case NS_NVM_KEY_DELETE:
|
||||
case NS_NVM_KEY_WRITE:
|
||||
if (status == PLATFORM_NVM_OK) {
|
||||
// write ok, flush the changes
|
||||
ns_nvm_request_ptr->operation = NS_NVM_FLUSH;
|
||||
platform_nvm_flush(ns_nvm_callback_func, ns_nvm_request_ptr);
|
||||
} else {
|
||||
// write failed, inform client
|
||||
ns_nvm_operation_end(ns_nvm_request_ptr, client_retval);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int ns_nvm_key_delete(ns_nvm_callback *callback, const char *key_name, void *context)
|
||||
{
|
||||
if (!callback || !key_name) {
|
||||
return NS_NVM_ERROR;
|
||||
}
|
||||
ns_nvm_request_t *ns_nvm_request_ptr = ns_nvm_create_request(callback, context, key_name, NULL, NULL, NS_NVM_KEY_DELETE);
|
||||
return ns_nvm_operation_start(ns_nvm_request_ptr);
|
||||
}
|
||||
|
||||
int ns_nvm_data_read(ns_nvm_callback *callback, const char *key_name, uint8_t *buf, uint16_t *buf_len, void *context)
|
||||
{
|
||||
if (!callback || !key_name || !buf || !buf_len) {
|
||||
return NS_NVM_ERROR;
|
||||
}
|
||||
ns_nvm_request_t *ns_nvm_request_ptr = ns_nvm_create_request(callback, context, key_name, buf, buf_len, NS_NVM_KEY_READ);
|
||||
return ns_nvm_operation_start(ns_nvm_request_ptr);
|
||||
}
|
||||
|
||||
int ns_nvm_data_write(ns_nvm_callback *callback, const char *key_name, uint8_t *buf, uint16_t *buf_len, void *context)
|
||||
{
|
||||
if (!callback || !key_name || !buf || !buf_len) {
|
||||
return NS_NVM_ERROR;
|
||||
}
|
||||
ns_nvm_request_t *ns_nvm_request_ptr = ns_nvm_create_request(callback, context, key_name, buf, buf_len, NS_NVM_KEY_WRITE);
|
||||
return ns_nvm_operation_start(ns_nvm_request_ptr);
|
||||
}
|
||||
|
||||
static int ns_nvm_operation_start(ns_nvm_request_t *nvm_request)
|
||||
{
|
||||
int ret = NS_NVM_OK;
|
||||
platform_nvm_status pnvm_status;
|
||||
|
||||
if (!nvm_request) {
|
||||
return NS_NVM_MEMORY;
|
||||
}
|
||||
if (ns_nvm_initialized == true) {
|
||||
// NVM already initialized, continue directly
|
||||
if (!ns_nvm_operation_in_progress) {
|
||||
ret = ns_nvm_operation_continue(nvm_request, true);
|
||||
} else {
|
||||
// add request to list and handle when existing calls has been handled.
|
||||
ns_list_add_to_end(&ns_nvm_request_list, nvm_request);
|
||||
}
|
||||
} else {
|
||||
ns_nvm_request_t *ns_nvm_request_ptr = ns_nvm_create_request(NULL, NULL, NULL, NULL, NULL, NS_NVM_INIT);
|
||||
if (!ns_nvm_request_ptr) {
|
||||
ns_dyn_mem_free(nvm_request);
|
||||
ns_dyn_mem_free(ns_nvm_request_ptr);
|
||||
return NS_NVM_MEMORY;
|
||||
}
|
||||
ns_nvm_request_ptr->original_request = nvm_request;
|
||||
pnvm_status = platform_nvm_init(ns_nvm_callback_func, ns_nvm_request_ptr);
|
||||
if (pnvm_status != PLATFORM_NVM_OK) {
|
||||
ns_dyn_mem_free(nvm_request);
|
||||
ns_dyn_mem_free(ns_nvm_request_ptr);
|
||||
return NS_NVM_ERROR;
|
||||
}
|
||||
ns_list_init(&ns_nvm_request_list);
|
||||
ns_nvm_initialized = true;
|
||||
ns_nvm_operation_in_progress = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ns_nvm_request_t *ns_nvm_create_request(ns_nvm_callback *callback, void *context, const char *key_name, uint8_t *buf, uint16_t *buf_len, uint8_t operation)
|
||||
{
|
||||
ns_nvm_request_t *ns_nvm_request_ptr = ns_dyn_mem_temporary_alloc(sizeof(ns_nvm_request_t));
|
||||
if (!ns_nvm_request_ptr) {
|
||||
return NULL;
|
||||
}
|
||||
ns_nvm_request_ptr->client_context = context;
|
||||
ns_nvm_request_ptr->callback = callback;
|
||||
ns_nvm_request_ptr->client_key_name = key_name;
|
||||
ns_nvm_request_ptr->operation = operation;
|
||||
ns_nvm_request_ptr->buffer = buf;
|
||||
ns_nvm_request_ptr->buffer_len = buf_len;
|
||||
|
||||
return ns_nvm_request_ptr;
|
||||
}
|
||||
|
||||
static int ns_nvm_operation_continue(ns_nvm_request_t *request, bool free_request)
|
||||
{
|
||||
platform_nvm_status ret = PLATFORM_NVM_OK;
|
||||
|
||||
ns_nvm_operation_in_progress = true;
|
||||
switch (request->operation) {
|
||||
case NS_NVM_KEY_WRITE:
|
||||
request->operation = NS_NVM_KEY_CREATE;
|
||||
ret = platform_nvm_key_create(ns_nvm_callback_func, request->client_key_name, *request->buffer_len, 0, request);
|
||||
break;
|
||||
case NS_NVM_KEY_READ:
|
||||
ret = platform_nvm_read(ns_nvm_callback_func, request->client_key_name, request->buffer, request->buffer_len, request);
|
||||
break;
|
||||
case NS_NVM_KEY_DELETE:
|
||||
ret = platform_nvm_key_delete(ns_nvm_callback_func, request->client_key_name, request);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret != PLATFORM_NVM_OK) {
|
||||
if (free_request == true) {
|
||||
// free request if requested
|
||||
ns_dyn_mem_free(request);
|
||||
}
|
||||
ns_nvm_operation_in_progress = false;
|
||||
return NS_NVM_ERROR;
|
||||
}
|
||||
|
||||
return NS_NVM_OK;
|
||||
}
|
||||
|
||||
static void ns_nvm_operation_end(ns_nvm_request_t *ns_nvm_request_ptr, int client_retval)
|
||||
{
|
||||
ns_nvm_request_ptr->callback(client_retval, ns_nvm_request_ptr->client_context);
|
||||
ns_dyn_mem_free(ns_nvm_request_ptr);
|
||||
ns_nvm_operation_in_progress = false;
|
||||
|
||||
ns_list_foreach_safe(ns_nvm_request_t, pending_req, &ns_nvm_request_list) {
|
||||
// there are pending requests to be processed
|
||||
ns_list_remove(&ns_nvm_request_list, pending_req);
|
||||
int ret = ns_nvm_operation_continue(pending_req, false);
|
||||
if (ret != NS_NVM_OK) {
|
||||
ns_nvm_operation_end(pending_req, ret);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user