socsvn commit: r268551 - in soc2014/dpl: bpfjit netmap-ipfw netmap-ipfw/extra netmap-ipfw/extra/sys netmap-ipfw/extra/sys/contrib netmap-ipfw/extra/sys/contrib/pf netmap-ipfw/extra/sys/contrib/pf/n...

dpl at FreeBSD.org dpl at FreeBSD.org
Sat May 24 16:33:28 UTC 2014


Author: dpl
Date: Sat May 24 16:33:26 2014
New Revision: 268551
URL: http://svnweb.FreeBSD.org/socsvn/?view=rev&rev=268551

Log:
  Add the code of netmap-ipfw and a theraven@ implementation of bpd using JIT. It will be used as an example.
  

Added:
  soc2014/dpl/bpfjit/
  soc2014/dpl/bpfjit/Makefile
  soc2014/dpl/bpfjit/dtc.cc
  soc2014/dpl/bpfjit/jit.cc
  soc2014/dpl/bpfjit/mktest.c
  soc2014/dpl/bpfjit/runtest.sh
  soc2014/dpl/netmap-ipfw/
  soc2014/dpl/netmap-ipfw/BSDmakefile
  soc2014/dpl/netmap-ipfw/Makefile
  soc2014/dpl/netmap-ipfw/Makefile.inc
  soc2014/dpl/netmap-ipfw/Makefile.kipfw
  soc2014/dpl/netmap-ipfw/README
  soc2014/dpl/netmap-ipfw/extra/
  soc2014/dpl/netmap-ipfw/extra/expand_number.c
  soc2014/dpl/netmap-ipfw/extra/glue.c
  soc2014/dpl/netmap-ipfw/extra/glue.h
  soc2014/dpl/netmap-ipfw/extra/humanize_number.c
  soc2014/dpl/netmap-ipfw/extra/ipfw2_mod.c
  soc2014/dpl/netmap-ipfw/extra/linux_defs.h
  soc2014/dpl/netmap-ipfw/extra/missing.c
  soc2014/dpl/netmap-ipfw/extra/missing.h
  soc2014/dpl/netmap-ipfw/extra/netmap_io.c
  soc2014/dpl/netmap-ipfw/extra/session.c
  soc2014/dpl/netmap-ipfw/extra/sys/
  soc2014/dpl/netmap-ipfw/extra/sys/contrib/
  soc2014/dpl/netmap-ipfw/extra/sys/contrib/pf/
  soc2014/dpl/netmap-ipfw/extra/sys/contrib/pf/net/
  soc2014/dpl/netmap-ipfw/extra/sys/contrib/pf/net/pfvar.h
  soc2014/dpl/netmap-ipfw/extra/sys/sys/
  soc2014/dpl/netmap-ipfw/extra/sys/sys/kernel.h
  soc2014/dpl/netmap-ipfw/extra/sys/sys/malloc.h
  soc2014/dpl/netmap-ipfw/extra/sys/sys/mbuf.h
  soc2014/dpl/netmap-ipfw/extra/sys/sys/module.h
  soc2014/dpl/netmap-ipfw/extra/sys/sys/systm.h
  soc2014/dpl/netmap-ipfw/extra/sys/sys/taskqueue.h
  soc2014/dpl/netmap-ipfw/ipfw/
  soc2014/dpl/netmap-ipfw/ipfw/Makefile
  soc2014/dpl/netmap-ipfw/ipfw/altq.c
  soc2014/dpl/netmap-ipfw/ipfw/dummynet.c
  soc2014/dpl/netmap-ipfw/ipfw/ipfw2.c
  soc2014/dpl/netmap-ipfw/ipfw/ipfw2.h
  soc2014/dpl/netmap-ipfw/ipfw/ipv6.c
  soc2014/dpl/netmap-ipfw/ipfw/main.c
  soc2014/dpl/netmap-ipfw/ipfw/nat.c
  soc2014/dpl/netmap-ipfw/sys/
  soc2014/dpl/netmap-ipfw/sys/net/
  soc2014/dpl/netmap-ipfw/sys/net/pfil.h
  soc2014/dpl/netmap-ipfw/sys/net/radix.c
  soc2014/dpl/netmap-ipfw/sys/net/radix.h
  soc2014/dpl/netmap-ipfw/sys/netgraph/
  soc2014/dpl/netmap-ipfw/sys/netgraph/ng_ipfw.h
  soc2014/dpl/netmap-ipfw/sys/netinet/
  soc2014/dpl/netmap-ipfw/sys/netinet/in_cksum.c
  soc2014/dpl/netmap-ipfw/sys/netinet/ip_dummynet.h
  soc2014/dpl/netmap-ipfw/sys/netinet/ip_fw.h
  soc2014/dpl/netmap-ipfw/sys/netinet/tcp.h
  soc2014/dpl/netmap-ipfw/sys/netinet/udp.h
  soc2014/dpl/netmap-ipfw/sys/netpfil/
  soc2014/dpl/netmap-ipfw/sys/netpfil/ipfw/
  soc2014/dpl/netmap-ipfw/sys/netpfil/ipfw/dn_heap.c
  soc2014/dpl/netmap-ipfw/sys/netpfil/ipfw/dn_heap.h
  soc2014/dpl/netmap-ipfw/sys/netpfil/ipfw/dn_sched.h
  soc2014/dpl/netmap-ipfw/sys/netpfil/ipfw/dn_sched_fifo.c
  soc2014/dpl/netmap-ipfw/sys/netpfil/ipfw/dn_sched_prio.c
  soc2014/dpl/netmap-ipfw/sys/netpfil/ipfw/dn_sched_qfq.c
  soc2014/dpl/netmap-ipfw/sys/netpfil/ipfw/dn_sched_rr.c
  soc2014/dpl/netmap-ipfw/sys/netpfil/ipfw/dn_sched_wf2q.c
  soc2014/dpl/netmap-ipfw/sys/netpfil/ipfw/ip_dn_glue.c
  soc2014/dpl/netmap-ipfw/sys/netpfil/ipfw/ip_dn_io.c
  soc2014/dpl/netmap-ipfw/sys/netpfil/ipfw/ip_dn_private.h
  soc2014/dpl/netmap-ipfw/sys/netpfil/ipfw/ip_dummynet.c
  soc2014/dpl/netmap-ipfw/sys/netpfil/ipfw/ip_fw2.c
  soc2014/dpl/netmap-ipfw/sys/netpfil/ipfw/ip_fw_dynamic.c
  soc2014/dpl/netmap-ipfw/sys/netpfil/ipfw/ip_fw_log.c
  soc2014/dpl/netmap-ipfw/sys/netpfil/ipfw/ip_fw_pfil.c
  soc2014/dpl/netmap-ipfw/sys/netpfil/ipfw/ip_fw_private.h
  soc2014/dpl/netmap-ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c
  soc2014/dpl/netmap-ipfw/sys/netpfil/ipfw/ip_fw_table.c

