diff --git a/tlvc/arch/i86/drivers/char/mem.c b/tlvc/arch/i86/drivers/char/mem.c index a0a6dd2e..8c270163 100644 --- a/tlvc/arch/i86/drivers/char/mem.c +++ b/tlvc/arch/i86/drivers/char/mem.c @@ -53,9 +53,9 @@ int memory_lseek(struct inode *inode, register struct file *filp, default: return -EINVAL; } - if (offset != filp->f_pos) { + //if (offset != filp->f_pos) { filp->f_pos = offset; - } + //} return 0; } @@ -354,7 +354,7 @@ static struct file_operations kmem_fops = { */ int memory_open(register struct inode *inode, struct file *filp) { -#ifdef DEBUG +#ifdef DEBUG_MM static char *mdev_nam[] = { /* Unimplemented minors will print out the correct device name diff --git a/tlvc/arch/i86/drivers/char/ntty.c b/tlvc/arch/i86/drivers/char/ntty.c index 81a35445..e2186082 100644 --- a/tlvc/arch/i86/drivers/char/ntty.c +++ b/tlvc/arch/i86/drivers/char/ntty.c @@ -295,7 +295,7 @@ size_t tty_write(struct inode *inode, struct file *file, char *data, size_t len) while (count < len) { ret = chq_wait_wr(&tty->outq, (file->f_flags & O_NONBLOCK) | count); if (ret < 0) { - if (count != 0 && ret == -EAGAIN) { + if (count != 0 && ret == -EAGAIN) { /* queue is full and something has been written already */ tty->ops->write(tty); wake_up(&tty->outq.wait); schedule(); @@ -395,6 +395,9 @@ int tty_ioctl(struct inode *inode, struct file *file, int cmd, char *arg) register struct tty *tty = determine_tty(inode->i_rdev); int ret, dev; + if (!determine_tty(inode->i_rdev)) /* not a tty device */ + return -EINVAL; + switch (cmd) { case TCGETS: ret = verified_memcpy_tofs(arg, &tty->termios, sizeof(struct termios)); @@ -414,6 +417,67 @@ int tty_ioctl(struct inode *inode, struct file *file, int cmd, char *arg) return -EINVAL; set_console(dev); return 0; + case TIOCGETP: /* Unix v7 gtty() */ +#ifdef CONFIG_COMPAT_V7 + { + struct sgttyb st; + st.sg_ispeed = st.sg_ospeed = tty->termios.c_cflag&CBAUD; + st.sg_erase = tty->termios.c_cc[VERASE]; + st.sg_kill = tty->termios.c_cc[VKILL]; + st.sg_flags = (O_RAW * !(tty->termios.c_oflag & OPOST)) + + (O_CBREAK * !(tty->termios.c_lflag & ICANON)) + + (O_ECHO * (tty->termios.c_lflag & ECHO)) + + (O_CRMOD * (tty->termios.c_iflag & ICRNL)); + if (tty->termios.c_cflag&PARENB) { + if (tty->termios.c_cflag&PARODD) st.sg_flags |= O_ODDP; + else st.sg_flags |= O_EVENP; + } + ret = verified_memcpy_tofs(arg, &st, sizeof(struct sgttyb)); + break; + } +#else + return -EINVAL; +#endif + case TIOCSETP: /* Unix V7 stty() */ +#ifdef CONFIG_COMPAT_V7 + { + struct sgttyb st; + ret = verified_memcpy_fromfs(&st, arg, sizeof(struct sgttyb)); + if (ret == 0) { + int fl; + + fl = st.sg_flags; + if (fl&O_RAW) { + tty->termios.c_lflag &= ~(ICANON | ISIG | ECHO); + tty->termios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + tty->termios.c_oflag &= ~OPOST; + } + if (fl&O_CBREAK) tty->termios.c_lflag &= ~ICANON; + if (fl&O_ECHO) tty->termios.c_lflag |= ECHO; + if (fl&O_CRMOD) { + tty->termios.c_iflag |= ICRNL; + tty->termios.c_oflag |= ONLCR; + } + if (fl&O_ODDP) { + tty->termios.c_cflag |= (PARENB | PARODD); + tty->termios.c_iflag |= ISTRIP; + } + if (fl&O_EVENP) { + tty->termios.c_cflag |= PARENB; + tty->termios.c_cflag &= ~PARODD; + tty->termios.c_iflag |= ISTRIP; + } + tty->termios.c_cflag &= ~CBAUD; + tty->termios.c_cflag |= st.sg_ispeed; + + /* Inform subdriver of new settings TEST THIS MAY NOT BE REQUIRED */ + //if (ret == 0 && tty->ops->ioctl != NULL) + ret = tty->ops->ioctl(tty, TCSETS, &tty->termios); + break; + } + } +#endif + return -EINVAL; default: ret = ((tty->ops->ioctl == NULL) ? -EINVAL diff --git a/tlvc/arch/i86/kernel/asm-offsets.c b/tlvc/arch/i86/kernel/asm-offsets.c index d00667be..9d0aa665 100644 --- a/tlvc/arch/i86/kernel/asm-offsets.c +++ b/tlvc/arch/i86/kernel/asm-offsets.c @@ -2,7 +2,7 @@ #include extern int TASK_KRNL_SP, TASK_USER_DS, TASK_USER_AX, TASK_USER_SS; -extern int TASK_USER_BX, TASK_USER_SI, TASK_USER_DI; +extern int TASK_USER_BX, TASK_USER_SI, TASK_USER_DI, TASK_USER_V7; void asm_offsets(void) { @@ -13,5 +13,6 @@ void asm_offsets(void) TASK_USER_BX = offsetof(struct task_struct, t_regs.bx); TASK_USER_SI = offsetof(struct task_struct, t_regs.si); TASK_USER_DI = offsetof(struct task_struct, t_regs.di); + TASK_USER_V7 = offsetof(struct task_struct, task_is_V7); /* (temporary) V7 binary flag */ } diff --git a/tlvc/arch/i86/kernel/divzero.c b/tlvc/arch/i86/kernel/divzero.c index b59ecb14..e404d25d 100644 --- a/tlvc/arch/i86/kernel/divzero.c +++ b/tlvc/arch/i86/kernel/divzero.c @@ -4,19 +4,19 @@ /* * Divide by zero and divide overflow exception handler - * NOTE: This handler is currently unused, see div0_handler_panic in irqtab.S * * 19 Aug 24 Greg Haerr */ -void div0_handler(int i, struct pt_regs *regs) +#define DEBUG 0 /* =1 for CS:IP detail of fault */ + +void div0_handler(int irq, struct pt_regs *regs) { - /* divide by 0 from nested interrupt or idle task means kernel code was executing */ if (intr_count > 1 /*|| current->t_regs.ss == kernel_ds*/) { /* * Trap from kernel code or idle task. * - * Not panicing at this point involves determining the CS:IP of the + * Not calling panic at this point involves determining the CS:IP of the * faulting instruction, then doing two different things depending on * whether the CPU is 8088/8086/V20/V30 or 286/386+: the former trap * pushes the CS:IP following the DIV, while the latter pushes CS:IP of @@ -30,15 +30,17 @@ void div0_handler(int i, struct pt_regs *regs) sys_stack = _MK_FP(regs->ss, regs->sp); printk("CS:IP %04x:%04x\n", sys_stack->cs, sys_stack->ip); #endif + panic("DIVIDE FAULT\n"); - } else { - /* For user mode faults, display error message and kill the process */ + } else { /* For user mode faults, display error message and kill the process */ printk("DIVIDE FAULT\n"); #if DEBUG + struct uregs __far *user_stack; user_stack = _MK_FP(current->t_regs.ss, current->t_regs.sp); printk("CS:IP %04x:%04x\n", user_stack->cs, user_stack->ip); #endif + sys_kill(current->pid, SIGABRT); /* no SIGFPE so send SIGABRT for now */ } } diff --git a/tlvc/arch/i86/kernel/irq.c b/tlvc/arch/i86/kernel/irq.c index 3fc8bee1..deb9c0d9 100644 --- a/tlvc/arch/i86/kernel/irq.c +++ b/tlvc/arch/i86/kernel/irq.c @@ -41,6 +41,9 @@ struct int_handler { static struct int_handler trampoline[NR_IRQS]; static irq_handler irq_action[NR_IRQS]; +#ifdef CONFIG_COMPAT_V7 +void abort_v7(void); +#endif /* called by _irqit assembler hook after saving registers */ void do_IRQ(int i, struct pt_regs *regs) @@ -120,7 +123,14 @@ int free_irq(int irq) */ void INITPROC irq_init(void) { - int_handler_add(IDX_SYSCALL, 0x80, _irqit); /* INT 80 for system calls */ + int_handler_add(IDX_SYSCALL, 0x80, _irqit); /* INT 80 for system calls */ +#ifdef CONFIG_COMPAT_V7 + int_handler_add(IDX_SYSCALL_V7, 0xf1, _irqit_v7); /* INT F1 for V7 system calls */ + int_handler_add(IDX_SYSFPU_V7, 0xf4, _sysfpu_v7); /* FPU check, precedes FPU instructions */ + int_handler_add(IDX_SYSIOT_V7, 0xf3, _abort_v7); /* Used by the V7 abort() library call */ + int_handler_add(IDX_SYSMAP_V7, 0xf5, _sysmap_v7); /* code mapper */ + int_handler_add(IDX_STKTRAP_V7, 0xf2, _stktrap_v7); /* Venix stack overflow trap */ +#endif #if defined(CONFIG_ARCH_IBMPC) || defined(CONFIG_ARCH_PC98) || \ defined(CONFIG_ARCH_SOLO86) || defined(CONFIG_ARCH_SWAN) || \ @@ -162,3 +172,11 @@ void INITPROC irq_init(void) enable_timer_tick(); /* reprogram timer for 100 HZ */ #endif } + +#ifdef CONFIG_COMPAT_V7_UNUSED +void abort_v7(void) +{ + printk("V7 IOT trap\n"); + sys_kill(current->pid, SIGABRT); +} +#endif diff --git a/tlvc/arch/i86/kernel/irqtab.S b/tlvc/arch/i86/kernel/irqtab.S index 4f82eae5..6d157460 100644 --- a/tlvc/arch/i86/kernel/irqtab.S +++ b/tlvc/arch/i86/kernel/irqtab.S @@ -54,6 +54,124 @@ .extern do_bottom_half .extern schedule .extern panic + .extern printk // for debugging + +/* + * June 2026: Optional compatibility syscall layer for Venix 2.1/86 binaries + * (Unix V7). In general the system calls are + * Linux compatible, using IRQ vector 0xF1 instead of our 0x80, and swapping + * registers AX and BX for syscall # and parm 1 respectively. CX and DX are also + * swapped while DI and SI are left alone as unsafe to use for syscalls. + * + * For most system calls we simply swap 2x2 registers and go, but there are + * important exceptions. Notably the stat structure is slightly different + * (inode_t is u_16 on venix) affecting stat/fstat. While TLVC never returns + * longs in registers (e.g lseek), Venix has many which does. The upper nibble + * of t_mode in struct stat is different (also stat/fstat). Syscall details in Wiki. + * Finally, Venix returns result or -1 in ax, while CX holds the error # if any. + * CX equals zero if all is good (creative use of the jcxz instruction). All + * this and more is handled here. + * + * A V7 syscall sets a flag in a (normally) unused variable in the task struct, + * which informs the syscall routine to do whatever is needed. This flag may + * be cleared in cases like nested syscalls, and not reliable for proper handling + * of a syscall return. An extra flag added to the syscall number in AX before + * pushing it on the stack guarantees correct exit handling. + */ + +#ifdef CONFIG_COMPAT_V7 + .global _irqit_v7 /* int F1 */ + .global _sysfpu_v7 /* int F4 */ + .global _abort_v7 /* int F3 */ + .global _stktrap_v7 /* int F2 */ + .global _sysmap_v7 /* int F5 */ + +_sysfpu_v7: + // FPU is required, just return + //mov $sysfpu_msg,%ax + //call _intmsg +_sysret: + add $4,%sp // skip dynamic handler return stack + iret + +_intmsg: + // entered with string ptr in AX + push %ds + push %si + mov %sp,%si + mov %ss:8(%si),%ds + push %ax + call printk + pop %ax + pop %si + pop %ds + ret + +_stktrap_v7: /* Stack trap INT, may need a more elaborate message */ + mov $stktrap_msg,%ax + call _intmsg + jmp _sysret + +_sysmap_v7: + // codemap not implemented, this is fatal + mov $codemap_msg,%ax + call _intmsg + add $2,%sp + // just drop into _abort + +_abort_v7: + push %ds + mov %sp,%si + mov %ss:4(%si),%ds // Get kernel DS set + + mov current,%bx; + mov $6,%ax + push %ax // SIGABRT/SIGIOT + push 2(%bx) // pid + + call sys_kill + add $4,%sp + pop %ds + ret + +//---------------- start syscall handling ----------------------------- + +_irqit_v7: // Handle Venix/V7 syscall + xchg %ax,%bx // rearrange args + xchg %cx,%dx + cmp $0x13,%al // lseek needs special treatment to get + // a long into the arg list... + jnz 1f + // TODO: maybe test (%bh != 0) in which case + // fd is bogus, just let is pass + push %dx + mov %si,%dx // whence is in %si + mov %dl,%bh // whence to high byte of fd + //or $0x80,%bh // add flag, to be 'converted src compatible' + // ... for now + pop %dx +1: + push %ds + push %si + push %di + + mov %sp,%si + mov %ss:8(%si),%ds // Get kernel DS set so we can do our thing + + push %bx + mov current,%bx + mov %ax,TASK_USER_V7(%bx) // set the task_V7 flag, used by the syscalls to adapt + // behaviour. Any non-zero number will do, but + // using the syscall # allows for some useful + // tracing/debugging. + + inc %ah // Additional 'protection': set a 'local' V7 return flag. + // The task_V7 flag may get turned off during syscall + // processing, IOW not a reliable trigger for correct + // syscall exit processing. + pop %bx + jmp _irqit_cont +#endif /* CONFIG_COMPAT_V7 */ .global _irqit _irqit: @@ -70,9 +188,18 @@ _irqit: // mov %sp,%si mov %ss:8(%si),%ds +#ifdef CONFIG_COMPAT_V7 + /* give native syscalls a guaranteed clean slate */ + push %bx + mov current,%bx + movw $0,TASK_USER_V7(%bx) + pop %bx + +#endif // // Determine which stack to use // +_irqit_cont: cmpw $1,intr_count jc utask // We were in user mode jz itask // Using a process's kernel stack @@ -144,11 +271,16 @@ save_regs: // movb (%di),%al cmpb $IDX_SYSCALL,%al - jne doirq +#ifdef CONFIG_COMPAT_V7 + je do_syscall + cmpb $IDX_SYSCALL_V7,%al +#endif + jne doirq /* ! ! ----------PROCESS SYSCALL---------- */ +do_syscall: sti call check_ustack // Check user mode stack @@ -157,8 +289,50 @@ save_regs: #endif pop %ax // get syscall function code in AX +#if 0 /* DEBUG message: syscall entry */ + push %ax + mov $callno,%bx + push %ax + push %bx + call printk + add $4,%sp + pop %ax +#endif + xor %ah,%ah // discard V7 flag, it's on the stack anyway call syscall push %ax // syscall return value in ax + push %dx // for V7 COMPAT, return longs +#ifdef CONFIG_COMPAT_V7 + //mov %sp,%bx + //test $0x100,12(%bx) + //jnz restore_regs_V7 + mov %sp,%bx + mov 14(%bx),%cx // syscall # + push %cx +#if 0 /* DEBUG: syscall exit */ + mov $callno1,%ax + mov current,%bx + push TASK_USER_V7(%bx) // V7 flag + push %cx // syscall # + push %ax // format string + call printk + add $6,%sp +#endif + + // update the V7 flag if needed, it may have been cleared + // during syscall execution... + pop %cx +#if 0 /* probably not needed --------------- */ + mov current,%bx + test $1,%ch + jz 1f + cmp $0,TASK_USER_V7(%bx) // insurance + jnz 1f + xor %ch,%ch + mov %cx,TASK_USER_V7(%bx) +1: +#endif +#endif #ifdef CONFIG_TRACE // strace.c must be compiled with tail optimization off to protect top of stack @@ -171,6 +345,7 @@ save_regs: 1: call do_signal // process signals cli + pop %dx jmp restore_regs // // Done. @@ -318,11 +493,51 @@ was_trap: restore_regs: decw intr_count pop %ax - pop %bx - pop %cx + +#ifdef CONFIG_COMPAT_V7 +// +// Some V7 syscalls return a 'long' in AX:DX. if the return value is a real 32bit value +// and not simply two ints like in pipe() and wait(), we need to check whether +// we're dealing with a valid return or an error code. The test is DX == 0xffff. +// If true, we're quite (although not completely) sure it's an error code. The exception +// (so far) is time(): If (when) we get REALLY close to the end of time :-), that is, +// less than 10 hrs (the upper bit of ax must be set in order for the value to qualify +// as an error), all time() returns will be considered errors. I can live with that. +// + mov %sp,%bx + //testw $0x100,10(%bx) // check original AX for V7 flag + //jz normal_restore + //mov 10(%si),%bx + mov current,%bx + cmp $0,TASK_USER_V7(%bx) + je normal_restore + mov TASK_USER_V7(%bx),%cx // has syscall # + add $6,%sp // discard BX, CX, DX + test $0x100,%cx // test the 'long return' bit + mov $0,%cx // CX is now the error flag + jz 1f // regular error handling + cmp $0xffff,%dx // long error code ?? + jnz skip_pops // dx=-1: normal ax error processing +1: + or %ax,%ax + jge skip_pops // No error +long_err: + neg %ax + mov %ax,%cx // CX goes to 'errno' in the app + mov $-1,%ax // app level error indicator +skip_pops: + pop %di + pop %si // CLEAN UP THIS + jmp s_pops +normal_restore: +#endif /* Normal register restore */ + pop %bx + pop %cx pop %dx + pop %di pop %si +s_pops: pop %bp // discard orig_AX pop %es pop %ds @@ -461,6 +676,15 @@ intr_count: // stacked interrupts count. Start with 1 #ifdef CHECK_SS ssmsg: .ascii "INVALID SS\0" #endif +callno: .ascii "syscall in 0x%x, \0" +callno1: + .ascii "out 0x%x (V7: %x)\n\0" +//sysfpu_msg: + //.ascii "int SYSFPU\n\0" +stktrap_msg: + .ascii "int STKTRAP\n\0" +codemap_msg: + .ascii "int CODEMAP\n\0" .bss .p2align 1 diff --git a/tlvc/arch/i86/kernel/process.c b/tlvc/arch/i86/kernel/process.c index de6580b8..eaf79b52 100644 --- a/tlvc/arch/i86/kernel/process.c +++ b/tlvc/arch/i86/kernel/process.c @@ -110,10 +110,10 @@ unsigned get_ustack(register struct task_struct *t,int off) /* * Called by sys_execve() */ -void arch_setup_user_stack (register struct task_struct * t, word_t entry, seg_t cseg) +void arch_setup_user_stack(register struct task_struct *t, word_t entry, seg_t cseg) { put_ustack(t, -2, USER_FLAGS); /* Flags */ - put_ustack(t, -4, cseg); /* user CS */ + put_ustack(t, -4, cseg); /* user CS */ put_ustack(t, -6, entry); /* user entry point */ put_ustack(t, -8, 0); /* user BP */ t->t_regs.sp -= 8; diff --git a/tlvc/arch/i86/kernel/strace.c b/tlvc/arch/i86/kernel/strace.c index fa6f1c65..a90b21f8 100644 --- a/tlvc/arch/i86/kernel/strace.c +++ b/tlvc/arch/i86/kernel/strace.c @@ -18,6 +18,10 @@ void check_ustack(void) segoff_t brk = current->t_endbrk; segoff_t stacklow = current->t_begstack - current->t_minstack; +#ifdef CONFIG_COMPAT_V7 + if (current->t_begstack < current->t_enddata) /* stack is below */ + return; +#endif if (sp < brk) { printk("(%P)STACK OVERFLOW by %u\n", brk - sp); printk("CURBREAK %x, SP %x\n", brk, sp); @@ -25,7 +29,9 @@ void check_ustack(void) } if (sp < stacklow) { /* notification only, allow process to continue */ - printk("(%P)STACK USING %u UNUSED HEAP\n", stacklow - sp); + //printk("(%P)STACK USING %u UNUSED HEAP\n", stacklow - sp); + printk("(%P)STACK USING %u UNUSED HEAP (begstack: %x, enddata %x)\n", + stacklow - sp, current->t_begstack, current->t_enddata); } if (sp > current->t_begstack) { printk("(%P)STACK UNDERFLOW: SP %x BEGSTACK %x\n", sp, current->t_begstack); diff --git a/tlvc/arch/i86/kernel/syscall.dat b/tlvc/arch/i86/kernel/syscall.dat index 0ff00960..0714eede 100644 --- a/tlvc/arch/i86/kernel/syscall.dat +++ b/tlvc/arch/i86/kernel/syscall.dat @@ -16,6 +16,7 @@ # # Package versions are matched. # Elks version - 0.3.0 +# May 23 2026 hs: Added time(), creat() and more for V7 compat layer # # Name No Args Flag, comment # @@ -26,18 +27,20 @@ write +4 3 open +5 3 ! close +6 1 wait4 +7 4 -creat 8 0 - Not needed alias for open +creat +8 2 = CONFIG_COMPAT_V7 link +9 2 unlink +10 1 execve +11 3 * execve minix style chdir +12 1 -time 13 1 - Use settimeofday +time +13 1 = CONFIG_COMPAT_V7 mknod +14 3 chmod +15 2 chown +16 3 brk +17 1 * This is only to tell the system stat +18 2 lseek +19 3 * nb 2nd arg is an io ptr to long not a long. +# Except if V7 compat: the high wd of offset +# is encoded into the 3rd arg. getpid +20 1 * this gets both pid & ppid mount +21 4 umount +22 1 @@ -53,13 +56,15 @@ chroot +31 1 vfork +32 0 access +33 2 nice 34 1 -sleep 35 1 - use alarm & signal, or select, instead +#sleep 35 1 - use alarm & signal, or select, instead +ftime +35 2 = CONFIG_COMPAT_V7 sync +36 0 kill +37 2 rename +38 2 mkdir +39 2 rmdir +40 1 -dup +41 1 . There is a fcntl lib function too. +dup +41 2 . There is a fcntl lib function too. +# Venix uses dup for dup2 calls too, hence 2 parms pipe +42 1 = CONFIG_PIPE times 43 2 - use gettimeofday and libc times instead profil 44 4 @ diff --git a/tlvc/arch/i86/mm/malloc.c b/tlvc/arch/i86/mm/malloc.c index 851a59b1..59660858 100644 --- a/tlvc/arch/i86/mm/malloc.c +++ b/tlvc/arch/i86/mm/malloc.c @@ -71,9 +71,11 @@ static segment_s *seg_free_get(segext_t size0, word_t type) segment_s *seg, *best_seg = 0; segext_t best_size = 0xFFFF; list_s *n; - segext_t size00 = size0, incr = 0; + segext_t size00 = size0; -#ifdef ALLOW_TOPDWN_ALLOC +#ifndef ALLOW_TOPDWN_ALLOC + segext_t incr = 0; +#else if (type & SEG_FLAG_ALIGN1K) { /* allocate from the top, always 1kaligned */ n = _seg_all.prev; size00 = (size0 + 0x3f) & ~0x3f; /* keep the alignment */ @@ -100,7 +102,9 @@ static segment_s *seg_free_get(segext_t size0, word_t type) if (/*(seg->flags == SEG_FLAG_FREE) &&*/ (size1 >= size00) && (size1 < best_size)) { best_seg = seg; best_size = size1; +#ifndef ALLOW_TOPDWN_ALLOC incr = size00 - size0; +#endif if (size1 == size00) break; } n = seg->free.next; @@ -266,12 +270,14 @@ void mm_get_usage(struct mem_usage *mu) int sys_brk(segoff_t newbrk) { - /***unsigned int memfree, memused; - mm_get_usage(&memfree, &memused); - printk("brk(%P): new %x, edat %x, ebrk %x, free %x sp %x, eseg %x, %d/%dK\n", +#if 0 + struct mem_usage m; + mm_get_usage(&m); + printk("brk(%P): new %x, edat 0x%x, ebrk 0x%x, free 0x%x, sp 0x%x, minstk @0x%x, endseg 0x%x, %d/%dK\n", newbrk, current->t_enddata, current->t_endbrk, - current->t_regs.sp - current->t_endbrk, - current->t_regs.sp, current->t_endseg, memfree, memused);***/ + current->t_regs.sp - current->t_endbrk, current->t_regs.sp, + current->t_endseg - current->t_minstack, current->t_endseg, m.main_free, m.main_used); +#endif if (newbrk < current->t_enddata) return -ENOMEM; @@ -298,7 +304,7 @@ int sys_sbrk(int increment, segoff_t *pbrk) segoff_t brk = current->t_endbrk; /* always return start of old break*/ int err; - debug("sbrk incr %u pointer %04x curbreak %04x\n", increment, pbrk, brk); + debugmem("sbrk(%P) incr %u pointer %04x curbreak %04x\n", increment, pbrk, brk); err = verify_area(VERIFY_WRITE, pbrk, sizeof(*pbrk)); if (err) return err; diff --git a/tlvc/fs/exec.c b/tlvc/fs/exec.c index ca3afe96..f7407aa6 100644 --- a/tlvc/fs/exec.c +++ b/tlvc/fs/exec.c @@ -28,6 +28,8 @@ * for details. * * 30 Jun 2024 Greg Haerr. Added support for loading OS/2 v1.x binaries. + * + * 01 Jul 2026 Helge Skrivervik. Support for V7/Venix syscalls and binaries. */ #include @@ -42,9 +44,11 @@ #include #include //#include +#include #include #include #include +#include /* for V7 compat */ #include #pragma GCC diagnostic ignored "-Wunused-label" @@ -52,6 +56,8 @@ #define debug_reloc debug #define debug_reloc2 debug #define debug_os2 debug +#define debug_v7 debug //printk +//#define DEBUG_V7 static int FARPROC execve_aout(struct inode *inode, struct file *filp, char *sptr, size_t slen); @@ -64,6 +70,26 @@ static int FARPROC execve_os2(struct inode *inode, struct file *filp, static segment_s *mm_table[MAX_SEGS]; /* holds process segments until exec guaranteed */ #endif +#ifdef CONFIG_COMPAT_V7 +static int v7_task; +static int fs_strlen(char *bf) +{ + int i = 0; +#ifdef DEBUG_V7_EXTRA /* very noisy! */ + int c; + while ((c=get_user_char(bf++))) { + kputchar(c); + i++; + } + printk("\n"); +#else + while (get_user_char(bf++)) i++; +#endif + return i; +} + +#endif + int sys_execve(const char *filename, char *sptr, size_t slen) { int retval; @@ -72,6 +98,57 @@ int sys_execve(const char *filename, char *sptr, size_t slen) struct inode *inode; word_t magic; +#ifdef CONFIG_COMPAT_V7 + char *stk_ptr; + + v7_task = current->task_is_V7; /* make sure we always know where we're coming from + * even if some 2nd level syscall clears the flag */ + if (v7_task) { /* The following is essentially a copy of the + * code found in the exec library file */ + int stack_bytes, rv; + char *pcp, *baseoff; + int argv_len=0, argv_count=0; + int envp_len=0, envp_count=0; + unsigned short *pip; + char **p; + char *cc; + + + for (p=(char **)sptr; p && (cc = (char *)get_user(p)) && argv_len >= 0; p++) { + int l = fs_strlen(cc)+1; + argv_count++; argv_len += l; + } + for (p=(char **)slen; p && (cc = (char *)get_user(p)) && argv_len >= 0; p++) { + envp_count++; envp_len += fs_strlen(cc)+1; + } + stack_bytes = 2 + (argv_count<<1) + 2 + argv_len + + (envp_count<<1) + 2 + envp_len; + stk_ptr = heap_alloc(stack_bytes, HEAP_TAG_EXSTK); + if (!stk_ptr) return -ENOMEM; + + pip = (unsigned short *)stk_ptr; + pcp = stk_ptr + 2*(1+argv_count+1+envp_count+1); + baseoff = stk_ptr; + *pip++ = argv_count; /* argc */ + for (p=(char **)sptr; p && (cc = (char *)get_user(p)); p++) { + *pip++ = pcp-baseoff; + rv = fs_strlen(cc)+1; + verified_memcpy_fromfs(pcp, cc, rv); + pcp += rv; + } + *pip++ = 0; /* end of argv */ + for (p=(char **)slen; p && (cc = (char *)get_user(p)); p++) { + *pip++ = pcp-baseoff; + rv = fs_strlen(cc)+1; + verified_memcpy_fromfs(pcp, cc, rv); + pcp += rv; + } + *pip++ = 0; /* end of envp */ + sptr = stk_ptr; + slen = stack_bytes; + } +#endif + /* Open the image */ debug_cache("\nEXEC(%P): '%t' env %d ", filename, slen); @@ -84,6 +161,9 @@ int sys_execve(const char *filename, char *sptr, size_t slen) if (!(filp->f_op) || !(filp->f_op->read)) goto error_exec2_5; /* Read the header */ +#ifdef DEBUG_V7 + printk("exec[%P]: V7 task-flag: %x, file %t\n", current->task_is_V7, filename); +#endif ds = current->t_regs.ds; current->t_regs.ds = kernel_ds; retval = filp->f_op->read(inode, filp, (char *)&magic, sizeof(magic)); @@ -95,7 +175,11 @@ int sys_execve(const char *filename, char *sptr, size_t slen) retval = execve_os2(inode, filp, sptr, slen); else #endif +#ifdef CONFIG_COMPAT_V7 + if (magic == AOUTMAGIC || magic == OMAGIC || magic == NMAGIC) +#else if (magic == AOUTMAGIC) +#endif retval = execve_aout(inode, filp, sptr, slen); else retval = -ENOEXEC; goto normal_out; @@ -105,6 +189,9 @@ int sys_execve(const char *filename, char *sptr, size_t slen) retval = -ENOEXEC; normal_out: close_filp(inode, filp); +#ifdef CONFIG_COMPAT_V7 + if (v7_task) heap_free(stk_ptr); +#endif if (retval) error_exec2: @@ -195,16 +282,20 @@ static int FARPROC execve_aout(struct inode *inode, struct file *filp, int retval; seg_t ds = current->t_regs.ds; seg_t base_data = 0; - segment_s * seg_code; - segment_s * seg_data; + segment_s *seg_code; + segment_s *seg_data; size_t len, min_len, heap, stack = 0; size_t bytes; segext_t paras; - ASYNCIO_REENTRANT struct minix_exec_hdr mh; /* 32 bytes */ + ASYNCIO_REENTRANT volatile struct minix_exec_hdr mh; /* 32 bytes */ #ifdef CONFIG_EXEC_MMODEL ASYNCIO_REENTRANT struct elks_supl_hdr esuph; /* 24 bytes */ int need_reloc_code = 1; #endif +#ifdef CONFIG_COMPAT_V7 + struct v7_exec *v7hdr = (struct v7_exec *)&mh; + unsigned magic; +#endif /* (Re)read the header */ current->t_regs.ds = kernel_ds; @@ -214,6 +305,30 @@ static int FARPROC execve_aout(struct inode *inode, struct file *filp, /* Sanity check it */ if (retval != sizeof(mh)) goto error_exec3; +#ifdef CONFIG_COMPAT_V7 + magic = v7hdr->a_magic; + if (magic == NMAGIC || magic == OMAGIC) { + /* rearrange the V7 exec header into struct minix mh */ + mh.minstack = v7hdr->a_stack; + mh.chmem = (size_t)v7hdr->a_heap; + mh.syms = v7hdr->a_syms; + mh.bseg = v7hdr->a_bss; /* may use memmove here */ + mh.dseg = v7hdr->a_data; + mh.tseg = v7hdr->a_text; + /* mh.entry matches between the two structs */ + v7hdr->a_stack = 0; + mh.hlen = 32; + mh.version = 3; /* simplify logic below */ + if (!mh.minstack) /* a_stack = 0 means default stack size, located */ + mh.minstack = 0x2000; /* above the heap, stretching to end of segment. */ +#ifdef DEBUG_V7_EXTRA + unsigned long *x = &mh.type; + for (int i = 0; i < 8; i++) + printk(":0x%08lx:\n", *(x+i)); +#endif + + } else +#endif if ((mh.type != MINIX_SPLITID_AHISTORICAL && mh.type != MINIX_SPLITID) || (size_t)mh.tseg == 0) { debug("EXEC: bad header, result %d\n", retval); @@ -221,6 +336,7 @@ static int FARPROC execve_aout(struct inode *inode, struct file *filp, } /* Look for the binary in memory */ + /* FIXME: This will match TINY Venix programs too, havoc will ensue */ seg_code = 0; currentp = &task[0]; do { @@ -280,10 +396,42 @@ static int FARPROC execve_aout(struct inode *inode, struct file *filp, /* * mh.version == 1: chmem is size of heap, 0 means use default heap * mh.version == 0: old ld86 used chmem as size of data+bss+heap+stack + * mh.version == 3: V7/Venix binaries, stack usually starts at zero. */ switch (mh.version) { default: goto error_exec3; + +#ifdef CONFIG_COMPAT_V7 + /* Memory layout on Venix/V7: + * TINY model 1: <8k stack> + * TINY model 2: occupying a full segment + * on Venix. Heap and stack mey be changed using chmem. + * SMALL model data seg: <8k stack> + * (the latter may not be universally true, but we'll stick with + * it for now). + * NOTE: On Venix/86 the default is to allocate a full 64k segment - data + * segment if small, code segment if tiny - to maximize heap size. + * We keep this as the default but let chmem enter modified values into + * the a.out header, where the unused a_drsize is now a_heap. + * + * Also, Venix/86 binaries expect segment sizes to be exact, no rounding or overflow, + * otherwise memory references become skewed and crashes ensue. + */ + case 3: /* Venix binary */ + stack = mh.minstack; + if (!mh.chmem) + len = 0xffff; + else + len = mh.dseg + mh.bseg + mh.chmem + stack; +#if 0 + len = mh.dseg + mh.bseg + (mh.chmem ? mh.chmem : INIT_HEAP); + stack = mh.minstack; + if (magic == NMAGIC) len += stack; /* allocation size for DSEG */ +#endif + goto v7_continue; +#endif + case 1: len = min_len; { @@ -308,6 +456,7 @@ static int FARPROC execve_aout(struct inode *inode, struct file *filp, } } debug("EXEC: stack %u heap %u env %u total %u\n", stack, heap, slen, len); + debug_v7("EXEC: stack %u heap %u env %u total %u\n", stack, heap, slen, len); break; case 0: len = mh.chmem; @@ -347,10 +496,11 @@ static int FARPROC execve_aout(struct inode *inode, struct file *filp, } len &= ~(size_t)15; +v7_continue: debug("EXEC: Malloc time\n"); /* - * Looks good. Get the memory we need + * Looks good. Allocate memory */ if (!seg_code) { @@ -381,16 +531,31 @@ static int FARPROC execve_aout(struct inode *inode, struct file *filp, #ifdef CONFIG_EXEC_MMODEL paras += bytes_to_paras((size_t)esuph.esh_ftseg); #endif - debug_reloc("EXEC: allocating %04x paras (%04x bytes) for text segment(s)\n", + debug_reloc("EXEC: allocating %04x paras (%d bytes) for text segment(s)\n", paras, bytes); +#ifdef CONFIG_COMPAT_V7 + if (magic == OMAGIC) { /* tiny model alloction - merge everything */ + //len += bytes + stack; + if (len != 0xffff) len += bytes; + paras = bytes_to_paras(len); + seg_code = seg_alloc(paras, SEG_FLAG_VSEG); + debug_v7("EXEC: allocating %04x paras (%d bytes) for TINY segment @ %x:%x\n", + paras, len, seg_code->base, (unsigned)mh.entry); + } else +#endif + { seg_code = seg_alloc(paras, SEG_FLAG_CSEG); + debug_v7("EXEC: allocating %04x paras (%d bytes) for text segment @ %x:%x\n", + paras, bytes, seg_code->base, (unsigned int)mh.entry); + } if (!seg_code) goto error_exec3; currentp->t_regs.ds = seg_code->base; - retval = filp->f_op->read(inode, filp, 0, bytes); + retval = filp->f_op->read(inode, filp, (char *)((unsigned int)mh.entry), bytes); if (retval != bytes) { debug("EXEC(tseg read): bad result %u, expected %u\n", retval, bytes); goto error_exec4; } + #ifdef CONFIG_EXEC_COMPRESS retval = -ENOEXEC; if (esuph.esh_compr_tseg && @@ -420,7 +585,7 @@ static int FARPROC execve_aout(struct inode *inode, struct file *filp, } #endif } else { - seg_get (seg_code); + seg_get(seg_code); code_seg_found_exec: #ifdef CONFIG_EXEC_MMODEL @@ -431,12 +596,20 @@ static int FARPROC execve_aout(struct inode *inode, struct file *filp, filp->f_pos += (size_t)mh.tseg; #endif } - paras = len >> 4; retval = -ENOMEM; - debug_reloc("EXEC: allocating %04x paras (%04x bytes) for data segment\n", paras, len); - seg_data = seg_alloc (paras, SEG_FLAG_DSEG); + debug_reloc("EXEC: allocating %04x paras (%d bytes) for data segment\n", paras, len); +#ifdef CONFIG_COMPAT_V7 + if (magic == OMAGIC) + seg_data = seg_code; + else +#endif + { + seg_data = seg_alloc(paras, SEG_FLAG_DSEG); if (!seg_data) goto error_exec4; + } + debug_v7("EXEC: allocating %04x paras (%d bytes) for data segment @ %x:0\n", paras, len, seg_data->base); + debug("EXEC: Malloc succeeded - cs=%x ds=%x\n", seg_code->base, seg_data->base); bytes = (size_t)mh.dseg; @@ -447,7 +620,15 @@ static int FARPROC execve_aout(struct inode *inode, struct file *filp, //paras += 1; /* add 16 bytes for safety offset */ } #endif - currentp->t_regs.ds = seg_data->base; + currentp->t_regs.ds = seg_data->base; /* OK even for OMAGIC */ +#ifdef CONFIG_COMPAT_V7 + if (magic == NMAGIC) base_data = mh.minstack; /* data above stack (MAY NEED TO FIX) */ + else if (magic == OMAGIC) { + base_data = (size_t)mh.tseg; + if ((size_t)mh.entry) + base_data += mh.minstack; /* stack below text */ + } +#endif retval = filp->f_op->read(inode, filp, (char *)base_data, bytes); if (retval != bytes) { debug("EXEC(dseg read): bad result %d, expected %u\n", retval, bytes); @@ -492,9 +673,21 @@ static int FARPROC execve_aout(struct inode *inode, struct file *filp, /* set data/stack limits and copy argc/argv */ currentp->t_enddata = (size_t)mh.dseg + (size_t)mh.bseg + base_data; - currentp->t_endseg = len; + currentp->t_endseg = len; /* top end of allocated data segment */ + currentp->t_regs.dx = currentp->t_minstack = stack; +#ifdef CONFIG_COMPAT_V7 + if (magic == NMAGIC || (magic == OMAGIC && (word_t)mh.entry)) + current->t_begstack = mh.minstack - 2 - slen; /* stack below text */ + else +#endif currentp->t_begstack = (currentp->t_endseg - slen) & ~1; /* force even SP and argv */ + +#ifdef CONFIG_COMPAT_V7 + if (v7_task) + fmemcpyb((char *)currentp->t_begstack, seg_data->base, sptr, kernel_ds, slen); + else +#endif fmemcpyb((char *)currentp->t_begstack, seg_data->base, sptr, ds, slen); finalize_exec(inode, seg_code, seg_data, (word_t)mh.entry, 0); @@ -592,6 +785,18 @@ static void FARPROC finalize_exec(struct inode *inode, segment_s *seg_code, * user stack and to CS:entry of the user process. */ arch_setup_user_stack(currentp, entry, seg_code->base); +#ifdef DEBUG_V7 + if (currentp->t_begstack < currentp->t_endbrk) { + printk("V7: ready to exec, sp: %x ds %x enddata %x\n", currentp->t_regs.sp, + currentp->t_regs.ds, currentp->t_enddata); + //for (int q=0; q<7; q++) pokeb(entry+q, seg_code->base, 0xa0); /* NOP replacing FPU trap */ + printk("\n code:"); + for (int q=0; q<16; q++) printk("%02x ", peekb(entry+q, seg_code->base)); + printk("\n stack:"); + for (int q=0; q<16; q++) printk("%02x ", peekb(currentp->t_regs.sp+q, seg_data->base)); + printk("\n"); + } +#endif } #ifdef CONFIG_EXEC_OS2 diff --git a/tlvc/fs/fcntl.c b/tlvc/fs/fcntl.c index c4c1e020..b9817dd8 100644 --- a/tlvc/fs/fcntl.c +++ b/tlvc/fs/fcntl.c @@ -39,16 +39,28 @@ int sys_dup2(unsigned int oldfd, unsigned int newfd) /* following POSIX.1 6.2.1, if newfd >= NR_OPEN, return -EBADF */ if (newfd < NR_OPEN) { sys_close(newfd); + printk("DUP[%P]: old %d, new %d\n", oldfd, newfd); return dupfd(oldfd, newfd); } } return -EBADF; } +#ifdef CONFIG_COMPAT_V7 +/* V7/Venix doesn't use/have dup2. Instead the 0100 bit of fildes is set to indicate dup2 */ +int sys_dup(unsigned int fildes, unsigned int newfd) +{ + //if (current->task_is_V7) printk("DUP[%P]: old %x, new %x\n", fildes, newfd); + if (current->task_is_V7 && (fildes&0100)) return(sys_dup2(fildes&~0100, newfd)); + //{ int r = sys_dup2(fildes&~0x40, newfd); printk("got r=%d for old0%d, f=%d\n", r, fildes&~0x40, newfd); return r;} + return dupfd(fildes, 0); +} +#else int sys_dup(unsigned int fildes) { return dupfd(fildes, 0); } +#endif int sys_fcntl(unsigned int fd, unsigned int cmd, unsigned int arg) { diff --git a/tlvc/fs/ioctl.c b/tlvc/fs/ioctl.c index f96bb060..c493812f 100644 --- a/tlvc/fs/ioctl.c +++ b/tlvc/fs/ioctl.c @@ -42,6 +42,7 @@ int sys_ioctl(int fd, unsigned int cmd, unsigned int arg) if (fd >= NR_OPEN || !(filp = current->files.fd[fd])) return -EBADF; + switch (cmd) { case FIOCLEX: FD_SET(fd, ¤t->files.close_on_exec); diff --git a/tlvc/fs/open.c b/tlvc/fs/open.c index 75369ed8..2fad783e 100644 --- a/tlvc/fs/open.c +++ b/tlvc/fs/open.c @@ -368,7 +368,10 @@ int sys_open(const char *filename, int flags, int mode) if ((mode_t)((flags + 1) & O_ACCMODE)) flag++; if (flag & (O_TRUNC | O_CREAT)) flag |= FMODE_WRITE; - debug_file("OPEN '%t' flags 0x%x\n", filename, flags); +#ifdef CONFIG_COMPAT_V7 + debug_file("V7 compat mode is %s\n", current->task_is_V7?"on":"off"); +#endif + debug_file("OPEN[%P] '%t' flags 0x%x\n", filename, flags); error = open_namei(filename, flag, mode, &inode, NULL); if (!error) { if ((error = open_fd(flags, inode)) < 0) @@ -378,6 +381,13 @@ int sys_open(const char *filename, int flags, int mode) return error; } +#ifdef CONFIG_COMPAT_V7 +int sys_creat(const char *filename, int mode) +{ + return(sys_open(filename, O_CREAT|O_TRUNC|O_WRONLY, mode)); +} +#endif + static void close_fp(register struct file *filp) { register struct inode *inode; @@ -414,7 +424,7 @@ int sys_close(unsigned int fd) register struct file *filp; register struct file_struct *cfiles = ¤t->files; - debug_file("CLOSE %d\n", fd); + debug_file("CLOSE[%P] %d\n", fd); if (fd < NR_OPEN) { clear_bit(fd, &cfiles->close_on_exec); if ((filp = cfiles->fd[fd])) { diff --git a/tlvc/fs/pipe.c b/tlvc/fs/pipe.c index 7d2b3a94..429c1db1 100644 --- a/tlvc/fs/pipe.c +++ b/tlvc/fs/pipe.c @@ -333,7 +333,7 @@ static int do_pipe(register int *fd) return 0; } -int sys_pipe(unsigned int *filedes) +long sys_pipe(unsigned int *filedes) { int fd[2]; int error; @@ -341,6 +341,13 @@ int sys_pipe(unsigned int *filedes) debug("PIPE: called.\n"); if ((error = do_pipe(fd))) return error; + //printk("PIPE[%P]: return %d/%d(%08lx)\n", fd[0], fd[1], *(long *)fd); +#ifdef CONFIG_COMPAT_V7 + if (current->task_is_V7) { + verified_memcpy_tofs(filedes, fd, 2 * sizeof(int)); + return *(long *)fd; + } +#endif debug("PIPE: Returned %d %d.\n", fd[0], fd[1]); diff --git a/tlvc/fs/read_write.c b/tlvc/fs/read_write.c index f61e59d7..98a8f2c7 100644 --- a/tlvc/fs/read_write.c +++ b/tlvc/fs/read_write.c @@ -15,25 +15,57 @@ #include #include -int sys_lseek(unsigned int fd, loff_t * p_offset, unsigned int origin) +/* V7 compat note: + * The high workd of the long offset parameter coming from Venix apps + * is encoded into the whence (lorigin) arg. The high bit flags this, + * introduced before the V7 shim layer was created and kept for compatibility + * with the syscall conversion option in the vrewrite.py tool. + * Also, the V7 call expects a long return: + * library syscall stub accordingly. +*/ + +loff_t sys_lseek(unsigned int fd, loff_t *p_offset, unsigned int lorigin) { register struct file *file; register struct file_operations *fop; loff_t offset; + //unsigned int origin = lorigin; + +#ifdef CONFIG_COMPAT_V7 + int v7 = current->task_is_V7; + if (v7 || (fd&0x8000)) { + /* The fd high bit flag is used by the code conversion + * tool when converting to TLVC native syscalls. + * Considered temporary */ + offset = (((loff_t)lorigin)<<16) | (unsigned int)p_offset; + lorigin = (fd>>8)&0x3; + fd &= 0xff; + printk("lseek: fd %d, offs %x, lorigin %x real offset %lx (%lu) (V7:%x)", + fd, p_offset, lorigin, offset, offset, v7); + } else +#endif + offset = (loff_t) get_user_long(p_offset); - offset = (loff_t) get_user_long(p_offset); if (fd >= NR_OPEN || !(file = current->files.fd[fd]) || !(file->f_inode)) return -EBADF; - if (origin > 2) return -EINVAL; + if (lorigin > 2) return -EINVAL; fop = file->f_op; - if (fop && fop->lseek) - return fop->lseek(file->f_inode, file, offset, origin); - + if (fop && fop->lseek) { +#ifdef CONFIG_COMPAT_V7 + int ret = fop->lseek(file->f_inode, file, offset, lorigin); + if (ret < 0) return ret; + offset = file->f_pos; + goto lseek_done; +#else + return fop->lseek(file->f_inode, file, offset, lorigin); /* really a bug: the new pos is not returned */ + +#endif + } /* this is the default handler if no lseek handler is present */ /* Note: We already determined above that origin is in range. */ - if (origin == 1) /* SEEK_CUR */ + if (lorigin == 1) /* SEEK_CUR */ offset += file->f_pos; - else if (origin) /* SEEK_END */ + else if (lorigin) /* SEEK_END */ offset += file->f_inode->i_size; if (offset < 0) return -EINVAL; @@ -46,13 +78,16 @@ int sys_lseek(unsigned int fd, loff_t * p_offset, unsigned int origin) #endif file->f_pos = offset; +#ifdef CONFIG_COMPAT_V7 +lseek_done: + if (v7) { printk(" return offset %lu\n", offset);return offset; } +#endif put_user_long((unsigned long int)offset, (void *)p_offset); - return 0; } /* fd_check -- validate file descriptor - * Failure is indicated by a returning a non-zero value. Success is + * Failure is indicated by returning a non-zero value. Success is * indicated by returning 0. The parameter "file" is used for passing * back the pointer to the file struct associated with fd. The value of * "file" is undefined when this function returns unsuccessfully. @@ -87,6 +122,9 @@ int sys_read(unsigned int fd, char *buf, size_t count) struct file *file; int retval; +#ifdef CONFIG_COMPAT_V7 /* may not be required FIXME */ + int v7 = current->task_is_V7; +#endif if (((retval = fd_check(fd, buf, count, FMODE_READ, &file)) == 0) && count) { retval = -EINVAL; @@ -96,6 +134,11 @@ int sys_read(unsigned int fd, char *buf, size_t count) schedule(); } } +#ifdef CONFIG_COMPAT_V7 + if (v7) { /* FIXME; may not be needed */ + current->task_is_V7 = v7; + } +#endif return retval; } @@ -138,5 +181,6 @@ int sys_write(unsigned int fd, char *buf, size_t count) schedule(); // FIX: Check if this can be removed } } + //if (current->task_is_V7) printk("wr: fd %d count %d ret %d\n", fd, count, written); return written; } diff --git a/tlvc/fs/stat.c b/tlvc/fs/stat.c index c0df067b..9167fb3e 100644 --- a/tlvc/fs/stat.c +++ b/tlvc/fs/stat.c @@ -75,15 +75,29 @@ static int cp_stat(register struct inode *inode, struct stat *statbuf) } } #endif - +#ifdef CONFIG_COMPAT_V7 + /* compensate for short ino_t in V7/Venix stat and different file type mode flags */ + if (current->task_is_V7) { + if ((tmp.st_mode&S_IFMT) > 0100000 || (tmp.st_mode&S_IFMT) == 010000) return -EBADF; + if (tmp.st_mode&S_IFMT) tmp.st_mode |= 0100000; // covers the regular file case too. + verified_memcpy_tofs((char *)statbuf, (char *)&tmp, 4); + return verified_memcpy_tofs((char *)statbuf+4, (char *)&tmp+6, sizeof(tmp)-6); + } else +#endif return verified_memcpy_tofs((char *) statbuf, (char *) &tmp, sizeof(tmp)); } int sys_stat(char *filename, struct stat *statbuf) { +#ifdef CONFIG_COMPAT_V7 + int is_V7 = current->task_is_V7; +#endif struct inode *inode; int error = namei(filename, &inode, 0, 0); +#ifdef CONFIG_COMPAT_V7 /* namei() may clear the V7 flag */ + current->task_is_V7 = is_V7; +#endif if (!error) { error = cp_stat(inode, statbuf); iput(inode); @@ -124,6 +138,12 @@ int sys_readlink(char *path, char *buf, size_t bufsiz) register struct inode_operations *iop; int error = -EINVAL; +#ifdef CONFIG_COMPAT_V7 + /* This is syscall 59, which is exece/execve() on V7/Venix. */ + if (current->task_is_V7) { + return(sys_execve(path, buf, bufsiz)); + } +#endif if ((bufsiz > 0) && !(error = verify_area(VERIFY_WRITE, buf, bufsiz)) && !(error = lnamei(path, &inode)) diff --git a/tlvc/include/arch/irq.h b/tlvc/include/arch/irq.h index 87cb236a..f898f49b 100644 --- a/tlvc/include/arch/irq.h +++ b/tlvc/include/arch/irq.h @@ -3,11 +3,21 @@ #ifdef __KERNEL__ /* irq numbers >= 16 are hardware exceptions/traps or syscall */ -#define IDX_SYSCALL 16 +#define IDX_SYSCALL 16 /* regular syscalls */ #define IDX_DIVZERO 17 -#define IDX_NMI 18 +#define IDX_NMI 18 #define IDX_NECV25_IBRK 19 /* NEC V25 specific IO Break Exception */ -#define NR_IRQS 20 /* = # IRQs plus special indexes above */ + +#ifdef CONFIG_COMPAT_V7 +#define IDX_SYSCALL_V7 20 /* Unix V7 syscalls - int 0xf1 */ +#define IDX_SYSFPU_V7 21 /* Venix FPU/math init, just returns, int 0xf4 */ +#define IDX_SYSIOT_V7 22 /* Venix abort() library call - int 0xf3 */ +#define IDX_SYSMAP_V7 23 /* Venix code mapper - int 0xf5 */ +#define IDX_STKTRAP_V7 24 /* Venix application stack trap, int 0xf2 */ +#define NR_IRQS 25 +#else +#define NR_IRQS 20 /* = # IRQs plus special indexes above */ +#endif #define INT_GENERIC 0 // use the generic interrupt handler (aka '_irqit') #define INT_SPECIFIC 1 // use a specific interrupt handler @@ -25,9 +35,17 @@ int free_irq(int irq); /* irqtab.S */ void _irqit (void); -void int_vector_set (int vect, word_t proc, word_t seg); +void int_vector_set(int vect, word_t proc, word_t seg); void idle_halt(void); +#ifdef CONFIG_COMPAT_V7 +void _irqit_v7(void); +void _sysfpu_v7(void); +void _abort_v7(void); +void _stktrap_v7(void); +void _sysmap_v7(void); +#endif + void div0_handler(int irq, struct pt_regs *regs); void nmi_handler(int irq, struct pt_regs *regs); void ibrk_handler(int irq, struct pt_regs *regs); /* NEC V25 specific IO Break handler */ diff --git a/tlvc/include/linuxmt/config.h b/tlvc/include/linuxmt/config.h index 171620fa..d98b7946 100644 --- a/tlvc/include/linuxmt/config.h +++ b/tlvc/include/linuxmt/config.h @@ -8,6 +8,11 @@ * Compile-time configuration */ +#define CONFIG_COMPAT_V7 /* Enable code to run converted Venix binaries */ +#ifdef CONFIG_COMPAT_V7 +#define CONFIG_EXEC_LOW_STACK /* stack below data is OK */ +#endif + #define CONFIG_OPTSEG_HIGH /* Load /bootopts (of any size) into high memory */ /* Not currently working on FAT boot devices */ #ifdef CONFIG_ARCH_IBMPC diff --git a/tlvc/include/linuxmt/debug.h b/tlvc/include/linuxmt/debug.h index 0f2c8c9c..8b83b9c3 100644 --- a/tlvc/include/linuxmt/debug.h +++ b/tlvc/include/linuxmt/debug.h @@ -26,7 +26,7 @@ #define DEBUG_INODE 0 /* track inode handling */ #define DEBUG_NET 0 /* networking*/ #define DEBUG_MAP 0 /* L1 mapping */ -#define DEBUG_MM 0 /* mem char device*/ +#define DEBUG_MM 0 /* mem char device, malloc, brk */ #define DEBUG_SCHED 0 /* scheduler/wait*/ #define DEBUG_SIG 0 /* signals*/ #define DEBUG_SUP 0 /* superblock, mount, umount*/ diff --git a/tlvc/include/linuxmt/fs.h b/tlvc/include/linuxmt/fs.h index a0bd7032..e4b3d4a4 100644 --- a/tlvc/include/linuxmt/fs.h +++ b/tlvc/include/linuxmt/fs.h @@ -507,7 +507,7 @@ extern int check_disk_change(kdev_t); extern int _namei(const char *,struct inode *,int,struct inode **); -extern int sys_dup(unsigned int); +//extern int sys_dup(unsigned int, unsigned int); extern struct buffer_head *bread(dev_t,block_t); extern struct buffer_head *bread32(dev_t,block32_t); diff --git a/tlvc/include/linuxmt/heap.h b/tlvc/include/linuxmt/heap.h index 7f425b63..50348b52 100644 --- a/tlvc/include/linuxmt/heap.h +++ b/tlvc/include/linuxmt/heap.h @@ -28,6 +28,7 @@ #define HEAP_TAG_CACHE 0x09 #define HEAP_TAG_NETWORK 0x0A /* packet buffer allocations */ #define HEAP_TAG_OPTSEG 0x0B +#define HEAP_TAG_EXSTK 0x0C /* V7 execve stack buffer */ // TODO: move free list node from header to body diff --git a/tlvc/include/linuxmt/mm.h b/tlvc/include/linuxmt/mm.h index 4ead804c..d2ab864c 100644 --- a/tlvc/include/linuxmt/mm.h +++ b/tlvc/include/linuxmt/mm.h @@ -29,6 +29,7 @@ typedef struct segment segment_s; #define SEG_FLAG_RAMDSK 0x06 /* ram disk buffers */ #define SEG_FLAG_BUFHEAD 0x07 /* bufheads for ext/xms buffers */ #define SEG_FLAG_NETBUF 0x08 /* ring buffers for LANCE NICs */ +#define SEG_FLAG_VSEG 0x09 /* tiny model test+data seg (V7) */ #ifdef __KERNEL__ #include diff --git a/tlvc/include/linuxmt/ntty.h b/tlvc/include/linuxmt/ntty.h index cb88d59a..a7c6b5cb 100644 --- a/tlvc/include/linuxmt/ntty.h +++ b/tlvc/include/linuxmt/ntty.h @@ -12,7 +12,7 @@ #define PTYOUTQ_SIZE 512 /* pty output queue size (=TDB_WRITE_MAX and telnetd buffer) */ /* For speed, the serial buffers must be sized in power of two steps */ -#define RSINQ_SIZE 1024 /* serial input queue, covers SLIP_MTU+128+8 */ +#define RSINQ_SIZE 512 /* serial input queue, increase to 1k if using SLIP w/o flow contr */ #define RSOUTQ_SIZE 128 /* serial output queue size */ /* @@ -87,6 +87,26 @@ struct tty { /* NOTE: first member used in fastser.S driver */ struct termios termios; }; +struct sgttyb { /* V7/early BSD compatibility */ + char sg_ispeed; /* input speed */ + char sg_ospeed; /* output speed */ + char sg_erase; /* erase character */ + char sg_kill; /* kill character */ + int sg_flags; /* mode flags */ +}; + /* V7 mode flags */ +#define O_CBREAK 0002 +#define O_LCASE 0004 +#define O_ECHO 0010 +#define O_CRMOD 0020 +#define O_RAW 0040 +#define O_ODDP 0100 +#define O_EVENP 0200 +#define O_ANYP 0300 +#define O_XTABS 06000 +#define O_CRT 0100000 + + extern struct tty ttys[]; extern int tty_intcheck(struct tty *,unsigned char); diff --git a/tlvc/include/linuxmt/sched.h b/tlvc/include/linuxmt/sched.h index f3760b2f..15382aff 100644 --- a/tlvc/include/linuxmt/sched.h +++ b/tlvc/include/linuxmt/sched.h @@ -71,7 +71,7 @@ struct task_struct { segoff_t t_endbrk; /* current break (end of heap) */ segoff_t t_begstack; /* start SP, argc/argv strings above */ segoff_t t_endseg; /* end of dataseg (data+bss+heap+stack) */ - segoff_t t_minstack; /* min stack size */ + segoff_t t_minstack; /* min (allocated) stack size */ /* Other */ unsigned long average; /* fixed point CPU % usage */ @@ -85,6 +85,7 @@ struct task_struct { * changing struct task size and having to recompile 'ps' etc when changed */ int kstack_max; int kstack_prevmax; +#define task_is_V7 kstack_max /*(ab)use kstack_max to flag Venix/V7 tasks */ unsigned int kstack_magic; /* To detect stack corruption */ __u16 t_kstack[KSTACK_BYTES/2]; diff --git a/tlvc/include/linuxmt/termios.h b/tlvc/include/linuxmt/termios.h index fb279d55..795fef65 100644 --- a/tlvc/include/linuxmt/termios.h +++ b/tlvc/include/linuxmt/termios.h @@ -7,6 +7,7 @@ /* This is just a magic number to make these relatively unique ('T') */ #define __TERMIOS_MAJ ('T'<<8) +#define __V7SGTTY_MAJ ('t'<<8) /*@+namechecks@*/ @@ -66,6 +67,12 @@ #define TIOCGICOUNT (__TERMIOS_MAJ+0x5D) /* read serial port inline interrupt counts */ #define TIOSETCONSOLE (__TERMIOS_MAJ+0x5E) /* set console dev_t*/ +#define TIOCGETP (__V7SGTTY_MAJ+0x8) /* For unix v7/early BSD emul */ +#define TIOCSETP (__V7SGTTY_MAJ+0x9) +#define TCGETS_V7 TIOCGETP +#define TCSETS_V7 TIOCSETP + + /* Used for packet mode */ #define TIOCPKT_DATA 0 #define TIOCPKT_FLUSHREAD 1 diff --git a/tlvc/include/linuxmt/timeb.h b/tlvc/include/linuxmt/timeb.h new file mode 100644 index 00000000..4b405149 --- /dev/null +++ b/tlvc/include/linuxmt/timeb.h @@ -0,0 +1,11 @@ +/* + * Structure returned by the V7 ftime system call + * Used by Venix binaries only + */ + +struct timeb { + long time; + unsigned int millitm; + int timezone; + int dstflag; +}; diff --git a/tlvc/include/linuxmt/v7.h b/tlvc/include/linuxmt/v7.h new file mode 100644 index 00000000..192ab333 --- /dev/null +++ b/tlvc/include/linuxmt/v7.h @@ -0,0 +1,23 @@ +/* + * v7.h - constants and more related to running V7 (Venix) binaries + * on TLVC. + * Helge Skrivervik, 2026 + */ + +#define OMAGIC 0x107 /* tiny model binary */ +#define NMAGIC 0x109 /* small model, stack usually @ bottom of DS */ + /* may include code mapping for large text */ + +struct v7_exec { +short a_magic; /* magic number */ +unsigned short a_stack; /* size of stack if Z type, 0 otherwise */ +long a_text; /* size of text segment */ +long a_data; /* size of initialized data */ +long a_bss; /* size of uninitialized data */ +long a_syms; /* size of symbol table */ +long a_entry; /* entry point */ +long a_trsize; /* size of text relocation */ +long a_heap; /* heap size unless default (max) */ + /* was a_drsize, size of data relocation */ +}; + diff --git a/tlvc/kernel/exit.c b/tlvc/kernel/exit.c index 5a1a03e8..f0c5cd9e 100644 --- a/tlvc/kernel/exit.c +++ b/tlvc/kernel/exit.c @@ -42,11 +42,37 @@ static void FARPROC reparent_children(void) } /* note: 'usage' parameter ignored */ +#ifdef CONFIG_COMPAT_V7 +long sys_wait4(pid_t i_pid, int *i_status, int i_options, void *usage) +#else int sys_wait4(pid_t pid, int *status, int options, void *usage) +#endif { register struct task_struct *p; int waitagain; +#ifdef CONFIG_COMPAT_V7 + pid_t pid = i_pid; + int *status = i_status; + int options = i_options; + + /* V7 wait() has *status as the only API level arg (classic wait()). + * *status is updated by the libc front end. IOW, wait is called with NO + * arguments - attempts to access them may screw up the stack! */ + /* On return, Venix expects child pid in AX, std syscall error handling, + * status in DX on return - thus the long. DX upper byte is exit status, + * DX low byte is the signal - if any. */ + + int retval[2] = { -1, -1 }; + int v7 = current->task_is_V7; + + if (v7) { + pid = -1; + options = 0; + status = 0; + printk("WAIT(%P)V7 for %d opts %x\n", pid, options); + } +#endif debug_wait("WAIT(%P) for %d opts %x\n", pid, options); for (;;) { @@ -61,9 +87,14 @@ int sys_wait4(pid_t pid, int *status, int options, void *usage) continue; } +#ifdef CONFIG_COMPAT_V7 + if (v7) + retval[1] = p->signal + p->exit_status; /* status already ih hi byte */ + else +#endif if (status) { - if (verified_memcpy_tofs(status, &p->exit_status, sizeof(int))) - return -EFAULT; + if (verified_memcpy_tofs(i_status, &p->exit_status, sizeof(int))) + return -EFAULT; } /* just return status on stopped state, don't release task*/ @@ -76,6 +107,13 @@ int sys_wait4(pid_t pid, int *status, int options, void *usage) } debug_wait("WAIT(%P) got %d\n", p->pid); +#ifdef CONFIG_COMPAT_V7 + printk("WAIT(%P) got %d status %x V7 %d\n", p->pid, retval[1], v7); + if (v7) { + retval[0] = p->pid; + return *(long *)retval; + } else +#endif return p->pid; } } else { @@ -95,13 +133,26 @@ int sys_wait4(pid_t pid, int *status, int options, void *usage) interruptible_sleep_on(¤t->child_wait); if (current->signal) { debug_wait("WAIT(%P) return -EINTR\n"); +#ifdef CONFIG_COMPAT_V7 + if (v7) { + retval[1] = p->signal + p->exit_status; + //printk("WAIT(%P): V7 wait interrupted, status 0x%04x\n", retval[1]); + return *(long *)retval; + } +#endif return -EINTR; } debug_wait("WAIT(%P) wakeup\n"); } - debug_wait("WAIT(%P) return -ECHILD\n"); - return -ECHILD; + debug_wait("WAIT(%P) return -ECHILD\n"); +#ifdef CONFIG_COMPAT_V7 + if (v7) { + retval[0] = -ECHILD; + return *(long *)retval; + } else +#endif + return -ECHILD; } void do_exit(int status) diff --git a/tlvc/kernel/fork.c b/tlvc/kernel/fork.c index 6ed800ac..e6301249 100644 --- a/tlvc/kernel/fork.c +++ b/tlvc/kernel/fork.c @@ -60,7 +60,7 @@ struct task_struct *find_empty_process(void) t->pid = get_pid(); t->ticks = 0; /* for CONFIG_CPU_USAGE */ t->average = 0; -#ifdef CHECK_KSTACK +#if defined(CHECK_KSTACK) || defined(CONFIG_COMPAT_V7) t->kstack_max = 0; t->kstack_prevmax = 0; #endif diff --git a/tlvc/kernel/signal.c b/tlvc/kernel/signal.c index c8778b13..1b6758c4 100644 --- a/tlvc/kernel/signal.c +++ b/tlvc/kernel/signal.c @@ -126,12 +126,31 @@ int sys_kill(pid_t pid, sig_t sig) return kill_process(pid, sig, 0); } -int sys_signal(int signr, __kern_sighandler_t handler) +int sys_signal(int signr, __kern_sighandler_t h) { - debug_sig("SIGNAL sys_signal %d action %x:%x pid %d\n", signr, - _FP_SEG(handler), _FP_OFF(handler), current->pid); + __kern_sighandler_t handler = h; + int retval = 0; + //debug_sig("SIGNAL sys_signal %d action %x:%x pid %d\n", signr, +// _FP_SEG(handler), _FP_OFF(handler), current->pid); if (((unsigned int)signr > NSIG) || signr == SIGKILL || signr == SIGSTOP) return -EINVAL; + +#ifdef CONFIG_COMPAT_V7 /* Don't do V7 signal handling for now */ + /* NOTE: the V7 'hander' arg is a 16bit pointer to a routine in the process' + * address space, not a long. A Venix signal handler returns via IRET. + * Finally, Venix expects the (address of the) previous signal handler + * (if any) to be returned. */ + if (current->task_is_V7) { + if (_FP_OFF(handler) == (unsigned int)KERN_SIG_IGN) + handler = KERN_SIG_IGN; /* zero out SEG part */ + else + handler = KERN_SIG_DFL; + retval = (unsigned int)_FP_OFF(handler); + } +#endif + debug_sig("SIGNAL sys_signal %d action %x:%x pid %d\n", signr, + _FP_SEG(handler), _FP_OFF(handler), current->pid); + if (handler == KERN_SIG_DFL) current->sig.action[signr - 1].sa_dispose = SIGDISP_DFL; else if (handler == KERN_SIG_IGN) @@ -148,5 +167,5 @@ int sys_signal(int signr, __kern_sighandler_t handler) current->sig.handler = handler; current->sig.action[signr - 1].sa_dispose = SIGDISP_CUSTOM; } - return 0; + return retval; } diff --git a/tlvc/kernel/sys.c b/tlvc/kernel/sys.c index 900ce8e8..31c8e07f 100644 --- a/tlvc/kernel/sys.c +++ b/tlvc/kernel/sys.c @@ -85,19 +85,40 @@ static int twovalues(int retval, int *copyval, int *copyaddr) return -EFAULT; return retval; } +#ifdef CONFIG_COMPAT_V7 +static unsigned long ret_ulong(unsigned int r, unsigned int e) +{ + unsigned int ret[2] = { r, e}; + current->task_is_V7 |= 0x100; /* no error in ax */ + return *(unsigned long *)ret; +} -uid_t sys_getuid(int *euid) +unsigned long sys_getuid(int *euid) { + if (current->task_is_V7) + return ret_ulong(current->uid, current->euid); +#else +uid_t sys_getuid(int *euid) { +#endif return twovalues(current->uid, (int *)¤t->euid, euid); } -uid_t sys_getgid(int *egid) +#ifdef CONFIG_COMPAT_V7 +unsigned long sys_getgid(int *egid) { + if (current->task_is_V7) + return ret_ulong(current->gid, current->egid); +#else +uid_t sys_getgid(int *egid) { +#endif return twovalues(current->gid, (int *)¤t->egid, egid); } pid_t sys_getpid(int *ppid) { +#ifdef CONFIG_COMPAT_V7 + if (current->task_is_V7) return current->pid; +#endif return twovalues(current->pid, (int *)¤t->ppid, ppid); } diff --git a/tlvc/kernel/time.c b/tlvc/kernel/time.c index 1d336338..c532d43d 100644 --- a/tlvc/kernel/time.c +++ b/tlvc/kernel/time.c @@ -126,3 +126,34 @@ int sys_gettimeofday(register struct timeval *tv, struct timezone *tz) /* success */ return 0; } + +#ifdef CONFIG_COMPAT_V7 +#include + +time_t sys_time(time_t *tloc) +{ + time_t t = current_time(); + if (tloc) + verified_memcpy_tofs(tloc, &t, sizeof(t)); + if (current->task_is_V7) + current->task_is_V7 |= 0x100; /* AX does not return errors */ + return t; /* AX+DX long return V7 style */ +} + +int sys_ftime(struct timeb *tb) +{ + jiff_t now; + struct timeb tmp_tb; + + tmp_tb.time = current_time(); + now = jiffies(); + tmp_tb.millitm = (unsigned short)(xtime.tv_usec/1000) + (unsigned short)((now - xtime_jiffies) % HZ) * 10; + tmp_tb.timezone = xzone.tz_minuteswest; + tmp_tb.dstflag = 1; /* DST enabled */ + verified_memcpy_tofs(tb, &tmp_tb, sizeof(struct timeb)); + + return 0; +} + +#endif +