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