Added: soc2014/dpl/bpfjit/Makefile
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ soc2014/dpl/bpfjit/Makefile	Sat May 24 16:33:26 2014	(r268551)
@@ -0,0 +1,9 @@
+
+jit: jit.cc
+	clang++ jit.cc `llvm-config --cxxflags --ldflags --libs mcjit jit bitwriter ipo instrumentation x86` -std=c++0x -g
+
+mktest: mktest.c
+	clang mktest.c -o mktest
+
+clean:
+	rm -f mktest jit out.bin

Added: soc2014/dpl/bpfjit/dtc.cc
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ soc2014/dpl/bpfjit/dtc.cc	Sat May 24 16:33:26 2014	(r268551)
@@ -0,0 +1,3858 @@
+/*-
+ * Copyright (c) 2012 David Chisnall
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+////////////////////////////////////////////////////////////////////////////////
+// NOTE: This file has numerous 80-column violations.  It needs splitting into
+// multiple files and some method definitions moving out of line, which will
+// reduce some lines.  Most of the very long lines are for loops with
+// iterators, which can be shortened a lot when we move this to C++11.
+////////////////////////////////////////////////////////////////////////////////
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <inttypes.h>
+
+
+#include <map>
+#include <set>
+#include <vector>
+#include <memory>
+#include <algorithm>
+
+#if __cplusplus < 201103L
+#define static_assert(x, y) ((void)0)
+#endif
+
+namespace dtc {
+
+/**
+ * Type for a buffer of bytes.  This is used for a lot of short-lived temporary
+ * variables, so may eventually be changed to something like LLVM's
+ * SmallVector, but currently the program runs in a tiny fraction of a second,
+ * so this is not an issue.
+ */
+typedef std::vector<uint8_t> byte_buffer;
+
+/**
+ * Helper function to push a big endian value into a byte buffer.  We use
+ * native-endian values for all of the in-memory data structures and only
+ * transform them into big endian form for output.
+ */
+template<typename T>
+void push_big_endian(byte_buffer &v, T val)
+{
+	static_assert(sizeof(T) > 1,
+		"Big endian doesn't make sense for single-byte values");
+	for (int bit=(sizeof(T) - 1)*8 ; bit>=0 ; bit-= 8)
+	{
+		v.push_back((val >> bit) & 0xff);
+	}
+}
+
+/**
+ * Simple inline non-locale-aware check that this is a valid ASCII
+ * digit.
+ */
+inline bool isdigit(char c)
+{
+	return (c >= '0') && (c <= '9');
+}
+
+/**
+ * Simple inline non-locale-aware check that this is a valid ASCII
+ * hex digit.
+ */
+inline bool ishexdigit(char c)
+{
+	return ((c >= '0') && (c <= '9')) || ((c >= 'a') && (c <= 'f')) || ((c >= 'A') && (c <= 'Z'));
+}
+
+/**
+ * Class encapsulating the input file.  Can be used as a const char*, but has
+ * range checking.  Attempting to access anything out of range will return a 0
+ * byte.  The input buffer can be cheaply copied, without copying the
+ * underlying memory, however it is the user's responsibility to ensure that
+ * such copies do not persist beyond the lifetime of the underlying memory.
+ *
+ * This also contains methods for reporting errors and for consuming the token
+ * stream.
+ */
+class input_buffer
+{
+	protected:
+	/**
+	 * The buffer.  This class doesn't own the buffer, but the
+	 * mmap_input_buffer subclass does.
+	 */
+	const char* buffer;
+	/**
+	 * The size of the buffer.
+	 */
+	size_t size;
+	private:
+	/**
+	 * The current place in the buffer where we are reading.  This class
+	 * keeps a separate size, pointer, and cursor so that we can move
+	 * forwards and backwards and still have checks that we haven't fallen
+	 * off either end.
+	 */
+	int cursor;
+	/**
+	 * Private constructor.  This is used to create input buffers that
+	 * refer to the same memory, but have different cursors.
+	 */
+	input_buffer(const char* b, size_t s, int c) : buffer(b), size(s),
+		cursor(c) {}
+	/**
+	 * Reads forward past any spaces.  The DTS format is not whitespace
+	 * sensitive and so we want to scan past whitespace when reading it.
+	 */
+	void skip_spaces()
+	{
+		if (cursor >= size) { return; }
+		if (cursor < 0) { return; }
+		char c = buffer[cursor];
+		while ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\f')
+		       || (c == '\v') || (c == '\r'))
+		{
+			cursor++;
+			if (cursor > size)
+			{
+				c = '\0';
+			}
+			else
+			{
+				c = buffer[cursor];
+			}
+		}
+	}
+	public:
+	/**
+	 * Virtual destructor.  Does nothing, but exists so that subclasses
+	 * that own the memory can run cleanup code for deallocating it.
+	 */
+	virtual ~input_buffer() {};
+	/**
+	 * Constructs an empty buffer.
+	 */
+	input_buffer() : buffer(0), size(0), cursor(0) {}
+	/**
+	 * Constructs a new buffer with a specified memory region and size.
+	 */
+	input_buffer(const char* b, size_t s) : buffer(b), size(s), cursor(0){}
+	/**
+	 * Returns a new input buffer referring into this input, clamped to the
+	 * specified size.  If the requested buffer would fall outside the
+	 * range of this one, then it returns an empty buffer.
+	 *
+	 * The returned buffer shares the same underlying storage as the
+	 * original.  This is intended to be used for splitting up the various
+	 * sections of a device tree blob.  Requesting a size of 0 will give a
+	 * buffer that extends to the end of the available memory.
+	 */
+	input_buffer buffer_from_offset(size_t offset, size_t s=0)
+	{
+		if (s == 0)
+		{
+			s = size - offset;
+		}
+		if (offset > size)
+		{
+			return input_buffer();
+		}
+		if (s > (size-offset))
+		{
+			return input_buffer();
+		}
+		return input_buffer(&buffer[offset], s);
+	}
+	/**
+	 * Returns true if this buffer has no unconsumed space in it.
+	 */
+	bool empty()
+	{
+		return cursor >= size;
+	}
+	/**
+	 * Dereferencing operator, allows the buffer to be treated as a char*
+	 * and dereferenced to give a character.  This returns a null byte if
+	 * the cursor is out of range.
+	 */
+	inline char operator*()
+	{
+		if (cursor >= size) { return '\0'; }
+		if (cursor < 0) { return '\0'; }
+		return buffer[cursor];
+	}
+	/**
+	 * Array subscripting operator, returns a character at the specified
+	 * index offset from the current cursor.  The offset may be negative,
+	 * to reread characters that have already been read.  If the current
+	 * cursor plus offset is outside of the range, this returns a nul
+	 * byte.
+	 */
+	inline char operator[](int offset)
+	{
+		if (cursor + offset >= size) { return '\0'; }
+		if (cursor + offset < 0) { return '\0'; }
+		return buffer[cursor + offset];
+	}
+	/**
+	 * Increments the cursor, iterating forward in the buffer.
+	 */
+	inline input_buffer &operator++()
+	{
+		cursor++; 
+		return *this;
+	}
+	/**
+	 * Cast to char* operator.  Returns a pointer into the buffer that can
+	 * be used for constructing strings.
+	 */
+	inline operator const char*()
+	{
+		if (cursor >= size) { return 0; }
+		if (cursor < 0) { return 0; }
+		return &buffer[cursor];
+	}
+	/**
+	 * Consumes a character.  Moves the cursor one character forward if the
+	 * next character matches the argument, returning true.  If the current
+	 * character does not match the argument, returns false.
+	 */
+	inline bool consume(char c)
+	{
+		if ((*this)[0] == c) 
+		{
+			++(*this);
+			return true;
+		}
+		return false;
+	}
+	/**
+	 * Consumes a string.  If the (null-terminated) string passed as the
+	 * argument appears in the input, advances the cursor to the end and
+	 * returns true.  Returns false if the string does not appear at the
+	 * current point in the input.
+	 */
+	bool consume(const char *str)
+	{
+		size_t len = strlen(str);
+		if (len > size - cursor)
+		{
+			return false;
+		}
+		else
+		{
+			for (int i=0 ; i<len ; ++i)
+			{
+				if (str[i] != buffer[cursor + i])
+				{
+					return false;
+				}
+			}
+			cursor += len;
+			return true;
+		}
+		return false;
+	}
+	/**
+	 * Reads an integer in base 8, 10, or 16.  Returns true and advances
+	 * the cursor to the end of the integer if the cursor points to an
+	 * integer, returns false and does not move the cursor otherwise.
+	 *
+	 * The parsed value is returned via the argument.
+	 */
+	bool consume_integer(long long &outInt)
+	{
+		// The first character must be a digit.  Hex and octal strings
+		// are prefixed by 0 and 0x, respectively.
+		if (!isdigit((*this)[0]))
+		{
+			return false;
+		}
+		char *end=0;
+		outInt = strtoll(&buffer[cursor], &end, 0);
+		if (end == &buffer[cursor])
+		{
+			return false;
+		}
+		cursor = end - buffer;
+		return true;
+	}
+	/**
+	 * Template function that consumes a binary value in big-endian format
+	 * from the input stream.  Returns true and advances the cursor if
+	 * there is a value of the correct size.  This function assumes that
+	 * all values must be natively aligned, and so advances the cursor to
+	 * the correct alignment before reading.
+	 */
+	template<typename T>
+	bool consume_binary(T &out)
+	{
+		int align = 0;
+		if (cursor % sizeof(T) != 0)
+		{
+			align = sizeof(T) - (cursor % sizeof(T));
+		}
+		if (size < cursor + align + sizeof(T))
+		{
+			return false;
+		}
+		cursor += align;
+		assert(cursor % sizeof(T) == 0);
+		out = 0;
+		for (int i=0 ; i<sizeof(T) ; ++i)
+		{
+			out <<= 8;
+			out |= (((T)buffer[cursor++]) & 0xff);
+		}
+		return true;
+	}
+	/**
+	 * Consumes two hex digits and return the resulting byte via the first
+	 * argument.  If the next two characters are hex digits, returns true
+	 * and advances the cursor.  If not, then returns false and leaves the
+	 * cursor in place.
+	 */
+	bool consume_hex_byte(uint8_t &outByte)
+	{
+		if (!ishexdigit((*this)[0]) && !ishexdigit((*this)[1]))
+		{
+			return false;
+		}
+		outByte = (digittoint((*this)[0]) << 4) | digittoint((*this)[1]);
+		cursor += 2;
+		return true;
+	}
+	/**
+	 * Advances the cursor to the start of the next token, skipping
+	 * comments and whitespace.  If the cursor already points to the start
+	 * of a token, then this function does nothing.
+	 */
+	input_buffer &next_token()
+	{
+		int start;
+		do {
+			start = cursor;
+			skip_spaces();
+			// Parse /* comments
+			if (((*this)[0] == '/') && ((*this)[1] == '*'))
+			{
+				// eat the start of the comment
+				++(*this);
+				++(*this);
+				do {
+					// Find the ending * of */
+					while ((**this != '\0') && (**this != '*'))
+					{
+						++(*this);
+					}
+					// Eat the *
+					++(*this);
+				} while ((**this != '\0') && (**this != '/'));
+				// Eat the /
+				++(*this);
+			}
+			// Parse // comments
+			if (((*this)[0] == '/') && ((*this)[1] == '/'))
+			{
+				// eat the start of the comment
+				++(*this);
+				++(*this);
+				// Find the ending * of */
+				while (**this != '\n')
+				{
+					++(*this);
+				}
+				// Eat the \n
+				++(*this);
+			}
+		} while (start != cursor);
+		return *this;
+	}
+	/**
+	 * Prints a message indicating the location of a parse error.
+	 */
+	void parse_error(const char *msg)
+	{
+		int line_count = 1;
+		int line_start = 0;
+		int line_end = cursor;
+		for (int i=cursor ; i>0 ; --i)
+		{
+			if (buffer[i] == '\n')
+			{
+				line_count++;
+				if (line_start == 0)
+				{
+					line_start = i+1;
+				}
+			}
+		}
+		for (int i=cursor+1 ; i<size ; ++i)
+		{
+			if (buffer[i] == '\n')
+			{
+				line_end = i;
+				break;
+			}
+		}
+		fprintf(stderr, "Error on line %d: %s\n", line_count, msg);
+		fwrite(&buffer[line_start], line_end-line_start, 1, stderr);
+		putc('\n', stderr);
+		for (int i=0 ; i<(cursor-line_start) ; ++i)
+		{
+			putc(' ', stderr);
+		}
+		putc('^', stderr);
+		putc('\n', stderr);
+	}
+	/**
+	 * Dumps the current cursor value and the unconsumed values in the
+	 * input buffer to the standard error.  This method is intended solely
+	 * for debugging.
+	 */
+	void dump()
+	{
+		fprintf(stderr, "Current cursor: %d\n", cursor);
+		fwrite(&buffer[cursor], size-cursor, 1, stderr);
+	}
+};
+/**
+ * Explicit specialisation for reading a single byte.
+ */
+template<>
+bool input_buffer::consume_binary(uint8_t &out)
+{
+	if (size < cursor + 1)
+	{
+		return false;
+	}
+	out = buffer[cursor++];
+	return true;
+}
+
+/**
+ * Subclass of input_buffer that mmap()s a file and owns the resulting memory.
+ * When this object is destroyed, the memory is unmapped.
+ */
+struct mmap_input_buffer : public input_buffer
+{
+	/**
+	 * Constructs a new buffer from the file passed in as a file
+	 * descriptor.  
+	 */
+	mmap_input_buffer(int fd) : input_buffer(0, 0)
+	{
+		struct stat sb;
+		if (fstat(fd, &sb))
+		{
+			perror("Failed to stat file");
+		}
+		size = sb.st_size;
+		buffer = (const char*)mmap(0, size, PROT_READ,
+			MAP_PREFAULT_READ, fd, 0);
+		if (buffer == 0)
+		{
+			perror("Failed to mmap file");
+		}
+	}
+	/**
+	 * Unmaps the buffer, if one exists.
+	 */
+	virtual ~mmap_input_buffer()
+	{
+		if (buffer != 0)
+		{
+			munmap((void*)buffer, size);
+		}
+	}
+};
+/**
+ * Input buffer read from standard input.  This is used for reading device tree
+ * blobs and source from standard input.  It reads the entire input into
+ * malloc'd memory, so will be very slow for large inputs.  DTS and DTB files
+ * are very rarely more than 10KB though, so this is probably not a problem.
+ */
+struct stream_input_buffer : public input_buffer
+{
+	/**
+	 * The buffer that will store the data read from the standard input.
+	 */
+	std::vector<char> b;
+	/**
+	 * Constructs a new buffer from the file passed in as a file
+	 * descriptor.  
+	 */
+	stream_input_buffer() : input_buffer(0, 0)
+	{
+		int c;
+		while ((c = fgetc(stdin)) != EOF)
+		{
+			b.push_back(c);
+		}
+		buffer = b.data();
+		size = b.size();
+	}
+};
+
+
+
+/**
+ * String, referring to a place in the input file.  We don't bother copying
+ * strings until we write them to the final output.  These strings should be
+ * two words long: a start and a length.  They are intended to be cheap to copy
+ * and store in collections.  Copying the string object does not copy the
+ * underlying storage.
+ *
+ * Strings are not nul-terminated.
+ */
+class string
+{
+	/**
+	 * The source files are ASCII, so we provide a non-locale-aware version of
+	 * isalpha.  This is a class so that it can be used with a template
+	 * function for parsing strings.
+	 */
+	struct is_alpha 
+	{
+		static inline bool check(const char c)
+		{
+			return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') &&
+			        (c <= 'Z'));
+		}
+	};
+	/**
+	 * Check whether a character is in the set allowed for node names.
+	 * This is a class so that it can be used with a template function for
+	 * parsing strings.
+	 */
+	struct is_node_name_character
+	{
+		static inline bool check(const char c)
+		{
+			switch(c)
+			{
+				default:
+					return false;
+				case 'a'...'z': case 'A'...'Z': case '0'...'9':
+				case ',': case '.': case '+': case '-':
+				case '_':
+					return true;
+			}
+		}
+	};
+	/**
+	 * Check whether a character is in the set allowed for property names.
+	 * This is a class so that it can be used with a template function for
+	 * parsing strings.
+	 */
+	struct is_property_name_character
+	{
+		static inline bool check(const char c)
+		{
+			switch(c)
+			{
+				default:
+					return false;
+				case 'a'...'z': case 'A'...'Z': case '0'...'9':
+				case ',': case '.': case '+': case '-':
+				case '_': case '#':
+					return true;
+			}
+		}
+	};
+	/** Start address.  Contained within the mmap()'d input file and not
+	 * owned by this object. */
+	const char *start;
+	/** length of the string.  DTS strings are allowed to contain nuls */
+	int length;
+	/** Generic function for parsing strings matching the character set
+	 * defined by the template argument.  */
+	template<class T>
+	static string parse(input_buffer &s)
+	{
+		const char *start = s;
+		int l=0;
+		while (T::check(*s)) { l++; ++s; }
+		return string(start, l);
+	}
+	public:
+	/**
+	 * Constructs a string referring into another buffer.
+	 */
+	string(const char *s, int l) : start(s), length(l) {}
+	/** Constructs a string from a C string.  */
+	string(const char *s) : start(s), length(strlen(s)) {}
+	/** Default constructor, returns an empty string. */
+	string() : start(0), length(0) {}
+	/** Construct a from an input buffer, ending with a nul terminator. */
+	string(input_buffer &s) : start((const char*)s), length(0)
+	{
+		while(s[length] != '\0')
+		{
+			length++;
+		}
+	}
+	/**
+	 * Returns the longest string in the input buffer starting at the
+	 * current cursor and composed entirely of characters that are valid in
+	 * node names.
+	 */
+	static string parse_node_name(input_buffer &s)
+	{
+		return parse<is_node_name_character>(s);
+	}
+	/**
+	 * Returns the longest string in the input buffer starting at the
+	 * current cursor and composed entirely of characters that are valid in
+	 * property names.
+	 */
+	static string parse_property_name(input_buffer &s)
+	{
+		return parse<is_property_name_character>(s);
+	}
+	/**
+	 * Parses either a node or a property name.  If is_property is true on
+	 * entry, then only property names are parsed.  If it is false, then it
+	 * will be set, on return, to indicate whether the parsed name is only
+	 * valid as a property.
+	 */
+	static string parse_node_or_property_name(input_buffer &s,
+	                                          bool &is_property)
+	{
+		if (is_property)
+		{
+			return parse_property_name(s);
+		}
+		const char *start = s;
+		int l=0;
+		while (is_node_name_character::check(*s))
+		{
+			l++;
+			++s;
+		}
+		while (is_property_name_character::check(*s))
+		{
+			l++;
+			++s;
+			is_property = true;
+		}
+		return string(start, l);
+	}
+	/**
+	 * Compares two strings for equality.  Strings are equal if they refer
+	 * to identical byte sequences.
+	 */
+	bool operator==(const string& other) const
+	{
+		return (length == other.length) &&
+		       (memcmp(start, other.start, length) == 0);
+	}
+	/**
+	 * Compares a string against a C string.  The trailing nul in the C
+	 * string is ignored for the purpose of comparison, so this will always
+	 * fail if the string contains nul bytes.
+	 */
+	bool operator==(const char *other) const
+	{
+		return strncmp(other, start, length) == 0;
+	}
+	/**
+	 * Inequality operator, defined as the inverse of the equality
+	 * operator.
+	 */
+	template <typename T>
+	bool operator!=(T other)
+	{
+		return !(*this == other);
+	}
+	/**
+	 * Comparison operator, defined to allow strings to be used as keys in
+	 * maps.
+	 */
+	bool operator<(const string& other) const
+	{
+		if (length < other.length) { return true; }
+		if (length > other.length) { return false; }
+		return memcmp(start, other.start, length) < 0;
+	}
+	/**
+	 * Returns true if this is the empty string, false otherwise.
+	 */
+	bool empty() const
+	{
+		return length == 0;
+	}
+	/**
+	 * Returns the size of the string, in bytes.
+	 */
+	size_t size() { return length; }
+	/**
+	 * Writes the string to the specified buffer.
+	 */
+	void push_to_buffer(byte_buffer &buffer, bool escapes=false)
+	{
+		for (int i=0 ; i<length ; ++i)
+		{
+			uint8_t c = start[i];
+			if (escapes && c == '\\' && i+1 < length)
+			{
+				c = start[++i];
+				switch (c)
+				{
+					// For now, we just ignore invalid escape sequences.
+					default:
+					case '"':
+					case '\'':
+					case '\\':
+						break;
+					case 'a':
+						c = '\a';
+						break;
+					case 'b':
+						c = '\b';
+						break;
+					case 't':
+						c = '\t';
+						break;
+					case 'n':
+						c = '\n';
+						break;
+					case 'v':
+						c = '\v';
+						break;
+					case 'f':
+						c = '\f';
+						break;
+					case 'r':
+						c = '\r';
+						break;
+					case '0'...'7':
+					{
+						int v = digittoint(c);
+						if (i+1 < length && start[i+1] <= '7' && start[i+1] >= '0')
+						{
+							v <<= 3;
+							v |= digittoint(start[i+1]);
+							i++;
+							if (i+1 < length && start[i+1] <= '7' && start[i+1] >= '0')
+							{
+								v <<= 3;
+								v |= digittoint(start[i+1]);
+							}
+						}
+						c = (uint8_t)v;
+						break;
+					}
+					case 'x':
+					{
+						++i;
+						if (i >= length)
+						{
+							break;
+						}
+						int v = digittoint(start[i]);
+						if (i+1 < length && ishexdigit(start[i+1]))
+						{
+							v <<= 4;
+							v |= digittoint(start[++i]);
+						}
+						c = (uint8_t)v;
+						break;
+					}
+				}
+			}
+			buffer.push_back(c);
+		}
+	}
+	/**
+	 * Prints the string to the specified output stream.
+	 */
+	void print(FILE *file)
+	{
+		fwrite(start, length, 1, file);
+	}
+	/**
+	 * Dumps the string to the standard error stream.  Intended to be used
+	 * for debugging.
+	 */
+	void dump()
+	{
+		print(stderr);
+	}
+};
+
+
+
+/**
+ * The dtb namespace contains code related to the generation of device tree
+ * blobs, the binary representation of flattened device trees.  The abstract
+ * tree representation calls into this code to generate the output.
+ */
+namespace dtb
+{
+/** The token types in the DTB, as defined by §7.4.1 of the ePAPR
+ * specification.  All of these values are written in big-endian format in the
+ * output.
+ */
+enum token_type
+{
+	/**
+	 * Marker indicating the start of a node in the tree.  This is followed
+	 * by the nul-terminated name.  If a unit address is specified, then
+	 * the name also contains the address, with an @ symbol between the end
+	 * of the name and the start of the address.
+	 *
+	 * The name is then padded such that the next token begins on a 4-byte
+	 * boundary.  The node may contain properties, other nodes, both, or be
+	 * empty.
+	 */
+	FDT_BEGIN_NODE = 0x00000001,
+	/**
+	 * Marker indicating the end of a node.  
+	 */
+	FDT_END_NODE   = 0x00000002,
+	/**
+	 * The start of a property.  This is followed by two 32-bit big-endian
+	 * values.  The first indicates the length of the property value, the
+	 * second its index in the strings table.  It is then followed by the
+	 * property value, if the value is of non-zero length.
+	 */
+	FDT_PROP       = 0x00000003,
+	/**
+	 * Ignored token.  May be used for padding inside DTB nodes.
+	 */
+	FDT_NOP        = 0x00000004,
+	/**
+	 * Marker indicating the end of the tree.
+	 */
+	FDT_END        = 0x00000009
+};
+
+/**
+ * Returns the token as a string.  This is used for debugging and for printing
+ * human-friendly error messages about malformed DTB input.
+ */
+const char *token_type_name(token_type t)
+{
+	switch(t)
+	{
+		case FDT_BEGIN_NODE:
+			return "FDT_BEGIN_NODE";
+		case FDT_END_NODE:
+			return "FDT_END_NODE";
+		case FDT_PROP:
+			return "FDT_PROP";
+		case FDT_NOP:
+			return "FDT_NOP";
+		case FDT_END:
+			return "FDT_END";
+	}
+	assert(0);
+}
+
+/**
+ * Abstract class for writing a section of the output.  We create one
+ * of these for each section that needs to be written.  It is intended to build
+ * a temporary buffer of the output in memory and then write it to a file
+ * stream.  The size can be returned after all of the data has been written
+ * into the internal buffer, so the sizes of the three tables can be calculated
+ * before storing them in the buffer.
+ */
+struct output_writer
+{
+	/**
+	 * Writes a label into the output stream.  This is only applicable for
+	 * assembly output, where the labels become symbols that can be
+	 * resolved at link time.
+	 */
+	virtual void write_label(string name)   = 0;
+	/**
+	 * Writes a comment into the output stream.  Useful only when debugging
+	 * the output.
+	 */
+	virtual void write_comment(string name) = 0;
+	/**
+	 * Writes a string.  A nul terminator is implicitly added.
+	 */
+	virtual void write_string(string name)  = 0;
+	/**
+	 * Writes a single 8-bit value.
+	 */
+	virtual void write_data(uint8_t)        = 0;
+	/**
+	 * Writes a single 32-bit value.  The value is written in big-endian
+	 * format, but should be passed in the host's native endian.
+	 */
+	virtual void write_data(uint32_t)       = 0;
+	/**
+	 * Writes a single 64-bit value.  The value is written in big-endian
+	 * format, but should be passed in the host's native endian.
+	 */
+	virtual void write_data(uint64_t)       = 0;
+	/**
+	 * Writes the collected output to the specified file descriptor.
+	 */
+	virtual void write_to_file(int fd)      = 0;
+	/**
+	 * Returns the number of bytes.
+	 */
+	virtual uint32_t size()                 = 0;
+	/**
+	 * Helper for writing tokens to the output stream.  This writes a
+	 * comment above the token describing its value, for easier debugging
+	 * of the output.
+	 */
+	void write_token(token_type t)
+	{
+		write_comment(token_type_name(t));
+		write_data((uint32_t)t);
+	}
+	/**
+	 * Helper function that writes a byte buffer to the output, one byte at
+	 * a time.
+	 */
+	void write_data(byte_buffer b)
+	{
+		for (byte_buffer::iterator i=b.begin(), e=b.end(); i!=e ; i++)
+		{
+			write_data(*i);
+		}
+	}
+};
+
+/**
+ * Binary file writer.  This class is responsible for writing the DTB output
+ * directly in blob format.
+ */
+class binary_writer : public output_writer
+{
+	/**
+	 * The internal buffer used to store the blob while it is being
+	 * constructed.
+	 */
+	byte_buffer buffer;
+	public:
+	/**
+	 *  The binary format does not support labels, so this method
+	 * does nothing.
+	 */
+	virtual void write_label(string name) {}
+	/**
+	 * Comments are ignored by the binary writer.

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-soc-all mailing list