/* * Copyright 2024 Google LLC * * 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 "util/legacy_checksum.h" #include #include // Software implementation of the legacy checksum. This emulates the behaviour // of the CRC peripheral in the STM32F2/F4 series MCUs and the bugs in the // legacy CRC driver implementation. While the raw throughput of the hardware // CRC peripheral is greater, it is not reentrant and so a mutex is required // to prevent concurrent access to the peripheral by multiple tasks. The // overhead of locking and unlocking the mutex, while not yet benchmarked, is // potentially significant enough to cancel out the increased throughput of // the hardware acceleration. There is also no evidence (for or against) that // checksums are a performance bottleneck in the first place. Given the added // complexity of the hardware-accelerated implementation, and the dubious // performance improvement of using it, hardware acceleration of checksums is a // case of premature optimization. // // That being said, the API for the legacy checksum is fully capable of // supporting a non-reentrant implementation. Simply acquire the mutex in // legacy_defective_checksum_init and release it in // legacy_defective_checksum_finish. // The implementation is based on a nybble-wide table driven CRC. // The CRC implementation (but not the checksum based on it) has the // model parameters: // Width: 32 // Poly: 04C11DB7 // Init: FFFFFFFF // RefIn: False // RefOut: False // XorOut: 00000000 // // The CRC lookup table was generated by legacy_checksum_crc_table.py static const uint32_t s_lookup_table[] = { 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, }; static uint32_t prv_crc_byte(uint32_t crc, uint8_t input) { crc = (crc << 4) ^ s_lookup_table[((crc >> 28) ^ (input >> 4)) & 0x0f]; crc = (crc << 4) ^ s_lookup_table[((crc >> 28) ^ (input >> 0)) & 0x0f]; return crc; } void legacy_defective_checksum_init(LegacyChecksum *checksum) { *checksum = (LegacyChecksum) { .reg = 0xffffffff, }; } void legacy_defective_checksum_update( LegacyChecksum * restrict checksum, const void * restrict data, size_t length) { const char * restrict data_bytes = data; uint32_t * restrict reg = &checksum->reg; uint8_t * restrict accumulator = checksum->accumulator; uint8_t * restrict accumulated_length = &checksum->accumulated_length; if (*accumulated_length) { for (; *accumulated_length < 3 && length; length--) { accumulator[(*accumulated_length)++] = *data_bytes++; } if (*accumulated_length == 3 && length) { *reg = prv_crc_byte(*reg, *data_bytes++); length--; *reg = prv_crc_byte(*reg, accumulator[2]); *reg = prv_crc_byte(*reg, accumulator[1]); *reg = prv_crc_byte(*reg, accumulator[0]); *accumulated_length = 0; } } for (; length >= 4; length -= 4) { *reg = prv_crc_byte(*reg, data_bytes[3]); *reg = prv_crc_byte(*reg, data_bytes[2]); *reg = prv_crc_byte(*reg, data_bytes[1]); *reg = prv_crc_byte(*reg, data_bytes[0]); data_bytes += 4; } for (; length; length--) { accumulator[(*accumulated_length)++] = *data_bytes++; } } uint32_t legacy_defective_checksum_finish(LegacyChecksum *checksum) { if (checksum->accumulated_length) { // CRC the final bytes forwards (reversed relative to the normal checksum) // padded on the left(!) with null bytes. for (int padding = 4 - checksum->accumulated_length; padding; padding--) { checksum->reg = prv_crc_byte(checksum->reg, 0); } for (int i = 0; i < checksum->accumulated_length; ++i) { checksum->reg = prv_crc_byte(checksum->reg, checksum->accumulator[i]); } } return checksum->reg; } uint32_t legacy_defective_checksum_memory(const void * restrict data, size_t length) { LegacyChecksum checksum; legacy_defective_checksum_init(&checksum); legacy_defective_checksum_update(&checksum, data, length); return legacy_defective_checksum_finish(&checksum); }