new development tools

This commit is contained in:
2016-11-25 13:41:57 +00:00
parent 6cc262b43b
commit 1ba806ca1b
42 changed files with 2346 additions and 44 deletions

View File

@ -29,6 +29,7 @@ import (
"path"
"path/filepath"
"regexp"
"sort"
"strings"
)
@ -43,6 +44,7 @@ type kernconfig struct {
}
type kernel struct {
CodeFile string
Systab string
Config kernconfig
Ramfiles map[string]string
@ -89,9 +91,9 @@ func (bf *buildfile) UnmarshalJSON(s []byte) error {
b.Projects = adjust(b.Projects)
b.Libs = adjust(b.Libs)
b.Cflags = adjust(b.Cflags)
b.SourceFiles = adjust(b.SourceFiles)
b.SourceFilesCmd = adjust(b.SourceFilesCmd)
b.ObjectFiles = adjust(b.ObjectFiles)
b.SourceFiles = b.SourceFiles
b.SourceFilesCmd = b.SourceFilesCmd
b.ObjectFiles = b.ObjectFiles
b.Include = adjust(b.Include)
b.Install = fromRoot(b.Install)
for i, e := range b.Env {
@ -138,6 +140,15 @@ func failOn(err error) {
}
}
func isValueInList(value string, list []string) bool {
for _, v := range list {
if v == value {
return true
}
}
return false
}
func adjust(s []string) []string {
for i, v := range s {
s[i] = fromRoot(v)
@ -145,6 +156,18 @@ func adjust(s []string) []string {
return s
}
func buildEnv(b *build) func(string) string{
return func(v string) string {
search := v + "="
for _, s := range b.Env {
if strings.Index(s, search) == 0 {
return strings.Replace(s, search, "", 1)
}
}
return os.Getenv(v)
}
}
// return the given absolute path as an absolute path rooted at the jehanne tree.
func fromRoot(p string) string {
p = os.ExpandEnv(p)
@ -179,6 +202,40 @@ func sh(cmd *exec.Cmd) {
failOn(shell.Run())
}
func mergeKernel(k *kernel, defaults *kernel) *kernel {
if k == nil {
return defaults
}
if defaults == nil {
return k
}
// The custom kernel Code will be added after the default from includes
// so that it has a chance to change de default behaviour.
k.Config.Code = append(defaults.Config.Code, k.Config.Code...)
k.Config.Dev = append(k.Config.Dev, defaults.Config.Dev...)
k.Config.Ip = append(k.Config.Ip, defaults.Config.Ip...)
k.Config.Link = append(k.Config.Link, defaults.Config.Link...)
k.Config.Sd = append(k.Config.Sd, defaults.Config.Sd...)
k.Config.Uart = append(k.Config.Uart, defaults.Config.Uart...)
k.Config.VGA = append(k.Config.VGA, defaults.Config.VGA...)
if k.CodeFile == "" {
k.CodeFile = defaults.CodeFile
}
if k.Systab == "" {
k.Systab = defaults.Systab
}
for name, path := range defaults.Ramfiles {
if _, ok := k.Ramfiles[name]; ok == false {
k.Ramfiles[name] = path
}
}
return k
}
func include(f string, b *build) {
if b.jsons[f] {
return
@ -203,6 +260,7 @@ func include(f string, b *build) {
b.SourceFilesCmd = append(b.SourceFilesCmd, build.SourceFilesCmd...)
b.Program += build.Program
b.Library += build.Library
b.Kernel = mergeKernel(b.Kernel, build.Kernel)
if build.Install != "" {
if b.Install != "" {
log.Fatalf("In file %s (target %s) included by %s (target %s): redefined Install.", f, n, build.path, build.name)
@ -244,7 +302,16 @@ func process(f string, r []*regexp.Regexp) []build {
d, err := ioutil.ReadFile(f)
failOn(err)
failOn(json.Unmarshal(d, &builds))
for n, build := range builds {
// Sort keys alphabetically (GoLang does not preserve the JSON order)
var keys []string
for n := range builds {
keys = append(keys, n)
}
sort.Strings(keys)
for _, n := range keys {
build := builds[n]
build.name = n
build.jsons = make(map[string]bool)
skip := true
@ -281,8 +348,15 @@ func buildkernel(b *build) {
if b.Kernel == nil {
return
}
envFunc := buildEnv(b)
for name, path := range b.Kernel.Ramfiles {
b.Kernel.Ramfiles[name] = os.Expand(path, envFunc);
}
codebuf := confcode(b.path, b.Kernel)
failOn(ioutil.WriteFile(b.name+".c", codebuf, 0666))
if b.Kernel.CodeFile == "" {
log.Fatalf("Missing Kernel.CodeFile in %v\n", b.path)
}
failOn(ioutil.WriteFile(b.Kernel.CodeFile, codebuf, 0666))
}
func wrapInQuote(args []string) []string {
@ -297,24 +371,57 @@ func wrapInQuote(args []string) []string {
return res
}
func convertLibPathsToArgs(b *build) []string {
libLocations := make([]string, 0)
args := make([]string, 0)
defaultLibLocation := fromRoot("/arch/$ARCH/lib")
for _, lib := range b.Libs {
ldir := filepath.Dir(lib)
if ldir != defaultLibLocation {
if !isValueInList(ldir, libLocations) {
libLocations = append(libLocations, ldir)
args = append(args, "-L", ldir)
}
}
lib = strings.Replace(lib, ldir + "/lib", "-l", 1)
lib = strings.Replace(lib, ".a", "", 1)
args = append(args, lib)
}
return args
}
func compile(b *build) {
log.Printf("Building %s\n", b.name)
// N.B. Plan 9 has a very well defined include structure, just three things:
// /amd64/include, /sys/include, .
args := b.Cflags
args := b.SourceFiles
args = append(args, b.Cflags...)
if !isValueInList("-c", b.Cflags) {
args = append(args, convertLibPathsToArgs(b)...)
args = append(args, b.Oflags...)
}
if len(b.SourceFilesCmd) > 0 {
for _, i := range b.SourceFilesCmd {
cmd := exec.Command(tools["cc"], append(args, i)...)
largs := make([]string, 3)
largs[0] = i
largs[1] = "-o"
largs[2] = strings.Replace(filepath.Base(i), filepath.Ext(i), "", 1)
cmd := exec.Command(tools["cc"], append(largs, args...)...)
run(b, *shellhack, cmd)
}
return
}
args = append(args, b.SourceFiles...)
if !isValueInList("-c", b.Cflags) {
args = append(args, "-o", b.Program)
}
cmd := exec.Command(tools["cc"], args...)
run(b, *shellhack, cmd)
}
func link(b *build) {
if !isValueInList("-c", b.Cflags) {
return
}
log.Printf("Linking %s\n", b.name)
if len(b.SourceFilesCmd) > 0 {
for _, n := range b.SourceFilesCmd {
@ -329,7 +436,6 @@ func link(b *build) {
o := f[:len(f)] + ".o"
args := []string{"-o", n, o}
args = append(args, b.Oflags...)
args = append(args, "-L", fromRoot("/arch/$ARCH/lib"))
args = append(args, b.Libs...)
run(b, *shellhack, exec.Command(tools["ld"], args...))
}
@ -338,7 +444,6 @@ func link(b *build) {
args := []string{"-o", b.Program}
args = append(args, b.ObjectFiles...)
args = append(args, b.Oflags...)
args = append(args, "-L", fromRoot("/arch/$ARCH/lib"))
args = append(args, b.Libs...)
run(b, *shellhack, exec.Command(tools["ld"], args...))
}
@ -412,12 +517,12 @@ func projects(b *build, r []*regexp.Regexp) {
for _, v := range b.Projects {
f, _ := findBuildfile(v)
log.Printf("Doing %s\n", f)
project(f, r)
project(f, r, b)
}
}
// assumes we are in the wd of the project.
func project(bf string, which []*regexp.Regexp) {
func project(bf string, which []*regexp.Regexp, container *build) {
cwd, err := os.Getwd()
failOn(err)
debug("Start new project cwd is %v", cwd)
@ -430,11 +535,25 @@ func project(bf string, which []*regexp.Regexp) {
debug("Processing %v: %d target", root, len(builds))
for _, b := range builds {
debug("Processing %v: %v", b.name, b)
if container != nil {
b.Env = append(container.Env, b.Env...)
}
projects(&b, regexpAll)
for _, c := range b.Pre {
// this is a hack: we just pass the command through as an exec.Cmd
run(&b, true, exec.Command(c))
}
envFunc := buildEnv(&b);
b.Program = os.Expand(b.Program, envFunc)
for i, s := range b.SourceFiles {
b.SourceFiles[i] = fromRoot(os.Expand(s, envFunc));
}
for i, s := range b.SourceFilesCmd {
b.SourceFilesCmd[i] = fromRoot(os.Expand(s, envFunc));
}
for i, s := range b.ObjectFiles {
b.ObjectFiles[i] = fromRoot(os.Expand(s, envFunc));
}
buildkernel(&b)
if len(b.SourceFiles) > 0 || len(b.SourceFilesCmd) > 0 {
compile(&b)
@ -511,7 +630,7 @@ func main() {
re = append(re, rx)
}
}
project(bf, re)
project(bf, re, nil)
}
func findTools(toolprefix string) {
@ -520,7 +639,10 @@ func findTools(toolprefix string) {
if x := os.Getenv(strings.ToUpper(k)); x != "" {
v = x
}
v, err = exec.LookPath(toolprefix + v)
if v != "sh" {
v = toolprefix + v;
}
v, err = exec.LookPath(v)
failOn(err)
tools[k] = v
}

View File

@ -98,20 +98,6 @@ VGAcur* vgacur[] = {
nil,
};
Physseg physseg[8] = {
{
.attr = SG_SHARED,
.name = "shared",
.size = SEGMAXPG,
},
{
.attr = SG_BSS,
.name = "memory",
.size = SEGMAXPG,
},
};
int nphysseg = 8;
{{ range .Config.Code }}{{ . }}
{{ end }}

View File

@ -0,0 +1,197 @@
package main
import (
"archive/tar"
"bytes"
"compress/bzip2"
"compress/gzip"
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"encoding/json"
"hash"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"os/exec"
"path"
"strings"
)
const (
dirPermissions = 0755
)
type Fetch struct {
Upstream string
Digest map[string]string
Compress string
RemovePrefix bool
Exclude []string
}
func main() {
j, err := ioutil.ReadFile("fetch.json")
if err != nil {
log.Fatal(err)
}
fetches := make(map[string]Fetch)
if err := json.Unmarshal(j, &fetches); err != nil {
log.Fatal(err)
}
for name, f := range(fetches) {
if _, err := os.Stat(name); err == nil {
log.Printf("Fetch: skip %v (already present)", name)
} else {
log.Printf("Fetch: %v from %v", name, f.Upstream)
if err := do(&f, name); err != nil {
log.Fatal(err)
}
}
}
}
func do(f *Fetch, name string) error {
fname := fetch(f)
s, err := os.Open(fname)
if err != nil {
return err
}
defer os.Remove(fname)
os.MkdirAll(name, dirPermissions)
var unZ io.Reader
switch f.Compress {
case "gzip":
unZ, err = gzip.NewReader(s)
if err != nil {
return err
}
case "bzip2":
unZ = bzip2.NewReader(s)
default:
unZ = s
}
ar := tar.NewReader(unZ)
h, err := ar.Next()
untar:
for ; err == nil; h, err = ar.Next() {
n := h.Name
if f.RemovePrefix {
n = strings.SplitN(n, "/", 2)[1]
}
for _, ex := range f.Exclude {
if strings.HasPrefix(n, ex) {
continue untar
}
}
n = path.Join(name, n)
if h.FileInfo().IsDir() {
os.MkdirAll(n, dirPermissions)
continue
}
os.MkdirAll(path.Dir(n), dirPermissions)
out, err := os.Create(n)
if err != nil {
log.Println(err)
continue
}
if n, err := io.Copy(out, ar); n != h.Size || err != nil {
return err
}
out.Close()
if err := os.Chmod(n, h.FileInfo().Mode()); err != nil {
return err;
}
}
if err != io.EOF {
return err
}
return nil
}
type match struct {
hash.Hash
Good []byte
Name string
}
func (m match) OK() bool {
return bytes.Equal(m.Good, m.Hash.Sum(nil))
}
func fetch(v *Fetch) string {
if len(v.Digest) == 0 {
log.Fatal("no checksums specifed")
}
f, err := ioutil.TempFile("", "cmdVendor")
if err != nil {
log.Fatal(err)
}
defer f.Close()
req, err := http.NewRequest("GET", v.Upstream, nil)
if err != nil {
log.Fatal(err)
}
client := &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
DisableCompression: true,
},
}
res, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
var digests []match
for k, v := range v.Digest {
g, err := hex.DecodeString(v)
if err != nil {
log.Fatal(err)
}
switch k {
case "sha224":
digests = append(digests, match{sha256.New224(), g, k})
case "sha256":
digests = append(digests, match{sha256.New(), g, k})
case "sha384":
digests = append(digests, match{sha512.New384(), g, k})
case "sha512":
digests = append(digests, match{sha512.New(), g, k})
}
}
ws := make([]io.Writer, len(digests))
for i := range digests {
ws[i] = digests[i]
}
w := io.MultiWriter(ws...)
if _, err := io.Copy(f, io.TeeReader(res.Body, w)); err != nil {
log.Fatal(err)
}
for _, h := range digests {
if !h.OK() {
log.Fatalf("mismatched %q hash\n\tWanted %x\n\tGot %x\n", h.Name, h.Good, h.Hash.Sum(nil))
}
}
return f.Name()
}
func run(exe string, arg ...string) error {
cmd := exec.Command(exe, arg...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}

View File

@ -0,0 +1,374 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2016 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"
"text/template"
)
type SyscallConf struct {
Ret []string
Args []string
Name string
Id uint32
}
type Sysconf struct {
Syscalls []SyscallConf
}
func usage(msg string) {
fmt.Fprint(os.Stderr, msg)
fmt.Fprint(os.Stderr, "Usage: ksyscalls path/to/sysconf.json\n")
flag.PrintDefaults()
os.Exit(1)
}
type SyscallWrapper struct {
Id uint32
Name string
Vars []string
CommonCode string
ExecCode string
EntryPrint string
ExitPrint string
SysRetField string
DefaultRet string
}
type KernelCode struct {
Externs []string
Wrappers []SyscallWrapper
}
func uregArg(index int) string{
switch(index){
case 0:
return "di";
case 1:
return "si";
case 2:
return "dx";
case 3:
return "r10";
case 4:
return "r8";
case 5:
return "r9";
}
return ""
}
func sysret(t string) string {
switch(t){
case "int", "int32_t":
return "i"
case "long", "int64_t":
return "vl"
case "uintptr_t", "void":
return "p"
case "void*", "char*", "char**", "uint8_t*", "int32_t*", "uint64_t*", "int64_t*":
return "v"
}
return " [?? " + t + "]"
}
func formatArg(i int, t string) string{
switch(t){
case "int", "int32_t":
return fmt.Sprintf("\tfmtprint(fmt, \" %%d\", a%d);\n", i)
case "unsigned int", "uint32_t":
/* unsigned int is reserved for flags */
return fmt.Sprintf("\tfmtprint(fmt, \" %%#ux\", a%d);\n", i)
case "long", "int64_t":
return fmt.Sprintf("\tfmtprint(fmt, \" %%lld\", a%d);\n", i)
case "unsigned long", "uint64_t":
return fmt.Sprintf("\tfmtprint(fmt, \" %%#lud\", a%d);\n", i)
case "void*", "uint8_t*", "const void*", "const uint8_t*":
return fmt.Sprintf("\tfmtprint(fmt, \" %%#p\", a%d);\n", i)
case "int32_t*", "int*", "const int32_t*", "const int*":
return fmt.Sprintf("\tfmtprint(fmt, \" %%#p(%%d)\", a%d, a%d);\n", i, i)
case "const char*", "char*":
return fmt.Sprintf("\tfmtuserstring(fmt, a%d);\n", i)
case "const char**", "char**":
return fmt.Sprintf("\tfmtuserstringlist(fmt, a%d);\n", i);
}
return " [?? " + t + "]"
}
func formatRet(t string) string{
switch(t){
case "int", "int32_t":
return fmt.Sprintf("\tfmtprint(fmt, \" %%d\", ret->%s);\n", sysret(t))
case "unsigned int", "uint32_t":
/* unsigned int is reserved for flags */
return fmt.Sprintf("\tfmtprint(fmt, \" %%#ux\", ret->%s);\n", sysret(t))
case "long", "int64_t":
return fmt.Sprintf("\tfmtprint(fmt, \" %%lld\", ret->%s);\n", sysret(t))
case "unsigned long", "uint64_t", "void":
return fmt.Sprintf("\tfmtprint(fmt, \" %%#llud\", ret->%s);\n", sysret(t))
case "void*", "uintptr_t", "const void*", "const uintptr_t":
return fmt.Sprintf("\tfmtprint(fmt, \" %%#p\", ret->%s);\n", sysret(t))
case "int32_t*", "int*", "const int32_t*", "const int*":
return fmt.Sprintf("\tfmtprint(fmt, \" %%#p(%%d)\", ret->%s, *ret->%s);\n", sysret(t), sysret(t))
}
return " [?? " + t + "]"
}
func generateKernelCode(calls []SyscallConf){
code := new(KernelCode)
for _, call := range(calls) {
/* extern definitions */
ext := "extern " + call.Ret[0] + " sys" + call.Name + "("
if len(call.Args) == 0 {
ext += "void";
} else {
for i, a := range(call.Args){
if i > 0 {
ext += ", "
}
ext += fmt.Sprintf("%s", a)
}
}
ext += ");\n"
wcall := new(SyscallWrapper)
wcall.Id = call.Id
wcall.Name = call.Name
wcall.SysRetField = sysret(call.Ret[0])
wcall.DefaultRet = fmt.Sprintf("ret.%s = (%s)-1;", wcall.SysRetField, call.Ret[0])
for i, a := range(call.Args){
wcall.Vars = append(wcall.Vars, fmt.Sprintf("%s a%v;", a, i))
wcall.CommonCode += fmt.Sprintf("\ta%v = (%s)ureg->%s;\n", i, a, uregArg(i))
}
wcall.ExecCode += "\tret->" + wcall.SysRetField + " = sys" + call.Name + "("
for i, _ := range(call.Args){
if i > 0 {
wcall.ExecCode += ", "
}
wcall.ExecCode += fmt.Sprintf("a%v", i)
}
wcall.ExecCode += ");"
if call.Name == "pwrite"{
wcall.EntryPrint += formatArg(0, call.Args[0])
wcall.EntryPrint += "\tfmtrwdata(fmt, (char*)a1, MIN(a2, 64));\n"
wcall.EntryPrint += formatArg(2, call.Args[2])
wcall.EntryPrint += formatArg(3, call.Args[3])
} else {
for i, a := range(call.Args){
wcall.EntryPrint += formatArg(i, a)
}
}
wcall.ExitPrint += formatRet(call.Ret[0])
if call.Name == "pread"{
wcall.ExitPrint += fmt.Sprintf("\tfmtrwdata(fmt, (char*)ureg->%s, MIN(ureg->%s, 64));\n", uregArg(1), uregArg(2))
}
code.Wrappers = append(code.Wrappers, *wcall)
code.Externs = append(code.Externs, ext)
}
tmpl, err := template.New("systab.c").Parse(`/*
* This file is part of Jehanne.
*
* Copyright (C) 2016 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 ksyscalls */
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "ureg.h"
extern void fmtrwdata(Fmt* f, char* a, int n);
extern void fmtuserstring(Fmt* f, const char* a);
extern void fmtuserstringlist(Fmt* f, const char** argv);
{{ range .Externs }}{{.}}{{ end }}
{{ range .Wrappers }}
static void
wrap_{{ .Name }}(ScRet* ret, Ureg* ureg)
{
{{ range .Vars }}{{.}}
{{ end }}
{{ .CommonCode }}
{{ .ExecCode }}
}
{{ end }}
int nsyscall = {{len .Wrappers}};
ScRet
default_syscall_ret(int syscall)
{
static ScRet zero;
ScRet ret = zero;
switch(syscall){
{{ range .Wrappers }}case {{ .Id }}:
{{ .DefaultRet }}
break;
{{ end }}
default:
ret.vl = -1;
break;
}
return ret;
}
char*
syscall_name(int syscall)
{
switch(syscall){
{{ range .Wrappers }}case {{ .Id }}:
return "{{ .Name }}";
{{ end }}
default:
return nil;
}
}
void
dispatch_syscall(int syscall, Ureg* ureg, ScRet* ret)
{
switch(syscall){
{{ range .Wrappers }}case {{ .Id }}:
wrap_{{ .Name }}(ret, ureg);
break;
{{ end }}
default:
panic("dispatch_syscall: bad sys call number %d pc %#p\n", syscall, ureg->ip);
}
}
{{ range .Wrappers }}
static void
enter_{{ .Name }}(Fmt* fmt, Ureg* ureg)
{
{{ range .Vars }}{{.}}
{{ end }}
{{ .CommonCode }}
fmtprint(fmt, "{{ .Name }} %#p > ", ureg->ip);
{{ .EntryPrint }}
}
{{ end }}
char*
syscallfmt(int syscall, Ureg* ureg)
{
Fmt fmt;
fmtstrinit(&fmt);
fmtprint(&fmt, "%d %s ", up->pid, up->text);
switch(syscall){
{{ range .Wrappers }}case {{ .Id }}:
enter_{{ .Name }}(&fmt, ureg);
break;
{{ end }}
default:
panic("syscallfmt: bad sys call number %d pc %#p\n", syscall, ureg->ip);
}
return fmtstrflush(&fmt);
}
{{ range .Wrappers }}
static void
exit_{{ .Name }}(Fmt* fmt, Ureg* ureg, ScRet* ret)
{
fmtprint(fmt, "{{ .Name }} %#p < ", ureg->ip);
{{ .ExitPrint }}
}
{{ end }}
char*
sysretfmt(int syscall, Ureg* ureg, ScRet* ret, uint64_t start, uint64_t stop)
{
Fmt fmt;
fmtstrinit(&fmt);
fmtprint(&fmt, "%d %s ", up->pid, up->text);
switch(syscall){
{{ range .Wrappers }}case {{ .Id }}:
exit_{{ .Name }}(&fmt, ureg, ret);
break;
{{ end }}
default:
panic("sysretfmt: bad sys call number %d pc %#p\n", syscall, ureg->ip);
}
if(0 > ret->vl){
fmtprint(&fmt, " %s %#llud %#llud\n", up->syserrstr, start, stop-start);
} else {
fmtprint(&fmt, " \"\" %#llud %#llud\n", start, stop-start);
}
return fmtstrflush(&fmt);
}
`)
err = tmpl.Execute(os.Stdout, code)
if err != nil {
log.Fatal(err)
}
}
func main() {
flag.Parse()
if flag.NArg() != 1 {
usage("no path to sysconf.json")
}
buf, err := ioutil.ReadFile(flag.Arg(0))
if err != nil {
log.Fatal(err)
}
var sysconf Sysconf
err = json.Unmarshal(buf, &sysconf)
if err != nil {
log.Fatal(err)
}
generateKernelCode(sysconf.Syscalls)
}

View File

@ -258,9 +258,9 @@ const(
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../../libc/9syscall/sys.h"
#include <9syscall/sys.h>
{{ range . }}extern void {{ .Sysname }}(Ar0*, ...);
{{ range . }}extern void {{ .Sysname }}(ScRet*, ScArg, ScArg, ScArg, ScArg, ScArg, ScArg);
{{ end }}
Systab systab[] = {
{{ range . }}[{{ .Define }}] { "{{ .Name }}", {{ .Sysname }}, {{ .Fudge }} },

View File

@ -0,0 +1,291 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2016 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 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){
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)
wcall.VarValues = append(wcall.VarValues, fmt.Sprintf("_sysargs[%d].%s = (a%d); \\\n\t", i, argTypeName(a), 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(*)()) {{ .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 }}
{{ .RetType }}
{{ .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 }}
#define sys_{{ .Name }}({{ .MacroArgs }}) ({ \
{{ range .VarValues }}{{.}}{{end}}{{ range .Vars }}{{.}}{{end}}register {{ .RetType }} __ret asm ("rax"); \
__asm__ __volatile__ ( \
"syscall" \
: "=&r" (__ret) \
: {{ .AsmArgs }} \
: {{ .AsmClobbers }} \
); \
__ret; })
{{ end }}
#ifdef PORTABLE_SYSCALLS
{{ range .Wrappers }}extern {{ .RetType }} {{ .Name }}({{ .FuncArgs }});
{{ end }}
extern int32_t read(int, void*, int32_t);
extern int32_t write(int, const void*, int32_t);
#else
{{ range .Wrappers }}# define {{ .Name }}(...) sys_{{ .Name }}(__VA_ARGS__)
{{ end }}
#define read(fd, buf, size) pread(fd, buf, size, -1)
#define write(fd, buf, size) pwrite(fd, buf, size, -1)
#endif
#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
}
}