Merge branch 'protect-datagram-fields' into 'stable-1.6'

Re-ordering protection for datagram fields

See merge request etherlab.org/ethercat!203
This commit is contained in:
Florian Pose
2026-07-16 14:07:09 +02:00
7 changed files with 97 additions and 55 deletions
+2
View File
@@ -1,5 +1,7 @@
# Version History
- Protect datagram receiving mechanism against re-ordering.
## Version 1.6.10
- Added RasPi 5 macb (Cadence GEM / RP1) driver for kernel 6.18.
+1
View File
@@ -67,6 +67,7 @@ noinst_HEADERS = \
sdo_request.c sdo_request.h \
slave.c slave.h \
slave_config.c slave_config.h \
smp.h \
soe_errors.c \
soe_request.c soe_request.h \
sync.c sync.h \
+15 -18
View File
@@ -1,6 +1,6 @@
/*****************************************************************************
*
* Copyright (C) 2006-2023 Florian Pose, Ingenieurgemeinschaft IgH
* Copyright (C) 2006-2026 Florian Pose, Ingenieurgemeinschaft IgH
*
* This file is part of the IgH EtherCAT Master.
*
@@ -32,6 +32,7 @@
#ifdef EC_EOE
#include "ethernet.h"
#endif
#include "smp.h"
#include "fsm_master.h"
#include "fsm_foe.h"
@@ -174,8 +175,9 @@ int ec_fsm_master_exec(
ec_fsm_master_t *fsm /**< Master state machine. */
)
{
if (fsm->datagram->state == EC_DATAGRAM_SENT
|| fsm->datagram->state == EC_DATAGRAM_QUEUED) {
ec_datagram_state_t state = smp_load_acquire(&fsm->datagram->state);
if (state == EC_DATAGRAM_SENT || state == EC_DATAGRAM_QUEUED) {
// datagram was not sent or received yet.
return 0;
}
@@ -315,8 +317,8 @@ void ec_fsm_master_state_broadcast(
for (dev_idx = EC_DEVICE_MAIN;
dev_idx < ec_master_num_devices(master); dev_idx++) {
fsm->slave_states[dev_idx] = 0x00;
fsm->slaves_responding[dev_idx] = 0; /* Reset to trigger rescan on
next link up. */
/* Reset to trigger rescan on next link up. */
fsm->slaves_responding[dev_idx] = 0;
}
}
fsm->link_state[fsm->dev_idx] = master->devices[fsm->dev_idx].link_state;
@@ -429,7 +431,6 @@ void ec_fsm_master_state_broadcast(
}
if (master->slave_count) {
// application applied configurations
if (master->config_changed) {
master->config_changed = 0;
@@ -438,8 +439,8 @@ void ec_fsm_master_state_broadcast(
fsm->slave = master->slaves; // begin with first slave
ec_fsm_master_enter_write_system_times(fsm);
} else {
}
else {
// fetch state from first slave
fsm->slave = master->slaves;
ec_datagram_fprd(fsm->datagram, fsm->slave->station_address,
@@ -535,14 +536,12 @@ int ec_fsm_master_action_process_int_request(
for (slave = master->slaves;
slave < master->slaves + master->slave_count;
slave++) {
if (!slave->config) {
continue;
}
list_for_each_entry(sdo_req, &slave->config->sdo_requests, list) {
if (sdo_req->state == EC_INT_REQUEST_QUEUED) {
if (ec_sdo_request_timed_out(sdo_req)) {
sdo_req->state = EC_INT_REQUEST_FAILURE;
EC_SLAVE_DBG(slave, 1, "Internal SDO request"
@@ -570,7 +569,6 @@ int ec_fsm_master_action_process_int_request(
list_for_each_entry(soe_req, &slave->config->soe_requests, list) {
if (soe_req->state == EC_INT_REQUEST_QUEUED) {
if (ec_soe_request_timed_out(soe_req)) {
soe_req->state = EC_INT_REQUEST_FAILURE;
EC_SLAVE_DBG(slave, 1, "Internal SoE request"
@@ -634,8 +632,9 @@ void ec_fsm_master_action_idle(
|| slave->sdo_dictionary_fetched
|| slave->current_state == EC_SLAVE_STATE_INIT
|| slave->current_state == EC_SLAVE_STATE_UNKNOWN
|| jiffies - slave->jiffies_preop < EC_WAIT_SDO_DICT * HZ
) continue;
|| jiffies - slave->jiffies_preop < EC_WAIT_SDO_DICT * HZ) {
continue;
}
EC_SLAVE_DBG(slave, 1, "Fetching SDO dictionary.\n");
@@ -715,7 +714,6 @@ void ec_fsm_master_action_configure(
// Does the slave have to be configured?
if ((slave->current_state != slave->requested_state
|| slave->force_config) && !slave->error_flag) {
// Start slave configuration
down(&master->config_sem);
master->config_busy = 1;
@@ -1033,7 +1031,7 @@ void ec_fsm_master_state_configure_slave(
wake_up_interruptible(&master->config_queue);
if (!ec_fsm_slave_config_success(&fsm->fsm_slave_config)) {
// TODO: mark slave_config as failed.
// TODO(fp): mark slave_config as failed.
}
fsm->idle = 1;
@@ -1051,7 +1049,6 @@ void ec_fsm_master_enter_write_system_times(
ec_master_t *master = fsm->master;
if (master->dc_ref_time) {
while (fsm->slave < master->slaves + master->slave_count) {
if (!fsm->slave->base_dc_supported
|| !fsm->slave->has_dc_system_time) {
@@ -1334,10 +1331,10 @@ void ec_fsm_master_state_write_sii(
if (request->offset <= 4 && request->offset + request->nwords > 4) {
// alias was written
slave->sii.alias = EC_READ_U16(request->words + 4);
// TODO: read alias from register 0x0012
// TODO(fp): read alias from register 0x0012
slave->effective_alias = slave->sii.alias;
}
// TODO: Evaluate other SII contents!
// TODO(fp): Evaluate other SII contents!
request->state = EC_INT_REQUEST_SUCCESS;
wake_up_all(&master->request_queue);
+3 -4
View File
@@ -121,10 +121,10 @@ void ec_fsm_slave_config_reconfigure(ec_fsm_slave_config_t *);
void ec_fsm_slave_config_init(
ec_fsm_slave_config_t *fsm, /**< slave state machine */
ec_datagram_t *datagram, /**< datagram structure to use */
ec_fsm_change_t *fsm_change, /**< State change state machine to use. */
ec_fsm_change_t *fsm_change, /**< State machine to use. */
ec_fsm_coe_t *fsm_coe, /**< CoE state machine to use. */
ec_fsm_soe_t *fsm_soe, /**< SoE state machine to use. */
ec_fsm_pdo_t *fsm_pdo, /**< PDO configuration state machine to use. */
ec_fsm_pdo_t *fsm_pdo, /**< PDO config. state machine to use. */
ec_fsm_eoe_t *fsm_eoe /**< EoE state machine to use. */
)
{
@@ -1030,7 +1030,7 @@ void ec_fsm_slave_config_state_pdo_conf(
ec_fsm_slave_config_t *fsm /**< slave state machine */
)
{
// TODO check for config here
// TODO(fp) check for config here
if (ec_fsm_pdo_exec(fsm->fsm_pdo, fsm->datagram)) {
return;
@@ -1476,7 +1476,6 @@ void ec_fsm_slave_config_state_dc_sync_check(
diff_ms = (datagram->jiffies_received - fsm->jiffies_start) * 1000 / HZ;
if (abs_sync_diff > EC_DC_MAX_SYNC_DIFF_NS) {
if (diff_ms >= EC_DC_SYNC_WAIT_MS) {
EC_SLAVE_WARN(slave, "Slave did not sync after %lu ms.\n",
diff_ms);
+4 -5
View File
@@ -24,8 +24,8 @@
/****************************************************************************/
#ifndef __EC_MASTER_GLOBALS_H__
#define __EC_MASTER_GLOBALS_H__
#ifndef MASTER_GLOBALS_H_
#define MASTER_GLOBALS_H_
#include "../globals.h"
#include "../include/ecrt.h"
@@ -171,8 +171,7 @@ typedef struct {
*/
typedef enum {
EC_DC_32, /**< 32 bit. */
EC_DC_64 /*< 64 bit for system time, system time offset and
port 0 receive time. */
EC_DC_64 /*< 64 bit for system time, time offset and port receive time. */
} ec_slave_dc_range_t;
/** EtherCAT slave sync signal configuration.
@@ -311,4 +310,4 @@ typedef struct ec_slave ec_slave_t; /**< \see ec_slave. */
/****************************************************************************/
#endif
#endif // MASTER_GLOBALS_H_
+16 -28
View File
@@ -1,6 +1,6 @@
/*****************************************************************************
*
* Copyright (C) 2006-2020 Florian Pose, Ingenieurgemeinschaft IgH
* Copyright (C) 2006-2026 Florian Pose, Ingenieurgemeinschaft IgH
*
* This file is part of the IgH EtherCAT Master.
*
@@ -43,6 +43,7 @@
#include "slave_config.h"
#include "device.h"
#include "datagram.h"
#include "smp.h"
#ifdef EC_EOE
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
@@ -62,23 +63,6 @@
rt_mutex_lock_interruptible(lock, 0)
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 47)
#define smp_store_release(p, v) \
do { \
smp_mb(); \
ACCESS_ONCE(*p) = (v); \
} while (0)
#define smp_load_acquire(p) \
({ \
typeof(*p) ___p1 = ACCESS_ONCE(*p); \
smp_mb(); \
___p1; \
})
#endif
#include "master.h"
/****************************************************************************/
@@ -863,7 +847,7 @@ void ec_master_inject_external_datagrams(
queue_size = new_queue_size;
}
else if (datagram->data_size > master->max_queue_size) {
datagram->state = EC_DATAGRAM_ERROR;
smp_store_release(&datagram->state, EC_DATAGRAM_ERROR);
EC_MASTER_ERR(master, "External datagram %s is too large,"
" size=%zu, max_queue_size=%zu\n",
datagram->name, datagram->data_size,
@@ -884,7 +868,7 @@ void ec_master_inject_external_datagrams(
unsigned int time_us;
#endif
datagram->state = EC_DATAGRAM_ERROR;
smp_store_release(&datagram->state, EC_DATAGRAM_ERROR);
#if defined EC_RT_SYSLOG || DEBUG_INJECT
#ifdef EC_HAVE_CYCLES
@@ -981,13 +965,13 @@ void ec_master_queue_datagram(
EC_MASTER_DBG(master, 1,
"Datagram %p already queued (skipping).\n", datagram);
#endif
datagram->state = EC_DATAGRAM_QUEUED;
smp_store_release(&datagram->state, EC_DATAGRAM_QUEUED);
return;
}
}
list_add_tail(&datagram->queue, &master->datagram_queue);
datagram->state = EC_DATAGRAM_QUEUED;
smp_store_release(&datagram->state, EC_DATAGRAM_QUEUED);
}
/****************************************************************************/
@@ -1116,12 +1100,12 @@ void ec_master_send_datagrams(
// set datagram states and sending timestamps
list_for_each_entry_safe(datagram, next, &sent_datagrams, sent) {
datagram->state = EC_DATAGRAM_SENT;
#ifdef EC_HAVE_CYCLES
datagram->cycles_sent = cycles_sent;
#endif
datagram->jiffies_sent = jiffies_sent;
list_del_init(&datagram->sent); // empty list of sent datagrams
list_del_init(&datagram->sent); // remove from sent queue
smp_store_release(&datagram->state, EC_DATAGRAM_SENT);
}
frame_count++;
@@ -1265,15 +1249,19 @@ void ec_master_receive_datagrams(
datagram->working_counter = EC_READ_U16(cur_data);
cur_data += EC_DATAGRAM_FOOTER_SIZE;
// dequeue the received datagram
datagram->state = EC_DATAGRAM_RECEIVED;
// set the receive time
#ifdef EC_HAVE_CYCLES
datagram->cycles_received =
master->devices[EC_DEVICE_MAIN].cycles_poll;
#endif
datagram->jiffies_received =
master->devices[EC_DEVICE_MAIN].jiffies_poll;
// dequeue the received datagram
list_del_init(&datagram->queue);
// set the state (with a barrier)
smp_store_release(&datagram->state, EC_DATAGRAM_RECEIVED);
}
}
@@ -2478,8 +2466,8 @@ int ecrt_master_send(ec_master_t *master)
list_for_each_entry_safe(datagram, n,
&master->datagram_queue, queue) {
if (datagram->device_index == dev_idx) {
datagram->state = EC_DATAGRAM_ERROR;
list_del_init(&datagram->queue);
smp_store_release(&datagram->state, EC_DATAGRAM_ERROR);
}
}
@@ -2527,7 +2515,7 @@ int ecrt_master_receive(ec_master_t *master)
datagram->jiffies_sent > timeout_jiffies) {
#endif
list_del_init(&datagram->queue);
datagram->state = EC_DATAGRAM_TIMED_OUT;
smp_store_release(&datagram->state, EC_DATAGRAM_TIMED_OUT);
master->stats.timeouts++;
#ifdef EC_RT_SYSLOG
+56
View File
@@ -0,0 +1,56 @@
/*****************************************************************************
*
* Copyright (C) 2006-2026 Florian Pose, Ingenieurgemeinschaft IgH
*
* This file is part of the IgH EtherCAT master.
*
* The file is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation; version 2.1 of the License.
*
* This file is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this file. If not, see <http://www.gnu.org/licenses/>.
*
****************************************************************************/
/** \file
* Definitions of Kernel SMP macros.
*/
/****************************************************************************/
#ifndef MASTER_SMP_H_
#define MASTER_SMP_H_
#include <linux/version.h>
/****************************************************************************/
/* Define SMP macros on kernel versions where they did not exist yet.
*/
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 47)
#define smp_store_release(p, v) \
do { \
smp_mb(); \
ACCESS_ONCE(*p) = (v); \
} while (0)
#define smp_load_acquire(p) \
({ \
typeof(*p) ___p1 = ACCESS_ONCE(*p); \
smp_mb(); \
___p1; \
})
#endif
/****************************************************************************/
#endif // MASTER_SMP_H_