master
  1const std = @import("../std.zig");
  2const builtin = @import("builtin");
  3
  4pub const fd_t = i32;
  5
  6pub const STDIN_FILENO = 0;
  7pub const STDOUT_FILENO = 1;
  8pub const STDERR_FILENO = 2;
  9pub const PATH_MAX = 1023;
 10pub const syscall_bits = switch (builtin.cpu.arch) {
 11    .x86_64 => @import("plan9/x86_64.zig"),
 12    else => @compileError("more plan9 syscall implementations (needs more inline asm in stage2"),
 13};
 14/// Ported from /sys/include/ape/errno.h
 15pub const E = enum(u16) {
 16    SUCCESS = 0,
 17    DOM = 1000,
 18    RANGE = 1001,
 19    PLAN9 = 1002,
 20
 21    @"2BIG" = 1,
 22    ACCES = 2,
 23    AGAIN = 3,
 24    // WOULDBLOCK = 3, // TODO errno.h has 2 names for 3
 25    BADF = 4,
 26    BUSY = 5,
 27    CHILD = 6,
 28    DEADLK = 7,
 29    EXIST = 8,
 30    FAULT = 9,
 31    FBIG = 10,
 32    INTR = 11,
 33    INVAL = 12,
 34    IO = 13,
 35    ISDIR = 14,
 36    MFILE = 15,
 37    MLINK = 16,
 38    NAMETOOLONG = 17,
 39    NFILE = 18,
 40    NODEV = 19,
 41    NOENT = 20,
 42    NOEXEC = 21,
 43    NOLCK = 22,
 44    NOMEM = 23,
 45    NOSPC = 24,
 46    NOSYS = 25,
 47    NOTDIR = 26,
 48    NOTEMPTY = 27,
 49    NOTTY = 28,
 50    NXIO = 29,
 51    PERM = 30,
 52    PIPE = 31,
 53    ROFS = 32,
 54    SPIPE = 33,
 55    SRCH = 34,
 56    XDEV = 35,
 57
 58    // bsd networking software
 59    NOTSOCK = 36,
 60    PROTONOSUPPORT = 37,
 61    // PROTOTYPE = 37, // TODO errno.h has two names for 37
 62    CONNREFUSED = 38,
 63    AFNOSUPPORT = 39,
 64    NOBUFS = 40,
 65    OPNOTSUPP = 41,
 66    ADDRINUSE = 42,
 67    DESTADDRREQ = 43,
 68    MSGSIZE = 44,
 69    NOPROTOOPT = 45,
 70    SOCKTNOSUPPORT = 46,
 71    PFNOSUPPORT = 47,
 72    ADDRNOTAVAIL = 48,
 73    NETDOWN = 49,
 74    NETUNREACH = 50,
 75    NETRESET = 51,
 76    CONNABORTED = 52,
 77    ISCONN = 53,
 78    NOTCONN = 54,
 79    SHUTDOWN = 55,
 80    TOOMANYREFS = 56,
 81    TIMEDOUT = 57,
 82    HOSTDOWN = 58,
 83    HOSTUNREACH = 59,
 84    GREG = 60,
 85
 86    // These added in 1003.1b-1993
 87    CANCELED = 61,
 88    INPROGRESS = 62,
 89
 90    // We just add these to be compatible with std.os, which uses them,
 91    // They should never get used.
 92    DQUOT,
 93    CONNRESET,
 94    OVERFLOW,
 95    LOOP,
 96    TXTBSY,
 97};
 98
 99/// Get the errno from a syscall return value. SUCCESS means no error.
