diff options
-rw-r--r-- | kernel/drivers/ata.c | 58 | ||||
-rw-r--r-- | kernel/include/comus/drivers/ata.h | 1 | ||||
-rw-r--r-- | kernel/include/comus/procs.h | 5 | ||||
-rw-r--r-- | kernel/include/comus/syscalls.h | 5 | ||||
-rw-r--r-- | kernel/syscall.c | 99 | ||||
-rw-r--r-- | user/forkman.c | 363 | ||||
-rw-r--r-- | user/include/unistd.h | 40 | ||||
-rw-r--r-- | user/lib/syscall.S | 3 |
8 files changed, 547 insertions, 27 deletions
diff --git a/kernel/drivers/ata.c b/kernel/drivers/ata.c index 6f53d04..2fc059f 100644 --- a/kernel/drivers/ata.c +++ b/kernel/drivers/ata.c @@ -252,6 +252,7 @@ static enum ide_error ide_channel_poll(struct ide_channel *channel, if (advanced_check) { uint8_t state = ide_channel_read(channel, ATA_REG_STATUS); + // check for errors or faults if (state & ATA_SR_ERROR) { return IDE_ERROR_POLL_STATUS_REGISTER_ERROR; } @@ -260,7 +261,7 @@ static enum ide_error ide_channel_poll(struct ide_channel *channel, return IDE_ERROR_POLL_DEVICE_FAULT; } - // BSY = 0; DF = 0; ERR = 0 so we should check for DRQ now. + // then check if drive is ready if ((state & ATA_SR_DRIVEREQUESTREADY) == 0) { return IDE_ERROR_POLL_DRIVE_REQUEST_NOT_READY; } @@ -322,7 +323,7 @@ static void ide_error_print(struct ide_device *dev, const enum ide_error err) static void ide_initialize(uint32_t BAR0, uint32_t BAR1, uint32_t BAR2, uint32_t BAR3, uint32_t BAR4) { - // 1- Detect I/O Ports which interface IDE Controller: + // calculate which io ports interface with the IDE controller ide_channels[ATA_PRIMARY] = (struct ide_channel){ .io_base = (BAR0 & 0xFFFFFFFC) + 0x1F0 * (!BAR0), .control_base = (BAR1 & 0xFFFFFFFC) + 0x3F6 * (!BAR1), @@ -334,11 +335,13 @@ static void ide_initialize(uint32_t BAR0, uint32_t BAR1, uint32_t BAR2, .bus_master_ide_base = (BAR4 & 0xFFFFFFFC) + 8, }; - // 2- Disable IRQs: + // disable irqs ide_channel_write(channel(ATA_PRIMARY), ATA_REG_CONTROL, 2); ide_channel_write(channel(ATA_SECONDARY), ATA_REG_CONTROL, 2); - // 3- Detect ATA-ATAPI Devices: + // detect disks by writing CMD_IDENTIFY to each one and checking for err. + // if device exists, ask for its ID space and copy out info about the + // device into the ide_device struct uint32_t device_count = 0; for (uint8_t channel_idx = 0; channel_idx < 2; channel_idx++) { // drive idx is like device_count but it starts at 0 per channel @@ -362,7 +365,7 @@ static void ide_initialize(uint32_t BAR0, uint32_t BAR1, uint32_t BAR2, kspin_milliseconds(1); if (ide_channel_read(chan, ATA_REG_STATUS) == 0) { - continue; // if status == 0, no device. + continue; // if status == 0, no device } bool ata_err = false; @@ -383,7 +386,7 @@ static void ide_initialize(uint32_t BAR0, uint32_t BAR1, uint32_t BAR2, } } - // probe for ATAPI devices, if needed + // probe for ATAPI devices, though they aren't implemented uint8_t type = IDE_ATA; if (ata_err) { uint8_t cl = ide_channel_read(chan, ATA_REG_LBA1); @@ -396,7 +399,7 @@ static void ide_initialize(uint32_t BAR0, uint32_t BAR1, uint32_t BAR2, WARN("ATAPI device found but ATAPI is not supported"); type = IDE_ATAPI; } else { - // unknown type (may not be a device). + // unknown type (may not be a device?) continue; } @@ -425,16 +428,16 @@ static void ide_initialize(uint32_t BAR0, uint32_t BAR1, uint32_t BAR2, // get size (depends on address mode): if (dev->supported_command_sets & (1 << 26)) { - // device uses 48-bit addressing: + // lba48 dev->size_in_sectors = *((uint32_t *)(id_space_buf + ATA_IDENT_MAX_LBA_EXT)); } else { - // device uses CHS or 28-bit dddressing: + // lba28 dev->size_in_sectors = *((uint32_t *)(id_space_buf + ATA_IDENT_MAX_LBA)); } - // string indicates model of device (Western Digital HDD, SONY DVD-RW...) + // string indicates model of device like "Western Digital HDD" etc for (uint8_t i = 0; i < 40; i += 2) { dev->model_str[i] = id_space_buf[ATA_IDENT_MODEL + i + 1]; dev->model_str[i + 1] = id_space_buf[ATA_IDENT_MODEL + i]; @@ -460,13 +463,13 @@ enum access_mode { static uint8_t get_ata_cmd_for_access(enum lba_mode lba_mode, enum access_mode mode) { - // routine that is followed: - // If ( DMA & LBA48) DO_DMA_EXT; - // If ( DMA & LBA28) DO_DMA_LBA; - // If ( DMA & LBA28) DO_DMA_CHS; - // If (!DMA & LBA48) DO_PIO_EXT; - // If (!DMA & LBA28) DO_PIO_LBA; - // If (!DMA & !LBA#) DO_PIO_CHS; + // outline of the algorithm: + // If ( dma & lba48) DO_DMA_EXT; + // If ( dma & lba28) DO_DMA_LBA; + // If ( dma & lba28) DO_DMA_CHS; + // If (!dma & lba48) DO_PIO_EXT; + // If (!dma & lba28) DO_PIO_LBA; + // If (!dma & !lba#) DO_PIO_CHS; if (mode == READ) { switch (lba_mode) { @@ -500,7 +503,7 @@ static uint8_t get_ata_cmd_for_access(enum lba_mode lba_mode, // if (lba_mode == 2 && dma == 1 && direction == 1) // cmd = ATA_CMD_WRITE_DMA_EXT; } - // NOTE: unreachable + panic("unreachable"); return -1; } @@ -520,7 +523,8 @@ static enum ide_error ide_device_ata_access(struct ide_device *dev, ide_channel_write(chan, ATA_REG_CONTROL, chan->no_interrupt = (ide_irq_invoked = 0x0) + 0x02); - // select one from LBA28, LBA48 or CHS + // select one from lba28, lba48 or CHS, and fill lba_io with the parameters + // for the disk access command if (lba >= 0x10000000 || numsects > UINT8_MAX) { // drive should support LBA in this case, or you are giving a bad LBA lba_mode = LBA48; @@ -561,7 +565,7 @@ static enum ide_error ide_device_ata_access(struct ide_device *dev, if (dev->drive_idx > 1) panic("unexpected drive_idx"); - // select Drive from the controller + // select our drive if (lba_mode == CHS) { ide_channel_write(chan, ATA_REG_HDDEVSEL, 0xA0 | (dev->drive_idx << 4) | head); @@ -572,7 +576,7 @@ static enum ide_error ide_device_ata_access(struct ide_device *dev, ide_channel_write(chan, ATA_REG_HDDEVSEL, 0x40 | (dev->drive_idx << 4)); } - // write Parameters + // actually write the parameters if (lba_mode == LBA48) { ide_channel_write(chan, ATA_REG_SECCOUNT1, (numsects >> 8) & 0xff); ide_channel_write(chan, ATA_REG_LBA3, lba_io[3]); @@ -589,7 +593,7 @@ static enum ide_error ide_device_ata_access(struct ide_device *dev, // TODO: if (dma) { ... } else { if (mode == READ) { - // PIO read + // just read all the bytes of the sectors out of the io port for (size_t i = 0; i < numsects; i++) { enum ide_error ret = ide_channel_poll(chan, 1); if (ret) @@ -599,7 +603,7 @@ static enum ide_error ide_device_ata_access(struct ide_device *dev, rep_inw(chan->io_base, &buf[i * 256], 256); } } else { - // PIO write + // just write all the bytes of the sectors into the io port for (size_t i = 0; i < numsects; i++) { enum ide_error err = ide_channel_poll(chan, 0); #if LOG_LEVEL >= LOG_LVL_WARN @@ -610,13 +614,15 @@ static enum ide_error ide_device_ata_access(struct ide_device *dev, #endif rep_outw(chan->io_base, &buf[i * 256], 256); } + + // flush the cache to fully complete the write ide_channel_write(chan, ATA_REG_COMMAND, (uint8_t[]){ ATA_CMD_CACHE_FLUSH, ATA_CMD_CACHE_FLUSH, ATA_CMD_CACHE_FLUSH_EXT }[lba_mode]); enum ide_error err = ide_channel_poll(chan, 0); #if LOG_LEVEL >= LOG_LVL_WARN if (err) { - WARN("DRIVE WRITE FAILED:"); + WARN("DRIVE WRITE FAILED, CACHE FLUSH ERR:"); ide_error_print(dev, err); } #endif @@ -647,7 +653,9 @@ enum ide_error ide_device_read_sectors(ide_device_t dev_identifier, // for (i = 0; i < numsects; i++) // err = ide_atapi_read(drive, lba + i, 1, es, edi + (i*2048)); //panic("atapi unimplemented- todo"); - return 1; + + // soft error instead of panic + return IDE_ERROR_UNIMPLEMENTED; } if (err) { diff --git a/kernel/include/comus/drivers/ata.h b/kernel/include/comus/drivers/ata.h index c404d84..c9ef207 100644 --- a/kernel/include/comus/drivers/ata.h +++ b/kernel/include/comus/drivers/ata.h @@ -27,6 +27,7 @@ enum ide_error { IDE_ERROR_POLL_DEVICE_FAULT, IDE_ERROR_POLL_STATUS_REGISTER_ERROR, IDE_ERROR_POLL_WRITE_PROTECTED, + IDE_ERROR_UNIMPLEMENTED, }; struct ide_devicelist { diff --git a/kernel/include/comus/procs.h b/kernel/include/comus/procs.h index 3df31c3..ee723aa 100644 --- a/kernel/include/comus/procs.h +++ b/kernel/include/comus/procs.h @@ -75,6 +75,11 @@ struct pcb { uint64_t syscall; uint64_t wakeup; uint8_t exit_status; + + // pipe to check for shared memory + void* shared_mem; + size_t shared_mem_pages; + pid_t shared_mem_source; }; /// ordering of pcb queues diff --git a/kernel/include/comus/syscalls.h b/kernel/include/comus/syscalls.h index 8b671c2..146bce1 100644 --- a/kernel/include/comus/syscalls.h +++ b/kernel/include/comus/syscalls.h @@ -31,9 +31,12 @@ #define SYS_drm 18 #define SYS_ticks 19 #define SYS_seek 20 +#define SYS_allocshared 21 +#define SYS_popsharedmem 22 +#define SYS_keypoll 23 // UPDATE THIS DEFINITION IF MORE SYSCALLS ARE ADDED! -#define N_SYSCALLS 21 +#define N_SYSCALLS 24 // interrupt vector entry for system calls #define VEC_SYSCALL 0x80 diff --git a/kernel/syscall.c b/kernel/syscall.c index 44ebfa0..8ed7631 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -3,6 +3,7 @@ #include <comus/user.h> #include <comus/cpu.h> #include <comus/syscalls.h> +#include <comus/input.h> #include <comus/drivers/acpi.h> #include <comus/drivers/gpu.h> #include <comus/drivers/pit.h> @@ -482,6 +483,99 @@ static int sys_ticks(void) return 0; } +static int sys_popsharedmem(void) +{ + RET(void *, res_mem); + *res_mem = NULL; + + if (pcb->shared_mem == NULL) { + return 1; + } + + struct pcb *const sharer = pcb_find_pid(pcb->shared_mem_source); + if (sharer == NULL) { + // process died or something since sharing + pcb->shared_mem = NULL; + return 1; + } + + void *result = + mem_mapaddr(pcb->memctx, mem_get_phys(sharer->memctx, pcb->shared_mem), + pcb->shared_mem, pcb->shared_mem_pages * PAGE_SIZE, + F_WRITEABLE | F_UNPRIVILEGED); + + // if (!result) { + // alert the other process that we cannot get its allocation? + // mem_free_pages(pcb->memctx, alloced); + // return 1; + // } + + *res_mem = result; + + pcb->shared_mem = NULL; + + return 0; +} + +static int sys_allocshared(void) +{ + ARG1(size_t, num_pages); + ARG2(unsigned short, otherpid); // same as pid_t + RET(void *, res_mem); + *res_mem = NULL; + assert(sizeof(unsigned short) == sizeof(pid_t), + "out of date sys_memshare syscall, pid_t changed?"); + + if (otherpid == pcb->pid || otherpid == 0) { + return 1; + } + + struct pcb *const otherpcb = pcb_find_pid(otherpid); + if (otherpcb == NULL) { + // no such target process exists + return 1; + } + if (otherpcb->shared_mem != NULL) { + // it has yet to consume the last allocshared given to it + return 1; + } + + void *alloced = + mem_alloc_pages(pcb->memctx, num_pages, F_WRITEABLE | F_UNPRIVILEGED); + + if (!alloced) { + return 1; + } + + otherpcb->shared_mem = alloced; + otherpcb->shared_mem_source = pcb->pid; + otherpcb->shared_mem_pages = num_pages; + + *res_mem = alloced; + + return 0; +} + +// NOTE: observes AND consumes the key event +static int sys_keypoll(void) +{ + ARG1(struct keycode *, keyev); + RET(int, waspressed); + + void *ouraddr = kmapuseraddr(pcb->memctx, keyev, sizeof(struct keycode)); + + if (keycode_pop(ouraddr)) { + kunmapaddr(ouraddr); + *waspressed = false; + return 0; + } + + kunmapaddr(ouraddr); + + *waspressed = true; + return 0; +} + static int sys_seek(void) { RET(long int, ret); @@ -498,6 +592,7 @@ static int sys_seek(void) return 0; } +// clang-format off static int (*syscall_tbl[N_SYSCALLS])(void) = { [SYS_exit] = sys_exit, [SYS_waitpid] = sys_waitpid, [SYS_fork] = sys_fork, [SYS_exec] = sys_exec, @@ -509,8 +604,10 @@ static int (*syscall_tbl[N_SYSCALLS])(void) = { [SYS_sleep] = sys_sleep, [SYS_brk] = sys_brk, [SYS_sbrk] = sys_sbrk, [SYS_poweroff] = sys_poweroff, [SYS_drm] = sys_drm, [SYS_ticks] = sys_ticks, - [SYS_seek] = sys_seek, + [SYS_seek] = sys_seek, [SYS_allocshared] = sys_allocshared, + [SYS_popsharedmem] = sys_popsharedmem, [SYS_keypoll] = sys_keypoll, }; +// clang-format on void syscall_handler(void) { diff --git a/user/forkman.c b/user/forkman.c new file mode 100644 index 0000000..538992b --- /dev/null +++ b/user/forkman.c @@ -0,0 +1,363 @@ +/* + * FORKMAN: a platformer game with IPC + * + * This executable will fork. One process is responsible for writing to the + * screen and getting user input, the other is responsible for game logic. + * + */ + +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <unistd.h> +#include "../kernel/include/comus/keycodes.h" + +#define DBG + +#define GAME_WIDTH 480 +#define GAME_HEIGHT 360 +#define SHARED_PAGES 10 +#define PAGE_SIZE 4096 +#define TILE_WIDTH 16 +#define TILE_HEIGHT 16 +#define GAME_WIDTH_TILES (GAME_WIDTH / TILE_WIDTH) +#define GAME_HEIGHT_TILES (GAME_HEIGHT / TILE_HEIGHT) +#define PLAYER_WIDTH 10 +#define PLAYER_HEIGHT 10 + +typedef struct { + double x; + double y; +} vec; + +typedef struct { + uint32_t *mapped_memory; + int width; + int height; + int bpp; + int scale; + size_t size; +} framebuffer; + +enum tile_type { + TILE_AIR, + TILE_SOLID, + TILE_GRATE, +}; + +typedef struct { + enum tile_type type; +} tile; + +#define CLIENT_BIT (1) +#define SERVER_BIT (2) +#define CLIENT_WAITING_BIT (1) +#define SERVER_WAITING_BIT (2) + +typedef struct { + volatile uint8_t inner; +} spinlock; + +enum key_state { + KEY_STATE_PRESSED, + KEY_STATE_UNPRESSED, + // TODO: JUST_PRESSED and JUST_RELEASED +}; + +typedef struct { + volatile size_t frame; + volatile size_t dummy_counter; + volatile uint8_t client_barrier; + volatile uint8_t server_barrier; + volatile enum key_state key_status[255]; + spinlock player_lock; + volatile vec player_pos; + volatile vec player_vel; + volatile tile tiles[GAME_HEIGHT_TILES * GAME_WIDTH_TILES]; + volatile uint8_t mem[PAGE_SIZE * (SHARED_PAGES - 1)]; +} sharedmem; + +static int display_server_entry(sharedmem *); +static int client_entry(sharedmem *); +static void barrier_wait(sharedmem *, int isclient); +static volatile tile *tile_at(sharedmem *, size_t x, size_t y); +static int is_inbounds(size_t x, size_t y); +static void spinlock_lock(volatile spinlock *lock, int isclient); +static void spinlock_unlock(volatile spinlock *lock, int isclient); + +int main(void) +{ + // static_assert where are you :( + if (SHARED_PAGES * 4096 < sizeof(sharedmem)) { + fprintf(stderr, "bad memory configuration"); + return 1; + } + + int child = fork(); + if (child < 0) { + fprintf(stderr, "fork failed!\n"); + return 1; + } + + if (child) { + sharedmem *shared = allocshared(SHARED_PAGES, child); + if (!shared) { + fprintf(stderr, "memory share failure\n"); + return 1; + } + + return display_server_entry(shared); + } else { + sharedmem *shared; + + while (!(shared = popsharedmem())) + sleep(1); + + return client_entry(shared); + } +} + +static void set_pixel(framebuffer *fb, size_t x, size_t y, + int state) // state is 0 or 1 +{ + const size_t idx = x + y * fb->width; +#ifdef DBG + if (idx > fb->size) { + printf("overflow?\n"); + exit(0); + } +#endif + fb->mapped_memory[idx] = state * (uint32_t)-1; +} + +static void draw_filled(framebuffer *fb, const size_t x, const size_t y, + int state) +{ + for (size_t rx = x * TILE_WIDTH; rx < (x + 1) * TILE_WIDTH; ++rx) { + for (size_t ry = y * TILE_HEIGHT; ry < (y + 1) * TILE_HEIGHT; ++ry) { + set_pixel(fb, rx, ry, state); + } + } +} + +static void draw_grate(framebuffer *fb, const size_t x, const size_t y) +{ + for (size_t rx = x * TILE_WIDTH; rx < (x + 1) * TILE_WIDTH; ++rx) { + for (size_t ry = y * TILE_HEIGHT; ry < (y + 1) * TILE_HEIGHT; ++ry) { + int state; + if (x == y) { + state = 1; + } else { + state = 0; + } + set_pixel(fb, rx, ry, state); + } + } +} + +static void draw_tiles(sharedmem *shared, framebuffer *fb) +{ + for (size_t x = 0; x < GAME_WIDTH_TILES; ++x) { + for (size_t y = 0; y < GAME_HEIGHT_TILES; ++y) { + volatile tile *tile = tile_at(shared, x, y); + switch (tile->type) { + case TILE_AIR: + draw_filled(fb, x, y, 0); + break; + case TILE_SOLID: + draw_filled(fb, x, y, 1); + break; + case TILE_GRATE: + draw_grate(fb, x, y); + break; + } + } + } +} + +static void draw_player(framebuffer *fb, vec pos) +{ + for (size_t x = pos.x; x < ((size_t)pos.x + PLAYER_WIDTH); ++x) { + for (size_t y = pos.y; y < ((size_t)pos.y + PLAYER_WIDTH); ++y) { + set_pixel(fb, x, y, 1); + } + } +} + +static void init_level(sharedmem *shared) +{ + for (size_t i = 0; i < GAME_WIDTH_TILES; ++i) { + tile_at(shared, i, 10)->type = TILE_GRATE; + } + + shared->player_pos = (vec){ .x = 5 * TILE_WIDTH, .y = 5 * TILE_HEIGHT }; +} + +static size_t get_total_time(size_t tick_start) // arbitrary units +{ + // 60 is arbitrary pretend fps + return ((ticks() - tick_start) / (1000 / 60)); +} + +static int display_server_entry(sharedmem *shared) +{ + framebuffer fb; + if (drm((void **)&fb.mapped_memory, &fb.width, &fb.height, &fb.bpp)) { + fprintf(stderr, "Unable to map framebuffer, display server failing\n"); + return 1; + } + + fb.size = (fb.width * fb.height * fb.bpp) / 8; + + barrier_wait(shared, 0); + + while (1) { + struct keycode keycode; + + if (keypoll(&keycode)) { + if (keycode.flags & KC_FLAG_KEY_DOWN) { + shared->key_status[(uint8_t)keycode.key] = KEY_STATE_PRESSED; + } + if (keycode.flags & KC_FLAG_KEY_UP) { + shared->key_status[(uint8_t)keycode.key] = KEY_STATE_UNPRESSED; + } + } + + draw_tiles(shared, &fb); + + spinlock_lock(&shared->player_lock, 0); + draw_player(&fb, shared->player_pos); + spinlock_unlock(&shared->player_lock, 0); + } + + return 0; +} + +static int client_entry(sharedmem *shared) +{ + init_level(shared); + + size_t start_ticks = ticks(); + + double last_time = get_total_time(start_ticks); + + barrier_wait(shared, 1); + do { + double time = get_total_time(start_ticks); + double delta_time = time - last_time; + spinlock_lock(&shared->player_lock, 1); + + shared->player_vel.y -= 9.8 * delta_time; + + // framerate dependent... + const vec drag = { + .x = shared->player_vel.x * 0.1, + .y = shared->player_vel.y * 0.1, + }; + shared->player_vel.x -= drag.x; + shared->player_vel.y -= drag.y; + + for (size_t i = 0; i < shared->player_vel.x; ++i) { + size_t x = shared->player_pos.x + 1; + size_t y = shared->player_pos.y + 1; + // boundscheck to consider outside tiles to be solid + if (is_inbounds(x, y) && tile_at(shared, x, y)->type != TILE_AIR) { + break; + } + shared->player_pos.x += 1; + shared->player_pos.y += 1; + } + + if (shared->key_status[KEY_SPACE] == KEY_STATE_PRESSED) { + shared->player_vel.y = 10; + } else if (shared->key_status[KEY_B] == KEY_STATE_PRESSED) { + shared->player_vel.y = -10; + } + spinlock_unlock(&shared->player_lock, 1); + + } while (1); + + return 0; +} + +static volatile tile *tile_at(sharedmem *shared, size_t x, size_t y) +{ +#ifdef DBG + if (!is_inbounds(x, y)) { + printf("out of bounds"); + exit(0); + } +#endif + const size_t idx = x + (y * GAME_WIDTH_TILES); + return shared->tiles + idx; +} + +static int is_inbounds(size_t x, size_t y) +{ + const size_t idx = x + (y * GAME_WIDTH_TILES); + return idx < (GAME_WIDTH_TILES * GAME_HEIGHT_TILES); +} + +static void spinlock_lock(volatile spinlock *lock, int isclient) +{ + const uint8_t bit = isclient ? CLIENT_BIT : SERVER_BIT; + const uint8_t otherbit = isclient ? SERVER_BIT : CLIENT_BIT; + + // wait for us to be the only waiter + while (1) { + if (lock->inner == 0) { + lock->inner |= bit; + // recover from the possibility that something happened between that + // if statement and the |= operation. check for other bits being set + if ((lock->inner ^ bit) != 0) { + // okay, somebody messed with something, undo what we did and + // keep waiting + lock->inner ^= bit; + continue; + } + + // okay all good, we are the exclusive owner + break; + } + } + +#ifdef DBG + // when we own the lock, only our bit should be active and the other proc's + // bit should not be active + if (lock->inner & otherbit || !(lock->inner & bit)) { + printf("SPINLOCK BAD\n"); + exit(1); + } +#endif +} + +static void spinlock_unlock(volatile spinlock *lock, int isclient) +{ + const uint8_t bit = isclient ? CLIENT_BIT : SERVER_BIT; + + // assume our thread of execution (process) is the only one changing our + // bit. this can be screwed over if the other process lies about whether + // it is server or client + lock->inner ^= bit; +} + +static void barrier_wait(sharedmem *shared, int isclient) +{ + if (isclient) { + if (shared->server_barrier) { + shared->server_barrier = 0; + } else { + shared->client_barrier = 1; + while (shared->client_barrier) + ; + } + } else { + if (shared->client_barrier) { + shared->client_barrier = 0; + } else { + shared->server_barrier = 1; + while (shared->server_barrier) + ; + } + } +} diff --git a/user/include/unistd.h b/user/include/unistd.h index 4f582d2..c54cd20 100644 --- a/user/include/unistd.h +++ b/user/include/unistd.h @@ -15,6 +15,14 @@ /* System Call Definitions */ +// NOTE: needs to match kernel input.h +struct keycode { + char key; + char flags; +}; + +typedef unsigned short pid_t; + enum { S_SET = 0, S_CUR = 1, @@ -185,6 +193,38 @@ extern void *brk(const void *addr); extern void *sbrk(intptr_t increment); /** + * Allocate a number of pages shared with another PID. Does not map the pages + * into the other process's vtable until the other process calls popsharedmem(). + * + * @param num_pages number of pages to allocate + * @param other_pid pid of other process + * @return pointer to the virtual address which will be accessible by both, + * after popsharedmem() is called. + */ +extern void *allocshared(size_t num_pages, int other_pid); + +/** + * Checks if another process has tried to share memory with us, and return it. + * No size information is returned, it is only guaranteed that there is at least + * one page in the shared allocation. To get around this, the sharer can write + * a size number to the start of the first page. + * + * @return page aligned pointer to the start of the shared pages, or NULL if no + * process has tried to share with us, or NULL if we the shared virtual address + * space is already occupied in the caller's pagetable. + */ +extern void *popsharedmem(void); + +/** + * Get the most recent key event, if there is one. + * + * @param poll the keycode to write out to + * @return 0 if there was no key event, in which case `poll` was not changed, or + * 1 if there was an event and the caller should read from `poll`. + */ +extern int keypoll(struct keycode *poll); + +/** * Poweroff the system. * * @return 1 on failure diff --git a/user/lib/syscall.S b/user/lib/syscall.S index 9f7025e..c45f641 100644 --- a/user/lib/syscall.S +++ b/user/lib/syscall.S @@ -30,3 +30,6 @@ SYSCALL poweroff SYS_poweroff SYSCALL drm SYS_drm SYSCALL ticks SYS_ticks SYSCALL seek SYS_seek +SYSCALL allocshared SYS_allocshared +SYSCALL popsharedmem SYS_popsharedmem +SYSCALL keypoll SYS_keypoll |