Skip to content
Snippets Groups Projects
Select Git revision
  • 608509681afcc43fd92bff248d35177951c2097d
  • master default protected
  • dev protected
  • replication_test
  • release-1.10 protected
  • release-1.9 protected
  • 551-init-broker-service-permissions
  • 549-test-oai-pmh
  • 545-saving-multiple-times-breaks-pid-metadata
  • 499-standalone-compute-service-2
  • 539-load-tests
  • hotfix/helm-chart
  • luca_ba_new_interface
  • 534-bug-when-adding-access-to-user-that-is-not-registered-at-dashboard-service
  • release-1.8 protected
  • 533-integrate-semantic-recommendation
  • feature/openshift
  • 518-spark-doesn-t-map-the-headers-correct
  • 485-fixity-checks
  • 530-various-schema-problems-with-subsets
  • release-1.7 protected
  • v1.10.2 protected
  • v1.10.1 protected
  • v1.10.0-rc13 protected
  • v1.10.0-rc12 protected
  • v1.10.0-rc11 protected
  • v1.10.0-rc10 protected
  • v1.10.0-rc9 protected
  • v1.10.0-rc8 protected
  • v1.10.0-rc7 protected
  • v1.10.0-rc6 protected
  • v1.10.0-rc5 protected
  • v1.10.0-rc4 protected
  • v1.10.0-rc3 protected
  • v1.10.0-rc2 protected
  • v1.10.0rc1 protected
  • v1.10.0rc0 protected
  • v1.10.0 protected
  • v1.9.3 protected
  • v1.9.2 protected
  • v1.9.2-rc0 protected
41 results

auth-service.md

