| 1 | const std = @import("../std.zig"); |
| 2 | const builtin = @import("builtin"); |
| 3 | |
| 4 | pub const fd_t = i32; |
| 5 | |
| 6 | pub const STDIN_FILENO = 0; |
| 7 | pub const STDOUT_FILENO = 1; |
| 8 | pub const STDERR_FILENO = 2; |
| 9 | pub const PATH_MAX = 1023; |
| 10 | pub 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 |
| 15 | pub 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. |
| 100 | pub 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 @fromBackingInt(@intCast(int)); |
| 104 | } |
| 105 | |
| 106 | // The max bytes that can be in the errstr buff |
| 107 | pub const ERRMAX = 128; |
| 108 | var errstr_buf: [ERRMAX]u8 = undefined; |
| 109 | /// Gets whatever the last errstr was |
| 110 | pub 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 | } |
| 114 | pub const Plink = anyopaque; |
| 115 | pub 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 | |
| 139 | pub var tos: *Tos = undefined; // set in start.zig |
| 140 | pub fn getpid() u32 { |
| 141 | return tos.pid; |
| 142 | } |
| 143 | pub 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 | }; |
| 186 | pub const sigset_t = c_long; |
| 187 | pub 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. |
| 189 | pub 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 | }; |
| 200 | pub 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. |
| 204 | pub 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 |
| 209 | pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) usize { |
| 210 | _ = oact; |
| 211 | _ = act; |
| 212 | _ = sig; |
| 213 | return 0; |
| 214 | } |
| 215 | pub 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 | |
| 270 | pub 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 | } |
| 273 | pub 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 | |
| 277 | pub 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 | } |
| 280 | pub 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 | |
| 284 | pub fn open(path: [*:0]const u8, flags: u32) usize { |
| 285 | return syscall_bits.syscall2(.OPEN, @intFromPtr(path), @bitCast(@as(isize, flags))); |
| 286 | } |
| 287 | |
| 288 | pub 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.dupeSentinel(u8, total_path, 0) catch unreachable; // should not exceed max_path_bytes + 1 |
| 303 | return open(total_path_z.ptr, flags); |
| 304 | } |
| 305 | |
| 306 | pub fn fd2path(fd: i32, buf: [*]u8, nbuf: usize) usize { |
| 307 | return syscall_bits.syscall3(.FD2PATH, @bitCast(@as(isize, fd)), @intFromPtr(buf), nbuf); |
| 308 | } |
| 309 | |
| 310 | pub 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 | |
| 314 | pub 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 | |
| 324 | pub fn exits(status: ?[*:0]const u8) noreturn { |
| 325 | _ = syscall_bits.syscall1(.EXITS, if (status) |s| @intFromPtr(s) else 0); |
| 326 | unreachable; |
| 327 | } |
| 328 | |
| 329 | pub fn close(fd: i32) usize { |
| 330 | return syscall_bits.syscall1(.CLOSE, @bitCast(@as(isize, fd))); |
| 331 | } |
| 332 | pub const mode_t = i32; |
| 333 | |
| 334 | pub const AccessMode = enum(u2) { |
| 335 | RDONLY, |
| 336 | WRONLY, |
| 337 | RDWR, |
| 338 | EXEC, |
| 339 | }; |
| 340 | |
| 341 | pub 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 | |
| 352 | pub 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) |
| 363 | pub fn brk_(addr: usize) i32 { |
| 364 | return @intCast(syscall_bits.syscall1(.BRK_, addr)); |
| 365 | } |
| 366 | var bloc: usize = 0; |
| 367 | var bloc_max: usize = 0; |
| 368 | |
| 369 | pub 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 | } |