├── README.md ├── config.h ├── data.c ├── devflop.c ├── devio.c ├── devmisc.c ├── devtty.c ├── devwd.c ├── dispatch.c ├── extern.h ├── extras.c ├── filelist ├── filesys.c ├── filler.mac ├── intro ├── loadunix.sub ├── machdep.c ├── makeunix.sub ├── process.c ├── scall1.c ├── scall2.c └── unix.h /README.md: -------------------------------------------------------------------------------- 1 | This is a public repo of Doug Braun's UZI (Unix: Z80 Implementation) source 2 | code. The author's site no longer serves the archive and it took some 3 | digging to find, so it's uploaded here for conservation purposes. 4 | 5 | Although no LICENSE file is attached, Braun has posted elsewhere that this 6 | code is public domain. If you are Doug Braun and wish me to remove this 7 | code, I will naturally comply (albeit with regret). 8 | 9 | The author's original page is here: http://www.dougbraun.com/uzi.html 10 | 11 | This is interesting because the Z80 had 64kB of RAM, no mmu and various 12 | other restrictions. This means it's a good jumping-off point for new projects 13 | aimed at highly constrained systems, whether it be embedded or micro 14 | guests on VMs. 15 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | /************************************************** 2 | UZI (Unix Z80 Implementation) Kernel: config.h 3 | ***************************************************/ 4 | 5 | 6 | /* Remake devio.c when this is changed */ 7 | #ifdef DEVIO 8 | 9 | extern wd_open(), wd_read(),wd_write(); 10 | extern fd_open(), fd_read(),fd_write(); 11 | extern tty_open(), tty_close(), tty_read(),tty_write(); 12 | extern lpr_open(), lpr_close(), lpr_write(); 13 | extern mem_read(),mem_write(); 14 | extern mt_read(), mt_write(), mt_open(), mt_close(); 15 | extern null_write(); 16 | 17 | 18 | static struct devsw dev_tab[] = /* The device driver switch table */ 19 | { 20 | { 0x2b38, wd_open, ok, wd_read, wd_write, nogood }, 21 | { 0, fd_open, ok, fd_read, fd_write, nogood }, /* floppy */ 22 | { 0x3844, wd_open, ok, wd_read, wd_write, nogood }, 23 | { 0x252b, wd_open, ok, wd_read, wd_write, nogood }, /* Swap */ 24 | { 0, lpr_open, lpr_close, nogood, lpr_write, nogood}, /* printer */ 25 | { 0, tty_open, tty_close, tty_read, tty_write, ok }, /* tty */ 26 | { 0, ok, ok, ok, null_write, nogood }, /* /dev/null */ 27 | { 0, ok, ok, mem_read, mem_write, nogood }, /* /dev/mem */ 28 | { 0, mt_open, mt_close, mt_read, mt_write, nogood } 29 | }; 30 | 31 | #endif 32 | 33 | #define NDEVS 3 /* Devices 0..NDEVS-1 are capable of being mounted */ 34 | #define TTYDEV 5 /* Device used by kernel for messages, panics */ 35 | #define SWAPDEV 3 /* Device for swapping. */ 36 | #define NBUFS 4 /* Number of block buffers */ 37 | 38 | -------------------------------------------------------------------------------- /data.c: -------------------------------------------------------------------------------- 1 | /************************************************** 2 | UZI (Unix Z80 Implementation) Kernel: data.c 3 | ***************************************************/ 4 | 5 | #include "unix.h" 6 | #define MAIN 7 | #asm 8080 8 | CSEG 9 | @init?:: 10 | EXTRN fs@init? 11 | JMP fs@init? 12 | #endasm 13 | #include "extern.h" 14 | 15 | -------------------------------------------------------------------------------- /devflop.c: -------------------------------------------------------------------------------- 1 | /************************************************** 2 | UZI (Unix Z80 Implementation) Kernel: devflop.c 3 | ***************************************************/ 4 | 5 | 6 | #include "unix.h" 7 | #include "extern.h" 8 | 9 | extern ei(); 10 | 11 | 12 | #define NUMTRKS 76 13 | #define NUMSECS 26 14 | 15 | static char ftrack, fsector, ferror; 16 | static char *fbuf; 17 | 18 | static read(), write(), reset(); 19 | 20 | 21 | fd_read(minor, rawflag) 22 | int16 minor; 23 | int rawflag; 24 | { 25 | return (fd(1, minor, rawflag)); 26 | } 27 | 28 | fd_write(minor, rawflag) 29 | int16 minor; 30 | int rawflag; 31 | { 32 | return (fd(0, minor, rawflag)); 33 | } 34 | 35 | 36 | 37 | static 38 | fd(rwflag, minor, rawflag) 39 | int rwflag; 40 | int minor; 41 | int rawflag; 42 | { 43 | register unsigned nblocks; 44 | register unsigned firstblk; 45 | 46 | if (rawflag) 47 | { 48 | if (rawflag == 2) 49 | { 50 | nblocks = swapcnt >> 7; 51 | fbuf = swapbase; 52 | firstblk = 4*swapblk; 53 | } 54 | else 55 | { 56 | nblocks = udata.u_count >> 7; 57 | fbuf = udata.u_base; 58 | firstblk = udata.u_offset.o_blkno * 4; 59 | } 60 | } 61 | else 62 | { 63 | nblocks = 4; 64 | fbuf = udata.u_buf->bf_data; 65 | firstblk = udata.u_buf->bf_blk * 4; 66 | } 67 | 68 | ftrack = firstblk / 26; 69 | fsector = firstblk % 26 + 1; 70 | ferror = 0; 71 | 72 | for (;;) 73 | { 74 | if (rwflag) 75 | read(); 76 | else 77 | write(); 78 | 79 | ifnot (--nblocks) 80 | break; 81 | 82 | if (++fsector == 27) 83 | { 84 | fsector = 1; 85 | ++ftrack; 86 | } 87 | fbuf += 128; 88 | } 89 | 90 | if (ferror) 91 | { 92 | kprintf("fd_%s: error %d track %d sector %d\n", 93 | rwflag?"read":"write", ferror, ftrack, fsector); 94 | panic(""); 95 | } 96 | 97 | return(nblocks); 98 | } 99 | 100 | 101 | fd_open(minor) 102 | int minor; 103 | { 104 | if (in(0x80) & 0x81) 105 | { 106 | udata.u_error = ENXIO; 107 | return (-1); 108 | } 109 | reset(); 110 | return(0); 111 | } 112 | 113 | 114 | fd_close(minor) 115 | int minor; 116 | { 117 | return(0); 118 | } 119 | 120 | 121 | fd_ioctl(minor) 122 | int minor; 123 | { 124 | return(-1); 125 | } 126 | 127 | 128 | 129 | #asm 8080 130 | ; ALL THE FUNCTIONS IN HERE ARE STATIC TO THIS PACKAGE 131 | 132 | ; 133 | ;THESE ARE 1771 FLOPPY DISK CONTROLLER COMMANDS, 134 | ;I/O PORT ADDRESSES, AND FLAG MASKS: 135 | ; 136 | RESTOR EQU 08H ;6MS STEP,LOAD HEAD 137 | SEEK EQU 18H ;6MS STEP,LOAD HEAD 138 | READC EQU 88H ;NO HEAD LOAD 139 | WRITEC EQU 0A8H ;NO HEAD LOAD 140 | RESET EQU 0D0H ;RESET STATUS COMMAND 141 | STATUS EQU 80H 142 | COMAND EQU 80H 143 | TRACK EQU 81H 144 | SECTOR EQU 82H 145 | DATA EQU 83H 146 | BUSY EQU 01 147 | RDMASK EQU 10011111B 148 | WRMASK EQU 0FFH 149 | ; 150 | ; 151 | ;THIS FLOPPY READ ROUTINE CALLS FREAD2. IF THE 152 | ;READ IS UNSUCCESSFUL, THE DRIVE IS HOMED, 153 | ;AND THE READ IS RETRIED. 154 | ; 155 | read?: PUSH B 156 | CALL FREAD2 157 | LDA ferror? 158 | ANA A ;SET FLAGS 159 | JZ RDONE ;READ WAS OK 160 | CALL FHOME 161 | CALL FREAD2 162 | RDONE: 163 | POP B 164 | RET 165 | ; 166 | FREAD2: CALL FWAIT 167 | LDA fsector? 168 | OUT SECTOR 169 | CALL FTKSET 170 | MVI A,11 171 | FREAD3: STA TRYNUM 172 | CALL FWAIT 173 | LHLD fbuf? 174 | MVI C,DATA 175 | MVI B,128 176 | MVI D,03 177 | DI ;ENTERING CRITICAL SECTION 178 | MVI A,READC 179 | OUT COMAND 180 | XTHL ;SHORT DELAY 181 | XTHL 182 | XTHL 183 | XTHL 184 | .1LOOP: IN STATUS 185 | ANA D 186 | DCR A 187 | JZ .1LOOP 188 | .Z80 189 | INI 190 | .8080 191 | JNZ .1LOOP 192 | CALL ei? ;LEAVING CRITICAL SECTION 193 | CALL FWAIT 194 | IN STATUS 195 | ANI RDMASK 196 | JZ FR1END 197 | LDA TRYNUM 198 | DCR A 199 | JNZ FREAD3 200 | MVI A,1 201 | FR1END: STA ferror? 202 | RET 203 | ; 204 | ;THIS IS THE FLOPPY WRITE ROUTINE. IT CALLS 205 | ;THE ACTUAL WRITING SUBROUTINE, AND IF IT 206 | ;WAS UNSUCCESSFUL, RESETS THE HEAD AND 207 | ;TRIES AGAIN. 208 | ; 209 | write?: PUSH B 210 | CALL FWR2 211 | LDA ferror? 212 | ANA A 213 | JZ WDONE 214 | CALL FHOME 215 | CALL FWR2 216 | LDA ferror? 217 | WDONE: POP B 218 | RET 219 | ; 220 | FWR2: CALL FWAIT 221 | LDA fsector? 222 | OUT SECTOR 223 | XTHL 224 | XTHL 225 | MVI A,RESET 226 | OUT COMAND 227 | XTHL 228 | XTHL 229 | XTHL 230 | XTHL 231 | IN STATUS 232 | ANI 00100000B 233 | JZ FWR5 ;JMP IF HEAD NOT LOADED 234 | LDA ftrack? ;DESIRED TRACK 235 | MOV C,A 236 | IN TRACK ;ACTUAL TRACK 237 | CMP C 238 | JZ FWR4 ;IF TRACK CORRECT 239 | CALL FTKSET 240 | CALL FWAIT 241 | LXI H,30 ;15MS DELAY 242 | CALL DELAY 243 | JMP FWR4 244 | FWR5: CALL FTKSET 245 | LXI H,50 ;25 MS DELAY 246 | CALL DELAY 247 | CALL FWAIT 248 | LXI H,20 ;15 MS DELAY 249 | CALL DELAY 250 | FWR4: MVI A,11 251 | FWR3: STA TRYNUM 252 | CALL FWAIT 253 | LHLD fbuf? 254 | MVI C,DATA 255 | MVI B,128 256 | MVI D,01 257 | MVI A,WRITEC 258 | DI ;ENTERING CRITICAL SECTION 259 | OUT COMAND 260 | .1A: IN STATUS 261 | ANI 01 262 | JZ .1A 263 | .1B: IN STATUS 264 | ANI 02 265 | JNZ .2LOOP 266 | IN STATUS 267 | ANI 01 268 | JZ .2ERROR 269 | JMP .1B 270 | .2LOOP: IN STATUS 271 | XRA D 272 | JZ .2LOOP 273 | .Z80 274 | OUTI 275 | .8080 276 | JNZ .2LOOP 277 | CALL ei? ;LEAVING CRITICAL SECTION 278 | CALL FWAIT 279 | IN STATUS 280 | ANI WRMASK 281 | JZ FW2END 282 | .2ERROR: LDA TRYNUM 283 | DCR A 284 | JNZ FWR3 285 | MVI A,1 286 | FW2END: STA ferror? 287 | RET 288 | ; 289 | ;THESE 3 SUBROUTINES ARE USED BY THE FREAD2 AND 290 | ;FWR2 ROUTINES: 291 | ; 292 | FTKSET: CALL FWAIT 293 | LDA ftrack? 294 | OUT DATA 295 | MVI A,SEEK 296 | OUT COMAND 297 | RET 298 | ; 299 | FWAIT: IN STATUS 300 | ANI 10000001B 301 | JNZ FWAIT 302 | RET 303 | ; 304 | reset?: 305 | FHOME: PUSH B 306 | CALL FWAIT 307 | MVI A,RESTOR 308 | OUT COMAND 309 | POP B 310 | RET 311 | ; 312 | 313 | 314 | ;THIS IS USED IN SEVERAL PLACES. IT GIVES 315 | ; ( .5 * HL ) MILLISECONDS OF DELAY. 316 | ; 317 | DELAY: MVI B,154 318 | .Z80 319 | DELAY1: DJNZ DELAY1 ;.5 MS DELAY 320 | .8080 321 | DCX H 322 | MOV A,H 323 | ORA L 324 | JNZ DELAY ;LOOP HL TIMES 325 | RET 326 | 327 | TRYNUM: DS 1 328 | #endasm 329 | 330 | -------------------------------------------------------------------------------- /devio.c: -------------------------------------------------------------------------------- 1 | /************************************************** 2 | UZI (Unix Z80 Implementation) Kernel: devio.c 3 | ***************************************************/ 4 | 5 | 6 | int ok(), nogood(); 7 | #define DEVIO 8 | 9 | #include "unix.h" 10 | #include "extern.h" 11 | 12 | /* Buffer pool management */ 13 | 14 | /******************************************************** 15 | The high-level interface is through bread() and bfree(). 16 | Bread() is given a device and block number, and a rewrite flag. 17 | If rewrite is 0, the block is actually read if it is not already 18 | in the buffer pool. If rewrite is set, it is assumed that the caller 19 | plans to rewrite the entire contents of the block, and it will 20 | not be read in, but only have a buffer named after it. 21 | 22 | Bfree() is given a buffer pointer and a dirty flag. 23 | If the dirty flag is 0, the buffer is made available for further 24 | use. If the flag is 1, the buffer is marked "dirty", and 25 | it will eventually be written out to disk. If the flag is 2, 26 | it will be immediately written out. 27 | 28 | Zerobuf() returns a buffer of zeroes not belonging to any 29 | device. It must be bfree'd after use, and must not be 30 | dirty. It is used when a read() wants to read an unallocated 31 | block of a file. 32 | 33 | Bufsync() write outs all dirty blocks. 34 | 35 | Note that a pointer to a buffer structure is the same as a 36 | pointer to the data. This is very important. 37 | 38 | ********************************************************/ 39 | 40 | unsigned bufclock = 0; /* Time-stamp counter for LRU */ 41 | 42 | char * 43 | bread( dev, blk, rewrite) 44 | int dev; 45 | blkno_t blk; 46 | int rewrite; 47 | { 48 | register bufptr bp; 49 | bufptr bfind(); 50 | bufptr freebuf(); 51 | 52 | if (bp = bfind(dev, blk)) 53 | { 54 | if (bp->bf_busy) 55 | panic("want busy block"); 56 | goto done; 57 | } 58 | bp = freebuf(); 59 | 60 | bp->bf_dev = dev; 61 | bp->bf_blk = blk; 62 | 63 | /* If rewrite is set, we are about to write over the entire block, 64 | so we don't need the previous contents */ 65 | 66 | ifnot (rewrite) 67 | if (bdread(bp) == -1) 68 | { 69 | udata.u_error = EIO; 70 | return (NULL); 71 | } 72 | 73 | if (rewrite == 2) 74 | bzero(bp->bf_data, 512); 75 | 76 | done: 77 | bp->bf_busy = 1; 78 | bp->bf_time = ++bufclock; /* Time stamp it */ 79 | return (bp->bf_data); 80 | } 81 | 82 | 83 | 84 | brelse(bp) 85 | bufptr bp; 86 | { 87 | bfree(bp, 0); 88 | } 89 | 90 | bawrite(bp) 91 | bufptr bp; 92 | { 93 | bfree(bp, 1); 94 | } 95 | 96 | 97 | 98 | bfree(bp, dirty) 99 | register bufptr bp; 100 | int dirty; 101 | { 102 | 103 | bp->bf_dirty |= dirty; 104 | bp->bf_busy = 0; 105 | 106 | if (dirty == 2) /* Extra dirty */ 107 | { 108 | if (bdwrite(bp) == -1) 109 | udata.u_error = EIO; 110 | bp->bf_dirty = 0; 111 | return (-1); 112 | } 113 | return (0); 114 | } 115 | 116 | 117 | char * 118 | zerobuf() 119 | { 120 | bufptr bp; 121 | bufptr freebuf(); 122 | 123 | bp = freebuf(); 124 | bp->bf_dev = -1; 125 | bzero(bp->bf_data,512); 126 | return(bp->bf_data); 127 | } 128 | 129 | 130 | bufsync() 131 | { 132 | register bufptr bp; 133 | 134 | for (bp=bufpool; bp < bufpool+NBUFS; ++bp) 135 | { 136 | if (bp->bf_dev != -1 && bp->bf_dirty) 137 | bdwrite(bp); 138 | } 139 | } 140 | 141 | 142 | bufptr 143 | bfind(dev, blk) 144 | int dev; 145 | blkno_t blk; 146 | { 147 | register bufptr bp; 148 | 149 | for (bp=bufpool; bp < bufpool+NBUFS; ++bp) 150 | { 151 | if (bp->bf_dev == dev && bp->bf_blk == blk) 152 | return (bp); 153 | } 154 | 155 | return (NULL); 156 | } 157 | 158 | 159 | bufptr 160 | freebuf() 161 | { 162 | register bufptr bp; 163 | register bufptr oldest; 164 | register int oldtime; 165 | 166 | /* Try to find a non-busy buffer 167 | and write out the data if it is dirty */ 168 | 169 | oldest = NULL; 170 | oldtime = 0; 171 | for (bp=bufpool; bp < bufpool+NBUFS; ++bp) 172 | { 173 | if (bufclock - bp->bf_time >= oldtime && !bp->bf_busy) 174 | { 175 | oldest = bp; 176 | oldtime = bufclock - bp->bf_time; 177 | } 178 | } 179 | 180 | ifnot (oldest) 181 | panic("no free buffers"); 182 | 183 | if (oldest->bf_dirty) 184 | { 185 | if (bdwrite(oldest) == -1) 186 | udata.u_error = EIO; 187 | oldest->bf_dirty = 0; 188 | } 189 | return (oldest); 190 | } 191 | 192 | 193 | bufinit() 194 | { 195 | register bufptr bp; 196 | 197 | for (bp=bufpool; bp < bufpool+NBUFS; ++bp) 198 | { 199 | bp->bf_dev = -1; 200 | } 201 | } 202 | 203 | 204 | bufdump() 205 | { 206 | register bufptr j; 207 | 208 | kprintf("\ndev\tblock\tdirty\tbusy\ttime clock %d\n", bufclock); 209 | for (j=bufpool; j < bufpool+NBUFS; ++j) 210 | kprintf("%d\t%u\t%d\t%d\t%u\n", 211 | j->bf_dev,j->bf_blk,j->bf_dirty,j->bf_busy,j->bf_time); 212 | } 213 | 214 | 215 | /*************************************************** 216 | Bdread() and bdwrite() are the block device interface routines. 217 | they are given a buffer pointer, which contains the device, block number, 218 | and data location. 219 | They basically validate the device and vector the call. 220 | 221 | Cdread() and cdwrite are the same for character (or "raw") devices, 222 | and are handed a device number. 223 | Udata.u_base, count, and offset have the rest of the data. 224 | ****************************************************/ 225 | 226 | bdread(bp) 227 | bufptr bp; 228 | { 229 | ifnot (validdev(bp->bf_dev)) 230 | panic("bdread: invalid dev"); 231 | 232 | udata.u_buf = bp; 233 | return ((*dev_tab[bp->bf_dev].dev_read)(dev_tab[bp->bf_dev].minor, 0)); 234 | } 235 | 236 | bdwrite(bp) 237 | bufptr bp; 238 | { 239 | ifnot (validdev(bp->bf_dev)) 240 | panic("bdwrite: invalid dev"); 241 | 242 | udata.u_buf = bp; 243 | return ((*dev_tab[bp->bf_dev].dev_write)(dev_tab[bp->bf_dev].minor, 0)); 244 | } 245 | 246 | cdread(dev) 247 | int dev; 248 | { 249 | ifnot (validdev(dev)) 250 | panic("cdread: invalid dev"); 251 | return ((*dev_tab[dev].dev_read)(dev_tab[dev].minor, 1)); 252 | } 253 | 254 | cdwrite(dev) 255 | int dev; 256 | { 257 | ifnot (validdev(dev)) 258 | panic("cdwrite: invalid dev"); 259 | return ((*dev_tab[dev].dev_write)(dev_tab[dev].minor, 1)); 260 | } 261 | 262 | 263 | swapread(dev, blkno, nbytes, buf) 264 | int dev; 265 | blkno_t blkno; 266 | unsigned nbytes; 267 | char *buf; 268 | { 269 | swapbase = buf; 270 | swapcnt = nbytes; 271 | swapblk = blkno; 272 | return ((*dev_tab[dev].dev_read)(dev_tab[dev].minor, 2)); 273 | } 274 | 275 | 276 | swapwrite(dev, blkno, nbytes, buf) 277 | int dev; 278 | blkno_t blkno; 279 | unsigned nbytes; 280 | char *buf; 281 | { 282 | swapbase = buf; 283 | swapcnt = nbytes; 284 | swapblk = blkno; 285 | return ((*dev_tab[dev].dev_write)(dev_tab[dev].minor, 2)); 286 | } 287 | 288 | 289 | /************************************************** 290 | The device driver read and write routines now have 291 | only two arguments, minor and rawflag. If rawflag is 292 | zero, a single block is desired, and the necessary data 293 | can be found in udata.u_buf. 294 | Otherwise, a "raw" or character read is desired, and 295 | udata.u_offset, udata.u_count, and udata.u_base 296 | should be consulted instead. 297 | Any device other than a disk will have only raw access. 298 | *****************************************************/ 299 | 300 | 301 | 302 | d_open(dev) 303 | int dev; 304 | { 305 | ifnot (validdev(dev)) 306 | return(-1); 307 | return ((*dev_tab[dev].dev_open)(dev_tab[dev].minor)); 308 | } 309 | 310 | 311 | d_close(dev) 312 | int dev; 313 | { 314 | ifnot (validdev(dev)) 315 | panic("d_close: bad device"); 316 | (*dev_tab[dev].dev_close)(dev_tab[dev].minor); 317 | } 318 | 319 | 320 | 321 | d_ioctl(dev,request,data) 322 | int dev; 323 | int request; 324 | char *data; 325 | { 326 | ifnot (validdev(dev)) 327 | { 328 | udata.u_error = ENXIO; 329 | return(-1); 330 | } 331 | if((*dev_tab[dev].dev_ioctl)(dev_tab[dev].minor,request,data)) 332 | { 333 | udata.u_error = EINVAL; 334 | return(-1); 335 | } 336 | return(0); 337 | } 338 | 339 | 340 | static 341 | ok() 342 | { 343 | return(0); 344 | } 345 | 346 | 347 | static 348 | nogood() 349 | { 350 | return(-1); 351 | } 352 | 353 | 354 | validdev(dev) 355 | { 356 | return(dev >= 0 && dev < (sizeof(dev_tab)/sizeof(struct devsw))); 357 | } 358 | 359 | 360 | /************************************************************* 361 | Character queue management routines 362 | ************************************************************/ 363 | 364 | 365 | /* add something to the tail */ 366 | insq(q,c) 367 | register struct s_queue *q; 368 | char c; 369 | { 370 | di(); 371 | if (q->q_count == q->q_size) 372 | { 373 | ei(); 374 | return(0); 375 | } 376 | *(q->q_tail) = c; 377 | ++q->q_count; 378 | if (++q->q_tail >= q->q_base + q->q_size) 379 | q->q_tail = q->q_base; 380 | ei(); 381 | return(1); 382 | } 383 | 384 | /* Remove something from the head. */ 385 | remq(q,cp) 386 | register struct s_queue *q; 387 | char *cp; 388 | { 389 | di(); 390 | ifnot (q->q_count) 391 | { 392 | ei(); 393 | return(0); 394 | } 395 | *cp = *(q->q_head); 396 | --q->q_count; 397 | if (++q->q_head >= q->q_base + q->q_size) 398 | q->q_head = q->q_base; 399 | ei(); 400 | return(1); 401 | } 402 | 403 | 404 | /* Remove something from the tail; the most recently added char. */ 405 | uninsq(q,cp) 406 | register struct s_queue *q; 407 | char *cp; 408 | { 409 | di(); 410 | ifnot (q->q_count) 411 | { 412 | ei(); 413 | return(0); 414 | } 415 | --q->q_count; 416 | if (--q->q_tail <= q->q_base) 417 | q->q_tail = q->q_base + q->q_size - 1; 418 | *cp = *(q->q_tail); 419 | ei(); 420 | return(1); 421 | } 422 | 423 | 424 | /* Returns true if the queue has more characters than its wakeup number */ 425 | fullq(q) 426 | struct s_queue *q; 427 | { 428 | di(); 429 | if (q->q_count > q->q_wakeup) 430 | { 431 | ei(); 432 | return (1); 433 | } 434 | ei(); 435 | return (0); 436 | } 437 | 438 | -------------------------------------------------------------------------------- /devmisc.c: -------------------------------------------------------------------------------- 1 | /************************************************** 2 | UZI (Unix Z80 Implementation) Kernel: devmisc.c 3 | ***************************************************/ 4 | 5 | 6 | #include "unix.h" 7 | #include "extern.h" 8 | 9 | mem_read(minor, rawflag) 10 | int minor; 11 | int rawflag; 12 | { 13 | bcopy((char *)(512*udata.u_offset.o_blkno+udata.u_offset.o_offset), 14 | udata.u_base, udata.u_count); 15 | return(udata.u_count); 16 | } 17 | 18 | mem_write(minor, rawflag) 19 | int minor; 20 | int rawflag; 21 | { 22 | bcopy(udata.u_base, 23 | (char *)(512*udata.u_offset.o_blkno+udata.u_offset.o_offset), 24 | udata.u_count); 25 | return(udata.u_count); 26 | } 27 | 28 | 29 | 30 | null_write(minor, rawflag) 31 | int minor; 32 | int rawflag; 33 | { 34 | return(udata.u_count); 35 | } 36 | 37 | 38 | 39 | static char lop = 0; 40 | 41 | lpr_open() 42 | { 43 | lop = 1; 44 | return(0); 45 | } 46 | 47 | lpr_close() 48 | { 49 | if (lop) 50 | { 51 | lop = 0; 52 | lpout('\f'); 53 | lpout('\f'); 54 | } 55 | return(0); 56 | } 57 | 58 | 59 | 60 | lpr_write(minor, rawflag) 61 | int minor; 62 | int rawflag; 63 | { 64 | unsigned n; 65 | 66 | n = udata.u_count; 67 | while (n--) 68 | lpout(*udata.u_base++); 69 | return(udata.u_count); 70 | } 71 | 72 | 73 | lpout(c) 74 | char c; 75 | { 76 | while(in(0x84)&02) 77 | ; 78 | 79 | out(c,0x85); 80 | out(0xfe,0x86); 81 | out(0xff,0x86); 82 | out(0xff,0x85); 83 | } 84 | 85 | 86 | #include "devmt.c" 87 | 88 | -------------------------------------------------------------------------------- /devtty.c: -------------------------------------------------------------------------------- 1 | /************************************************** 2 | UZI (Unix Z80 Implementation) Kernel: devtty.c 3 | ***************************************************/ 4 | 5 | 6 | #include "unix.h" 7 | extern struct u_data udata; 8 | 9 | #define TTYSIZ 132 10 | 11 | char ttyinbuf[TTYSIZ]; 12 | 13 | struct s_queue ttyinq = { 14 | ttyinbuf, 15 | ttyinbuf, 16 | ttyinbuf, 17 | TTYSIZ, 18 | 0, 19 | TTYSIZ/2 20 | }; 21 | 22 | int stopflag; /* Flag for ^S/^Q */ 23 | int flshflag; /* Flag for ^O */ 24 | 25 | tty_read(minor, rawflag) 26 | int16 minor; 27 | int16 rawflag; 28 | { 29 | int nread; 30 | 31 | nread = 0; 32 | while (nread < udata.u_count) 33 | { 34 | for (;;) 35 | { 36 | di(); 37 | if (remq(&ttyinq,udata.u_base)) 38 | break; 39 | psleep(&ttyinq); 40 | if (udata.u_cursig || udata.u_ptab->p_pending) /* messy */ 41 | { 42 | udata.u_error = EINTR; 43 | return(-1); 44 | } 45 | } 46 | ei(); 47 | 48 | if (nread++ == 0 && *udata.u_base == '\004') /* ^D */ 49 | return(0); 50 | 51 | if (*udata.u_base == '\n') 52 | break; 53 | ++udata.u_base; 54 | } 55 | return(nread); 56 | } 57 | 58 | 59 | 60 | tty_write(minor, rawflag) 61 | int16 minor; 62 | int16 rawflag; 63 | { 64 | int towrite; 65 | 66 | towrite = udata.u_count; 67 | 68 | while (udata.u_count-- != 0) 69 | { 70 | for (;;) /* Wait on the ^S/^Q flag */ 71 | { 72 | di(); 73 | ifnot (stopflag) 74 | break; 75 | psleep(&stopflag); 76 | if (udata.u_cursig || udata.u_ptab->p_pending) /* messy */ 77 | { 78 | udata.u_error = EINTR; 79 | return(-1); 80 | } 81 | } 82 | ei(); 83 | 84 | ifnot (flshflag) 85 | { 86 | if (*udata.u_base == '\n') 87 | _putc('\r'); 88 | _putc(*udata.u_base); 89 | } 90 | ++udata.u_base; 91 | } 92 | return(towrite); 93 | } 94 | 95 | 96 | 97 | tty_open(minor) 98 | int minor; 99 | { 100 | return(0); 101 | } 102 | 103 | 104 | tty_close(minor) 105 | int minor; 106 | { 107 | return(0); 108 | } 109 | 110 | 111 | tty_ioctl(minor) 112 | int minor; 113 | { 114 | return(-1); 115 | } 116 | 117 | 118 | 119 | /* This tty interrupt routine checks to see if the uart receiver actually 120 | caused the interrupt. If so it adds the character to the tty input 121 | queue, echoing and processing backspace and carriage return. If the queue 122 | contains a full line, it wakes up anything waiting on it. If it is totally 123 | full, it beeps at the user. */ 124 | 125 | tty_int() 126 | { 127 | register char c; 128 | register found; 129 | char oc; 130 | 131 | found = 0; 132 | 133 | again: 134 | if( (in(0x72)&0x81) != 0x81 ) 135 | return (found); 136 | c = in(0x73) & 0x7f; 137 | 138 | if (c==0x1a) /* ^Z */ 139 | idump(); /* For debugging */ 140 | 141 | if (c == '\003') /* ^C */ 142 | sendsig(NULL, SIGINT); 143 | else if (c == '\017') /* ^O */ 144 | flshflag = !flshflag; 145 | else if (c == '\023') /* ^S */ 146 | stopflag = 1; 147 | else if (c == '\021') /* ^Q */ 148 | { 149 | stopflag = 0; 150 | wakeup(&stopflag); 151 | } 152 | else if (c == '\b') 153 | { 154 | if (uninsq(&ttyinq,&oc)) 155 | { 156 | if (oc == '\n') 157 | insq(&ttyinq,oc); /* Don't erase past newline */ 158 | else 159 | { 160 | _putc('\b'); 161 | _putc(' '); 162 | _putc('\b'); 163 | } 164 | } 165 | } 166 | else 167 | { 168 | if (c == '\r' || c == '\n') 169 | { 170 | c = '\n'; 171 | _putc('\r'); 172 | } 173 | 174 | if (insq(&ttyinq,c)) 175 | _putc(c); 176 | else 177 | _putc('\007'); /* Beep if no more room */ 178 | } 179 | 180 | if (c == '\n' || c == '\004') /* ^D */ 181 | wakeup(&ttyinq); 182 | 183 | found = 1; 184 | goto again; /* Loop until the uart has no data ready */ 185 | } 186 | 187 | #ifdef vax 188 | 189 | _putc(c) 190 | char c; 191 | { 192 | write(1,&c,1); 193 | } 194 | 195 | #else 196 | 197 | _putc(c) 198 | char c; 199 | { 200 | while(!(in(0x72)&02)) 201 | ; 202 | out(c,0x73); 203 | } 204 | 205 | #endif 206 | 207 | 208 | -------------------------------------------------------------------------------- /devwd.c: -------------------------------------------------------------------------------- 1 | /************************************************** 2 | UZI (Unix Z80 Implementation) Kernel: devwd.c 3 | ***************************************************/ 4 | 5 | 6 | #include "unix.h" 7 | #include "extern.h" 8 | 9 | #define LUN 1 10 | #define RDCMD 0x28 11 | #define WRCMD 0x2a 12 | 13 | static char cmdblk[10] = { 0, LUN<<5, 0, 0, 0, 0, 0, 0, 0, 0 }; 14 | 15 | extern char *dptr; 16 | extern int dlen; 17 | extern char *cptr; 18 | extern int busid; 19 | extern scsiop(); 20 | 21 | wd_read(minor, rawflag) 22 | unsigned minor; 23 | int rawflag; 24 | { 25 | cmdblk[0] = RDCMD; 26 | if (setup(minor, rawflag)) 27 | return(0); 28 | 29 | chkstat(scsiop(),1); 30 | 31 | return(cmdblk[8] << 9); 32 | } 33 | 34 | wd_write(minor, rawflag) 35 | unsigned minor; 36 | int rawflag; 37 | { 38 | cmdblk[0] = WRCMD; 39 | if (setup(minor, rawflag)) 40 | return(0); 41 | 42 | chkstat(scsiop(),0); 43 | return(cmdblk[8] << 9); 44 | } 45 | 46 | 47 | static 48 | setup(minor, rawflag) 49 | unsigned minor; 50 | int rawflag; 51 | { 52 | register blkno_t block; 53 | 54 | cptr = cmdblk; 55 | busid = 1; 56 | 57 | if (rawflag) 58 | { 59 | if (rawflag == 2) 60 | { 61 | cmdblk[8] = swapcnt >> 9; 62 | dlen = swapcnt; 63 | dptr = swapbase; 64 | block = swapblk; 65 | } 66 | else 67 | { 68 | cmdblk[8] = udata.u_count >> 9; 69 | dlen = udata.u_count; 70 | dptr = udata.u_base; 71 | block = udata.u_offset.o_blkno; 72 | } 73 | } 74 | else 75 | { 76 | cmdblk[8] = 1; 77 | dlen = 512; 78 | dptr = udata.u_buf->bf_data; 79 | block = udata.u_buf->bf_blk; 80 | } 81 | 82 | block += (minor & 0xff00); 83 | if (block > (minor << 9)) 84 | { 85 | if (cmdblk[0] == WRCMD) 86 | udata.u_error = ENXIO; 87 | return (1); 88 | } 89 | 90 | cmdblk[5] = block; 91 | cmdblk[4] = block >> 8; 92 | return(0); 93 | } 94 | 95 | 96 | static 97 | chkstat(stat, rdflag) 98 | int stat; 99 | int rdflag; 100 | { 101 | if (stat) 102 | { 103 | kprintf("wd %s failure stat %x", rdflag ? "read": "write", stat); 104 | panic(""); 105 | } 106 | } 107 | 108 | 109 | wd_open(minor) 110 | int minor; 111 | { 112 | return(0); 113 | } 114 | 115 | 116 | /* The following is generic SCSI driver code, also used by devmt.c */ 117 | 118 | char *dptr; 119 | int dlen; 120 | char *cptr; 121 | int busid; 122 | 123 | scsiop() 124 | { 125 | 126 | #asm 8080 127 | ; 128 | ; 129 | OUTIR MACRO 130 | DB 0EDH 131 | DB 0B3H 132 | ENDM 133 | ; 134 | OUTI MACRO 135 | DB 0EDH 136 | DB 0A3H 137 | ENDM 138 | ; 139 | SDATA EQU 0D8H 140 | SCMD EQU 0D9H 141 | ; 142 | DMAPORT EQU 78H 143 | ; 144 | ;ENTRY POINT: 145 | ; 146 | PUSH B 147 | ; 148 | LOOP1: CALL SWAIT 149 | JZ HUNG ;ABORT IF PENDING TRANSACTION 150 | 151 | LDA busid? ;OUTPUT SCSI BUS ADDRESS 152 | OUT SDATA 153 | MVI A,1 ;SELECT CONTROLLER 154 | OUT SCMD ;ASSERT SELECT 155 | ..A2: IN SCMD ;WAIT FOR BSY TO BE ASSERTED 156 | ANI 01 157 | JNZ ..A2 158 | XRA A 159 | OUT SCMD ;DEASSERT IT 160 | ; 161 | LHLD cptr? 162 | .LOOP2: CALL SWAIT ;WAIT FOR REQ 163 | JNZ LOST 164 | IN SCMD 165 | ANI 1FH 166 | CPI 01100B ;CONTINUE AS LONG AS IT WANTS COMMANDS 167 | JNZ ECMD 168 | ANI 00100B 169 | JZ SEQ ;ABORT IF IT HAS A MESSAGE 170 | MOV A,M ;TRANSMIT COMMAND 171 | OUT SDATA 172 | INX H 173 | JMP .LOOP2 174 | ; 175 | ECMD: LHLD dlen? 176 | MOV A,H 177 | ORA L 178 | JZ EDATA ;SKIP DATA I/O IF NECESSARY 179 | CALL SWAIT 180 | JNZ LOST 181 | IN SCMD ;SEE IF IT REALLY WANTS DATA 182 | ANI 10H 183 | JZ EDATA 184 | IN SCMD 185 | ANI 08H ;CHECK FOR DATA READ OR WRITE 186 | JNZ WIO 187 | 188 | ; FILL IN THE DMA PROGRAM WITH THE CORRECT ADDRESS AND LENGTH 189 | LHLD dptr? 190 | SHLD RDADR 191 | LHLD dlen? 192 | DCX H 193 | SHLD RDCNT 194 | ; 195 | LXI H,RDBLK 196 | MVI B,RDLEN 197 | MVI C,DMAPORT 198 | OUTIR ;SEND PROGRAM TO DMA CONTROLLER 199 | JMP EDATA 200 | ; 201 | WIO: 202 | LHLD dptr? 203 | SHLD WRADR 204 | LHLD dlen? 205 | DCX H 206 | SHLD WRCNT 207 | ; 208 | LXI H,WRBLK 209 | MVI B,WRLEN 210 | MVI C,DMAPORT 211 | OUTIR ;SEND PROGRAM TO DMA CONTROLLER 212 | 213 | ; 214 | EDATA: 215 | CALL SWAIT ;WAIT UNTIL THE CONTROLLER WANTS TO SEND NON-DATA 216 | JNZ LOST 217 | IN SCMD 218 | ANI 10H 219 | JNZ EDATA 220 | ; 221 | ;GET STATUS AND SHUT DOWN 222 | ; 223 | MVI A,0A3H 224 | OUT DMAPORT ;TURN OFF DMA CONTROLLER 225 | 226 | IN SCMD 227 | ANI 1FH 228 | CPI 00100B 229 | JNZ SEQ ;JUMP IF IT DOESN'T WANT TO SEND STATUS 230 | ; 231 | IN SDATA 232 | MOV L,A 233 | MVI H,0 234 | CALL SWAIT 235 | JNZ LOST 236 | IN SCMD 237 | ANI 1FH 238 | CPI 00000B 239 | JNZ SEQ 240 | IN SDATA ;READ FINAL MESSAGE BYTE 241 | ; 242 | DONE: POP B 243 | MOV A,H 244 | ORA L 245 | RET 246 | ; 247 | ; 248 | LOST: LXI H,-1 249 | JMP DONE 250 | ; 251 | SEQ: CALL SWAIT 252 | LXI H,-2 253 | JNZ DONE 254 | IN SDATA ;EAT EXTRA DATA 255 | JMP SEQ 256 | ; 257 | HUNG: 258 | CALL WRESET 259 | LXI H,-3 260 | JMP DONE 261 | ; 262 | ; THIS WAITS FOR REQ TO BE ASSERTED OR FOR THE CONNECTION TO BE LOST. 263 | ;A NON-ZERO RETURN MEANS CONNECTION WAS LOST 264 | ; 265 | SWAIT: 266 | IN SCMD 267 | ANI 01 268 | RNZ ;RETURN IF NOT EVEN BUSY 269 | IN SCMD 270 | ANI 22H ;MASK OUT REQ AND MAKE SURE ACK FROM LAST CYCLE IS GONE 271 | JNZ SWAIT 272 | RET 273 | ; 274 | WRESET: 275 | MVI A,2 276 | OUT SCMD 277 | XTHL 278 | XTHL 279 | XTHL 280 | XTHL 281 | XRA A 282 | OUT SCMD 283 | RET 284 | ; 285 | ; 286 | ; THESE ARE THE DMA PROGRAMS FOR READ/WRITE 287 | ; 288 | RDBLK: ;PORT A IS THE CONTROLLER, PORT B IS THE MEMORY 289 | 290 | DB 0A3H ;RESET DMA 291 | DB 01101101B ;WR0 292 | DB SDATA 293 | RDCNT: DS 2 294 | DB 01101100B ;WR1 295 | DB 11001100B ;PORT A CYCLE LENGTH = 4 296 | DB 01010000B ;WR2 297 | DB 11001101B ;PORT B CYCLE LENGTH = 3 298 | DB 11001101B ;WR4 BURST MODE 299 | RDADR: DS 2 300 | DB 10001010B ;WR5 READY ACTIVE HIGH 301 | DB 0CFH ;WR6 LOAD COMMAND 302 | DB 87H ;WR6 GO COMMAND 303 | 304 | RDLEN EQU $ - RDBLK 305 | ; 306 | ; 307 | WRBLK: 308 | 309 | DB 0A3H ;RESET DMA 310 | DB 01101101B ;WR0 311 | DB SDATA 312 | WRCNT: DS 2 313 | DB 01101100B ;WR1 314 | DB 11001101B ;PORT A CYCLE LENGTH = 3 (FOR CONTROLLER) 315 | DB 01010000B ;WR2 316 | DB 11001101B ;PORT B CYCLE LENGTH = 3 317 | DB 11001101B ;WR4 BURST MODE 318 | WRADR: DS 2 319 | DB 10001010B ;WR5 READY ACTIVE HIGH 320 | DB 0CFH ;WR6 LOAD COMMAND 321 | DB 00000001B ;WR0 (ONLY DIFFERENCE) 322 | DB 0CFH ;WR6 LOAD COMMAND 323 | DB 87H ;WR6 GO COMMAND 324 | 325 | WRLEN EQU $ - WRBLK 326 | 327 | #endasm 328 | 329 | } 330 | 331 | -------------------------------------------------------------------------------- /dispatch.c: -------------------------------------------------------------------------------- 1 | /************************************************** 2 | UZI (Unix Z80 Implementation) Kernel: dispatch.c 3 | ***************************************************/ 4 | 5 | 6 | /* Dispatch table for system calls */ 7 | int __exit(), 8 | _open(), 9 | _close(), 10 | _creat(), 11 | _mknod(), 12 | _link(), 13 | _unlink(), 14 | _read(), 15 | _write(), 16 | _seek(), 17 | _chdir(), 18 | _sync(), 19 | _access(), 20 | _chmod(), 21 | _chown(), 22 | _stat(), 23 | _fstat(), 24 | _dup(), 25 | _getpid(), 26 | _getppid(), 27 | _getuid(), 28 | _umask(), 29 | _getfsys(), 30 | _execve(), 31 | _wait(), 32 | _setuid(), 33 | _setgid(), 34 | _time(), 35 | _stime(), 36 | _ioctl(), 37 | _brk(), 38 | _sbrk(), 39 | _fork(), 40 | _mount(), 41 | _umount(), 42 | _signal(), 43 | _dup2(), 44 | _pause(), 45 | _alarm(), 46 | _kill(), 47 | _pipe(), 48 | _getgid(), 49 | _times(); 50 | 51 | int (*disp_tab[])() = 52 | { __exit, 53 | _open, 54 | _close, 55 | _creat, 56 | _mknod, 57 | _link, 58 | _unlink, 59 | _read, 60 | _write, 61 | _seek, 62 | _chdir, 63 | _sync, 64 | _access, 65 | _chmod, 66 | _chown, 67 | _stat, 68 | _fstat, 69 | _dup, 70 | _getpid, 71 | _getppid, 72 | _getuid, 73 | _umask, 74 | _getfsys, 75 | _execve, 76 | _wait, 77 | _setuid, 78 | _setgid, 79 | _time, 80 | _stime, 81 | _ioctl, 82 | _brk, 83 | _sbrk, 84 | _fork, 85 | _mount, 86 | _umount, 87 | _signal, 88 | _dup2, 89 | _pause, 90 | _alarm, 91 | _kill, 92 | _pipe, 93 | _getgid, 94 | _times 95 | }; 96 | 97 | char dtsize = sizeof(disp_tab) / sizeof(int(*)()) - 1; 98 | 99 | 100 | -------------------------------------------------------------------------------- /extern.h: -------------------------------------------------------------------------------- 1 | /************************************************** 2 | UZI (Unix Z80 Implementation) Kernel: extern.h 3 | ***************************************************/ 4 | 5 | 6 | /* These are the global data structures */ 7 | 8 | #ifdef MAIN 9 | #define extern 10 | #endif 11 | 12 | 13 | extern struct u_data udata; /* MUST BE FIRST */ 14 | extern struct p_tab ptab[PTABSIZE]; 15 | 16 | extern inoptr root; /* Address of root dir in inode table */ 17 | extern int16 ROOTDEV; /* Device number of root filesystem. */ 18 | 19 | extern struct cinode i_tab[ITABSIZE]; /* In-core inode table */ 20 | extern struct oft of_tab[OFTSIZE]; /* Open File Table */ 21 | 22 | extern struct filesys fs_tab[NDEVS]; /* Table entry for each 23 | device with a filesystem. */ 24 | extern struct blkbuf bufpool[NBUFS]; 25 | 26 | extern ptptr initproc; /* The process table address of the first process. */ 27 | extern int16 inint; /* flag is set whenever interrupts are being serviced */ 28 | 29 | extern int16 sec; /* Tick counter for counting off one second */ 30 | extern int16 runticks; /* Number of ticks current process has been 31 | swapped in */ 32 | 33 | extern time_t tod; /* Time of day */ 34 | extern time_t ticks; /* Cumulative tick counter, in minutes and ticks */ 35 | 36 | extern char *swapbase; /* Used by device driver for swapping */ 37 | extern unsigned swapcnt; 38 | extern blkno_t swapblk; 39 | 40 | extern char vector[3]; /* Place for interrupt vector */ 41 | 42 | #ifdef MAIN 43 | #undef extern 44 | #endif 45 | 46 | 47 | -------------------------------------------------------------------------------- /extras.c: -------------------------------------------------------------------------------- 1 | /************************************************** 2 | UZI (Unix Z80 Implementation) Kernel: extras.c 3 | ***************************************************/ 4 | 5 | 6 | bcopy() 7 | { 8 | #asm 8080 9 | ; BCOPY(SRC,DEST,COUNT) 10 | ; 11 | POP H 12 | SHLD HOLDER 13 | .Z80 14 | LD (BCHLDR),BC 15 | .8080 16 | POP B 17 | POP D 18 | POP H 19 | PUSH H 20 | PUSH H 21 | PUSH H 22 | .Z80 23 | LDIR 24 | .8080 25 | LHLD HOLDER 26 | .Z80 27 | LD BC,(BCHLDR) 28 | .8080 29 | PCHL 30 | #endasm 31 | } 32 | 33 | #asm 34 | ; 35 | HOLDER: DS 2 36 | BCHLDR: DS 2 37 | ; 38 | ; 39 | #endasm 40 | 41 | 42 | bzero(ptr,count) 43 | char *ptr; 44 | int count; 45 | { 46 | *ptr = 0; 47 | bcopy(ptr,ptr+1,count-1); 48 | } 49 | 50 | 51 | abort() 52 | { 53 | #asm 8080 54 | DI 55 | JMP $ 56 | #endasm 57 | } 58 | 59 | -------------------------------------------------------------------------------- /filelist: -------------------------------------------------------------------------------- 1 | intro 2 | filelist 3 | config.h 4 | unix.h 5 | extern.h 6 | data.c 7 | dispatch.c 8 | machdep.c 9 | scall1.c 10 | scall2.c 11 | filesys.c 12 | process.c 13 | devio.c 14 | devtty.c 15 | devwd.c 16 | devflop.c 17 | devmisc.c 18 | extras.c 19 | filler.mac 20 | makeunix.sub 21 | loadunix.sub 22 | -------------------------------------------------------------------------------- /filesys.c: -------------------------------------------------------------------------------- 1 | /************************************************** 2 | UZI (Unix Z80 Implementation) Kernel: filesys.c 3 | ***************************************************/ 4 | 5 | 6 | #include "unix.h" 7 | #include "extern.h" 8 | 9 | 10 | char *bread(); 11 | 12 | 13 | /* N_open is given a string containing a path name, 14 | and returns a inode table pointer. If it returns NULL, 15 | the file did not exist. If the parent existed, 16 | and parent is not null, parent will be filled in with 17 | the parents inoptr. Otherwise, parent will be set to NULL. */ 18 | 19 | inoptr 20 | n_open(name,parent) 21 | register char *name; 22 | register inoptr *parent; 23 | { 24 | 25 | register inoptr wd; /* the directory we are currently searching. */ 26 | register inoptr ninode; 27 | register inoptr temp; 28 | inoptr srch_dir(); 29 | inoptr srch_mt(); 30 | 31 | if (*name == '/') 32 | wd = root; 33 | else 34 | wd = udata.u_cwd; 35 | 36 | i_ref(ninode = wd); 37 | i_ref(ninode); 38 | 39 | for(;;) 40 | { 41 | if (ninode) 42 | magic(ninode); 43 | 44 | /* See if we are at a mount point */ 45 | if (ninode) 46 | ninode = srch_mt(ninode); 47 | 48 | while (*name == '/') /* Skip (possibly repeated) slashes */ 49 | ++name; 50 | ifnot (*name) /* No more components of path? */ 51 | break; 52 | ifnot (ninode) 53 | { 54 | udata.u_error = ENOENT; 55 | goto nodir; 56 | } 57 | i_deref(wd); 58 | wd = ninode; 59 | if (getmode(wd) != F_DIR) 60 | { 61 | udata.u_error = ENOTDIR; 62 | goto nodir; 63 | } 64 | ifnot (getperm(wd) & OTH_EX) 65 | { 66 | udata.u_error = EPERM; 67 | goto nodir; 68 | } 69 | 70 | /* See if we are going up through a mount point */ 71 | if ( wd->c_num == ROOTINODE && wd->c_dev != ROOTDEV && name[1] == '.') 72 | { 73 | temp = fs_tab[wd->c_dev].s_mntpt; 74 | ++temp->c_refs; 75 | i_deref(wd); 76 | wd = temp; 77 | } 78 | 79 | ninode = srch_dir(wd,name); 80 | 81 | while (*name != '/' && *name ) 82 | ++name; 83 | } 84 | 85 | if (parent) 86 | *parent = wd; 87 | else 88 | i_deref(wd); 89 | ifnot (parent || ninode) 90 | udata.u_error = ENOENT; 91 | return (ninode); 92 | 93 | nodir: 94 | if (parent) 95 | *parent = NULLINODE; 96 | i_deref(wd); 97 | return(NULLINODE); 98 | 99 | } 100 | 101 | 102 | 103 | /* Srch_dir is given a inode pointer of an open directory 104 | and a string containing a filename, and searches the directory 105 | for the file. If it exists, it opens it and returns the inode pointer, 106 | otherwise NULL. This depends on the fact that ba_read will return unallocated 107 | blocks as zero-filled, and a partially allocated block will be padded with 108 | zeroes. */ 109 | 110 | inoptr 111 | srch_dir(wd,compname) 112 | register inoptr wd; 113 | register char *compname; 114 | { 115 | register int curentry; 116 | register blkno_t curblock; 117 | register struct direct *buf; 118 | register int nblocks; 119 | unsigned inum; 120 | inoptr i_open(); 121 | blkno_t bmap(); 122 | 123 | nblocks = wd->c_node.i_size.o_blkno; 124 | if (wd->c_node.i_size.o_offset) 125 | ++nblocks; 126 | 127 | for (curblock=0; curblock < nblocks; ++curblock) 128 | { 129 | buf = (struct direct *)bread( wd->c_dev, bmap(wd, curblock, 1), 0); 130 | for (curentry = 0; curentry < 32; ++curentry) 131 | { 132 | if (namecomp(compname,buf[curentry].d_name)) 133 | { 134 | inum = buf[curentry&0x1f].d_ino; 135 | brelse(buf); 136 | return(i_open(wd->c_dev, inum)); 137 | } 138 | } 139 | brelse(buf); 140 | } 141 | return(NULLINODE); 142 | } 143 | 144 | 145 | 146 | /* Srch_mt sees if the given inode is a mount point. If 147 | so it dereferences it, and references and returns a pointer 148 | to the root of the mounted filesystem. */ 149 | 150 | inoptr 151 | srch_mt(ino) 152 | register inoptr ino; 153 | { 154 | register int j; 155 | inoptr i_open(); 156 | 157 | for (j=0; j < NDEVS; ++j) 158 | if (fs_tab[j].s_mounted == SMOUNTED && fs_tab[j].s_mntpt == ino) 159 | { 160 | i_deref(ino); 161 | return(i_open(j,ROOTINODE)); 162 | } 163 | 164 | return(ino); 165 | } 166 | 167 | 168 | /* I_open is given an inode number and a device number, 169 | and makes an entry in the inode table for them, or 170 | increases it reference count if it is already there. 171 | An inode # of zero means a newly allocated inode */ 172 | 173 | inoptr 174 | i_open(dev,ino) 175 | register int dev; 176 | register unsigned ino; 177 | { 178 | 179 | struct dinode *buf; 180 | register inoptr nindex; 181 | int i; 182 | register inoptr j; 183 | int new; 184 | static nexti = i_tab; 185 | unsigned i_alloc(); 186 | 187 | if (dev<0 || dev>=NDEVS) 188 | panic("i_open: Bad dev"); 189 | 190 | new = 0; 191 | ifnot (ino) /* Want a new one */ 192 | { 193 | new = 1; 194 | ifnot (ino = i_alloc(dev)) 195 | { 196 | udata.u_error = ENOSPC; 197 | return (NULLINODE); 198 | } 199 | } 200 | 201 | if (ino < ROOTINODE || ino >= (fs_tab[dev].s_isize-2)*8) 202 | { 203 | warning("i_open: bad inode number"); 204 | return (NULLINODE); 205 | } 206 | 207 | 208 | nindex = NULLINODE; 209 | j = nexti; 210 | for (i=0; i < ITABSIZE; ++i) 211 | { 212 | nexti =j; 213 | if (++j >= i_tab+ITABSIZE) 214 | j = i_tab; 215 | 216 | ifnot (j->c_refs) 217 | nindex = j; 218 | 219 | if (j->c_dev == dev && j->c_num == ino) 220 | { 221 | nindex = j; 222 | goto found; 223 | } 224 | } 225 | 226 | /* Not already in table. */ 227 | 228 | ifnot (nindex) /* No unrefed slots in inode table */ 229 | { 230 | udata.u_error = ENFILE; 231 | return(NULLINODE); 232 | } 233 | 234 | buf = (struct dinode *)bread(dev, (ino>>3)+2, 0); 235 | bcopy((char *)&(buf[ino & 0x07]), (char *)&(nindex->c_node), 64); 236 | brelse(buf); 237 | 238 | nindex->c_dev = dev; 239 | nindex->c_num = ino; 240 | nindex->c_magic = CMAGIC; 241 | 242 | found: 243 | if (new) 244 | { 245 | if (nindex->c_node.i_nlink || nindex->c_node.i_mode & F_MASK) 246 | goto badino; 247 | } 248 | else 249 | { 250 | ifnot (nindex->c_node.i_nlink && nindex->c_node.i_mode & F_MASK) 251 | goto badino; 252 | } 253 | 254 | ++nindex->c_refs; 255 | return(nindex); 256 | 257 | badino: 258 | warning("i_open: bad disk inode"); 259 | return (NULLINODE); 260 | } 261 | 262 | 263 | 264 | /* Ch_link modifies or makes a new entry in the directory for the name 265 | and inode pointer given. The directory is searched for oldname. 266 | When found, it is changed to newname, and it inode # is that of 267 | *nindex. A oldname of "" matches a unused slot, and a nindex 268 | of NULLINODE means an inode # of 0. A return status of 0 means there 269 | was no space left in the filesystem, or a non-empty oldname was not found, 270 | or the user did not have write permission. */ 271 | 272 | ch_link(wd,oldname,newname,nindex) 273 | register inoptr wd; 274 | char *oldname; 275 | char *newname; 276 | inoptr nindex; 277 | { 278 | struct direct curentry; 279 | 280 | ifnot (getperm(wd) & OTH_WR) 281 | { 282 | udata.u_error = EPERM; 283 | return (0); 284 | } 285 | 286 | /* Search the directory for the desired slot. */ 287 | 288 | udata.u_offset.o_blkno = 0; 289 | udata.u_offset.o_offset = 0; 290 | 291 | for (;;) 292 | { 293 | udata.u_count = 16; 294 | udata.u_base = (char *)&curentry; 295 | readi(wd); 296 | 297 | /* Read until EOF or name is found */ 298 | /* readi() advances udata.u_offset */ 299 | if (udata.u_count == 0 || namecomp(oldname, curentry.d_name)) 300 | break; 301 | } 302 | 303 | if (udata.u_count == 0 && *oldname) 304 | return (0); /* Entry not found */ 305 | 306 | bcopy(newname, curentry.d_name, 14); 307 | if (nindex) 308 | curentry.d_ino = nindex->c_num; 309 | else 310 | curentry.d_ino = 0; 311 | 312 | /* If an existing slot is being used, we must back up the file offset */ 313 | if (udata.u_count) 314 | { 315 | ifnot (udata.u_offset.o_offset) 316 | { 317 | --udata.u_offset.o_blkno; 318 | udata.u_offset.o_offset = 512; 319 | } 320 | udata.u_offset.o_offset -= 16; 321 | } 322 | 323 | udata.u_count = 16; 324 | udata.u_base = (char *)&curentry; 325 | writei(wd); 326 | 327 | if (udata.u_error) 328 | return (0); 329 | 330 | setftime(wd, A_TIME|M_TIME|C_TIME); /* Sets c_dirty */ 331 | 332 | /* Update file length to next block */ 333 | if (wd->c_node.i_size.o_offset) 334 | { 335 | wd->c_node.i_size.o_offset = 0; 336 | ++wd->c_node.i_size.o_blkno; 337 | } 338 | 339 | return (1); 340 | } 341 | 342 | 343 | 344 | /* Filename is given a path name, and returns a pointer 345 | to the final component of it. */ 346 | 347 | char * 348 | filename(path) 349 | char *path; 350 | { 351 | register char *ptr; 352 | 353 | ptr = path; 354 | while (*ptr) 355 | ++ptr; 356 | while (*ptr != '/' && ptr-- > path) 357 | ; 358 | return (ptr+1); 359 | } 360 | 361 | 362 | /* Namecomp compares two strings to see if they are the same file name. 363 | It stops at 14 chars or a null or a slash. It returns 0 for difference. */ 364 | 365 | namecomp(n1,n2) 366 | register char *n1; 367 | register char *n2; 368 | { 369 | register int n; 370 | 371 | n = 14; 372 | while (*n1 && *n1 != '/') 373 | { 374 | if (*n1++ != *n2++) 375 | return(0); 376 | ifnot (--n) 377 | return(-1); 378 | } 379 | return(*n2 == '\0' || *n2 == '/'); 380 | } 381 | 382 | 383 | 384 | /* Newfile is given a pointer to a directory and a name, and 385 | creates an entry in the directory for the name, dereferences 386 | the parent, and returns a pointer to the new inode. 387 | It allocates an inode number, 388 | and creates a new entry in the inode table for the new file, 389 | and initializes the inode table entry for the new file. The new file 390 | will have one reference, and 0 links to it. 391 | Better make sure there isn't already an entry with the same name. */ 392 | 393 | inoptr 394 | newfile(pino, name) 395 | register inoptr pino; 396 | register char *name; 397 | { 398 | 399 | register inoptr nindex; 400 | register int j; 401 | inoptr i_open(); 402 | 403 | ifnot (nindex = i_open(pino->c_dev, 0)) 404 | goto nogood; 405 | 406 | nindex->c_node.i_mode = F_REG; /* For the time being */ 407 | nindex->c_node.i_nlink = 1; 408 | nindex->c_node.i_size.o_offset = 0; 409 | nindex->c_node.i_size.o_blkno = 0; 410 | for (j=0; j <20; j++) 411 | nindex->c_node.i_addr[j] = 0; 412 | wr_inode(nindex); 413 | 414 | ifnot (ch_link(pino,"",filename(name),nindex)) 415 | { 416 | i_deref(nindex); 417 | goto nogood; 418 | } 419 | 420 | i_deref (pino); 421 | return(nindex); 422 | 423 | nogood: 424 | i_deref (pino); 425 | return (NULLINODE); 426 | } 427 | 428 | 429 | 430 | /* Check the given device number, and return its address in the mount table. 431 | Also time-stamp the superblock of dev, and mark it modified. 432 | Used when freeing and allocating blocks and inodes. */ 433 | 434 | fsptr 435 | getdev(devno) 436 | register int devno; 437 | { 438 | register fsptr dev; 439 | 440 | dev = fs_tab + devno; 441 | if (devno < 0 || devno >= NDEVS || !dev->s_mounted) 442 | panic("getdev: bad dev"); 443 | rdtime(&(dev->s_time)); 444 | dev->s_fmod = 1; 445 | return (dev); 446 | } 447 | 448 | 449 | /* Returns true if the magic number of a superblock is corrupt */ 450 | 451 | baddev(dev) 452 | fsptr dev; 453 | { 454 | return (dev->s_mounted != SMOUNTED); 455 | } 456 | 457 | 458 | /* I_alloc finds an unused inode number, and returns it, 459 | or 0 if there are no more inodes available. */ 460 | 461 | unsigned 462 | i_alloc(devno) 463 | int devno; 464 | { 465 | fsptr dev; 466 | blkno_t blk; 467 | struct dinode *buf; 468 | register int j; 469 | register int k; 470 | unsigned ino; 471 | 472 | if (baddev(dev = getdev(devno))) 473 | goto corrupt; 474 | 475 | tryagain: 476 | if (dev->s_ninode) 477 | { 478 | ifnot (dev->s_tinode) 479 | goto corrupt; 480 | ino = dev->s_inode[--dev->s_ninode]; 481 | if (ino < 2 || ino >= (dev->s_isize-2)*8) 482 | goto corrupt; 483 | --dev->s_tinode; 484 | return(ino); 485 | } 486 | 487 | /* We must scan the inodes, and fill up the table */ 488 | 489 | _sync(); /* Make on-disk inodes consistent */ 490 | k = 0; 491 | for (blk = 2; blk < dev->s_isize; blk++) 492 | { 493 | buf = (struct dinode *)bread(devno, blk, 0); 494 | for (j=0; j < 8; j++) 495 | { 496 | ifnot (buf[j].i_mode || buf[j].i_nlink) 497 | dev->s_inode[k++] = 8*(blk-2) + j; 498 | if (k==50) 499 | { 500 | brelse(buf); 501 | goto done; 502 | } 503 | } 504 | brelse(buf); 505 | } 506 | 507 | done: 508 | ifnot (k) 509 | { 510 | if (dev->s_tinode) 511 | goto corrupt; 512 | udata.u_error = ENOSPC; 513 | return(0); 514 | } 515 | 516 | dev->s_ninode = k; 517 | goto tryagain; 518 | 519 | corrupt: 520 | warning("i_alloc: corrupt superblock"); 521 | dev->s_mounted = 1; 522 | udata.u_error = ENOSPC; 523 | return(0); 524 | } 525 | 526 | 527 | /* I_free is given a device and inode number, 528 | and frees the inode. It is assumed that there 529 | are no references to the inode in the inode table or 530 | in the filesystem. */ 531 | 532 | i_free(devno, ino) 533 | register int devno; 534 | register unsigned ino; 535 | { 536 | register fsptr dev; 537 | 538 | if (baddev(dev = getdev(devno))) 539 | return; 540 | 541 | if (ino < 2 || ino >= (dev->s_isize-2)*8) 542 | panic("i_free: bad ino"); 543 | 544 | ++dev->s_tinode; 545 | if (dev->s_ninode < 50) 546 | dev->s_inode[dev->s_ninode++] = ino; 547 | } 548 | 549 | 550 | /* Blk_alloc is given a device number, and allocates an unused block 551 | from it. A returned block number of zero means no more blocks. */ 552 | 553 | blkno_t 554 | blk_alloc(devno) 555 | register int devno; 556 | { 557 | 558 | register fsptr dev; 559 | register blkno_t newno; 560 | blkno_t *buf; 561 | register int j; 562 | 563 | if (baddev(dev = getdev(devno))) 564 | goto corrupt2; 565 | 566 | if (dev->s_nfree <= 0 || dev->s_nfree > 50) 567 | goto corrupt; 568 | 569 | newno = dev->s_free[--dev->s_nfree]; 570 | ifnot (newno) 571 | { 572 | if (dev->s_tfree != 0) 573 | goto corrupt; 574 | udata.u_error = ENOSPC; 575 | ++dev->s_nfree; 576 | return(0); 577 | } 578 | 579 | /* See if we must refill the s_free array */ 580 | 581 | ifnot (dev->s_nfree) 582 | { 583 | buf = (blkno_t *)bread(devno,newno, 0); 584 | dev->s_nfree = buf[0]; 585 | for (j=0; j < 50; j++) 586 | { 587 | dev->s_free[j] = buf[j+1]; 588 | } 589 | brelse((char *)buf); 590 | } 591 | 592 | validblk(devno, newno); 593 | 594 | ifnot (dev->s_tfree) 595 | goto corrupt; 596 | --dev->s_tfree; 597 | 598 | /* Zero out the new block */ 599 | buf = bread(devno, newno, 2); 600 | bzero(buf, 512); 601 | bawrite(buf); 602 | return(newno); 603 | 604 | corrupt: 605 | warning("blk_alloc: corrupt"); 606 | dev->s_mounted = 1; 607 | corrupt2: 608 | udata.u_error = ENOSPC; 609 | return(0); 610 | } 611 | 612 | 613 | /* Blk_free is given a device number and a block number, 614 | and frees the block. */ 615 | 616 | blk_free(devno,blk) 617 | register int devno; 618 | register blkno_t blk; 619 | { 620 | register fsptr dev; 621 | register char *buf; 622 | 623 | ifnot (blk) 624 | return; 625 | 626 | if (baddev(dev = getdev(devno))) 627 | return; 628 | 629 | validblk(devno, blk); 630 | 631 | if (dev->s_nfree == 50) 632 | { 633 | buf = bread(devno, blk, 1); 634 | bcopy((char *)&(dev->s_nfree), buf, 512); 635 | bawrite(buf); 636 | dev->s_nfree = 0; 637 | } 638 | 639 | ++dev->s_tfree; 640 | dev->s_free[(dev->s_nfree)++] = blk; 641 | 642 | } 643 | 644 | 645 | 646 | 647 | /* Oft_alloc and oft_deref allocate and dereference (and possibly free) 648 | entries in the open file table. */ 649 | 650 | oft_alloc() 651 | { 652 | register int j; 653 | 654 | for (j=0; j < OFTSIZE ; ++j) 655 | { 656 | ifnot (of_tab[j].o_refs) 657 | { 658 | of_tab[j].o_refs = 1; 659 | of_tab[j].o_inode = NULLINODE; 660 | return (j); 661 | } 662 | } 663 | udata.u_error = ENFILE; 664 | return(-1); 665 | } 666 | 667 | oft_deref(of) 668 | register int of; 669 | { 670 | register struct oft *ofptr; 671 | 672 | ofptr = of_tab + of; 673 | 674 | if (!(--ofptr->o_refs) && ofptr->o_inode) 675 | { 676 | i_deref(ofptr->o_inode); 677 | ofptr->o_inode = NULLINODE; 678 | } 679 | } 680 | 681 | 682 | 683 | /* Uf_alloc finds an unused slot in the user file table. */ 684 | 685 | uf_alloc() 686 | { 687 | register int j; 688 | 689 | for (j=0; j < UFTSIZE ; ++j) 690 | { 691 | if (udata.u_files[j] & 0x80) /* Portable, unlike == -1 */ 692 | { 693 | return (j); 694 | } 695 | } 696 | udata.u_error = ENFILE; 697 | return(-1); 698 | } 699 | 700 | 701 | 702 | /* I_ref increases the reference count of the given inode table entry. */ 703 | 704 | i_ref(ino) 705 | inoptr ino; 706 | { 707 | if (++(ino->c_refs) == 2*ITABSIZE) /* Arbitrary limit. */ 708 | panic("too many i-refs"); 709 | } 710 | 711 | 712 | /* I_deref decreases the reference count of an inode, and frees it 713 | from the table if there are no more references to it. If it also 714 | has no links, the inode itself and its blocks (if not a device) is freed. */ 715 | 716 | i_deref(ino) 717 | register inoptr ino; 718 | { 719 | magic(ino); 720 | 721 | ifnot (ino->c_refs) 722 | panic("inode freed."); 723 | 724 | if ((ino->c_node.i_mode & F_MASK) == F_PIPE) 725 | wakeup((char *)ino); 726 | 727 | /* If the inode has no links and no refs, it must have 728 | its blocks freed. */ 729 | 730 | ifnot (--ino->c_refs || ino->c_node.i_nlink) 731 | f_trunc(ino); 732 | 733 | /* If the inode was modified, we must write it to disk. */ 734 | if (!(ino->c_refs) && ino->c_dirty) 735 | { 736 | ifnot (ino->c_node.i_nlink) 737 | { 738 | ino->c_node.i_mode = 0; 739 | i_free(ino->c_dev, ino->c_num); 740 | } 741 | wr_inode(ino); 742 | } 743 | } 744 | 745 | 746 | /* Wr_inode writes out the given inode in the inode table out to disk, 747 | and resets its dirty bit. */ 748 | 749 | wr_inode(ino) 750 | register inoptr ino; 751 | { 752 | struct dinode *buf; 753 | register blkno_t blkno; 754 | 755 | magic(ino); 756 | 757 | blkno = (ino->c_num >> 3) + 2; 758 | buf = (struct dinode *)bread(ino->c_dev, blkno,0); 759 | bcopy((char *)(&ino->c_node), 760 | (char *)((char **)&buf[ino->c_num & 0x07]), 64); 761 | bfree(buf, 2); 762 | ino->c_dirty = 0; 763 | } 764 | 765 | 766 | /* isdevice(ino) returns true if ino points to a device */ 767 | isdevice(ino) 768 | inoptr ino; 769 | { 770 | return (ino->c_node.i_mode & 020000); 771 | } 772 | 773 | 774 | /* This returns the device number of an inode representing a device */ 775 | devnum(ino) 776 | inoptr ino; 777 | { 778 | return (*(ino->c_node.i_addr)); 779 | } 780 | 781 | 782 | /* F_trunc frees all the blocks associated with the file, 783 | if it is a disk file. */ 784 | 785 | f_trunc(ino) 786 | register inoptr ino; 787 | { 788 | int dev; 789 | int j; 790 | 791 | dev = ino->c_dev; 792 | 793 | /* First deallocate the double indirect blocks */ 794 | freeblk(dev, ino->c_node.i_addr[19], 2); 795 | 796 | /* Also deallocate the indirect blocks */ 797 | freeblk(dev, ino->c_node.i_addr[18], 1); 798 | 799 | /* Finally, free the direct blocks */ 800 | for (j=17; j >= 0; --j) 801 | freeblk(dev, ino->c_node.i_addr[j], 0); 802 | 803 | bzero((char *)ino->c_node.i_addr, sizeof(ino->c_node.i_addr)); 804 | 805 | ino->c_dirty = 1; 806 | ino->c_node.i_size.o_blkno = 0; 807 | ino->c_node.i_size.o_offset = 0; 808 | } 809 | 810 | 811 | /* Companion function to f_trunc(). */ 812 | freeblk(dev, blk, level) 813 | int dev; 814 | blkno_t blk; 815 | int level; 816 | { 817 | blkno_t *buf; 818 | int j; 819 | 820 | ifnot (blk) 821 | return; 822 | 823 | if (level) 824 | { 825 | buf = (blkno_t *)bread(dev, blk, 0); 826 | for (j=255; j >= 0; --j) 827 | freeblk(dev, buf[j], level-1); 828 | brelse((char *)buf); 829 | } 830 | 831 | blk_free(dev,blk); 832 | } 833 | 834 | 835 | 836 | /* Changes: blk_alloc zeroes block it allocates */ 837 | 838 | /* 839 | * Bmap defines the structure of file system storage 840 | * by returning the physical block number on a device given the 841 | * inode and the logical block number in a file. 842 | * The block is zeroed if created. 843 | */ 844 | blkno_t 845 | bmap(ip, bn, rwflg) 846 | inoptr ip; 847 | register blkno_t bn; 848 | int rwflg; 849 | { 850 | register int i; 851 | register bufptr bp; 852 | register int j; 853 | register blkno_t nb; 854 | int sh; 855 | int dev; 856 | 857 | blkno_t blk_alloc(); 858 | 859 | if (getmode(ip) == F_BDEV) 860 | return (bn); 861 | 862 | dev = ip->c_dev; 863 | 864 | /* 865 | * blocks 0..17 are direct blocks 866 | */ 867 | if(bn < 18) { 868 | nb = ip->c_node.i_addr[bn]; 869 | if(nb == 0) { 870 | if(rwflg || (nb = blk_alloc(dev))==0) 871 | return(NULLBLK); 872 | ip->c_node.i_addr[bn] = nb; 873 | ip->c_dirty = 1; 874 | } 875 | return(nb); 876 | } 877 | 878 | /* 879 | * addresses 18 and 19 880 | * have single and double indirect blocks. 881 | * the first step is to determine 882 | * how many levels of indirection. 883 | */ 884 | bn -= 18; 885 | sh = 0; 886 | j = 2; 887 | if (bn & 0xff00) /* bn > 255 so double indirect */ 888 | { 889 | sh = 8; 890 | bn -= 256; 891 | j = 1; 892 | } 893 | 894 | /* 895 | * fetch the address from the inode 896 | * Create the first indirect block if needed. 897 | */ 898 | ifnot (nb = ip->c_node.i_addr[20-j]) 899 | { 900 | if(rwflg || !(nb = blk_alloc(dev))) 901 | return(NULLBLK); 902 | ip->c_node.i_addr[20-j] = nb; 903 | ip->c_dirty = 1; 904 | } 905 | 906 | /* 907 | * fetch through the indirect blocks 908 | */ 909 | for(; j<=2; j++) { 910 | bp = (bufptr)bread(dev, nb, 0); 911 | /****** 912 | if(bp->bf_error) { 913 | brelse(bp); 914 | return((blkno_t)0); 915 | } 916 | ******/ 917 | i = (bn>>sh) & 0xff; 918 | if (nb = ((blkno_t *)bp)[i]) 919 | brelse(bp); 920 | else 921 | { 922 | if(rwflg || !(nb = blk_alloc(dev))) { 923 | brelse(bp); 924 | return(NULLBLK); 925 | } 926 | ((blkno_t *)bp)[i] = nb; 927 | bawrite(bp); 928 | } 929 | sh -= 8; 930 | } 931 | 932 | return(nb); 933 | } 934 | 935 | 936 | 937 | /* Validblk panics if the given block number is not a valid data block 938 | for the given device. */ 939 | 940 | validblk(dev, num) 941 | int dev; 942 | blkno_t num; 943 | { 944 | register fsptr devptr; 945 | 946 | devptr = fs_tab + dev; 947 | 948 | if (devptr->s_mounted == 0) 949 | panic("validblk: not mounted"); 950 | 951 | if (num < devptr->s_isize || num >= devptr->s_fsize) 952 | panic("validblk: invalid blk"); 953 | } 954 | 955 | 956 | 957 | /* This returns the inode pointer associated with a user's 958 | file descriptor, checking for valid data structures */ 959 | 960 | inoptr 961 | getinode(uindex) 962 | register int uindex; 963 | { 964 | register int oftindex; 965 | register inoptr inoindex; 966 | 967 | if (uindex < 0 || uindex >= UFTSIZE || udata.u_files[uindex] & 0x80 ) 968 | { 969 | udata.u_error = EBADF; 970 | return (NULLINODE); 971 | } 972 | 973 | if ((oftindex = udata.u_files[uindex]) < 0 || oftindex >= OFTSIZE) 974 | panic("Getinode: bad desc table"); 975 | 976 | if ((inoindex = of_tab[oftindex].o_inode) < i_tab || 977 | inoindex >= i_tab+ITABSIZE) 978 | panic("Getinode: bad OFT"); 979 | 980 | magic(inoindex); 981 | 982 | return(inoindex); 983 | } 984 | 985 | /* Super returns true if we are the superuser */ 986 | super() 987 | { 988 | return(udata.u_euid == 0); 989 | } 990 | 991 | /* Getperm looks at the given inode and the effective user/group ids, and 992 | returns the effective permissions in the low-order 3 bits. */ 993 | 994 | getperm(ino) 995 | inoptr ino; 996 | { 997 | int mode; 998 | 999 | if (super()) 1000 | return(07); 1001 | 1002 | mode = ino->c_node.i_mode; 1003 | if (ino->c_node.i_uid == udata.u_euid) 1004 | mode >>= 6; 1005 | else if (ino->c_node.i_gid == udata.u_egid) 1006 | mode >>= 3; 1007 | 1008 | return(mode & 07); 1009 | } 1010 | 1011 | 1012 | /* This sets the times of the given inode, according to the flags */ 1013 | 1014 | setftime(ino, flag) 1015 | register inoptr ino; 1016 | register int flag; 1017 | { 1018 | ino->c_dirty = 1; 1019 | 1020 | if (flag & A_TIME) 1021 | rdtime(&(ino->c_node.i_atime)); 1022 | if (flag & M_TIME) 1023 | rdtime(&(ino->c_node.i_mtime)); 1024 | if (flag & C_TIME) 1025 | rdtime(&(ino->c_node.i_ctime)); 1026 | } 1027 | 1028 | 1029 | getmode(ino) 1030 | inoptr ino; 1031 | { 1032 | return( ino->c_node.i_mode & F_MASK); 1033 | } 1034 | 1035 | 1036 | /* Fmount places the given device in the mount table with 1037 | mount point ino */ 1038 | 1039 | fmount(dev,ino) 1040 | register int dev; 1041 | register inoptr ino; 1042 | { 1043 | char *buf; 1044 | register struct filesys *fp; 1045 | 1046 | if (d_open(dev) != 0) 1047 | panic("fmount: Cant open filesystem"); 1048 | /* Dev 0 blk 1 */ 1049 | fp = fs_tab + dev; 1050 | buf = bread(dev, 1, 0); 1051 | bcopy(buf, (char *)fp, sizeof(struct filesys)); 1052 | brelse(buf); 1053 | 1054 | /* See if there really is a filesystem on the device */ 1055 | if (fp->s_mounted != SMOUNTED || 1056 | fp->s_isize >= fp->s_fsize) 1057 | return (-1); 1058 | 1059 | fp->s_mntpt = ino; 1060 | if (ino) 1061 | ++ino->c_refs; 1062 | 1063 | return (0); 1064 | } 1065 | 1066 | 1067 | magic(ino) 1068 | inoptr ino; 1069 | { 1070 | if (ino->c_magic != CMAGIC) 1071 | panic("Corrupt inode"); 1072 | } 1073 | 1074 | -------------------------------------------------------------------------------- /filler.mac: -------------------------------------------------------------------------------- 1 | ;************************************************** 2 | ; UZI (Unix Z80 Implementation) Kernel: filler.mac 3 | ;************************************************** 4 | 5 | 6 | dseg 7 | start: db 1,2,3,4,5,6,7,8 8 | ds 8000h-8-8-3-3-100h 9 | db "MOMBASSA" 10 | ;this should be at address 7ffd. 11 | end start 12 | 13 | -------------------------------------------------------------------------------- /intro: -------------------------------------------------------------------------------- 1 | UZI: UNIX Z-80 IMPLEMENTATION 2 | 3 | Written by Douglas Braun 4 | 5 | 6 | Introduction: 7 | 8 | UZI is an implementation of the Unix kernel written for a Z-80 based 9 | computer. It implementts almost all of the functionality of the 10 | 7th Edition Unix kernel. UZI was written to run on one specific 11 | collection of custom-built hardware, but since it can easily have device 12 | drivers added to it, and it does not use any memory management hardware, 13 | it should be possible to port it to numerous computers that current use 14 | the CP/M operating system. The source code is written mostly in C, 15 | and was compiled with The Code Works' Q/C compiler. UZI's code was 16 | written from scratch, and contains no AT&T code, so it is not subject 17 | to any of AT&T's copyright or licensing restrictions. Numerous 7th 18 | Edition programs have been ported to UZI with little or no difficulty, 19 | including the complete Bourne shell, ed, sed, dc, cpp, etc. 20 | 21 | 22 | How it works: 23 | 24 | Since there is no standard memory management hardware on 8080-family 25 | computers, UZI uses "total swapping" to achieve multiprocessing. 26 | This has two implications: First, UZI requires a reasonably fast 27 | hard disk. Second, there is no point in running a different process 28 | while a process is waiting for disk I/O. This simplifies the design 29 | of the block device drivers, since they do not have to be interrupt-based. 30 | 31 | UZI itself occupies the upper 32K of memory, and the currently running 32 | process occupies the lower 32K. Since UZI currently barely fits in 32K, 33 | a full 64K of RAM is necessary. 34 | 35 | UZI does need some additional hardware support. First, there must be 36 | some sort of clock or timer that can provide a periodic interrupt. 37 | Also, the current implementation uses an additional real-time clock 38 | to get the time for file timestamps, etc. The current TTY driver assumes 39 | an interrupt-driven keyboard, which should exist on most systems. 40 | The distribution contains code for hard and floppy disk drivers, but 41 | since these were written for custom hardware, they are provided only 42 | as templates to write new ones. 43 | 44 | 45 | How UZI is different than real Unix: 46 | 47 | UZI implements almost all of the 7th Edition functionality. 48 | All file I/O, directories, mountable file systems, user and group IDs, 49 | pipes, and applicable device I/O are supported. Process control 50 | (fork(), execve(), signal(), kill(), pause(), alarm(), and wait()) are fully 51 | supported. The number of processes is limited only by the swap space 52 | available. As mentioned above, UZI implements Unix well enough to 53 | run the Bourne shell in its full functionality. The only changes made 54 | to the shell's source code were to satisfy the limitations of the C compiler. 55 | 56 | Here is a (possibly incomplete) list of missing features and limitations: 57 | 58 | The debugger- and profiler-related system calls do not exist. 59 | 60 | The old 6th edition seek() was implemented, instead of lseek(). 61 | 62 | The supplied TTY driver is bare-bones. It supports only one port, 63 | and most IOCTLs are not supported. 64 | 65 | Inode numbers are only 16-bit, so filesystems are 32 Meg or less. 66 | 67 | File dates are not in the standard format. Instead they look like 68 | those used by MS-DOS. 69 | 70 | The 4.2BSD execve() was implemented. Additional flavors of exec() 71 | are supported by the library. 72 | 73 | The format of the device driver switch table is unlike that of 74 | the 7th Edition. 75 | 76 | The necessary semaphores and locking mechanisms to implement 77 | reentrant disk I/O are not there. This would make it harder to 78 | implement interrupt-driven disk I/O without busy-waiting. 79 | 80 | 81 | A Description of this Release: 82 | 83 | Here is a list of the files supplied, and a brief description of each: 84 | 85 | 86 | intro: What you are reading 87 | 88 | config.h: Setup parameters, such as table sizes, and the device 89 | driver switch table. 90 | 91 | unix.h: All strcuture declarations, typedefs and defines. 92 | (Includes things like errno.h). 93 | 94 | extern.h: Declarations of all global variables and tables. 95 | 96 | data.c: Dummy to source extern.h and devine globals. 97 | 98 | dispatch.c: System call dispatch table. 99 | 100 | scall1.c: System calls, mostly file-related. 101 | 102 | scall2.c: Rest of system calls. 103 | 104 | filesys.c: Routines for managing file system. 105 | 106 | process.c: Routines for process management and context switching. 107 | Somewhat machine-dependent. 108 | 109 | devio.c: Generic I/O routines, including queue routines. 110 | 111 | devtty.c: Simple TTY driver, slightly-machine dependent. 112 | 113 | devwd.c: Hard disk driver. Very machine-dependent. 114 | 115 | devflop.c: Floppy disk driver. Very machine-dependent. 116 | 117 | devmisc.c: Simple device drivers, such as /dev/mem. 118 | 119 | machdep.c: Machine-dependent code, especially real-time-clock and 120 | interrupt handling code. 121 | 122 | extras.c: Procedures missing from the Q/C compiler's library. 123 | 124 | filler.mac: Dummy to make linker load UZI at correct address. 125 | 126 | makeunix.sub: CP/M SUBMIT file to compile everything. 127 | 128 | loadunix.sub: CP/M SUBMIT file to load everything. 129 | 130 | 131 | Miscellaneous Notes: 132 | 133 | UZI was compiled with the Code Works Q/C C compiler and the Microsoft 134 | M80 assembler under the CP/M operating system, on the same hardware 135 | it runs on. Also used was a version of cpp ported to CP/M, since 136 | the Q/C compiler does not handle macros with arguments. However, there 137 | are only a couple of these in the code, and they could easily be removed. 138 | 139 | Because UZI occupies the upper 32K of memory, the standard L80 linker 140 | could not be used to link it. Instead, a homebrew L80 replacement linker 141 | was used. This generated a 64K-byte CP/M .COM file, which has the lower 142 | 32K pruned by the CP/M PIP utility. This is the reason for appearance 143 | of the string "MOMBASSA" in filler.mac and loadunix.sub. 144 | 145 | To boot UZI, a short CP/M program was run that reads in the UZI image, 146 | copies it to the upper 32K of memory, and jumps to its start address. 147 | Other CP/M programs were written to build, inspect, and check UZI filesystems 148 | under CP/M. These made it possible to have a root file system made before 149 | starting up UZI. If the demand exists, these programs can be included 150 | in another release. 151 | 152 | 153 | Running programs under UZI: 154 | 155 | A number of 7th Edition, System V, and 4.2BSD programs were ported to 156 | UZI. Most notably, the Bourne shell and ed run fine under UZI. 157 | In addition the 4.2BSD stdio library was also ported. This, along 158 | with the Code Works Q/C library and miscellaneous System V library 159 | functions, was used when porting programs. 160 | 161 | Due to obvious legal reasons, the source or executables for most of these 162 | programs cannot be released. However, some kernel-dependent programs 163 | such as ps and fsck were written from scratch and can be included in future 164 | releases. Also, a package was created that can be linked to CP/M .COM 165 | files that will allow them to run under UZI. This was used to get 166 | the M80 assembler and L80 linker to run under UZI. Cpp was also 167 | ported to UZI. However, it was not possible to fit the Q/C compiler 168 | into 32K, so all programs (and UZI itself) were cross-compiled under CP/M. 169 | 170 | The Minix operating system, written for PCs by Andrew Tanenbaum et al, 171 | contains many programs that should compile and run under UZI. Since 172 | Minix is much less encumbered by licensing provisions than real Unix, 173 | it would make sense to port Minix programs to UZI. In fact, UZI itself 174 | could be ported to the PC, and used as a replacement for the Minix kernel. 175 | -------------------------------------------------------------------------------- /loadunix.sub: -------------------------------------------------------------------------------- 1 | loader $1 -o 500 filler data process machdep dispatch scall1 scall2 filesys devio devtty devwd devflop devmisc extras 2 | pip unix.bin=$$$$$$.com[osmombassa^Z] 3 | era $$$$$$.com 4 | -------------------------------------------------------------------------------- /machdep.c: -------------------------------------------------------------------------------- 1 | /************************************************** 2 | UZI (Unix Z80 Implementation) Kernel: machdep.c 3 | ***************************************************/ 4 | 5 | 6 | #include "unix.h" 7 | #include "extern.h" 8 | 9 | 10 | /* This is called at the very beginning to initialize everything. */ 11 | /* It is the equivalent of main() */ 12 | 13 | fs_init() 14 | { 15 | di(); 16 | stkreset(); 17 | /* Initialize the interrupt vector */ 18 | initvec(); 19 | inint = 0; 20 | udata.u_insys = 1; 21 | /* Turn off clock */ 22 | out(0,0xf1); 23 | ei(); 24 | 25 | init2(); /* in process.c */ 26 | } 27 | 28 | 29 | /* This checks to see if a user-suppled address is legitimate */ 30 | valadr(base,size) 31 | char *base; 32 | uint16 size; 33 | { 34 | if (base < PROGBASE || base+size >= (char *)&udata) 35 | { 36 | udata.u_error = EFAULT; 37 | return(0); 38 | } 39 | return(1); 40 | } 41 | 42 | 43 | /* This adds two tick counts together. 44 | The t_time field holds up to one second of ticks, 45 | while the t_date field counts minutes */ 46 | 47 | addtick(t1,t2) 48 | time_t *t1, *t2; 49 | { 50 | 51 | t1->t_time += t2->t_time; 52 | t1->t_date += t2->t_date; 53 | if (t1->t_time >= 60*TICKSPERSEC) 54 | { 55 | t1->t_time -= 60*TICKSPERSEC; 56 | ++t1->t_date; 57 | } 58 | } 59 | 60 | incrtick(t) 61 | time_t *t; 62 | { 63 | if (++t->t_time == 60*TICKSPERSEC) 64 | { 65 | t->t_time = 0; 66 | ++t->t_date; 67 | } 68 | } 69 | 70 | 71 | stkreset() 72 | { 73 | #asm 8080 74 | POP H 75 | LXI SP,udata?-2 76 | PCHL 77 | #endasm 78 | } 79 | 80 | 81 | tempstack() 82 | { 83 | #asm 8080 84 | POP H 85 | LXI SP,100H 86 | PCHL 87 | #endasm 88 | } 89 | 90 | 91 | 92 | initvec() 93 | { 94 | #asm 8080 95 | LXI H,vector? 96 | INX H 97 | MOV A,L 98 | ANI 0FEH 99 | MOV L,A ;set hl to first even address in vector[]. 100 | MOV A,H 101 | .Z80 102 | LD I,A ;SET INTERRUPT REGISTER TO UPPER 8 BITS 103 | .8080 104 | MOV A,L 105 | OUT 076H ;set external vector register with low order byte 106 | LXI D,service? 107 | MOV M,E 108 | INX H 109 | MOV M,D ;STORE ADDRESS OF SERVICE ROUTINE IN vector[]. 110 | RET 111 | #endasm 112 | } 113 | 114 | extern int unix(); 115 | 116 | 117 | doexec() 118 | { 119 | #asm 8080 120 | POP H 121 | POP H ;get argument 122 | SPHL ;set stack pointer to it 123 | MVI A,0C3H ;jump inst 124 | STA 0030H ;dest of RST6 instruction. 125 | LXI H,unix? ;entry address 126 | SHLD 0031H 127 | XRA A 128 | STA udata? + ?OSYS 129 | JMP 0100H 130 | #endasm 131 | } 132 | 133 | 134 | static int cursig; 135 | static int (*curvec)(); 136 | 137 | /* This interrupt device routine calls the service routine of each device 138 | that could have interrupted. */ 139 | 140 | service() 141 | { 142 | 143 | ; 144 | #asm 8080 145 | PUSH PSW 146 | PUSH B 147 | PUSH D 148 | PUSH H 149 | .Z80 150 | PUSH IX 151 | PUSH IY 152 | .8080 153 | #endasm 154 | 155 | inint = 1; 156 | 157 | if (tty_int()) 158 | goto found; 159 | if (clk_int()) 160 | goto found; 161 | /* if ( ) ... */ 162 | 163 | warning("Spurious interrupt"); 164 | 165 | found: 166 | inint = 0; 167 | 168 | /* Deal with a pending caught signal, if any */ 169 | if (!udata.u_insys) 170 | calltrap(); 171 | ; 172 | 173 | #asm 8080 174 | .Z80 175 | POP IY 176 | POP IX 177 | .8080 178 | POP H 179 | POP D 180 | POP B 181 | POP PSW 182 | EI 183 | RET 184 | #endasm 185 | 186 | } 187 | 188 | 189 | 190 | calltrap() 191 | { 192 | /* Deal with a pending caught signal, if any. */ 193 | /* udata.u_insys should be false, and interrupts enabled. 194 | remember, the user may never return from the trap routine */ 195 | 196 | if (udata.u_cursig) 197 | { 198 | cursig = udata.u_cursig; 199 | curvec = udata.u_sigvec[cursig]; 200 | udata.u_cursig = 0; 201 | udata.u_sigvec[cursig] = SIG_DFL; /* Reset to default */ 202 | ei(); 203 | (*curvec)(cursig); 204 | di(); 205 | } 206 | } 207 | 208 | 209 | 210 | /* Port addresses of clock chip registers. */ 211 | 212 | #define SECS 0xe2 213 | #define MINS 0xe3 214 | #define HRS 0xe4 215 | #define DAY 0xe6 216 | #define MON 0xe7 217 | #define YEAR 86 218 | 219 | sttime() 220 | { 221 | panic("Calling sttime"); 222 | } 223 | 224 | 225 | rdtime(tloc) 226 | time_t *tloc; 227 | { 228 | di(); 229 | tloc->t_time = tod.t_time; 230 | tloc->t_date = tod.t_date; 231 | ei(); 232 | } 233 | 234 | 235 | /* Update global time of day */ 236 | rdtod() 237 | { 238 | tod.t_time = (tread(SECS)>>1) | (tread(MINS)<<5) | (tread(HRS)<<11); 239 | tod.t_date = tread(DAY) | (tread(MON)<<5) | (YEAR<<9); 240 | } 241 | 242 | 243 | /* Read BCD clock register, convert to binary. */ 244 | tread(port) 245 | uint16 port; 246 | { 247 | int n; 248 | 249 | n = in(port); 250 | return ( 10*((n>>4)&0x0f) + (n&0x0f) ); 251 | } 252 | 253 | 254 | /* Disable interrupts */ 255 | di() 256 | { 257 | #asm 8080 258 | DI ;disable interrupts 259 | #endasm 260 | } 261 | 262 | /* Enable interrupts if we are not in service routine */ 263 | ei() 264 | { 265 | if (inint) 266 | return; 267 | ; /* Empty statement necessary to fool compiler */ 268 | 269 | #asm 8080 270 | EI ;disable interrupts 271 | #endasm 272 | } 273 | 274 | 275 | 276 | /* This shifts an unsigned int right 8 places. */ 277 | 278 | shift8() 279 | { 280 | #asm 8080 281 | POP D ;ret addr 282 | POP H 283 | MOV L,H 284 | MVI H,0 285 | MOV A,L 286 | ANA A ;set Z flag on result 287 | PUSH H 288 | PUSH D ;restore stack 289 | #endasm 290 | } 291 | 292 | 293 | /* This prints an error message and dies. */ 294 | 295 | panic(s) 296 | char *s; 297 | { 298 | di(); 299 | inint = 1; 300 | kprintf("PANIC: %s\n",s); 301 | idump(); 302 | abort(); 303 | } 304 | 305 | 306 | warning(s) 307 | char *s; 308 | { 309 | kprintf("WARNING: %s\n",s); 310 | } 311 | 312 | 313 | puts(s) 314 | char *s; 315 | { 316 | while (*s) 317 | kputchar(*(s++)); 318 | } 319 | 320 | kputchar(c) 321 | int c; 322 | { 323 | if (c == '\n') 324 | _putc('\r'); 325 | _putc(c); 326 | if (c == '\t') 327 | puts("\177\177\177\177\177\177\177\177\177\177"); 328 | } 329 | 330 | 331 | 332 | idump() 333 | { 334 | inoptr ip; 335 | ptptr pp; 336 | extern struct cinode i_tab[]; 337 | 338 | kprintf( 339 | "\tMAGIC\tDEV\tNUM\tMODE\tNLINK\t(DEV)\tREFS\tDIRTY err %d root %d\n", 340 | udata.u_error, root - i_tab); 341 | 342 | for (ip=i_tab; ip < i_tab+ITABSIZE; ++ip) 343 | { 344 | kprintf("%d\t%d\t%d\t%u\t0%o\t%d\t%d\t%d\t%d\n", 345 | ip-i_tab, ip->c_magic,ip->c_dev, ip->c_num, 346 | ip->c_node.i_mode,ip->c_node.i_nlink,ip->c_node.i_addr[0], 347 | ip->c_refs,ip->c_dirty); 348 | /***** 349 | ifnot (ip->c_magic) 350 | break; 351 | ******/ 352 | } 353 | 354 | kprintf("\n\tSTAT\tWAIT\tPID\tPPTR\tALARM\tPENDING\tIGNORED\n"); 355 | for (pp=ptab; pp < ptab+PTABSIZE; ++pp) 356 | { 357 | kprintf("%d\t%d\t0x%x\t%d\t%d\t%d\t0x%x\t0x%x\n", 358 | pp-ptab, pp->p_status, pp->p_wait, pp->p_pid, 359 | pp->p_pptr-ptab, pp->p_alarm, pp->p_pending, 360 | pp->p_ignored); 361 | ifnot(pp->p_pptr) 362 | break; 363 | } 364 | 365 | bufdump(); 366 | 367 | kprintf("\ninsys %d ptab %d call %d cwd %d sp 0x%x\n", 368 | udata.u_insys,udata.u_ptab-ptab, udata.u_callno, udata.u_cwd-i_tab, 369 | udata.u_sp); 370 | } 371 | 372 | 373 | 374 | /* Short version of printf to save space */ 375 | kprintf(nargs) 376 | { 377 | register char **arg, *fmt; 378 | register c, base; 379 | char s[7], *itob(); 380 | 381 | arg = (char **)&nargs + nargs; 382 | fmt = *arg; 383 | while (c = *fmt++) { 384 | if (c != '%') { 385 | kputchar(c); 386 | continue; 387 | } 388 | switch (c = *fmt++) { 389 | case 'c': 390 | kputchar(*--arg); 391 | continue; 392 | case 'd': 393 | base = -10; 394 | goto prt; 395 | case 'o': 396 | base = 8; 397 | goto prt; 398 | case 'u': 399 | base = 10; 400 | goto prt; 401 | case 'x': 402 | base = 16; 403 | prt: 404 | puts(itob(*--arg, s, base)); 405 | continue; 406 | case 's': 407 | puts(*--arg); 408 | continue; 409 | default: 410 | kputchar(c); 411 | continue; 412 | } 413 | } 414 | } 415 | 416 | -------------------------------------------------------------------------------- /makeunix.sub: -------------------------------------------------------------------------------- 1 | b:submit b:qcc data 2 | b:submit b:qcc filesys 3 | b:submit b:qcc scall1 4 | b:submit b:qcc scall2 5 | b:submit b:qcc devio 6 | b:submit b:qcc devwd 7 | b:submit b:qcc devmisc 8 | b:submit b:qcc devtty 9 | b:submit b:qcc devflop 10 | b:submit b:qcc dispatch 11 | b:submit b:qcc machdep 12 | b:submit b:qcc process 13 | b:submit loadunix 14 | 15 | -------------------------------------------------------------------------------- /process.c: -------------------------------------------------------------------------------- 1 | /************************************************** 2 | UZI (Unix Z80 Implementation) Kernel: process.c 3 | ***************************************************/ 4 | 5 | 6 | #include "unix.h" 7 | #include "extern.h" 8 | 9 | init2() 10 | { 11 | register char *j; 12 | static char bootchar; 13 | static char *arg[2] = { "init", NULL }; 14 | inoptr i_open(), n_open(); 15 | ptptr ptab_alloc(); 16 | 17 | bufinit(); 18 | 19 | /* Create the context for the first process */ 20 | newproc(udata.u_ptab = initproc = ptab_alloc()); 21 | initproc->p_status = P_RUNNING; 22 | 23 | /* User's file table */ 24 | for (j=udata.u_files; j < (udata.u_files+UFTSIZE); ++j) 25 | *j = -1; 26 | 27 | /* Turn on the clock */ 28 | out(02,0xf1); 29 | ei(); 30 | 31 | /* Wait until the clock has interrupted, to set tod 32 | while (!tod.t_date) ; /* Loop */ 33 | /* 34 | 35 | /* Open the console tty device */ 36 | if (d_open(TTYDEV) != 0) 37 | panic("no tty"); 38 | 39 | kprintf("boot: "); 40 | udata.u_base = &bootchar; 41 | udata.u_count = 1; 42 | cdread(TTYDEV); 43 | ROOTDEV = bootchar - '0'; 44 | 45 | /* Mount the root device */ 46 | if (fmount(ROOTDEV,NULLINODE)) 47 | panic("no filesys"); 48 | 49 | ifnot (root = i_open(ROOTDEV,ROOTINODE)) 50 | panic("no root"); 51 | 52 | i_ref(udata.u_cwd = root); 53 | rdtime(&udata.u_time); 54 | 55 | udata.u_argn2 = (int16)("/init"); 56 | udata.u_argn1 = (int16)(&arg[0]); 57 | udata.u_argn = (int16)(&arg[1]); 58 | _execve(); 59 | /* 60 | _execve("/init",&arg[0],&arg[1]); 61 | */ 62 | panic("no /init"); 63 | 64 | } 65 | 66 | 67 | /* psleep() puts a process to sleep on the given event. 68 | If another process is runnable, it swaps out the current one 69 | and starts the new one. 70 | Normally when psleep is called, the interrupts have already been 71 | disabled. An event of 0 means a pause(), while an event equal 72 | to the process's own ptab address is a wait(). */ 73 | 74 | psleep(event) 75 | char *event; 76 | { 77 | register dummy; /* Force saving of registers */ 78 | 79 | di(); 80 | if (udata.u_ptab->p_status != P_RUNNING) 81 | panic("psleep: voodoo"); 82 | if (!event) 83 | udata.u_ptab->p_status = P_PAUSE; 84 | else if (event == (char *)udata.u_ptab) 85 | udata.u_ptab->p_status = P_WAIT; 86 | else 87 | udata.u_ptab->p_status = P_SLEEP; 88 | 89 | udata.u_ptab->p_wait = event; 90 | 91 | ei(); 92 | 93 | swapout(); /* Swap us out, and start another process */ 94 | 95 | /* Swapout doesn't return until we have been swapped back in */ 96 | } 97 | 98 | 99 | /* wakeup() looks for any process waiting on the event, 100 | and make them runnable */ 101 | 102 | wakeup(event) 103 | char *event; 104 | { 105 | register ptptr p; 106 | 107 | di(); 108 | for(p=ptab;p < ptab+PTABSIZE; ++p) 109 | { 110 | if (p->p_status > P_RUNNING && p->p_wait == event) 111 | { 112 | p->p_status = P_READY; 113 | p->p_wait = (char *)NULL; 114 | } 115 | } 116 | ei(); 117 | } 118 | 119 | 120 | /* Getproc returns the process table pointer of a runnable process. 121 | It is actually the scheduler. 122 | If there are none, it loops. This is the only time-wasting loop in the 123 | system. */ 124 | 125 | ptptr 126 | getproc() 127 | { 128 | register status; 129 | static ptptr pp = ptab; /* Pointer for round-robin scheduling */ 130 | 131 | for (;;) 132 | { 133 | if (++pp >= ptab + PTABSIZE) 134 | pp = ptab; 135 | 136 | di(); 137 | status = pp->p_status; 138 | ei(); 139 | 140 | if (status == P_RUNNING) 141 | panic("getproc: extra running"); 142 | if (status == P_READY) 143 | return(pp); 144 | } 145 | } 146 | 147 | /* Temp storage for swapout() */ 148 | char *stkptr; 149 | 150 | 151 | /* Swapout swaps out the current process, finds another that is READY, 152 | possibly the same process, and swaps it in. 153 | When a process is restarted after calling swapout, 154 | it thinks it has just returned from swapout(). */ 155 | 156 | /* This function can have no arguments or auto variables */ 157 | swapout() 158 | { 159 | static ptptr newp; 160 | ptptr getproc(); 161 | 162 | 163 | /* See if any signals are pending */ 164 | chksigs(); 165 | 166 | /* Get a new process */ 167 | newp = getproc(); 168 | 169 | /* If there is nothing else to run, just return */ 170 | if (newp == udata.u_ptab) 171 | { 172 | udata.u_ptab->p_status = P_RUNNING; 173 | return (runticks = 0); 174 | } 175 | 176 | ; 177 | /* Save the stack pointer and critical registers */ 178 | #asm 179 | LD HL,01 ;this will return 1 if swapped. 180 | PUSH HL ;will be return value 181 | PUSH BC 182 | PUSH IX 183 | LD HL,0 184 | ADD HL,SP ;get sp into hl 185 | LD (stkptr?),HL 186 | #endasm 187 | udata.u_sp = stkptr; 188 | 189 | swrite(); 190 | /* Read the new process in, and return into its context. */ 191 | swapin(newp); 192 | 193 | /* We should never get here. */ 194 | panic("swapin failed"); 195 | } 196 | 197 | 198 | /* This actually writes out the image */ 199 | swrite() 200 | { 201 | blkno_t blk; 202 | blk = udata.u_ptab->p_swap; 203 | 204 | /* Start by writing out the user data. */ 205 | 206 | /* The user data is written so that it is packed to the top of one block */ 207 | swapwrite(SWAPDEV, blk, 512, ((char *)(&udata+1))-512 ); 208 | 209 | /* The user address space is written in two i/o operations, 210 | one from 0x100 to the break, and then from the stack up. */ 211 | /* Notice that this might also include part or all of the user data, 212 | but never anything above it. */ 213 | 214 | swapwrite(SWAPDEV, 215 | blk+1, 216 | (((char *)(&udata+1))-PROGBASE) & ~511, 217 | PROGBASE); 218 | 219 | } 220 | 221 | /* No automatics can be used past tempstack(); */ 222 | swapin(pp) 223 | ptptr pp; 224 | { 225 | static blkno_t blk; 226 | static ptptr newp; 227 | 228 | di(); 229 | newp = pp; 230 | blk = newp->p_swap; 231 | ei(); 232 | 233 | tempstack(); 234 | 235 | swapread(SWAPDEV, blk, 512, ((char *)(&udata+1))-512 ); 236 | 237 | /* The user address space is read in two i/o operations, 238 | one from 0x100 to the break, and then from the stack up. */ 239 | /* Notice that this might also include part or all of the user data, 240 | but never anything above it. */ 241 | 242 | swapread(SWAPDEV, 243 | blk+1, 244 | (((char *)(&udata+1))-PROGBASE) & ~511, 245 | PROGBASE); 246 | 247 | if (newp != udata.u_ptab) 248 | panic("mangled swapin"); 249 | di(); 250 | newp->p_status = P_RUNNING; 251 | runticks = 0; 252 | ei(); 253 | /* Restore the registers */ 254 | 255 | stkptr = udata.u_sp; 256 | #asm 257 | LD HL,(stkptr?) 258 | LD SP,HL 259 | POP IX 260 | POP BC 261 | POP HL 262 | LD A,H 263 | OR L 264 | RET ;return into the context of the swapped-in process 265 | #endasm 266 | 267 | } 268 | 269 | 270 | /* Temp storage for dofork */ 271 | int16 newid; 272 | 273 | /* dofork implements forking. */ 274 | /* This function can have no arguments or auto variables */ 275 | 276 | dofork() 277 | { 278 | static ptptr p; 279 | ptptr ptab_alloc(); 280 | 281 | ifnot (p = ptab_alloc()) 282 | { 283 | udata.u_error = EAGAIN; 284 | return(-1); 285 | } 286 | di(); 287 | udata.u_ptab->p_status = P_READY; /* Parent is READY */ 288 | newid = p->p_pid; 289 | ei(); 290 | 291 | /* Save the stack pointer and critical registers */ 292 | /* When the process is swapped back in, it will be as if 293 | it returns with the value of the childs pid. */ 294 | 295 | #asm 296 | LD HL,(newid?) 297 | PUSH HL 298 | PUSH BC 299 | PUSH IX 300 | LD HL,0 301 | ADD HL,SP ;get sp into hl 302 | LD (stkptr?),HL 303 | #endasm 304 | 305 | udata.u_sp = stkptr; 306 | swrite(); 307 | 308 | #asm 309 | POP HL ;repair stack pointer 310 | POP HL 311 | POP HL 312 | #endasm 313 | 314 | /* Make a new process table entry, etc. */ 315 | newproc(p); 316 | 317 | di(); 318 | runticks = 0; 319 | p->p_status = P_RUNNING; 320 | ei(); 321 | return (0); /* Return to child */ 322 | } 323 | 324 | 325 | /* Newproc fixes up the tables for the child of a fork */ 326 | 327 | newproc(p) 328 | ptptr p; /* New process table entry */ 329 | { 330 | register char *j; 331 | 332 | /* Note that ptab_alloc clears most of the entry */ 333 | di(); 334 | p->p_swap = (p - ptab) * 65 + 1; /* Allow 65 blocks per process */ 335 | p->p_status = P_RUNNING; 336 | 337 | p->p_pptr = udata.u_ptab; 338 | p->p_ignored = udata.u_ptab->p_ignored; 339 | p->p_uid = udata.u_ptab->p_uid; 340 | udata.u_ptab = p; 341 | bzero(&udata.u_utime,4*sizeof(time_t)); /* Clear tick counters */ 342 | ei(); 343 | 344 | rdtime(&udata.u_time); 345 | i_ref(udata.u_cwd); 346 | udata.u_cursig = udata.u_error = 0; 347 | 348 | for (j=udata.u_files; j < (udata.u_files+UFTSIZE); ++j) 349 | if (*j >= 0) 350 | ++of_tab[*j].o_refs; 351 | } 352 | 353 | 354 | 355 | /* This allocates a new process table slot, and fills 356 | in its p_pid field with a unique number. */ 357 | 358 | ptptr 359 | ptab_alloc() 360 | { 361 | register ptptr p; 362 | register ptptr pp; 363 | static int nextpid = 0; 364 | 365 | di(); 366 | for(p=ptab;p < ptab+PTABSIZE; ++p) 367 | { 368 | if (p->p_status == P_EMPTY) 369 | goto found; 370 | } 371 | ei(); 372 | return(NULL); 373 | 374 | found: 375 | 376 | /* See if next pid number is unique */ 377 | nogood: 378 | if (nextpid++ > 32000) 379 | nextpid = 1; 380 | for (pp=ptab; pp < ptab+PTABSIZE; ++pp) 381 | { 382 | if (pp->p_status != P_EMPTY && pp->p_pid == nextpid) 383 | goto nogood; 384 | } 385 | 386 | bzero(p,sizeof(struct p_tab)); 387 | p->p_pid = nextpid; 388 | p->p_status = P_FORKING; 389 | ei(); 390 | return (p); 391 | } 392 | 393 | 394 | 395 | /* This is the clock interrupt routine. Its job is to 396 | increment the clock counters, increment the tick count of the 397 | running process, and either swap it out if it has been in long enough 398 | and is in user space or mark it to be swapped out if in system space. 399 | Also it decrements the alarm clock of processes. 400 | This must have no automatic or register variables */ 401 | 402 | clk_int() 403 | { 404 | static ptptr p; 405 | 406 | ifnot (in(0xf0)) /* See if clock actually interrupted, and turn it off */ 407 | return(0); 408 | 409 | /* Increment processes and global tick counters */ 410 | if (udata.u_ptab->p_status == P_RUNNING) 411 | incrtick(udata.u_insys ? &udata.u_stime : &udata.u_utime); 412 | 413 | incrtick(&ticks); 414 | 415 | /* Do once-per-second things */ 416 | 417 | if (++sec == TICKSPERSEC) 418 | { 419 | /* Update global time counters */ 420 | sec = 0; 421 | 422 | rdtod(); /* Update time-of-day */ 423 | 424 | /* Update process alarm clocks */ 425 | for (p=ptab; p < ptab+PTABSIZE; ++p) 426 | { 427 | if (p->p_alarm) 428 | ifnot(--p->p_alarm) 429 | sendsig(p,SIGALRM); 430 | } 431 | } 432 | 433 | 434 | /* Check run time of current process */ 435 | if (++runticks >= MAXTICKS && !udata.u_insys) /* Time to swap out */ 436 | { 437 | udata.u_insys = 1; 438 | inint = 0; 439 | udata.u_ptab->p_status = P_READY; 440 | swapout(); 441 | di(); 442 | udata.u_insys = 0; /* We have swapped back in */ 443 | } 444 | 445 | return(1); 446 | } 447 | 448 | 449 | 450 | extern int (*disp_tab[])(); 451 | 452 | static int j; 453 | 454 | /* No auto vars here, so carry flag will be preserved */ 455 | unix(argn3, argn2, argn1, argn, uret, callno) 456 | int16 argn3, argn2, argn1, argn; 457 | char *uret; 458 | int callno; 459 | { 460 | udata.u_argn3 = argn3; 461 | udata.u_argn2 = argn2; 462 | udata.u_argn1 = argn1; 463 | udata.u_argn = argn; 464 | udata.u_retloc = uret; 465 | udata.u_callno = callno; 466 | 467 | udata.u_insys = 1; 468 | udata.u_error = 0; 469 | ei(); 470 | 471 | #ifdef DEBUG 472 | kprintf ("\t\t\t\t\tcall %d (%x, %x, %x)\n",callno,argn2,argn1,argn); 473 | #endif 474 | 475 | /* Branch to correct routine */ 476 | 477 | udata.u_retval = (*disp_tab[udata.u_callno])(); 478 | 479 | #ifdef DEBUG 480 | kprintf("\t\t\t\t\t\tcall %d ret %x err %d\n", 481 | udata.u_callno,udata.u_retval, udata.u_error); 482 | #endif 483 | 484 | 485 | chksigs(); 486 | di(); 487 | if (runticks >= MAXTICKS) 488 | { 489 | udata.u_ptab->p_status = P_READY; 490 | swapout(); 491 | } 492 | ei(); 493 | 494 | udata.u_insys = 0; 495 | calltrap(); /* Call trap routine if necessary */ 496 | 497 | /* If an error, return errno with carry set */ 498 | 499 | if (udata.u_error) 500 | { 501 | ; 502 | #asm 503 | LD HL, (udata? + ?OERR) 504 | POP BC ;restore frame pointer 505 | PUSH BC 506 | POP IX 507 | SCF 508 | RET 509 | #endasm 510 | ; 511 | } 512 | 513 | return(udata.u_retval); 514 | } 515 | 516 | 517 | 518 | /* This sees if the current process has any signals set, and deals with them */ 519 | chksigs() 520 | { 521 | register j; 522 | 523 | di(); 524 | ifnot (udata.u_ptab->p_pending) 525 | { 526 | ei(); 527 | return; 528 | } 529 | 530 | for (j=1; j < NSIGS; ++j) 531 | { 532 | ifnot (sigmask(j) & udata.u_ptab->p_pending) 533 | continue; 534 | if (udata.u_sigvec[j] == SIG_DFL) 535 | { 536 | ei(); 537 | doexit(0,j); 538 | } 539 | 540 | if (udata.u_sigvec[j] != SIG_IGN) 541 | { 542 | /* Arrange to call the user routine at return */ 543 | udata.u_ptab->p_pending &= !sigmask(j); 544 | udata.u_cursig = j; 545 | } 546 | } 547 | ei(); 548 | } 549 | 550 | 551 | sendsig(proc,sig) 552 | ptptr proc; 553 | int16 sig; 554 | { 555 | register ptptr p; 556 | 557 | if (proc) 558 | ssig(proc,sig); 559 | else 560 | for (p=ptab; p < ptab+PTABSIZE; ++p) 561 | if (p->p_status) 562 | ssig(p,sig); 563 | 564 | } 565 | 566 | ssig(proc,sig) 567 | register ptptr proc; 568 | int16 sig; 569 | { 570 | register stat; 571 | 572 | di(); 573 | ifnot(proc->p_status) 574 | goto done; /* Presumably was killed just now */ 575 | 576 | if (proc->p_ignored & sigmask(sig)) 577 | goto done; 578 | 579 | stat = proc->p_status; 580 | if (stat == P_PAUSE || stat == P_WAIT || stat == P_SLEEP) 581 | proc->p_status = P_READY; 582 | 583 | proc->p_wait = (char *)NULL; 584 | proc->p_pending |= sigmask(sig); 585 | done: 586 | ei(); 587 | } 588 | 589 | 590 | -------------------------------------------------------------------------------- /scall1.c: -------------------------------------------------------------------------------- 1 | /************************************************** 2 | UZI (Unix Z80 Implementation) Kernel: scall1.c 3 | ***************************************************/ 4 | 5 | 6 | /*LINTLIBRARY*/ 7 | #include "unix.h" 8 | #include "extern.h" 9 | 10 | 11 | /******************************************* 12 | open(name, flag) 13 | char *name; 14 | register int16 flag; 15 | *********************************************/ 16 | 17 | #define name (char *)udata.u_argn1 18 | #define flag (int16)udata.u_argn 19 | 20 | _open() 21 | { 22 | int16 uindex; 23 | register int16 oftindex; 24 | register inoptr ino; 25 | register int16 perm; 26 | inoptr n_open(); 27 | 28 | if (flag < 0 || flag > 2) 29 | { 30 | udata.u_error = EINVAL; 31 | return (-1); 32 | } 33 | if ((uindex = uf_alloc()) == -1) 34 | return (-1); 35 | 36 | if ((oftindex = oft_alloc()) == -1) 37 | goto nooft; 38 | 39 | ifnot (ino = n_open(name,NULLINOPTR)) 40 | goto cantopen; 41 | 42 | of_tab[oftindex].o_inode = ino; 43 | 44 | perm = getperm(ino); 45 | if (((flag == O_RDONLY || flag == O_RDWR) && !(perm & OTH_RD)) || 46 | ((flag == O_WRONLY || flag == O_RDWR) && !(perm & OTH_WR))) 47 | { 48 | udata.u_error = EPERM; 49 | goto cantopen; 50 | } 51 | 52 | if (getmode(ino) == F_DIR && 53 | (flag == O_WRONLY || flag == O_RDWR)) 54 | { 55 | udata.u_error = EISDIR; 56 | goto cantopen; 57 | } 58 | 59 | if (isdevice(ino) && d_open((int)ino->c_node.i_addr[0]) != 0) 60 | { 61 | udata.u_error = ENXIO; 62 | goto cantopen; 63 | } 64 | 65 | udata.u_files[uindex] = oftindex; 66 | 67 | of_tab[oftindex].o_ptr.o_offset = 0; 68 | of_tab[oftindex].o_ptr.o_blkno = 0; 69 | of_tab[oftindex].o_access = flag; 70 | 71 | return (uindex); 72 | 73 | cantopen: 74 | oft_deref(oftindex); /* This will call i_deref() */ 75 | nooft: 76 | udata.u_files[uindex] = -1; 77 | return (-1); 78 | } 79 | 80 | #undef name 81 | #undef flag 82 | 83 | 84 | 85 | /********************************************* 86 | close(uindex) 87 | int16 uindex; 88 | **********************************************/ 89 | 90 | #define uindex (int16)udata.u_argn 91 | 92 | _close() 93 | { 94 | return(doclose(uindex)); 95 | } 96 | 97 | #undef uindex 98 | 99 | 100 | doclose(uindex) 101 | int16 uindex; 102 | { 103 | register int16 oftindex; 104 | inoptr ino; 105 | inoptr getinode(); 106 | 107 | ifnot(ino = getinode(uindex)) 108 | return(-1); 109 | oftindex = udata.u_files[uindex]; 110 | 111 | if (isdevice(ino) 112 | /* && ino->c_refs == 1 && of_tab[oftindex].o_refs == 1 */ ) 113 | d_close((int)(ino->c_node.i_addr[0])); 114 | 115 | udata.u_files[uindex] = -1; 116 | oft_deref(oftindex); 117 | 118 | return(0); 119 | } 120 | 121 | 122 | 123 | /**************************************** 124 | creat(name, mode) 125 | char *name; 126 | int16 mode; 127 | *****************************************/ 128 | 129 | #define name (char *)udata.u_argn1 130 | #define mode (int16)udata.u_argn 131 | 132 | _creat() 133 | { 134 | register inoptr ino; 135 | register int16 uindex; 136 | register int16 oftindex; 137 | inoptr parent; 138 | register int16 j; 139 | inoptr n_open(); 140 | inoptr newfile(); 141 | 142 | parent = NULLINODE; 143 | 144 | if ((uindex = uf_alloc()) == -1) 145 | return (-1); 146 | if ((oftindex = oft_alloc()) == -1) 147 | return (-1); 148 | 149 | if (ino = n_open(name,&parent)) /* The file exists */ 150 | { 151 | i_deref(parent); 152 | if (getmode(ino) == F_DIR) 153 | { 154 | i_deref(ino); 155 | udata.u_error = EISDIR; 156 | goto nogood; 157 | } 158 | ifnot (getperm(ino) & OTH_WR) 159 | { 160 | i_deref(ino); 161 | udata.u_error = EACCES; 162 | goto nogood; 163 | } 164 | if (getmode(ino) == F_REG) 165 | { 166 | /* Truncate the file to zero length */ 167 | f_trunc(ino); 168 | /* Reset any oft pointers */ 169 | for (j=0; j < OFTSIZE; ++j) 170 | if (of_tab[j].o_inode == ino) 171 | of_tab[j].o_ptr.o_blkno = of_tab[j].o_ptr.o_offset = 0; 172 | } 173 | 174 | } 175 | else 176 | { 177 | if (parent && (ino = newfile(parent,name))) 178 | /* Parent was derefed in newfile */ 179 | { 180 | ino->c_node.i_mode = (F_REG | (mode & MODE_MASK & ~udata.u_mask)); 181 | setftime(ino, A_TIME|M_TIME|C_TIME); 182 | /* The rest of the inode is initialized in newfile() */ 183 | wr_inode(ino); 184 | } 185 | else 186 | { 187 | /* Doesn't exist and can't make it */ 188 | if (parent) 189 | i_deref(parent); 190 | goto nogood; 191 | } 192 | } 193 | 194 | udata.u_files[uindex] = oftindex; 195 | 196 | of_tab[oftindex].o_ptr.o_offset = 0; 197 | of_tab[oftindex].o_ptr.o_blkno = 0; 198 | of_tab[oftindex].o_inode = ino; 199 | of_tab[oftindex].o_access = O_WRONLY; 200 | 201 | return (uindex); 202 | 203 | nogood: 204 | oft_deref(oftindex); 205 | return (-1); 206 | 207 | } 208 | 209 | #undef name 210 | #undef mode 211 | 212 | 213 | 214 | /******************************************** 215 | pipe(fildes) 216 | int fildes[]; 217 | *******************************************/ 218 | 219 | #define fildes (int *)udata.u_argn 220 | 221 | _pipe() 222 | { 223 | register int16 u1, u2, oft1, oft2; 224 | register inoptr ino; 225 | inoptr i_open(); 226 | 227 | if ((u1 = uf_alloc()) == -1) 228 | goto nogood2; 229 | if ((oft1 = oft_alloc()) == -1) 230 | goto nogood2; 231 | udata.u_files[u1] = oft1; 232 | 233 | if ((u2 = uf_alloc()) == -1) 234 | goto nogood; 235 | if ((oft2 = oft_alloc()) == -1) 236 | { 237 | oft_deref(oft1); 238 | goto nogood; 239 | } 240 | 241 | ifnot (ino = i_open(ROOTDEV, 0)) 242 | { 243 | oft_deref(oft1); 244 | oft_deref(oft2); 245 | goto nogood; 246 | } 247 | 248 | udata.u_files[u2] = oft2; 249 | 250 | of_tab[oft1].o_ptr.o_offset = 0; 251 | of_tab[oft1].o_ptr.o_blkno = 0; 252 | of_tab[oft1].o_inode = ino; 253 | of_tab[oft1].o_access = O_RDONLY; 254 | 255 | of_tab[oft2].o_ptr.o_offset = 0; 256 | of_tab[oft2].o_ptr.o_blkno = 0; 257 | of_tab[oft2].o_inode = ino; 258 | of_tab[oft2].o_access = O_WRONLY; 259 | 260 | ++ino->c_refs; 261 | ino->c_node.i_mode = F_PIPE | 0777; /* No permissions necessary on pipes */ 262 | ino->c_node.i_nlink = 0; /* a pipe is not in any directory */ 263 | 264 | *fildes = u1; 265 | *(fildes+1) = u2; 266 | return (0); 267 | 268 | nogood: 269 | udata.u_files[u1] = -1; 270 | nogood2: 271 | return(-1); 272 | 273 | } 274 | 275 | #undef fildes 276 | 277 | 278 | 279 | /******************************************** 280 | link(name1, name2) 281 | char *name1; 282 | char *name2; 283 | *********************************************/ 284 | 285 | #define name1 (char *)udata.u_argn1 286 | #define name2 (char *)udata.u_argn 287 | 288 | _link() 289 | { 290 | register inoptr ino; 291 | register inoptr ino2; 292 | inoptr parent2; 293 | char *filename(); 294 | inoptr n_open(); 295 | 296 | ifnot (ino = n_open(name1,NULLINOPTR)) 297 | return(-1); 298 | 299 | if (getmode(ino) == F_DIR && !super()) 300 | { 301 | udata.u_error = EPERM; 302 | goto nogood; 303 | } 304 | 305 | /* Make sure file2 doesn't exist, and get its parent */ 306 | if (ino2 = n_open(name2,&parent2)) 307 | { 308 | i_deref(ino2); 309 | i_deref(parent2); 310 | udata.u_error = EEXIST; 311 | goto nogood; 312 | } 313 | 314 | ifnot (parent2) 315 | goto nogood; 316 | 317 | if (ino->c_dev != parent2->c_dev) 318 | { 319 | i_deref(parent2); 320 | udata.u_error = EXDEV; 321 | goto nogood; 322 | } 323 | 324 | if (ch_link(parent2,"",filename(name2),ino) == 0) 325 | goto nogood; 326 | 327 | 328 | /* Update the link count. */ 329 | ++ino->c_node.i_nlink; 330 | wr_inode(ino); 331 | setftime(ino, C_TIME); 332 | 333 | i_deref(parent2); 334 | i_deref(ino); 335 | return(0); 336 | 337 | nogood: 338 | i_deref(ino); 339 | return(-1); 340 | 341 | } 342 | 343 | #undef name1 344 | #undef name2 345 | 346 | 347 | 348 | /********************************************************** 349 | unlink(path) 350 | char *path; 351 | **************************************************/ 352 | 353 | #define path (char *)udata.u_argn 354 | 355 | _unlink() 356 | { 357 | register inoptr ino; 358 | inoptr pino; 359 | char *filename(); 360 | inoptr i_open(); 361 | inoptr n_open(); 362 | 363 | ino = n_open(path,&pino); 364 | 365 | ifnot (pino && ino) 366 | { 367 | udata.u_error = ENOENT; 368 | return (-1); 369 | } 370 | 371 | if (getmode(ino) == F_DIR && !super()) 372 | { 373 | udata.u_error = EPERM; 374 | goto nogood; 375 | } 376 | 377 | /* Remove the directory entry */ 378 | 379 | if (ch_link(pino,filename(path),"",NULLINODE) == 0) 380 | goto nogood; 381 | 382 | /* Decrease the link count of the inode */ 383 | 384 | ifnot (ino->c_node.i_nlink--) 385 | { 386 | ino->c_node.i_nlink += 2; 387 | warning("_unlink: bad nlink"); 388 | } 389 | setftime(ino, C_TIME); 390 | i_deref(pino); 391 | i_deref(ino); 392 | return(0); 393 | 394 | nogood: 395 | i_deref(pino); 396 | i_deref(ino); 397 | return(-1); 398 | } 399 | 400 | #undef path 401 | 402 | 403 | 404 | /***************************************************** 405 | read(d, buf, nbytes) 406 | int16 d; 407 | char *buf; 408 | uint16 nbytes; 409 | **********************************************/ 410 | 411 | #define d (int16)udata.u_argn2 412 | #define buf (char *)udata.u_argn1 413 | #define nbytes (uint16)udata.u_argn 414 | 415 | _read() 416 | { 417 | register inoptr ino; 418 | inoptr rwsetup(); 419 | 420 | /* Set up u_base, u_offset, ino; check permissions, file num. */ 421 | if ((ino = rwsetup(1)) == NULLINODE) 422 | return (-1); /* bomb out if error */ 423 | 424 | readi(ino); 425 | updoff(); 426 | 427 | return (udata.u_count); 428 | } 429 | 430 | #undef d 431 | #undef buf 432 | #undef nbytes 433 | 434 | 435 | /*********************************** 436 | write(d, buf, nbytes) 437 | int16 d; 438 | char *buf; 439 | uint16 nbytes; 440 | ***********************************/ 441 | 442 | #define d (int16)udata.u_argn2 443 | #define buf (char *)udata.u_argn1 444 | #define nbytes (uint16)udata.u_argn 445 | 446 | _write() 447 | { 448 | register inoptr ino; 449 | off_t *offp; 450 | inoptr rwsetup(); 451 | 452 | /* Set up u_base, u_offset, ino; check permissions, file num. */ 453 | if ((ino = rwsetup(0)) == NULLINODE) 454 | return (-1); /* bomb out if error */ 455 | 456 | writei(ino); 457 | updoff(); 458 | 459 | return (udata.u_count); 460 | } 461 | 462 | #undef d 463 | #undef buf 464 | #undef nbytes 465 | 466 | 467 | 468 | inoptr 469 | rwsetup(rwflag) 470 | int rwflag; 471 | { 472 | register inoptr ino; 473 | register struct oft *oftp; 474 | inoptr getinode(); 475 | 476 | udata.u_base = (char *)udata.u_argn1; /* buf */ 477 | udata.u_count = (uint16)udata.u_argn; /* nbytes */ 478 | 479 | if ((ino = getinode(udata.u_argn2)) == NULLINODE) 480 | return (NULLINODE); 481 | 482 | oftp = of_tab + udata.u_files[udata.u_argn2]; 483 | if (oftp->o_access == (rwflag ? O_WRONLY : O_RDONLY)) 484 | { 485 | udata.u_error = EBADF; 486 | return (NULLINODE); 487 | } 488 | 489 | setftime(ino, rwflag ? A_TIME : (A_TIME | M_TIME | C_TIME)); 490 | 491 | /* Initialize u_offset from file pointer */ 492 | udata.u_offset.o_blkno = oftp->o_ptr.o_blkno; 493 | udata.u_offset.o_offset = oftp->o_ptr.o_offset; 494 | 495 | return (ino); 496 | } 497 | 498 | 499 | 500 | readi(ino) 501 | register inoptr ino; 502 | { 503 | register uint16 amount; 504 | register uint16 toread; 505 | register blkno_t pblk; 506 | register char *bp; 507 | int dev; 508 | int ispipe; 509 | char *bread(); 510 | char *zerobuf(); 511 | blkno_t bmap(); 512 | 513 | dev = ino->c_dev; 514 | ispipe = 0; 515 | switch (getmode(ino)) 516 | { 517 | 518 | case F_DIR: 519 | case F_REG: 520 | 521 | /* See of end of file will limit read */ 522 | toread = udata.u_count = 523 | ino->c_node.i_size.o_blkno-udata.u_offset.o_blkno >= 64 ? 524 | udata.u_count : 525 | min(udata.u_count, 526 | 512*(ino->c_node.i_size.o_blkno-udata.u_offset.o_blkno) + 527 | (ino->c_node.i_size.o_offset-udata.u_offset.o_offset)); 528 | goto loop; 529 | 530 | case F_PIPE: 531 | ispipe = 1; 532 | while (psize(ino) == 0) 533 | { 534 | if (ino->c_refs == 1) /* No writers */ 535 | break; 536 | /* Sleep if empty pipe */ 537 | psleep(ino); 538 | } 539 | toread = udata.u_count = min(udata.u_count, psize(ino)); 540 | goto loop; 541 | 542 | case F_BDEV: 543 | toread = udata.u_count; 544 | dev = *(ino->c_node.i_addr); 545 | 546 | loop: 547 | while (toread) 548 | { 549 | if ((pblk = bmap(ino, udata.u_offset.o_blkno, 1)) != NULLBLK) 550 | bp = bread(dev, pblk, 0); 551 | else 552 | bp = zerobuf(); 553 | 554 | bcopy(bp+udata.u_offset.o_offset, udata.u_base, 555 | (amount = min(toread, 512 - udata.u_offset.o_offset))); 556 | brelse(bp); 557 | 558 | udata.u_base += amount; 559 | addoff(&udata.u_offset, amount); 560 | if (ispipe && udata.u_offset.o_blkno >= 18) 561 | udata.u_offset.o_blkno = 0; 562 | toread -= amount; 563 | if (ispipe) 564 | { 565 | addoff(&(ino->c_node.i_size), -amount); 566 | wakeup(ino); 567 | } 568 | } 569 | 570 | break; 571 | 572 | case F_CDEV: 573 | udata.u_count = cdread(ino->c_node.i_addr[0]); 574 | 575 | if (udata.u_count != -1) 576 | addoff(&udata.u_offset, udata.u_count); 577 | break; 578 | 579 | default: 580 | udata.u_error = ENODEV; 581 | } 582 | } 583 | 584 | 585 | 586 | /* Writei (and readi) need more i/o error handling */ 587 | 588 | writei(ino) 589 | register inoptr ino; 590 | { 591 | register uint16 amount; 592 | register uint16 towrite; 593 | register char *bp; 594 | int ispipe; 595 | blkno_t pblk; 596 | int created; /* Set by bmap if newly allocated block used */ 597 | int dev; 598 | char *zerobuf(); 599 | char *bread(); 600 | blkno_t bmap(); 601 | 602 | dev = ino->c_dev; 603 | 604 | switch (getmode(ino)) 605 | { 606 | 607 | case F_BDEV: 608 | dev = *(ino->c_node.i_addr); 609 | case F_DIR: 610 | case F_REG: 611 | ispipe = 0; 612 | towrite = udata.u_count; 613 | goto loop; 614 | 615 | case F_PIPE: 616 | ispipe = 1; 617 | while ((towrite = udata.u_count) > (16*512) - psize(ino)) 618 | { 619 | if (ino->c_refs == 1) /* No readers */ 620 | { 621 | udata.u_count = -1; 622 | udata.u_error = EPIPE; 623 | ssig(udata.u_ptab, SIGPIPE); 624 | return; 625 | } 626 | /* Sleep if empty pipe */ 627 | psleep(ino); 628 | } 629 | 630 | /* Sleep if empty pipe */ 631 | goto loop; 632 | 633 | loop: 634 | 635 | while (towrite) 636 | { 637 | amount = min(towrite, 512 - udata.u_offset.o_offset); 638 | 639 | 640 | if ((pblk = bmap(ino, udata.u_offset.o_blkno, 0)) == NULLBLK) 641 | break; /* No space to make more blocks */ 642 | 643 | /* If we are writing an entire block, we don't care 644 | about its previous contents */ 645 | bp = bread(dev, pblk, (amount == 512)); 646 | 647 | bcopy(udata.u_base, bp+udata.u_offset.o_offset, amount); 648 | bawrite(bp); 649 | 650 | udata.u_base += amount; 651 | addoff(&udata.u_offset, amount); 652 | if(ispipe) 653 | { 654 | if (udata.u_offset.o_blkno >= 18) 655 | udata.u_offset.o_blkno = 0; 656 | addoff(&(ino->c_node.i_size), amount); 657 | /* Wake up any readers */ 658 | wakeup(ino); 659 | } 660 | towrite -= amount; 661 | } 662 | 663 | /* Update size if file grew */ 664 | ifnot (ispipe) 665 | { 666 | if ( udata.u_offset.o_blkno > ino->c_node.i_size.o_blkno || 667 | (udata.u_offset.o_blkno == ino->c_node.i_size.o_blkno && 668 | udata.u_offset.o_offset > ino->c_node.i_size.o_offset)) 669 | { 670 | ino->c_node.i_size.o_blkno = udata.u_offset.o_blkno; 671 | ino->c_node.i_size.o_offset = udata.u_offset.o_offset; 672 | ino->c_dirty = 1; 673 | } 674 | } 675 | 676 | break; 677 | 678 | case F_CDEV: 679 | udata.u_count = cdwrite(ino->c_node.i_addr[0]); 680 | 681 | if (udata.u_count != -1) 682 | addoff(&udata.u_offset, udata.u_count); 683 | break; 684 | 685 | default: 686 | udata.u_error = ENODEV; 687 | } 688 | 689 | } 690 | 691 | 692 | min(a, b) 693 | int a, b; 694 | { 695 | return ( a < b ? a : b); 696 | } 697 | 698 | 699 | psize(ino) 700 | inoptr ino; 701 | { 702 | return (512*ino->c_node.i_size.o_blkno+ino->c_node.i_size.o_offset); 703 | } 704 | 705 | 706 | 707 | addoff(ofptr, amount) 708 | off_t *ofptr; 709 | int amount; 710 | { 711 | if (amount >= 0) 712 | { 713 | ofptr->o_offset += amount % 512; 714 | if (ofptr->o_offset >= 512) 715 | { 716 | ofptr->o_offset -= 512; 717 | ++ofptr->o_blkno; 718 | } 719 | ofptr->o_blkno += amount/512; 720 | } 721 | else 722 | { 723 | ofptr->o_offset -= (-amount) % 512; 724 | if (ofptr->o_offset < 0) 725 | { 726 | ofptr->o_offset += 512; 727 | --ofptr->o_blkno; 728 | } 729 | ofptr->o_blkno -= (-amount)/512; 730 | } 731 | } 732 | 733 | 734 | updoff() 735 | { 736 | register off_t *offp; 737 | 738 | /* Update current file pointer */ 739 | offp = &of_tab[udata.u_files[udata.u_argn2]].o_ptr; 740 | offp->o_blkno = udata.u_offset.o_blkno; 741 | offp->o_offset = udata.u_offset.o_offset; 742 | } 743 | 744 | 745 | 746 | /**************************************** 747 | seek(file,offset,flag) 748 | int16 file; 749 | uint16 offset; 750 | int16 flag; 751 | *****************************************/ 752 | 753 | #define file (int16)udata.u_argn2 754 | #define offset (uint16)udata.u_argn1 755 | #define flag (int16)udata.u_argn 756 | 757 | _seek() 758 | { 759 | register inoptr ino; 760 | register int16 oftno; 761 | register uint16 retval; 762 | inoptr getinode(); 763 | 764 | if ((ino = getinode(file)) == NULLINODE) 765 | return(-1); 766 | 767 | if (getmode(ino) == F_PIPE) 768 | { 769 | udata.u_error = ESPIPE; 770 | return(-1); 771 | } 772 | 773 | oftno = udata.u_files[file]; 774 | 775 | 776 | if (flag <= 2) 777 | retval = of_tab[oftno].o_ptr.o_offset; 778 | else 779 | retval = of_tab[oftno].o_ptr.o_blkno; 780 | 781 | switch(flag) 782 | { 783 | case 0: 784 | of_tab[oftno].o_ptr.o_blkno = 0; 785 | of_tab[oftno].o_ptr.o_offset = offset; 786 | break; 787 | case 1: 788 | of_tab[oftno].o_ptr.o_offset += offset; 789 | break; 790 | case 2: 791 | of_tab[oftno].o_ptr.o_blkno = ino->c_node.i_size.o_blkno; 792 | of_tab[oftno].o_ptr.o_offset = ino->c_node.i_size.o_offset + offset; 793 | break; 794 | case 3: 795 | of_tab[oftno].o_ptr.o_blkno = offset; 796 | break; 797 | case 4: 798 | of_tab[oftno].o_ptr.o_blkno += offset; 799 | break; 800 | case 5: 801 | of_tab[oftno].o_ptr.o_blkno = ino->c_node.i_size.o_blkno + offset; 802 | break; 803 | default: 804 | udata.u_error = EINVAL; 805 | return(-1); 806 | } 807 | 808 | while ((unsigned)of_tab[oftno].o_ptr.o_offset >= 512) 809 | { 810 | of_tab[oftno].o_ptr.o_offset -= 512; 811 | ++of_tab[oftno].o_ptr.o_blkno; 812 | } 813 | 814 | return((int16)retval); 815 | } 816 | 817 | #undef file 818 | #undef offset 819 | #undef flag 820 | 821 | 822 | 823 | /************************************ 824 | chdir(dir) 825 | char *dir; 826 | ************************************/ 827 | 828 | #define dir (char *)udata.u_argn 829 | 830 | _chdir() 831 | { 832 | register inoptr newcwd; 833 | inoptr n_open(); 834 | 835 | ifnot (newcwd = n_open(dir,NULLINOPTR)) 836 | return(-1); 837 | 838 | if (getmode(newcwd) != F_DIR) 839 | { 840 | udata.u_error = ENOTDIR; 841 | i_deref(newcwd); 842 | return(-1); 843 | } 844 | i_deref(udata.u_cwd); 845 | udata.u_cwd = newcwd; 846 | return(0); 847 | } 848 | 849 | #undef dir 850 | 851 | 852 | 853 | /************************************* 854 | mknod(name,mode,dev) 855 | char *name; 856 | int16 mode; 857 | int16 dev; 858 | ***************************************/ 859 | 860 | #define name (char *)udata.u_argn2 861 | #define mode (int16)udata.u_argn1 862 | #define dev (int16)udata.u_argn 863 | 864 | _mknod() 865 | { 866 | register inoptr ino; 867 | inoptr parent; 868 | inoptr n_open(); 869 | inoptr newfile(); 870 | 871 | udata.u_error = 0; 872 | ifnot (super()) 873 | { 874 | udata.u_error = EPERM; 875 | return(-1); 876 | } 877 | 878 | if (ino = n_open(name,&parent)) 879 | { 880 | udata.u_error = EEXIST; 881 | goto nogood; 882 | } 883 | 884 | ifnot (parent) 885 | { 886 | udata.u_error = ENOENT; 887 | goto nogood3; 888 | } 889 | 890 | ifnot (ino = newfile(parent,name)) 891 | goto nogood2; 892 | 893 | /* Initialize mode and dev */ 894 | ino->c_node.i_mode = mode & ~udata.u_mask; 895 | ino->c_node.i_addr[0] = isdevice(ino) ? dev : 0; 896 | setftime(ino, A_TIME|M_TIME|C_TIME); 897 | wr_inode(ino); 898 | 899 | i_deref(ino); 900 | return (0); 901 | 902 | nogood: 903 | i_deref(ino); 904 | nogood2: 905 | i_deref(parent); 906 | nogood3: 907 | return (-1); 908 | } 909 | 910 | #undef name 911 | #undef mode 912 | #undef dev 913 | 914 | 915 | 916 | /**************************************** 917 | sync() 918 | ***************************************/ 919 | 920 | _sync() 921 | { 922 | register j; 923 | register inoptr ino; 924 | register char *buf; 925 | char *bread(); 926 | 927 | /* Write out modified inodes */ 928 | 929 | for (ino=i_tab; ino < i_tab+ITABSIZE; ++ino) 930 | if ((ino->c_refs) > 0 && ino->c_dirty != 0) 931 | { 932 | wr_inode(ino); 933 | ino->c_dirty = 0; 934 | } 935 | 936 | /* Write out modified super blocks */ 937 | /* This fills the rest of the super block with garbage. */ 938 | 939 | for (j=0; j < NDEVS; ++j) 940 | { 941 | if (fs_tab[j].s_mounted == SMOUNTED && fs_tab[j].s_fmod) 942 | { 943 | fs_tab[j].s_fmod = 0; 944 | buf = bread(j, 1, 1); 945 | bcopy((char *)&fs_tab[j], buf, 512); 946 | bfree(buf, 2); 947 | } 948 | } 949 | 950 | bufsync(); /* Clear buffer pool */ 951 | } 952 | 953 | 954 | /**************************************** 955 | access(path,mode) 956 | char *path; 957 | int16 mode; 958 | ****************************************/ 959 | 960 | #define path (char *)udata.u_argn1 961 | #define mode (int16)udata.u_argn 962 | 963 | _access() 964 | { 965 | register inoptr ino; 966 | register int16 euid; 967 | register int16 egid; 968 | register int16 retval; 969 | inoptr n_open(); 970 | 971 | if ((mode & 07) && !*(path)) 972 | { 973 | udata.u_error = ENOENT; 974 | return (-1); 975 | } 976 | 977 | /* Temporarily make eff. id real id. */ 978 | euid = udata.u_euid; 979 | egid = udata.u_egid; 980 | udata.u_euid = udata.u_ptab->p_uid; 981 | udata.u_egid = udata.u_gid; 982 | 983 | ifnot (ino = n_open(path,NULLINOPTR)) 984 | { 985 | retval = -1; 986 | goto nogood; 987 | } 988 | 989 | retval = 0; 990 | if (~getperm(ino) & (mode&07)) 991 | { 992 | udata.u_error = EPERM; 993 | retval = -1; 994 | } 995 | 996 | i_deref(ino); 997 | nogood: 998 | udata.u_euid = euid; 999 | udata.u_egid = egid; 1000 | 1001 | return(retval); 1002 | } 1003 | 1004 | #undef path 1005 | #undef mode 1006 | 1007 | 1008 | 1009 | /******************************************* 1010 | chmod(path,mode) 1011 | char *path; 1012 | int16 mode; 1013 | *******************************************/ 1014 | 1015 | #define path (char *)udata.u_argn1 1016 | #define mode (int16)udata.u_argn 1017 | 1018 | _chmod() 1019 | { 1020 | 1021 | inoptr ino; 1022 | inoptr n_open(); 1023 | 1024 | ifnot (ino = n_open(path,NULLINOPTR)) 1025 | return (-1); 1026 | 1027 | if (ino->c_node.i_uid != udata.u_euid && !super()) 1028 | { 1029 | i_deref(ino); 1030 | udata.u_error = EPERM; 1031 | return(-1); 1032 | } 1033 | 1034 | ino->c_node.i_mode = (mode & MODE_MASK) | (ino->c_node.i_mode & F_MASK); 1035 | setftime(ino, C_TIME); 1036 | i_deref(ino); 1037 | return(0); 1038 | } 1039 | 1040 | #undef path 1041 | #undef mode 1042 | 1043 | 1044 | 1045 | /*********************************************** 1046 | chown(path, owner, group) 1047 | char *path; 1048 | int owner; 1049 | int group; 1050 | **********************************************/ 1051 | 1052 | #define path (char *)udata.u_argn2 1053 | #define owner (int16)udata.u_argn1 1054 | #define group (int16)udata.u_argn 1055 | 1056 | _chown() 1057 | { 1058 | register inoptr ino; 1059 | inoptr n_open(); 1060 | 1061 | ifnot (ino = n_open(path,NULLINOPTR)) 1062 | return (-1); 1063 | 1064 | if (ino->c_node.i_uid != udata.u_euid && !super()) 1065 | { 1066 | i_deref(ino); 1067 | udata.u_error = EPERM; 1068 | return(-1); 1069 | } 1070 | 1071 | ino->c_node.i_uid = owner; 1072 | ino->c_node.i_gid = group; 1073 | setftime(ino, C_TIME); 1074 | i_deref(ino); 1075 | return(0); 1076 | } 1077 | 1078 | #undef path 1079 | #undef owner 1080 | #undef group 1081 | 1082 | 1083 | 1084 | /************************************** 1085 | stat(path,buf) 1086 | char *path; 1087 | char *buf; 1088 | ****************************************/ 1089 | 1090 | #define path (char *)udata.u_argn1 1091 | #define buf (char *)udata.u_argn 1092 | 1093 | _stat() 1094 | { 1095 | 1096 | register inoptr ino; 1097 | inoptr n_open(); 1098 | 1099 | ifnot (valadr(buf,sizeof(struct stat)) && (ino = n_open(path,NULLINOPTR))) 1100 | { 1101 | return (-1); 1102 | } 1103 | 1104 | stcpy(ino,buf); 1105 | i_deref(ino); 1106 | return(0); 1107 | } 1108 | 1109 | #undef path 1110 | #undef buf 1111 | 1112 | 1113 | 1114 | /******************************************** 1115 | fstat(fd, buf) 1116 | int16 fd; 1117 | char *buf; 1118 | ********************************************/ 1119 | 1120 | #define fd (int16)udata.u_argn1 1121 | #define buf (char *)udata.u_argn 1122 | 1123 | _fstat() 1124 | { 1125 | register inoptr ino; 1126 | inoptr getinode(); 1127 | 1128 | ifnot (valadr(buf,sizeof(struct stat))) 1129 | return(-1); 1130 | 1131 | if ((ino = getinode(fd)) == NULLINODE) 1132 | return(-1); 1133 | 1134 | stcpy(ino,buf); 1135 | return(0); 1136 | } 1137 | 1138 | #undef fd 1139 | #undef buf 1140 | 1141 | 1142 | /* Utility for stat and fstat */ 1143 | stcpy(ino, buf) 1144 | inoptr ino; 1145 | char *buf; 1146 | { 1147 | /* violently system-dependent */ 1148 | bcopy((char *)&(ino->c_dev), buf, 12); 1149 | bcopy((char *)&(ino->c_node.i_addr[0]), buf+12, 2); 1150 | bcopy((char *)&(ino->c_node.i_size), buf+14, 16); 1151 | } 1152 | 1153 | 1154 | 1155 | /************************************ 1156 | dup(oldd) 1157 | int16 oldd; 1158 | ************************************/ 1159 | 1160 | #define oldd (uint16)udata.u_argn 1161 | 1162 | _dup() 1163 | { 1164 | register int newd; 1165 | inoptr getinode(); 1166 | 1167 | if (getinode(oldd) == NULLINODE) 1168 | return(-1); 1169 | 1170 | if ((newd = uf_alloc()) == -1) 1171 | return (-1); 1172 | 1173 | udata.u_files[newd] = udata.u_files[oldd]; 1174 | ++of_tab[udata.u_files[oldd]].o_refs; 1175 | 1176 | return(newd); 1177 | } 1178 | 1179 | #undef oldd 1180 | 1181 | 1182 | 1183 | /**************************************** 1184 | dup2(oldd, newd) 1185 | int16 oldd; 1186 | int16 newd; 1187 | ****************************************/ 1188 | 1189 | #define oldd (int16)udata.u_argn1 1190 | #define newd (int16)udata.u_argn 1191 | 1192 | _dup2() 1193 | { 1194 | inoptr getinode(); 1195 | 1196 | if (getinode(oldd) == NULLINODE) 1197 | return(-1); 1198 | 1199 | if (newd < 0 || newd >= UFTSIZE) 1200 | { 1201 | udata.u_error = EBADF; 1202 | return (-1); 1203 | } 1204 | 1205 | ifnot (udata.u_files[newd] & 0x80) 1206 | doclose(newd); 1207 | 1208 | udata.u_files[newd] = udata.u_files[oldd]; 1209 | ++of_tab[udata.u_files[oldd]].o_refs; 1210 | 1211 | return(0); 1212 | } 1213 | 1214 | #undef oldd 1215 | #undef newd 1216 | 1217 | 1218 | 1219 | /************************************** 1220 | umask(mask) 1221 | int mask; 1222 | *************************************/ 1223 | 1224 | #define mask (int16)udata.u_argn 1225 | 1226 | _umask() 1227 | { 1228 | register int omask; 1229 | 1230 | omask = udata.u_mask; 1231 | udata.u_mask = mask & 0777; 1232 | return(omask); 1233 | } 1234 | 1235 | #undef mask 1236 | 1237 | 1238 | 1239 | /* Special system call returns super-block of given 1240 | filesystem for users to determine free space, etc. 1241 | Should be replaced with a sync() followed by a read 1242 | of block 1 of the device. */ 1243 | 1244 | /*********************************************** 1245 | getfsys(dev,buf) 1246 | int16 dev; 1247 | struct filesys *buf; 1248 | **************************************************/ 1249 | 1250 | #define dev (int16)udata.u_argn1 1251 | #define buf (struct filesys *)udata.u_argn 1252 | 1253 | _getfsys() 1254 | { 1255 | if (dev < 0 || dev >= NDEVS || fs_tab[dev].s_mounted != SMOUNTED) 1256 | { 1257 | udata.u_error = ENXIO; 1258 | return(-1); 1259 | } 1260 | 1261 | bcopy((char *)&fs_tab[dev],(char *)buf,sizeof(struct filesys)); 1262 | return(0); 1263 | } 1264 | 1265 | #undef dev 1266 | #undef buf 1267 | 1268 | 1269 | 1270 | /**************************************** 1271 | ioctl(fd, request, data) 1272 | int fd; 1273 | int request; 1274 | char *data; 1275 | *******************************************/ 1276 | 1277 | #define fd (int)udata.u_argn2 1278 | #define request (int)udata.u_argn1 1279 | #define data (char *)udata.u_argn 1280 | 1281 | _ioctl() 1282 | { 1283 | 1284 | register inoptr ino; 1285 | register int dev; 1286 | inoptr getinode(); 1287 | 1288 | if ((ino = getinode(fd)) == NULLINODE) 1289 | return(-1); 1290 | 1291 | ifnot (isdevice(ino)) 1292 | { 1293 | udata.u_error = ENOTTY; 1294 | return(-1); 1295 | } 1296 | 1297 | ifnot (getperm(ino) & OTH_WR) 1298 | { 1299 | udata.u_error = EPERM; 1300 | return(-1); 1301 | } 1302 | 1303 | dev = ino->c_node.i_addr[0]; 1304 | 1305 | if (d_ioctl(dev, request,data)) 1306 | return(-1); 1307 | return(0); 1308 | } 1309 | 1310 | #undef fd 1311 | #undef request 1312 | #undef data 1313 | 1314 | 1315 | 1316 | /* This implementation of mount ignores the rwflag */ 1317 | 1318 | /***************************************** 1319 | mount(spec, dir, rwflag) 1320 | char *spec; 1321 | char *dir; 1322 | int rwflag; 1323 | *******************************************/ 1324 | 1325 | #define spec (char *)udata.u_argn2 1326 | #define dir (char *)udata.u_argn1 1327 | #define rwflag (int)udata.u_argn 1328 | 1329 | _mount() 1330 | { 1331 | register inoptr sino, dino; 1332 | register int dev; 1333 | inoptr n_open(); 1334 | 1335 | ifnot(super()) 1336 | { 1337 | udata.u_error = EPERM; 1338 | return (-1); 1339 | } 1340 | 1341 | ifnot (sino = n_open(spec,NULLINOPTR)) 1342 | return (-1); 1343 | 1344 | ifnot (dino = n_open(dir,NULLINOPTR)) 1345 | { 1346 | i_deref(sino); 1347 | return (-1); 1348 | } 1349 | 1350 | if (getmode(sino) != F_BDEV) 1351 | { 1352 | udata.u_error = ENOTBLK; 1353 | goto nogood; 1354 | } 1355 | 1356 | if (getmode(dino) != F_DIR) 1357 | { 1358 | udata.u_error = ENOTDIR; 1359 | goto nogood; 1360 | } 1361 | 1362 | dev = (int)sino->c_node.i_addr[0]; 1363 | 1364 | if ( dev >= NDEVS || d_open(dev)) 1365 | { 1366 | udata.u_error = ENXIO; 1367 | goto nogood; 1368 | } 1369 | 1370 | if (fs_tab[dev].s_mounted || dino->c_refs != 1 || dino->c_num == ROOTINODE) 1371 | { 1372 | udata.u_error = EBUSY; 1373 | goto nogood; 1374 | } 1375 | 1376 | _sync(); 1377 | 1378 | if (fmount(dev,dino)) 1379 | { 1380 | udata.u_error = EBUSY; 1381 | goto nogood; 1382 | } 1383 | 1384 | i_deref(dino); 1385 | i_deref(sino); 1386 | return(0); 1387 | 1388 | nogood: 1389 | i_deref(dino); 1390 | i_deref(sino); 1391 | return (-1); 1392 | } 1393 | 1394 | #undef spec 1395 | #undef dir 1396 | #undef rwflag 1397 | 1398 | 1399 | 1400 | /****************************************** 1401 | umount(spec) 1402 | char *spec; 1403 | ******************************************/ 1404 | 1405 | #define spec (char *)udata.u_argn 1406 | 1407 | _umount() 1408 | { 1409 | register inoptr sino; 1410 | register int dev; 1411 | register inoptr ptr; 1412 | inoptr n_open(); 1413 | 1414 | ifnot(super()) 1415 | { 1416 | udata.u_error = EPERM; 1417 | return (-1); 1418 | } 1419 | 1420 | ifnot (sino = n_open(spec,NULLINOPTR)) 1421 | return (-1); 1422 | 1423 | if (getmode(sino) != F_BDEV) 1424 | { 1425 | udata.u_error = ENOTBLK; 1426 | goto nogood; 1427 | } 1428 | 1429 | dev = (int)sino->c_node.i_addr[0]; 1430 | ifnot (validdev(dev)) 1431 | { 1432 | udata.u_error = ENXIO; 1433 | goto nogood; 1434 | } 1435 | 1436 | if (!fs_tab[dev].s_mounted) 1437 | { 1438 | udata.u_error = EINVAL; 1439 | goto nogood; 1440 | } 1441 | 1442 | for (ptr = i_tab; ptr < i_tab+ITABSIZE; ++ptr) 1443 | if (ptr->c_refs > 0 && ptr->c_dev == dev) 1444 | { 1445 | udata.u_error = EBUSY; 1446 | goto nogood; 1447 | } 1448 | 1449 | _sync(); 1450 | fs_tab[dev].s_mounted = 0; 1451 | i_deref(fs_tab[dev].s_mntpt); 1452 | 1453 | i_deref(sino); 1454 | return(0); 1455 | 1456 | nogood: 1457 | i_deref(sino); 1458 | return (-1); 1459 | } 1460 | 1461 | #undef spec 1462 | 1463 | -------------------------------------------------------------------------------- /scall2.c: -------------------------------------------------------------------------------- 1 | /************************************************** 2 | UZI (Unix Z80 Implementation) Kernel: scall2.c 3 | ***************************************************/ 4 | 5 | 6 | /*LINTLIBRARY*/ 7 | #include "unix.h" 8 | #include "extern.h" 9 | 10 | 11 | /* Getpid() */ 12 | 13 | _getpid() 14 | { 15 | return(udata.u_ptab->p_pid); 16 | } 17 | 18 | /* Getppid() */ 19 | 20 | _getppid() 21 | { 22 | return(udata.u_ptab->p_pptr->p_pid); 23 | } 24 | 25 | 26 | /* Getuid() */ 27 | 28 | _getuid() 29 | { 30 | return(udata.u_ptab->p_uid); 31 | } 32 | 33 | 34 | _getgid() 35 | { 36 | return(udata.u_gid); 37 | } 38 | 39 | 40 | /********************************* 41 | setuid(uid) 42 | int uid; 43 | ***********************************/ 44 | 45 | #define uid (int)udata.u_argn 46 | 47 | _setuid() 48 | { 49 | if (super() || udata.u_ptab->p_uid == uid) 50 | { 51 | udata.u_ptab->p_uid = uid; 52 | udata.u_euid = uid; 53 | return(0); 54 | } 55 | udata.u_error = EPERM; 56 | return(-1); 57 | } 58 | 59 | #undef uid 60 | 61 | 62 | 63 | /***************************************** 64 | setgid(gid) 65 | int gid; 66 | ****************************************/ 67 | 68 | #define gid (int16)udata.u_argn 69 | 70 | _setgid() 71 | { 72 | if (super() || udata.u_gid == gid) 73 | { 74 | udata.u_gid = gid; 75 | udata.u_egid = gid; 76 | return(0); 77 | } 78 | udata.u_error = EPERM; 79 | return(-1); 80 | } 81 | 82 | #undef gid; 83 | 84 | 85 | 86 | /*********************************** 87 | time(tvec) 88 | int tvec[]; 89 | **************************************/ 90 | 91 | #define tvec (int *)udata.u_argn 92 | 93 | _time() 94 | { 95 | rdtime(tvec); /* In machdep.c */ 96 | return(0); 97 | } 98 | 99 | #undef tvec 100 | 101 | 102 | /************************************** 103 | stime(tvec) 104 | int tvec[]; 105 | **********************************/ 106 | 107 | #define tvec (int *)udata.u_argn 108 | 109 | _stime() 110 | { 111 | /* 112 | ifnot (super()) 113 | { 114 | udata.u_error = EPERM; 115 | return(-1); 116 | } 117 | sttime(tvec); 118 | return(0); 119 | */ 120 | 121 | udata.u_error = EPERM; 122 | return(-1); 123 | } 124 | 125 | #undef tvec 126 | 127 | 128 | 129 | /******************************************** 130 | times(buf) 131 | char *buf; 132 | **********************************************/ 133 | 134 | #define buf (char *)udata.u_argn 135 | 136 | _times() 137 | { 138 | ifnot (valadr(buf,6*sizeof(time_t))) 139 | return(-1); 140 | 141 | di(); 142 | bcopy(&udata.u_utime, buf, 4*sizeof(time_t)); 143 | bcopy(&ticks, buf + 4*sizeof(time_t), sizeof(time_t)); 144 | ei(); 145 | return(0); 146 | } 147 | 148 | #undef buf 149 | 150 | 151 | 152 | /* User's execve() call. All other flavors are library routines. */ 153 | 154 | /***************************************** 155 | execve(name, argv, envp) 156 | char *name; 157 | char *argv[]; 158 | char *envp[]; 159 | *****************************************/ 160 | 161 | #define name (char *)udata.u_argn2 162 | #define argv (char **)udata.u_argn1 163 | #define envp (char **)udata.u_argn 164 | 165 | _execve() 166 | { 167 | register inoptr ino; 168 | register char *buf; 169 | inoptr n_open(); 170 | char *bread(); 171 | blkno_t bmap(); 172 | 173 | ifnot (ino = n_open(name,NULLINOPTR)) 174 | return(-1); 175 | 176 | if (ino->c_node.i_size.o_blkno >= ((uint16)(&udata)/512)) 177 | { 178 | udata.u_error = ENOMEM; 179 | goto nogood; 180 | } 181 | 182 | ifnot ( (getperm(ino) & OTH_EX) && 183 | (ino->c_node.i_mode & F_REG) && 184 | (ino->c_node.i_mode & (OWN_EX | OTH_EX | GRP_EX)) ) 185 | { 186 | udata.u_error = EACCES; 187 | goto nogood; 188 | } 189 | 190 | setftime(ino, A_TIME); 191 | 192 | /* Gather the arguments, and put them on the root device */ 193 | /* Put environment on another block */ 194 | if (wargs(argv, 0) || wargs(envp, 1)) 195 | goto nogood; 196 | 197 | /* Read in the first block of the new program */ 198 | buf = bread( ino->c_dev, bmap(ino, 0, 1), 0); 199 | 200 | if ((*buf & 0xff) != EMAGIC) 201 | { 202 | udata.u_error = ENOEXEC; 203 | goto nogood2; 204 | } 205 | 206 | /* Here, check the setuid stuff. No other changes need be made in 207 | the user data */ 208 | if (ino->c_node.i_mode & SET_UID) 209 | udata.u_euid = ino->c_node.i_uid; 210 | 211 | if (ino->c_node.i_mode & SET_GID) 212 | udata.u_egid = ino->c_node.i_gid; 213 | 214 | bcopy(buf,PROGBASE,512); 215 | bfree(buf, 0); 216 | 217 | /* At this point, we are committed to reading in and executing 218 | the program. We switch to a local stack, and pass to it 219 | the necessary parameter: ino */ 220 | 221 | udata.u_ino = ino; /* Termorarily stash these here */ 222 | 223 | tempstk(); 224 | exec2(); /* Never returns */ 225 | 226 | nogood2: 227 | bfree(buf, 0); 228 | nogood: 229 | i_deref(ino); 230 | return(-1); 231 | 232 | } 233 | 234 | #undef name 235 | #undef argv 236 | #undef envp 237 | 238 | 239 | exec2() 240 | { 241 | register blkno_t blk; 242 | register char **argv; 243 | register char **envp; 244 | register int (**sp)(); 245 | int argc; 246 | char *rargs(); 247 | register char *progptr; 248 | char *buf; 249 | blkno_t pblk; 250 | blkno_t bmap(); 251 | char *bread(); 252 | 253 | /* Read in the rest of the program */ 254 | progptr = PROGBASE+512; 255 | for (blk = 1; blk <= udata.u_ino->c_node.i_size.o_blkno; ++blk) 256 | { 257 | pblk = bmap(udata.u_ino, blk, 1); 258 | if (pblk != -1) 259 | { 260 | buf = bread( udata.u_ino->c_dev, pblk, 0); 261 | bcopy(buf, progptr, 512); 262 | bfree(buf, 0); 263 | } 264 | progptr += 512; 265 | } 266 | i_deref(udata.u_ino); 267 | 268 | /* Zero out the free memory */ 269 | bzero(progptr,(uint16)((char *)&udata - progptr)); 270 | udata.u_break = progptr; 271 | 272 | 273 | /* Read back the arguments and the environment */ 274 | argv = (char **)rargs((char *)&udata, 0, &argc); 275 | envp = (char **)rargs((char *)argv, 1, NULL); 276 | 277 | /* Fill in udata.u_name */ 278 | bcopy(*argv,udata.u_name,8); 279 | 280 | /* Turn off caught signals */ 281 | for (sp= udata.u_sigvec; sp < (udata.u_sigvec+NSIGS); ++sp) 282 | if (*sp != SIG_IGN) 283 | *sp = SIG_DFL; 284 | 285 | /* Shove argc and the address of argv just below envp */ 286 | *(envp - 1) = (char *)argc; 287 | *(envp - 2) = (char *)argv; 288 | 289 | /* Go jump into the program, first setting the stack */ 290 | doexec((int16 *)(udata.u_isp = envp - 2)); 291 | 292 | } 293 | 294 | 295 | wargs(argv,blk) 296 | char **argv; 297 | int blk; 298 | { 299 | register char *ptr; /* Address of base of arg strings in user space */ 300 | register int n; 301 | struct s_argblk *argbuf; 302 | register char *bufp; 303 | register int j; 304 | char *zerobuf(); 305 | char *bread(); 306 | 307 | /* Gather the arguments, and put them on the swap device */ 308 | argbuf = (struct s_argblk *)bread(SWAPDEV, udata.u_ptab->p_swap+blk, 2); 309 | bufp = argbuf->a_buf; 310 | for (j=0; argv[j] != NULL; ++j) 311 | { 312 | ptr = argv[j]; 313 | do 314 | { 315 | *bufp++ = *ptr; 316 | if (bufp >= argbuf->a_buf+500) 317 | { 318 | udata.u_error = E2BIG; 319 | bfree((char *)argbuf, 1); 320 | return (1); 321 | } 322 | } 323 | while (*ptr++ != '\0'); 324 | } 325 | 326 | argbuf->a_argc = j; /* Store argc in argbuf. */ 327 | argbuf->a_arglen = bufp - argbuf->a_buf; /*Store total string size. */ 328 | 329 | /* Swap out the arguments into the given swap block */ 330 | bfree((char *)argbuf, 1); 331 | 332 | return (0); 333 | } 334 | 335 | 336 | 337 | char * 338 | rargs(ptr,blk,cnt) 339 | register char *ptr; 340 | int blk; 341 | int *cnt; 342 | { 343 | struct s_argblk *argbuf; 344 | register char **argv; /* Address of users argv[], just below ptr */ 345 | register int n; 346 | char *bread(); 347 | 348 | /* Read back the arguments */ 349 | argbuf = (struct s_argblk *)bread(SWAPDEV,udata.u_ptab->p_swap+blk, 0); 350 | 351 | /* Move them into the users address space, at the very top */ 352 | ptr -= argbuf->a_arglen; 353 | if (argbuf->a_arglen) 354 | bcopy(argbuf->a_buf, ptr, argbuf->a_arglen); 355 | 356 | /* Set argv to point below the argument strings */ 357 | argv = (char **)ptr - (argbuf->a_argc + 1); 358 | 359 | /* Set each element of argv[] to point to its argument string */ 360 | argv[0] = ptr; 361 | for (n=1; n < argbuf->a_argc; ++n) 362 | argv[n] = argv[n-1] + strlen(argv[n-1]) + 1; 363 | argv[argbuf->a_argc] = NULL; 364 | 365 | if (cnt) 366 | *cnt = argbuf->a_argc; 367 | 368 | bfree((char *)argbuf, 0); 369 | return (argv); 370 | } 371 | 372 | 373 | 374 | /********************************** 375 | brk(addr) 376 | char *addr; 377 | ************************************/ 378 | 379 | #define addr (char *)udata.u_argn 380 | 381 | _brk() 382 | { 383 | char dummy; /* A thing to take address of */ 384 | 385 | /* A hack to get approx val of stack ptr. */ 386 | if (addr < PROGBASE || (addr+64) >= (char *)&dummy) 387 | { 388 | udata.u_error = ENOMEM; 389 | return(-1); 390 | } 391 | udata.u_break = addr; 392 | return(0); 393 | } 394 | 395 | #undef addr 396 | 397 | 398 | 399 | /************************************ 400 | sbrk(incr) 401 | uint16 incr; 402 | ***************************************/ 403 | 404 | #define incr (uint16)udata.u_argn 405 | 406 | _sbrk() 407 | { 408 | register char *oldbrk; 409 | 410 | udata.u_argn += (oldbrk = udata.u_break); 411 | if (_brk()) 412 | return(-1); 413 | 414 | return((int)oldbrk); 415 | } 416 | 417 | #undef incr 418 | 419 | 420 | 421 | /************************************** 422 | wait(statloc) 423 | int *statloc; 424 | ****************************************/ 425 | 426 | #define statloc (int *)udata.u_argn 427 | 428 | _wait() 429 | { 430 | register ptptr p; 431 | register int retval; 432 | 433 | if (statloc > (int *)(&udata)) 434 | { 435 | udata.u_error = EFAULT; 436 | return(-1); 437 | } 438 | 439 | di(); 440 | /* See if we have any children. */ 441 | for (p=ptab;p < ptab+PTABSIZE; ++p) 442 | { 443 | if (p->p_status && p->p_pptr == udata.u_ptab && p != udata.u_ptab) 444 | goto ok; 445 | } 446 | udata.u_error = ECHILD; 447 | ei(); 448 | return (-1); 449 | 450 | ok: 451 | /* Search for an exited child; */ 452 | for (;;) 453 | { 454 | chksigs(); 455 | if (udata.u_cursig) 456 | { 457 | udata.u_error = EINTR; 458 | return(-1); 459 | } 460 | di(); 461 | for(p=ptab;p < ptab+PTABSIZE; ++p) 462 | { 463 | if (p->p_status == P_ZOMBIE && p->p_pptr == udata.u_ptab) 464 | { 465 | if (statloc) 466 | *statloc = p->p_exitval; 467 | p->p_status = P_EMPTY; 468 | retval = p->p_pid; 469 | 470 | /* Add in child's time info */ 471 | /* It was stored on top of p_wait in the childs process 472 | table entry */ 473 | addtick(&udata.u_cutime, &(p->p_wait)); 474 | addtick(&udata.u_cstime, (char *)(&(p->p_wait)) + 475 | sizeof(time_t)); 476 | 477 | ei(); 478 | return(retval); 479 | } 480 | } 481 | /* Nothing yet, so wait */ 482 | psleep(udata.u_ptab); 483 | } 484 | 485 | } 486 | 487 | #undef statloc 488 | 489 | 490 | 491 | /************************************** 492 | _exit(val) 493 | int16 val; 494 | **************************************/ 495 | 496 | #define val (int16)udata.u_argn 497 | 498 | __exit() 499 | { 500 | doexit(val,0); 501 | } 502 | 503 | #undef val 504 | 505 | 506 | 507 | doexit(val,val2) 508 | int16 val; 509 | int16 val2; 510 | { 511 | register int16 j; 512 | register ptptr p; 513 | 514 | for (j=0; j < UFTSIZE; ++j) 515 | { 516 | ifnot (udata.u_files[j] & 0x80) /* Portable equivalent of == -1 */ 517 | doclose(j); 518 | } 519 | 520 | /* _sync(); /* Not necessary, but a good idea. */ 521 | 522 | di(); 523 | udata.u_ptab->p_exitval = (val<<8) | (val2 & 0xff); 524 | 525 | /* Set child's parents to init */ 526 | for(p=ptab;p < ptab+PTABSIZE; ++p) 527 | { 528 | if (p->p_status && p->p_pptr == udata.u_ptab) 529 | p->p_pptr = initproc; 530 | } 531 | i_deref(udata.u_cwd); 532 | 533 | /* Stash away child's execution tick counts in process table, 534 | overlaying some no longer necessary stuff. */ 535 | addtick(&udata.u_utime,&udata.u_cutime); 536 | addtick(&udata.u_stime,&udata.u_cstime); 537 | bcopy(&udata.u_utime, &(udata.u_ptab->p_wait), 2 * sizeof(time_t)); 538 | 539 | /* Wake up a waiting parent, if any. */ 540 | if (udata.u_ptab != initproc) 541 | wakeup((char *)udata.u_ptab->p_pptr); 542 | udata.u_ptab->p_status = P_ZOMBIE; 543 | ei(); 544 | swapin(getproc()); 545 | panic("doexit:won't exit"); 546 | } 547 | 548 | 549 | _fork() 550 | { 551 | return (dofork()); 552 | } 553 | 554 | 555 | 556 | _pause() 557 | { 558 | psleep(0); 559 | udata.u_error = EINTR; 560 | return(-1); 561 | } 562 | 563 | 564 | /************************************* 565 | signal(sig, func) 566 | int16 sig; 567 | int16 (*func)(); 568 | ***************************************/ 569 | 570 | #define sig (int16)udata.u_argn1 571 | #define func (int (*)())udata.u_argn 572 | 573 | _signal() 574 | { 575 | int retval; 576 | 577 | di(); 578 | if (sig < 1 || sig == SIGKILL || sig >= NSIGS) 579 | { 580 | udata.u_error = EINVAL; 581 | goto nogood; 582 | } 583 | 584 | if (func == SIG_IGN) 585 | udata.u_ptab->p_ignored |= sigmask(sig); 586 | else 587 | { 588 | if (func != SIG_DFL && ((char *)func < PROGBASE || 589 | (struct u_data *)func >= &udata)) 590 | { 591 | udata.u_error = EFAULT; 592 | goto nogood; 593 | } 594 | udata.u_ptab->p_ignored &= ~sigmask(sig); 595 | } 596 | retval = udata.u_sigvec[sig]; 597 | udata.u_sigvec[sig] = func; 598 | ei(); 599 | return(retval); 600 | 601 | nogood: 602 | ei(); 603 | return(-1); 604 | } 605 | 606 | #undef sig 607 | #undef func 608 | 609 | 610 | 611 | /************************************** 612 | kill(pid, sig) 613 | int16 pid; 614 | int16 sig; 615 | *****************************************/ 616 | 617 | #define pid (int16)udata.u_argn1 618 | #define sig (int16)udata.u_argn 619 | 620 | _kill() 621 | { 622 | ptptr p; 623 | 624 | if (sig <= 0 || sig > 15) 625 | goto nogood; 626 | 627 | for (p=ptab; p < ptab+PTABSIZE; ++p) 628 | { 629 | if (p->p_pid == pid) 630 | { 631 | sendsig(p,sig); 632 | return(0); 633 | } 634 | } 635 | 636 | nogood: 637 | udata.u_error = EINVAL; 638 | return(-1); 639 | } 640 | 641 | #undef pid 642 | #undef sig 643 | 644 | 645 | 646 | /******************************** 647 | alarm(secs) 648 | uint16 secs; 649 | *********************************/ 650 | 651 | #define secs (int16)udata.u_argn 652 | 653 | _alarm() 654 | { 655 | int retval; 656 | 657 | di(); 658 | retval = udata.u_ptab->p_alarm; 659 | udata.u_ptab->p_alarm = secs; 660 | ei(); 661 | return(retval); 662 | } 663 | 664 | #undef secs 665 | 666 | -------------------------------------------------------------------------------- /unix.h: -------------------------------------------------------------------------------- 1 | /************************************************** 2 | UZI (Unix Z80 Implementation) Kernel: unix.h 3 | ***************************************************/ 4 | 5 | 6 | #ifndef vax 7 | #define CPM 8 | #endif 9 | 10 | #define UFTSIZE 10 /* Number of user files */ 11 | #define OFTSIZE 15 /* Open file table size */ 12 | #define ITABSIZE 20 /* Inode table size */ 13 | #define PTABSIZE 20 /* Process table size */ 14 | 15 | #define NSIGS 16 /* Number of signals <= 16 */ 16 | 17 | #define ROOTINODE 1 /* Inode # of / for all mounted filesystems. */ 18 | 19 | #define TICKSPERSEC 10 /*Ticks per second */ 20 | #define MAXTICKS 10 /* Max ticks before swapping out (time slice) */ 21 | 22 | #define ARGBLK 0 /* Block number on SWAPDEV for arguments */ 23 | #define PROGBASE ((char *)(0x100)) 24 | #define MAXEXEC 0 /* Max no of blks of executable file */ 25 | 26 | #define EMAGIC 0xc3 /* Header of executable */ 27 | #define CMAGIC 24721 /* Random number for cinode c_magic */ 28 | #define SMOUNTED 12742 /* Magic number to specify mounted filesystem */ 29 | #define NULL 0 30 | 31 | 32 | /* These macros are simply to trick the compiler into generating 33 | more compact code. */ 34 | 35 | #define ifnull(e) if(e){}else 36 | #define ifnot(e) if(e){}else 37 | #define ifzero(e) if(e){}else 38 | 39 | 40 | 41 | #ifdef CPM 42 | typedef unsigned uint16; 43 | typedef int int16; 44 | #else 45 | typedef unsigned short uint16; 46 | typedef short int16; 47 | #endif 48 | 49 | 50 | typedef struct s_queue { 51 | char *q_base; /* Pointer to data */ 52 | char *q_head; /* Pointer to addr of next char to read. */ 53 | char *q_tail; /* Pointer to where next char to insert goes. */ 54 | int q_size; /* Max size of queue */ 55 | int q_count; /* How many characters presently in queue */ 56 | int q_wakeup; /* Threshold for waking up processes waiting on queue */ 57 | } queue_t; 58 | 59 | 60 | 61 | typedef struct time_s { 62 | uint16 t_time; 63 | uint16 t_date; 64 | } time_t; 65 | 66 | 67 | /* User's structure for times() system call */ 68 | 69 | struct tms { 70 | time_t tms_utime; 71 | time_t tms_stime; 72 | time_t tms_cutime; 73 | time_t tms_cstime; 74 | time_t tms_etime; /* Elapsed real time */ 75 | } ; 76 | 77 | 78 | /* Flags for setftime() */ 79 | #define A_TIME 1 80 | #define M_TIME 2 81 | #define C_TIME 4 82 | 83 | 84 | typedef struct off_t { 85 | uint16 o_blkno; /* Block number */ 86 | int16 o_offset; /* Offset within block 0-511 */ 87 | } off_t; 88 | 89 | 90 | typedef uint16 blkno_t; /* Can have 65536 512-byte blocks in filesystem */ 91 | #define NULLBLK ((blkno_t)-1) 92 | 93 | 94 | typedef struct blkbuf { 95 | char bf_data[512]; /* This MUST be first ! */ 96 | char bf_dev; 97 | blkno_t bf_blk; 98 | char bf_dirty; 99 | char bf_busy; 100 | uint16 bf_time; /* LRU time stamp */ 101 | /* struct blkbuf *bf_next; /* LRU free list pointer */ 102 | } blkbuf, *bufptr; 103 | 104 | 105 | typedef struct dinode { 106 | uint16 i_mode; 107 | uint16 i_nlink; 108 | uint16 i_uid; 109 | uint16 i_gid; 110 | off_t i_size; 111 | time_t i_atime; 112 | time_t i_mtime; 113 | time_t i_ctime; 114 | blkno_t i_addr[20]; 115 | } dinode; /* Exactly 64 bytes long! */ 116 | 117 | 118 | struct stat /* Really only used by users */ 119 | { 120 | int16 st_dev; 121 | uint16 st_ino; 122 | uint16 st_mode; 123 | uint16 st_nlink; 124 | uint16 st_uid; 125 | uint16 st_gid; 126 | uint16 st_rdev; 127 | off_t st_size; 128 | time_t st_atime; 129 | time_t st_mtime; 130 | time_t st_ctime; 131 | }; 132 | 133 | /* Bit masks for i_mode and st_mode */ 134 | 135 | #define OTH_EX 0001 136 | #define OTH_WR 0002 137 | #define OTH_RD 0004 138 | #define GRP_EX 0010 139 | #define GRP_WR 0020 140 | #define GRP_RD 0040 141 | #define OWN_EX 0100 142 | #define OWN_WR 0200 143 | #define OWN_RD 0400 144 | 145 | #define SAV_TXT 01000 146 | #define SET_GID 02000 147 | #define SET_UID 04000 148 | 149 | #define MODE_MASK 07777 150 | 151 | #define F_REG 0100000 152 | #define F_DIR 040000 153 | #define F_PIPE 010000 154 | #define F_BDEV 060000 155 | #define F_CDEV 020000 156 | 157 | #define F_MASK 0170000 158 | 159 | 160 | 161 | typedef struct cinode { 162 | int c_magic; /* Used to check for corruption. */ 163 | int c_dev; /* Inode's device */ 164 | unsigned c_num; /* Inode # */ 165 | dinode c_node; 166 | char c_refs; /* In-core reference count */ 167 | char c_dirty; /* Modified flag. */ 168 | } cinode, *inoptr; 169 | 170 | #define NULLINODE ((inoptr)NULL) 171 | #define NULLINOPTR ((inoptr*)NULL) 172 | 173 | 174 | typedef struct direct { 175 | uint16 d_ino; 176 | char d_name[14]; 177 | } direct; 178 | 179 | 180 | 181 | typedef struct filesys { 182 | int16 s_mounted; 183 | uint16 s_isize; 184 | uint16 s_fsize; 185 | int16 s_nfree; 186 | blkno_t s_free[50]; 187 | int16 s_ninode; 188 | uint16 s_inode[50]; 189 | int16 s_fmod; 190 | time_t s_time; 191 | blkno_t s_tfree; 192 | uint16 s_tinode; 193 | inoptr s_mntpt; /* Mount point */ 194 | } filesys, *fsptr; 195 | 196 | typedef struct oft { 197 | off_t o_ptr; /* File position point16er */ 198 | inoptr o_inode; /* Pointer into in-core inode table */ 199 | char o_access; /* O_RDONLY, O_WRONLY, or O_RDWR */ 200 | char o_refs; /* Reference count: depends on # of active children*/ 201 | } oft; 202 | 203 | 204 | /* Process table p_status values */ 205 | 206 | #define P_EMPTY 0 /* Unused slot */ 207 | #define P_RUNNING 1 /* Currently running process */ 208 | #define P_READY 2 /* Runnable */ 209 | #define P_SLEEP 3 /* Sleeping; can be awakened by signal */ 210 | #define P_XSLEEP 4 /* Sleeping, don't wake up for signal */ 211 | #define P_PAUSE 5 /* Sleeping for pause(); can wakeup for signal */ 212 | #define P_FORKING 6 /* In process of forking; do not mess with */ 213 | #define P_WAIT 7 /* Executed a wait() */ 214 | #define P_ZOMBIE 8 /* Exited. */ 215 | 216 | 217 | #define SIGHUP 1 218 | #define SIGINT 2 219 | #define SIGQUIT 3 220 | #define SIGILL 4 221 | #define SIGTRAP 5 222 | #define SIGIOT 6 223 | #define SIGEMT 7 224 | #define SIGFPE 8 225 | #define SIGKILL 9 226 | #define SIGBUS 10 227 | #define SIGSEGV 11 228 | #define SIGSYS 12 229 | #define SIGPIPE 13 230 | #define SIGALRM 14 231 | #define SIGTERM 15 232 | 233 | #define SIG_DFL (int (*)())0 234 | #define SIG_IGN (int (*)())1 235 | 236 | #define sigmask(sig) (1<<(sig)) 237 | 238 | /* Process table entry */ 239 | 240 | typedef struct p_tab { 241 | char p_status; /* Process status */ 242 | int p_pid; /* Process ID */ 243 | int p_uid; 244 | struct p_tab *p_pptr; /* Process parent's table entry */ 245 | blkno_t p_swap; /* Starting block of swap space */ 246 | unsigned p_alarm; /* Seconds until alarm goes off */ 247 | unsigned p_exitval; /* Exit value */ 248 | /* Everything below here is overlaid by time info at exit */ 249 | char *p_wait; /* Address of thing waited for */ 250 | int p_priority; /* Process priority */ 251 | uint16 p_pending; /* Pending signals */ 252 | uint16 p_ignored; /* Ignored signals */ 253 | } p_tab, *ptptr; 254 | 255 | /* Per-process data (Swapped with process) */ 256 | 257 | #asm 8080 258 | ?OSYS equ 2 ;byte offsets of elements of u_data 259 | ?OCALL equ 3 260 | ?ORET equ 4 ;return location 261 | ?ORVAL equ 6 ;return value 262 | ?OERR equ 8 ;error number 263 | ?OSP equ 10 ;users stack pointer 264 | ?OBC equ 12 ;users frame pointer 265 | #endasm 266 | 267 | typedef struct u_data { 268 | struct p_tab *u_ptab; /* Process table pointer */ 269 | char u_insys; /* True if in kernel */ 270 | char u_callno; /* sys call being executed. */ 271 | char *u_retloc; /* Return location from sys call */ 272 | int u_retval; /* Return value from sys call */ 273 | int u_error; /* Last error number */ 274 | char *u_sp; /* Used when a process is swapped. */ 275 | char *u_bc; /* Place to save user's frame pointer */ 276 | int u_argn; /* Last arg */ 277 | int u_argn1; /* This way because args on stack backwards */ 278 | int u_argn2; 279 | int u_argn3; /* args n-3, n-2, n-1, and n */ 280 | 281 | char * u_base; /* Source or dest for I/O */ 282 | unsigned u_count; /* Amount for I/O */ 283 | off_t u_offset; /* Place in file for I/O */ 284 | struct blkbuf *u_buf; 285 | 286 | int u_gid; 287 | int u_euid; 288 | int u_egid; 289 | int u_mask; /* umask: file creation mode mask */ 290 | time_t u_time; /* Start time */ 291 | char u_files[UFTSIZE]; /* Process file table: 292 | contains indexes into open file table. */ 293 | inoptr u_cwd; /* Index into inode table of cwd. */ 294 | char *u_break; /* Top of data space */ 295 | 296 | inoptr u_ino; /* Used during execve() */ 297 | char *u_isp; /* Value of initial sp (argv) */ 298 | 299 | int (*u_sigvec[NSIGS])(); /* Array of signal vectors */ 300 | int u_cursig; /* Signal currently being caught */ 301 | char u_name[8]; /* Name invoked with */ 302 | time_t u_utime; /* Elapsed ticks in user mode */ 303 | time_t u_stime; /* Ticks in system mode */ 304 | time_t u_cutime; /* Total childrens ticks */ 305 | time_t u_cstime; 306 | 307 | } u_data; 308 | 309 | 310 | /* Struct to temporarily hold arguments in execve */ 311 | struct s_argblk { 312 | int a_argc; 313 | int a_arglen; 314 | int a_envc; 315 | char a_buf[512-3*sizeof(int)]; 316 | }; 317 | 318 | 319 | /* The device driver switch table */ 320 | 321 | typedef struct devsw { 322 | int minor; /* The minor device number (an argument to below) */ 323 | int (*dev_open)(); /* The routines for reading, etc */ 324 | int (*dev_close)(); /* format: op(minor,blkno,offset,count,buf); */ 325 | int (*dev_read)(); /* offset would be ignored for block devices */ 326 | int (*dev_write)(); /* blkno and offset ignored for tty, etc. */ 327 | int (*dev_ioctl)(); /* count is rounded to 512 for block devices */ 328 | } devsw; 329 | 330 | 331 | 332 | /* Open() parameters. */ 333 | 334 | #define O_RDONLY 0 335 | #define O_WRONLY 1 336 | #define O_RDWR 2 337 | 338 | /* 339 | * Error codes 340 | */ 341 | 342 | #define EPERM 1 343 | #define ENOENT 2 344 | #define ESRCH 3 345 | #define EINTR 4 346 | #define EIO 5 347 | #define ENXIO 6 348 | #define E2BIG 7 349 | #define ENOEXEC 8 350 | #define EBADF 9 351 | #define ECHILD 10 352 | #define EAGAIN 11 353 | #define ENOMEM 12 354 | #define EACCES 13 355 | #define EFAULT 14 356 | #define ENOTBLK 15 357 | #define EBUSY 16 358 | #define EEXIST 17 359 | #define EXDEV 18 360 | #define ENODEV 19 361 | #define ENOTDIR 20 362 | #define EISDIR 21 363 | #define EINVAL 22 364 | #define ENFILE 23 365 | #define EMFILE 24 366 | #define ENOTTY 25 367 | #define ETXTBSY 26 368 | #define EFBIG 27 369 | #define ENOSPC 28 370 | #define ESPIPE 29 371 | #define EROFS 30 372 | #define EMLINK 31 373 | #define EPIPE 32 374 | #define ENAMETOOLONG 63 375 | 376 | #include "config.h" 377 | 378 | --------------------------------------------------------------------------------