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}