1/*
2 * Copyright 2004-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <cstdio>
8#include <cstdlib>
9#include <fstream>
10#include <list>
11#include <stack>
12#include <string>
13#include <string.h>
14#include <vector>
15
16#include "gensyscalls_common.h"
17
18#include "arch_gensyscalls.h"
19	// for the alignment type macros (only for the type names)
20
21#ifndef SYSCALL_LONG_PARAMETER_ALIGNMENT_TYPE
22#define SYSCALL_LONG_PARAMETER_ALIGNMENT_TYPE SYSCALL_PARAMETER_ALIGNMENT_TYPE
23#endif
24
25
26// macro trickery to create a string literal
27#define MAKE_STRING(x)	#x
28#define EVAL_MACRO(macro, x) macro(x)
29
30
31const char* kUsage =
32	"Usage: gensyscallinfos <header> <syscall infos> <syscall types sizes>\n"
33	"\n"
34	"Given the (preprocessed) header file that defines the syscall prototypes "
35		"the\n"
36	"command generates a source file consisting of syscall infos, which is "
37		"needed\n"
38	"to build gensyscalls, which in turn generates the assembly syscall\n"
39	"definitions and code for the kernelland syscall dispatcher.\n"
40	"\n"
41	"  <header>               - Input: The preprocessed header file with the\n"
42	"                           syscall prototypes.\n"
43	"  <syscall infos>        - Output: The syscall infos source file needed "
44		"to\n"
45	"                           build gensyscalls.\n"
46	"  <syscall types sizes>  - Output: A source file that will by another "
47		"build\n"
48	"                           step turned into a header file included by\n"
49	"                           <syscall infos>.\n";
50
51
52static void
53print_usage(bool error)
54{
55	fputs(kUsage, (error ? stderr : stdout));
56}
57
58
59struct Type {
60	Type(const char* type) : type(type) {}
61	Type(const string& type) : type(type) {}
62
63	string type;
64};
65
66
67struct NamedType : Type {
68	NamedType(const char* type, const char* name)
69		: Type(type), name(name) {}
70	NamedType(const string& type, const string& name)
71		: Type(type), name(name) {}
72
73	string name;
74};
75
76
77class Function {
78public:
79	Function() : fReturnType("") {}
80
81	void SetName(const string& name)
82	{
83		fName = name;
84	}
85
86	const string& GetName() const
87	{
88		return fName;
89	}
90
91	void AddParameter(const NamedType& type)
92	{
93		fParameters.push_back(type);
94	}
95
96	int CountParameters() const
97	{
98		return fParameters.size();
99	}
100
101	const NamedType& ParameterAt(int index) const
102	{
103		return fParameters[index];
104	}
105
106	void SetReturnType(const Type& type)
107	{
108		fReturnType = type;
109	}
110
111	const Type& GetReturnType() const
112	{
113		return fReturnType;
114	}
115
116protected:
117	string				fName;
118	vector<NamedType>	fParameters;
119	Type				fReturnType;
120};
121
122
123class Syscall : public Function {
124public:
125	string GetKernelName() const
126	{
127		int baseIndex = 0;
128		if (fName.find("_kern_") == 0)
129			baseIndex = strlen("_kern_");
130		else if (fName.find("sys_") == 0)
131			baseIndex = strlen("sys_");
132		string kernelName("_user_");
133		kernelName.append(string(fName, baseIndex));
134		return kernelName;
135	}
136};
137
138
139class Tokenizer {
140public:
141	Tokenizer(istream& input)
142		: fInput(input),
143		  fHasCurrent(false)
144	{
145	}
146
147	string GetCurrentToken()
148	{
149		if (!fHasCurrent)
150			throw Exception("GetCurrentToken(): No current token!");
151		return fTokens.front();
152	}
153
154	string GetNextToken()
155	{
156		return GetNextToken(NULL);
157	}
158
159	string GetNextToken(stack<string>& skippedTokens)
160	{
161		return GetNextToken(&skippedTokens);
162	}
163
164	string GetNextToken(stack<string>* skippedTokens)
165	{
166		if (fHasCurrent) {
167			fTokens.pop_front();
168			fHasCurrent = false;
169		}
170		while (fTokens.empty())
171			_ReadLine();
172		fHasCurrent = true;
173		if (skippedTokens)
174			skippedTokens->push(fTokens.front());
175		return fTokens.front();
176	}
177
178	void ExpectToken(const string& expectedToken)
179	{
180		string token = GetCurrentToken();
181		if (expectedToken != token) {
182			throw ParseException(string("Unexpected token `") + token
183				+ "'. Expected was `" + expectedToken + "'.");
184		}
185	}
186
187	void ExpectNextToken(const string& expectedToken)
188	{
189		GetNextToken();
190		ExpectToken(expectedToken);
191	}
192
193	bool CheckToken(const string& expectedToken)
194	{
195		string token = GetCurrentToken();
196		return (expectedToken == token);
197	}
198
199	bool CheckNextToken(const string& expectedToken)
200	{
201		GetNextToken();
202		bool result = CheckToken(expectedToken);
203		if (!result)
204			PutCurrentToken();
205		return result;
206	}
207
208	void PutCurrentToken()
209	{
210		if (!fHasCurrent)
211			throw Exception("GetCurrentToken(): No current token!");
212		fHasCurrent = false;
213	}
214
215	void PutToken(string token)
216	{
217		if (fHasCurrent) {
218			fTokens.pop_front();
219			fHasCurrent = false;
220		}
221		fTokens.push_front(token);
222	}
223
224	void PutTokens(stack<string>& tokens)
225	{
226		if (fHasCurrent) {
227			fTokens.pop_front();
228			fHasCurrent = false;
229		}
230		while (!tokens.empty()) {
231			fTokens.push_front(tokens.top());
232			tokens.pop();
233		}
234	}
235
236private:
237	void _ReadLine()
238	{
239		// read the line
240		char buffer[10240];
241		if (!fInput.getline(buffer, sizeof(buffer)))
242			throw EOFException("Unexpected end of input.");
243		// parse it
244		vector<string> line;
245		int len = strlen(buffer);
246		int tokenStart = 0;
247		for (int i = 0; i < len; i++) {
248			char c = buffer[i];
249			if (isspace(c)) {
250				if (tokenStart < i)
251					line.push_back(string(buffer + tokenStart, buffer + i));
252				tokenStart = i + 1;
253				continue;
254			}
255			switch (buffer[i]) {
256				case '#':
257				case '(':
258				case ')':
259				case '*':
260				case '&':
261				case '[':
262				case ']':
263				case ';':
264				case ',':
265					if (tokenStart < i) {
266						line.push_back(string(buffer + tokenStart,
267												 buffer + i));
268					}
269					line.push_back(string(buffer + i, buffer + i + 1));
270					tokenStart = i + 1;
271					break;
272			}
273		}
274		if (tokenStart < len)
275			line.push_back(string(buffer + tokenStart, buffer + len));
276		// drop the line, if it starts with "# <number>", as those are
277		// directions from the pre-processor to the compiler
278		if (line.size() >= 2) {
279			if (line[0] == "#" && isdigit(line[1][0]))
280				return;
281		}
282		for (int i = 0; i < (int)line.size(); i++)
283			fTokens.push_back(line[i]);
284	}
285
286private:
287	istream&		fInput;
288	list<string>	fTokens;
289	bool			fHasCurrent;
290};
291
292
293class Main {
294public:
295
296	int Run(int argc, char** argv)
297	{
298		// parse parameters
299		if (argc >= 2
300			&& (string(argv[1]) == "-h" || string(argv[1]) == "--help")) {
301			print_usage(false);
302			return 0;
303		}
304		if (argc != 4) {
305			print_usage(true);
306			return 1;
307		}
308		_ParseSyscalls(argv[1]);
309		_WriteSyscallInfoFile(argv[2]);
310		_WriteSyscallTypeSizes(argv[3]);
311		return 0;
312	}
313
314private:
315	void _ParseSyscalls(const char* filename)
316	{
317		// open the input file
318		ifstream file(filename, ifstream::in);
319		if (!file.is_open())
320			throw IOException(string("Failed to open `") + filename + "'.");
321		// parse the syscalls
322		Tokenizer tokenizer(file);
323		// find "#pragma syscalls begin"
324		while (true) {
325			while (tokenizer.GetNextToken() != "#");
326			stack<string> skippedTokens;
327			if (tokenizer.GetNextToken(skippedTokens) == "pragma"
328				&& tokenizer.GetNextToken(skippedTokens) == "syscalls"
329				&& tokenizer.GetNextToken(skippedTokens) == "begin") {
330				break;
331			}
332			tokenizer.PutTokens(skippedTokens);
333		}
334		// parse the functions
335		while (!tokenizer.CheckNextToken("#")) {
336			Syscall syscall;
337			_ParseSyscall(tokenizer, syscall);
338			fSyscalls.push_back(syscall);
339		}
340		// expect "pragma syscalls end"
341		tokenizer.ExpectNextToken("pragma");
342		tokenizer.ExpectNextToken("syscalls");
343		tokenizer.ExpectNextToken("end");
344	}
345
346	void _WriteSyscallInfoFile(const char* filename)
347	{
348		// open the syscall info file
349		ofstream file(filename, ofstream::out | ofstream::trunc);
350		if (!file.is_open())
351			throw IOException(string("Failed to open `") + filename + "'.");
352
353		// write preamble
354		file << "#include \"gensyscalls.h\"" << endl;
355		file << "#include \"syscall_types_sizes.h\"" << endl;
356		file << endl;
357
358		file << "const char* const kReturnTypeAlignmentType = \""
359			EVAL_MACRO(MAKE_STRING, SYSCALL_RETURN_TYPE_ALIGNMENT_TYPE)
360			<< "\";" << endl;
361		file << "const char* const kParameterAlignmentType = \""
362			EVAL_MACRO(MAKE_STRING, SYSCALL_PARAMETER_ALIGNMENT_TYPE)
363			<< "\";" << endl;
364		file << "const char* const kLongParameterAlignmentType = \""
365			EVAL_MACRO(MAKE_STRING, SYSCALL_LONG_PARAMETER_ALIGNMENT_TYPE)
366			<< "\";" << endl;
367		file << "const int kReturnTypeAlignmentSize = "
368			"SYSCALL_RETURN_TYPE_ALIGNMENT_SIZE;" << endl;
369		file << "const int kParameterAlignmentSize = "
370			"SYSCALL_PARAMETER_ALIGNMENT_SIZE;" << endl;
371		file << "const int kLongParameterAlignmentSize = "
372			"SYSCALL_LONG_PARAMETER_ALIGNMENT_SIZE;" << endl;
373		file << endl;
374
375		file << "SyscallVector* create_syscall_vector() {" << endl;
376		file << "\tSyscallVector* syscallVector = SyscallVector::Create();"
377			<< endl;
378		file << "\tSyscall* syscall;" << endl;
379
380		// syscalls
381		for (int i = 0; i < (int)fSyscalls.size(); i++) {
382			const Syscall& syscall = fSyscalls[i];
383
384			// syscall = syscallVector->CreateSyscall("syscallName",
385			//	"syscallKernelName");
386			file << "\tsyscall = syscallVector->CreateSyscall(\""
387				<< syscall.GetName() << "\", \""
388				<< syscall.GetKernelName() << "\");" << endl;
389
390			const Type& returnType = syscall.GetReturnType();
391
392			// syscall->SetReturnType<(SYSCALL_RETURN_TYPE_SIZE_<i>,
393			//		"returnType");
394			file << "\tsyscall->SetReturnType("
395				<< "SYSCALL_RETURN_TYPE_SIZE_" << i << ", \""
396				<< returnType.type << "\");" << endl;
397
398			// parameters
399			int paramCount = syscall.CountParameters();
400			for (int k = 0; k < paramCount; k++) {
401				const NamedType& param = syscall.ParameterAt(k);
402				// syscall->AddParameter(SYSCALL_PARAMETER_SIZE_<i>_<k>,
403				//		"parameterTypeName", "parameterName");
404				file << "\tsyscall->AddParameter("
405					<< "SYSCALL_PARAMETER_SIZE_" << i << "_" << k
406					<< ", \"" << param.type << "\", \"" << param.name << "\");"
407					<< endl;
408			}
409			file << endl;
410		}
411
412		// postamble
413		file << "\treturn syscallVector;" << endl;
414		file << "}" << endl;
415	}
416
417	void _WriteSyscallTypeSizes(const char* filename)
418	{
419		// open the syscall info file
420		ofstream file(filename, ofstream::out | ofstream::trunc);
421		if (!file.is_open())
422			throw IOException(string("Failed to open `") + filename + "'.");
423
424		// write preamble
425		file << "#include <computed_asm_macros.h>" << endl;
426		file << "#include <syscalls.h>" << endl;
427		file << endl;
428		file << "#include \"arch_gensyscalls.h\"" << endl;
429		file << endl;
430		file << "#ifndef SYSCALL_LONG_PARAMETER_ALIGNMENT_TYPE" << endl;
431		file << "#define SYSCALL_LONG_PARAMETER_ALIGNMENT_TYPE SYSCALL_PARAMETER_ALIGNMENT_TYPE"
432			<< endl;
433		file << "#endif" << endl;
434		file << "void dummy() {" << endl;
435
436		file << "DEFINE_COMPUTED_ASM_MACRO(SYSCALL_RETURN_TYPE_ALIGNMENT_SIZE, "
437			"sizeof(SYSCALL_RETURN_TYPE_ALIGNMENT_TYPE));" << endl;
438  		file << "DEFINE_COMPUTED_ASM_MACRO(SYSCALL_PARAMETER_ALIGNMENT_SIZE, "
439			"sizeof(SYSCALL_PARAMETER_ALIGNMENT_TYPE));" << endl;
440		file << "DEFINE_COMPUTED_ASM_MACRO(SYSCALL_LONG_PARAMETER_ALIGNMENT_SIZE, "
441			"sizeof(SYSCALL_LONG_PARAMETER_ALIGNMENT_TYPE));" << endl;
442		file << endl;
443
444		// syscalls
445		for (int i = 0; i < (int)fSyscalls.size(); i++) {
446			const Syscall& syscall = fSyscalls[i];
447			const Type& returnType = syscall.GetReturnType();
448
449			if (returnType.type == "void") {
450				file << "DEFINE_COMPUTED_ASM_MACRO(SYSCALL_RETURN_TYPE_SIZE_"
451					<< i << ", 0);" << endl;
452			} else {
453				file << "DEFINE_COMPUTED_ASM_MACRO(SYSCALL_RETURN_TYPE_SIZE_"
454					<< i << ", sizeof(" << returnType.type << "));" << endl;
455			}
456
457			// parameters
458			int paramCount = syscall.CountParameters();
459			for (int k = 0; k < paramCount; k++) {
460				const NamedType& param = syscall.ParameterAt(k);
461				file << "DEFINE_COMPUTED_ASM_MACRO(SYSCALL_PARAMETER_SIZE_" << i
462					<< "_" << k << ", sizeof(" << param.type << "));" << endl;
463			}
464			file << endl;
465		}
466
467		// postamble
468		file << "}" << endl;
469	}
470
471	void _ParseSyscall(Tokenizer& tokenizer, Syscall& syscall)
472	{
473		// get return type and function name
474		vector<string> returnType;
475		while (tokenizer.GetNextToken() != "(") {
476			string token = tokenizer.GetCurrentToken();
477			// strip leading "extern"
478			if (!returnType.empty() || token != "extern")
479				returnType.push_back(token);
480		}
481		if (returnType.size() < 2) {
482			throw ParseException("Error while parsing function "
483				"return type.");
484		}
485		syscall.SetName(returnType[returnType.size() - 1]);
486		returnType.pop_back();
487		string returnTypeString(returnType[0]);
488		for (int i = 1; i < (int)returnType.size(); i++) {
489			returnTypeString += " ";
490			returnTypeString += returnType[i];
491		}
492		syscall.SetReturnType(returnTypeString);
493		// get arguments
494		if (!tokenizer.CheckNextToken(")")) {
495			_ParseParameter(tokenizer, syscall);
496			while (!tokenizer.CheckNextToken(")")) {
497				tokenizer.ExpectNextToken(",");
498				_ParseParameter(tokenizer, syscall);
499			}
500		}
501		tokenizer.ExpectNextToken(";");
502	}
503
504	void _ParseParameter(Tokenizer& tokenizer, Syscall& syscall)
505	{
506		vector<string> type;
507		while (tokenizer.GetNextToken() != ")"
508			&& tokenizer.GetCurrentToken() != ",") {
509			string token = tokenizer.GetCurrentToken();
510			type.push_back(token);
511			if (token == "(") {
512				// This must be a function pointer. We deal with that in a
513				// separate method.
514				_ParseFunctionPointerParameter(tokenizer, syscall, type);
515				return;
516			}
517		}
518		tokenizer.PutCurrentToken();
519		if (type.size() < 2) {
520			if (type.size() == 1 && type[0] == "void") {
521				// that's OK
522				return;
523			}
524			throw ParseException("Error while parsing function parameter.");
525		}
526
527		// last component is the parameter name
528		string typeName = type.back();
529		type.pop_back();
530
531		string typeString(type[0]);
532		for (int i = 1; i < (int)type.size(); i++) {
533			typeString += " ";
534			typeString += type[i];
535		}
536		syscall.AddParameter(NamedType(typeString, typeName));
537	}
538
539	void _ParseFunctionPointerParameter(Tokenizer& tokenizer, Syscall& syscall,
540		vector<string>& type)
541	{
542		// When this method is entered, the return type and the opening
543		// parenthesis must already be parse and stored in the supplied type
544		// vector.
545		if (type.size() < 2) {
546			throw ParseException("Error parsing function parameter. "
547				"No return type.");
548		}
549		// read all the "*"s there are
550		while (tokenizer.CheckNextToken("*"))
551			type.push_back("*");
552		// now comes the parameter name, if specified -- skip it
553		string typeName;
554		if (!tokenizer.CheckNextToken(")")) {
555			typeName = tokenizer.GetNextToken();
556			tokenizer.ExpectNextToken(")");
557		} else {
558			throw ParseException("Error while parsing function parameter. "
559				"No parameter name.");
560		}
561		type.push_back(")");
562		// the function parameters
563		tokenizer.ExpectNextToken("(");
564		type.push_back("(");
565		while (!tokenizer.CheckNextToken(")"))
566			type.push_back(tokenizer.GetNextToken());
567		type.push_back(")");
568		// compose the type string and add it to the syscall parameters
569		string typeString(type[0]);
570		for (int i = 1; i < (int)type.size(); i++) {
571			typeString += " ";
572			typeString += type[i];
573		}
574		syscall.AddParameter(NamedType(typeString, typeName));
575	}
576
577private:
578	vector<Syscall>	fSyscalls;
579};
580
581
582int
583main(int argc, char** argv)
584{
585	try {
586		return Main().Run(argc, argv);
587	} catch (const Exception& exception) {
588		fprintf(stderr, "%s\n", exception.what());
589		return 1;
590	}
591}
592