100pub fn errno(r: usize) E {
101    const signed_r: isize = @bitCast(r);
102    const int = if (signed_r > -4096 and signed_r < 0) -signed_r else 0;
103    return @enumFromInt(int);
104}
105
106// The max bytes that can be in the errstr buff
107pub const ERRMAX = 128;
108var errstr_buf: [ERRMAX]u8 = undefined;
109/// Gets whatever the last errstr was
110pub fn errstr() []const u8 {
111    _ = syscall_bits.syscall2(.ERRSTR, @intFromPtr(&errstr_buf), ERRMAX);
112    return std.mem.span(@as([*:0]u8, @ptrCast(&errstr_buf)));
113}
114pub const Plink = anyopaque;
115pub const Tos = extern struct {
116    /// Per process profiling
117    prof: extern struct {
118        /// known to be 0(ptr)
119        pp: *Plink,
120        /// known to be 4(ptr)
121        next: *Plink,
122        last: *Plink,
123        first: *Plink,
124        pid: u32,
125        what: u32,
126    },
127    /// cycle clock frequency if there is one, 0 otherwise
128    cyclefreq: u64,
129    /// cycles spent in kernel
130    kcycles: i64,
131    /// cycles spent in process (kernel + user)
132    pcycles: i64,
133    /// might as well put the pid here
134    pid: u32,
135    clock: u32,
136    // top of stack is here
137};
138
139pub var tos: *Tos = undefined; // set in start.zig
140pub fn getpid() u32 {
141    return tos.pid;
142}
143pub const SIG = struct {
144    /// hangup
145    pub const HUP = 1;
146    /// interrupt
147    pub const INT = 2;
148    /// quit
149    pub const QUIT = 3;
150    /// illegal instruction (not reset when caught)
151    pub const ILL = 4;
152    /// used by abort
153    pub const ABRT = 5;
154    /// floating point exception
155    pub const FPE = 6;
156    /// kill (cannot be caught or ignored)
157    pub const KILL = 7;
158    /// segmentation violation
159    pub const SEGV = 8;
160    /// write on a pipe with no one to read it
161    pub const PIPE = 9;
162    /// alarm clock
163    pub const ALRM = 10;
164    /// software termination signal from kill
165    pub const TERM = 11;
166    /// user defined signal 1
167    pub const USR1 = 12;
168    /// user defined signal 2
169    pub const USR2 = 13;
170    /// bus error
171    pub const BUS = 14;
172    // The following symbols must be defined, but the signals needn't be supported
173    /// child process terminated or stopped
174    pub const CHLD = 15;
175    /// continue if stopped
176    pub const CONT = 16;
177    /// stop
178    pub const STOP = 17;
179    /// interactive stop
180    pub const TSTP = 18;
181    /// read from ctl tty by member of background
182    pub const TTIN = 19;
183    /// write to ctl tty by member of background
184    pub const TTOU = 20;
185};
186pub const sigset_t = c_long;
187pub const siginfo_t = c_long;
188// TODO plan9 doesn't have sigaction_fn. Sigaction is not a union, but we include it here to be compatible.
189pub const Sigaction = extern struct {
190    pub const handler_fn = *const fn (i32) callconv(.c) void;
191    pub const sigaction_fn = *const fn (i32, *const siginfo_t, ?*anyopaque) callconv(.c) void;
192
193    handler: extern union {
194        handler: ?handler_fn,
195        sigaction: ?sigaction_fn,
196    },
197    mask: sigset_t,
198    flags: c_int,
199};
200pub const AT = struct {
201    pub const FDCWD = -100; // we just make up a constant; FDCWD and openat don't actually exist in plan9
202};
203// Plan 9 doesn't do signals.  This is just needed to get through start.zig.
204pub fn sigemptyset() sigset_t {
205    return 0;
206}
207// TODO implement sigaction
208// right now it is just a shim to allow using start.zig code
209pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) usize {
210    _ = oact;
211    _ = act;
212    _ = sig;
213    return 0;
214}
215pub const SYS = enum(usize) {
216    SYSR1 = 0,
217    _ERRSTR = 1,
218    BIND = 2,
219    CHDIR = 3,
220    CLOSE = 4,
221    DUP = 5,
222    ALARM = 6,
223    EXEC = 7,
224    EXITS = 8,
225    _FSESSION = 9,
226    FAUTH = 10,
227    _FSTAT = 11,
228    SEGBRK = 12,
229    _MOUNT = 13,
230    OPEN = 14,
231    _READ = 15,
232    OSEEK = 16,
233    SLEEP = 17,
234    _STAT = 18,
235    RFORK = 19,
236    _WRITE = 20,
237    PIPE = 21,
238    CREATE = 22,
239    FD2PATH = 23,
240    BRK_ = 24,
241    REMOVE = 25,
242    _WSTAT = 26,
243    _FWSTAT = 27,
244    NOTIFY = 28,
245    NOTED = 29,
246    SEGATTACH = 30,
247    SEGDETACH = 31,
248    SEGFREE = 32,
249    SEGFLUSH = 33,
250    RENDEZVOUS = 34,
251    UNMOUNT = 35,
252    _WAIT = 36,
253    SEMACQUIRE = 37,
254    SEMRELEASE = 38,
255    SEEK = 39,
256    FVERSION = 40,
257    ERRSTR = 41,
258    STAT = 42,
259    FSTAT = 43,
260    WSTAT = 44,
261    FWSTAT = 45,
262    MOUNT = 46,
263    AWAIT = 47,
264    PREAD = 50,
265    PWRITE = 51,
266    TSEMACQUIRE = 52,
267    _NSEC = 53,
268};
269
270pub fn write(fd: i32, buf: [*]const u8, count: usize) usize {
271    return syscall_bits.syscall4(.PWRITE, @bitCast(@as(isize, fd)), @intFromPtr(buf), count, @bitCast(@as(isize, -1)));
272}
273pub fn pwrite(fd: i32, buf: [*]const u8, count: usize, offset: isize) usize {
274    return syscall_bits.syscall4(.PWRITE, @bitCast(@as(isize, fd)), @intFromPtr(buf), count, @bitCast(offset));
275}
276
277pub fn read(fd: i32, buf: [*]const u8, count: usize) usize {
278    return syscall_bits.syscall4(.PREAD, @bitCast(@as(isize, fd)), @intFromPtr(buf), count, @bitCast(@as(isize, -1)));
279}
280pub fn pread(fd: i32, buf: [*]const u8, count: usize, offset: isize) usize {
281    return syscall_bits.syscall4(.PREAD, @bitCast(@as(isize, fd)), @intFromPtr(buf), count, @bitCast(offset));
282}
283
284pub fn open(path: [*:0]const u8, flags: u32) usize {
285    return syscall_bits.syscall2(.OPEN, @intFromPtr(path), @bitCast(@as(isize, flags)));
286}
287
288pub fn openat(dirfd: i32, path: [*:0]const u8, flags: u32, _: mode_t) usize {
289    // we skip perms because only create supports perms
290    if (dirfd == AT.FDCWD) { // openat(AT_FDCWD, ...) == open(...)
291        return open(path, flags);
292    }
293    var dir_path_buf: [std.fs.max_path_bytes]u8 = undefined;
294    var total_path_buf: [std.fs.max_path_bytes + 1]u8 = undefined;
295    const rc = fd2path(dirfd, &dir_path_buf, std.fs.max_path_bytes);
296    if (rc != 0) return rc;
297    var fba = std.heap.FixedBufferAllocator.init(&total_path_buf);
298    var alloc = fba.allocator();
299    const dir_path = std.mem.span(@as([*:0]u8, @ptrCast(&dir_path_buf)));
300    const total_path = std.fs.path.join(alloc, &.{ dir_path, std.mem.span(path) }) catch unreachable; // the allocation shouldn't fail because it should not exceed max_path_bytes
301    fba.reset();
302    const total_path_z = alloc.dupeZ(u8, total_path) catch unreachable; // should not exceed max_path_bytes + 1
303    return open(total_path_z.ptr, flags);
304}
305
306pub fn fd2path(fd: i32, buf: [*]u8, nbuf: usize) usize {
307    return syscall_bits.syscall3(.FD2PATH, @bitCast(@as(isize, fd)), @intFromPtr(buf), nbuf);
308}
309
310pub fn create(path: [*:0]const u8, omode: mode_t, perms: usize) usize {
311    return syscall_bits.syscall3(.CREATE, @intFromPtr(path), @bitCast(@as(isize, omode)), perms);
312}
313
314pub fn exit(status: u8) noreturn {
315    if (status == 0) {
316        exits(null);
317    } else {
318        // TODO plan9 does not have exit codes. You either exit with 0 or a string
319        const arr: [1:0]u8 = .{status};
320        exits(&arr);
321    }
322}
323
324pub fn exits(status: ?[*:0]const u8) noreturn {
325    _ = syscall_bits.syscall1(.EXITS, if (status) |s| @intFromPtr(s) else 0);
326    unreachable;
327}
328
329pub fn close(fd: i32) usize {
330    return syscall_bits.syscall1(.CLOSE, @bitCast(@as(isize, fd)));
331}
332pub const mode_t = i32;
333
334pub const AccessMode = enum(u2) {
335    RDONLY,
336    WRONLY,
337    RDWR,
338    EXEC,
339};
340
341pub const O = packed struct(u32) {
342    access: AccessMode,
343    _2: u2 = 0,
344    TRUNC: bool = false,
345    CEXEC: bool = false,
346    RCLOSE: bool = false,
347    _7: u5 = 0,
348    EXCL: bool = false,
349    _: u19 = 0,
350};
351
352pub const ExecData = struct {
353    pub extern const etext: anyopaque;
354    pub extern const edata: anyopaque;
355    pub extern const end: anyopaque;
356};
357
358/// Brk sets the system's idea of the lowest bss location not
359/// used by the program (called the break) to addr rounded up to
360/// the next multiple of 8 bytes.  Locations not less than addr
361/// and below the stack pointer may cause a memory violation if
362/// accessed. -9front brk(2)
363pub fn brk_(addr: usize) i32 {
364    return @intCast(syscall_bits.syscall1(.BRK_, addr));
365}
366var bloc: usize = 0;
367var bloc_max: usize = 0;
368
369pub fn sbrk(n: usize) usize {
370    if (bloc == 0) {
371        // we are at the start
372        bloc = @intFromPtr(&ExecData.end);
373        bloc_max = @intFromPtr(&ExecData.end);
374    }
375    const bl = std.mem.alignForward(usize, bloc, std.heap.pageSize());
376    const n_aligned = std.mem.alignForward(usize, n, std.heap.pageSize());
377    if (bl + n_aligned > bloc_max) {
378        // we need to allocate
379        if (brk_(bl + n_aligned) < 0) return 0;
380        bloc_max = bl + n_aligned;
381    }
382    bloc = bloc + n_aligned;
383    return bl;
384}