devtools/src/jehanne/cmd/usyscalls/usyscalls.go

273 lines
6.1 KiB
Go

/*
* This file is part of Jehanne.
*
* Copyright (C) 2016-2019 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* Jehanne 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"strings"
"text/template"
)
const gplHeader string = `/*
* This file is part of Jehanne.
*
* Copyright (C) 2016-2019 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* Jehanne 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
/* automatically generated by usyscalls */
`
type SyscallConf struct {
Ret []string
Args []string
Name string
Id uint32
}
type Sysconf struct {
Syscalls []SyscallConf
}
type SyscallWrapper struct {
Id uint32
Name string
FuncArgs string
MacroArgs string
VarValues []string
Vars []string
AsmArgs string
AsmClobbers string
RetType string
}
type HeaderCode struct {
Wrappers []SyscallWrapper
}
func usage(msg string) {
fmt.Fprint(os.Stderr, msg)
fmt.Fprint(os.Stderr, "Usage: usyscalls header|code path/to/sysconf.json\n")
flag.PrintDefaults()
os.Exit(1)
}
func argTypeName(t string) string{
switch(t){
case "int", "int32_t":
return "i"
case "unsigned int", "uint32_t":
/* unsigned int is reserved for flags */
return "ui"
case "long", "int64_t":
return "l"
case "unsigned long", "uint64_t":
return "ul"
case "void*", "uint8_t*", "const void*", "const uint8_t*":
return "p"
case "int32_t*", "int*", "const int32_t*", "const int*":
return "p"
case "const char*", "char*":
return "p"
case "const char**", "char**":
return "p"
}
return " [?? " + t + "]"
}
func argRegister(index int, t string) string{
prefix := ""
switch(t){
case "int", "unsigned int", "uint32_t", "int32_t":
prefix = "e"
default:
prefix = "r"
}
switch(index){
case 0:
return prefix + "di";
case 1:
return prefix + "si";
case 2:
return prefix + "dx";
case 3:
return "r10";
case 4:
return "r8";
case 5:
return "r9";
}
return ""
}
func getHeaderData(calls []SyscallConf) *HeaderCode {
code := new(HeaderCode)
for _, call := range(calls) {
wcall := new(SyscallWrapper)
wcall.Id = call.Id
wcall.Name = call.Name
wcall.RetType = call.Ret[0]
clobberMemory := false
wcall.AsmClobbers = "\"cc\", \"rcx\", \"r11\""
wcall.AsmArgs = fmt.Sprintf("\"0\"(%d)", wcall.Id)
for i, a := range(call.Args){
typeName := argTypeName(a)
if i > 0 {
wcall.FuncArgs += ", "
wcall.MacroArgs += ", "
}
if strings.HasSuffix(a, "*") && !strings.HasPrefix(a, "const"){
clobberMemory = true
}
wcall.FuncArgs += fmt.Sprintf("%s a%d", a, i)
wcall.MacroArgs += fmt.Sprintf("/* %s */ a%d", a, i)
if typeName == "p" {
wcall.VarValues = append(wcall.VarValues, fmt.Sprintf("_sysargs[%d].%s = ((volatile void*)(a%d)); \\\n\t", i, typeName, i))
} else {
wcall.VarValues = append(wcall.VarValues, fmt.Sprintf("_sysargs[%d].%s = (a%d); \\\n\t", i, typeName, i))
}
wcall.Vars = append(wcall.Vars, fmt.Sprintf("register %s __r%d asm(\"%s\") = _sysargs[%d].%s; \\\n\t", a, i, argRegister(i, a), i, argTypeName(a)))
wcall.AsmArgs += fmt.Sprintf(", \"r\"(__r%d)", i)
}
if clobberMemory {
wcall.AsmClobbers += ", \"memory\""
}
code.Wrappers = append(code.Wrappers, *wcall)
}
return code
}
func generateSyscallTable(calls []SyscallConf){
code := getHeaderData(calls)
tmpl, err := template.New("tab.c").Parse(`
{{ range .Wrappers }}"{{ .Name }}", (int(*)()) sys_{{ .Name }},
{{ end }}
`)
err = tmpl.Execute(os.Stdout, code)
if err != nil {
log.Fatal(err)
}
}
func generateLibcCode(calls []SyscallConf){
code := getHeaderData(calls)
tmpl, err := template.New("syscalls.c").Parse(gplHeader + `
#define PORTABLE_SYSCALLS
#include <u.h>
{{ range .Wrappers }}
#pragma weak sys_{{ .Name }}
{{ .RetType }}
sys_{{ .Name }}({{ .FuncArgs }})
{
register {{ .RetType }} __ret asm ("rax");
__asm__ __volatile__ (
"movq %%rcx, %%r10" "\n\t"
"movq ${{ .Id }}, %%rax" "\n\t"
"syscall"
: "=r" (__ret)
: /* args are ready */
: {{ .AsmClobbers }}
);
return __ret;
}
{{ end }}
`)
err = tmpl.Execute(os.Stdout, code)
if err != nil {
log.Fatal(err)
}
}
func generateSyscallHeader(calls []SyscallConf){
funcMap := template.FuncMap{
"title": strings.Title,
}
code := getHeaderData(calls)
tmpl, err := template.New("syscall.h").Funcs(funcMap).Parse(gplHeader + `
typedef enum Syscalls
{
{{ range .Wrappers }} Sys{{ .Name|title }} = {{ .Id }},
{{ end }}} Syscalls;
#ifndef KERNEL
{{ range .Wrappers }}extern {{ .RetType }} sys_{{ .Name }}({{ .FuncArgs }});
{{ end }}
#endif
`)
err = tmpl.Execute(os.Stdout, code)
if err != nil {
log.Fatal(err)
}
}
func main() {
flag.Parse()
if flag.NArg() != 2 {
usage("no path to sysconf.json")
}
buf, err := ioutil.ReadFile(flag.Arg(1))
if err != nil {
log.Fatal(err)
}
var sysconf Sysconf
err = json.Unmarshal(buf, &sysconf)
if err != nil {
log.Fatal(err)
}
mode := flag.Arg(0)
switch(mode){
case "header":
generateSyscallHeader(sysconf.Syscalls)
break
case "code":
generateLibcCode(sysconf.Syscalls)
break
case "tab":
generateSyscallTable(sysconf.Syscalls)
break
}
}