bp->bf_time = ++bufclock; /* Time stamp it */
return bp->__bf_data;
}
+
+void tmpfree(void *p)
+{
+ brelse(p);
+}
#endif
/*
uget((uaddr),(buf)->__bf_data + (off), (len))
#define blkptr(buf, off, len) ((void *)((buf)->__bf_data + (off)))
#define blkzero(buf) memset(buf->__bf_data, 0, BLKSIZE)
-#define tmpfree(x) brelse((void *)x)
#else
extern void blktok(void *kaddr, struct blkbuf *buf, uint16_t off, uint16_t len);
extern void blkfromk(void *kaddr, struct blkbuf *buf, uint16_t off, uint16_t len);
/* Worst case is needing to copy over about 64 bytes */
extern void *blkptr(struct blkbuf *buf, uint16_t offset, uint16_t len);
extern void blkzero(struct blkbuf *buf);
-extern void tmpfree(void *p);
#endif
/* TODO: consider smaller inodes or clever caching. 2BSD uses small
extern void bawrite(bufptr);
extern int bfree(bufptr bp, uint8_t dirty); /* dirty: 0=clean, 1=dirty (write back), 2=dirty+immediate write */
extern void *tmpbuf(void);
+extern void tmpfree(void *p);
extern bufptr zerobuf(void);
extern void bufsync(void);
extern bufptr bfind(uint16_t dev, blkno_t blk);
extern void wakeup(void *event);
extern void pwake(ptptr p);
extern ptptr getproc(void);
-extern void newproc(ptptr p);
+extern void makeproc(ptptr p, u_data *u);
extern ptptr ptab_alloc(void);
extern void ssig(ptptr proc, uint8_t sig);
extern void recalc_cursig(void);
.module z80fixedbank
.globl _ptab_alloc
- .globl _newproc
+ .globl _makeproc
.globl _chksigs
.globl _getproc
.globl _platform_monitor
.globl _need_resched
.globl _nready
.globl _platform_idle
+ .globl _udata
.globl map_kernel_restore
.globl map_process_a
pop bc
; Make a new process table entry, etc.
- ld hl, (fork_proc_ptr)
+ ld hl, #_udata
+ ld hl, (fork_proc_ptr)
push hl
push af
- call _newproc
+ call _makeproc
pop af
pop bc
+ pop bc
; runticks = 0;
ld hl, #0
.module z80fixedbank
.globl _ptab_alloc
- .globl _newproc
+ .globl _makeproc
.globl _chksigs
.globl _getproc
.globl _platform_monitor
.globl _nready
.globl _platform_idle
.globl _int_disabled
+ .globl _udata
.globl map_kernel
.globl map_process
pop bc
; Make a new process table entry, etc.
+ ld hl,#_udata
+ push hl
ld hl, (fork_proc_ptr)
push hl
- call _newproc
+ call _makeproc
pop bc
+ pop bc
; runticks = 0;
ld hl, #0
; Generic handling for single process in memory Z80 systems.
;
; This code could really do with some optimizing.
+;
+; FIXME: IRQ enable logic during swap
+;
+; FIXME: turn on parent first behaviour when ready
;
.module tricks
.globl _ptab_alloc
- .globl _newproc
+ .globl _makeproc
.globl _chksigs
.globl _getproc
.globl _platform_monitor
.globl interrupt_handler
.globl _swapper
.globl _swapout
+ .globl _swapout_new
.globl _int_disabled
+ .globl _udata
+ .globl _tmpbuf
+ .globl _tmpfree
; imported debug symbols
.globl outstring, outde, outhl, outbc, outnewline, outchar, outcharhex
; save machine state
ld hl, #0 ; return code set here is ignored, but _switchin can
+
; return from either _switchout OR _dofork, so they must both write
; U_DATA__U_SP with the following on the stack:
push hl ; return code
_switchin:
di
+ ld a,#1
+ ld (_int_disabled),a
pop bc ; return address
pop de ; new process pointer
;
; check u_data->u_ptab matches what we wanted
ld hl, (U_DATA__U_PTAB) ; u_data->u_ptab
or a ; clear carry flag
- sbc hl, de ; subtract, result will be zero if DE==IX
+ sbc hl, de ; subtract, result will be zero if DE==HL
jr nz, switchinfail
; wants optimising up a bit
ld (fork_proc_ptr), hl
- ; prepare return value in parent process -- HL = p->p_pid;
- ld de, #P_TAB__P_PID_OFFSET
- add hl, de
- ld a, (hl)
- inc hl
- ld h, (hl)
- ld l, a
-
; Save the stack pointer and critical registers.
; When this process (the parent) is switched back in, it will be as if
; it returns with the value of the child's pid.
- push hl ; HL still has p->p_pid from above, the return value in the parent
+ ld hl,#0
+ push hl ; #0 child
push ix
push iy
- ; save kernel stack pointer -- when it comes back in the parent we'll be in
+ ; save kernel stack pointer -- when it comes back in the child we'll be in
; _switchin which will immediately return (appearing to be _dofork()
; returning) and with HL (ie return code) containing the child PID.
; Hurray.
; now we're in a safe state for _switchin to return in the parent
; process.
- ld hl, (U_DATA__U_PTAB)
+
+ ; Make a new process table entry, etc.
+
+ ; Copy the parent properties into the temporary udata copy
+ call _tmpbuf
+ ld a,h
+ or l
+ jp z,ohpoo
push hl
- call _swapout
+ ex de,hl
+ ld hl, #_udata
+ ld bc, #U_DATA__TOTALSIZE
+ ldir
+
+ ; Recover the buffer pointer
pop hl
+ push hl
+
+ ; Make the child udata out of the temporary buffer
+ push hl
+ ld hl, (fork_proc_ptr)
+ push hl
+ call _makeproc
+ pop bc
+ pop bc
+
+ ; in the child process, fork() returns zero.
+ ;
+ ; And we exit, with the kernel mapped, the child assembled in the
+ ; copy area as if it had done a switchout, the parent meanwhile
+ ; continues happily on
+
+ ; now we're in a safe state for _switchin to return in the child
+ ; process swap out the image and the new udata
+ ; Stack the buffer as a second argument
+ pop hl
+ push hl
+ push hl
+
+ ld hl, (fork_proc_ptr)
+ push hl
+ call _swapout_new
+ pop hl
+ pop hl
+
+ ; Buffer pointer is sitting top of stack still
+ ; so use it as an argument to tmpfree
+ call _tmpfree
+
+ pop hl
+
+ ld hl, (fork_proc_ptr)
+ ; prepare return value in parent process -- HL = p->p_pid;
+ ld de, #P_TAB__P_PID_OFFSET
+ add hl, de
+ ld a, (hl)
+ inc hl
+ ld h, (hl)
+ ld l, a
; now the copy operation is complete we can get rid of the stuff
; _switchin will be expecting from our copy of the stack.
pop bc
pop bc
pop bc
+ ; Return pid of child we forked into swap
+ ret
- ; Make a new process table entry, etc.
- ld hl, (fork_proc_ptr)
- push hl
- call _newproc
- pop bc
+ohpoo:
+ ld hl,#nobufs
+ call outstring
+ jp _platform_monitor
- ; runticks = 0;
- ld hl, #0
- ld (_runticks), hl
- ; in the child process, fork() returns zero.
- ;
- ; And we exit, with the kernel mapped, the child now being deemed
- ; to be the live uarea. The parent is frozen in time and space as
- ; if it had done a switchout().
- ret
+nobufs:
+ .asciz 'nobufs'
;
; We can keep a stack in common because we will complete our
; use of it before we switch common block. In this case we have
TODO
-Debug floppy handling
-
Fix memory size reporting 64 v 48K
Floppy drive B - set drive info and propogate it
Floppy drive B - set parameters at init time
-Petty coloured stripes when reading/writing floppy
Move buffers into upper part of bank 7 to see if can get kernel below
C000 + E000 upwards to get bigger user image ?
0-3 Kernel
4-7 User, video in 7
+May also be able to later do
+
+0-2 Kernel 3 high kernel common
+ 7 upper 8K buffers and high stubs
+ 7 lower scree
+4/5/6 User 64K 3 high kernel common , 3 low user
+
+4/5/6 User 48K 7 screen, upper 8K buffers
+
+
0/4 hold page0 copies, 3 7 both hold pageh copies - fine as video is
in bottom of that chunk.
/* Swap based one process in RAM */
#define CONFIG_SWAP_ONLY
+#define CONFIG_PARENT_FIRST
#define CONFIG_SPLIT_UDATA
#define UDATA_BLKS 1
#define UDATA_SIZE 0x200
#define CONFIG_DYNAMIC_BUFPOOL
#define CONFIG_DYNAMIC_SWAP
+#define MAXTICKS 20 /* Has to be high because we are swap only */
/* Custom banking */
/* Newproc fixes up the tables for the child of a fork but also for init
* Call in the processes context!
* This process MUST be run immediately (since it sets status P_RUNNING)
+ *
+ * The fork code has already copied the udata into u so we only need to
+ * touch things that changed. u may or may not be the current udata
*/
-void newproc(regptr ptptr p)
+void makeproc(regptr ptptr p, u_data *u)
{ /* Passed New process table entry */
- uint8_t *j;
+ uint8_t *j, *e;
irqflags_t irq;
+ ptptr pp;
irq = di();
/* Note that ptab_alloc clears most of the entry */
/* calculate base page of process based on ptab table offset */
- udata.u_page = p->p_page;
- udata.u_page2 = p->p_page2;
+ u->u_page = p->p_page;
+ u->u_page2 = p->p_page2;
program_vectors(&p->p_page); /* set up vectors in new process and
if needed copy any common code */
-
+#ifdef CONFIG_PARENT_FIRST
+ p->p_status = P_READY;
+#else
p->p_status = P_RUNNING;
+#endif
nready++; /* runnable process count */
- p->p_pptr = udata.u_ptab;
- p->p_sig[0].s_ignored = udata.u_ptab->p_sig[0].s_ignored;
- p->p_sig[1].s_ignored = udata.u_ptab->p_sig[1].s_ignored;
- p->p_sig[0].s_held = udata.u_ptab->p_sig[0].s_held;
- p->p_sig[1].s_held = udata.u_ptab->p_sig[1].s_held;
- p->p_tty = udata.u_ptab->p_tty;
- p->p_uid = udata.u_ptab->p_uid;
+ pp = u->u_ptab; /* Because it is a copy of the parent */
+
+ p->p_pptr = pp;
+ p->p_sig[0].s_ignored = pp->p_sig[0].s_ignored;
+ p->p_sig[1].s_ignored = pp->p_sig[1].s_ignored;
+ p->p_sig[0].s_held = pp->p_sig[0].s_held;
+ p->p_sig[1].s_held = pp->p_sig[1].s_held;
+ p->p_tty = pp->p_tty;
+ p->p_uid = pp->p_uid;
/* Set default priority */
p->p_priority = MAXTICKS;
p->p_udata = &udata;
#endif
- udata.u_ptab = p;
+ u->u_ptab = p; /* Fixup from parent */
memset(&p->p_utime, 0, 4 * sizeof(clock_t)); /* Clear tick counters */
rdtime32(&p->p_time);
- if (udata.u_cwd)
- i_ref(udata.u_cwd);
- if (udata.u_root)
- i_ref(udata.u_root);
- udata.u_cursig = 0;
- udata.u_error = 0;
- for (j = udata.u_files; j < (udata.u_files + UFTSIZE); ++j) {
+ if (u->u_cwd)
+ i_ref(u->u_cwd);
+ if (u->u_root)
+ i_ref(u->u_root);
+ u->u_cursig = 0;
+ u->u_error = 0;
+
+ e = u->u_files + UFTSIZE;
+ for (j = u->u_files; j < e; ++j) {
if (*j != NO_FILE)
++of_tab[*j].o_refs;
}
/* Check run time of current process. We don't charge time while
swapping as the last thing we want to do is to swap a process in
and decide it took time to swap in so needs to go away again! */
- /* FIXME: can we kill off inint ? */
if (!inswap && (++runticks >= udata.u_ptab->p_priority)
- && !udata.u_insys && inint && nready > 1) {
- need_resched = 1;
+ && !udata.u_insys && inint) {
+ /* It might appear to make the best sense to just leave
+ runticks ticking upwards if nobody else needs to run but
+ this has two problems. The obvious one is that it may wrap
+ but less obviously it can also cause thrashing on fork()
+ in some memory models */
+ if (nready > 1) {
+ need_resched = 1;
#ifdef DEBUG_PREEMPT
- kprintf("[preempt %p %d]", udata.u_ptab,
- udata.u_ptab->p_priority);
+ kprintf("[preempt %p %d]", udata.u_ptab,
+ udata.u_ptab->p_priority);
#endif
- }
+ } else /* Nobody else to run, user gets new time quantum */
+ runticks = 0;
+ }
#endif
}
di();
if (runticks >= udata.u_ptab->p_priority && nready > 1) {
+#ifdef DEBUG_PREEMPT
+ kprintf("P: %d %x %d\n", runticks, udata.u_ptab, udata.u_ptab->p_priority);
+#endif
/* Time to switch out? - we may have overstayed our welcome inside
a syscall so swtch straight afterwards */
udata.u_ptab->p_status = P_READY;
signal_parent(udata.u_ptab);
nready--;
nproc--;
-
switchin(getproc());
panic(PANIC_DOEXIT);
}
#include <kdata.h>
#include <printf.h>
+#undef DEBUG
+
#ifdef CONFIG_SWAP_ONLY
void pagemap_free(ptptr p)
* Swap out the memory of a process to make room
* for something else
*/
-int swapout(ptptr p)
+int swapout_new(ptptr p, void *u)
{
uint16_t page = p->p_page;
uint16_t blk;
uint16_t map;
#ifdef DEBUG
- kprintf("Swapping out %x (%d)\n", p, p->p_page);
+ kprintf("Swapping out %x (%d)\n", p, p->p_pid);
#endif
if (!page)
panic(PANIC_ALREADYSWAP);
if (map == 0)
return ENOMEM;
blk = map * SWAP_SIZE;
- /* Write the app (and possibly the uarea etc..) to disk */
+ /* Write the app (and uarea etc..) to disk */
#ifdef CONFIG_SPLIT_UDATA
- /* Note the page for the udata bit as it goes direct to udata */
- swapwrite(SWAPDEV, blk, UDATA_SIZE, (uaddr_t)&udata, 0);
+ /* Write the udata block as kernel. */
+ udata.u_dptr = u;
+ udata.u_block = blk;
+ udata.u_nblock = UDATA_SIZE >> BLKSHIFT; /* 1 */
+ ((*dev_tab[major(SWAPDEV)].dev_write) (minor(SWAPDEV), 0, 0));
+ /* Use the standard swapwrite helper for the rest */
swapwrite(SWAPDEV, blk + UDATA_BLKS, SWAPTOP - SWAPBASE,
SWAPBASE, 1);
#else
- swapwrite(SWAPDEV, blk, SWAPTOP - SWAPBASE,
- SWAPBASE, 1);
+#error "Not supported"
#endif
p->p_page = 0;
p->p_page2 = map;
#ifdef DEBUG
- kprintf("%x: swapout done %d\n", p, p->p_page);
+ kprintf("%x: swapout done %d\n", p, p->p_page2);
#endif
return 0;
}
+int swapout(ptptr p)
+{
+ return swapout_new(p, &udata);
+}
+
/*
* Swap ourself in: must be on the swap stack when we do this
*/
uint16_t blk = map * SWAP_SIZE;
#ifdef DEBUG
- kprintf("Swapin %x, %d\n", p, p->p_page);
+ kprintf("Swapin %x (%d, %d)\n", p, p->p_page2, p->p_pid);
#endif
if (!p->p_page) {
kprintf("%x: nopage!\n", p);
return;
}
-#ifdef CONFIG_SPLIT_UDATA
- /* Note the page for the udata bit as it goes direct to udata */
+ /* Note the page for the udata bit as it goes direct to udata and
+ is always in common */
swapread(SWAPDEV, blk, UDATA_SIZE, (uaddr_t)&udata, 0);
swapread(SWAPDEV, blk + UDATA_BLKS, SWAPTOP - SWAPBASE,
SWAPBASE, 1);
-#else
- swapread(SWAPDEV, blk, SWAPTOP - SWAPBASE,
- SWAPBASE, 1);
-#endif
+
#ifdef DEBUG
- kprintf("%x: swapin done %d\n", p, p->p_page);
+ kprintf("%x: swapin done %d\n", p, p->p_page2);
#endif
}
void create_init(void)
{
- uint8_t *j;
+ uint8_t *j, *e;
udata.u_top = PROGLOAD + 512; /* Plenty for the boot */
init_process = ptab_alloc();
udata.u_ptab = init_process;
init_process->p_top = udata.u_top;
map_init();
- newproc(init_process);
+
+ /* wipe file table */
+ e = udata.u_files + UFTSIZE;
+ for (j = udata.u_files; j < e; ++j)
+ *j = NO_FILE;
+
+ makeproc(init_process, &udata);
+ init_process->p_status = P_RUNNING;
udata.u_insys = 1;
init_process->p_status = P_RUNNING;
- /* wipe file table */
- for (j = udata.u_files; j < (udata.u_files + UFTSIZE); ++j) {
- *j = NO_FILE;
- }
/* Poke the execve arguments into user data space so _execve() can read them back */
/* Some systems only have a tiny window we can use at boot as most of
this space is loaded with common memory */
/*
* We're going to run our child process next, so mark this process as
* being ready to run
+ *
+ * FIXME: push this down into dofork
*/
+#ifndef CONFIG_PARENT_FIRST
udata.u_ptab->p_status = P_READY;
+#endif
+
/*
* Kick off the new process (the bifurcation happens inside here, we
* *MAY* returns in both the child and parent contexts, however in a
nready--;
}
irqrestore(irq);
-
return r;
}