Blame
  • rdcu_rmap.c 16.83 KiB
    /**
     * @file   rdcu_rmap.c
     * @author Armin Luntzer (armin.luntzer@univie.ac.at),
     * @date   2018
     *
     * @copyright GPLv2
     * This program is free software; you can redistribute it and/or modify it
     * under the terms and conditions of the GNU General Public License,
     * version 2, as published by the Free Software Foundation.
     *
     * This program is distributed in the hope it will be useful, but WITHOUT
     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
     * more details.
     *
     * @brief RMAP RDCU link interface
     * @see FPGA Requirement Specification PLATO-IWF-PL-RS-005 Issue 0.7
     *
     *
     * Ideally, we would use asynchronous operations here, with a transaction log
     * that is served by another thread. However, we cannot assume that such
     * features will be available, so we'll do this by maintaining a mirror of the
     * RDCU's registers and memory, where instead of actively blocking with get()
     * and set() RMAP calls, they operate on the local copy and the user issues
     * sync() calls.
     *
     * To monitor the syncronisation status, we maintaining a transaction log
     * tracking the submitted command set. Response packets could be processed
     * by interrupt (or thread), but in this variant, we process the return packets
     * when the user calls rdcu_ctrl_sync_status()
     *
     * This is probably the nicest solution when it comes to call overhead, but it
     * requires 8 MiB of memory for the SRAM mirror and the some for the registers.
     *
     * Note that for simplicity , we assume that there is a working heap allocator
     * available, please adapt all malloc/free calls to your needs, or ask us
     * to do that for you.
     *
     * NOTE: in order to run this on the GR712RC eval board, we set the SRAM mirror
     *	 image to the boards SDRAM in rdcu_ctrl_init() and just malloc() it for
     *	 the PC (see rdcu_ctrl_init)
     *
     *	 The interface requires that you provide an RX and a TX function,
     *	 see rdcu_ctrl_init for the call interface.
     *	 The TX function shall to return 0 on success, everything else
     *	 is considered an error in submission. The RX function shall return
     *	 the size of the packet buffer and accept NULL as call argument, on
     *	 which it shall return the buffer size required to store the next
     *	 pending packet.
     *	 You can use these functions to adapt the actual backend, i.e. use
     *	 your particular SpW interface or just redirect RX/TX to files
     *	 or via a network connection.
     *
     * NOTE: We don't have to serve more than one RDCU at any given time, so we
     *	 track addresses and paths internally in a single instance. This also
     *	 makes the interface less cluttered. Besides, I'm lazy.
     *
     *
     * @warn when operational, we expect to have exclusive control of the SpW link
     *
     * TODO: RMAP command response evaluation
     */
    
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include <byteorder.h>
    #include <rmap.h>
    #include <rdcu_rmap.h>
    
    
    
    static uint8_t rdcu_addr;
    static uint8_t icu_addr;
    
    static uint8_t *dpath;	/* destination path (to the RDCU) */
    static uint8_t *rpath;	/* return path (to the ICU) */
    static uint8_t dpath_len;
    static uint8_t rpath_len;
    
    static uint8_t dst_key;	/* destination command key */
    
    
    
    
     /* generic calls, functions must be provided to init() */
    static int32_t (*rmap_tx)(const void *hdr,  uint32_t hdr_size,
    			  const uint8_t non_crc_bytes,
    			  const void *data, uint32_t data_size);
    static uint32_t (*rmap_rx)(uint8_t *pkt);
    
    static int data_mtu;	/* maximum data transfer size per unit */
    
    
    
    
    
    
    
    
    
    
    /* For now we maintain a simple transaction log that works like this:
     * we allow up to 128 transfers, simply because this is how many response
     * packets the GRSPW2 SpW descriptor table can hold at any one time without
     * blocking the link.
     *
     * Every time a new transfer is to be submitted, we grab the first free
     * slot we encounter in the "in_use" array and use the index as the transaction
     * identifier of the RMAP packet. (Yes, this is potentially slow, as we may have
     * to iterate over the whole array every time if we're very busy with
     * transmissions, so there is room for improvement.)
     *
     * Every time a slot is retrieved, the "pending" counter is incremented to
     * have a fast indicator of the synchronisation status, i.e. if "pending"
     * is not set, the synchronisation procedure is complete and the local data may
     * be read, or the remote data has been written and further commands may may
     * be issued.
     *
     * The local (mirror) start address of the requested remote address is stored
     * into the same slot in the "local_addr" array, so we'll know where to put the
     * data if we issue an RMAP_read command. This may be omitted for write
     * commands.
     *
     * Every time a response packet is received, the data (if any) is written to the
     * local address, using the length specified by RMAP packet, so be careful where
     * you place your buffers or registers. On success, the "in_use" slot is cleared
     * and the pending counter is improved.
     *
     * XXX: careful, no locking is used on any of the log data, so this is
     * single-thread-use only!
     *
     */
    #define TRANS_LOG_SIZE 64	/* GRSPW2 TX descriptor limit */
    static struct {
    
    	uint8_t  in_use[TRANS_LOG_SIZE];
    	void    *local_addr[TRANS_LOG_SIZE];
    
    	int pending;
    } trans_log;
    
    
    /**
     * @brief grab a slot in the transaction log
     *
     * @param local_addr the local memory address
     *
     * @returns -1 on no slots, >= 0 for the transaction id
     */
    
    static int trans_log_grab_slot(void *local_addr)
    {
    	int i;
    	int slot = -1;
    
    	for (i = 0; i < TRANS_LOG_SIZE; i++) {
    
    		if (trans_log.in_use[i])
    			continue;
    
    		/* got one */
    		slot = i;
    		trans_log.in_use[slot] = 1;
    		trans_log.local_addr[slot] = local_addr;
    		trans_log.pending++;
    		break;
    	}
    
    	return slot;
    }
    
    
    /**
     * @brief release a slot in the transaction log
     *
     * @param slot the id of the slot
     *
     */
    
    static void trans_log_release_slot(int slot)
    {
    
    	if (slot < 0)
    		return;
    
    	if (slot >= TRANS_LOG_SIZE)
    		return;
    
    	if (!trans_log.in_use[slot])
    		return;
    
    	trans_log.in_use[slot] = 0;
    	trans_log.pending--;
    }
    
    
    /**
     * @brief get the local address for a slot
     *
     * @param slot the id of the slot
     *
     * @returns the address or NULL if not found/slot not in use
     */
    
    static void *trans_log_get_addr(int slot)
    {
    	if (slot < 0)
    		return NULL;
    
    	if (slot >= TRANS_LOG_SIZE)
    		return NULL;
    
    	if (!trans_log.in_use[slot])
    		return NULL;
    
    	return trans_log.local_addr[slot];
    }
    
    /**
     * @brief n rmap command transaction
     *
     * @returns number of packets processed or < 0 on error
     */
    
    static int rdcu_process_rx(void)
    {
    	int n;
    	int cnt = 0;
    
    	uint32_t *local_addr;
    
    	uint8_t *spw_pckt;
    
    	struct rmap_pkt *rp;
    
    
    	if (!rmap_rx)
    		return -1;
    
    	/* process all pending responses */
    	while ((n = rmap_rx(NULL))) {
    		/* we received something, allocate enough space for the packet */
    		spw_pckt = (uint8_t *) malloc(n);
    		if (!spw_pckt) {
    			printf("malloc() for packet failed!\n");
    			return -1;
    		}
    
    		/* read the packet */
    		n = rmap_rx(spw_pckt);
    
    		if (!n) {
    			printf("Unknown error in rmap_rx()\n");
    			free(spw_pckt);
    			return -1;
    		}
    
    		cnt++;
    
    		if (0)
    			rmap_parse_pkt(spw_pckt);
    
    		/* convert format */
    		rp = rmap_pkt_from_buffer(spw_pckt);
    		free(spw_pckt);
    
    		if (!rp) {
    			printf("Error converting to RMAP packet\n");
    			continue;
    		}
    
    		local_addr = trans_log_get_addr(rp->tr_id);
    
    		if (!local_addr) {
    			printf("warning: response packet received not in "
    			       "transaction log\n");
    			rmap_erase_packet(rp);
    			continue;
    		}
    
    		if (rp->data_len) {
    			memcpy(local_addr, rp->data, rp->data_len);
    #if __LITTLE_ENDIAN
    			if (rp->data_len & 0x3)
    				printf("warning: length of response packet"
    				       "received is not a multiple of 4 bytes\n");
    			{
    				uint32_t i;
    
    				for (i = 0; i < rp->data_len/4; i++)
    					be32_to_cpus(&local_addr[i]);
    			}
    #endif /* __LITTLE_ENDIAN */
    		}
    
    
    		trans_log_release_slot(rp->tr_id);
    		rmap_erase_packet(rp);
    	}
    
    	return cnt;
    }
    
    
    /**
     * @brief submit an rmap command transaction
     *
     * @param cmd the rmap command
     * @param cmd_size the size of the rmap command
     * @param data the payload (may be NULL)
     * @param data_size the size of the payload
     *
     * @returns 0 on success, otherwise error
     */
    
    int rdcu_submit_tx(const uint8_t *cmd,  int cmd_size,
    		   const uint8_t *data, int data_size)
    {
    	/* try to process pending responses */
    	rdcu_process_rx();
    
    	if (!rmap_tx)
    		return -1;
    
    	if (0)
    		printf("Transmitting RMAP command\n");
    
    	if (rmap_tx(cmd, cmd_size, dpath_len, data, data_size)) {
    		printf("rmap_tx() returned error!\n");
    		return -1;
    	}
    
    	return 0;
    }
    
    
    /**
     * @brief generate an rmap command packet
     *
     * @param trans_id a transaction identifier
     *
     * @param cmd the command buffer; if NULL, the function returns the needed size
     *
     * @param rmap_cmd_type the rmap command type of the packet
     *
     * @param addr the address to read from or write to
     *
     * @param size the number of bytes to read or write
     *
     * @returns the size of the command data buffer or 0 on error
     */
    
    int rdcu_gen_cmd(uint16_t trans_id, uint8_t *cmd,
    		 uint8_t rmap_cmd_type,
    		 uint32_t addr, uint32_t size)
    {
    	int n;
    
    	struct rmap_pkt *pkt;
    
    	pkt = rmap_create_packet();
    	if (!pkt) {
    		printf("Error creating packet\n");
    		return 0;
    	}
    
    	rmap_set_dst(pkt, rdcu_addr);
    	rmap_set_src(pkt, icu_addr);
    	rmap_set_dest_path(pkt, dpath, dpath_len);
    	rmap_set_reply_path(pkt, rpath, rpath_len);
    	rmap_set_key(pkt, dst_key);
    	rmap_set_cmd(pkt, rmap_cmd_type);
    	rmap_set_tr_id(pkt, trans_id);
    	rmap_set_data_addr(pkt, addr);
    	rmap_set_data_len(pkt, size);
    
    	/* determine header size */
    	n = rmap_build_hdr(pkt, NULL);
    
    	if (!cmd) {
    		rmap_erase_packet(pkt);
    		return n;
    	}
    
    	bzero(cmd, n);
    
    	n = rmap_build_hdr(pkt, cmd);
    
    	rmap_erase_packet(pkt);
    
    	return n;
    }
    
    
    
    
    
    /**
     * @brief submit a sync command
     *
     * @param fn the RDCU command generation function
     * @param addr the local address of the corresponding remote address
     * @param data_len the length of the data payload (0 for read commands)
     *
     * @return 0 on success, otherwise error
     */
    
    
    int rdcu_sync(int (*fn)(uint16_t trans_id, uint8_t *cmd),
    	      void *addr, int data_len)
    {
    	int n;
    	int slot;
    
    	uint8_t *rmap_cmd;
    	uint8_t *data = addr;
    
    	slot = trans_log_grab_slot(addr);
    	if (slot < 0)
    		return -1;
    
    
    	/* determine size of command */
    	n = fn(slot, NULL);
    
    	rmap_cmd = (uint8_t *) malloc(n);
    	if (!rmap_cmd) {
    		printf("Error allocating rmap cmd");
    		return -1;
    	}
    
    	/* now fill actual command */
    	n = fn(slot, rmap_cmd);
    	if (!n) {
    		printf("Error creating command packet\n");
    		free(rmap_cmd);
    		return -1;
    	}
    
    #if __LITTLE_ENDIAN
    	if (data_len & 0x3)
    		printf("warning: length of send packet is not a multiple of "
    		       "4 bytes\n");
    
    	if (data_len) {
    		int i;
    
    		data = (uint8_t *) malloc(data_len);
    		for (i = 0; i < data_len/4; i++)
    			((uint32_t *)data)[i] =
    				cpu_to_be32(((uint32_t *)addr)[i]);
    	}
    #endif /* __LITTLE_ENDIAN */
    
    	n = rdcu_submit_tx(rmap_cmd, n, data, data_len);
    	free(rmap_cmd);
    #if __LITTLE_ENDIAN
    	free(data);
    #endif /* __LITTLE_ENDIAN */
    
    	return n;
    }
    
    
    
    /**
     * @brief submit a data sync command
     *
     * @param fn a RDCU data transfer generation function
     * @param addr the remote address
     * @param data the local data address
     * @param data_len the length of the data payload
     * @param read 0: write, otherwise read
     *
     * @return 0 on success, < 0: error, > 0: retry
     *
     * @note this one is a little redundant, but otherwise we'd have a lot of
     *	 unused parameters on most of the control functions
     *
     * XXX need a paramter for read...meh...must think of something else
     */
    
    
    int rdcu_sync_data(int (*fn)(uint16_t trans_id, uint8_t *cmd,
    			     uint32_t addr, uint32_t data_len),
    		   uint32_t addr, void *data, uint32_t data_len, int read)
    {
    	int n;
    	int slot;
    
    	uint8_t *rmap_cmd;
    
    
    
    	rdcu_process_rx();
    
    	slot = trans_log_grab_slot(data);
    	if (slot < 0) {
    		if (0)
    		printf("Error: all slots busy!\n");
    		return 1;
    	}
    
    
    	/* determine size of command */
    	n = fn(slot, NULL, addr, data_len);
    
    	rmap_cmd = (uint8_t *) malloc(n);
    	if (!rmap_cmd) {
    		printf("Error allocating rmap cmd");
    		return -1;
    	}
    
    	/* now fill actual command */
    	n = fn(slot, rmap_cmd, addr, data_len);
    	if (!n) {
    		printf("Error creating command packet\n");
    		free(rmap_cmd);
    		return -1;
    	}
    
    	if (read)
    		n = rdcu_submit_tx(rmap_cmd, n, NULL, 0);
    	else
    		n = rdcu_submit_tx(rmap_cmd, n, data, data_len);
    
    	free(rmap_cmd);
    
    	return n;
    }
    
    
    
    /**
     * @brief create a complete package from header and payload data including CRC8
     *
     * @note this is a helper function to generate complete binary RMAP packet dumps
     *
     * @param blob the blob buffer; if NULL, the function returns the needed size
     *
     * @param[in]  cmd an rmap command buffer
     * @param[in]  cmd_size the size of the rmap command buffer
     * @param[in]  non_crc_bytes leading bytes in the header not path of the CRC
     * @param[in]  data a data buffer (may be NULL)
     * @param[in]  data_size the size of the data buffer (ignored if data is NULL)
     *
     * @returns the size of the blob or 0 on error
     */
    
    int rdcu_package(uint8_t *blob,
    		 const uint8_t *cmd,  int cmd_size,
    		 const uint8_t non_crc_bytes,
    		 const uint8_t *data, int data_size)
    {
    	int n;
    	int has_data_crc = 0;
    	struct rmap_instruction *ri;
    
    
    
    	if (!cmd_size) {
    		blob = NULL;
    		return 0;
    	}
    
    
    	/* allocate space for header, header crc, data, data crc */
    	n = cmd_size + 1;
    
    	ri = (struct rmap_instruction *) &cmd[non_crc_bytes + RMAP_INSTRUCTION];
    
    	/* see if the type of command needs a data crc field at the end */
    	switch (ri->cmd) {
    		case RMAP_READ_MODIFY_WRITE_ADDR_INC:
    		case RMAP_WRITE_ADDR_SINGLE:
    		case RMAP_WRITE_ADDR_INC:
    		case RMAP_WRITE_ADDR_SINGLE_VERIFY:
    		case RMAP_WRITE_ADDR_INC_VERIFY:
    		case RMAP_WRITE_ADDR_SINGLE_VERIFY_REPLY:
    		case RMAP_WRITE_ADDR_INC_VERIFY_REPLY:
    			has_data_crc = 1;
    			n += 1;
    			break;
    		default:
    			break;
    	}
    
    
    	if (data)
    		n += data_size;
    
    	if (!blob)
    		return n;
    
    
    	memcpy(&blob[0], cmd, cmd_size);
    
    	blob[cmd_size] = rmap_crc8(&cmd[non_crc_bytes],
    				   cmd_size - non_crc_bytes);
    
    	if (data) {
    		memcpy(&blob[cmd_size + 1], data, data_size);
    		blob[cmd_size + 1 + data_size] = rmap_crc8(data, data_size);
    	} else {
    		/* if no data is present, data crc is 0x0 */
    		if (has_data_crc)
    			blob[cmd_size + 1] = 0x0;
    	}
    
    
    	return n;
    }
    
    
    /**
     * @brief sets the logical address of the RDCU
     * @param addr the address
     */
    
    void rdcu_set_destination_logical_address(uint8_t addr)
    {
    	rdcu_addr = addr;
    }
    
    /**
     * @brief sets the logical address of the ICU
     * @param addr the address
     */
    
    void rdcu_set_source_logical_address(uint8_t addr)
    {
    	icu_addr = addr;
    }
    
    
    /**
     * @brief set the destination path to the RDCU
     * @param path a byte array containing the path (may be NULL)
     * @param len the number of elements in the array
     *
     * @returns 0 on success, otherwise error
     *
     * @note the path array is taken as a reference, make sure to keep it around
     *	 the maximum length of the path is 15 elements
     *	 setting either path NULL or len 0 disables destination path addressing
     */
    
    int rdcu_set_destination_path(uint8_t *path, uint8_t len)
    {
    	if (len > RMAP_MAX_PATH_LEN)
    		return -1;
    
    	if (!path || !len) {
    		dpath     = NULL;
    		dpath_len = 0;
    		return 0;
    	}
    
    	dpath     = path;
    	dpath_len = len;
    
    	return 0;
    }
    
    
    /**
     * @brief set the return path to the ICU
     * @param path a byte array containing the path (may be NULL)
     * @param len the number of elements in the array
     *
     * @returns 0 on success, otherwise error
     *
     * @note the path array is taken as a reference, make sure to keep it around
     *	 the maximum length of the path is 12 elements
     *	 the number of elements must be a multiple of 4 (due to RMAP protocol)
     *	 setting either path NULL or len 0 disables return path addressing
     */
    
    int rdcu_set_return_path(uint8_t *path, uint8_t len)
    {
    	if (len > RMAP_MAX_REPLY_PATH_LEN)
    		return -1;
    
    	if (!path || !len) {
    		rpath     = NULL;
    		rpath_len = 0;
    		return 0;
    	}
    
    	rpath     = path;
    	rpath_len = len;
    
    	return 0;
    }
    
    
    /**
     * @brief set the destination command key to use
     *
     * @param key the destination key
     */
    
    void rdcu_set_destination_key(uint8_t key)
    {
    	dst_key = key;
    }
    
    
    /**
     * @brief get the RDCU <-> ICU mirror RMAP synchronisation status
     *
     * @returns 0: synchronised, > 0: operations pending
     */
    
    int rdcu_rmap_sync_status(void)
    {
    	/* try to process pending responses */
    	rdcu_process_rx();
    
    	return trans_log.pending;
    }
    
    
    /**
     * @brief reset all entries in the RMAP transaction log
     */
    
    void rdcu_rmap_reset_log(void)
    {
    	bzero(trans_log.in_use, TRANS_LOG_SIZE);
    	trans_log.pending = 0;
    }
    
    
    /**
     * @brief initialise the rdcu control library
     *
     * @param mtu the maximum data transfer size per unit
     *
     * @param rmap_tx a function pointer to transmit an rmap command
     * @param rmap_rx function pointer to receive an rmap command
     *
     * @note rmap_tx is expected to return 0 on success
     *	 rmap_rx is expected to return the number of packet bytes
     *
     * @returns 0 on success, otherwise error
     */
    
    int rdcu_rmap_init(int mtu,
    		   int32_t (*tx)(const void *hdr,  uint32_t hdr_size,
    				 const uint8_t non_crc_bytes,
    				 const void *data, uint32_t data_size),
    		   uint32_t (*rx)(uint8_t *pkt))
    {
    	if (!tx)
    		return -1;
    
    	if (!rx)
    		return -1;
    
    	rmap_tx = tx;
    	rmap_rx = rx;
    
    	data_mtu = mtu;
    
    	return 0;
    }