This commit is contained in:
Emile Clark-Boman 2025-07-22 02:42:34 +10:00
commit b6b7d72717
96 changed files with 37739 additions and 0 deletions

45
Changelog Normal file
View file

@ -0,0 +1,45 @@
2019-12-21:
- added complete JSLinux demo
- RISC-V: added initrd support
- RISC-V: fixed FMIN/FMAX instructions
2018-09-23:
- added support for separate RISC-V BIOS and kernel
2018-09-15:
- renamed to TinyEMU (temu)
- single executable for all emulated machines
2018-08-29:
- compilation fixes
2017-08-06:
- added JSON configuration file
- added graphical display with SDL
- added VirtIO input support
- added PCI bus and VirtIO PCI support
- x86: added IDE, PS/2, vmmouse and VGA devices
- added user mode network interface
2017-06-10:
- RISCV: avoid unnecessary kernel patches
- x86: accept standard kernel images
2017-05-25:
- RISCV: faster emulation (1.4x)
- Support of user level ISA version 2.2 and priviledged architecture
version 1.10
- added small x86 emulator (x86emu) based on KVM
- modified the fs_net network protocol to match the vfsync protocol
- handle console resize
- JS emulator:
- added scrollbar in terminal
- added file import and export
- added copy/paste support

19
MIT-LICENSE.txt Normal file
View file

@ -0,0 +1,19 @@
Copyright (c) 2016-2017 Fabrice Bellard
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

135
Makefile Normal file
View file

@ -0,0 +1,135 @@
#
# TinyEMU
#
# Copyright (c) 2016-2018 Fabrice Bellard
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# if set, network filesystem is enabled. libcurl and libcrypto
# (openssl) must be installed.
CONFIG_FS_NET=y
# SDL support (optional)
CONFIG_SDL=y
# if set, compile the 128 bit emulator. Note: the 128 bit target does
# not compile if gcc does not support the int128 type (32 bit hosts).
CONFIG_INT128=y
# build x86 emulator
CONFIG_X86EMU=y
# win32 build (not usable yet)
#CONFIG_WIN32=y
# user space network redirector
CONFIG_SLIRP=y
ifdef CONFIG_WIN32
CROSS_PREFIX=i686-w64-mingw32-
EXE=.exe
else
CROSS_PREFIX=
EXE=
endif
CC=$(CROSS_PREFIX)gcc
STRIP=$(CROSS_PREFIX)strip
CFLAGS=-O2 -Wall -g -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -MMD
CFLAGS+=-D_GNU_SOURCE -DCONFIG_VERSION=\"$(shell cat VERSION)\"
LDFLAGS=
bindir=/usr/local/bin
INSTALL=install
PROGS+= temu$(EXE)
ifndef CONFIG_WIN32
ifdef CONFIG_FS_NET
PROGS+=build_filelist splitimg
endif
endif
all: $(PROGS)
EMU_OBJS:=virtio.o pci.o fs.o cutils.o iomem.o simplefb.o \
json.o machine.o temu.o
ifdef CONFIG_SLIRP
CFLAGS+=-DCONFIG_SLIRP
EMU_OBJS+=$(addprefix slirp/, bootp.o ip_icmp.o mbuf.o slirp.o tcp_output.o cksum.o ip_input.o misc.o socket.o tcp_subr.o udp.o if.o ip_output.o sbuf.o tcp_input.o tcp_timer.o)
endif
ifndef CONFIG_WIN32
EMU_OBJS+=fs_disk.o
EMU_LIBS=-lrt
endif
ifdef CONFIG_FS_NET
CFLAGS+=-DCONFIG_FS_NET
EMU_OBJS+=fs_net.o fs_wget.o fs_utils.o block_net.o
EMU_LIBS+=-lcurl -lcrypto
ifdef CONFIG_WIN32
EMU_LIBS+=-lwsock32
endif # CONFIG_WIN32
endif # CONFIG_FS_NET
ifdef CONFIG_SDL
EMU_LIBS+=-lSDL
EMU_OBJS+=sdl.o
CFLAGS+=-DCONFIG_SDL
ifdef CONFIG_WIN32
LDFLAGS+=-mwindows
endif
endif
EMU_OBJS+=riscv_machine.o softfp.o riscv_cpu32.o riscv_cpu64.o
ifdef CONFIG_INT128
CFLAGS+=-DCONFIG_RISCV_MAX_XLEN=128
EMU_OBJS+=riscv_cpu128.o
else
CFLAGS+=-DCONFIG_RISCV_MAX_XLEN=64
endif
ifdef CONFIG_X86EMU
CFLAGS+=-DCONFIG_X86EMU
EMU_OBJS+=x86_cpu.o x86_machine.o ide.o ps2.o vmmouse.o pckbd.o vga.o
endif
temu$(EXE): $(EMU_OBJS)
$(CC) $(LDFLAGS) -o $@ $^ $(EMU_LIBS)
riscv_cpu32.o: riscv_cpu.c
$(CC) $(CFLAGS) -DMAX_XLEN=32 -c -o $@ $<
riscv_cpu64.o: riscv_cpu.c
$(CC) $(CFLAGS) -DMAX_XLEN=64 -c -o $@ $<
riscv_cpu128.o: riscv_cpu.c
$(CC) $(CFLAGS) -DMAX_XLEN=128 -c -o $@ $<
build_filelist: build_filelist.o fs_utils.o cutils.o
$(CC) $(LDFLAGS) -o $@ $^ -lm
splitimg: splitimg.o
$(CC) $(LDFLAGS) -o $@ $^
install: $(PROGS)
$(STRIP) $(PROGS)
$(INSTALL) -m755 $(PROGS) "$(DESTDIR)$(bindir)"
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
clean:
rm -f *.o *.d *~ $(PROGS) slirp/*.o slirp/*.d slirp/*~
-include $(wildcard *.d)
-include $(wildcard slirp/*.d)

65
Makefile.js Normal file
View file

@ -0,0 +1,65 @@
#
# TinyEMU emulator
#
# Copyright (c) 2016-2018 Fabrice Bellard
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# Build the Javascript version of TinyEMU
EMCC=emcc
EMCFLAGS=-O2 --llvm-opts 2 -Wall -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -MMD -fno-strict-aliasing -DCONFIG_FS_NET
#EMCFLAGS+=-Werror
EMLDFLAGS=-O3 --memory-init-file 0 --closure 0 -s NO_EXIT_RUNTIME=1 -s NO_FILESYSTEM=1 -s "EXPORTED_FUNCTIONS=['_console_queue_char','_vm_start','_fs_import_file','_display_key_event','_display_mouse_event','_display_wheel_event','_net_write_packet','_net_set_carrier']" -s 'EXTRA_EXPORTED_RUNTIME_METHODS=["ccall", "cwrap"]' -s BINARYEN_TRAP_MODE=clamp --js-library js/lib.js
EMLDFLAGS_ASMJS:=$(EMLDFLAGS) -s WASM=0
EMLDFLAGS_WASM:=$(EMLDFLAGS) -s WASM=1 -s TOTAL_MEMORY=67108864 -s ALLOW_MEMORY_GROWTH=1
PROGS=js/riscvemu32.js js/riscvemu32-wasm.js js/riscvemu64.js js/riscvemu64-wasm.js
all: $(PROGS)
JS_OBJS=jsemu.js.o softfp.js.o virtio.js.o fs.js.o fs_net.js.o fs_wget.js.o fs_utils.js.o simplefb.js.o pci.js.o json.js.o block_net.js.o
JS_OBJS+=iomem.js.o cutils.js.o aes.js.o sha256.js.o
RISCVEMU64_OBJS=$(JS_OBJS) riscv_cpu64.js.o riscv_machine.js.o machine.js.o
RISCVEMU32_OBJS=$(JS_OBJS) riscv_cpu32.js.o riscv_machine.js.o machine.js.o
js/riscvemu64.js: $(RISCVEMU64_OBJS) js/lib.js
$(EMCC) $(EMLDFLAGS_ASMJS) -o $@ $(RISCVEMU64_OBJS)
js/riscvemu32.js: $(RISCVEMU32_OBJS) js/lib.js
$(EMCC) $(EMLDFLAGS_ASMJS) -o $@ $(RISCVEMU32_OBJS)
js/riscvemu64-wasm.js: $(RISCVEMU64_OBJS) js/lib.js
$(EMCC) $(EMLDFLAGS_WASM) -o $@ $(RISCVEMU64_OBJS)
js/riscvemu32-wasm.js: $(RISCVEMU32_OBJS) js/lib.js
$(EMCC) $(EMLDFLAGS_WASM) -o $@ $(RISCVEMU32_OBJS)
riscv_cpu32.js.o: riscv_cpu.c
$(EMCC) $(EMCFLAGS) -DMAX_XLEN=32 -DCONFIG_RISCV_MAX_XLEN=32 -c -o $@ $<
riscv_cpu64.js.o: riscv_cpu.c
$(EMCC) $(EMCFLAGS) -DMAX_XLEN=64 -DCONFIG_RISCV_MAX_XLEN=64 -c -o $@ $<
%.js.o: %.c
$(EMCC) $(EMCFLAGS) -c -o $@ $<
-include $(wildcard *.d)

1
VERSION Normal file
View file

@ -0,0 +1 @@
2019-12-21

1321
aes.c Normal file

File diff suppressed because it is too large Load diff

49
aes.h Normal file
View file

@ -0,0 +1,49 @@
/*
* OpenSSL compatible AES header
*
* Copyright (c) 2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef AES_H
#define AES_H
#define AES_MAXNR 14
#define AES_BLOCK_SIZE 16
struct aes_key_st {
uint32_t rd_key[4 *(AES_MAXNR + 1)];
int rounds;
};
typedef struct aes_key_st AES_KEY;
int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
AES_KEY *key);
int AES_set_decrypt_key(const unsigned char *userKey, const int bits,
AES_KEY *key);
void AES_encrypt(const unsigned char *in, unsigned char *out,
const AES_KEY *key);
void AES_decrypt(const unsigned char *in, unsigned char *out,
const AES_KEY *key);
void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
const unsigned long length, const AES_KEY *key,
unsigned char *ivec, const int enc);
#endif

531
block_net.c Normal file
View file

@ -0,0 +1,531 @@
/*
* HTTP block device
*
* Copyright (c) 2016-2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include "cutils.h"
#include "virtio.h"
#include "fs_wget.h"
#include "list.h"
#include "fbuf.h"
#include "machine.h"
typedef enum {
CBLOCK_LOADING,
CBLOCK_LOADED,
} CachedBlockStateEnum;
typedef struct CachedBlock {
struct list_head link;
struct BlockDeviceHTTP *bf;
unsigned int block_num;
CachedBlockStateEnum state;
FileBuffer fbuf;
} CachedBlock;
#define BLK_FMT "%sblk%09u.bin"
#define GROUP_FMT "%sgrp%09u.bin"
#define PREFETCH_GROUP_LEN_MAX 32
typedef struct {
struct BlockDeviceHTTP *bf;
int group_num;
int n_block_num;
CachedBlock *tab_block[PREFETCH_GROUP_LEN_MAX];
} PrefetchGroupRequest;
/* modified data is stored per cluster (smaller than cached blocks to
avoid losing space) */
typedef struct Cluster {
FileBuffer fbuf;
} Cluster;
typedef struct BlockDeviceHTTP {
BlockDevice *bs;
int max_cache_size_kb;
char url[1024];
int prefetch_count;
void (*start_cb)(void *opaque);
void *start_opaque;
int64_t nb_sectors;
int block_size; /* in sectors, power of two */
int nb_blocks;
struct list_head cached_blocks; /* list of CachedBlock */
int n_cached_blocks;
int n_cached_blocks_max;
/* write support */
int sectors_per_cluster; /* power of two */
Cluster **clusters; /* NULL if no written data */
int n_clusters;
int n_allocated_clusters;
/* statistics */
int64_t n_read_sectors;
int64_t n_read_blocks;
int64_t n_write_sectors;
/* current read request */
BOOL is_write;
uint64_t sector_num;
int cur_block_num;
int sector_index, sector_count;
BlockDeviceCompletionFunc *cb;
void *opaque;
uint8_t *io_buf;
/* prefetch */
int prefetch_group_len;
} BlockDeviceHTTP;
static void bf_update_block(CachedBlock *b, const uint8_t *data);
static void bf_read_onload(void *opaque, int err, void *data, size_t size);
static void bf_init_onload(void *opaque, int err, void *data, size_t size);
static void bf_prefetch_group_onload(void *opaque, int err, void *data,
size_t size);
static CachedBlock *bf_find_block(BlockDeviceHTTP *bf, unsigned int block_num)
{
CachedBlock *b;
struct list_head *el;
list_for_each(el, &bf->cached_blocks) {
b = list_entry(el, CachedBlock, link);
if (b->block_num == block_num) {
/* move to front */
if (bf->cached_blocks.next != el) {
list_del(&b->link);
list_add(&b->link, &bf->cached_blocks);
}
return b;
}
}
return NULL;
}
static void bf_free_block(BlockDeviceHTTP *bf, CachedBlock *b)
{
bf->n_cached_blocks--;
file_buffer_reset(&b->fbuf);
list_del(&b->link);
free(b);
}
static CachedBlock *bf_add_block(BlockDeviceHTTP *bf, unsigned int block_num)
{
CachedBlock *b;
if (bf->n_cached_blocks >= bf->n_cached_blocks_max) {
struct list_head *el, *el1;
/* start by looking at the least unused blocks */
list_for_each_prev_safe(el, el1, &bf->cached_blocks) {
b = list_entry(el, CachedBlock, link);
if (b->state == CBLOCK_LOADED) {
bf_free_block(bf, b);
if (bf->n_cached_blocks < bf->n_cached_blocks_max)
break;
}
}
}
b = mallocz(sizeof(CachedBlock));
b->bf = bf;
b->block_num = block_num;
b->state = CBLOCK_LOADING;
file_buffer_init(&b->fbuf);
file_buffer_resize(&b->fbuf, bf->block_size * 512);
list_add(&b->link, &bf->cached_blocks);
bf->n_cached_blocks++;
return b;
}
static int64_t bf_get_sector_count(BlockDevice *bs)
{
BlockDeviceHTTP *bf = bs->opaque;
return bf->nb_sectors;
}
static void bf_start_load_block(BlockDevice *bs, int block_num)
{
BlockDeviceHTTP *bf = bs->opaque;
char filename[1024];
CachedBlock *b;
b = bf_add_block(bf, block_num);
bf->n_read_blocks++;
/* make a XHR to read the block */
#if 0
printf("%u,\n", block_num);
#endif
#if 0
printf("load_blk=%d cached=%d read=%d KB (%d KB) write=%d KB (%d KB)\n",
block_num, bf->n_cached_blocks,
(int)(bf->n_read_sectors / 2),
(int)(bf->n_read_blocks * bf->block_size / 2),
(int)(bf->n_write_sectors / 2),
(int)(bf->n_allocated_clusters * bf->sectors_per_cluster / 2));
#endif
snprintf(filename, sizeof(filename), BLK_FMT, bf->url, block_num);
// printf("wget %s\n", filename);
fs_wget(filename, NULL, NULL, b, bf_read_onload, TRUE);
}
static void bf_start_load_prefetch_group(BlockDevice *bs, int group_num,
const int *tab_block_num,
int n_block_num)
{
BlockDeviceHTTP *bf = bs->opaque;
CachedBlock *b;
PrefetchGroupRequest *req;
char filename[1024];
BOOL req_flag;
int i;
req_flag = FALSE;
req = malloc(sizeof(*req));
req->bf = bf;
req->group_num = group_num;
req->n_block_num = n_block_num;
for(i = 0; i < n_block_num; i++) {
b = bf_find_block(bf, tab_block_num[i]);
if (!b) {
b = bf_add_block(bf, tab_block_num[i]);
req_flag = TRUE;
} else {
/* no need to read the block if it is already loading or
loaded */
b = NULL;
}
req->tab_block[i] = b;
}
if (req_flag) {
snprintf(filename, sizeof(filename), GROUP_FMT, bf->url, group_num);
// printf("wget %s\n", filename);
fs_wget(filename, NULL, NULL, req, bf_prefetch_group_onload, TRUE);
/* XXX: should add request in a list to free it for clean exit */
} else {
free(req);
}
}
static void bf_prefetch_group_onload(void *opaque, int err, void *data,
size_t size)
{
PrefetchGroupRequest *req = opaque;
BlockDeviceHTTP *bf = req->bf;
CachedBlock *b;
int block_bytes, i;
if (err < 0) {
fprintf(stderr, "Could not load group %u\n", req->group_num);
exit(1);
}
block_bytes = bf->block_size * 512;
assert(size == block_bytes * req->n_block_num);
for(i = 0; i < req->n_block_num; i++) {
b = req->tab_block[i];
if (b) {
bf_update_block(b, (const uint8_t *)data + block_bytes * i);
}
}
free(req);
}
static int bf_rw_async1(BlockDevice *bs, BOOL is_sync)
{
BlockDeviceHTTP *bf = bs->opaque;
int offset, block_num, n, cluster_num;
CachedBlock *b;
Cluster *c;
for(;;) {
n = bf->sector_count - bf->sector_index;
if (n == 0)
break;
cluster_num = bf->sector_num / bf->sectors_per_cluster;
c = bf->clusters[cluster_num];
if (c) {
offset = bf->sector_num % bf->sectors_per_cluster;
n = min_int(n, bf->sectors_per_cluster - offset);
if (bf->is_write) {
file_buffer_write(&c->fbuf, offset * 512,
bf->io_buf + bf->sector_index * 512, n * 512);
} else {
file_buffer_read(&c->fbuf, offset * 512,
bf->io_buf + bf->sector_index * 512, n * 512);
}
bf->sector_index += n;
bf->sector_num += n;
} else {
block_num = bf->sector_num / bf->block_size;
offset = bf->sector_num % bf->block_size;
n = min_int(n, bf->block_size - offset);
bf->cur_block_num = block_num;
b = bf_find_block(bf, block_num);
if (b) {
if (b->state == CBLOCK_LOADING) {
/* wait until the block is loaded */
return 1;
} else {
if (bf->is_write) {
int cluster_size, cluster_offset;
uint8_t *buf;
/* allocate a new cluster */
c = mallocz(sizeof(Cluster));
cluster_size = bf->sectors_per_cluster * 512;
buf = malloc(cluster_size);
file_buffer_init(&c->fbuf);
file_buffer_resize(&c->fbuf, cluster_size);
bf->clusters[cluster_num] = c;
/* copy the cached block data to the cluster */
cluster_offset = (cluster_num * bf->sectors_per_cluster) &
(bf->block_size - 1);
file_buffer_read(&b->fbuf, cluster_offset * 512,
buf, cluster_size);
file_buffer_write(&c->fbuf, 0, buf, cluster_size);
free(buf);
bf->n_allocated_clusters++;
continue; /* write to the allocated cluster */
} else {
file_buffer_read(&b->fbuf, offset * 512,
bf->io_buf + bf->sector_index * 512, n * 512);
}
bf->sector_index += n;
bf->sector_num += n;
}
} else {
bf_start_load_block(bs, block_num);
return 1;
}
bf->cur_block_num = -1;
}
}
if (!is_sync) {
// printf("end of request\n");
/* end of request */
bf->cb(bf->opaque, 0);
}
return 0;
}
static void bf_update_block(CachedBlock *b, const uint8_t *data)
{
BlockDeviceHTTP *bf = b->bf;
BlockDevice *bs = bf->bs;
assert(b->state == CBLOCK_LOADING);
file_buffer_write(&b->fbuf, 0, data, bf->block_size * 512);
b->state = CBLOCK_LOADED;
/* continue I/O read/write if necessary */
if (b->block_num == bf->cur_block_num) {
bf_rw_async1(bs, FALSE);
}
}
static void bf_read_onload(void *opaque, int err, void *data, size_t size)
{
CachedBlock *b = opaque;
BlockDeviceHTTP *bf = b->bf;
if (err < 0) {
fprintf(stderr, "Could not load block %u\n", b->block_num);
exit(1);
}
assert(size == bf->block_size * 512);
bf_update_block(b, data);
}
static int bf_read_async(BlockDevice *bs,
uint64_t sector_num, uint8_t *buf, int n,
BlockDeviceCompletionFunc *cb, void *opaque)
{
BlockDeviceHTTP *bf = bs->opaque;
// printf("bf_read_async: sector_num=%" PRId64 " n=%d\n", sector_num, n);
bf->is_write = FALSE;
bf->sector_num = sector_num;
bf->io_buf = buf;
bf->sector_count = n;
bf->sector_index = 0;
bf->cb = cb;
bf->opaque = opaque;
bf->n_read_sectors += n;
return bf_rw_async1(bs, TRUE);
}
static int bf_write_async(BlockDevice *bs,
uint64_t sector_num, const uint8_t *buf, int n,
BlockDeviceCompletionFunc *cb, void *opaque)
{
BlockDeviceHTTP *bf = bs->opaque;
// printf("bf_write_async: sector_num=%" PRId64 " n=%d\n", sector_num, n);
bf->is_write = TRUE;
bf->sector_num = sector_num;
bf->io_buf = (uint8_t *)buf;
bf->sector_count = n;
bf->sector_index = 0;
bf->cb = cb;
bf->opaque = opaque;
bf->n_write_sectors += n;
return bf_rw_async1(bs, TRUE);
}
BlockDevice *block_device_init_http(const char *url,
int max_cache_size_kb,
void (*start_cb)(void *opaque),
void *start_opaque)
{
BlockDevice *bs;
BlockDeviceHTTP *bf;
char *p;
bs = mallocz(sizeof(*bs));
bf = mallocz(sizeof(*bf));
strcpy(bf->url, url);
/* get the path with the trailing '/' */
p = strrchr(bf->url, '/');
if (!p)
p = bf->url;
else
p++;
*p = '\0';
init_list_head(&bf->cached_blocks);
bf->max_cache_size_kb = max_cache_size_kb;
bf->start_cb = start_cb;
bf->start_opaque = start_opaque;
bf->bs = bs;
bs->opaque = bf;
bs->get_sector_count = bf_get_sector_count;
bs->read_async = bf_read_async;
bs->write_async = bf_write_async;
fs_wget(url, NULL, NULL, bs, bf_init_onload, TRUE);
return bs;
}
static void bf_init_onload(void *opaque, int err, void *data, size_t size)
{
BlockDevice *bs = opaque;
BlockDeviceHTTP *bf = bs->opaque;
int block_size_kb, block_num;
JSONValue cfg, array;
if (err < 0) {
fprintf(stderr, "Could not load block device file (err=%d)\n", -err);
exit(1);
}
/* parse the disk image info */
cfg = json_parse_value_len(data, size);
if (json_is_error(cfg)) {
vm_error("error: %s\n", json_get_error(cfg));
config_error:
json_free(cfg);
exit(1);
}
if (vm_get_int(cfg, "block_size", &block_size_kb) < 0)
goto config_error;
bf->block_size = block_size_kb * 2;
if (bf->block_size <= 0 ||
(bf->block_size & (bf->block_size - 1)) != 0) {
vm_error("invalid block_size\n");
goto config_error;
}
if (vm_get_int(cfg, "n_block", &bf->nb_blocks) < 0)
goto config_error;
if (bf->nb_blocks <= 0) {
vm_error("invalid n_block\n");
goto config_error;
}
bf->nb_sectors = bf->block_size * (uint64_t)bf->nb_blocks;
bf->n_cached_blocks = 0;
bf->n_cached_blocks_max = max_int(1, bf->max_cache_size_kb / block_size_kb);
bf->cur_block_num = -1; /* no request in progress */
bf->sectors_per_cluster = 8; /* 4 KB */
bf->n_clusters = (bf->nb_sectors + bf->sectors_per_cluster - 1) / bf->sectors_per_cluster;
bf->clusters = mallocz(sizeof(bf->clusters[0]) * bf->n_clusters);
if (vm_get_int_opt(cfg, "prefetch_group_len",
&bf->prefetch_group_len, 1) < 0)
goto config_error;
if (bf->prefetch_group_len > PREFETCH_GROUP_LEN_MAX) {
vm_error("prefetch_group_len is too large");
goto config_error;
}
array = json_object_get(cfg, "prefetch");
if (!json_is_undefined(array)) {
int idx, prefetch_len, l, i;
JSONValue el;
int tab_block_num[PREFETCH_GROUP_LEN_MAX];
if (array.type != JSON_ARRAY) {
vm_error("expecting an array\n");
goto config_error;
}
prefetch_len = array.u.array->len;
idx = 0;
while (idx < prefetch_len) {
l = min_int(prefetch_len - idx, bf->prefetch_group_len);
for(i = 0; i < l; i++) {
el = json_array_get(array, idx + i);
if (el.type != JSON_INT) {
vm_error("expecting an integer\n");
goto config_error;
}
tab_block_num[i] = el.u.int32;
}
if (l == 1) {
block_num = tab_block_num[0];
if (!bf_find_block(bf, block_num)) {
bf_start_load_block(bs, block_num);
}
} else {
bf_start_load_prefetch_group(bs, idx / bf->prefetch_group_len,
tab_block_num, l);
}
idx += l;
}
}
json_free(cfg);
if (bf->start_cb) {
bf->start_cb(bf->start_opaque);
}
}

311
build_filelist.c Normal file
View file

@ -0,0 +1,311 @@
/*
* File list builder for RISCVEMU network filesystem
*
* Copyright (c) 2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include <stdarg.h>
#include <sys/statfs.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <errno.h>
#include <sys/sysmacros.h>
#include "cutils.h"
#include "fs_utils.h"
void print_str(FILE *f, const char *str)
{
const char *s;
int c;
s = str;
while (*s != '\0') {
if (*s <= ' ' || *s > '~')
goto use_quote;
s++;
}
fputs(str, f);
return;
use_quote:
s = str;
fputc('"', f);
while (*s != '\0') {
c = *(uint8_t *)s;
if (c < ' ' || c == 127) {
fprintf(f, "\\x%02x", c);
} else if (c == '\\' || c == '\"') {
fprintf(f, "\\%c", c);
} else {
fputc(c, f);
}
s++;
}
fputc('"', f);
}
#define COPY_BUF_LEN (1024 * 1024)
static void copy_file(const char *src_filename, const char *dst_filename)
{
uint8_t *buf;
FILE *fi, *fo;
int len;
buf = malloc(COPY_BUF_LEN);
fi = fopen(src_filename, "rb");
if (!fi) {
perror(src_filename);
exit(1);
}
fo = fopen(dst_filename, "wb");
if (!fo) {
perror(dst_filename);
exit(1);
}
for(;;) {
len = fread(buf, 1, COPY_BUF_LEN, fi);
if (len == 0)
break;
fwrite(buf, 1, len, fo);
}
fclose(fo);
fclose(fi);
}
typedef struct {
char *files_path;
uint64_t next_inode_num;
uint64_t fs_size;
uint64_t fs_max_size;
FILE *f;
} ScanState;
static void add_file_size(ScanState *s, uint64_t size)
{
s->fs_size += block_align(size, FS_BLOCK_SIZE);
if (s->fs_size > s->fs_max_size) {
fprintf(stderr, "Filesystem Quota exceeded (%" PRId64 " bytes)\n", s->fs_max_size);
exit(1);
}
}
void scan_dir(ScanState *s, const char *path)
{
FILE *f = s->f;
DIR *dirp;
struct dirent *de;
const char *name;
struct stat st;
char *path1;
uint32_t mode, v;
dirp = opendir(path);
if (!dirp) {
perror(path);
exit(1);
}
for(;;) {
de = readdir(dirp);
if (!de)
break;
name = de->d_name;
if (!strcmp(name, ".") || !strcmp(name, ".."))
continue;
path1 = compose_path(path, name);
if (lstat(path1, &st) < 0) {
perror(path1);
exit(1);
}
mode = st.st_mode & 0xffff;
fprintf(f, "%06o %u %u",
mode,
(int)st.st_uid,
(int)st.st_gid);
if (S_ISCHR(mode) || S_ISBLK(mode)) {
fprintf(f, " %u %u",
(int)major(st.st_rdev),
(int)minor(st.st_rdev));
}
if (S_ISREG(mode)) {
fprintf(f, " %" PRIu64, st.st_size);
}
/* modification time (at most ms resolution) */
fprintf(f, " %u", (int)st.st_mtim.tv_sec);
v = st.st_mtim.tv_nsec;
if (v != 0) {
fprintf(f, ".");
while (v != 0) {
fprintf(f, "%u", v / 100000000);
v = (v % 100000000) * 10;
}
}
fprintf(f, " ");
print_str(f, name);
if (S_ISLNK(mode)) {
char buf[1024];
int len;
len = readlink(path1, buf, sizeof(buf) - 1);
if (len < 0) {
perror("readlink");
exit(1);
}
buf[len] = '\0';
fprintf(f, " ");
print_str(f, buf);
} else if (S_ISREG(mode) && st.st_size > 0) {
char buf1[FILEID_SIZE_MAX], *fname;
FSFileID file_id;
file_id = s->next_inode_num++;
fprintf(f, " %" PRIx64, file_id);
file_id_to_filename(buf1, file_id);
fname = compose_path(s->files_path, buf1);
copy_file(path1, fname);
add_file_size(s, st.st_size);
}
fprintf(f, "\n");
if (S_ISDIR(mode)) {
scan_dir(s, path1);
}
free(path1);
}
closedir(dirp);
fprintf(f, ".\n"); /* end of directory */
}
void help(void)
{
printf("usage: build_filelist [options] source_path dest_path\n"
"\n"
"Options:\n"
"-m size_mb set the max filesystem size in MiB\n");
exit(1);
}
#define LOCK_FILENAME "lock"
int main(int argc, char **argv)
{
const char *dst_path, *src_path;
ScanState s_s, *s = &s_s;
FILE *f;
char *filename;
FSFileID root_id;
char fname[FILEID_SIZE_MAX];
struct stat st;
uint64_t first_inode, fs_max_size;
int c;
first_inode = 1;
fs_max_size = (uint64_t)1 << 30;
for(;;) {
c = getopt(argc, argv, "hi:m:");
if (c == -1)
break;
switch(c) {
case 'h':
help();
case 'i':
first_inode = strtoul(optarg, NULL, 0);
break;
case 'm':
fs_max_size = (uint64_t)strtoul(optarg, NULL, 0) << 20;
break;
default:
exit(1);
}
}
if (optind + 1 >= argc)
help();
src_path = argv[optind];
dst_path = argv[optind + 1];
mkdir(dst_path, 0755);
s->files_path = compose_path(dst_path, ROOT_FILENAME);
s->next_inode_num = first_inode;
s->fs_size = 0;
s->fs_max_size = fs_max_size;
mkdir(s->files_path, 0755);
root_id = s->next_inode_num++;
file_id_to_filename(fname, root_id);
filename = compose_path(s->files_path, fname);
f = fopen(filename, "wb");
if (!f) {
perror(filename);
exit(1);
}
fprintf(f, "Version: 1\n");
fprintf(f, "Revision: 1\n");
fprintf(f, "\n");
s->f = f;
scan_dir(s, src_path);
fclose(f);
/* take into account the filelist size */
if (stat(filename, &st) < 0) {
perror(filename);
exit(1);
}
add_file_size(s, st.st_size);
free(filename);
filename = compose_path(dst_path, HEAD_FILENAME);
f = fopen(filename, "wb");
if (!f) {
perror(filename);
exit(1);
}
fprintf(f, "Version: 1\n");
fprintf(f, "Revision: 1\n");
fprintf(f, "NextFileID: %" PRIx64 "\n", s->next_inode_num);
fprintf(f, "FSFileCount: %" PRIu64 "\n", s->next_inode_num - 1);
fprintf(f, "FSSize: %" PRIu64 "\n", s->fs_size);
fprintf(f, "FSMaxSize: %" PRIu64 "\n", s->fs_max_size);
fprintf(f, "Key:\n"); /* not encrypted */
fprintf(f, "RootID: %" PRIx64 "\n", root_id);
fclose(f);
free(filename);
filename = compose_path(dst_path, LOCK_FILENAME);
f = fopen(filename, "wb");
if (!f) {
perror(filename);
exit(1);
}
fclose(f);
free(filename);
return 0;
}

120
cutils.c Normal file
View file

@ -0,0 +1,120 @@
/*
* Misc C utilities
*
* Copyright (c) 2016-2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include <stdarg.h>
#include <sys/time.h>
#include <ctype.h>
#include "cutils.h"
void *mallocz(size_t size)
{
void *ptr;
ptr = malloc(size);
if (!ptr)
return NULL;
memset(ptr, 0, size);
return ptr;
}
void pstrcpy(char *buf, int buf_size, const char *str)
{
int c;
char *q = buf;
if (buf_size <= 0)
return;
for(;;) {
c = *str++;
if (c == 0 || q >= buf + buf_size - 1)
break;
*q++ = c;
}
*q = '\0';
}
char *pstrcat(char *buf, int buf_size, const char *s)
{
int len;
len = strlen(buf);
if (len < buf_size)
pstrcpy(buf + len, buf_size - len, s);
return buf;
}
int strstart(const char *str, const char *val, const char **ptr)
{
const char *p, *q;
p = str;
q = val;
while (*q != '\0') {
if (*p != *q)
return 0;
p++;
q++;
}
if (ptr)
*ptr = p;
return 1;
}
void dbuf_init(DynBuf *s)
{
memset(s, 0, sizeof(*s));
}
void dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len)
{
size_t end, new_size;
new_size = end = offset + len;
if (new_size > s->allocated_size) {
new_size = max_int(new_size, s->allocated_size * 3 / 2);
s->buf = realloc(s->buf, new_size);
s->allocated_size = new_size;
}
memcpy(s->buf + offset, data, len);
if (end > s->size)
s->size = end;
}
void dbuf_putc(DynBuf *s, uint8_t c)
{
dbuf_write(s, s->size, &c, 1);
}
void dbuf_putstr(DynBuf *s, const char *str)
{
dbuf_write(s, s->size, (const uint8_t *)str, strlen(str));
}
void dbuf_free(DynBuf *s)
{
free(s->buf);
memset(s, 0, sizeof(*s));
}

194
cutils.h Normal file
View file

@ -0,0 +1,194 @@
/*
* C utilities
*
* Copyright (c) 2016 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef CUTILS_H
#define CUTILS_H
#include <inttypes.h>
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#define force_inline inline __attribute__((always_inline))
#define no_inline __attribute__((noinline))
#define __maybe_unused __attribute__((unused))
#define xglue(x, y) x ## y
#define glue(x, y) xglue(x, y)
#define stringify(s) tostring(s)
#define tostring(s) #s
#ifndef offsetof
#define offsetof(type, field) ((size_t) &((type *)0)->field)
#endif
#define countof(x) (sizeof(x) / sizeof(x[0]))
#define DLL_PUBLIC __attribute__ ((visibility ("default")))
#ifndef _BOOL_defined
#define _BOOL_defined
#undef FALSE
#undef TRUE
typedef int BOOL;
enum {
FALSE = 0,
TRUE = 1,
};
#endif
/* this test works at least with gcc */
#if defined(__SIZEOF_INT128__)
#define HAVE_INT128
#endif
#ifdef HAVE_INT128
typedef __int128 int128_t;
typedef unsigned __int128 uint128_t;
#endif
static inline int max_int(int a, int b)
{
if (a > b)
return a;
else
return b;
}
static inline int min_int(int a, int b)
{
if (a < b)
return a;
else
return b;
}
void *mallocz(size_t size);
#if defined(_WIN32)
static inline uint32_t bswap_32(uint32_t v)
{
return ((v & 0xff000000) >> 24) | ((v & 0x00ff0000) >> 8) |
((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24);
}
#else
#include <byteswap.h>
#endif
static inline uint16_t get_le16(const uint8_t *ptr)
{
return ptr[0] | (ptr[1] << 8);
}
static inline uint32_t get_le32(const uint8_t *ptr)
{
return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
}
static inline uint64_t get_le64(const uint8_t *ptr)
{
return get_le32(ptr) | ((uint64_t)get_le32(ptr + 4) << 32);
}
static inline void put_le16(uint8_t *ptr, uint16_t v)
{
ptr[0] = v;
ptr[1] = v >> 8;
}
static inline void put_le32(uint8_t *ptr, uint32_t v)
{
ptr[0] = v;
ptr[1] = v >> 8;
ptr[2] = v >> 16;
ptr[3] = v >> 24;
}
static inline void put_le64(uint8_t *ptr, uint64_t v)
{
put_le32(ptr, v);
put_le32(ptr + 4, v >> 32);
}
static inline uint32_t get_be32(const uint8_t *d)
{
return (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3];
}
static inline void put_be32(uint8_t *d, uint32_t v)
{
d[0] = v >> 24;
d[1] = v >> 16;
d[2] = v >> 8;
d[3] = v >> 0;
}
static inline void put_be64(uint8_t *d, uint64_t v)
{
put_be32(d, v >> 32);
put_be32(d + 4, v);
}
#ifdef WORDS_BIGENDIAN
static inline uint32_t cpu_to_be32(uint32_t v)
{
return v;
}
#else
static inline uint32_t cpu_to_be32(uint32_t v)
{
return bswap_32(v);
}
#endif
/* XXX: optimize */
static inline int ctz32(uint32_t a)
{
int i;
if (a == 0)
return 32;
for(i = 0; i < 32; i++) {
if ((a >> i) & 1)
return i;
}
return 32;
}
void *mallocz(size_t size);
void pstrcpy(char *buf, int buf_size, const char *str);
char *pstrcat(char *buf, int buf_size, const char *s);
int strstart(const char *str, const char *val, const char **ptr);
typedef struct {
uint8_t *buf;
size_t size;
size_t allocated_size;
} DynBuf;
void dbuf_init(DynBuf *s);
void dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len);
void dbuf_putc(DynBuf *s, uint8_t c);
void dbuf_putstr(DynBuf *s, const char *str);
void dbuf_free(DynBuf *s);
#endif /* CUTILS_H */

22
fbuf.h Normal file
View file

@ -0,0 +1,22 @@
#ifndef FBUF_H
#define FBUF_H
typedef struct {
#if defined(EMSCRIPTEN)
int handle;
#else
uint8_t *data;
#endif
size_t allocated_size;
} FileBuffer;
void file_buffer_init(FileBuffer *bs);
void file_buffer_reset(FileBuffer *bs);
int file_buffer_resize(FileBuffer *bs, size_t new_size);
void file_buffer_write(FileBuffer *bs, size_t offset, const uint8_t *buf,
size_t size);
void file_buffer_set(FileBuffer *bs, size_t offset, int val, size_t size);
void file_buffer_read(FileBuffer *bs, size_t offset, uint8_t *buf,
size_t size);
#endif /* FBUF_H */

104
fs.c Normal file
View file

@ -0,0 +1,104 @@
/*
* Filesystem utilities
*
* Copyright (c) 2016 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include <stdarg.h>
#include "cutils.h"
#include "fs.h"
FSFile *fs_dup(FSDevice *fs, FSFile *f)
{
FSQID qid;
fs->fs_walk(fs, &f, &qid, f, 0, NULL);
return f;
}
FSFile *fs_walk_path1(FSDevice *fs, FSFile *f, const char *path,
char **pname)
{
const char *p;
char *name;
FSFile *f1;
FSQID qid;
int len, ret;
BOOL is_last, is_first;
if (path[0] == '/')
path++;
is_first = TRUE;
for(;;) {
p = strchr(path, '/');
if (!p) {
name = (char *)path;
if (pname) {
*pname = name;
if (is_first) {
ret = fs->fs_walk(fs, &f, &qid, f, 0, NULL);
if (ret < 0)
f = NULL;
}
return f;
}
is_last = TRUE;
} else {
len = p - path;
name = malloc(len + 1);
memcpy(name, path, len);
name[len] = '\0';
is_last = FALSE;
}
ret = fs->fs_walk(fs, &f1, &qid, f, 1, &name);
if (!is_last)
free(name);
if (!is_first)
fs->fs_delete(fs, f);
f = f1;
is_first = FALSE;
if (ret <= 0) {
fs->fs_delete(fs, f);
f = NULL;
break;
} else if (is_last) {
break;
}
path = p + 1;
}
return f;
}
FSFile *fs_walk_path(FSDevice *fs, FSFile *f, const char *path)
{
return fs_walk_path1(fs, f, path, NULL);
}
void fs_end(FSDevice *fs)
{
fs->fs_end(fs);
free(fs);
}

211
fs.h Normal file
View file

@ -0,0 +1,211 @@
/*
* Filesystem abstraction
*
* Copyright (c) 2016 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/* FSQID.type */
#define P9_QTDIR 0x80
#define P9_QTAPPEND 0x40
#define P9_QTEXCL 0x20
#define P9_QTMOUNT 0x10
#define P9_QTAUTH 0x08
#define P9_QTTMP 0x04
#define P9_QTSYMLINK 0x02
#define P9_QTLINK 0x01
#define P9_QTFILE 0x00
/* mode bits */
#define P9_S_IRWXUGO 0x01FF
#define P9_S_ISVTX 0x0200
#define P9_S_ISGID 0x0400
#define P9_S_ISUID 0x0800
#define P9_S_IFMT 0xF000
#define P9_S_IFIFO 0x1000
#define P9_S_IFCHR 0x2000
#define P9_S_IFDIR 0x4000
#define P9_S_IFBLK 0x6000
#define P9_S_IFREG 0x8000
#define P9_S_IFLNK 0xA000
#define P9_S_IFSOCK 0xC000
/* flags for lopen()/lcreate() */
#define P9_O_RDONLY 0x00000000
#define P9_O_WRONLY 0x00000001
#define P9_O_RDWR 0x00000002
#define P9_O_NOACCESS 0x00000003
#define P9_O_CREAT 0x00000040
#define P9_O_EXCL 0x00000080
#define P9_O_NOCTTY 0x00000100
#define P9_O_TRUNC 0x00000200
#define P9_O_APPEND 0x00000400
#define P9_O_NONBLOCK 0x00000800
#define P9_O_DSYNC 0x00001000
#define P9_O_FASYNC 0x00002000
#define P9_O_DIRECT 0x00004000
#define P9_O_LARGEFILE 0x00008000
#define P9_O_DIRECTORY 0x00010000
#define P9_O_NOFOLLOW 0x00020000
#define P9_O_NOATIME 0x00040000
#define P9_O_CLOEXEC 0x00080000
#define P9_O_SYNC 0x00100000
/* for fs_setattr() */
#define P9_SETATTR_MODE 0x00000001
#define P9_SETATTR_UID 0x00000002
#define P9_SETATTR_GID 0x00000004
#define P9_SETATTR_SIZE 0x00000008
#define P9_SETATTR_ATIME 0x00000010
#define P9_SETATTR_MTIME 0x00000020
#define P9_SETATTR_CTIME 0x00000040
#define P9_SETATTR_ATIME_SET 0x00000080
#define P9_SETATTR_MTIME_SET 0x00000100
#define P9_EPERM 1
#define P9_ENOENT 2
#define P9_EIO 5
#define P9_EEXIST 17
#define P9_ENOTDIR 20
#define P9_EINVAL 22
#define P9_ENOSPC 28
#define P9_ENOTEMPTY 39
#define P9_EPROTO 71
#define P9_ENOTSUP 524
typedef struct FSDevice FSDevice;
typedef struct FSFile FSFile;
typedef struct {
uint32_t f_bsize;
uint64_t f_blocks;
uint64_t f_bfree;
uint64_t f_bavail;
uint64_t f_files;
uint64_t f_ffree;
} FSStatFS;
typedef struct {
uint8_t type; /* P9_IFx */
uint32_t version;
uint64_t path;
} FSQID;
typedef struct {
FSQID qid;
uint32_t st_mode;
uint32_t st_uid;
uint32_t st_gid;
uint64_t st_nlink;
uint64_t st_rdev;
uint64_t st_size;
uint64_t st_blksize;
uint64_t st_blocks;
uint64_t st_atime_sec;
uint32_t st_atime_nsec;
uint64_t st_mtime_sec;
uint32_t st_mtime_nsec;
uint64_t st_ctime_sec;
uint32_t st_ctime_nsec;
} FSStat;
#define P9_LOCK_TYPE_RDLCK 0
#define P9_LOCK_TYPE_WRLCK 1
#define P9_LOCK_TYPE_UNLCK 2
#define P9_LOCK_FLAGS_BLOCK 1
#define P9_LOCK_FLAGS_RECLAIM 2
#define P9_LOCK_SUCCESS 0
#define P9_LOCK_BLOCKED 1
#define P9_LOCK_ERROR 2
#define P9_LOCK_GRACE 3
#define FSCMD_NAME ".fscmd"
typedef struct {
uint8_t type;
uint32_t flags;
uint64_t start;
uint64_t length;
uint32_t proc_id;
char *client_id;
} FSLock;
typedef void FSOpenCompletionFunc(FSDevice *fs, FSQID *qid, int err,
void *opaque);
struct FSDevice {
void (*fs_end)(FSDevice *s);
void (*fs_delete)(FSDevice *s, FSFile *f);
void (*fs_statfs)(FSDevice *fs, FSStatFS *st);
int (*fs_attach)(FSDevice *fs, FSFile **pf, FSQID *qid, uint32_t uid,
const char *uname, const char *aname);
int (*fs_walk)(FSDevice *fs, FSFile **pf, FSQID *qids,
FSFile *f, int n, char **names);
int (*fs_mkdir)(FSDevice *fs, FSQID *qid, FSFile *f,
const char *name, uint32_t mode, uint32_t gid);
int (*fs_open)(FSDevice *fs, FSQID *qid, FSFile *f, uint32_t flags,
FSOpenCompletionFunc *cb, void *opaque);
int (*fs_create)(FSDevice *fs, FSQID *qid, FSFile *f, const char *name,
uint32_t flags, uint32_t mode, uint32_t gid);
int (*fs_stat)(FSDevice *fs, FSFile *f, FSStat *st);
int (*fs_setattr)(FSDevice *fs, FSFile *f, uint32_t mask,
uint32_t mode, uint32_t uid, uint32_t gid,
uint64_t size, uint64_t atime_sec, uint64_t atime_nsec,
uint64_t mtime_sec, uint64_t mtime_nsec);
void (*fs_close)(FSDevice *fs, FSFile *f);
int (*fs_readdir)(FSDevice *fs, FSFile *f, uint64_t offset,
uint8_t *buf, int count);
int (*fs_read)(FSDevice *fs, FSFile *f, uint64_t offset,
uint8_t *buf, int count);
int (*fs_write)(FSDevice *fs, FSFile *f, uint64_t offset,
const uint8_t *buf, int count);
int (*fs_link)(FSDevice *fs, FSFile *df, FSFile *f, const char *name);
int (*fs_symlink)(FSDevice *fs, FSQID *qid,
FSFile *f, const char *name, const char *symgt, uint32_t gid);
int (*fs_mknod)(FSDevice *fs, FSQID *qid,
FSFile *f, const char *name, uint32_t mode, uint32_t major,
uint32_t minor, uint32_t gid);
int (*fs_readlink)(FSDevice *fs, char *buf, int buf_size, FSFile *f);
int (*fs_renameat)(FSDevice *fs, FSFile *f, const char *name,
FSFile *new_f, const char *new_name);
int (*fs_unlinkat)(FSDevice *fs, FSFile *f, const char *name);
int (*fs_lock)(FSDevice *fs, FSFile *f, const FSLock *lock);
int (*fs_getlock)(FSDevice *fs, FSFile *f, FSLock *lock);
};
FSDevice *fs_disk_init(const char *root_path);
FSDevice *fs_mem_init(void);
FSDevice *fs_net_init(const char *url, void (*start)(void *opaque), void *opaque);
void fs_net_set_pwd(FSDevice *fs, const char *pwd);
#ifdef EMSCRIPTEN
void fs_import_file(const char *filename, uint8_t *buf, int buf_len);
#endif
void fs_export_file(const char *filename,
const uint8_t *buf, int buf_len);
void fs_end(FSDevice *fs);
void fs_dump_cache_load(FSDevice *fs1, const char *filename);
FSFile *fs_dup(FSDevice *fs, FSFile *f);
FSFile *fs_walk_path1(FSDevice *fs, FSFile *f, const char *path,
char **pname);
FSFile *fs_walk_path(FSDevice *fs, FSFile *f, const char *path);

659
fs_disk.c Normal file
View file

@ -0,0 +1,659 @@
/*
* Filesystem on disk
*
* Copyright (c) 2016 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include <stdarg.h>
#include <sys/statfs.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <errno.h>
#include "cutils.h"
#include "list.h"
#include "fs.h"
typedef struct {
FSDevice common;
char *root_path;
} FSDeviceDisk;
static void fs_close(FSDevice *fs, FSFile *f);
struct FSFile {
uint32_t uid;
char *path; /* complete path */
BOOL is_opened;
BOOL is_dir;
union {
int fd;
DIR *dirp;
} u;
};
static void fs_delete(FSDevice *fs, FSFile *f)
{
if (f->is_opened)
fs_close(fs, f);
free(f->path);
free(f);
}
/* warning: path belong to fid_create() */
static FSFile *fid_create(FSDevice *s1, char *path, uint32_t uid)
{
FSFile *f;
f = mallocz(sizeof(*f));
f->path = path;
f->uid = uid;
return f;
}
static int errno_table[][2] = {
{ P9_EPERM, EPERM },
{ P9_ENOENT, ENOENT },
{ P9_EIO, EIO },
{ P9_EEXIST, EEXIST },
{ P9_EINVAL, EINVAL },
{ P9_ENOSPC, ENOSPC },
{ P9_ENOTEMPTY, ENOTEMPTY },
{ P9_EPROTO, EPROTO },
{ P9_ENOTSUP, ENOTSUP },
};
static int errno_to_p9(int err)
{
int i;
if (err == 0)
return 0;
for(i = 0; i < countof(errno_table); i++) {
if (err == errno_table[i][1])
return errno_table[i][0];
}
return P9_EINVAL;
}
static int open_flags[][2] = {
{ P9_O_CREAT, O_CREAT },
{ P9_O_EXCL, O_EXCL },
// { P9_O_NOCTTY, O_NOCTTY },
{ P9_O_TRUNC, O_TRUNC },
{ P9_O_APPEND, O_APPEND },
{ P9_O_NONBLOCK, O_NONBLOCK },
{ P9_O_DSYNC, O_DSYNC },
// { P9_O_FASYNC, O_FASYNC },
// { P9_O_DIRECT, O_DIRECT },
// { P9_O_LARGEFILE, O_LARGEFILE },
// { P9_O_DIRECTORY, O_DIRECTORY },
{ P9_O_NOFOLLOW, O_NOFOLLOW },
// { P9_O_NOATIME, O_NOATIME },
// { P9_O_CLOEXEC, O_CLOEXEC },
{ P9_O_SYNC, O_SYNC },
};
static int p9_flags_to_host(int flags)
{
int ret, i;
ret = (flags & P9_O_NOACCESS);
for(i = 0; i < countof(open_flags); i++) {
if (flags & open_flags[i][0])
ret |= open_flags[i][1];
}
return ret;
}
static void stat_to_qid(FSQID *qid, const struct stat *st)
{
if (S_ISDIR(st->st_mode))
qid->type = P9_QTDIR;
else if (S_ISLNK(st->st_mode))
qid->type = P9_QTSYMLINK;
else
qid->type = P9_QTFILE;
qid->version = 0; /* no caching on client */
qid->path = st->st_ino;
}
static void fs_statfs(FSDevice *fs1, FSStatFS *st)
{
FSDeviceDisk *fs = (FSDeviceDisk *)fs1;
struct statfs st1;
statfs(fs->root_path, &st1);
st->f_bsize = st1.f_bsize;
st->f_blocks = st1.f_blocks;
st->f_bfree = st1.f_bfree;
st->f_bavail = st1.f_bavail;
st->f_files = st1.f_files;
st->f_ffree = st1.f_ffree;
}
static char *compose_path(const char *path, const char *name)
{
int path_len, name_len;
char *d;
path_len = strlen(path);
name_len = strlen(name);
d = malloc(path_len + 1 + name_len + 1);
memcpy(d, path, path_len);
d[path_len] = '/';
memcpy(d + path_len + 1, name, name_len + 1);
return d;
}
static int fs_attach(FSDevice *fs1, FSFile **pf,
FSQID *qid, uint32_t uid,
const char *uname, const char *aname)
{
FSDeviceDisk *fs = (FSDeviceDisk *)fs1;
struct stat st;
FSFile *f;
if (lstat(fs->root_path, &st) != 0) {
*pf = NULL;
return -errno_to_p9(errno);
}
f = fid_create(fs1, strdup(fs->root_path), uid);
stat_to_qid(qid, &st);
*pf = f;
return 0;
}
static int fs_walk(FSDevice *fs, FSFile **pf, FSQID *qids,
FSFile *f, int n, char **names)
{
char *path, *path1;
struct stat st;
int i;
path = strdup(f->path);
for(i = 0; i < n; i++) {
path1 = compose_path(path, names[i]);
if (lstat(path1, &st) != 0) {
free(path1);
break;
}
free(path);
path = path1;
stat_to_qid(&qids[i], &st);
}
*pf = fid_create(fs, path, f->uid);
return i;
}
static int fs_mkdir(FSDevice *fs, FSQID *qid, FSFile *f,
const char *name, uint32_t mode, uint32_t gid)
{
char *path;
struct stat st;
path = compose_path(f->path, name);
if (mkdir(path, mode) < 0) {
free(path);
return -errno_to_p9(errno);
}
if (lstat(path, &st) != 0) {
free(path);
return -errno_to_p9(errno);
}
free(path);
stat_to_qid(qid, &st);
return 0;
}
static int fs_open(FSDevice *fs, FSQID *qid, FSFile *f, uint32_t flags,
FSOpenCompletionFunc *cb, void *opaque)
{
struct stat st;
fs_close(fs, f);
if (stat(f->path, &st) != 0)
return -errno_to_p9(errno);
stat_to_qid(qid, &st);
if (flags & P9_O_DIRECTORY) {
DIR *dirp;
dirp = opendir(f->path);
if (!dirp)
return -errno_to_p9(errno);
f->is_opened = TRUE;
f->is_dir = TRUE;
f->u.dirp = dirp;
} else {
int fd;
fd = open(f->path, p9_flags_to_host(flags) & ~O_CREAT);
if (fd < 0)
return -errno_to_p9(errno);
f->is_opened = TRUE;
f->is_dir = FALSE;
f->u.fd = fd;
}
return 0;
}
static int fs_create(FSDevice *fs, FSQID *qid, FSFile *f, const char *name,
uint32_t flags, uint32_t mode, uint32_t gid)
{
struct stat st;
char *path;
int ret, fd;
fs_close(fs, f);
path = compose_path(f->path, name);
fd = open(path, p9_flags_to_host(flags) | O_CREAT, mode);
if (fd < 0) {
free(path);
return -errno_to_p9(errno);
}
ret = lstat(path, &st);
if (ret != 0) {
free(path);
close(fd);
return -errno_to_p9(errno);
}
free(f->path);
f->path = path;
f->is_opened = TRUE;
f->is_dir = FALSE;
f->u.fd = fd;
stat_to_qid(qid, &st);
return 0;
}
static int fs_readdir(FSDevice *fs, FSFile *f, uint64_t offset,
uint8_t *buf, int count)
{
struct dirent *de;
int len, pos, name_len, type, d_type;
if (!f->is_opened || !f->is_dir)
return -P9_EPROTO;
if (offset == 0)
rewinddir(f->u.dirp);
else
seekdir(f->u.dirp, offset);
pos = 0;
for(;;) {
de = readdir(f->u.dirp);
if (de == NULL)
break;
name_len = strlen(de->d_name);
len = 13 + 8 + 1 + 2 + name_len;
if ((pos + len) > count)
break;
offset = telldir(f->u.dirp);
d_type = de->d_type;
if (d_type == DT_UNKNOWN) {
char *path;
struct stat st;
path = compose_path(f->path, de->d_name);
if (lstat(path, &st) == 0) {
d_type = st.st_mode >> 12;
} else {
d_type = DT_REG; /* default */
}
free(path);
}
if (d_type == DT_DIR)
type = P9_QTDIR;
else if (d_type == DT_LNK)
type = P9_QTSYMLINK;
else
type = P9_QTFILE;
buf[pos++] = type;
put_le32(buf + pos, 0); /* version */
pos += 4;
put_le64(buf + pos, de->d_ino);
pos += 8;
put_le64(buf + pos, offset);
pos += 8;
buf[pos++] = d_type;
put_le16(buf + pos, name_len);
pos += 2;
memcpy(buf + pos, de->d_name, name_len);
pos += name_len;
}
return pos;
}
static int fs_read(FSDevice *fs, FSFile *f, uint64_t offset,
uint8_t *buf, int count)
{
int ret;
if (!f->is_opened || f->is_dir)
return -P9_EPROTO;
ret = pread(f->u.fd, buf, count, offset);
if (ret < 0)
return -errno_to_p9(errno);
else
return ret;
}
static int fs_write(FSDevice *fs, FSFile *f, uint64_t offset,
const uint8_t *buf, int count)
{
int ret;
if (!f->is_opened || f->is_dir)
return -P9_EPROTO;
ret = pwrite(f->u.fd, buf, count, offset);
if (ret < 0)
return -errno_to_p9(errno);
else
return ret;
}
static void fs_close(FSDevice *fs, FSFile *f)
{
if (!f->is_opened)
return;
if (f->is_dir)
closedir(f->u.dirp);
else
close(f->u.fd);
f->is_opened = FALSE;
}
static int fs_stat(FSDevice *fs, FSFile *f, FSStat *st)
{
struct stat st1;
if (lstat(f->path, &st1) != 0)
return -P9_ENOENT;
stat_to_qid(&st->qid, &st1);
st->st_mode = st1.st_mode;
st->st_uid = st1.st_uid;
st->st_gid = st1.st_gid;
st->st_nlink = st1.st_nlink;
st->st_rdev = st1.st_rdev;
st->st_size = st1.st_size;
st->st_blksize = st1.st_blksize;
st->st_blocks = st1.st_blocks;
st->st_atime_sec = st1.st_atim.tv_sec;
st->st_atime_nsec = st1.st_atim.tv_nsec;
st->st_mtime_sec = st1.st_mtim.tv_sec;
st->st_mtime_nsec = st1.st_mtim.tv_nsec;
st->st_ctime_sec = st1.st_ctim.tv_sec;
st->st_ctime_nsec = st1.st_ctim.tv_nsec;
return 0;
}
static int fs_setattr(FSDevice *fs, FSFile *f, uint32_t mask,
uint32_t mode, uint32_t uid, uint32_t gid,
uint64_t size, uint64_t atime_sec, uint64_t atime_nsec,
uint64_t mtime_sec, uint64_t mtime_nsec)
{
BOOL ctime_updated = FALSE;
if (mask & (P9_SETATTR_UID | P9_SETATTR_GID)) {
if (lchown(f->path, (mask & P9_SETATTR_UID) ? uid : -1,
(mask & P9_SETATTR_GID) ? gid : -1) < 0)
return -errno_to_p9(errno);
ctime_updated = TRUE;
}
/* must be done after uid change for suid */
if (mask & P9_SETATTR_MODE) {
if (chmod(f->path, mode) < 0)
return -errno_to_p9(errno);
ctime_updated = TRUE;
}
if (mask & P9_SETATTR_SIZE) {
if (truncate(f->path, size) < 0)
return -errno_to_p9(errno);
ctime_updated = TRUE;
}
if (mask & (P9_SETATTR_ATIME | P9_SETATTR_MTIME)) {
struct timespec ts[2];
if (mask & P9_SETATTR_ATIME) {
if (mask & P9_SETATTR_ATIME_SET) {
ts[0].tv_sec = atime_sec;
ts[0].tv_nsec = atime_nsec;
} else {
ts[0].tv_sec = 0;
ts[0].tv_nsec = UTIME_NOW;
}
} else {
ts[0].tv_sec = 0;
ts[0].tv_nsec = UTIME_OMIT;
}
if (mask & P9_SETATTR_MTIME) {
if (mask & P9_SETATTR_MTIME_SET) {
ts[1].tv_sec = mtime_sec;
ts[1].tv_nsec = mtime_nsec;
} else {
ts[1].tv_sec = 0;
ts[1].tv_nsec = UTIME_NOW;
}
} else {
ts[1].tv_sec = 0;
ts[1].tv_nsec = UTIME_OMIT;
}
if (utimensat(AT_FDCWD, f->path, ts, AT_SYMLINK_NOFOLLOW) < 0)
return -errno_to_p9(errno);
ctime_updated = TRUE;
}
if ((mask & P9_SETATTR_CTIME) && !ctime_updated) {
if (lchown(f->path, -1, -1) < 0)
return -errno_to_p9(errno);
}
return 0;
}
static int fs_link(FSDevice *fs, FSFile *df, FSFile *f, const char *name)
{
char *path;
path = compose_path(df->path, name);
if (link(f->path, path) < 0) {
free(path);
return -errno_to_p9(errno);
}
free(path);
return 0;
}
static int fs_symlink(FSDevice *fs, FSQID *qid,
FSFile *f, const char *name, const char *symgt, uint32_t gid)
{
char *path;
struct stat st;
path = compose_path(f->path, name);
if (symlink(symgt, path) < 0) {
free(path);
return -errno_to_p9(errno);
}
if (lstat(path, &st) != 0) {
free(path);
return -errno_to_p9(errno);
}
free(path);
stat_to_qid(qid, &st);
return 0;
}
static int fs_mknod(FSDevice *fs, FSQID *qid,
FSFile *f, const char *name, uint32_t mode, uint32_t major,
uint32_t minor, uint32_t gid)
{
char *path;
struct stat st;
path = compose_path(f->path, name);
if (mknod(path, mode, makedev(major, minor)) < 0) {
free(path);
return -errno_to_p9(errno);
}
if (lstat(path, &st) != 0) {
free(path);
return -errno_to_p9(errno);
}
free(path);
stat_to_qid(qid, &st);
return 0;
}
static int fs_readlink(FSDevice *fs, char *buf, int buf_size, FSFile *f)
{
int ret;
ret = readlink(f->path, buf, buf_size - 1);
if (ret < 0)
return -errno_to_p9(errno);
buf[ret] = '\0';
return 0;
}
static int fs_renameat(FSDevice *fs, FSFile *f, const char *name,
FSFile *new_f, const char *new_name)
{
char *path, *new_path;
int ret;
path = compose_path(f->path, name);
new_path = compose_path(new_f->path, new_name);
ret = rename(path, new_path);
free(path);
free(new_path);
if (ret < 0)
return -errno_to_p9(errno);
return 0;
}
static int fs_unlinkat(FSDevice *fs, FSFile *f, const char *name)
{
char *path;
int ret;
path = compose_path(f->path, name);
ret = remove(path);
free(path);
if (ret < 0)
return -errno_to_p9(errno);
return 0;
}
static int fs_lock(FSDevice *fs, FSFile *f, const FSLock *lock)
{
int ret;
struct flock fl;
/* XXX: lock directories too */
if (!f->is_opened || f->is_dir)
return -P9_EPROTO;
fl.l_type = lock->type;
fl.l_whence = SEEK_SET;
fl.l_start = lock->start;
fl.l_len = lock->length;
ret = fcntl(f->u.fd, F_SETLK, &fl);
if (ret == 0) {
ret = P9_LOCK_SUCCESS;
} else if (errno == EAGAIN || errno == EACCES) {
ret = P9_LOCK_BLOCKED;
} else {
ret = -errno_to_p9(errno);
}
return ret;
}
static int fs_getlock(FSDevice *fs, FSFile *f, FSLock *lock)
{
int ret;
struct flock fl;
/* XXX: lock directories too */
if (!f->is_opened || f->is_dir)
return -P9_EPROTO;
fl.l_type = lock->type;
fl.l_whence = SEEK_SET;
fl.l_start = lock->start;
fl.l_len = lock->length;
ret = fcntl(f->u.fd, F_GETLK, &fl);
if (ret < 0) {
ret = -errno_to_p9(errno);
} else {
lock->type = fl.l_type;
lock->start = fl.l_start;
lock->length = fl.l_len;
}
return ret;
}
static void fs_disk_end(FSDevice *fs1)
{
FSDeviceDisk *fs = (FSDeviceDisk *)fs1;
free(fs->root_path);
}
FSDevice *fs_disk_init(const char *root_path)
{
FSDeviceDisk *fs;
struct stat st;
lstat(root_path, &st);
if (!S_ISDIR(st.st_mode))
return NULL;
fs = mallocz(sizeof(*fs));
fs->common.fs_end = fs_disk_end;
fs->common.fs_delete = fs_delete;
fs->common.fs_statfs = fs_statfs;
fs->common.fs_attach = fs_attach;
fs->common.fs_walk = fs_walk;
fs->common.fs_mkdir = fs_mkdir;
fs->common.fs_open = fs_open;
fs->common.fs_create = fs_create;
fs->common.fs_stat = fs_stat;
fs->common.fs_setattr = fs_setattr;
fs->common.fs_close = fs_close;
fs->common.fs_readdir = fs_readdir;
fs->common.fs_read = fs_read;
fs->common.fs_write = fs_write;
fs->common.fs_link = fs_link;
fs->common.fs_symlink = fs_symlink;
fs->common.fs_mknod = fs_mknod;
fs->common.fs_readlink = fs_readlink;
fs->common.fs_renameat = fs_renameat;
fs->common.fs_unlinkat = fs_unlinkat;
fs->common.fs_lock = fs_lock;
fs->common.fs_getlock = fs_getlock;
fs->root_path = strdup(root_path);
return (FSDevice *)fs;
}

2910
fs_net.c Normal file

File diff suppressed because it is too large Load diff

370
fs_utils.c Normal file
View file

@ -0,0 +1,370 @@
/*
* Misc FS utilities
*
* Copyright (c) 2016-2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <ctype.h>
#include <sys/file.h>
#include "cutils.h"
#include "list.h"
#include "fs_utils.h"
/* last byte is the version */
const uint8_t encrypted_file_magic[4] = { 0xfb, 0xa2, 0xe9, 0x01 };
char *compose_path(const char *path, const char *name)
{
int path_len, name_len;
char *d, *q;
if (path[0] == '\0') {
d = strdup(name);
} else {
path_len = strlen(path);
name_len = strlen(name);
d = malloc(path_len + 1 + name_len + 1);
q = d;
memcpy(q, path, path_len);
q += path_len;
if (path[path_len - 1] != '/')
*q++ = '/';
memcpy(q, name, name_len + 1);
}
return d;
}
char *compose_url(const char *base_url, const char *name)
{
if (strchr(name, ':')) {
return strdup(name);
} else {
return compose_path(base_url, name);
}
}
void skip_line(const char **pp)
{
const char *p;
p = *pp;
while (*p != '\n' && *p != '\0')
p++;
if (*p == '\n')
p++;
*pp = p;
}
char *quoted_str(const char *str)
{
const char *s;
char *q;
int c;
char *buf;
if (str[0] == '\0')
goto use_quote;
s = str;
while (*s != '\0') {
if (*s <= ' ' || *s > '~')
goto use_quote;
s++;
}
return strdup(str);
use_quote:
buf = malloc(strlen(str) * 4 + 2 + 1);
q = buf;
s = str;
*q++ = '"';
while (*s != '\0') {
c = *(uint8_t *)s;
if (c < ' ' || c == 127) {
q += sprintf(q, "\\x%02x", c);
} else if (c == '\\' || c == '\"') {
q += sprintf(q, "\\%c", c);
} else {
*q++ = c;
}
s++;
}
*q++ = '"';
*q = '\0';
return buf;
}
int parse_fname(char *buf, int buf_size, const char **pp)
{
const char *p;
char *q;
int c, h;
p = *pp;
while (isspace_nolf(*p))
p++;
if (*p == '\0')
return -1;
q = buf;
if (*p == '"') {
p++;
for(;;) {
c = *p++;
if (c == '\0' || c == '\n') {
return -1;
} else if (c == '\"') {
break;
} else if (c == '\\') {
c = *p++;
switch(c) {
case '\'':
case '\"':
case '\\':
goto add_char;
case 'n':
c = '\n';
goto add_char;
case 'r':
c = '\r';
goto add_char;
case 't':
c = '\t';
goto add_char;
case 'x':
h = from_hex(*p++);
if (h < 0)
return -1;
c = h << 4;
h = from_hex(*p++);
if (h < 0)
return -1;
c |= h;
goto add_char;
default:
return -1;
}
} else {
add_char:
if (q >= buf + buf_size - 1)
return -1;
*q++ = c;
}
}
} else {
while (!isspace_nolf(*p) && *p != '\0' && *p != '\n') {
if (q >= buf + buf_size - 1)
return -1;
*q++ = *p++;
}
}
*q = '\0';
*pp = p;
return 0;
}
int parse_uint32_base(uint32_t *pval, const char **pp, int base)
{
const char *p, *p1;
p = *pp;
while (isspace_nolf(*p))
p++;
*pval = strtoul(p, (char **)&p1, base);
if (p1 == p)
return -1;
*pp = p1;
return 0;
}
int parse_uint64_base(uint64_t *pval, const char **pp, int base)
{
const char *p, *p1;
p = *pp;
while (isspace_nolf(*p))
p++;
*pval = strtoull(p, (char **)&p1, base);
if (p1 == p)
return -1;
*pp = p1;
return 0;
}
int parse_uint64(uint64_t *pval, const char **pp)
{
return parse_uint64_base(pval, pp, 0);
}
int parse_uint32(uint32_t *pval, const char **pp)
{
return parse_uint32_base(pval, pp, 0);
}
int parse_time(uint32_t *psec, uint32_t *pnsec, const char **pp)
{
const char *p;
uint32_t v, m;
p = *pp;
if (parse_uint32(psec, &p) < 0)
return -1;
v = 0;
if (*p == '.') {
p++;
/* XXX: inefficient */
m = 1000000000;
v = 0;
while (*p >= '0' && *p <= '9') {
m /= 10;
v += (*p - '0') * m;
p++;
}
}
*pnsec = v;
*pp = p;
return 0;
}
int parse_file_id(FSFileID *pval, const char **pp)
{
return parse_uint64_base(pval, pp, 16);
}
char *file_id_to_filename(char *buf, FSFileID file_id)
{
sprintf(buf, "%016" PRIx64, file_id);
return buf;
}
void encode_hex(char *str, const uint8_t *buf, int len)
{
int i;
for(i = 0; i < len; i++)
sprintf(str + 2 * i, "%02x", buf[i]);
}
int decode_hex(uint8_t *buf, const char *str, int len)
{
int h0, h1, i;
for(i = 0; i < len; i++) {
h0 = from_hex(str[2 * i]);
if (h0 < 0)
return -1;
h1 = from_hex(str[2 * i + 1]);
if (h1 < 0)
return -1;
buf[i] = (h0 << 4) | h1;
}
return 0;
}
/* return NULL if no end of header found */
const char *skip_header(const char *p)
{
p = strstr(p, "\n\n");
if (!p)
return NULL;
return p + 2;
}
/* return 0 if OK, < 0 if error */
int parse_tag(char *buf, int buf_size, const char *str, const char *tag)
{
char tagname[128], *q;
const char *p, *p1;
int len;
p = str;
for(;;) {
if (*p == '\0' || *p == '\n')
break;
q = tagname;
while (*p != ':' && *p != '\n' && *p != '\0') {
if ((q - tagname) < sizeof(tagname) - 1)
*q++ = *p;
p++;
}
*q = '\0';
if (*p != ':')
return -1;
p++;
while (isspace_nolf(*p))
p++;
p1 = p;
p = strchr(p, '\n');
if (!p)
len = strlen(p1);
else
len = p - p1;
if (!strcmp(tagname, tag)) {
if (len > buf_size - 1)
len = buf_size - 1;
memcpy(buf, p1, len);
buf[len] = '\0';
return 0;
}
if (!p)
break;
else
p++;
}
return -1;
}
int parse_tag_uint64(uint64_t *pval, const char *str, const char *tag)
{
char buf[64];
const char *p;
if (parse_tag(buf, sizeof(buf), str, tag))
return -1;
p = buf;
return parse_uint64(pval, &p);
}
int parse_tag_file_id(FSFileID *pval, const char *str, const char *tag)
{
char buf[64];
const char *p;
if (parse_tag(buf, sizeof(buf), str, tag))
return -1;
p = buf;
return parse_uint64_base(pval, &p, 16);
}
int parse_tag_version(const char *str)
{
uint64_t version;
if (parse_tag_uint64(&version, str, "Version"))
return -1;
return version;
}
BOOL is_url(const char *path)
{
return (strstart(path, "http:", NULL) ||
strstart(path, "https:", NULL) ||
strstart(path, "file:", NULL));
}

95
fs_utils.h Normal file
View file

@ -0,0 +1,95 @@
/*
* Misc FS utilities
*
* Copyright (c) 2016-2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#define HEAD_FILENAME "head"
#define ROOT_FILENAME "files"
#define FILEID_SIZE_MAX 32
#define FS_KEY_LEN 16
/* default block size to determine the total filesytem size */
#define FS_BLOCK_SIZE_LOG2 12
#define FS_BLOCK_SIZE (1 << FS_BLOCK_SIZE_LOG2)
typedef enum {
FS_ERR_OK = 0,
FS_ERR_GENERIC = -1,
FS_ERR_SYNTAX = -2,
FS_ERR_REVISION = -3,
FS_ERR_FILE_ID = -4,
FS_ERR_IO = -5,
FS_ERR_NOENT = -6,
FS_ERR_COUNTERS = -7,
FS_ERR_QUOTA = -8,
FS_ERR_PROTOCOL_VERSION = -9,
FS_ERR_HEAD = -10,
} FSCommitErrorCode;
typedef uint64_t FSFileID;
static inline BOOL isspace_nolf(int c)
{
return (c == ' ' || c == '\t');
}
static inline int from_hex(int c)
{
if (c >= '0' && c <= '9')
return c - '0';
else if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
else if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
else
return -1;
}
static inline uint64_t block_align(uint64_t val, uint64_t align)
{
return (val + align - 1) & ~(align - 1);
}
void pstrcpy(char *buf, int buf_size, const char *str);
char *pstrcat(char *buf, int buf_size, const char *s);
char *compose_path(const char *path, const char *name);
char *compose_url(const char *base_url, const char *name);
void skip_line(const char **pp);
char *quoted_str(const char *str);
int parse_fname(char *buf, int buf_size, const char **pp);
int parse_uint32_base(uint32_t *pval, const char **pp, int base);
int parse_uint64_base(uint64_t *pval, const char **pp, int base);
int parse_uint64(uint64_t *pval, const char **pp);
int parse_uint32(uint32_t *pval, const char **pp);
int parse_time(uint32_t *psec, uint32_t *pnsec, const char **pp);
int parse_file_id(FSFileID *pval, const char **pp);
char *file_id_to_filename(char *buf, FSFileID file_id);
void encode_hex(char *str, const uint8_t *buf, int len);
int decode_hex(uint8_t *buf, const char *str, int len);
BOOL is_url(const char *path);
const char *skip_header(const char *p);
int parse_tag(char *buf, int buf_size, const char *str, const char *tag);
int parse_tag_uint64(uint64_t *pval, const char *str, const char *tag);
int parse_tag_file_id(FSFileID *pval, const char *str, const char *tag);
int parse_tag_version(const char *str);

625
fs_wget.c Normal file
View file

@ -0,0 +1,625 @@
/*
* HTTP file download
*
* Copyright (c) 2016-2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include <stdarg.h>
#include <sys/time.h>
#include <ctype.h>
#include "cutils.h"
#include "list.h"
#include "fs.h"
#include "fs_utils.h"
#include "fs_wget.h"
#if defined(EMSCRIPTEN)
#include <emscripten.h>
#else
#include <curl/multi.h>
#endif
/***********************************************/
/* HTTP get */
#ifdef EMSCRIPTEN
struct XHRState {
void *opaque;
WGetWriteCallback *cb;
};
static int downloading_count;
void fs_wget_init(void)
{
}
extern void fs_wget_update_downloading(int flag);
static void fs_wget_update_downloading_count(int incr)
{
int prev_state, state;
prev_state = (downloading_count > 0);
downloading_count += incr;
state = (downloading_count > 0);
if (prev_state != state)
fs_wget_update_downloading(state);
}
static void fs_wget_onerror(unsigned int handle, void *opaque, int status,
const char *status_text)
{
XHRState *s = opaque;
if (status <= 0)
status = -404; /* HTTP not found error */
else
status = -status;
fs_wget_update_downloading_count(-1);
if (s->cb)
s->cb(s->opaque, status, NULL, 0);
}
static void fs_wget_onload(unsigned int handle,
void *opaque, void *data, unsigned int size)
{
XHRState *s = opaque;
fs_wget_update_downloading_count(-1);
if (s->cb)
s->cb(s->opaque, 0, data, size);
}
extern int emscripten_async_wget3_data(const char* url, const char* requesttype, const char *user, const char *password, const uint8_t *post_data, int post_data_len, void *arg, int free, em_async_wget2_data_onload_func onload, em_async_wget2_data_onerror_func onerror, em_async_wget2_data_onprogress_func onprogress);
XHRState *fs_wget2(const char *url, const char *user, const char *password,
WGetReadCallback *read_cb, uint64_t post_data_len,
void *opaque, WGetWriteCallback *cb, BOOL single_write)
{
XHRState *s;
const char *request;
uint8_t *post_data;
s = mallocz(sizeof(*s));
s->opaque = opaque;
s->cb = cb;
if (post_data_len != 0) {
request = "POST";
post_data = malloc(post_data_len);
read_cb(opaque, post_data, post_data_len);
} else {
request = "GET";
post_data = NULL;
}
fs_wget_update_downloading_count(1);
emscripten_async_wget3_data(url, request, user, password,
post_data, post_data_len, s, 1, fs_wget_onload,
fs_wget_onerror, NULL);
if (post_data_len != 0)
free(post_data);
return s;
}
void fs_wget_free(XHRState *s)
{
s->cb = NULL;
s->opaque = NULL;
}
#else
struct XHRState {
struct list_head link;
CURL *eh;
void *opaque;
WGetWriteCallback *write_cb;
WGetReadCallback *read_cb;
BOOL single_write;
DynBuf dbuf; /* used if single_write */
};
typedef struct {
struct list_head link;
int64_t timeout;
void (*cb)(void *opaque);
void *opaque;
} AsyncCallState;
static CURLM *curl_multi_ctx;
static struct list_head xhr_list; /* list of XHRState.link */
void fs_wget_init(void)
{
if (curl_multi_ctx)
return;
curl_global_init(CURL_GLOBAL_ALL);
curl_multi_ctx = curl_multi_init();
init_list_head(&xhr_list);
}
void fs_wget_end(void)
{
curl_multi_cleanup(curl_multi_ctx);
curl_global_cleanup();
}
static size_t fs_wget_write_cb(char *ptr, size_t size, size_t nmemb,
void *userdata)
{
XHRState *s = userdata;
size *= nmemb;
if (s->single_write) {
dbuf_write(&s->dbuf, s->dbuf.size, (void *)ptr, size);
} else {
s->write_cb(s->opaque, 1, ptr, size);
}
return size;
}
static size_t fs_wget_read_cb(char *ptr, size_t size, size_t nmemb,
void *userdata)
{
XHRState *s = userdata;
size *= nmemb;
return s->read_cb(s->opaque, ptr, size);
}
XHRState *fs_wget2(const char *url, const char *user, const char *password,
WGetReadCallback *read_cb, uint64_t post_data_len,
void *opaque, WGetWriteCallback *write_cb, BOOL single_write)
{
XHRState *s;
s = mallocz(sizeof(*s));
s->eh = curl_easy_init();
s->opaque = opaque;
s->write_cb = write_cb;
s->read_cb = read_cb;
s->single_write = single_write;
dbuf_init(&s->dbuf);
curl_easy_setopt(s->eh, CURLOPT_PRIVATE, s);
curl_easy_setopt(s->eh, CURLOPT_WRITEDATA, s);
curl_easy_setopt(s->eh, CURLOPT_WRITEFUNCTION, fs_wget_write_cb);
curl_easy_setopt(s->eh, CURLOPT_HEADER, 0);
curl_easy_setopt(s->eh, CURLOPT_URL, url);
curl_easy_setopt(s->eh, CURLOPT_VERBOSE, 0L);
curl_easy_setopt(s->eh, CURLOPT_ACCEPT_ENCODING, "");
if (user) {
curl_easy_setopt(s->eh, CURLOPT_USERNAME, user);
curl_easy_setopt(s->eh, CURLOPT_PASSWORD, password);
}
if (post_data_len != 0) {
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers,
"Content-Type: application/octet-stream");
curl_easy_setopt(s->eh, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(s->eh, CURLOPT_POST, 1L);
curl_easy_setopt(s->eh, CURLOPT_POSTFIELDSIZE_LARGE,
(curl_off_t)post_data_len);
curl_easy_setopt(s->eh, CURLOPT_READDATA, s);
curl_easy_setopt(s->eh, CURLOPT_READFUNCTION, fs_wget_read_cb);
}
curl_multi_add_handle(curl_multi_ctx, s->eh);
list_add_tail(&s->link, &xhr_list);
return s;
}
void fs_wget_free(XHRState *s)
{
dbuf_free(&s->dbuf);
curl_easy_cleanup(s->eh);
list_del(&s->link);
free(s);
}
/* timeout is in ms */
void fs_net_set_fdset(int *pfd_max, fd_set *rfds, fd_set *wfds, fd_set *efds,
int *ptimeout)
{
long timeout;
int n, fd_max;
CURLMsg *msg;
if (!curl_multi_ctx)
return;
curl_multi_perform(curl_multi_ctx, &n);
for(;;) {
msg = curl_multi_info_read(curl_multi_ctx, &n);
if (!msg)
break;
if (msg->msg == CURLMSG_DONE) {
XHRState *s;
long http_code;
curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, (char **)&s);
curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE,
&http_code);
/* signal the end of the transfer or error */
if (http_code == 200) {
if (s->single_write) {
s->write_cb(s->opaque, 0, s->dbuf.buf, s->dbuf.size);
} else {
s->write_cb(s->opaque, 0, NULL, 0);
}
} else {
s->write_cb(s->opaque, -http_code, NULL, 0);
}
curl_multi_remove_handle(curl_multi_ctx, s->eh);
curl_easy_cleanup(s->eh);
dbuf_free(&s->dbuf);
list_del(&s->link);
free(s);
}
}
curl_multi_fdset(curl_multi_ctx, rfds, wfds, efds, &fd_max);
*pfd_max = max_int(*pfd_max, fd_max);
curl_multi_timeout(curl_multi_ctx, &timeout);
if (timeout >= 0)
*ptimeout = min_int(*ptimeout, timeout);
}
void fs_net_event_loop(FSNetEventLoopCompletionFunc *cb, void *opaque)
{
fd_set rfds, wfds, efds;
int timeout, fd_max;
struct timeval tv;
if (!curl_multi_ctx)
return;
for(;;) {
fd_max = -1;
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_ZERO(&efds);
timeout = 10000;
fs_net_set_fdset(&fd_max, &rfds, &wfds, &efds, &timeout);
if (cb) {
if (cb(opaque))
break;
} else {
if (list_empty(&xhr_list))
break;
}
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
select(fd_max + 1, &rfds, &wfds, &efds, &tv);
}
}
#endif /* !EMSCRIPTEN */
XHRState *fs_wget(const char *url, const char *user, const char *password,
void *opaque, WGetWriteCallback *cb, BOOL single_write)
{
return fs_wget2(url, user, password, NULL, 0, opaque, cb, single_write);
}
/***********************************************/
/* file decryption */
#define ENCRYPTED_FILE_HEADER_SIZE (4 + AES_BLOCK_SIZE)
#define DEC_BUF_SIZE (256 * AES_BLOCK_SIZE)
struct DecryptFileState {
DecryptFileCB *write_cb;
void *opaque;
int dec_state;
int dec_buf_pos;
AES_KEY *aes_state;
uint8_t iv[AES_BLOCK_SIZE];
uint8_t dec_buf[DEC_BUF_SIZE];
};
DecryptFileState *decrypt_file_init(AES_KEY *aes_state,
DecryptFileCB *write_cb,
void *opaque)
{
DecryptFileState *s;
s = mallocz(sizeof(*s));
s->write_cb = write_cb;
s->opaque = opaque;
s->aes_state = aes_state;
return s;
}
int decrypt_file(DecryptFileState *s, const uint8_t *data,
size_t size)
{
int l, len, ret;
while (size != 0) {
switch(s->dec_state) {
case 0:
l = min_int(size, ENCRYPTED_FILE_HEADER_SIZE - s->dec_buf_pos);
memcpy(s->dec_buf + s->dec_buf_pos, data, l);
s->dec_buf_pos += l;
if (s->dec_buf_pos >= ENCRYPTED_FILE_HEADER_SIZE) {
if (memcmp(s->dec_buf, encrypted_file_magic, 4) != 0)
return -1;
memcpy(s->iv, s->dec_buf + 4, AES_BLOCK_SIZE);
s->dec_state = 1;
s->dec_buf_pos = 0;
}
break;
case 1:
l = min_int(size, DEC_BUF_SIZE - s->dec_buf_pos);
memcpy(s->dec_buf + s->dec_buf_pos, data, l);
s->dec_buf_pos += l;
if (s->dec_buf_pos >= DEC_BUF_SIZE) {
/* keep one block in case it is the padding */
len = s->dec_buf_pos - AES_BLOCK_SIZE;
AES_cbc_encrypt(s->dec_buf, s->dec_buf, len,
s->aes_state, s->iv, FALSE);
ret = s->write_cb(s->opaque, s->dec_buf, len);
if (ret < 0)
return ret;
memcpy(s->dec_buf, s->dec_buf + s->dec_buf_pos - AES_BLOCK_SIZE,
AES_BLOCK_SIZE);
s->dec_buf_pos = AES_BLOCK_SIZE;
}
break;
default:
abort();
}
data += l;
size -= l;
}
return 0;
}
/* write last blocks */
int decrypt_file_flush(DecryptFileState *s)
{
int len, pad_len, ret;
if (s->dec_state != 1)
return -1;
len = s->dec_buf_pos;
if (len == 0 ||
(len % AES_BLOCK_SIZE) != 0)
return -1;
AES_cbc_encrypt(s->dec_buf, s->dec_buf, len,
s->aes_state, s->iv, FALSE);
pad_len = s->dec_buf[s->dec_buf_pos - 1];
if (pad_len < 1 || pad_len > AES_BLOCK_SIZE)
return -1;
len -= pad_len;
if (len != 0) {
ret = s->write_cb(s->opaque, s->dec_buf, len);
if (ret < 0)
return ret;
}
return 0;
}
void decrypt_file_end(DecryptFileState *s)
{
free(s);
}
/* XHR file */
typedef struct {
FSDevice *fs;
FSFile *f;
int64_t pos;
FSWGetFileCB *cb;
void *opaque;
FSFile *posted_file;
int64_t read_pos;
DecryptFileState *dec_state;
} FSWGetFileState;
static int fs_wget_file_write_cb(void *opaque, const uint8_t *data,
size_t size)
{
FSWGetFileState *s = opaque;
FSDevice *fs = s->fs;
int ret;
ret = fs->fs_write(fs, s->f, s->pos, data, size);
if (ret < 0)
return ret;
s->pos += ret;
return ret;
}
static void fs_wget_file_on_load(void *opaque, int err, void *data, size_t size)
{
FSWGetFileState *s = opaque;
FSDevice *fs = s->fs;
int ret;
int64_t ret_size;
// printf("err=%d size=%ld\n", err, size);
if (err < 0) {
ret_size = err;
goto done;
} else {
if (s->dec_state) {
ret = decrypt_file(s->dec_state, data, size);
if (ret >= 0 && err == 0) {
/* handle the end of file */
decrypt_file_flush(s->dec_state);
}
} else {
ret = fs_wget_file_write_cb(s, data, size);
}
if (ret < 0) {
ret_size = ret;
goto done;
} else if (err == 0) {
/* end of transfer */
ret_size = s->pos;
done:
s->cb(fs, s->f, ret_size, s->opaque);
if (s->dec_state)
decrypt_file_end(s->dec_state);
free(s);
}
}
}
static size_t fs_wget_file_read_cb(void *opaque, void *data, size_t size)
{
FSWGetFileState *s = opaque;
FSDevice *fs = s->fs;
int ret;
if (!s->posted_file)
return 0;
ret = fs->fs_read(fs, s->posted_file, s->read_pos, data, size);
if (ret < 0)
return 0;
s->read_pos += ret;
return ret;
}
void fs_wget_file2(FSDevice *fs, FSFile *f, const char *url,
const char *user, const char *password,
FSFile *posted_file, uint64_t post_data_len,
FSWGetFileCB *cb, void *opaque,
AES_KEY *aes_state)
{
FSWGetFileState *s;
s = mallocz(sizeof(*s));
s->fs = fs;
s->f = f;
s->pos = 0;
s->cb = cb;
s->opaque = opaque;
s->posted_file = posted_file;
s->read_pos = 0;
if (aes_state) {
s->dec_state = decrypt_file_init(aes_state, fs_wget_file_write_cb, s);
}
fs_wget2(url, user, password, fs_wget_file_read_cb, post_data_len,
s, fs_wget_file_on_load, FALSE);
}
/***********************************************/
/* PBKDF2 */
#ifdef USE_BUILTIN_CRYPTO
#define HMAC_BLOCK_SIZE 64
typedef struct {
SHA256_CTX ctx;
uint8_t K[HMAC_BLOCK_SIZE + SHA256_DIGEST_LENGTH];
} HMAC_SHA256_CTX;
void hmac_sha256_init(HMAC_SHA256_CTX *s, const uint8_t *key, int key_len)
{
int i, l;
if (key_len > HMAC_BLOCK_SIZE) {
SHA256(key, key_len, s->K);
l = SHA256_DIGEST_LENGTH;
} else {
memcpy(s->K, key, key_len);
l = key_len;
}
memset(s->K + l, 0, HMAC_BLOCK_SIZE - l);
for(i = 0; i < HMAC_BLOCK_SIZE; i++)
s->K[i] ^= 0x36;
SHA256_Init(&s->ctx);
SHA256_Update(&s->ctx, s->K, HMAC_BLOCK_SIZE);
}
void hmac_sha256_update(HMAC_SHA256_CTX *s, const uint8_t *buf, int len)
{
SHA256_Update(&s->ctx, buf, len);
}
/* out has a length of SHA256_DIGEST_LENGTH */
void hmac_sha256_final(HMAC_SHA256_CTX *s, uint8_t *out)
{
int i;
SHA256_Final(s->K + HMAC_BLOCK_SIZE, &s->ctx);
for(i = 0; i < HMAC_BLOCK_SIZE; i++)
s->K[i] ^= (0x36 ^ 0x5c);
SHA256(s->K, HMAC_BLOCK_SIZE + SHA256_DIGEST_LENGTH, out);
}
#define SALT_LEN_MAX 32
void pbkdf2_hmac_sha256(const uint8_t *pwd, int pwd_len,
const uint8_t *salt, int salt_len,
int iter, int key_len, uint8_t *out)
{
uint8_t F[SHA256_DIGEST_LENGTH], U[SALT_LEN_MAX + 4];
HMAC_SHA256_CTX ctx;
int it, U_len, j, l;
uint32_t i;
assert(salt_len <= SALT_LEN_MAX);
i = 1;
while (key_len > 0) {
memset(F, 0, SHA256_DIGEST_LENGTH);
memcpy(U, salt, salt_len);
U[salt_len] = i >> 24;
U[salt_len + 1] = i >> 16;
U[salt_len + 2] = i >> 8;
U[salt_len + 3] = i;
U_len = salt_len + 4;
for(it = 0; it < iter; it++) {
hmac_sha256_init(&ctx, pwd, pwd_len);
hmac_sha256_update(&ctx, U, U_len);
hmac_sha256_final(&ctx, U);
for(j = 0; j < SHA256_DIGEST_LENGTH; j++)
F[j] ^= U[j];
U_len = SHA256_DIGEST_LENGTH;
}
l = min_int(key_len, SHA256_DIGEST_LENGTH);
memcpy(out, F, l);
out += l;
key_len -= l;
i++;
}
}
#else
void pbkdf2_hmac_sha256(const uint8_t *pwd, int pwd_len,
const uint8_t *salt, int salt_len,
int iter, int key_len, uint8_t *out)
{
PKCS5_PBKDF2_HMAC((const char *)pwd, pwd_len, salt, salt_len,
iter, EVP_sha256(), key_len, out);
}
#endif /* !USE_BUILTIN_CRYPTO */

93
fs_wget.h Normal file
View file

@ -0,0 +1,93 @@
/*
* HTTP file download
*
* Copyright (c) 2016-2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#if defined(EMSCRIPTEN)
#define USE_BUILTIN_CRYPTO
#endif
#ifdef USE_BUILTIN_CRYPTO
#include "aes.h"
#include "sha256.h"
#else
#include <openssl/aes.h>
#include <openssl/sha.h>
#include <openssl/evp.h>
#endif
#ifdef _WIN32
#include <winsock2.h>
#endif
#define LOG() printf("%s:%d\n", __func__, __LINE__)
/* XHR */
/* err < 0: error (no data provided)
err = 0: end of transfer (data can be provided too)
err = 1: data chunk
*/
typedef void WGetWriteCallback(void *opaque, int err, void *data, size_t size);
typedef size_t WGetReadCallback(void *opaque, void *data, size_t size);
typedef struct XHRState XHRState;
XHRState *fs_wget(const char *url, const char *user, const char *password,
void *opaque, WGetWriteCallback *cb, BOOL single_write);
void fs_wget_free(XHRState *s);
void fs_wget_init(void);
void fs_wget_end(void);
#ifndef EMSCRIPTEN
typedef BOOL FSNetEventLoopCompletionFunc(void *opaque);
void fs_net_set_fdset(int *pfd_max, fd_set *rfds, fd_set *wfds, fd_set *efds,
int *ptimeout);
void fs_net_event_loop(FSNetEventLoopCompletionFunc *cb, void *opaque);
#endif
/* crypto */
extern const uint8_t encrypted_file_magic[4];
typedef int DecryptFileCB(void *opaque, const uint8_t *data, size_t len);
typedef struct DecryptFileState DecryptFileState;
DecryptFileState *decrypt_file_init(AES_KEY *aes_state,
DecryptFileCB *write_cb,
void *opaque);
int decrypt_file(DecryptFileState *s, const uint8_t *data,
size_t size);
int decrypt_file_flush(DecryptFileState *s);
void decrypt_file_end(DecryptFileState *s);
void pbkdf2_hmac_sha256(const uint8_t *pwd, int pwd_len,
const uint8_t *salt, int salt_len,
int iter, int key_len, uint8_t *out);
/* XHR file */
typedef void FSWGetFileCB(FSDevice *fs, FSFile *f, int64_t size, void *opaque);
void fs_wget_file2(FSDevice *fs, FSFile *f, const char *url,
const char *user, const char *password,
FSFile *posted_file, uint64_t post_data_len,
FSWGetFileCB *cb, void *opaque,
AES_KEY *aes_state);

835
ide.c Normal file
View file

@ -0,0 +1,835 @@
/*
* IDE emulation
*
* Copyright (c) 2003-2016 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include "cutils.h"
#include "ide.h"
//#define DEBUG_IDE
/* Bits of HD_STATUS */
#define ERR_STAT 0x01
#define INDEX_STAT 0x02
#define ECC_STAT 0x04 /* Corrected error */
#define DRQ_STAT 0x08
#define SEEK_STAT 0x10
#define SRV_STAT 0x10
#define WRERR_STAT 0x20
#define READY_STAT 0x40
#define BUSY_STAT 0x80
/* Bits for HD_ERROR */
#define MARK_ERR 0x01 /* Bad address mark */
#define TRK0_ERR 0x02 /* couldn't find track 0 */
#define ABRT_ERR 0x04 /* Command aborted */
#define MCR_ERR 0x08 /* media change request */
#define ID_ERR 0x10 /* ID field not found */
#define MC_ERR 0x20 /* media changed */
#define ECC_ERR 0x40 /* Uncorrectable ECC error */
#define BBD_ERR 0x80 /* pre-EIDE meaning: block marked bad */
#define ICRC_ERR 0x80 /* new meaning: CRC error during transfer */
/* Bits of HD_NSECTOR */
#define CD 0x01
#define IO 0x02
#define REL 0x04
#define TAG_MASK 0xf8
#define IDE_CMD_RESET 0x04
#define IDE_CMD_DISABLE_IRQ 0x02
/* ATA/ATAPI Commands pre T13 Spec */
#define WIN_NOP 0x00
/*
* 0x01->0x02 Reserved
*/
#define CFA_REQ_EXT_ERROR_CODE 0x03 /* CFA Request Extended Error Code */
/*
* 0x04->0x07 Reserved
*/
#define WIN_SRST 0x08 /* ATAPI soft reset command */
#define WIN_DEVICE_RESET 0x08
/*
* 0x09->0x0F Reserved
*/
#define WIN_RECAL 0x10
#define WIN_RESTORE WIN_RECAL
/*
* 0x10->0x1F Reserved
*/
#define WIN_READ 0x20 /* 28-Bit */
#define WIN_READ_ONCE 0x21 /* 28-Bit without retries */
#define WIN_READ_LONG 0x22 /* 28-Bit */
#define WIN_READ_LONG_ONCE 0x23 /* 28-Bit without retries */
#define WIN_READ_EXT 0x24 /* 48-Bit */
#define WIN_READDMA_EXT 0x25 /* 48-Bit */
#define WIN_READDMA_QUEUED_EXT 0x26 /* 48-Bit */
#define WIN_READ_NATIVE_MAX_EXT 0x27 /* 48-Bit */
/*
* 0x28
*/
#define WIN_MULTREAD_EXT 0x29 /* 48-Bit */
/*
* 0x2A->0x2F Reserved
*/
#define WIN_WRITE 0x30 /* 28-Bit */
#define WIN_WRITE_ONCE 0x31 /* 28-Bit without retries */
#define WIN_WRITE_LONG 0x32 /* 28-Bit */
#define WIN_WRITE_LONG_ONCE 0x33 /* 28-Bit without retries */
#define WIN_WRITE_EXT 0x34 /* 48-Bit */
#define WIN_WRITEDMA_EXT 0x35 /* 48-Bit */
#define WIN_WRITEDMA_QUEUED_EXT 0x36 /* 48-Bit */
#define WIN_SET_MAX_EXT 0x37 /* 48-Bit */
#define CFA_WRITE_SECT_WO_ERASE 0x38 /* CFA Write Sectors without erase */
#define WIN_MULTWRITE_EXT 0x39 /* 48-Bit */
/*
* 0x3A->0x3B Reserved
*/
#define WIN_WRITE_VERIFY 0x3C /* 28-Bit */
/*
* 0x3D->0x3F Reserved
*/
#define WIN_VERIFY 0x40 /* 28-Bit - Read Verify Sectors */
#define WIN_VERIFY_ONCE 0x41 /* 28-Bit - without retries */
#define WIN_VERIFY_EXT 0x42 /* 48-Bit */
/*
* 0x43->0x4F Reserved
*/
#define WIN_FORMAT 0x50
/*
* 0x51->0x5F Reserved
*/
#define WIN_INIT 0x60
/*
* 0x61->0x5F Reserved
*/
#define WIN_SEEK 0x70 /* 0x70-0x7F Reserved */
#define CFA_TRANSLATE_SECTOR 0x87 /* CFA Translate Sector */
#define WIN_DIAGNOSE 0x90
#define WIN_SPECIFY 0x91 /* set drive geometry translation */
#define WIN_DOWNLOAD_MICROCODE 0x92
#define WIN_STANDBYNOW2 0x94
#define WIN_STANDBY2 0x96
#define WIN_SETIDLE2 0x97
#define WIN_CHECKPOWERMODE2 0x98
#define WIN_SLEEPNOW2 0x99
/*
* 0x9A VENDOR
*/
#define WIN_PACKETCMD 0xA0 /* Send a packet command. */
#define WIN_PIDENTIFY 0xA1 /* identify ATAPI device */
#define WIN_QUEUED_SERVICE 0xA2
#define WIN_SMART 0xB0 /* self-monitoring and reporting */
#define CFA_ERASE_SECTORS 0xC0
#define WIN_MULTREAD 0xC4 /* read sectors using multiple mode*/
#define WIN_MULTWRITE 0xC5 /* write sectors using multiple mode */
#define WIN_SETMULT 0xC6 /* enable/disable multiple mode */
#define WIN_READDMA_QUEUED 0xC7 /* read sectors using Queued DMA transfers */
#define WIN_READDMA 0xC8 /* read sectors using DMA transfers */
#define WIN_READDMA_ONCE 0xC9 /* 28-Bit - without retries */
#define WIN_WRITEDMA 0xCA /* write sectors using DMA transfers */
#define WIN_WRITEDMA_ONCE 0xCB /* 28-Bit - without retries */
#define WIN_WRITEDMA_QUEUED 0xCC /* write sectors using Queued DMA transfers */
#define CFA_WRITE_MULTI_WO_ERASE 0xCD /* CFA Write multiple without erase */
#define WIN_GETMEDIASTATUS 0xDA
#define WIN_ACKMEDIACHANGE 0xDB /* ATA-1, ATA-2 vendor */
#define WIN_POSTBOOT 0xDC
#define WIN_PREBOOT 0xDD
#define WIN_DOORLOCK 0xDE /* lock door on removable drives */
#define WIN_DOORUNLOCK 0xDF /* unlock door on removable drives */
#define WIN_STANDBYNOW1 0xE0
#define WIN_IDLEIMMEDIATE 0xE1 /* force drive to become "ready" */
#define WIN_STANDBY 0xE2 /* Set device in Standby Mode */
#define WIN_SETIDLE1 0xE3
#define WIN_READ_BUFFER 0xE4 /* force read only 1 sector */
#define WIN_CHECKPOWERMODE1 0xE5
#define WIN_SLEEPNOW1 0xE6
#define WIN_FLUSH_CACHE 0xE7
#define WIN_WRITE_BUFFER 0xE8 /* force write only 1 sector */
#define WIN_WRITE_SAME 0xE9 /* read ata-2 to use */
/* SET_FEATURES 0x22 or 0xDD */
#define WIN_FLUSH_CACHE_EXT 0xEA /* 48-Bit */
#define WIN_IDENTIFY 0xEC /* ask drive to identify itself */
#define WIN_MEDIAEJECT 0xED
#define WIN_IDENTIFY_DMA 0xEE /* same as WIN_IDENTIFY, but DMA */
#define WIN_SETFEATURES 0xEF /* set special drive features */
#define EXABYTE_ENABLE_NEST 0xF0
#define WIN_SECURITY_SET_PASS 0xF1
#define WIN_SECURITY_UNLOCK 0xF2
#define WIN_SECURITY_ERASE_PREPARE 0xF3
#define WIN_SECURITY_ERASE_UNIT 0xF4
#define WIN_SECURITY_FREEZE_LOCK 0xF5
#define WIN_SECURITY_DISABLE 0xF6
#define WIN_READ_NATIVE_MAX 0xF8 /* return the native maximum address */
#define WIN_SET_MAX 0xF9
#define DISABLE_SEAGATE 0xFB
#define MAX_MULT_SECTORS 128
typedef struct IDEState IDEState;
typedef void EndTransferFunc(IDEState *);
struct IDEState {
IDEIFState *ide_if;
BlockDevice *bs;
int cylinders, heads, sectors;
int mult_sectors;
int64_t nb_sectors;
/* ide regs */
uint8_t feature;
uint8_t error;
uint16_t nsector; /* 0 is 256 to ease computations */
uint8_t sector;
uint8_t lcyl;
uint8_t hcyl;
uint8_t select;
uint8_t status;
int io_nb_sectors;
int req_nb_sectors;
EndTransferFunc *end_transfer_func;
int data_index;
int data_end;
uint8_t io_buffer[MAX_MULT_SECTORS*512 + 4];
};
struct IDEIFState {
IRQSignal *irq;
IDEState *cur_drive;
IDEState *drives[2];
/* 0x3f6 command */
uint8_t cmd;
};
static void ide_sector_read_cb(void *opaque, int ret);
static void ide_sector_read_cb_end(IDEState *s);
static void ide_sector_write_cb2(void *opaque, int ret);
static void padstr(char *str, const char *src, int len)
{
int i, v;
for(i = 0; i < len; i++) {
if (*src)
v = *src++;
else
v = ' ';
*(char *)((long)str ^ 1) = v;
str++;
}
}
/* little endian assume */
static void stw(uint16_t *buf, int v)
{
*buf = v;
}
static void ide_identify(IDEState *s)
{
uint16_t *tab;
uint32_t oldsize;
tab = (uint16_t *)s->io_buffer;
memset(tab, 0, 512 * 2);
stw(tab + 0, 0x0040);
stw(tab + 1, s->cylinders);
stw(tab + 3, s->heads);
stw(tab + 4, 512 * s->sectors); /* sectors */
stw(tab + 5, 512); /* sector size */
stw(tab + 6, s->sectors);
stw(tab + 20, 3); /* buffer type */
stw(tab + 21, 512); /* cache size in sectors */
stw(tab + 22, 4); /* ecc bytes */
padstr((char *)(tab + 27), "RISCVEMU HARDDISK", 40);
stw(tab + 47, 0x8000 | MAX_MULT_SECTORS);
stw(tab + 48, 0); /* dword I/O */
stw(tab + 49, 1 << 9); /* LBA supported, no DMA */
stw(tab + 51, 0x200); /* PIO transfer cycle */
stw(tab + 52, 0x200); /* DMA transfer cycle */
stw(tab + 54, s->cylinders);
stw(tab + 55, s->heads);
stw(tab + 56, s->sectors);
oldsize = s->cylinders * s->heads * s->sectors;
stw(tab + 57, oldsize);
stw(tab + 58, oldsize >> 16);
if (s->mult_sectors)
stw(tab + 59, 0x100 | s->mult_sectors);
stw(tab + 60, s->nb_sectors);
stw(tab + 61, s->nb_sectors >> 16);
stw(tab + 80, (1 << 1) | (1 << 2));
stw(tab + 82, (1 << 14));
stw(tab + 83, (1 << 14));
stw(tab + 84, (1 << 14));
stw(tab + 85, (1 << 14));
stw(tab + 86, 0);
stw(tab + 87, (1 << 14));
}
static void ide_set_signature(IDEState *s)
{
s->select &= 0xf0;
s->nsector = 1;
s->sector = 1;
s->lcyl = 0;
s->hcyl = 0;
}
static void ide_abort_command(IDEState *s)
{
s->status = READY_STAT | ERR_STAT;
s->error = ABRT_ERR;
}
static void ide_set_irq(IDEState *s)
{
IDEIFState *ide_if = s->ide_if;
if (!(ide_if->cmd & IDE_CMD_DISABLE_IRQ)) {
set_irq(ide_if->irq, 1);
}
}
/* prepare data transfer and tell what to do after */
static void ide_transfer_start(IDEState *s, int size,
EndTransferFunc *end_transfer_func)
{
s->end_transfer_func = end_transfer_func;
s->data_index = 0;
s->data_end = size;
}
static void ide_transfer_stop(IDEState *s)
{
s->end_transfer_func = ide_transfer_stop;
s->data_index = 0;
s->data_end = 0;
}
static int64_t ide_get_sector(IDEState *s)
{
int64_t sector_num;
if (s->select & 0x40) {
/* lba */
sector_num = ((s->select & 0x0f) << 24) | (s->hcyl << 16) |
(s->lcyl << 8) | s->sector;
} else {
sector_num = ((s->hcyl << 8) | s->lcyl) *
s->heads * s->sectors +
(s->select & 0x0f) * s->sectors + (s->sector - 1);
}
return sector_num;
}
static void ide_set_sector(IDEState *s, int64_t sector_num)
{
unsigned int cyl, r;
if (s->select & 0x40) {
s->select = (s->select & 0xf0) | ((sector_num >> 24) & 0x0f);
s->hcyl = (sector_num >> 16) & 0xff;
s->lcyl = (sector_num >> 8) & 0xff;
s->sector = sector_num & 0xff;
} else {
cyl = sector_num / (s->heads * s->sectors);
r = sector_num % (s->heads * s->sectors);
s->hcyl = (cyl >> 8) & 0xff;
s->lcyl = cyl & 0xff;
s->select = (s->select & 0xf0) | ((r / s->sectors) & 0x0f);
s->sector = (r % s->sectors) + 1;
}
}
static void ide_sector_read(IDEState *s)
{
int64_t sector_num;
int ret, n;
sector_num = ide_get_sector(s);
n = s->nsector;
if (n == 0)
n = 256;
if (n > s->req_nb_sectors)
n = s->req_nb_sectors;
#if defined(DEBUG_IDE)
printf("read sector=%" PRId64 " count=%d\n", sector_num, n);
#endif
s->io_nb_sectors = n;
ret = s->bs->read_async(s->bs, sector_num, s->io_buffer, n,
ide_sector_read_cb, s);
if (ret < 0) {
/* error */
ide_abort_command(s);
ide_set_irq(s);
} else if (ret == 0) {
/* synchronous case (needed for performance) */
ide_sector_read_cb(s, 0);
} else {
/* async case */
s->status = READY_STAT | SEEK_STAT | BUSY_STAT;
s->error = 0; /* not needed by IDE spec, but needed by Windows */
}
}
static void ide_sector_read_cb(void *opaque, int ret)
{
IDEState *s = opaque;
int n;
EndTransferFunc *func;
n = s->io_nb_sectors;
ide_set_sector(s, ide_get_sector(s) + n);
s->nsector = (s->nsector - n) & 0xff;
if (s->nsector == 0)
func = ide_sector_read_cb_end;
else
func = ide_sector_read;
ide_transfer_start(s, 512 * n, func);
ide_set_irq(s);
s->status = READY_STAT | SEEK_STAT | DRQ_STAT;
s->error = 0; /* not needed by IDE spec, but needed by Windows */
}
static void ide_sector_read_cb_end(IDEState *s)
{
/* no more sector to read from disk */
s->status = READY_STAT | SEEK_STAT;
s->error = 0; /* not needed by IDE spec, but needed by Windows */
ide_transfer_stop(s);
}
static void ide_sector_write_cb1(IDEState *s)
{
int64_t sector_num;
int ret;
ide_transfer_stop(s);
sector_num = ide_get_sector(s);
#if defined(DEBUG_IDE)
printf("write sector=%" PRId64 " count=%d\n",
sector_num, s->io_nb_sectors);
#endif
ret = s->bs->write_async(s->bs, sector_num, s->io_buffer, s->io_nb_sectors,
ide_sector_write_cb2, s);
if (ret < 0) {
/* error */
ide_abort_command(s);
ide_set_irq(s);
} else if (ret == 0) {
/* synchronous case (needed for performance) */
ide_sector_write_cb2(s, 0);
} else {
/* async case */
s->status = READY_STAT | SEEK_STAT | BUSY_STAT;
}
}
static void ide_sector_write_cb2(void *opaque, int ret)
{
IDEState *s = opaque;
int n;
n = s->io_nb_sectors;
ide_set_sector(s, ide_get_sector(s) + n);
s->nsector = (s->nsector - n) & 0xff;
if (s->nsector == 0) {
/* no more sectors to write */
s->status = READY_STAT | SEEK_STAT;
} else {
n = s->nsector;
if (n > s->req_nb_sectors)
n = s->req_nb_sectors;
s->io_nb_sectors = n;
ide_transfer_start(s, 512 * n, ide_sector_write_cb1);
s->status = READY_STAT | SEEK_STAT | DRQ_STAT;
}
ide_set_irq(s);
}
static void ide_sector_write(IDEState *s)
{
int n;
n = s->nsector;
if (n == 0)
n = 256;
if (n > s->req_nb_sectors)
n = s->req_nb_sectors;
s->io_nb_sectors = n;
ide_transfer_start(s, 512 * n, ide_sector_write_cb1);
s->status = READY_STAT | SEEK_STAT | DRQ_STAT;
}
static void ide_identify_cb(IDEState *s)
{
ide_transfer_stop(s);
s->status = READY_STAT;
}
static void ide_exec_cmd(IDEState *s, int val)
{
#if defined(DEBUG_IDE)
printf("ide: exec_cmd=0x%02x\n", val);
#endif
switch(val) {
case WIN_IDENTIFY:
ide_identify(s);
s->status = READY_STAT | SEEK_STAT | DRQ_STAT;
ide_transfer_start(s, 512, ide_identify_cb);
ide_set_irq(s);
break;
case WIN_SPECIFY:
case WIN_RECAL:
s->error = 0;
s->status = READY_STAT | SEEK_STAT;
ide_set_irq(s);
break;
case WIN_SETMULT:
if (s->nsector > MAX_MULT_SECTORS ||
(s->nsector & (s->nsector - 1)) != 0) {
ide_abort_command(s);
} else {
s->mult_sectors = s->nsector;
#if defined(DEBUG_IDE)
printf("ide: setmult=%d\n", s->mult_sectors);
#endif
s->status = READY_STAT;
}
ide_set_irq(s);
break;
case WIN_READ:
case WIN_READ_ONCE:
s->req_nb_sectors = 1;
ide_sector_read(s);
break;
case WIN_WRITE:
case WIN_WRITE_ONCE:
s->req_nb_sectors = 1;
ide_sector_write(s);
break;
case WIN_MULTREAD:
if (!s->mult_sectors) {
ide_abort_command(s);
ide_set_irq(s);
} else {
s->req_nb_sectors = s->mult_sectors;
ide_sector_read(s);
}
break;
case WIN_MULTWRITE:
if (!s->mult_sectors) {
ide_abort_command(s);
ide_set_irq(s);
} else {
s->req_nb_sectors = s->mult_sectors;
ide_sector_write(s);
}
break;
case WIN_READ_NATIVE_MAX:
ide_set_sector(s, s->nb_sectors - 1);
s->status = READY_STAT;
ide_set_irq(s);
break;
default:
ide_abort_command(s);
ide_set_irq(s);
break;
}
}
static void ide_ioport_write(void *opaque, uint32_t offset,
uint32_t val, int size_log2)
{
IDEIFState *s1 = opaque;
IDEState *s = s1->cur_drive;
int addr = offset + 1;
#ifdef DEBUG_IDE
printf("ide: write addr=0x%02x val=0x%02x\n", addr, val);
#endif
switch(addr) {
case 0:
break;
case 1:
if (s) {
s->feature = val;
}
break;
case 2:
if (s) {
s->nsector = val;
}
break;
case 3:
if (s) {
s->sector = val;
}
break;
case 4:
if (s) {
s->lcyl = val;
}
break;
case 5:
if (s) {
s->hcyl = val;
}
break;
case 6:
/* select drive */
s = s1->cur_drive = s1->drives[(val >> 4) & 1];
if (s) {
s->select = val;
}
break;
default:
case 7:
/* command */
if (s) {
ide_exec_cmd(s, val);
}
break;
}
}
static uint32_t ide_ioport_read(void *opaque, uint32_t offset, int size_log2)
{
IDEIFState *s1 = opaque;
IDEState *s = s1->cur_drive;
int ret, addr = offset + 1;
if (!s) {
ret = 0x00;
} else {
switch(addr) {
case 0:
ret = 0xff;
break;
case 1:
ret = s->error;
break;
case 2:
ret = s->nsector;
break;
case 3:
ret = s->sector;
break;
case 4:
ret = s->lcyl;
break;
case 5:
ret = s->hcyl;
break;
case 6:
ret = s->select;
break;
default:
case 7:
ret = s->status;
set_irq(s1->irq, 0);
break;
}
}
#ifdef DEBUG_IDE
printf("ide: read addr=0x%02x val=0x%02x\n", addr, ret);
#endif
return ret;
}
static uint32_t ide_status_read(void *opaque, uint32_t offset, int size_log2)
{
IDEIFState *s1 = opaque;
IDEState *s = s1->cur_drive;
int ret;
if (s) {
ret = s->status;
} else {
ret = 0;
}
#ifdef DEBUG_IDE
printf("ide: read status=0x%02x\n", ret);
#endif
return ret;
}
static void ide_cmd_write(void *opaque, uint32_t offset,
uint32_t val, int size_log2)
{
IDEIFState *s1 = opaque;
IDEState *s;
int i;
#ifdef DEBUG_IDE
printf("ide: cmd write=0x%02x\n", val);
#endif
if (!(s1->cmd & IDE_CMD_RESET) && (val & IDE_CMD_RESET)) {
/* low to high */
for(i = 0; i < 2; i++) {
s = s1->drives[i];
if (s) {
s->status = BUSY_STAT | SEEK_STAT;
s->error = 0x01;
}
}
} else if ((s1->cmd & IDE_CMD_RESET) && !(val & IDE_CMD_RESET)) {
/* high to low */
for(i = 0; i < 2; i++) {
s = s1->drives[i];
if (s) {
s->status = READY_STAT | SEEK_STAT;
ide_set_signature(s);
}
}
}
s1->cmd = val;
}
static void ide_data_writew(void *opaque, uint32_t offset,
uint32_t val, int size_log2)
{
IDEIFState *s1 = opaque;
IDEState *s = s1->cur_drive;
int p;
uint8_t *tab;
if (!s)
return;
p = s->data_index;
tab = s->io_buffer;
tab[p] = val & 0xff;
tab[p + 1] = (val >> 8) & 0xff;
p += 2;
s->data_index = p;
if (p >= s->data_end)
s->end_transfer_func(s);
}
static uint32_t ide_data_readw(void *opaque, uint32_t offset, int size_log2)
{
IDEIFState *s1 = opaque;
IDEState *s = s1->cur_drive;
int p, ret;
uint8_t *tab;
if (!s) {
ret = 0;
} else {
p = s->data_index;
tab = s->io_buffer;
ret = tab[p] | (tab[p + 1] << 8);
p += 2;
s->data_index = p;
if (p >= s->data_end)
s->end_transfer_func(s);
}
return ret;
}
static IDEState *ide_drive_init(IDEIFState *ide_if, BlockDevice *bs)
{
IDEState *s;
uint32_t cylinders;
uint64_t nb_sectors;
s = malloc(sizeof(*s));
memset(s, 0, sizeof(*s));
s->ide_if = ide_if;
s->bs = bs;
nb_sectors = s->bs->get_sector_count(s->bs);
cylinders = nb_sectors / (16 * 63);
if (cylinders > 16383)
cylinders = 16383;
else if (cylinders < 2)
cylinders = 2;
s->cylinders = cylinders;
s->heads = 16;
s->sectors = 63;
s->nb_sectors = nb_sectors;
s->mult_sectors = MAX_MULT_SECTORS;
/* ide regs */
s->feature = 0;
s->error = 0;
s->nsector = 0;
s->sector = 0;
s->lcyl = 0;
s->hcyl = 0;
s->select = 0xa0;
s->status = READY_STAT | SEEK_STAT;
/* init I/O buffer */
s->data_index = 0;
s->data_end = 0;
s->end_transfer_func = ide_transfer_stop;
s->req_nb_sectors = 0; /* temp for read/write */
s->io_nb_sectors = 0; /* temp for read/write */
return s;
}
IDEIFState *ide_init(PhysMemoryMap *port_map, uint32_t addr, uint32_t addr2,
IRQSignal *irq, BlockDevice **tab_bs)
{
int i;
IDEIFState *s;
s = malloc(sizeof(IDEIFState));
memset(s, 0, sizeof(*s));
s->irq = irq;
s->cmd = 0;
cpu_register_device(port_map, addr, 1, s, ide_data_readw, ide_data_writew,
DEVIO_SIZE16);
cpu_register_device(port_map, addr + 1, 7, s, ide_ioport_read, ide_ioport_write,
DEVIO_SIZE8);
if (addr2) {
cpu_register_device(port_map, addr2, 1, s, ide_status_read, ide_cmd_write,
DEVIO_SIZE8);
}
for(i = 0; i < 2; i++) {
if (tab_bs[i])
s->drives[i] = ide_drive_init(s, tab_bs[i]);
}
s->cur_drive = s->drives[0];
return s;
}
/* dummy PCI device for the IDE */
PCIDevice *piix3_ide_init(PCIBus *pci_bus, int devfn)
{
PCIDevice *d;
d = pci_register_device(pci_bus, "PIIX3 IDE", devfn, 0x8086, 0x7010, 0x00, 0x0101);
pci_device_set_config8(d, 0x09, 0x00); /* ISA IDE ports, no DMA */
return d;
}

32
ide.h Normal file
View file

@ -0,0 +1,32 @@
/*
* IDE emulation
*
* Copyright (c) 2003-2016 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "virtio.h"
#include "iomem.h"
#include "pci.h"
typedef struct IDEIFState IDEIFState;
IDEIFState *ide_init(PhysMemoryMap *port_map, uint32_t addr, uint32_t addr2,
IRQSignal *irq, BlockDevice **tab_bs);
PCIDevice *piix3_ide_init(PCIBus *pci_bus, int devfn);

264
iomem.c Normal file
View file

@ -0,0 +1,264 @@
/*
* IO memory handling
*
* Copyright (c) 2016-2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include "cutils.h"
#include "iomem.h"
static PhysMemoryRange *default_register_ram(PhysMemoryMap *s, uint64_t addr,
uint64_t size, int devram_flags);
static void default_free_ram(PhysMemoryMap *s, PhysMemoryRange *pr);
static const uint32_t *default_get_dirty_bits(PhysMemoryMap *map, PhysMemoryRange *pr);
static void default_set_addr(PhysMemoryMap *map,
PhysMemoryRange *pr, uint64_t addr, BOOL enabled);
PhysMemoryMap *phys_mem_map_init(void)
{
PhysMemoryMap *s;
s = mallocz(sizeof(*s));
s->register_ram = default_register_ram;
s->free_ram = default_free_ram;
s->get_dirty_bits = default_get_dirty_bits;
s->set_ram_addr = default_set_addr;
return s;
}
void phys_mem_map_end(PhysMemoryMap *s)
{
int i;
PhysMemoryRange *pr;
for(i = 0; i < s->n_phys_mem_range; i++) {
pr = &s->phys_mem_range[i];
if (pr->is_ram) {
s->free_ram(s, pr);
}
}
free(s);
}
/* return NULL if not found */
/* XXX: optimize */
PhysMemoryRange *get_phys_mem_range(PhysMemoryMap *s, uint64_t paddr)
{
PhysMemoryRange *pr;
int i;
for(i = 0; i < s->n_phys_mem_range; i++) {
pr = &s->phys_mem_range[i];
if (paddr >= pr->addr && paddr < pr->addr + pr->size)
return pr;
}
return NULL;
}
PhysMemoryRange *register_ram_entry(PhysMemoryMap *s, uint64_t addr,
uint64_t size, int devram_flags)
{
PhysMemoryRange *pr;
assert(s->n_phys_mem_range < PHYS_MEM_RANGE_MAX);
assert((size & (DEVRAM_PAGE_SIZE - 1)) == 0 && size != 0);
pr = &s->phys_mem_range[s->n_phys_mem_range++];
pr->map = s;
pr->is_ram = TRUE;
pr->devram_flags = devram_flags & ~DEVRAM_FLAG_DISABLED;
pr->addr = addr;
pr->org_size = size;
if (devram_flags & DEVRAM_FLAG_DISABLED)
pr->size = 0;
else
pr->size = pr->org_size;
pr->phys_mem = NULL;
pr->dirty_bits = NULL;
return pr;
}
static PhysMemoryRange *default_register_ram(PhysMemoryMap *s, uint64_t addr,
uint64_t size, int devram_flags)
{
PhysMemoryRange *pr;
pr = register_ram_entry(s, addr, size, devram_flags);
pr->phys_mem = mallocz(size);
if (!pr->phys_mem) {
fprintf(stderr, "Could not allocate VM memory\n");
exit(1);
}
if (devram_flags & DEVRAM_FLAG_DIRTY_BITS) {
size_t nb_pages;
int i;
nb_pages = size >> DEVRAM_PAGE_SIZE_LOG2;
pr->dirty_bits_size = ((nb_pages + 31) / 32) * sizeof(uint32_t);
pr->dirty_bits_index = 0;
for(i = 0; i < 2; i++) {
pr->dirty_bits_tab[i] = mallocz(pr->dirty_bits_size);
}
pr->dirty_bits = pr->dirty_bits_tab[pr->dirty_bits_index];
}
return pr;
}
/* return a pointer to the bitmap of dirty bits and reset them */
static const uint32_t *default_get_dirty_bits(PhysMemoryMap *map,
PhysMemoryRange *pr)
{
uint32_t *dirty_bits;
BOOL has_dirty_bits;
size_t n, i;
dirty_bits = pr->dirty_bits;
has_dirty_bits = FALSE;
n = pr->dirty_bits_size / sizeof(uint32_t);
for(i = 0; i < n; i++) {
if (dirty_bits[i] != 0) {
has_dirty_bits = TRUE;
break;
}
}
if (has_dirty_bits && pr->size != 0) {
/* invalidate the corresponding CPU write TLBs */
map->flush_tlb_write_range(map->opaque, pr->phys_mem, pr->org_size);
}
pr->dirty_bits_index ^= 1;
pr->dirty_bits = pr->dirty_bits_tab[pr->dirty_bits_index];
memset(pr->dirty_bits, 0, pr->dirty_bits_size);
return dirty_bits;
}
/* reset the dirty bit of one page at 'offset' inside 'pr' */
void phys_mem_reset_dirty_bit(PhysMemoryRange *pr, size_t offset)
{
size_t page_index;
uint32_t mask, *dirty_bits_ptr;
PhysMemoryMap *map;
if (pr->dirty_bits) {
page_index = offset >> DEVRAM_PAGE_SIZE_LOG2;
mask = 1 << (page_index & 0x1f);
dirty_bits_ptr = pr->dirty_bits + (page_index >> 5);
if (*dirty_bits_ptr & mask) {
*dirty_bits_ptr &= ~mask;
/* invalidate the corresponding CPU write TLBs */
map = pr->map;
map->flush_tlb_write_range(map->opaque,
pr->phys_mem + (offset & ~(DEVRAM_PAGE_SIZE - 1)),
DEVRAM_PAGE_SIZE);
}
}
}
static void default_free_ram(PhysMemoryMap *s, PhysMemoryRange *pr)
{
free(pr->phys_mem);
}
PhysMemoryRange *cpu_register_device(PhysMemoryMap *s, uint64_t addr,
uint64_t size, void *opaque,
DeviceReadFunc *read_func, DeviceWriteFunc *write_func,
int devio_flags)
{
PhysMemoryRange *pr;
assert(s->n_phys_mem_range < PHYS_MEM_RANGE_MAX);
assert(size <= 0xffffffff);
pr = &s->phys_mem_range[s->n_phys_mem_range++];
pr->map = s;
pr->addr = addr;
pr->org_size = size;
if (devio_flags & DEVIO_DISABLED)
pr->size = 0;
else
pr->size = pr->org_size;
pr->is_ram = FALSE;
pr->opaque = opaque;
pr->read_func = read_func;
pr->write_func = write_func;
pr->devio_flags = devio_flags;
return pr;
}
static void default_set_addr(PhysMemoryMap *map,
PhysMemoryRange *pr, uint64_t addr, BOOL enabled)
{
if (enabled) {
if (pr->size == 0 || pr->addr != addr) {
/* enable or move mapping */
if (pr->is_ram) {
map->flush_tlb_write_range(map->opaque,
pr->phys_mem, pr->org_size);
}
pr->addr = addr;
pr->size = pr->org_size;
}
} else {
if (pr->size != 0) {
/* disable mapping */
if (pr->is_ram) {
map->flush_tlb_write_range(map->opaque,
pr->phys_mem, pr->org_size);
}
pr->addr = 0;
pr->size = 0;
}
}
}
void phys_mem_set_addr(PhysMemoryRange *pr, uint64_t addr, BOOL enabled)
{
PhysMemoryMap *map = pr->map;
if (!pr->is_ram) {
default_set_addr(map, pr, addr, enabled);
} else {
return map->set_ram_addr(map, pr, addr, enabled);
}
}
/* return NULL if no valid RAM page. The access can only be done in the page */
uint8_t *phys_mem_get_ram_ptr(PhysMemoryMap *map, uint64_t paddr, BOOL is_rw)
{
PhysMemoryRange *pr = get_phys_mem_range(map, paddr);
uintptr_t offset;
if (!pr || !pr->is_ram)
return NULL;
offset = paddr - pr->addr;
if (is_rw)
phys_mem_set_dirty_bit(pr, offset);
return pr->phys_mem + (uintptr_t)offset;
}
/* IRQ support */
void irq_init(IRQSignal *irq, SetIRQFunc *set_irq, void *opaque, int irq_num)
{
irq->set_irq = set_irq;
irq->opaque = opaque;
irq->irq_num = irq_num;
}

148
iomem.h Normal file
View file

@ -0,0 +1,148 @@
/*
* IO memory handling
*
* Copyright (c) 2016-2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef IOMEM_H
#define IOMEM_H
typedef void DeviceWriteFunc(void *opaque, uint32_t offset,
uint32_t val, int size_log2);
typedef uint32_t DeviceReadFunc(void *opaque, uint32_t offset, int size_log2);
#define DEVIO_SIZE8 (1 << 0)
#define DEVIO_SIZE16 (1 << 1)
#define DEVIO_SIZE32 (1 << 2)
/* not supported, could add specific 64 bit callbacks when needed */
//#define DEVIO_SIZE64 (1 << 3)
#define DEVIO_DISABLED (1 << 4)
#define DEVRAM_FLAG_ROM (1 << 0) /* not writable */
#define DEVRAM_FLAG_DIRTY_BITS (1 << 1) /* maintain dirty bits */
#define DEVRAM_FLAG_DISABLED (1 << 2) /* allocated but not mapped */
#define DEVRAM_PAGE_SIZE_LOG2 12
#define DEVRAM_PAGE_SIZE (1 << DEVRAM_PAGE_SIZE_LOG2)
typedef struct PhysMemoryMap PhysMemoryMap;
typedef struct {
PhysMemoryMap *map;
uint64_t addr;
uint64_t org_size; /* original size */
uint64_t size; /* =org_size or 0 if the mapping is disabled */
BOOL is_ram;
/* the following is used for RAM access */
int devram_flags;
uint8_t *phys_mem;
int dirty_bits_size; /* in bytes */
uint32_t *dirty_bits; /* NULL if not used */
uint32_t *dirty_bits_tab[2];
int dirty_bits_index; /* 0-1 */
/* the following is used for I/O access */
void *opaque;
DeviceReadFunc *read_func;
DeviceWriteFunc *write_func;
int devio_flags;
} PhysMemoryRange;
#define PHYS_MEM_RANGE_MAX 32
struct PhysMemoryMap {
int n_phys_mem_range;
PhysMemoryRange phys_mem_range[PHYS_MEM_RANGE_MAX];
PhysMemoryRange *(*register_ram)(PhysMemoryMap *s, uint64_t addr,
uint64_t size, int devram_flags);
void (*free_ram)(PhysMemoryMap *s, PhysMemoryRange *pr);
const uint32_t *(*get_dirty_bits)(PhysMemoryMap *s, PhysMemoryRange *pr);
void (*set_ram_addr)(PhysMemoryMap *s, PhysMemoryRange *pr, uint64_t addr,
BOOL enabled);
void *opaque;
void (*flush_tlb_write_range)(void *opaque, uint8_t *ram_addr,
size_t ram_size);
};
PhysMemoryMap *phys_mem_map_init(void);
void phys_mem_map_end(PhysMemoryMap *s);
PhysMemoryRange *register_ram_entry(PhysMemoryMap *s, uint64_t addr,
uint64_t size, int devram_flags);
static inline PhysMemoryRange *cpu_register_ram(PhysMemoryMap *s, uint64_t addr,
uint64_t size, int devram_flags)
{
return s->register_ram(s, addr, size, devram_flags);
}
PhysMemoryRange *cpu_register_device(PhysMemoryMap *s, uint64_t addr,
uint64_t size, void *opaque,
DeviceReadFunc *read_func, DeviceWriteFunc *write_func,
int devio_flags);
PhysMemoryRange *get_phys_mem_range(PhysMemoryMap *s, uint64_t paddr);
void phys_mem_set_addr(PhysMemoryRange *pr, uint64_t addr, BOOL enabled);
static inline const uint32_t *phys_mem_get_dirty_bits(PhysMemoryRange *pr)
{
PhysMemoryMap *map = pr->map;
return map->get_dirty_bits(map, pr);
}
static inline void phys_mem_set_dirty_bit(PhysMemoryRange *pr, size_t offset)
{
size_t page_index;
uint32_t mask, *dirty_bits_ptr;
if (pr->dirty_bits) {
page_index = offset >> DEVRAM_PAGE_SIZE_LOG2;
mask = 1 << (page_index & 0x1f);
dirty_bits_ptr = pr->dirty_bits + (page_index >> 5);
*dirty_bits_ptr |= mask;
}
}
static inline BOOL phys_mem_is_dirty_bit(PhysMemoryRange *pr, size_t offset)
{
size_t page_index;
uint32_t *dirty_bits_ptr;
if (!pr->dirty_bits)
return TRUE;
page_index = offset >> DEVRAM_PAGE_SIZE_LOG2;
dirty_bits_ptr = pr->dirty_bits + (page_index >> 5);
return (*dirty_bits_ptr >> (page_index & 0x1f)) & 1;
}
void phys_mem_reset_dirty_bit(PhysMemoryRange *pr, size_t offset);
uint8_t *phys_mem_get_ram_ptr(PhysMemoryMap *map, uint64_t paddr, BOOL is_rw);
/* IRQ support */
typedef void SetIRQFunc(void *opaque, int irq_num, int level);
typedef struct {
SetIRQFunc *set_irq;
void *opaque;
int irq_num;
} IRQSignal;
void irq_init(IRQSignal *irq, SetIRQFunc *set_irq, void *opaque, int irq_num);
static inline void set_irq(IRQSignal *irq, int level)
{
irq->set_irq(irq->opaque, irq->irq_num, level);
}
#endif /* IOMEM_H */

280
js/lib.js Normal file
View file

@ -0,0 +1,280 @@
/*
* JS emulator library
*
* Copyright (c) 2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
mergeInto(LibraryManager.library, {
console_write: function(opaque, buf, len)
{
var str;
/* Note: we really send byte values. It would be up to the
* terminal to support UTF-8 */
str = String.fromCharCode.apply(String, HEAPU8.subarray(buf, buf + len));
term.write(str);
},
console_get_size: function(pw, ph)
{
var w, h, r;
r = term.getSize();
HEAPU32[pw >> 2] = r[0];
HEAPU32[ph >> 2] = r[1];
},
fs_export_file: function(filename, buf, buf_len)
{
var _filename = Pointer_stringify(filename);
// console.log("exporting " + _filename);
var data = HEAPU8.subarray(buf, buf + buf_len);
var file = new Blob([data], { type: "application/octet-stream" });
var url = URL.createObjectURL(file);
var a = document.createElement("a");
a.href = url;
a.setAttribute("download", _filename);
a.innerHTML = "downloading";
document.body.appendChild(a);
/* click on the link and remove it */
setTimeout(function() {
a.click();
document.body.removeChild(a);
}, 50);
},
emscripten_async_wget3_data: function(url, request, user, password, post_data, post_data_len, arg, free, onload, onerror, onprogress) {
var _url = Pointer_stringify(url);
var _request = Pointer_stringify(request);
var _user;
var _password;
var http = new XMLHttpRequest();
if (user)
_user = Pointer_stringify(user);
else
_user = null;
if (password)
_password = Pointer_stringify(password);
else
_password = null;
http.open(_request, _url, true);
http.responseType = 'arraybuffer';
if (_user) {
http.setRequestHeader("Authorization", "Basic " + btoa(_user + ':' + _password));
}
var handle = Browser.getNextWgetRequestHandle();
// LOAD
http.onload = function http_onload(e) {
if (http.status == 200 || _url.substr(0,4).toLowerCase() != "http") {
var byteArray = new Uint8Array(http.response);
var buffer = _malloc(byteArray.length);
HEAPU8.set(byteArray, buffer);
if (onload) Runtime.dynCall('viiii', onload, [handle, arg, buffer, byteArray.length]);
if (free) _free(buffer);
} else {
if (onerror) Runtime.dynCall('viiii', onerror, [handle, arg, http.status, http.statusText]);
}
delete Browser.wgetRequests[handle];
};
// ERROR
http.onerror = function http_onerror(e) {
if (onerror) {
Runtime.dynCall('viiii', onerror, [handle, arg, http.status, http.statusText]);
}
delete Browser.wgetRequests[handle];
};
// PROGRESS
http.onprogress = function http_onprogress(e) {
if (onprogress) Runtime.dynCall('viiii', onprogress, [handle, arg, e.loaded, e.lengthComputable || e.lengthComputable === undefined ? e.total : 0]);
};
// ABORT
http.onabort = function http_onabort(e) {
delete Browser.wgetRequests[handle];
};
// Useful because the browser can limit the number of redirection
try {
if (http.channel instanceof Ci.nsIHttpChannel)
http.channel.redirectionLimit = 0;
} catch (ex) { /* whatever */ }
if (_request == "POST") {
var _post_data = HEAPU8.subarray(post_data, post_data + post_data_len);
//Send the proper header information along with the request
http.setRequestHeader("Content-type", "application/octet-stream");
http.setRequestHeader("Content-length", post_data_len);
http.setRequestHeader("Connection", "close");
http.send(_post_data);
} else {
http.send(null);
}
Browser.wgetRequests[handle] = http;
return handle;
},
fs_wget_update_downloading: function (flag)
{
update_downloading(Boolean(flag));
},
fb_refresh: function(opaque, data, x, y, w, h, stride)
{
var i, j, v, src, image_data, dst_pos, display, dst_pos1, image_stride;
display = graphic_display;
/* current x = 0 and w = width for all refreshes */
// console.log("fb_refresh: x=" + x + " y=" + y + " w=" + w + " h=" + h);
image_data = display.image.data;
image_stride = display.width * 4;
dst_pos1 = (y * display.width + x) * 4;
for(i = 0; i < h; i = (i + 1) | 0) {
src = data;
dst_pos = dst_pos1;
for(j = 0; j < w; j = (j + 1) | 0) {
v = HEAPU32[src >> 2];
image_data[dst_pos] = (v >> 16) & 0xff;
image_data[dst_pos + 1] = (v >> 8) & 0xff;
image_data[dst_pos + 2] = v & 0xff;
image_data[dst_pos + 3] = 0xff; /* XXX: do it once */
src = (src + 4) | 0;
dst_pos = (dst_pos + 4) | 0;
}
data = (data + stride) | 0;
dst_pos1 = (dst_pos1 + image_stride) | 0;
}
display.ctx.putImageData(display.image, 0, 0, x, y, w, h);
},
net_recv_packet: function(bs, buf, buf_len)
{
if (net_state) {
net_state.recv_packet(HEAPU8.subarray(buf, buf + buf_len));
}
},
/* file buffer API */
file_buffer_get_new_handle: function()
{
var h;
if (typeof Browser.fbuf_table == "undefined") {
Browser.fbuf_table = new Object();
Browser.fbuf_next_handle = 1;
}
for(;;) {
h = Browser.fbuf_next_handle;
Browser.fbuf_next_handle++;
if (Browser.fbuf_next_handle == 0x80000000)
Browser.fbuf_next_handle = 1;
if (typeof Browser.fbuf_table[h] == "undefined") {
// console.log("new handle=" + h);
return h;
}
}
},
file_buffer_init: function(bs)
{
var h;
HEAPU32[bs >> 2] = 0;
HEAPU32[(bs + 4) >> 2] = 0;
},
file_buffer_resize__deps: ['file_buffer_get_new_handle'],
file_buffer_resize: function(bs, new_size)
{
var h, size, new_data, size1, i, data;
h = HEAPU32[bs >> 2];
size = HEAPU32[(bs + 4) >> 2];
if (new_size == 0) {
if (h != 0) {
delete Browser.fbuf_table[h];
h = 0;
}
} else if (size == 0) {
h = _file_buffer_get_new_handle();
new_data = new Uint8Array(new_size);
Browser.fbuf_table[h] = new_data;
} else if (size != new_size) {
data = Browser.fbuf_table[h];
new_data = new Uint8Array(new_size);
if (new_size > size) {
new_data.set(data, 0);
} else {
for(i = 0; i < new_size; i = (i + 1) | 0)
new_data[i] = data[i];
}
Browser.fbuf_table[h] = new_data;
}
HEAPU32[bs >> 2] = h;
HEAPU32[(bs + 4) >> 2] = new_size;
return 0;
},
file_buffer_reset: function(bs)
{
_file_buffer_resize(bs, 0);
_file_buffer_init(bs);
},
file_buffer_write: function(bs, offset, buf, size)
{
var h, data, i;
h = HEAPU32[bs >> 2];
if (h) {
data = Browser.fbuf_table[h];
for(i = 0; i < size; i = (i + 1) | 0) {
data[offset + i] = HEAPU8[buf + i];
}
}
},
file_buffer_read: function(bs, offset, buf, size)
{
var h, data, i;
h = HEAPU32[bs >> 2];
if (h) {
data = Browser.fbuf_table[h];
for(i = 0; i < size; i = (i + 1) | 0) {
HEAPU8[buf + i] = data[offset + i];
}
}
},
file_buffer_set: function(bs, offset, val, size)
{
var h, data, i;
h = HEAPU32[bs >> 2];
if (h) {
data = Browser.fbuf_table[h];
for(i = 0; i < size; i = (i + 1) | 0) {
data[offset + i] = val;
}
}
},
});

349
jsemu.c Normal file
View file

@ -0,0 +1,349 @@
/*
* JS emulator main
*
* Copyright (c) 2016-2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <emscripten.h>
#include "cutils.h"
#include "iomem.h"
#include "virtio.h"
#include "machine.h"
#include "list.h"
#include "fbuf.h"
void virt_machine_run(void *opaque);
/* provided in lib.js */
extern void console_write(void *opaque, const uint8_t *buf, int len);
extern void console_get_size(int *pw, int *ph);
extern void fb_refresh(void *opaque, void *data,
int x, int y, int w, int h, int stride);
extern void net_recv_packet(EthernetDevice *bs,
const uint8_t *buf, int len);
static uint8_t console_fifo[1024];
static int console_fifo_windex;
static int console_fifo_rindex;
static int console_fifo_count;
static BOOL console_resize_pending;
static int global_width;
static int global_height;
static VirtMachine *global_vm;
static BOOL global_carrier_state;
static int console_read(void *opaque, uint8_t *buf, int len)
{
int out_len, l;
len = min_int(len, console_fifo_count);
console_fifo_count -= len;
out_len = 0;
while (len != 0) {
l = min_int(len, sizeof(console_fifo) - console_fifo_rindex);
memcpy(buf + out_len, console_fifo + console_fifo_rindex, l);
len -= l;
out_len += l;
console_fifo_rindex += l;
if (console_fifo_rindex == sizeof(console_fifo))
console_fifo_rindex = 0;
}
return out_len;
}
/* called from JS */
void console_queue_char(int c)
{
if (console_fifo_count < sizeof(console_fifo)) {
console_fifo[console_fifo_windex] = c;
if (++console_fifo_windex == sizeof(console_fifo))
console_fifo_windex = 0;
console_fifo_count++;
}
}
/* called from JS */
void display_key_event(int is_down, int key_code)
{
if (global_vm) {
vm_send_key_event(global_vm, is_down, key_code);
}
}
/* called from JS */
static int mouse_last_x, mouse_last_y, mouse_last_buttons;
void display_mouse_event(int dx, int dy, int buttons)
{
if (global_vm) {
if (vm_mouse_is_absolute(global_vm) || 1) {
dx = min_int(dx, global_width - 1);
dy = min_int(dy, global_height - 1);
dx = (dx * VIRTIO_INPUT_ABS_SCALE) / global_width;
dy = (dy * VIRTIO_INPUT_ABS_SCALE) / global_height;
} else {
/* relative mouse is not supported */
dx = 0;
dy = 0;
}
mouse_last_x = dx;
mouse_last_y = dy;
mouse_last_buttons = buttons;
vm_send_mouse_event(global_vm, dx, dy, 0, buttons);
}
}
/* called from JS */
void display_wheel_event(int dz)
{
if (global_vm) {
vm_send_mouse_event(global_vm, mouse_last_x, mouse_last_y, dz,
mouse_last_buttons);
}
}
/* called from JS */
void net_write_packet(const uint8_t *buf, int buf_len)
{
EthernetDevice *net = global_vm->net;
if (net) {
net->device_write_packet(net, buf, buf_len);
}
}
/* called from JS */
void net_set_carrier(BOOL carrier_state)
{
EthernetDevice *net;
global_carrier_state = carrier_state;
if (global_vm && global_vm->net) {
net = global_vm->net;
net->device_set_carrier(net, carrier_state);
}
}
static void fb_refresh1(FBDevice *fb_dev, void *opaque,
int x, int y, int w, int h)
{
int stride = fb_dev->stride;
fb_refresh(opaque, fb_dev->fb_data + y * stride + x * 4, x, y, w, h,
stride);
}
static CharacterDevice *console_init(void)
{
CharacterDevice *dev;
console_resize_pending = TRUE;
dev = mallocz(sizeof(*dev));
dev->write_data = console_write;
dev->read_data = console_read;
return dev;
}
typedef struct {
VirtMachineParams *p;
int ram_size;
char *cmdline;
BOOL has_network;
char *pwd;
} VMStartState;
static void init_vm(void *arg);
static void init_vm_fs(void *arg);
static void init_vm_drive(void *arg);
void vm_start(const char *url, int ram_size, const char *cmdline,
const char *pwd, int width, int height, BOOL has_network)
{
VMStartState *s;
s = mallocz(sizeof(*s));
s->ram_size = ram_size;
s->cmdline = strdup(cmdline);
if (pwd)
s->pwd = strdup(pwd);
global_width = width;
global_height = height;
s->has_network = has_network;
s->p = mallocz(sizeof(VirtMachineParams));
virt_machine_set_defaults(s->p);
virt_machine_load_config_file(s->p, url, init_vm_fs, s);
}
static void init_vm_fs(void *arg)
{
VMStartState *s = arg;
VirtMachineParams *p = s->p;
if (p->fs_count > 0) {
assert(p->fs_count == 1);
p->tab_fs[0].fs_dev = fs_net_init(p->tab_fs[0].filename,
init_vm_drive, s);
if (s->pwd) {
fs_net_set_pwd(p->tab_fs[0].fs_dev, s->pwd);
}
} else {
init_vm_drive(s);
}
}
static void init_vm_drive(void *arg)
{
VMStartState *s = arg;
VirtMachineParams *p = s->p;
if (p->drive_count > 0) {
assert(p->drive_count == 1);
p->tab_drive[0].block_dev =
block_device_init_http(p->tab_drive[0].filename,
131072,
init_vm, s);
} else {
init_vm(s);
}
}
static void init_vm(void *arg)
{
VMStartState *s = arg;
VirtMachine *m;
VirtMachineParams *p = s->p;
int i;
p->rtc_real_time = TRUE;
p->ram_size = s->ram_size << 20;
if (s->cmdline && s->cmdline[0] != '\0') {
vm_add_cmdline(s->p, s->cmdline);
}
if (global_width > 0 && global_height > 0) {
/* enable graphic output if needed */
if (!p->display_device)
p->display_device = strdup("simplefb");
p->width = global_width;
p->height = global_height;
} else {
p->console = console_init();
}
if (p->eth_count > 0 && !s->has_network) {
/* remove the interfaces */
for(i = 0; i < p->eth_count; i++) {
free(p->tab_eth[i].ifname);
free(p->tab_eth[i].driver);
}
p->eth_count = 0;
}
if (p->eth_count > 0) {
EthernetDevice *net;
int i;
assert(p->eth_count == 1);
net = mallocz(sizeof(EthernetDevice));
net->mac_addr[0] = 0x02;
for(i = 1; i < 6; i++)
net->mac_addr[i] = (int)(emscripten_random() * 256);
net->write_packet = net_recv_packet;
net->opaque = NULL;
p->tab_eth[0].net = net;
}
m = virt_machine_init(p);
global_vm = m;
virt_machine_free_config(s->p);
if (m->net) {
m->net->device_set_carrier(m->net, global_carrier_state);
}
free(s->p);
free(s->cmdline);
if (s->pwd) {
memset(s->pwd, 0, strlen(s->pwd));
free(s->pwd);
}
free(s);
emscripten_async_call(virt_machine_run, m, 0);
}
/* need to be long enough to hide the non zero delay of setTimeout(_, 0) */
#define MAX_EXEC_TOTAL_CYCLE 3000000
#define MAX_EXEC_CYCLE 200000
#define MAX_SLEEP_TIME 10 /* in ms */
void virt_machine_run(void *opaque)
{
VirtMachine *m = opaque;
int delay, i;
FBDevice *fb_dev;
if (m->console_dev && virtio_console_can_write_data(m->console_dev)) {
uint8_t buf[128];
int ret, len;
len = virtio_console_get_write_len(m->console_dev);
len = min_int(len, sizeof(buf));
ret = m->console->read_data(m->console->opaque, buf, len);
if (ret > 0)
virtio_console_write_data(m->console_dev, buf, ret);
if (console_resize_pending) {
int w, h;
console_get_size(&w, &h);
virtio_console_resize_event(m->console_dev, w, h);
console_resize_pending = FALSE;
}
}
fb_dev = m->fb_dev;
if (fb_dev) {
/* refresh the display */
fb_dev->refresh(fb_dev, fb_refresh1, NULL);
}
i = 0;
for(;;) {
/* wait for an event: the only asynchronous event is the RTC timer */
delay = virt_machine_get_sleep_duration(m, MAX_SLEEP_TIME);
if (delay != 0 || i >= MAX_EXEC_TOTAL_CYCLE / MAX_EXEC_CYCLE)
break;
virt_machine_interp(m, MAX_EXEC_CYCLE);
i++;
}
if (delay == 0) {
emscripten_async_call(virt_machine_run, m, 0);
} else {
emscripten_async_call(virt_machine_run, m, MAX_SLEEP_TIME);
}
}

464
json.c Normal file
View file

@ -0,0 +1,464 @@
/*
* Pseudo JSON parser
*
* Copyright (c) 2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <ctype.h>
#include "cutils.h"
#include "json.h"
#include "fs_utils.h"
static JSONValue parse_string(const char **pp)
{
char buf[4096], *q;
const char *p;
int c, h;
q = buf;
p = *pp;
p++;
for(;;) {
c = *p++;
if (c == '\0' || c == '\n') {
return json_error_new("unterminated string");
} else if (c == '\"') {
break;
} else if (c == '\\') {
c = *p++;
switch(c) {
case '\'':
case '\"':
case '\\':
goto add_char;
case 'n':
c = '\n';
goto add_char;
case 'r':
c = '\r';
goto add_char;
case 't':
c = '\t';
goto add_char;
case 'x':
h = from_hex(*p++);
if (h < 0)
return json_error_new("invalig hex digit");
c = h << 4;
h = from_hex(*p++);
if (h < 0)
return json_error_new("invalig hex digit");
c |= h;
goto add_char;
default:
return json_error_new("unknown escape code");
}
} else {
add_char:
if (q >= buf + sizeof(buf) - 1)
return json_error_new("string too long");
*q++ = c;
}
}
*q = '\0';
*pp = p;
return json_string_new(buf);
}
static JSONProperty *json_object_get2(JSONObject *obj, const char *name)
{
JSONProperty *f;
int i;
for(i = 0; i < obj->len; i++) {
f = &obj->props[i];
if (!strcmp(f->name.u.str->data, name))
return f;
}
return NULL;
}
JSONValue json_object_get(JSONValue val, const char *name)
{
JSONProperty *f;
JSONObject *obj;
if (val.type != JSON_OBJ)
return json_undefined_new();
obj = val.u.obj;
f = json_object_get2(obj, name);
if (!f)
return json_undefined_new();
return f->value;
}
int json_object_set(JSONValue val, const char *name, JSONValue prop_val)
{
JSONObject *obj;
JSONProperty *f;
int new_size;
if (val.type != JSON_OBJ)
return -1;
obj = val.u.obj;
f = json_object_get2(obj, name);
if (f) {
json_free(f->value);
f->value = prop_val;
} else {
if (obj->len >= obj->size) {
new_size = max_int(obj->len + 1, obj->size * 3 / 2);
obj->props = realloc(obj->props, new_size * sizeof(JSONProperty));
obj->size = new_size;
}
f = &obj->props[obj->len++];
f->name = json_string_new(name);
f->value = prop_val;
}
return 0;
}
JSONValue json_array_get(JSONValue val, unsigned int idx)
{
JSONArray *array;
if (val.type != JSON_ARRAY)
return json_undefined_new();
array = val.u.array;
if (idx < array->len) {
return array->tab[idx];
} else {
return json_undefined_new();
}
}
int json_array_set(JSONValue val, unsigned int idx, JSONValue prop_val)
{
JSONArray *array;
int new_size;
if (val.type != JSON_ARRAY)
return -1;
array = val.u.array;
if (idx < array->len) {
json_free(array->tab[idx]);
array->tab[idx] = prop_val;
} else if (idx == array->len) {
if (array->len >= array->size) {
new_size = max_int(array->len + 1, array->size * 3 / 2);
array->tab = realloc(array->tab, new_size * sizeof(JSONValue));
array->size = new_size;
}
array->tab[array->len++] = prop_val;
} else {
return -1;
}
return 0;
}
const char *json_get_str(JSONValue val)
{
if (val.type != JSON_STR)
return NULL;
return val.u.str->data;
}
const char *json_get_error(JSONValue val)
{
if (val.type != JSON_EXCEPTION)
return NULL;
return val.u.str->data;
}
JSONValue json_string_new2(const char *str, int len)
{
JSONValue val;
JSONString *str1;
str1 = malloc(sizeof(JSONString) + len + 1);
str1->len = len;
memcpy(str1->data, str, len + 1);
val.type = JSON_STR;
val.u.str = str1;
return val;
}
JSONValue json_string_new(const char *str)
{
return json_string_new2(str, strlen(str));
}
JSONValue __attribute__((format(printf, 1, 2))) json_error_new(const char *fmt, ...)
{
JSONValue val;
va_list ap;
char buf[256];
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
val = json_string_new(buf);
val.type = JSON_EXCEPTION;
return val;
}
JSONValue json_object_new(void)
{
JSONValue val;
JSONObject *obj;
obj = mallocz(sizeof(JSONObject));
val.type = JSON_OBJ;
val.u.obj = obj;
return val;
}
JSONValue json_array_new(void)
{
JSONValue val;
JSONArray *array;
array = mallocz(sizeof(JSONArray));
val.type = JSON_ARRAY;
val.u.array = array;
return val;
}
void json_free(JSONValue val)
{
switch(val.type) {
case JSON_STR:
case JSON_EXCEPTION:
free(val.u.str);
break;
case JSON_INT:
case JSON_BOOL:
case JSON_NULL:
case JSON_UNDEFINED:
break;
case JSON_ARRAY:
{
JSONArray *array = val.u.array;
int i;
for(i = 0; i < array->len; i++) {
json_free(array->tab[i]);
}
free(array);
}
break;
case JSON_OBJ:
{
JSONObject *obj = val.u.obj;
JSONProperty *f;
int i;
for(i = 0; i < obj->len; i++) {
f = &obj->props[i];
json_free(f->name);
json_free(f->value);
}
free(obj);
}
break;
default:
abort();
}
}
static void skip_spaces(const char **pp)
{
const char *p;
p = *pp;
for(;;) {
if (isspace(*p)) {
p++;
} else if (p[0] == '/' && p[1] == '/') {
p += 2;
while (*p != '\0' && *p != '\n')
p++;
} else if (p[0] == '/' && p[1] == '*') {
p += 2;
while (*p != '\0' && (p[0] != '*' || p[1] != '/'))
p++;
if (*p != '\0')
p += 2;
} else {
break;
}
}
*pp = p;
}
static inline BOOL is_ident_first(int c)
{
return (c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
c == '_' || c == '$';
}
static int parse_ident(char *buf, int buf_size, const char **pp)
{
char *q;
const char *p;
p = *pp;
q = buf;
*q++ = *p++; /* first char is already tested */
while (is_ident_first(*p) || isdigit(*p)) {
if ((q - buf) >= buf_size - 1)
return -1;
*q++ = *p++;
}
*pp = p;
*q = '\0';
return 0;
}
JSONValue json_parse_value2(const char **pp)
{
char buf[128];
const char *p;
JSONValue val, val1, tag;
p = *pp;
skip_spaces(&p);
if (*p == '\0') {
return json_error_new("unexpected end of file");
}
if (isdigit(*p)) {
val = json_int32_new(strtol(p, (char **)&p, 0));
} else if (*p == '"') {
val = parse_string(&p);
} else if (*p == '{') {
p++;
val = json_object_new();
for(;;) {
skip_spaces(&p);
if (*p == '}') {
p++;
break;
}
if (*p == '"') {
tag = parse_string(&p);
if (json_is_error(tag))
return tag;
} else if (is_ident_first(*p)) {
if (parse_ident(buf, sizeof(buf), &p) < 0)
goto invalid_prop;
tag = json_string_new(buf);
} else {
goto invalid_prop;
}
// printf("property: %s\n", json_get_str(tag));
if (tag.u.str->len == 0) {
invalid_prop:
return json_error_new("Invalid property name");
}
skip_spaces(&p);
if (*p != ':') {
return json_error_new("':' expected");
}
p++;
val1 = json_parse_value2(&p);
json_object_set(val, tag.u.str->data, val1);
skip_spaces(&p);
if (*p == ',') {
p++;
} else if (*p != '}') {
return json_error_new("expecting ',' or '}'");
}
}
} else if (*p == '[') {
int idx;
p++;
val = json_array_new();
idx = 0;
for(;;) {
skip_spaces(&p);
if (*p == ']') {
p++;
break;
}
val1 = json_parse_value2(&p);
json_array_set(val, idx++, val1);
skip_spaces(&p);
if (*p == ',') {
p++;
} else if (*p != ']') {
return json_error_new("expecting ',' or ']'");
}
}
} else if (is_ident_first(*p)) {
if (parse_ident(buf, sizeof(buf), &p) < 0)
goto unknown_id;
if (!strcmp(buf, "null")) {
val = json_null_new();
} else if (!strcmp(buf, "true")) {
val = json_bool_new(TRUE);
} else if (!strcmp(buf, "false")) {
val = json_bool_new(FALSE);
} else {
unknown_id:
return json_error_new("unknown identifier: '%s'", buf);
}
} else {
return json_error_new("unexpected character");
}
*pp = p;
return val;
}
JSONValue json_parse_value(const char *p)
{
JSONValue val;
val = json_parse_value2(&p);
if (json_is_error(val))
return val;
skip_spaces(&p);
if (*p != '\0') {
json_free(val);
return json_error_new("unexpected characters at the end");
}
return val;
}
JSONValue json_parse_value_len(const char *p, int len)
{
char *str;
JSONValue val;
str = malloc(len + 1);
memcpy(str, p, len);
str[len] = '\0';
val = json_parse_value(str);
free(str);
return val;
}

132
json.h Normal file
View file

@ -0,0 +1,132 @@
/*
* Pseudo JSON parser
*
* Copyright (c) 2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef JSON_H
#define JSON_H
typedef enum {
JSON_STR,
JSON_INT,
JSON_OBJ,
JSON_ARRAY,
JSON_BOOL,
JSON_NULL,
JSON_UNDEFINED,
JSON_EXCEPTION,
} JSONTypeEnum;
typedef struct {
int len;
char data[0];
} JSONString;
typedef struct JSONValue {
JSONTypeEnum type;
union {
JSONString *str;
int int32;
BOOL b;
struct JSONObject *obj;
struct JSONArray *array;
} u;
} JSONValue;
typedef struct JSONProperty {
JSONValue name;
JSONValue value;
} JSONProperty;
typedef struct JSONObject {
int len;
int size;
JSONProperty *props;
} JSONObject;
typedef struct JSONArray {
int len;
int size;
JSONValue *tab;
} JSONArray;
JSONValue json_string_new2(const char *str, int len);
JSONValue json_string_new(const char *str);
JSONValue __attribute__((format(printf, 1, 2))) json_error_new(const char *fmt, ...);
void json_free(JSONValue val);
JSONValue json_object_new(void);
JSONValue json_object_get(JSONValue val, const char *name);
int json_object_set(JSONValue val, const char *name, JSONValue prop_val);
JSONValue json_array_new(void);
JSONValue json_array_get(JSONValue val, unsigned int idx);
int json_array_set(JSONValue val, unsigned int idx, JSONValue prop_val);
static inline BOOL json_is_error(JSONValue val)
{
return val.type == JSON_EXCEPTION;
}
static inline BOOL json_is_undefined(JSONValue val)
{
return val.type == JSON_UNDEFINED;
}
static inline JSONValue json_undefined_new(void)
{
JSONValue val;
val.type = JSON_UNDEFINED;
val.u.int32 = 0;
return val;
}
static inline JSONValue json_null_new(void)
{
JSONValue val;
val.type = JSON_NULL;
val.u.int32 = 0;
return val;
}
static inline JSONValue json_int32_new(int v)
{
JSONValue val;
val.type = JSON_INT;
val.u.int32 = v;
return val;
}
static inline JSONValue json_bool_new(BOOL v)
{
JSONValue val;
val.type = JSON_BOOL;
val.u.b = v;
return val;
}
const char *json_get_str(JSONValue val);
const char *json_get_error(JSONValue val);
JSONValue json_parse_value(const char *p);
JSONValue json_parse_value_len(const char *p, int len);
#endif /* JSON_H */

94
list.h Normal file
View file

@ -0,0 +1,94 @@
/*
* Linux klist like system
*
* Copyright (c) 2016-2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef LIST_H
#define LIST_H
struct list_head {
struct list_head *prev;
struct list_head *next;
};
/* return the pointer of type 'type *' containing 'el' as field 'member' */
#define list_entry(el, type, member) \
((type *)((uint8_t *)(el) - offsetof(type, member)))
static inline void init_list_head(struct list_head *head)
{
head->prev = head;
head->next = head;
}
/* insert 'el' between 'prev' and 'next' */
static inline void __list_add(struct list_head *el,
struct list_head *prev, struct list_head *next)
{
prev->next = el;
el->prev = prev;
el->next = next;
next->prev = el;
}
/* add 'el' at the head of the list 'head' (= after element head) */
static inline void list_add(struct list_head *el, struct list_head *head)
{
__list_add(el, head, head->next);
}
/* add 'el' at the end of the list 'head' (= before element head) */
static inline void list_add_tail(struct list_head *el, struct list_head *head)
{
__list_add(el, head->prev, head);
}
static inline void list_del(struct list_head *el)
{
struct list_head *prev, *next;
prev = el->prev;
next = el->next;
prev->next = next;
next->prev = prev;
el->prev = NULL; /* fail safe */
el->next = NULL; /* fail safe */
}
static inline int list_empty(struct list_head *el)
{
return el->next == el;
}
#define list_for_each(el, head) \
for(el = (head)->next; el != (head); el = el->next)
#define list_for_each_safe(el, el1, head) \
for(el = (head)->next, el1 = el->next; el != (head); \
el = el1, el1 = el->next)
#define list_for_each_prev(el, head) \
for(el = (head)->prev; el != (head); el = el->prev)
#define list_for_each_prev_safe(el, el1, head) \
for(el = (head)->prev, el1 = el->prev; el != (head); \
el = el1, el1 = el->prev)
#endif /* LIST_H */

640
machine.c Normal file
View file

@ -0,0 +1,640 @@
/*
* VM utilities
*
* Copyright (c) 2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include "cutils.h"
#include "iomem.h"
#include "virtio.h"
#include "machine.h"
#include "fs_utils.h"
#ifdef CONFIG_FS_NET
#include "fs_wget.h"
#endif
void __attribute__((format(printf, 1, 2))) vm_error(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
#ifdef EMSCRIPTEN
vprintf(fmt, ap);
#else
vfprintf(stderr, fmt, ap);
#endif
va_end(ap);
}
int vm_get_int(JSONValue obj, const char *name, int *pval)
{
JSONValue val;
val = json_object_get(obj, name);
if (json_is_undefined(val)) {
vm_error("expecting '%s' property\n", name);
return -1;
}
if (val.type != JSON_INT) {
vm_error("%s: integer expected\n", name);
return -1;
}
*pval = val.u.int32;
return 0;
}
int vm_get_int_opt(JSONValue obj, const char *name, int *pval, int def_val)
{
JSONValue val;
val = json_object_get(obj, name);
if (json_is_undefined(val)) {
*pval = def_val;
return 0;
}
if (val.type != JSON_INT) {
vm_error("%s: integer expected\n", name);
return -1;
}
*pval = val.u.int32;
return 0;
}
static int vm_get_str2(JSONValue obj, const char *name, const char **pstr,
BOOL is_opt)
{
JSONValue val;
val = json_object_get(obj, name);
if (json_is_undefined(val)) {
if (is_opt) {
*pstr = NULL;
return 0;
} else {
vm_error("expecting '%s' property\n", name);
return -1;
}
}
if (val.type != JSON_STR) {
vm_error("%s: string expected\n", name);
return -1;
}
*pstr = val.u.str->data;
return 0;
}
static int vm_get_str(JSONValue obj, const char *name, const char **pstr)
{
return vm_get_str2(obj, name, pstr, FALSE);
}
static int vm_get_str_opt(JSONValue obj, const char *name, const char **pstr)
{
return vm_get_str2(obj, name, pstr, TRUE);
}
static char *strdup_null(const char *str)
{
if (!str)
return NULL;
else
return strdup(str);
}
/* currently only for "TZ" */
static char *cmdline_subst(const char *cmdline)
{
DynBuf dbuf;
const char *p;
char var_name[32], *q, buf[32];
dbuf_init(&dbuf);
p = cmdline;
while (*p != '\0') {
if (p[0] == '$' && p[1] == '{') {
p += 2;
q = var_name;
while (*p != '\0' && *p != '}') {
if ((q - var_name) < sizeof(var_name) - 1)
*q++ = *p;
p++;
}
*q = '\0';
if (*p == '}')
p++;
if (!strcmp(var_name, "TZ")) {
time_t ti;
struct tm tm;
int n, sg;
/* get the offset to UTC */
time(&ti);
localtime_r(&ti, &tm);
n = tm.tm_gmtoff / 60;
sg = '-';
if (n < 0) {
sg = '+';
n = -n;
}
snprintf(buf, sizeof(buf), "UTC%c%02d:%02d",
sg, n / 60, n % 60);
dbuf_putstr(&dbuf, buf);
}
} else {
dbuf_putc(&dbuf, *p++);
}
}
dbuf_putc(&dbuf, 0);
return (char *)dbuf.buf;
}
static BOOL find_name(const char *name, const char *name_list)
{
size_t len;
const char *p, *r;
p = name_list;
for(;;) {
r = strchr(p, ',');
if (!r) {
if (!strcmp(name, p))
return TRUE;
break;
} else {
len = r - p;
if (len == strlen(name) && !memcmp(name, p, len))
return TRUE;
p = r + 1;
}
}
return FALSE;
}
static const VirtMachineClass *virt_machine_list[] = {
#if defined(EMSCRIPTEN)
/* only a single machine in the EMSCRIPTEN target */
#ifndef CONFIG_X86EMU
&riscv_machine_class,
#endif
#else
&riscv_machine_class,
#endif /* !EMSCRIPTEN */
#ifdef CONFIG_X86EMU
&pc_machine_class,
#endif
NULL,
};
static const VirtMachineClass *virt_machine_find_class(const char *machine_name)
{
const VirtMachineClass *vmc, **pvmc;
for(pvmc = virt_machine_list; *pvmc != NULL; pvmc++) {
vmc = *pvmc;
if (find_name(machine_name, vmc->machine_names))
return vmc;
}
return NULL;
}
static int virt_machine_parse_config(VirtMachineParams *p,
char *config_file_str, int len)
{
int version, val;
const char *tag_name, *str;
char buf1[256];
JSONValue cfg, obj, el;
cfg = json_parse_value_len(config_file_str, len);
if (json_is_error(cfg)) {
vm_error("error: %s\n", json_get_error(cfg));
json_free(cfg);
return -1;
}
if (vm_get_int(cfg, "version", &version) < 0)
goto tag_fail;
if (version != VM_CONFIG_VERSION) {
if (version > VM_CONFIG_VERSION) {
vm_error("The emulator is too old to run this VM: please upgrade\n");
return -1;
} else {
vm_error("The VM configuration file is too old for this emulator version: please upgrade the VM configuration file\n");
return -1;
}
}
if (vm_get_str(cfg, "machine", &str) < 0)
goto tag_fail;
p->machine_name = strdup(str);
p->vmc = virt_machine_find_class(p->machine_name);
if (!p->vmc) {
vm_error("Unknown machine name: %s\n", p->machine_name);
goto tag_fail;
}
p->vmc->virt_machine_set_defaults(p);
tag_name = "memory_size";
if (vm_get_int(cfg, tag_name, &val) < 0)
goto tag_fail;
p->ram_size = (uint64_t)val << 20;
tag_name = "bios";
if (vm_get_str_opt(cfg, tag_name, &str) < 0)
goto tag_fail;
if (str) {
p->files[VM_FILE_BIOS].filename = strdup(str);
}
tag_name = "kernel";
if (vm_get_str_opt(cfg, tag_name, &str) < 0)
goto tag_fail;
if (str) {
p->files[VM_FILE_KERNEL].filename = strdup(str);
}
tag_name = "initrd";
if (vm_get_str_opt(cfg, tag_name, &str) < 0)
goto tag_fail;
if (str) {
p->files[VM_FILE_INITRD].filename = strdup(str);
}
if (vm_get_str_opt(cfg, "cmdline", &str) < 0)
goto tag_fail;
if (str) {
p->cmdline = cmdline_subst(str);
}
for(;;) {
snprintf(buf1, sizeof(buf1), "drive%d", p->drive_count);
obj = json_object_get(cfg, buf1);
if (json_is_undefined(obj))
break;
if (p->drive_count >= MAX_DRIVE_DEVICE) {
vm_error("Too many drives\n");
return -1;
}
if (vm_get_str(obj, "file", &str) < 0)
goto tag_fail;
p->tab_drive[p->drive_count].filename = strdup(str);
if (vm_get_str_opt(obj, "device", &str) < 0)
goto tag_fail;
p->tab_drive[p->drive_count].device = strdup_null(str);
p->drive_count++;
}
for(;;) {
snprintf(buf1, sizeof(buf1), "fs%d", p->fs_count);
obj = json_object_get(cfg, buf1);
if (json_is_undefined(obj))
break;
if (p->fs_count >= MAX_DRIVE_DEVICE) {
vm_error("Too many filesystems\n");
return -1;
}
if (vm_get_str(obj, "file", &str) < 0)
goto tag_fail;
p->tab_fs[p->fs_count].filename = strdup(str);
if (vm_get_str_opt(obj, "tag", &str) < 0)
goto tag_fail;
if (!str) {
if (p->fs_count == 0)
strcpy(buf1, "/dev/root");
else
snprintf(buf1, sizeof(buf1), "/dev/root%d", p->fs_count);
str = buf1;
}
p->tab_fs[p->fs_count].tag = strdup(str);
p->fs_count++;
}
for(;;) {
snprintf(buf1, sizeof(buf1), "eth%d", p->eth_count);
obj = json_object_get(cfg, buf1);
if (json_is_undefined(obj))
break;
if (p->eth_count >= MAX_ETH_DEVICE) {
vm_error("Too many ethernet interfaces\n");
return -1;
}
if (vm_get_str(obj, "driver", &str) < 0)
goto tag_fail;
p->tab_eth[p->eth_count].driver = strdup(str);
if (!strcmp(str, "tap")) {
if (vm_get_str(obj, "ifname", &str) < 0)
goto tag_fail;
p->tab_eth[p->eth_count].ifname = strdup(str);
}
p->eth_count++;
}
p->display_device = NULL;
obj = json_object_get(cfg, "display0");
if (!json_is_undefined(obj)) {
if (vm_get_str(obj, "device", &str) < 0)
goto tag_fail;
p->display_device = strdup(str);
if (vm_get_int(obj, "width", &p->width) < 0)
goto tag_fail;
if (vm_get_int(obj, "height", &p->height) < 0)
goto tag_fail;
if (vm_get_str_opt(obj, "vga_bios", &str) < 0)
goto tag_fail;
if (str) {
p->files[VM_FILE_VGA_BIOS].filename = strdup(str);
}
}
if (vm_get_str_opt(cfg, "input_device", &str) < 0)
goto tag_fail;
p->input_device = strdup_null(str);
if (vm_get_str_opt(cfg, "accel", &str) < 0)
goto tag_fail;
if (str) {
if (!strcmp(str, "none")) {
p->accel_enable = FALSE;
} else if (!strcmp(str, "auto")) {
p->accel_enable = TRUE;
} else {
vm_error("unsupported 'accel' config: %s\n", str);
return -1;
}
}
tag_name = "rtc_local_time";
el = json_object_get(cfg, tag_name);
if (!json_is_undefined(el)) {
if (el.type != JSON_BOOL) {
vm_error("%s: boolean expected\n", tag_name);
goto tag_fail;
}
p->rtc_local_time = el.u.b;
}
json_free(cfg);
return 0;
tag_fail:
json_free(cfg);
return -1;
}
typedef void FSLoadFileCB(void *opaque, uint8_t *buf, int buf_len);
typedef struct {
VirtMachineParams *vm_params;
void (*start_cb)(void *opaque);
void *opaque;
FSLoadFileCB *file_load_cb;
void *file_load_opaque;
int file_index;
} VMConfigLoadState;
static void config_file_loaded(void *opaque, uint8_t *buf, int buf_len);
static void config_additional_file_load(VMConfigLoadState *s);
static void config_additional_file_load_cb(void *opaque,
uint8_t *buf, int buf_len);
/* XXX: win32, URL */
char *get_file_path(const char *base_filename, const char *filename)
{
int len, len1;
char *fname, *p;
if (!base_filename)
goto done;
if (strchr(filename, ':'))
goto done; /* full URL */
if (filename[0] == '/')
goto done;
p = strrchr(base_filename, '/');
if (!p) {
done:
return strdup(filename);
}
len = p + 1 - base_filename;
len1 = strlen(filename);
fname = malloc(len + len1 + 1);
memcpy(fname, base_filename, len);
memcpy(fname + len, filename, len1 + 1);
return fname;
}
#ifdef EMSCRIPTEN
static int load_file(uint8_t **pbuf, const char *filename)
{
abort();
}
#else
/* return -1 if error. */
static int load_file(uint8_t **pbuf, const char *filename)
{
FILE *f;
int size;
uint8_t *buf;
f = fopen(filename, "rb");
if (!f) {
perror(filename);
exit(1);
}
fseek(f, 0, SEEK_END);
size = ftell(f);
fseek(f, 0, SEEK_SET);
buf = malloc(size);
if (fread(buf, 1, size, f) != size) {
fprintf(stderr, "%s: read error\n", filename);
exit(1);
}
fclose(f);
*pbuf = buf;
return size;
}
#endif
#ifdef CONFIG_FS_NET
static void config_load_file_cb(void *opaque, int err, void *data, size_t size)
{
VMConfigLoadState *s = opaque;
// printf("err=%d data=%p size=%ld\n", err, data, size);
if (err < 0) {
vm_error("Error %d while loading file\n", -err);
exit(1);
}
s->file_load_cb(s->file_load_opaque, data, size);
}
#endif
static void config_load_file(VMConfigLoadState *s, const char *filename,
FSLoadFileCB *cb, void *opaque)
{
// printf("loading %s\n", filename);
#ifdef CONFIG_FS_NET
if (is_url(filename)) {
s->file_load_cb = cb;
s->file_load_opaque = opaque;
fs_wget(filename, NULL, NULL, s, config_load_file_cb, TRUE);
} else
#endif
{
uint8_t *buf;
int size;
size = load_file(&buf, filename);
cb(opaque, buf, size);
free(buf);
}
}
void virt_machine_load_config_file(VirtMachineParams *p,
const char *filename,
void (*start_cb)(void *opaque),
void *opaque)
{
VMConfigLoadState *s;
s = mallocz(sizeof(*s));
s->vm_params = p;
s->start_cb = start_cb;
s->opaque = opaque;
p->cfg_filename = strdup(filename);
config_load_file(s, filename, config_file_loaded, s);
}
static void config_file_loaded(void *opaque, uint8_t *buf, int buf_len)
{
VMConfigLoadState *s = opaque;
VirtMachineParams *p = s->vm_params;
if (virt_machine_parse_config(p, (char *)buf, buf_len) < 0)
exit(1);
/* load the additional files */
s->file_index = 0;
config_additional_file_load(s);
}
static void config_additional_file_load(VMConfigLoadState *s)
{
VirtMachineParams *p = s->vm_params;
while (s->file_index < VM_FILE_COUNT &&
p->files[s->file_index].filename == NULL) {
s->file_index++;
}
if (s->file_index == VM_FILE_COUNT) {
if (s->start_cb)
s->start_cb(s->opaque);
free(s);
} else {
char *fname;
fname = get_file_path(p->cfg_filename,
p->files[s->file_index].filename);
config_load_file(s, fname,
config_additional_file_load_cb, s);
free(fname);
}
}
static void config_additional_file_load_cb(void *opaque,
uint8_t *buf, int buf_len)
{
VMConfigLoadState *s = opaque;
VirtMachineParams *p = s->vm_params;
p->files[s->file_index].buf = malloc(buf_len);
memcpy(p->files[s->file_index].buf, buf, buf_len);
p->files[s->file_index].len = buf_len;
/* load the next files */
s->file_index++;
config_additional_file_load(s);
}
void vm_add_cmdline(VirtMachineParams *p, const char *cmdline)
{
char *new_cmdline, *old_cmdline;
if (cmdline[0] == '!') {
new_cmdline = strdup(cmdline + 1);
} else {
old_cmdline = p->cmdline;
if (!old_cmdline)
old_cmdline = "";
new_cmdline = malloc(strlen(old_cmdline) + 1 + strlen(cmdline) + 1);
strcpy(new_cmdline, old_cmdline);
strcat(new_cmdline, " ");
strcat(new_cmdline, cmdline);
}
free(p->cmdline);
p->cmdline = new_cmdline;
}
void virt_machine_free_config(VirtMachineParams *p)
{
int i;
free(p->machine_name);
free(p->cmdline);
for(i = 0; i < VM_FILE_COUNT; i++) {
free(p->files[i].filename);
free(p->files[i].buf);
}
for(i = 0; i < p->drive_count; i++) {
free(p->tab_drive[i].filename);
free(p->tab_drive[i].device);
}
for(i = 0; i < p->fs_count; i++) {
free(p->tab_fs[i].filename);
free(p->tab_fs[i].tag);
}
for(i = 0; i < p->eth_count; i++) {
free(p->tab_eth[i].driver);
free(p->tab_eth[i].ifname);
}
free(p->input_device);
free(p->display_device);
free(p->cfg_filename);
}
VirtMachine *virt_machine_init(const VirtMachineParams *p)
{
const VirtMachineClass *vmc = p->vmc;
return vmc->virt_machine_init(p);
}
void virt_machine_set_defaults(VirtMachineParams *p)
{
memset(p, 0, sizeof(*p));
}
void virt_machine_end(VirtMachine *s)
{
s->vmc->virt_machine_end(s);
}

196
machine.h Normal file
View file

@ -0,0 +1,196 @@
/*
* VM definitions
*
* Copyright (c) 2016-2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "json.h"
typedef struct FBDevice FBDevice;
typedef void SimpleFBDrawFunc(FBDevice *fb_dev, void *opaque,
int x, int y, int w, int h);
struct FBDevice {
/* the following is set by the device */
int width;
int height;
int stride; /* current stride in bytes */
uint8_t *fb_data; /* current pointer to the pixel data */
int fb_size; /* frame buffer memory size (info only) */
void *device_opaque;
void (*refresh)(struct FBDevice *fb_dev,
SimpleFBDrawFunc *redraw_func, void *opaque);
};
#define MAX_DRIVE_DEVICE 4
#define MAX_FS_DEVICE 4
#define MAX_ETH_DEVICE 1
#define VM_CONFIG_VERSION 1
typedef enum {
VM_FILE_BIOS,
VM_FILE_VGA_BIOS,
VM_FILE_KERNEL,
VM_FILE_INITRD,
VM_FILE_COUNT,
} VMFileTypeEnum;
typedef struct {
char *filename;
uint8_t *buf;
int len;
} VMFileEntry;
typedef struct {
char *device;
char *filename;
BlockDevice *block_dev;
} VMDriveEntry;
typedef struct {
char *device;
char *tag; /* 9p mount tag */
char *filename;
FSDevice *fs_dev;
} VMFSEntry;
typedef struct {
char *driver;
char *ifname;
EthernetDevice *net;
} VMEthEntry;
typedef struct VirtMachineClass VirtMachineClass;
typedef struct {
char *cfg_filename;
const VirtMachineClass *vmc;
char *machine_name;
uint64_t ram_size;
BOOL rtc_real_time;
BOOL rtc_local_time;
char *display_device; /* NULL means no display */
int width, height; /* graphic width & height */
CharacterDevice *console;
VMDriveEntry tab_drive[MAX_DRIVE_DEVICE];
int drive_count;
VMFSEntry tab_fs[MAX_FS_DEVICE];
int fs_count;
VMEthEntry tab_eth[MAX_ETH_DEVICE];
int eth_count;
char *cmdline; /* bios or kernel command line */
BOOL accel_enable; /* enable acceleration (KVM) */
char *input_device; /* NULL means no input */
/* kernel, bios and other auxiliary files */
VMFileEntry files[VM_FILE_COUNT];
} VirtMachineParams;
typedef struct VirtMachine {
const VirtMachineClass *vmc;
/* network */
EthernetDevice *net;
/* console */
VIRTIODevice *console_dev;
CharacterDevice *console;
/* graphics */
FBDevice *fb_dev;
} VirtMachine;
struct VirtMachineClass {
const char *machine_names;
void (*virt_machine_set_defaults)(VirtMachineParams *p);
VirtMachine *(*virt_machine_init)(const VirtMachineParams *p);
void (*virt_machine_end)(VirtMachine *s);
int (*virt_machine_get_sleep_duration)(VirtMachine *s, int delay);
void (*virt_machine_interp)(VirtMachine *s, int max_exec_cycle);
BOOL (*vm_mouse_is_absolute)(VirtMachine *s);
void (*vm_send_mouse_event)(VirtMachine *s1, int dx, int dy, int dz,
unsigned int buttons);
void (*vm_send_key_event)(VirtMachine *s1, BOOL is_down, uint16_t key_code);
};
extern const VirtMachineClass riscv_machine_class;
extern const VirtMachineClass pc_machine_class;
void __attribute__((format(printf, 1, 2))) vm_error(const char *fmt, ...);
int vm_get_int(JSONValue obj, const char *name, int *pval);
int vm_get_int_opt(JSONValue obj, const char *name, int *pval, int def_val);
void virt_machine_set_defaults(VirtMachineParams *p);
void virt_machine_load_config_file(VirtMachineParams *p,
const char *filename,
void (*start_cb)(void *opaque),
void *opaque);
void vm_add_cmdline(VirtMachineParams *p, const char *cmdline);
char *get_file_path(const char *base_filename, const char *filename);
void virt_machine_free_config(VirtMachineParams *p);
VirtMachine *virt_machine_init(const VirtMachineParams *p);
void virt_machine_end(VirtMachine *s);
static inline int virt_machine_get_sleep_duration(VirtMachine *s, int delay)
{
return s->vmc->virt_machine_get_sleep_duration(s, delay);
}
static inline void virt_machine_interp(VirtMachine *s, int max_exec_cycle)
{
s->vmc->virt_machine_interp(s, max_exec_cycle);
}
static inline BOOL vm_mouse_is_absolute(VirtMachine *s)
{
return s->vmc->vm_mouse_is_absolute(s);
}
static inline void vm_send_mouse_event(VirtMachine *s1, int dx, int dy, int dz,
unsigned int buttons)
{
s1->vmc->vm_send_mouse_event(s1, dx, dy, dz, buttons);
}
static inline void vm_send_key_event(VirtMachine *s1, BOOL is_down, uint16_t key_code)
{
s1->vmc->vm_send_key_event(s1, is_down, key_code);
}
/* gui */
void sdl_refresh(VirtMachine *m);
void sdl_init(int width, int height);
/* simplefb.c */
typedef struct SimpleFBState SimpleFBState;
SimpleFBState *simplefb_init(PhysMemoryMap *map, uint64_t phys_addr,
FBDevice *fb_dev, int width, int height);
void simplefb_refresh(FBDevice *fb_dev,
SimpleFBDrawFunc *redraw_func, void *opaque,
PhysMemoryRange *mem_range,
int fb_page_count);
/* vga.c */
typedef struct VGAState VGAState;
VGAState *pci_vga_init(PCIBus *bus, FBDevice *fb_dev,
int width, int height,
const uint8_t *vga_rom_buf, int vga_rom_size);
/* block_net.c */
BlockDevice *block_device_init_http(const char *url,
int max_cache_size_kb,
void (*start_cb)(void *opaque),
void *start_opaque);

43
netinit.sh Executable file
View file

@ -0,0 +1,43 @@
#!/bin/sh
#
# RISCVEMU Ethernet bridge and NAT configuration (run with sudo)
#
# Copyright (c) 2017 Fabrice Bellard
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# host network interface connected to Internet (change it)
internet_ifname="enp0s20f0u1"
# setup bridge interface
ip link add br0 type bridge
# create and add tap0 interface to bridge
ip tuntap add dev tap0 mode tap user $USER
ip link set tap0 master br0
ip link set dev br0 up
ip link set dev tap0 up
ifconfig br0 192.168.3.1
# setup NAT to access to Internet
echo 1 > /proc/sys/net/ipv4/ip_forward
# delete forwarding reject rule if present
#iptables -D FORWARD 1
iptables -t nat -A POSTROUTING -o $internet_ifname -j MASQUERADE

588
pci.c Normal file
View file

@ -0,0 +1,588 @@
/*
* Simple PCI bus driver
*
* Copyright (c) 2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include <stdarg.h>
#include "cutils.h"
#include "pci.h"
//#define DEBUG_CONFIG
typedef struct {
uint32_t size; /* 0 means no mapping defined */
uint8_t type;
uint8_t enabled; /* true if mapping is enabled */
void *opaque;
PCIBarSetFunc *bar_set;
} PCIIORegion;
struct PCIDevice {
PCIBus *bus;
uint8_t devfn;
IRQSignal irq[4];
uint8_t config[256];
uint8_t next_cap_offset; /* offset of the next capability */
char *name; /* for debug only */
PCIIORegion io_regions[PCI_NUM_REGIONS];
};
struct PCIBus {
int bus_num;
PCIDevice *device[256];
PhysMemoryMap *mem_map;
PhysMemoryMap *port_map;
uint32_t irq_state[4][8]; /* one bit per device */
IRQSignal irq[4];
};
static int bus_map_irq(PCIDevice *d, int irq_num)
{
int slot_addend;
slot_addend = (d->devfn >> 3) - 1;
return (irq_num + slot_addend) & 3;
}
static void pci_device_set_irq(void *opaque, int irq_num, int level)
{
PCIDevice *d = opaque;
PCIBus *b = d->bus;
uint32_t mask;
int i, irq_level;
// printf("%s: pci_device_seq_irq: %d %d\n", d->name, irq_num, level);
irq_num = bus_map_irq(d, irq_num);
mask = 1 << (d->devfn & 0x1f);
if (level)
b->irq_state[irq_num][d->devfn >> 5] |= mask;
else
b->irq_state[irq_num][d->devfn >> 5] &= ~mask;
/* compute the IRQ state */
mask = 0;
for(i = 0; i < 8; i++)
mask |= b->irq_state[irq_num][i];
irq_level = (mask != 0);
set_irq(&b->irq[irq_num], irq_level);
}
static int devfn_alloc(PCIBus *b)
{
int devfn;
for(devfn = 0; devfn < 256; devfn += 8) {
if (!b->device[devfn])
return devfn;
}
return -1;
}
/* devfn < 0 means to allocate it */
PCIDevice *pci_register_device(PCIBus *b, const char *name, int devfn,
uint16_t vendor_id, uint16_t device_id,
uint8_t revision, uint16_t class_id)
{
PCIDevice *d;
int i;
if (devfn < 0) {
devfn = devfn_alloc(b);
if (devfn < 0)
return NULL;
}
if (b->device[devfn])
return NULL;
d = mallocz(sizeof(PCIDevice));
d->bus = b;
d->name = strdup(name);
d->devfn = devfn;
put_le16(d->config + 0x00, vendor_id);
put_le16(d->config + 0x02, device_id);
d->config[0x08] = revision;
put_le16(d->config + 0x0a, class_id);
d->config[0x0e] = 0x00; /* header type */
d->next_cap_offset = 0x40;
for(i = 0; i < 4; i++)
irq_init(&d->irq[i], pci_device_set_irq, d, i);
b->device[devfn] = d;
return d;
}
IRQSignal *pci_device_get_irq(PCIDevice *d, unsigned int irq_num)
{
assert(irq_num < 4);
return &d->irq[irq_num];
}
static uint32_t pci_device_config_read(PCIDevice *d, uint32_t addr,
int size_log2)
{
uint32_t val;
switch(size_log2) {
case 0:
val = *(uint8_t *)(d->config + addr);
break;
case 1:
/* Note: may be unaligned */
if (addr <= 0xfe)
val = get_le16(d->config + addr);
else
val = *(uint8_t *)(d->config + addr);
break;
case 2:
/* always aligned */
val = get_le32(d->config + addr);
break;
default:
abort();
}
#ifdef DEBUG_CONFIG
printf("pci_config_read: dev=%s addr=0x%02x val=0x%x s=%d\n",
d->name, addr, val, 1 << size_log2);
#endif
return val;
}
PhysMemoryMap *pci_device_get_mem_map(PCIDevice *d)
{
return d->bus->mem_map;
}
PhysMemoryMap *pci_device_get_port_map(PCIDevice *d)
{
return d->bus->port_map;
}
void pci_register_bar(PCIDevice *d, unsigned int bar_num,
uint32_t size, int type,
void *opaque, PCIBarSetFunc *bar_set)
{
PCIIORegion *r;
uint32_t val, config_addr;
assert(bar_num < PCI_NUM_REGIONS);
assert((size & (size - 1)) == 0); /* power of two */
assert(size >= 4);
r = &d->io_regions[bar_num];
assert(r->size == 0);
r->size = size;
r->type = type;
r->enabled = FALSE;
r->opaque = opaque;
r->bar_set = bar_set;
/* set the config value */
val = 0;
if (bar_num == PCI_ROM_SLOT) {
config_addr = 0x30;
} else {
val |= r->type;
config_addr = 0x10 + 4 * bar_num;
}
put_le32(&d->config[config_addr], val);
}
static void pci_update_mappings(PCIDevice *d)
{
int cmd, i, offset;
uint32_t new_addr;
BOOL new_enabled;
PCIIORegion *r;
cmd = get_le16(&d->config[PCI_COMMAND]);
for(i = 0; i < PCI_NUM_REGIONS; i++) {
r = &d->io_regions[i];
if (i == PCI_ROM_SLOT) {
offset = 0x30;
} else {
offset = 0x10 + i * 4;
}
new_addr = get_le32(&d->config[offset]);
new_enabled = FALSE;
if (r->size != 0) {
if ((r->type & PCI_ADDRESS_SPACE_IO) &&
(cmd & PCI_COMMAND_IO)) {
new_enabled = TRUE;
} else {
if (cmd & PCI_COMMAND_MEMORY) {
if (i == PCI_ROM_SLOT) {
new_enabled = (new_addr & 1);
} else {
new_enabled = TRUE;
}
}
}
}
if (new_enabled) {
/* new address */
new_addr = get_le32(&d->config[offset]) & ~(r->size - 1);
r->bar_set(r->opaque, i, new_addr, TRUE);
r->enabled = TRUE;
} else if (r->enabled) {
r->bar_set(r->opaque, i, 0, FALSE);
r->enabled = FALSE;
}
}
}
/* return != 0 if write is not handled */
static int pci_write_bar(PCIDevice *d, uint32_t addr,
uint32_t val)
{
PCIIORegion *r;
int reg;
if (addr == 0x30)
reg = PCI_ROM_SLOT;
else
reg = (addr - 0x10) >> 2;
// printf("%s: write bar addr=%x data=%x\n", d->name, addr, val);
r = &d->io_regions[reg];
if (r->size == 0)
return -1;
if (reg == PCI_ROM_SLOT) {
val = val & ((~(r->size - 1)) | 1);
} else {
val = (val & ~(r->size - 1)) | r->type;
}
put_le32(d->config + addr, val);
pci_update_mappings(d);
return 0;
}
static void pci_device_config_write8(PCIDevice *d, uint32_t addr,
uint32_t data)
{
int can_write;
if (addr == PCI_STATUS || addr == (PCI_STATUS + 1)) {
/* write 1 reset bits */
d->config[addr] &= ~data;
return;
}
switch(d->config[0x0e]) {
case 0x00:
case 0x80:
switch(addr) {
case 0x00:
case 0x01:
case 0x02:
case 0x03:
case 0x08:
case 0x09:
case 0x0a:
case 0x0b:
case 0x0e:
case 0x10 ... 0x27: /* base */
case 0x30 ... 0x33: /* rom */
case 0x3d:
can_write = 0;
break;
default:
can_write = 1;
break;
}
break;
default:
case 0x01:
switch(addr) {
case 0x00:
case 0x01:
case 0x02:
case 0x03:
case 0x08:
case 0x09:
case 0x0a:
case 0x0b:
case 0x0e:
case 0x38 ... 0x3b: /* rom */
case 0x3d:
can_write = 0;
break;
default:
can_write = 1;
break;
}
break;
}
if (can_write)
d->config[addr] = data;
}
static void pci_device_config_write(PCIDevice *d, uint32_t addr,
uint32_t data, int size_log2)
{
int size, i;
uint32_t addr1;
#ifdef DEBUG_CONFIG
printf("pci_config_write: dev=%s addr=0x%02x val=0x%x s=%d\n",
d->name, addr, data, 1 << size_log2);
#endif
if (size_log2 == 2 &&
((addr >= 0x10 && addr < 0x10 + 4 * 6) ||
addr == 0x30)) {
if (pci_write_bar(d, addr, data) == 0)
return;
}
size = 1 << size_log2;
for(i = 0; i < size; i++) {
addr1 = addr + i;
if (addr1 <= 0xff) {
pci_device_config_write8(d, addr1, (data >> (i * 8)) & 0xff);
}
}
if (PCI_COMMAND >= addr && PCI_COMMAND < addr + size) {
pci_update_mappings(d);
}
}
static void pci_data_write(PCIBus *s, uint32_t addr,
uint32_t data, int size_log2)
{
PCIDevice *d;
int bus_num, devfn, config_addr;
bus_num = (addr >> 16) & 0xff;
if (bus_num != s->bus_num)
return;
devfn = (addr >> 8) & 0xff;
d = s->device[devfn];
if (!d)
return;
config_addr = addr & 0xff;
pci_device_config_write(d, config_addr, data, size_log2);
}
static const uint32_t val_ones[3] = { 0xff, 0xffff, 0xffffffff };
static uint32_t pci_data_read(PCIBus *s, uint32_t addr, int size_log2)
{
PCIDevice *d;
int bus_num, devfn, config_addr;
bus_num = (addr >> 16) & 0xff;
if (bus_num != s->bus_num)
return val_ones[size_log2];
devfn = (addr >> 8) & 0xff;
d = s->device[devfn];
if (!d)
return val_ones[size_log2];
config_addr = addr & 0xff;
return pci_device_config_read(d, config_addr, size_log2);
}
/* warning: only valid for one DEVIO page. Return NULL if no memory at
the given address */
uint8_t *pci_device_get_dma_ptr(PCIDevice *d, uint64_t addr, BOOL is_rw)
{
return phys_mem_get_ram_ptr(d->bus->mem_map, addr, is_rw);
}
void pci_device_set_config8(PCIDevice *d, uint8_t addr, uint8_t val)
{
d->config[addr] = val;
}
void pci_device_set_config16(PCIDevice *d, uint8_t addr, uint16_t val)
{
put_le16(&d->config[addr], val);
}
int pci_device_get_devfn(PCIDevice *d)
{
return d->devfn;
}
/* return the offset of the capability or < 0 if error. */
int pci_add_capability(PCIDevice *d, const uint8_t *buf, int size)
{
int offset;
offset = d->next_cap_offset;
if ((offset + size) > 256)
return -1;
d->next_cap_offset += size;
d->config[PCI_STATUS] |= PCI_STATUS_CAP_LIST;
memcpy(d->config + offset, buf, size);
d->config[offset + 1] = d->config[PCI_CAPABILITY_LIST];
d->config[PCI_CAPABILITY_LIST] = offset;
return offset;
}
/* i440FX host bridge */
struct I440FXState {
PCIBus *pci_bus;
PCIDevice *pci_dev;
PCIDevice *piix3_dev;
uint32_t config_reg;
uint8_t pic_irq_state[16];
IRQSignal *pic_irqs; /* 16 irqs */
};
static void i440fx_write_addr(void *opaque, uint32_t offset,
uint32_t data, int size_log2)
{
I440FXState *s = opaque;
s->config_reg = data;
}
static uint32_t i440fx_read_addr(void *opaque, uint32_t offset, int size_log2)
{
I440FXState *s = opaque;
return s->config_reg;
}
static void i440fx_write_data(void *opaque, uint32_t offset,
uint32_t data, int size_log2)
{
I440FXState *s = opaque;
if (s->config_reg & 0x80000000) {
if (size_log2 == 2) {
/* it is simpler to assume 32 bit config accesses are
always aligned */
pci_data_write(s->pci_bus, s->config_reg & ~3, data, size_log2);
} else {
pci_data_write(s->pci_bus, s->config_reg | offset, data, size_log2);
}
}
}
static uint32_t i440fx_read_data(void *opaque, uint32_t offset, int size_log2)
{
I440FXState *s = opaque;
if (!(s->config_reg & 0x80000000))
return val_ones[size_log2];
if (size_log2 == 2) {
/* it is simpler to assume 32 bit config accesses are
always aligned */
return pci_data_read(s->pci_bus, s->config_reg & ~3, size_log2);
} else {
return pci_data_read(s->pci_bus, s->config_reg | offset, size_log2);
}
}
static void i440fx_set_irq(void *opaque, int irq_num, int irq_level)
{
I440FXState *s = opaque;
PCIDevice *hd = s->piix3_dev;
int pic_irq;
/* map to the PIC irq (different IRQs can be mapped to the same
PIC irq) */
hd->config[0x60 + irq_num] &= ~0x80;
pic_irq = hd->config[0x60 + irq_num];
if (pic_irq < 16) {
if (irq_level)
s->pic_irq_state[pic_irq] |= 1 << irq_num;
else
s->pic_irq_state[pic_irq] &= ~(1 << irq_num);
set_irq(&s->pic_irqs[pic_irq], (s->pic_irq_state[pic_irq] != 0));
}
}
I440FXState *i440fx_init(PCIBus **pbus, int *ppiix3_devfn,
PhysMemoryMap *mem_map, PhysMemoryMap *port_map,
IRQSignal *pic_irqs)
{
I440FXState *s;
PCIBus *b;
PCIDevice *d;
int i;
s = mallocz(sizeof(*s));
b = mallocz(sizeof(PCIBus));
b->bus_num = 0;
b->mem_map = mem_map;
b->port_map = port_map;
s->pic_irqs = pic_irqs;
for(i = 0; i < 4; i++) {
irq_init(&b->irq[i], i440fx_set_irq, s, i);
}
cpu_register_device(port_map, 0xcf8, 1, s, i440fx_read_addr, i440fx_write_addr,
DEVIO_SIZE32);
cpu_register_device(port_map, 0xcfc, 4, s, i440fx_read_data, i440fx_write_data,
DEVIO_SIZE8 | DEVIO_SIZE16 | DEVIO_SIZE32);
d = pci_register_device(b, "i440FX", 0, 0x8086, 0x1237, 0x02, 0x0600);
put_le16(&d->config[PCI_SUBSYSTEM_VENDOR_ID], 0x1af4); /* Red Hat, Inc. */
put_le16(&d->config[PCI_SUBSYSTEM_ID], 0x1100); /* QEMU virtual machine */
s->pci_dev = d;
s->pci_bus = b;
s->piix3_dev = pci_register_device(b, "PIIX3", 8, 0x8086, 0x7000,
0x00, 0x0601);
pci_device_set_config8(s->piix3_dev, 0x0e, 0x80); /* header type */
*pbus = b;
*ppiix3_devfn = s->piix3_dev->devfn;
return s;
}
/* in case no BIOS is used, map the interrupts. */
void i440fx_map_interrupts(I440FXState *s, uint8_t *elcr,
const uint8_t *pci_irqs)
{
PCIBus *b = s->pci_bus;
PCIDevice *d, *hd;
int irq_num, pic_irq, devfn, i;
/* set a default PCI IRQ mapping to PIC IRQs */
hd = s->piix3_dev;
elcr[0] = 0;
elcr[1] = 0;
for(i = 0; i < 4; i++) {
irq_num = pci_irqs[i];
hd->config[0x60 + i] = irq_num;
elcr[irq_num >> 3] |= (1 << (irq_num & 7));
}
for(devfn = 0; devfn < 256; devfn++) {
d = b->device[devfn];
if (!d)
continue;
if (d->config[PCI_INTERRUPT_PIN]) {
irq_num = 0;
irq_num = bus_map_irq(d, irq_num);
pic_irq = hd->config[0x60 + irq_num];
if (pic_irq < 16) {
d->config[PCI_INTERRUPT_LINE] = pic_irq;
}
}
}
}

81
pci.h Normal file
View file

@ -0,0 +1,81 @@
/*
* Simple PCI bus driver
*
* Copyright (c) 2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef PCI_H
#define PCI_H
#include "iomem.h"
typedef struct PCIBus PCIBus;
typedef struct PCIDevice PCIDevice;
/* bar type */
#define PCI_ADDRESS_SPACE_MEM 0x00
#define PCI_ADDRESS_SPACE_IO 0x01
#define PCI_ADDRESS_SPACE_MEM_PREFETCH 0x08
#define PCI_ROM_SLOT 6
#define PCI_NUM_REGIONS 7
/* PCI config addresses */
#define PCI_VENDOR_ID 0x00 /* 16 bits */
#define PCI_DEVICE_ID 0x02 /* 16 bits */
#define PCI_COMMAND 0x04 /* 16 bits */
#define PCI_COMMAND_IO (1 << 0)
#define PCI_COMMAND_MEMORY (1 << 1)
#define PCI_STATUS 0x06 /* 16 bits */
#define PCI_STATUS_CAP_LIST (1 << 4)
#define PCI_CLASS_PROG 0x09
#define PCI_SUBSYSTEM_VENDOR_ID 0x2c /* 16 bits */
#define PCI_SUBSYSTEM_ID 0x2e /* 16 bits */
#define PCI_CAPABILITY_LIST 0x34 /* 8 bits */
#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */
#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */
typedef void PCIBarSetFunc(void *opaque, int bar_num, uint32_t addr,
BOOL enabled);
PCIDevice *pci_register_device(PCIBus *b, const char *name, int devfn,
uint16_t vendor_id, uint16_t device_id,
uint8_t revision, uint16_t class_id);
PhysMemoryMap *pci_device_get_mem_map(PCIDevice *d);
PhysMemoryMap *pci_device_get_port_map(PCIDevice *d);
void pci_register_bar(PCIDevice *d, unsigned int bar_num,
uint32_t size, int type,
void *opaque, PCIBarSetFunc *bar_set);
IRQSignal *pci_device_get_irq(PCIDevice *d, unsigned int irq_num);
uint8_t *pci_device_get_dma_ptr(PCIDevice *d, uint64_t addr, BOOL is_rw);
void pci_device_set_config8(PCIDevice *d, uint8_t addr, uint8_t val);
void pci_device_set_config16(PCIDevice *d, uint8_t addr, uint16_t val);
int pci_device_get_devfn(PCIDevice *d);
int pci_add_capability(PCIDevice *d, const uint8_t *buf, int size);
typedef struct I440FXState I440FXState;
I440FXState *i440fx_init(PCIBus **pbus, int *ppiix3_devfn,
PhysMemoryMap *mem_map, PhysMemoryMap *port_map,
IRQSignal *pic_irqs);
void i440fx_map_interrupts(I440FXState *s, uint8_t *elcr,
const uint8_t *pci_irqs);
#endif /* PCI_H */

342
pckbd.c Normal file
View file

@ -0,0 +1,342 @@
/*
* QEMU PC keyboard emulation
*
* Copyright (c) 2003 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include "cutils.h"
#include "iomem.h"
#include "ps2.h"
#include "virtio.h"
#include "machine.h"
/* debug PC keyboard */
//#define DEBUG_KBD
/* debug PC keyboard : only mouse */
//#define DEBUG_MOUSE
/* Keyboard Controller Commands */
#define KBD_CCMD_READ_MODE 0x20 /* Read mode bits */
#define KBD_CCMD_WRITE_MODE 0x60 /* Write mode bits */
#define KBD_CCMD_GET_VERSION 0xA1 /* Get controller version */
#define KBD_CCMD_MOUSE_DISABLE 0xA7 /* Disable mouse interface */
#define KBD_CCMD_MOUSE_ENABLE 0xA8 /* Enable mouse interface */
#define KBD_CCMD_TEST_MOUSE 0xA9 /* Mouse interface test */
#define KBD_CCMD_SELF_TEST 0xAA /* Controller self test */
#define KBD_CCMD_KBD_TEST 0xAB /* Keyboard interface test */
#define KBD_CCMD_KBD_DISABLE 0xAD /* Keyboard interface disable */
#define KBD_CCMD_KBD_ENABLE 0xAE /* Keyboard interface enable */
#define KBD_CCMD_READ_INPORT 0xC0 /* read input port */
#define KBD_CCMD_READ_OUTPORT 0xD0 /* read output port */
#define KBD_CCMD_WRITE_OUTPORT 0xD1 /* write output port */
#define KBD_CCMD_WRITE_OBUF 0xD2
#define KBD_CCMD_WRITE_AUX_OBUF 0xD3 /* Write to output buffer as if
initiated by the auxiliary device */
#define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */
#define KBD_CCMD_DISABLE_A20 0xDD /* HP vectra only ? */
#define KBD_CCMD_ENABLE_A20 0xDF /* HP vectra only ? */
#define KBD_CCMD_RESET 0xFE
/* Status Register Bits */
#define KBD_STAT_OBF 0x01 /* Keyboard output buffer full */
#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */
#define KBD_STAT_SELFTEST 0x04 /* Self test successful */
#define KBD_STAT_CMD 0x08 /* Last write was a command write (0=data) */
#define KBD_STAT_UNLOCKED 0x10 /* Zero if keyboard locked */
#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */
#define KBD_STAT_GTO 0x40 /* General receive/xmit timeout */
#define KBD_STAT_PERR 0x80 /* Parity error */
/* Controller Mode Register Bits */
#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generate IRQ1 */
#define KBD_MODE_MOUSE_INT 0x02 /* Mouse data generate IRQ12 */
#define KBD_MODE_SYS 0x04 /* The system flag (?) */
#define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */
#define KBD_MODE_DISABLE_KBD 0x10 /* Disable keyboard interface */
#define KBD_MODE_DISABLE_MOUSE 0x20 /* Disable mouse interface */
#define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */
#define KBD_MODE_RFU 0x80
#define KBD_PENDING_KBD 1
#define KBD_PENDING_AUX 2
struct KBDState {
uint8_t write_cmd; /* if non zero, write data to port 60 is expected */
uint8_t status;
uint8_t mode;
/* Bitmask of devices with data available. */
uint8_t pending;
PS2KbdState *kbd;
PS2MouseState *mouse;
IRQSignal *irq_kbd;
IRQSignal *irq_mouse;
};
static void qemu_system_reset_request(void)
{
printf("system_reset_request\n");
exit(1);
/* XXX */
}
static void ioport_set_a20(int val)
{
}
static int ioport_get_a20(void)
{
return 1;
}
/* update irq and KBD_STAT_[MOUSE_]OBF */
/* XXX: not generating the irqs if KBD_MODE_DISABLE_KBD is set may be
incorrect, but it avoids having to simulate exact delays */
static void kbd_update_irq(KBDState *s)
{
int irq_kbd_level, irq_mouse_level;
irq_kbd_level = 0;
irq_mouse_level = 0;
s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF);
if (s->pending) {
s->status |= KBD_STAT_OBF;
/* kbd data takes priority over aux data. */
if (s->pending == KBD_PENDING_AUX) {
s->status |= KBD_STAT_MOUSE_OBF;
if (s->mode & KBD_MODE_MOUSE_INT)
irq_mouse_level = 1;
} else {
if ((s->mode & KBD_MODE_KBD_INT) &&
!(s->mode & KBD_MODE_DISABLE_KBD))
irq_kbd_level = 1;
}
}
set_irq(s->irq_kbd, irq_kbd_level);
set_irq(s->irq_mouse, irq_mouse_level);
}
static void kbd_update_kbd_irq(void *opaque, int level)
{
KBDState *s = (KBDState *)opaque;
if (level)
s->pending |= KBD_PENDING_KBD;
else
s->pending &= ~KBD_PENDING_KBD;
kbd_update_irq(s);
}
static void kbd_update_aux_irq(void *opaque, int level)
{
KBDState *s = (KBDState *)opaque;
if (level)
s->pending |= KBD_PENDING_AUX;
else
s->pending &= ~KBD_PENDING_AUX;
kbd_update_irq(s);
}
static uint32_t kbd_read_status(void *opaque, uint32_t addr, int size_log2)
{
KBDState *s = opaque;
int val;
val = s->status;
#if defined(DEBUG_KBD)
printf("kbd: read status=0x%02x\n", val);
#endif
return val;
}
static void kbd_queue(KBDState *s, int b, int aux)
{
if (aux)
ps2_queue(s->mouse, b);
else
ps2_queue(s->kbd, b);
}
static void kbd_write_command(void *opaque, uint32_t addr, uint32_t val,
int size_log2)
{
KBDState *s = opaque;
#if defined(DEBUG_KBD)
printf("kbd: write cmd=0x%02x\n", val);
#endif
switch(val) {
case KBD_CCMD_READ_MODE:
kbd_queue(s, s->mode, 1);
break;
case KBD_CCMD_WRITE_MODE:
case KBD_CCMD_WRITE_OBUF:
case KBD_CCMD_WRITE_AUX_OBUF:
case KBD_CCMD_WRITE_MOUSE:
case KBD_CCMD_WRITE_OUTPORT:
s->write_cmd = val;
break;
case KBD_CCMD_MOUSE_DISABLE:
s->mode |= KBD_MODE_DISABLE_MOUSE;
break;
case KBD_CCMD_MOUSE_ENABLE:
s->mode &= ~KBD_MODE_DISABLE_MOUSE;
break;
case KBD_CCMD_TEST_MOUSE:
kbd_queue(s, 0x00, 0);
break;
case KBD_CCMD_SELF_TEST:
s->status |= KBD_STAT_SELFTEST;
kbd_queue(s, 0x55, 0);
break;
case KBD_CCMD_KBD_TEST:
kbd_queue(s, 0x00, 0);
break;
case KBD_CCMD_KBD_DISABLE:
s->mode |= KBD_MODE_DISABLE_KBD;
kbd_update_irq(s);
break;
case KBD_CCMD_KBD_ENABLE:
s->mode &= ~KBD_MODE_DISABLE_KBD;
kbd_update_irq(s);
break;
case KBD_CCMD_READ_INPORT:
kbd_queue(s, 0x00, 0);
break;
case KBD_CCMD_READ_OUTPORT:
/* XXX: check that */
val = 0x01 | (ioport_get_a20() << 1);
if (s->status & KBD_STAT_OBF)
val |= 0x10;
if (s->status & KBD_STAT_MOUSE_OBF)
val |= 0x20;
kbd_queue(s, val, 0);
break;
case KBD_CCMD_ENABLE_A20:
ioport_set_a20(1);
break;
case KBD_CCMD_DISABLE_A20:
ioport_set_a20(0);
break;
case KBD_CCMD_RESET:
qemu_system_reset_request();
break;
case 0xff:
/* ignore that - I don't know what is its use */
break;
default:
fprintf(stderr, "qemu: unsupported keyboard cmd=0x%02x\n", val);
break;
}
}
static uint32_t kbd_read_data(void *opaque, uint32_t addr, int size_log2)
{
KBDState *s = opaque;
uint32_t val;
if (s->pending == KBD_PENDING_AUX)
val = ps2_read_data(s->mouse);
else
val = ps2_read_data(s->kbd);
#ifdef DEBUG_KBD
printf("kbd: read data=0x%02x\n", val);
#endif
return val;
}
static void kbd_write_data(void *opaque, uint32_t addr, uint32_t val, int size_log2)
{
KBDState *s = opaque;
#ifdef DEBUG_KBD
printf("kbd: write data=0x%02x\n", val);
#endif
switch(s->write_cmd) {
case 0:
ps2_write_keyboard(s->kbd, val);
break;
case KBD_CCMD_WRITE_MODE:
s->mode = val;
ps2_keyboard_set_translation(s->kbd, (s->mode & KBD_MODE_KCC) != 0);
/* ??? */
kbd_update_irq(s);
break;
case KBD_CCMD_WRITE_OBUF:
kbd_queue(s, val, 0);
break;
case KBD_CCMD_WRITE_AUX_OBUF:
kbd_queue(s, val, 1);
break;
case KBD_CCMD_WRITE_OUTPORT:
ioport_set_a20((val >> 1) & 1);
if (!(val & 1)) {
qemu_system_reset_request();
}
break;
case KBD_CCMD_WRITE_MOUSE:
ps2_write_mouse(s->mouse, val);
break;
default:
break;
}
s->write_cmd = 0;
}
static void kbd_reset(void *opaque)
{
KBDState *s = opaque;
s->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT;
s->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED;
}
KBDState *i8042_init(PS2KbdState **pkbd,
PS2MouseState **pmouse,
PhysMemoryMap *port_map,
IRQSignal *kbd_irq, IRQSignal *mouse_irq, uint32_t io_base)
{
KBDState *s;
s = mallocz(sizeof(*s));
s->irq_kbd = kbd_irq;
s->irq_mouse = mouse_irq;
kbd_reset(s);
cpu_register_device(port_map, io_base, 1, s, kbd_read_data, kbd_write_data,
DEVIO_SIZE8);
cpu_register_device(port_map, io_base + 4, 1, s, kbd_read_status, kbd_write_command,
DEVIO_SIZE8);
s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s);
s->mouse = ps2_mouse_init(kbd_update_aux_irq, s);
*pkbd = s->kbd;
*pmouse = s->mouse;
return s;
}

489
ps2.c Normal file
View file

@ -0,0 +1,489 @@
/*
* QEMU PS/2 keyboard/mouse emulation
*
* Copyright (c) 2003 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include "cutils.h"
#include "iomem.h"
#include "ps2.h"
/* debug PC keyboard */
//#define DEBUG_KBD
/* debug PC keyboard : only mouse */
//#define DEBUG_MOUSE
/* Keyboard Commands */
#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */
#define KBD_CMD_ECHO 0xEE
#define KBD_CMD_GET_ID 0xF2 /* get keyboard ID */
#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */
#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */
#define KBD_CMD_RESET_DISABLE 0xF5 /* reset and disable scanning */
#define KBD_CMD_RESET_ENABLE 0xF6 /* reset and enable scanning */
#define KBD_CMD_RESET 0xFF /* Reset */
/* Keyboard Replies */
#define KBD_REPLY_POR 0xAA /* Power on reset */
#define KBD_REPLY_ACK 0xFA /* Command ACK */
#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */
/* Mouse Commands */
#define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */
#define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */
#define AUX_SET_RES 0xE8 /* Set resolution */
#define AUX_GET_SCALE 0xE9 /* Get scaling factor */
#define AUX_SET_STREAM 0xEA /* Set stream mode */
#define AUX_POLL 0xEB /* Poll */
#define AUX_RESET_WRAP 0xEC /* Reset wrap mode */
#define AUX_SET_WRAP 0xEE /* Set wrap mode */
#define AUX_SET_REMOTE 0xF0 /* Set remote mode */
#define AUX_GET_TYPE 0xF2 /* Get type */
#define AUX_SET_SAMPLE 0xF3 /* Set sample rate */
#define AUX_ENABLE_DEV 0xF4 /* Enable aux device */
#define AUX_DISABLE_DEV 0xF5 /* Disable aux device */
#define AUX_SET_DEFAULT 0xF6
#define AUX_RESET 0xFF /* Reset aux device */
#define AUX_ACK 0xFA /* Command byte ACK. */
#define MOUSE_STATUS_REMOTE 0x40
#define MOUSE_STATUS_ENABLED 0x20
#define MOUSE_STATUS_SCALE21 0x10
#define PS2_QUEUE_SIZE 256
typedef struct {
uint8_t data[PS2_QUEUE_SIZE];
int rptr, wptr, count;
} PS2Queue;
typedef struct {
PS2Queue queue;
int32_t write_cmd;
void (*update_irq)(void *, int);
void *update_arg;
} PS2State;
struct PS2KbdState {
PS2State common;
int scan_enabled;
/* Qemu uses translated PC scancodes internally. To avoid multiple
conversions we do the translation (if any) in the PS/2 emulation
not the keyboard controller. */
int translate;
};
struct PS2MouseState {
PS2State common;
uint8_t mouse_status;
uint8_t mouse_resolution;
uint8_t mouse_sample_rate;
uint8_t mouse_wrap;
uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */
uint8_t mouse_detect_state;
int mouse_dx; /* current values, needed for 'poll' mode */
int mouse_dy;
int mouse_dz;
uint8_t mouse_buttons;
};
void ps2_queue(void *opaque, int b)
{
PS2State *s = (PS2State *)opaque;
PS2Queue *q = &s->queue;
if (q->count >= PS2_QUEUE_SIZE)
return;
q->data[q->wptr] = b;
if (++q->wptr == PS2_QUEUE_SIZE)
q->wptr = 0;
q->count++;
s->update_irq(s->update_arg, 1);
}
#define INPUT_MAKE_KEY_MIN 96
#define INPUT_MAKE_KEY_MAX 127
static const uint8_t linux_input_to_keycode_set1[INPUT_MAKE_KEY_MAX - INPUT_MAKE_KEY_MIN + 1] = {
0x1c, 0x1d, 0x35, 0x00, 0x38, 0x00, 0x47, 0x48,
0x49, 0x4b, 0x4d, 0x4f, 0x50, 0x51, 0x52, 0x53,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, 0x5c, 0x5d,
};
/* keycode is a Linux input layer keycode. We only support the PS/2
keycode set 1 */
void ps2_put_keycode(PS2KbdState *s, BOOL is_down, int keycode)
{
if (keycode >= INPUT_MAKE_KEY_MIN) {
if (keycode > INPUT_MAKE_KEY_MAX)
return;
keycode = linux_input_to_keycode_set1[keycode - INPUT_MAKE_KEY_MIN];
if (keycode == 0)
return;
ps2_queue(&s->common, 0xe0);
}
ps2_queue(&s->common, keycode | ((!is_down) << 7));
}
uint32_t ps2_read_data(void *opaque)
{
PS2State *s = (PS2State *)opaque;
PS2Queue *q;
int val, index;
q = &s->queue;
if (q->count == 0) {
/* NOTE: if no data left, we return the last keyboard one
(needed for EMM386) */
/* XXX: need a timer to do things correctly */
index = q->rptr - 1;
if (index < 0)
index = PS2_QUEUE_SIZE - 1;
val = q->data[index];
} else {
val = q->data[q->rptr];
if (++q->rptr == PS2_QUEUE_SIZE)
q->rptr = 0;
q->count--;
/* reading deasserts IRQ */
s->update_irq(s->update_arg, 0);
/* reassert IRQs if data left */
s->update_irq(s->update_arg, q->count != 0);
}
return val;
}
static void ps2_reset_keyboard(PS2KbdState *s)
{
s->scan_enabled = 1;
}
void ps2_write_keyboard(void *opaque, int val)
{
PS2KbdState *s = (PS2KbdState *)opaque;
switch(s->common.write_cmd) {
default:
case -1:
switch(val) {
case 0x00:
ps2_queue(&s->common, KBD_REPLY_ACK);
break;
case 0x05:
ps2_queue(&s->common, KBD_REPLY_RESEND);
break;
case KBD_CMD_GET_ID:
ps2_queue(&s->common, KBD_REPLY_ACK);
ps2_queue(&s->common, 0xab);
ps2_queue(&s->common, 0x83);
break;
case KBD_CMD_ECHO:
ps2_queue(&s->common, KBD_CMD_ECHO);
break;
case KBD_CMD_ENABLE:
s->scan_enabled = 1;
ps2_queue(&s->common, KBD_REPLY_ACK);
break;
case KBD_CMD_SET_LEDS:
case KBD_CMD_SET_RATE:
s->common.write_cmd = val;
ps2_queue(&s->common, KBD_REPLY_ACK);
break;
case KBD_CMD_RESET_DISABLE:
ps2_reset_keyboard(s);
s->scan_enabled = 0;
ps2_queue(&s->common, KBD_REPLY_ACK);
break;
case KBD_CMD_RESET_ENABLE:
ps2_reset_keyboard(s);
s->scan_enabled = 1;
ps2_queue(&s->common, KBD_REPLY_ACK);
break;
case KBD_CMD_RESET:
ps2_reset_keyboard(s);
ps2_queue(&s->common, KBD_REPLY_ACK);
ps2_queue(&s->common, KBD_REPLY_POR);
break;
default:
ps2_queue(&s->common, KBD_REPLY_ACK);
break;
}
break;
case KBD_CMD_SET_LEDS:
ps2_queue(&s->common, KBD_REPLY_ACK);
s->common.write_cmd = -1;
break;
case KBD_CMD_SET_RATE:
ps2_queue(&s->common, KBD_REPLY_ACK);
s->common.write_cmd = -1;
break;
}
}
/* Set the scancode translation mode.
0 = raw scancodes.
1 = translated scancodes (used by qemu internally). */
void ps2_keyboard_set_translation(void *opaque, int mode)
{
PS2KbdState *s = (PS2KbdState *)opaque;
s->translate = mode;
}
static void ps2_mouse_send_packet(PS2MouseState *s)
{
unsigned int b;
int dx1, dy1, dz1;
dx1 = s->mouse_dx;
dy1 = s->mouse_dy;
dz1 = s->mouse_dz;
/* XXX: increase range to 8 bits ? */
if (dx1 > 127)
dx1 = 127;
else if (dx1 < -127)
dx1 = -127;
if (dy1 > 127)
dy1 = 127;
else if (dy1 < -127)
dy1 = -127;
b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
ps2_queue(&s->common, b);
ps2_queue(&s->common, dx1 & 0xff);
ps2_queue(&s->common, dy1 & 0xff);
/* extra byte for IMPS/2 or IMEX */
switch(s->mouse_type) {
default:
break;
case 3:
if (dz1 > 127)
dz1 = 127;
else if (dz1 < -127)
dz1 = -127;
ps2_queue(&s->common, dz1 & 0xff);
break;
case 4:
if (dz1 > 7)
dz1 = 7;
else if (dz1 < -7)
dz1 = -7;
b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
ps2_queue(&s->common, b);
break;
}
/* update deltas */
s->mouse_dx -= dx1;
s->mouse_dy -= dy1;
s->mouse_dz -= dz1;
}
void ps2_mouse_event(PS2MouseState *s,
int dx, int dy, int dz, int buttons_state)
{
/* check if deltas are recorded when disabled */
if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
return;
s->mouse_dx += dx;
s->mouse_dy -= dy;
s->mouse_dz += dz;
/* XXX: SDL sometimes generates nul events: we delete them */
if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0 &&
s->mouse_buttons == buttons_state)
return;
s->mouse_buttons = buttons_state;
if (!(s->mouse_status & MOUSE_STATUS_REMOTE) &&
(s->common.queue.count < (PS2_QUEUE_SIZE - 16))) {
for(;;) {
/* if not remote, send event. Multiple events are sent if
too big deltas */
ps2_mouse_send_packet(s);
if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0)
break;
}
}
}
void ps2_write_mouse(void *opaque, int val)
{
PS2MouseState *s = (PS2MouseState *)opaque;
#ifdef DEBUG_MOUSE
printf("kbd: write mouse 0x%02x\n", val);
#endif
switch(s->common.write_cmd) {
default:
case -1:
/* mouse command */
if (s->mouse_wrap) {
if (val == AUX_RESET_WRAP) {
s->mouse_wrap = 0;
ps2_queue(&s->common, AUX_ACK);
return;
} else if (val != AUX_RESET) {
ps2_queue(&s->common, val);
return;
}
}
switch(val) {
case AUX_SET_SCALE11:
s->mouse_status &= ~MOUSE_STATUS_SCALE21;
ps2_queue(&s->common, AUX_ACK);
break;
case AUX_SET_SCALE21:
s->mouse_status |= MOUSE_STATUS_SCALE21;
ps2_queue(&s->common, AUX_ACK);
break;
case AUX_SET_STREAM:
s->mouse_status &= ~MOUSE_STATUS_REMOTE;
ps2_queue(&s->common, AUX_ACK);
break;
case AUX_SET_WRAP:
s->mouse_wrap = 1;
ps2_queue(&s->common, AUX_ACK);
break;
case AUX_SET_REMOTE:
s->mouse_status |= MOUSE_STATUS_REMOTE;
ps2_queue(&s->common, AUX_ACK);
break;
case AUX_GET_TYPE:
ps2_queue(&s->common, AUX_ACK);
ps2_queue(&s->common, s->mouse_type);
break;
case AUX_SET_RES:
case AUX_SET_SAMPLE:
s->common.write_cmd = val;
ps2_queue(&s->common, AUX_ACK);
break;
case AUX_GET_SCALE:
ps2_queue(&s->common, AUX_ACK);
ps2_queue(&s->common, s->mouse_status);
ps2_queue(&s->common, s->mouse_resolution);
ps2_queue(&s->common, s->mouse_sample_rate);
break;
case AUX_POLL:
ps2_queue(&s->common, AUX_ACK);
ps2_mouse_send_packet(s);
break;
case AUX_ENABLE_DEV:
s->mouse_status |= MOUSE_STATUS_ENABLED;
ps2_queue(&s->common, AUX_ACK);
break;
case AUX_DISABLE_DEV:
s->mouse_status &= ~MOUSE_STATUS_ENABLED;
ps2_queue(&s->common, AUX_ACK);
break;
case AUX_SET_DEFAULT:
s->mouse_sample_rate = 100;
s->mouse_resolution = 2;
s->mouse_status = 0;
ps2_queue(&s->common, AUX_ACK);
break;
case AUX_RESET:
s->mouse_sample_rate = 100;
s->mouse_resolution = 2;
s->mouse_status = 0;
s->mouse_type = 0;
ps2_queue(&s->common, AUX_ACK);
ps2_queue(&s->common, 0xaa);
ps2_queue(&s->common, s->mouse_type);
break;
default:
break;
}
break;
case AUX_SET_SAMPLE:
s->mouse_sample_rate = val;
/* detect IMPS/2 or IMEX */
switch(s->mouse_detect_state) {
default:
case 0:
if (val == 200)
s->mouse_detect_state = 1;
break;
case 1:
if (val == 100)
s->mouse_detect_state = 2;
else if (val == 200)
s->mouse_detect_state = 3;
else
s->mouse_detect_state = 0;
break;
case 2:
if (val == 80)
s->mouse_type = 3; /* IMPS/2 */
s->mouse_detect_state = 0;
break;
case 3:
if (val == 80)
s->mouse_type = 4; /* IMEX */
s->mouse_detect_state = 0;
break;
}
ps2_queue(&s->common, AUX_ACK);
s->common.write_cmd = -1;
break;
case AUX_SET_RES:
s->mouse_resolution = val;
ps2_queue(&s->common, AUX_ACK);
s->common.write_cmd = -1;
break;
}
}
static void ps2_reset(void *opaque)
{
PS2State *s = (PS2State *)opaque;
PS2Queue *q;
s->write_cmd = -1;
q = &s->queue;
q->rptr = 0;
q->wptr = 0;
q->count = 0;
}
PS2KbdState *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg)
{
PS2KbdState *s = (PS2KbdState *)mallocz(sizeof(PS2KbdState));
s->common.update_irq = update_irq;
s->common.update_arg = update_arg;
ps2_reset(&s->common);
return s;
}
PS2MouseState *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg)
{
PS2MouseState *s = (PS2MouseState *)mallocz(sizeof(PS2MouseState));
s->common.update_irq = update_irq;
s->common.update_arg = update_arg;
ps2_reset(&s->common);
return s;
}

34
ps2.h Normal file
View file

@ -0,0 +1,34 @@
/* ps2.c */
typedef struct PS2MouseState PS2MouseState;
typedef struct PS2KbdState PS2KbdState;
PS2KbdState *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg);
PS2MouseState *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg);
void ps2_write_mouse(void *, int val);
void ps2_write_keyboard(void *, int val);
uint32_t ps2_read_data(void *);
void ps2_queue(void *, int b);
void ps2_keyboard_set_translation(void *opaque, int mode);
void ps2_put_keycode(PS2KbdState *s, BOOL is_down, int keycode);
void ps2_mouse_event(PS2MouseState *s,
int dx, int dy, int dz, int buttons_state);
/* vmmouse.c */
typedef struct VMMouseState VMMouseState;
VMMouseState *vmmouse_init(PS2MouseState *ps2_mouse);
BOOL vmmouse_is_absolute(VMMouseState *s);
void vmmouse_send_mouse_event(VMMouseState *s, int x, int y, int dz,
int buttons);
void vmmouse_handler(VMMouseState *s, uint32_t *regs);
/* pckbd.c */
typedef struct KBDState KBDState;
KBDState *i8042_init(PS2KbdState **pkbd,
PS2MouseState **pmouse,
PhysMemoryMap *port_map,
IRQSignal *kbd_irq, IRQSignal *mouse_irq,
uint32_t io_base);

204
readme.txt Normal file
View file

@ -0,0 +1,204 @@
TinyEMU System Emulator by Fabrice Bellard
==========================================
1) Features
-----------
- RISC-V system emulator supporting the RV128IMAFDQC base ISA (user
level ISA version 2.2, priviledged architecture version 1.10)
including:
- 32/64/128 bit integer registers
- 32/64/128 bit floating point instructions
- Compressed instructions
- dynamic XLEN change
- x86 system emulator based on KVM
- VirtIO console, network, block device, input and 9P filesystem
- Graphical display with SDL
- JSON configuration file
- Remote HTTP block device and filesystem
- small code, easy to modify, no external dependancies
- Javascript demo version
2) Installation
---------------
- The libraries libcurl, OpenSSL and SDL should be installed. On a Fedora
system you can do it with:
sudo dnf install openssl-devel libcurl-devel SDL-devel
It is possible to compile the programs without these libraries by
commenting CONFIG_FS_NET and/or CONFIG_SDL in the Makefile.
- Edit the Makefile to disable the 128 bit target if you compile on a
32 bit host (for the 128 bit RISCV target the compiler must support
the __int128 C extension).
- Use 'make' to compile the binaries.
- You can optionally install the program to '/usr/local/bin' with:
make install
3) Usage
--------
3.1 Quick examples
------------------
- Use the VM images available from https://bellard.org/jslinux (no
need to download them):
Terminal:
./temu https://bellard.org/jslinux/buildroot-riscv64.cfg
Graphical (with SDL):
./temu https://bellard.org/jslinux/buildroot-x86-xwin.cfg
./temu https://bellard.org/jslinux/win2k.cfg
- Download the example RISC-V Linux image
(diskimage-linux-riscv-yyyy-mm-dd.tar.gz) and use it:
./temu root-riscv64.cfg
./temu rv128test/rv128test.cfg
- Access to your local hard disk (/tmp directory) in the guest:
./temu root_9p-riscv64.cfg
then type:
mount -t 9p /dev/root /mnt
in the guest. The content of the host '/tmp' directory is visible in '/mnt'.
3.2 Invocation
--------------
usage: temu [options] config_file
options are:
-m ram_size set the RAM size in MB
-rw allow write access to the disk image (default=snapshot)
-ctrlc the C-c key stops the emulator instead of being sent to the
emulated software
-append cmdline append cmdline to the kernel command line
-no-accel disable VM acceleration (KVM, x86 machine only)
Console keys:
Press C-a x to exit the emulator, C-a h to get some help.
3.3 Network usage
-----------------
The easiest way is to use the "user" mode network driver. No specific
configuration is necessary.
TinyEMU also supports a "tap" network driver to redirect the network
traffic from a VirtIO network adapter.
You can look at the netinit.sh script to create the tap network
interface and to redirect the virtual traffic to Internet thru a
NAT. The exact configuration may depend on the Linux distribution and
local firewall configuration.
The VM configuration file must include:
eth0: { driver: "tap", ifname: "tap0" }
and configure the network in the guest system with:
ifconfig eth0 192.168.3.2
route add -net 0.0.0.0 gw 192.168.3.1 eth0
3.4 Network filesystem
----------------------
TinyEMU supports the VirtIO 9P filesystem to access local or remote
filesystems. For remote filesystems, it does HTTP requests to download
the files. The protocol is compatible with the vfsync utility. In the
"mount" command, "/dev/rootN" must be used as device name where N is
the index of the filesystem. When N=0 it is omitted.
The build_filelist tool builds the file list from a root directory. A
simple web server is enough to serve the files.
The '.preload' file gives a list of files to preload when opening a
given file.
3.5 Network block device
------------------------
TinyEMU supports an HTTP block device. The disk image is split into
small files. Use the 'splitimg' utility to generate images. The URL of
the JSON blk.txt file must be provided as disk image filename.
4) Technical notes
------------------
4.1) 128 bit support
The RISC-V specification does not define all the instruction encodings
for the 128 bit integer and floating point operations. The missing
ones were interpolated from the 32 and 64 ones.
Unfortunately there is no RISC-V 128 bit toolchain nor OS now
(volunteers for the Linux port ?), so rv128test.bin may be the first
128 bit code for RISC-V !
4.2) Floating point emulation
The floating point emulation is bit exact and supports all the
specified instructions for 32, 64 and 128 bit floating point
numbers. It uses the new SoftFP library.
4.3) HTIF console
The standard HTIF console uses registers at variable addresses which
are deduced by loading specific ELF symbols. TinyEMU does not rely on
an ELF loader, so it is much simpler to use registers at fixed
addresses (0x40008000). A small modification was made in the
"riscv-pk" boot loader to support it. The HTIF console is only used to
display boot messages and to power off the virtual system. The OS
should use the VirtIO console.
4.4) Javascript version
The Javascript version (JSLinux) can be compiled with Makefile.js and
emscripten. A complete precompiled and preconfigured demo is available
in the jslinux-yyyy-mm-dd.tar.gz archive (read the readme.txt file
inside the archive).
4.5) x86 emulator
A small x86 emulator is included. It is not really an emulator because
it uses the Linux KVM API to run the x86 code at near native
performance. The x86 emulator uses the same set of VirtIO devices as
the RISCV emulator and is able to run many operating systems.
The x86 emulator accepts a Linux kernel image (bzImage). No BIOS image
is necessary.
The x86 emulator comes from my JS/Linux project (2011) which was one
of the first emulator running Linux fully implemented in
Javascript. It is provided to allow easy access to the x86 images
hosted at https://bellard.org/jslinux .
5) License / Credits
--------------------
TinyEMU is released under the MIT license. If there is no explicit
license in a file, the license from MIT-LICENSE.txt applies.
The SLIRP library has its own license (two clause BSD license).

1377
riscv_cpu.c Normal file

File diff suppressed because it is too large Load diff

118
riscv_cpu.h Normal file
View file

@ -0,0 +1,118 @@
/*
* RISCV CPU emulator
*
* Copyright (c) 2016-2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef RISCV_CPU_H
#define RISCV_CPU_H
#include <stdlib.h>
#include "cutils.h"
#include "iomem.h"
#define MIP_USIP (1 << 0)
#define MIP_SSIP (1 << 1)
#define MIP_HSIP (1 << 2)
#define MIP_MSIP (1 << 3)
#define MIP_UTIP (1 << 4)
#define MIP_STIP (1 << 5)
#define MIP_HTIP (1 << 6)
#define MIP_MTIP (1 << 7)
#define MIP_UEIP (1 << 8)
#define MIP_SEIP (1 << 9)
#define MIP_HEIP (1 << 10)
#define MIP_MEIP (1 << 11)
typedef struct RISCVCPUState RISCVCPUState;
typedef struct {
RISCVCPUState *(*riscv_cpu_init)(PhysMemoryMap *mem_map);
void (*riscv_cpu_end)(RISCVCPUState *s);
void (*riscv_cpu_interp)(RISCVCPUState *s, int n_cycles);
uint64_t (*riscv_cpu_get_cycles)(RISCVCPUState *s);
void (*riscv_cpu_set_mip)(RISCVCPUState *s, uint32_t mask);
void (*riscv_cpu_reset_mip)(RISCVCPUState *s, uint32_t mask);
uint32_t (*riscv_cpu_get_mip)(RISCVCPUState *s);
BOOL (*riscv_cpu_get_power_down)(RISCVCPUState *s);
uint32_t (*riscv_cpu_get_misa)(RISCVCPUState *s);
void (*riscv_cpu_flush_tlb_write_range_ram)(RISCVCPUState *s,
uint8_t *ram_ptr, size_t ram_size);
} RISCVCPUClass;
typedef struct {
const RISCVCPUClass *class_ptr;
} RISCVCPUCommonState;
int riscv_cpu_get_max_xlen(void);
extern const RISCVCPUClass riscv_cpu_class32;
extern const RISCVCPUClass riscv_cpu_class64;
extern const RISCVCPUClass riscv_cpu_class128;
RISCVCPUState *riscv_cpu_init(PhysMemoryMap *mem_map, int max_xlen);
static inline void riscv_cpu_end(RISCVCPUState *s)
{
const RISCVCPUClass *c = ((RISCVCPUCommonState *)s)->class_ptr;
c->riscv_cpu_end(s);
}
static inline void riscv_cpu_interp(RISCVCPUState *s, int n_cycles)
{
const RISCVCPUClass *c = ((RISCVCPUCommonState *)s)->class_ptr;
c->riscv_cpu_interp(s, n_cycles);
}
static inline uint64_t riscv_cpu_get_cycles(RISCVCPUState *s)
{
const RISCVCPUClass *c = ((RISCVCPUCommonState *)s)->class_ptr;
return c->riscv_cpu_get_cycles(s);
}
static inline void riscv_cpu_set_mip(RISCVCPUState *s, uint32_t mask)
{
const RISCVCPUClass *c = ((RISCVCPUCommonState *)s)->class_ptr;
c->riscv_cpu_set_mip(s, mask);
}
static inline void riscv_cpu_reset_mip(RISCVCPUState *s, uint32_t mask)
{
const RISCVCPUClass *c = ((RISCVCPUCommonState *)s)->class_ptr;
c->riscv_cpu_reset_mip(s, mask);
}
static inline uint32_t riscv_cpu_get_mip(RISCVCPUState *s)
{
const RISCVCPUClass *c = ((RISCVCPUCommonState *)s)->class_ptr;
return c->riscv_cpu_get_mip(s);
}
static inline BOOL riscv_cpu_get_power_down(RISCVCPUState *s)
{
const RISCVCPUClass *c = ((RISCVCPUCommonState *)s)->class_ptr;
return c->riscv_cpu_get_power_down(s);
}
static inline uint32_t riscv_cpu_get_misa(RISCVCPUState *s)
{
const RISCVCPUClass *c = ((RISCVCPUCommonState *)s)->class_ptr;
return c->riscv_cpu_get_misa(s);
}
static inline void riscv_cpu_flush_tlb_write_range_ram(RISCVCPUState *s,
uint8_t *ram_ptr, size_t ram_size)
{
const RISCVCPUClass *c = ((RISCVCPUCommonState *)s)->class_ptr;
c->riscv_cpu_flush_tlb_write_range_ram(s, ram_ptr, ram_size);
}
#endif /* RISCV_CPU_H */

304
riscv_cpu_fp_template.h Normal file
View file

@ -0,0 +1,304 @@
/*
* RISCV emulator
*
* Copyright (c) 2016 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#if F_SIZE == 32
#define OPID 0
#define F_HIGH F32_HIGH
#elif F_SIZE == 64
#define OPID 1
#define F_HIGH F64_HIGH
#elif F_SIZE == 128
#define OPID 3
#define F_HIGH 0
#else
#error unsupported F_SIZE
#endif
#define FSIGN_MASK glue(FSIGN_MASK, F_SIZE)
case (0x00 << 2) | OPID:
rm = get_insn_rm(s, rm);
if (rm < 0)
goto illegal_insn;
s->fp_reg[rd] = glue(add_sf, F_SIZE)(s->fp_reg[rs1],
s->fp_reg[rs2],
rm, &s->fflags) | F_HIGH;
s->fs = 3;
break;
case (0x01 << 2) | OPID:
rm = get_insn_rm(s, rm);
if (rm < 0)
goto illegal_insn;
s->fp_reg[rd] = glue(sub_sf, F_SIZE)(s->fp_reg[rs1],
s->fp_reg[rs2],
rm, &s->fflags) | F_HIGH;
s->fs = 3;
break;
case (0x02 << 2) | OPID:
rm = get_insn_rm(s, rm);
if (rm < 0)
goto illegal_insn;
s->fp_reg[rd] = glue(mul_sf, F_SIZE)(s->fp_reg[rs1],
s->fp_reg[rs2],
rm, &s->fflags) | F_HIGH;
s->fs = 3;
break;
case (0x03 << 2) | OPID:
rm = get_insn_rm(s, rm);
if (rm < 0)
goto illegal_insn;
s->fp_reg[rd] = glue(div_sf, F_SIZE)(s->fp_reg[rs1],
s->fp_reg[rs2],
rm, &s->fflags) | F_HIGH;
s->fs = 3;
break;
case (0x0b << 2) | OPID:
rm = get_insn_rm(s, rm);
if (rm < 0 || rs2 != 0)
goto illegal_insn;
s->fp_reg[rd] = glue(sqrt_sf, F_SIZE)(s->fp_reg[rs1],
rm, &s->fflags) | F_HIGH;
s->fs = 3;
break;
case (0x04 << 2) | OPID:
switch(rm) {
case 0: /* fsgnj */
s->fp_reg[rd] = (s->fp_reg[rs1] & ~FSIGN_MASK) |
(s->fp_reg[rs2] & FSIGN_MASK);
break;
case 1: /* fsgnjn */
s->fp_reg[rd] = (s->fp_reg[rs1] & ~FSIGN_MASK) |
((s->fp_reg[rs2] & FSIGN_MASK) ^ FSIGN_MASK);
break;
case 2: /* fsgnjx */
s->fp_reg[rd] = s->fp_reg[rs1] ^
(s->fp_reg[rs2] & FSIGN_MASK);
break;
default:
goto illegal_insn;
}
s->fs = 3;
break;
case (0x05 << 2) | OPID:
switch(rm) {
case 0: /* fmin */
s->fp_reg[rd] = glue(min_sf, F_SIZE)(s->fp_reg[rs1],
s->fp_reg[rs2],
&s->fflags,
FMINMAX_IEEE754_201X) | F_HIGH;
break;
case 1: /* fmax */
s->fp_reg[rd] = glue(max_sf, F_SIZE)(s->fp_reg[rs1],
s->fp_reg[rs2],
&s->fflags,
FMINMAX_IEEE754_201X) | F_HIGH;
break;
default:
goto illegal_insn;
}
s->fs = 3;
break;
case (0x18 << 2) | OPID:
rm = get_insn_rm(s, rm);
if (rm < 0)
goto illegal_insn;
switch(rs2) {
case 0: /* fcvt.w.[sdq] */
val = (int32_t)glue(glue(cvt_sf, F_SIZE), _i32)(s->fp_reg[rs1], rm,
&s->fflags);
break;
case 1: /* fcvt.wu.[sdq] */
val = (int32_t)glue(glue(cvt_sf, F_SIZE), _u32)(s->fp_reg[rs1], rm,
&s->fflags);
break;
#if XLEN >= 64
case 2: /* fcvt.l.[sdq] */
val = (int64_t)glue(glue(cvt_sf, F_SIZE), _i64)(s->fp_reg[rs1], rm,
&s->fflags);
break;
case 3: /* fcvt.lu.[sdq] */
val = (int64_t)glue(glue(cvt_sf, F_SIZE), _u64)(s->fp_reg[rs1], rm,
&s->fflags);
break;
#endif
#if XLEN >= 128
/* XXX: the index is not defined in the spec */
case 4: /* fcvt.t.[sdq] */
val = glue(glue(cvt_sf, F_SIZE), _i128)(s->fp_reg[rs1], rm,
&s->fflags);
break;
case 5: /* fcvt.tu.[sdq] */
val = glue(glue(cvt_sf, F_SIZE), _u128)(s->fp_reg[rs1], rm,
&s->fflags);
break;
#endif
default:
goto illegal_insn;
}
if (rd != 0)
s->reg[rd] = val;
break;
case (0x14 << 2) | OPID:
switch(rm) {
case 0: /* fle */
val = glue(le_sf, F_SIZE)(s->fp_reg[rs1], s->fp_reg[rs2],
&s->fflags);
break;
case 1: /* flt */
val = glue(lt_sf, F_SIZE)(s->fp_reg[rs1], s->fp_reg[rs2],
&s->fflags);
break;
case 2: /* feq */
val = glue(eq_quiet_sf, F_SIZE)(s->fp_reg[rs1], s->fp_reg[rs2],
&s->fflags);
break;
default:
goto illegal_insn;
}
if (rd != 0)
s->reg[rd] = val;
break;
case (0x1a << 2) | OPID:
rm = get_insn_rm(s, rm);
if (rm < 0)
goto illegal_insn;
switch(rs2) {
case 0: /* fcvt.[sdq].w */
s->fp_reg[rd] = glue(cvt_i32_sf, F_SIZE)(s->reg[rs1], rm,
&s->fflags) | F_HIGH;
break;
case 1: /* fcvt.[sdq].wu */
s->fp_reg[rd] = glue(cvt_u32_sf, F_SIZE)(s->reg[rs1], rm,
&s->fflags) | F_HIGH;
break;
#if XLEN >= 64
case 2: /* fcvt.[sdq].l */
s->fp_reg[rd] = glue(cvt_i64_sf, F_SIZE)(s->reg[rs1], rm,
&s->fflags) | F_HIGH;
break;
case 3: /* fcvt.[sdq].lu */
s->fp_reg[rd] = glue(cvt_u64_sf, F_SIZE)(s->reg[rs1], rm,
&s->fflags) | F_HIGH;
break;
#endif
#if XLEN >= 128
/* XXX: the index is not defined in the spec */
case 4: /* fcvt.[sdq].t */
s->fp_reg[rd] = glue(cvt_i128_sf, F_SIZE)(s->reg[rs1], rm,
&s->fflags) | F_HIGH;
break;
case 5: /* fcvt.[sdq].tu */
s->fp_reg[rd] = glue(cvt_u128_sf, F_SIZE)(s->reg[rs1], rm,
&s->fflags) | F_HIGH;
break;
#endif
default:
goto illegal_insn;
}
s->fs = 3;
break;
case (0x08 << 2) | OPID:
rm = get_insn_rm(s, rm);
if (rm < 0)
goto illegal_insn;
switch(rs2) {
#if F_SIZE == 32 && FLEN >= 64
case 1: /* cvt.s.d */
s->fp_reg[rd] = cvt_sf64_sf32(s->fp_reg[rs1], rm, &s->fflags) | F32_HIGH;
break;
#if FLEN >= 128
case 3: /* cvt.s.q */
s->fp_reg[rd] = cvt_sf128_sf32(s->fp_reg[rs1], rm, &s->fflags) | F32_HIGH;
break;
#endif
#endif /* F_SIZE == 32 */
#if F_SIZE == 64
case 0: /* cvt.d.s */
s->fp_reg[rd] = cvt_sf32_sf64(s->fp_reg[rs1], &s->fflags) | F64_HIGH;
break;
#if FLEN >= 128
case 1: /* cvt.d.q */
s->fp_reg[rd] = cvt_sf128_sf64(s->fp_reg[rs1], rm, &s->fflags) | F64_HIGH;
break;
#endif
#endif /* F_SIZE == 64 */
#if F_SIZE == 128
case 0: /* cvt.q.s */
s->fp_reg[rd] = cvt_sf32_sf128(s->fp_reg[rs1], &s->fflags);
break;
case 1: /* cvt.q.d */
s->fp_reg[rd] = cvt_sf64_sf128(s->fp_reg[rs1], &s->fflags);
break;
#endif /* F_SIZE == 128 */
default:
goto illegal_insn;
}
s->fs = 3;
break;
case (0x1c << 2) | OPID:
if (rs2 != 0)
goto illegal_insn;
switch(rm) {
#if F_SIZE <= XLEN
case 0: /* fmv.x.s */
#if F_SIZE == 32
val = (int32_t)s->fp_reg[rs1];
#elif F_SIZE == 64
val = (int64_t)s->fp_reg[rs1];
#else
val = (int128_t)s->fp_reg[rs1];
#endif
break;
#endif /* F_SIZE <= XLEN */
case 1: /* fclass */
val = glue(fclass_sf, F_SIZE)(s->fp_reg[rs1]);
break;
default:
goto illegal_insn;
}
if (rd != 0)
s->reg[rd] = val;
break;
#if F_SIZE <= XLEN
case (0x1e << 2) | OPID: /* fmv.s.x */
if (rs2 != 0 || rm != 0)
goto illegal_insn;
#if F_SIZE == 32
s->fp_reg[rd] = (int32_t)s->reg[rs1];
#elif F_SIZE == 64
s->fp_reg[rd] = (int64_t)s->reg[rs1];
#else
s->fp_reg[rd] = (int128_t)s->reg[rs1];
#endif
s->fs = 3;
break;
#endif /* F_SIZE <= XLEN */
#undef F_SIZE
#undef F_HIGH
#undef OPID
#undef FSIGN_MASK

293
riscv_cpu_priv.h Normal file
View file

@ -0,0 +1,293 @@
/*
* RISCV CPU emulator private definitions
*
* Copyright (c) 2016-2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef RISCV_CPU_PRIV_H
#define RISCV_CPU_PRIV_H
#include "riscv_cpu.h"
#include "cutils.h"
#define __exception __attribute__((warn_unused_result))
#ifndef FLEN
#if MAX_XLEN == 128
#define FLEN 128
#else
#define FLEN 64
#endif
#endif /* !FLEN */
#define CONFIG_EXT_C /* compressed instructions */
#if defined(EMSCRIPTEN)
#define USE_GLOBAL_STATE
/* use local variables slows down the generated JS code */
#define USE_GLOBAL_VARIABLES
#endif
#if MAX_XLEN == 32
typedef uint32_t target_ulong;
typedef int32_t target_long;
#define PR_target_ulong "08x"
#elif MAX_XLEN == 64
typedef uint64_t target_ulong;
typedef int64_t target_long;
#define PR_target_ulong "016" PRIx64
#elif MAX_XLEN == 128
typedef uint128_t target_ulong;
typedef int128_t target_long;
#define PR_target_ulong "016" PRIx64 /* XXX */
#else
#error unsupported MAX_XLEN
#endif
/* FLEN is the floating point register width */
#if FLEN > 0
#if FLEN == 32
typedef uint32_t fp_uint;
#define F32_HIGH 0
#elif FLEN == 64
typedef uint64_t fp_uint;
#define F32_HIGH ((fp_uint)-1 << 32)
#define F64_HIGH 0
#elif FLEN == 128
typedef uint128_t fp_uint;
#define F32_HIGH ((fp_uint)-1 << 32)
#define F64_HIGH ((fp_uint)-1 << 64)
#else
#error unsupported FLEN
#endif
#endif
/* MLEN is the maximum memory access width */
#if MAX_XLEN <= 32 && FLEN <= 32
#define MLEN 32
#elif MAX_XLEN <= 64 && FLEN <= 64
#define MLEN 64
#else
#define MLEN 128
#endif
#if MLEN == 32
typedef uint32_t mem_uint_t;
#elif MLEN == 64
typedef uint64_t mem_uint_t;
#elif MLEN == 128
typedef uint128_t mem_uint_t;
#else
#unsupported MLEN
#endif
#define TLB_SIZE 256
#define CAUSE_MISALIGNED_FETCH 0x0
#define CAUSE_FAULT_FETCH 0x1
#define CAUSE_ILLEGAL_INSTRUCTION 0x2
#define CAUSE_BREAKPOINT 0x3
#define CAUSE_MISALIGNED_LOAD 0x4
#define CAUSE_FAULT_LOAD 0x5
#define CAUSE_MISALIGNED_STORE 0x6
#define CAUSE_FAULT_STORE 0x7
#define CAUSE_USER_ECALL 0x8
#define CAUSE_SUPERVISOR_ECALL 0x9
#define CAUSE_HYPERVISOR_ECALL 0xa
#define CAUSE_MACHINE_ECALL 0xb
#define CAUSE_FETCH_PAGE_FAULT 0xc
#define CAUSE_LOAD_PAGE_FAULT 0xd
#define CAUSE_STORE_PAGE_FAULT 0xf
/* Note: converted to correct bit position at runtime */
#define CAUSE_INTERRUPT ((uint32_t)1 << 31)
#define PRV_U 0
#define PRV_S 1
#define PRV_H 2
#define PRV_M 3
/* misa CSR */
#define MCPUID_SUPER (1 << ('S' - 'A'))
#define MCPUID_USER (1 << ('U' - 'A'))
#define MCPUID_I (1 << ('I' - 'A'))
#define MCPUID_M (1 << ('M' - 'A'))
#define MCPUID_A (1 << ('A' - 'A'))
#define MCPUID_F (1 << ('F' - 'A'))
#define MCPUID_D (1 << ('D' - 'A'))
#define MCPUID_Q (1 << ('Q' - 'A'))
#define MCPUID_C (1 << ('C' - 'A'))
/* mstatus CSR */
#define MSTATUS_SPIE_SHIFT 5
#define MSTATUS_MPIE_SHIFT 7
#define MSTATUS_SPP_SHIFT 8
#define MSTATUS_MPP_SHIFT 11
#define MSTATUS_FS_SHIFT 13
#define MSTATUS_UXL_SHIFT 32
#define MSTATUS_SXL_SHIFT 34
#define MSTATUS_UIE (1 << 0)
#define MSTATUS_SIE (1 << 1)
#define MSTATUS_HIE (1 << 2)
#define MSTATUS_MIE (1 << 3)
#define MSTATUS_UPIE (1 << 4)
#define MSTATUS_SPIE (1 << MSTATUS_SPIE_SHIFT)
#define MSTATUS_HPIE (1 << 6)
#define MSTATUS_MPIE (1 << MSTATUS_MPIE_SHIFT)
#define MSTATUS_SPP (1 << MSTATUS_SPP_SHIFT)
#define MSTATUS_HPP (3 << 9)
#define MSTATUS_MPP (3 << MSTATUS_MPP_SHIFT)
#define MSTATUS_FS (3 << MSTATUS_FS_SHIFT)
#define MSTATUS_XS (3 << 15)
#define MSTATUS_MPRV (1 << 17)
#define MSTATUS_SUM (1 << 18)
#define MSTATUS_MXR (1 << 19)
//#define MSTATUS_TVM (1 << 20)
//#define MSTATUS_TW (1 << 21)
//#define MSTATUS_TSR (1 << 22)
#define MSTATUS_UXL_MASK ((uint64_t)3 << MSTATUS_UXL_SHIFT)
#define MSTATUS_SXL_MASK ((uint64_t)3 << MSTATUS_SXL_SHIFT)
#define PG_SHIFT 12
#define PG_MASK ((1 << PG_SHIFT) - 1)
typedef struct {
target_ulong vaddr;
uintptr_t mem_addend;
} TLBEntry;
struct RISCVCPUState {
RISCVCPUCommonState common; /* must be first */
target_ulong pc;
target_ulong reg[32];
#ifdef USE_GLOBAL_VARIABLES
/* faster to use global variables with emscripten */
uint8_t *__code_ptr, *__code_end;
target_ulong __code_to_pc_addend;
#endif
#if FLEN > 0
fp_uint fp_reg[32];
uint32_t fflags;
uint8_t frm;
#endif
uint8_t cur_xlen; /* current XLEN value, <= MAX_XLEN */
uint8_t priv; /* see PRV_x */
uint8_t fs; /* MSTATUS_FS value */
uint8_t mxl; /* MXL field in MISA register */
int32_t n_cycles; /* only used inside the CPU loop */
uint64_t insn_counter;
BOOL power_down_flag;
int pending_exception; /* used during MMU exception handling */
target_ulong pending_tval;
/* CSRs */
target_ulong mstatus;
target_ulong mtvec;
target_ulong mscratch;
target_ulong mepc;
target_ulong mcause;
target_ulong mtval;
target_ulong mhartid; /* ro */
uint32_t misa;
uint32_t mie;
uint32_t mip;
uint32_t medeleg;
uint32_t mideleg;
uint32_t mcounteren;
target_ulong stvec;
target_ulong sscratch;
target_ulong sepc;
target_ulong scause;
target_ulong stval;
#if MAX_XLEN == 32
uint32_t satp;
#else
uint64_t satp; /* currently 64 bit physical addresses max */
#endif
uint32_t scounteren;
target_ulong load_res; /* for atomic LR/SC */
PhysMemoryMap *mem_map;
TLBEntry tlb_read[TLB_SIZE];
TLBEntry tlb_write[TLB_SIZE];
TLBEntry tlb_code[TLB_SIZE];
};
#define target_read_slow glue(glue(riscv, MAX_XLEN), _read_slow)
#define target_write_slow glue(glue(riscv, MAX_XLEN), _write_slow)
DLL_PUBLIC int target_read_slow(RISCVCPUState *s, mem_uint_t *pval,
target_ulong addr, int size_log2);
DLL_PUBLIC int target_write_slow(RISCVCPUState *s, target_ulong addr,
mem_uint_t val, int size_log2);
/* return 0 if OK, != 0 if exception */
#define TARGET_READ_WRITE(size, uint_type, size_log2) \
static inline __exception int target_read_u ## size(RISCVCPUState *s, uint_type *pval, target_ulong addr) \
{\
uint32_t tlb_idx;\
tlb_idx = (addr >> PG_SHIFT) & (TLB_SIZE - 1);\
if (likely(s->tlb_read[tlb_idx].vaddr == (addr & ~(PG_MASK & ~((size / 8) - 1))))) { \
*pval = *(uint_type *)(s->tlb_read[tlb_idx].mem_addend + (uintptr_t)addr);\
} else {\
mem_uint_t val;\
int ret;\
ret = target_read_slow(s, &val, addr, size_log2);\
if (ret)\
return ret;\
*pval = val;\
}\
return 0;\
}\
\
static inline __exception int target_write_u ## size(RISCVCPUState *s, target_ulong addr,\
uint_type val) \
{\
uint32_t tlb_idx;\
tlb_idx = (addr >> PG_SHIFT) & (TLB_SIZE - 1);\
if (likely(s->tlb_write[tlb_idx].vaddr == (addr & ~(PG_MASK & ~((size / 8) - 1))))) { \
*(uint_type *)(s->tlb_write[tlb_idx].mem_addend + (uintptr_t)addr) = val;\
return 0;\
} else {\
return target_write_slow(s, addr, val, size_log2);\
}\
}
TARGET_READ_WRITE(8, uint8_t, 0)
TARGET_READ_WRITE(16, uint16_t, 1)
TARGET_READ_WRITE(32, uint32_t, 2)
#if MLEN >= 64
TARGET_READ_WRITE(64, uint64_t, 3)
#endif
#if MLEN >= 128
TARGET_READ_WRITE(128, uint128_t, 4)
#endif
#endif /* RISCV_CPU_PRIV_H */

1739
riscv_cpu_template.h Normal file

File diff suppressed because it is too large Load diff

1053
riscv_machine.c Normal file

File diff suppressed because it is too large Load diff

275
sdl.c Normal file
View file

@ -0,0 +1,275 @@
/*
* SDL display driver
*
* Copyright (c) 2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include <fcntl.h>
#include <errno.h>
#include <SDL/SDL.h>
#include "cutils.h"
#include "virtio.h"
#include "machine.h"
#define KEYCODE_MAX 127
static SDL_Surface *screen;
static SDL_Surface *fb_surface;
static int screen_width, screen_height, fb_width, fb_height, fb_stride;
static SDL_Cursor *sdl_cursor_hidden;
static uint8_t key_pressed[KEYCODE_MAX + 1];
static void sdl_update_fb_surface(FBDevice *fb_dev)
{
if (!fb_surface)
goto force_alloc;
if (fb_width != fb_dev->width ||
fb_height != fb_dev->height ||
fb_stride != fb_dev->stride) {
force_alloc:
if (fb_surface != NULL)
SDL_FreeSurface(fb_surface);
fb_width = fb_dev->width;
fb_height = fb_dev->height;
fb_stride = fb_dev->stride;
fb_surface = SDL_CreateRGBSurfaceFrom(fb_dev->fb_data,
fb_dev->width, fb_dev->height,
32, fb_dev->stride,
0x00ff0000,
0x0000ff00,
0x000000ff,
0x00000000);
if (!fb_surface) {
fprintf(stderr, "Could not create SDL framebuffer surface\n");
exit(1);
}
}
}
static void sdl_update(FBDevice *fb_dev, void *opaque,
int x, int y, int w, int h)
{
SDL_Rect r;
// printf("sdl_update: %d %d %d %d\n", x, y, w, h);
r.x = x;
r.y = y;
r.w = w;
r.h = h;
SDL_BlitSurface(fb_surface, &r, screen, &r);
SDL_UpdateRect(screen, r.x, r.y, r.w, r.h);
}
#if defined(_WIN32)
static int sdl_get_keycode(const SDL_KeyboardEvent *ev)
{
return ev->keysym.scancode;
}
#else
/* we assume Xorg is used with a PC keyboard. Return 0 if no keycode found. */
static int sdl_get_keycode(const SDL_KeyboardEvent *ev)
{
int keycode;
keycode = ev->keysym.scancode;
if (keycode < 9) {
keycode = 0;
} else if (keycode < 127 + 8) {
keycode -= 8;
} else {
keycode = 0;
}
return keycode;
}
#endif
/* release all pressed keys */
static void sdl_reset_keys(VirtMachine *m)
{
int i;
for(i = 1; i <= KEYCODE_MAX; i++) {
if (key_pressed[i]) {
vm_send_key_event(m, FALSE, i);
key_pressed[i] = FALSE;
}
}
}
static void sdl_handle_key_event(const SDL_KeyboardEvent *ev, VirtMachine *m)
{
int keycode, keypress;
keycode = sdl_get_keycode(ev);
if (keycode) {
if (keycode == 0x3a || keycode ==0x45) {
/* SDL does not generate key up for numlock & caps lock */
vm_send_key_event(m, TRUE, keycode);
vm_send_key_event(m, FALSE, keycode);
} else {
keypress = (ev->type == SDL_KEYDOWN);
if (keycode <= KEYCODE_MAX)
key_pressed[keycode] = keypress;
vm_send_key_event(m, keypress, keycode);
}
} else if (ev->type == SDL_KEYUP) {
/* workaround to reset the keyboard state (used when changing
desktop with ctrl-alt-x on Linux) */
sdl_reset_keys(m);
}
}
static void sdl_send_mouse_event(VirtMachine *m, int x1, int y1,
int dz, int state, BOOL is_absolute)
{
int buttons, x, y;
buttons = 0;
if (state & SDL_BUTTON(SDL_BUTTON_LEFT))
buttons |= (1 << 0);
if (state & SDL_BUTTON(SDL_BUTTON_RIGHT))
buttons |= (1 << 1);
if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE))
buttons |= (1 << 2);
if (is_absolute) {
x = (x1 * 32768) / screen_width;
y = (y1 * 32768) / screen_height;
} else {
x = x1;
y = y1;
}
vm_send_mouse_event(m, x, y, dz, buttons);
}
static void sdl_handle_mouse_motion_event(const SDL_Event *ev, VirtMachine *m)
{
BOOL is_absolute = vm_mouse_is_absolute(m);
int x, y;
if (is_absolute) {
x = ev->motion.x;
y = ev->motion.y;
} else {
x = ev->motion.xrel;
y = ev->motion.yrel;
}
sdl_send_mouse_event(m, x, y, 0, ev->motion.state, is_absolute);
}
static void sdl_handle_mouse_button_event(const SDL_Event *ev, VirtMachine *m)
{
BOOL is_absolute = vm_mouse_is_absolute(m);
int state, dz;
dz = 0;
if (ev->type == SDL_MOUSEBUTTONDOWN) {
if (ev->button.button == SDL_BUTTON_WHEELUP) {
dz = 1;
} else if (ev->button.button == SDL_BUTTON_WHEELDOWN) {
dz = -1;
}
}
state = SDL_GetMouseState(NULL, NULL);
/* just in case */
if (ev->type == SDL_MOUSEBUTTONDOWN)
state |= SDL_BUTTON(ev->button.button);
else
state &= ~SDL_BUTTON(ev->button.button);
if (is_absolute) {
sdl_send_mouse_event(m, ev->button.x, ev->button.y,
dz, state, is_absolute);
} else {
sdl_send_mouse_event(m, 0, 0, dz, state, is_absolute);
}
}
void sdl_refresh(VirtMachine *m)
{
SDL_Event ev_s, *ev = &ev_s;
if (!m->fb_dev)
return;
sdl_update_fb_surface(m->fb_dev);
m->fb_dev->refresh(m->fb_dev, sdl_update, NULL);
while (SDL_PollEvent(ev)) {
switch (ev->type) {
case SDL_KEYDOWN:
case SDL_KEYUP:
sdl_handle_key_event(&ev->key, m);
break;
case SDL_MOUSEMOTION:
sdl_handle_mouse_motion_event(ev, m);
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
sdl_handle_mouse_button_event(ev, m);
break;
case SDL_QUIT:
exit(0);
}
}
}
static void sdl_hide_cursor(void)
{
uint8_t data = 0;
sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
SDL_ShowCursor(1);
SDL_SetCursor(sdl_cursor_hidden);
}
void sdl_init(int width, int height)
{
int flags;
screen_width = width;
screen_height = height;
if (SDL_Init (SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE)) {
fprintf(stderr, "Could not initialize SDL - exiting\n");
exit(1);
}
flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL;
screen = SDL_SetVideoMode(width, height, 0, flags);
if (!screen || !screen->pixels) {
fprintf(stderr, "Could not open SDL display\n");
exit(1);
}
SDL_WM_SetCaption("TinyEMU", "TinyEMU");
sdl_hide_cursor();
}

341
sha256.c Normal file
View file

@ -0,0 +1,341 @@
/* LibTomCrypt, modular cryptographic library -- Tom St Denis
*
* LibTomCrypt is a library that provides various cryptographic
* algorithms in a highly modular and flexible manner.
*
* The library is free for all purposes without any express
* guarantee it works.
*
* Tom St Denis, tomstdenis@gmail.com, http://libtom.org
*/
#include <stdlib.h>
#include <string.h>
#include "cutils.h"
#include "sha256.h"
#define LOAD32H(a, b) a = get_be32(b)
#define STORE32H(a, b) put_be32(b, a)
#define STORE64H(a, b) put_be64(b, a)
#define RORc(x, y) ( ((((uint32_t)(x)&0xFFFFFFFFUL)>>(uint32_t)((y)&31)) | ((uint32_t)(x)<<(uint32_t)(32-((y)&31)))) & 0xFFFFFFFFUL)
#if defined(CONFIG_EMBUE)
#define LTC_SMALL_CODE
#endif
/**
@file sha256.c
LTC_SHA256 by Tom St Denis
*/
#ifdef LTC_SMALL_CODE
/* the K array */
static const uint32_t K[64] = {
0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL,
0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL,
0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL,
0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL,
0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL,
0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL,
0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL,
0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL,
0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL,
0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
};
#endif
/* Various logical functions */
#define Ch(x,y,z) (z ^ (x & (y ^ z)))
#define Maj(x,y,z) (((x | y) & z) | (x & y))
#define S(x, n) RORc((x),(n))
#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n))
#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22))
#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25))
#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3))
#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10))
/* compress 512-bits */
static void sha256_compress(SHA256_CTX *s, unsigned char *buf)
{
uint32_t S[8], W[64], t0, t1;
#ifdef LTC_SMALL_CODE
uint32_t t;
#endif
int i;
/* copy state into S */
for (i = 0; i < 8; i++) {
S[i] = s->state[i];
}
/* copy the state into 512-bits into W[0..15] */
for (i = 0; i < 16; i++) {
LOAD32H(W[i], buf + (4*i));
}
/* fill W[16..63] */
for (i = 16; i < 64; i++) {
W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16];
}
/* Compress */
#ifdef LTC_SMALL_CODE
#define RND(a,b,c,d,e,f,g,h,i) \
t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \
t1 = Sigma0(a) + Maj(a, b, c); \
d += t0; \
h = t0 + t1;
for (i = 0; i < 64; ++i) {
RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i);
t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4];
S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t;
}
#else
#define RND(a,b,c,d,e,f,g,h,i,ki) \
t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i]; \
t1 = Sigma0(a) + Maj(a, b, c); \
d += t0; \
h = t0 + t1;
RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],0,0x428a2f98);
RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],1,0x71374491);
RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],2,0xb5c0fbcf);
RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],3,0xe9b5dba5);
RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],4,0x3956c25b);
RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],5,0x59f111f1);
RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],6,0x923f82a4);
RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],7,0xab1c5ed5);
RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],8,0xd807aa98);
RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],9,0x12835b01);
RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],10,0x243185be);
RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],11,0x550c7dc3);
RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],12,0x72be5d74);
RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],13,0x80deb1fe);
RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],14,0x9bdc06a7);
RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],15,0xc19bf174);
RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],16,0xe49b69c1);
RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],17,0xefbe4786);
RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],18,0x0fc19dc6);
RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],19,0x240ca1cc);
RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],20,0x2de92c6f);
RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],21,0x4a7484aa);
RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],22,0x5cb0a9dc);
RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],23,0x76f988da);
RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],24,0x983e5152);
RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],25,0xa831c66d);
RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],26,0xb00327c8);
RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],27,0xbf597fc7);
RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],28,0xc6e00bf3);
RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],29,0xd5a79147);
RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],30,0x06ca6351);
RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],31,0x14292967);
RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],32,0x27b70a85);
RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],33,0x2e1b2138);
RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],34,0x4d2c6dfc);
RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],35,0x53380d13);
RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],36,0x650a7354);
RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],37,0x766a0abb);
RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],38,0x81c2c92e);
RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],39,0x92722c85);
RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],40,0xa2bfe8a1);
RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],41,0xa81a664b);
RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],42,0xc24b8b70);
RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],43,0xc76c51a3);
RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],44,0xd192e819);
RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],45,0xd6990624);
RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],46,0xf40e3585);
RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],47,0x106aa070);
RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],48,0x19a4c116);
RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],49,0x1e376c08);
RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],50,0x2748774c);
RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],51,0x34b0bcb5);
RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],52,0x391c0cb3);
RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],53,0x4ed8aa4a);
RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],54,0x5b9cca4f);
RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],55,0x682e6ff3);
RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],56,0x748f82ee);
RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],57,0x78a5636f);
RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],58,0x84c87814);
RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],59,0x8cc70208);
RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],60,0x90befffa);
RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],61,0xa4506ceb);
RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],62,0xbef9a3f7);
RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],63,0xc67178f2);
#undef RND
#endif
/* feedback */
for (i = 0; i < 8; i++) {
s->state[i] = s->state[i] + S[i];
}
}
#ifdef LTC_CLEAN_STACK
static int sha256_compress(hash_state * md, unsigned char *buf)
{
int err;
err = _sha256_compress(md, buf);
burn_stack(sizeof(uint32_t) * 74);
return err;
}
#endif
/**
Initialize the hash state
@param md The hash state you wish to initialize
@return CRYPT_OK if successful
*/
void SHA256_Init(SHA256_CTX *s)
{
s->curlen = 0;
s->length = 0;
s->state[0] = 0x6A09E667UL;
s->state[1] = 0xBB67AE85UL;
s->state[2] = 0x3C6EF372UL;
s->state[3] = 0xA54FF53AUL;
s->state[4] = 0x510E527FUL;
s->state[5] = 0x9B05688CUL;
s->state[6] = 0x1F83D9ABUL;
s->state[7] = 0x5BE0CD19UL;
}
void SHA256_Update(SHA256_CTX *s, const uint8_t *in, unsigned long inlen)
{
unsigned long n;
if (s->curlen > sizeof(s->buf)) {
abort();
}
if ((s->length + inlen) < s->length) {
abort();
}
while (inlen > 0) {
if (s->curlen == 0 && inlen >= 64) {
sha256_compress(s, (unsigned char *)in);
s->length += 64 * 8;
in += 64;
inlen -= 64;
} else {
n = min_int(inlen, 64 - s->curlen);
memcpy(s->buf + s->curlen, in, (size_t)n);
s->curlen += n;
in += n;
inlen -= n;
if (s->curlen == 64) {
sha256_compress(s, s->buf);
s->length += 8*64;
s->curlen = 0;
}
}
} }
/**
Terminate the hash to get the digest
@param md The hash state
@param out [out] The destination of the hash (32 bytes)
@return CRYPT_OK if successful
*/
void SHA256_Final(uint8_t *out, SHA256_CTX *s)
{
int i;
if (s->curlen >= sizeof(s->buf)) {
abort();
}
/* increase the length of the message */
s->length += s->curlen * 8;
/* append the '1' bit */
s->buf[s->curlen++] = (unsigned char)0x80;
/* if the length is currently above 56 bytes we append zeros
* then compress. Then we can fall back to padding zeros and length
* encoding like normal.
*/
if (s->curlen > 56) {
while (s->curlen < 64) {
s->buf[s->curlen++] = (unsigned char)0;
}
sha256_compress(s, s->buf);
s->curlen = 0;
}
/* pad upto 56 bytes of zeroes */
while (s->curlen < 56) {
s->buf[s->curlen++] = (unsigned char)0;
}
/* store length */
STORE64H(s->length, s->buf+56);
sha256_compress(s, s->buf);
/* copy output */
for (i = 0; i < 8; i++) {
STORE32H(s->state[i], out+(4*i));
}
#ifdef LTC_CLEAN_STACK
zeromem(md, sizeof(hash_state));
#endif
}
void SHA256(const uint8_t *buf, int buf_len, uint8_t *out)
{
SHA256_CTX ctx;
SHA256_Init(&ctx);
SHA256_Update(&ctx, buf, buf_len);
SHA256_Final(out, &ctx);
}
#if 0
/**
Self-test the hash
@return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
*/
int sha256_test(void)
{
#ifndef LTC_TEST
return CRYPT_NOP;
#else
static const struct {
char *msg;
unsigned char hash[32];
} tests[] = {
{ "abc",
{ 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad }
},
{ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
{ 0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8,
0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39,
0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67,
0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1 }
},
};
int i;
unsigned char tmp[32];
hash_state md;
for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) {
sha256_init(&md);
sha256_process(&md, (unsigned char*)tests[i].msg, (unsigned long)strlen(tests[i].msg));
sha256_done(&md, tmp);
if (XMEMCMP(tmp, tests[i].hash, 32) != 0) {
return CRYPT_FAIL_TESTVECTOR;
}
}
return CRYPT_OK;
#endif
}
#endif

40
sha256.h Normal file
View file

@ -0,0 +1,40 @@
/*
* OpenSSL compatible SHA256 header
*
* Copyright (c) 2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef SHA256_H
#define SHA256_H
#define SHA256_DIGEST_LENGTH 32
typedef struct {
uint64_t length;
uint32_t state[8], curlen;
uint8_t buf[64];
} SHA256_CTX;
void SHA256_Init(SHA256_CTX *s);
void SHA256_Update(SHA256_CTX *s, const uint8_t *in, unsigned long inlen);
void SHA256_Final(uint8_t *out, SHA256_CTX *s);
void SHA256(const uint8_t *buf, int buf_len, uint8_t *out);
#endif /* SHA256_H */

127
simplefb.c Normal file
View file

@ -0,0 +1,127 @@
/*
* Simple frame buffer
*
* Copyright (c) 2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include "cutils.h"
#include "iomem.h"
#include "virtio.h"
#include "machine.h"
//#define DEBUG_VBE
#define FB_ALLOC_ALIGN 65536
struct SimpleFBState {
FBDevice *fb_dev;
int fb_page_count;
PhysMemoryRange *mem_range;
};
#define MAX_MERGE_DISTANCE 3
void simplefb_refresh(FBDevice *fb_dev,
SimpleFBDrawFunc *redraw_func, void *opaque,
PhysMemoryRange *mem_range,
int fb_page_count)
{
const uint32_t *dirty_bits;
uint32_t dirty_val;
int y0, y1, page_y0, page_y1, byte_pos, page_index, bit_pos;
dirty_bits = phys_mem_get_dirty_bits(mem_range);
page_index = 0;
y0 = y1 = 0;
while (page_index < fb_page_count) {
dirty_val = dirty_bits[page_index >> 5];
if (dirty_val != 0) {
bit_pos = 0;
while (dirty_val != 0) {
while (((dirty_val >> bit_pos) & 1) == 0)
bit_pos++;
dirty_val &= ~(1 << bit_pos);
byte_pos = (page_index + bit_pos) * DEVRAM_PAGE_SIZE;
page_y0 = byte_pos / fb_dev->stride;
page_y1 = ((byte_pos + DEVRAM_PAGE_SIZE - 1) / fb_dev->stride) + 1;
page_y1 = min_int(page_y1, fb_dev->height);
if (y0 == y1) {
y0 = page_y0;
y1 = page_y1;
} else if (page_y0 <= (y1 + MAX_MERGE_DISTANCE)) {
/* union with current region */
y1 = page_y1;
} else {
/* flush */
redraw_func(fb_dev, opaque,
0, y0, fb_dev->width, y1 - y0);
y0 = page_y0;
y1 = page_y1;
}
}
}
page_index += 32;
}
if (y0 != y1) {
redraw_func(fb_dev, opaque,
0, y0, fb_dev->width, y1 - y0);
}
}
static void simplefb_refresh1(FBDevice *fb_dev,
SimpleFBDrawFunc *redraw_func, void *opaque)
{
SimpleFBState *s = fb_dev->device_opaque;
simplefb_refresh(fb_dev, redraw_func, opaque, s->mem_range,
s->fb_page_count);
}
SimpleFBState *simplefb_init(PhysMemoryMap *map, uint64_t phys_addr,
FBDevice *fb_dev, int width, int height)
{
SimpleFBState *s;
s = mallocz(sizeof(*s));
s->fb_dev = fb_dev;
fb_dev->width = width;
fb_dev->height = height;
fb_dev->stride = width * 4;
fb_dev->fb_size = (height * fb_dev->stride + FB_ALLOC_ALIGN - 1) & ~(FB_ALLOC_ALIGN - 1);
s->fb_page_count = fb_dev->fb_size >> DEVRAM_PAGE_SIZE_LOG2;
s->mem_range = cpu_register_ram(map, phys_addr, fb_dev->fb_size,
DEVRAM_FLAG_DIRTY_BITS);
fb_dev->fb_data = s->mem_range->phys_mem;
fb_dev->device_opaque = s;
fb_dev->refresh = simplefb_refresh1;
return s;
}

314
slirp/bootp.c Normal file
View file

@ -0,0 +1,314 @@
/*
* QEMU BOOTP/DHCP server
*
* Copyright (c) 2004 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "slirp.h"
/* XXX: only DHCP is supported */
#define LEASE_TIME (24 * 3600)
static const uint8_t rfc1533_cookie[] = { RFC1533_COOKIE };
#ifdef DEBUG
#define DPRINTF(fmt, ...) \
do if (slirp_debug & DBG_CALL) { fprintf(dfd, fmt, ## __VA_ARGS__); fflush(dfd); } while (0)
#else
#define DPRINTF(fmt, ...) do{}while(0)
#endif
static BOOTPClient *get_new_addr(Slirp *slirp, struct in_addr *paddr,
const uint8_t *macaddr)
{
BOOTPClient *bc;
int i;
for(i = 0; i < NB_BOOTP_CLIENTS; i++) {
bc = &slirp->bootp_clients[i];
if (!bc->allocated || !memcmp(macaddr, bc->macaddr, 6))
goto found;
}
return NULL;
found:
bc = &slirp->bootp_clients[i];
bc->allocated = 1;
paddr->s_addr = slirp->vdhcp_startaddr.s_addr + htonl(i);
return bc;
}
static BOOTPClient *request_addr(Slirp *slirp, const struct in_addr *paddr,
const uint8_t *macaddr)
{
uint32_t req_addr = ntohl(paddr->s_addr);
uint32_t dhcp_addr = ntohl(slirp->vdhcp_startaddr.s_addr);
BOOTPClient *bc;
if (req_addr >= dhcp_addr &&
req_addr < (dhcp_addr + NB_BOOTP_CLIENTS)) {
bc = &slirp->bootp_clients[req_addr - dhcp_addr];
if (!bc->allocated || !memcmp(macaddr, bc->macaddr, 6)) {
bc->allocated = 1;
return bc;
}
}
return NULL;
}
static BOOTPClient *find_addr(Slirp *slirp, struct in_addr *paddr,
const uint8_t *macaddr)
{
BOOTPClient *bc;
int i;
for(i = 0; i < NB_BOOTP_CLIENTS; i++) {
if (!memcmp(macaddr, slirp->bootp_clients[i].macaddr, 6))
goto found;
}
return NULL;
found:
bc = &slirp->bootp_clients[i];
bc->allocated = 1;
paddr->s_addr = slirp->vdhcp_startaddr.s_addr + htonl(i);
return bc;
}
static void dhcp_decode(const struct bootp_t *bp, int *pmsg_type,
struct in_addr *preq_addr)
{
const uint8_t *p, *p_end;
int len, tag;
*pmsg_type = 0;
preq_addr->s_addr = htonl(0L);
p = bp->bp_vend;
p_end = p + DHCP_OPT_LEN;
if (memcmp(p, rfc1533_cookie, 4) != 0)
return;
p += 4;
while (p < p_end) {
tag = p[0];
if (tag == RFC1533_PAD) {
p++;
} else if (tag == RFC1533_END) {
break;
} else {
p++;
if (p >= p_end)
break;
len = *p++;
DPRINTF("dhcp: tag=%d len=%d\n", tag, len);
switch(tag) {
case RFC2132_MSG_TYPE:
if (len >= 1)
*pmsg_type = p[0];
break;
case RFC2132_REQ_ADDR:
if (len >= 4) {
memcpy(&(preq_addr->s_addr), p, 4);
}
break;
default:
break;
}
p += len;
}
}
if (*pmsg_type == DHCPREQUEST && preq_addr->s_addr == htonl(0L) &&
bp->bp_ciaddr.s_addr) {
memcpy(&(preq_addr->s_addr), &bp->bp_ciaddr, 4);
}
}
static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
{
BOOTPClient *bc = NULL;
struct mbuf *m;
struct bootp_t *rbp;
struct sockaddr_in saddr, daddr;
struct in_addr preq_addr;
int dhcp_msg_type, val;
uint8_t *q;
/* extract exact DHCP msg type */
dhcp_decode(bp, &dhcp_msg_type, &preq_addr);
DPRINTF("bootp packet op=%d msgtype=%d", bp->bp_op, dhcp_msg_type);
if (preq_addr.s_addr != htonl(0L))
DPRINTF(" req_addr=%08x\n", ntohl(preq_addr.s_addr));
else
DPRINTF("\n");
if (dhcp_msg_type == 0)
dhcp_msg_type = DHCPREQUEST; /* Force reply for old BOOTP clients */
if (dhcp_msg_type != DHCPDISCOVER &&
dhcp_msg_type != DHCPREQUEST)
return;
/* XXX: this is a hack to get the client mac address */
memcpy(slirp->client_ethaddr, bp->bp_hwaddr, 6);
m = m_get(slirp);
if (!m) {
return;
}
m->m_data += IF_MAXLINKHDR;
rbp = (struct bootp_t *)m->m_data;
m->m_data += sizeof(struct udpiphdr);
memset(rbp, 0, sizeof(struct bootp_t));
if (dhcp_msg_type == DHCPDISCOVER) {
if (preq_addr.s_addr != htonl(0L)) {
bc = request_addr(slirp, &preq_addr, slirp->client_ethaddr);
if (bc) {
daddr.sin_addr = preq_addr;
}
}
if (!bc) {
new_addr:
bc = get_new_addr(slirp, &daddr.sin_addr, slirp->client_ethaddr);
if (!bc) {
DPRINTF("no address left\n");
return;
}
}
memcpy(bc->macaddr, slirp->client_ethaddr, 6);
} else if (preq_addr.s_addr != htonl(0L)) {
bc = request_addr(slirp, &preq_addr, slirp->client_ethaddr);
if (bc) {
daddr.sin_addr = preq_addr;
memcpy(bc->macaddr, slirp->client_ethaddr, 6);
} else {
daddr.sin_addr.s_addr = 0;
}
} else {
bc = find_addr(slirp, &daddr.sin_addr, bp->bp_hwaddr);
if (!bc) {
/* if never assigned, behaves as if it was already
assigned (windows fix because it remembers its address) */
goto new_addr;
}
}
saddr.sin_addr = slirp->vhost_addr;
saddr.sin_port = htons(BOOTP_SERVER);
daddr.sin_port = htons(BOOTP_CLIENT);
rbp->bp_op = BOOTP_REPLY;
rbp->bp_xid = bp->bp_xid;
rbp->bp_htype = 1;
rbp->bp_hlen = 6;
memcpy(rbp->bp_hwaddr, bp->bp_hwaddr, 6);
rbp->bp_yiaddr = daddr.sin_addr; /* Client IP address */
rbp->bp_siaddr = saddr.sin_addr; /* Server IP address */
q = rbp->bp_vend;
memcpy(q, rfc1533_cookie, 4);
q += 4;
if (bc) {
DPRINTF("%s addr=%08x\n",
(dhcp_msg_type == DHCPDISCOVER) ? "offered" : "ack'ed",
ntohl(daddr.sin_addr.s_addr));
if (dhcp_msg_type == DHCPDISCOVER) {
*q++ = RFC2132_MSG_TYPE;
*q++ = 1;
*q++ = DHCPOFFER;
} else /* DHCPREQUEST */ {
*q++ = RFC2132_MSG_TYPE;
*q++ = 1;
*q++ = DHCPACK;
}
if (slirp->bootp_filename)
snprintf((char *)rbp->bp_file, sizeof(rbp->bp_file), "%s",
slirp->bootp_filename);
*q++ = RFC2132_SRV_ID;
*q++ = 4;
memcpy(q, &saddr.sin_addr, 4);
q += 4;
*q++ = RFC1533_NETMASK;
*q++ = 4;
memcpy(q, &slirp->vnetwork_mask, 4);
q += 4;
if (!slirp->restricted) {
*q++ = RFC1533_GATEWAY;
*q++ = 4;
memcpy(q, &saddr.sin_addr, 4);
q += 4;
*q++ = RFC1533_DNS;
*q++ = 4;
memcpy(q, &slirp->vnameserver_addr, 4);
q += 4;
}
*q++ = RFC2132_LEASE_TIME;
*q++ = 4;
val = htonl(LEASE_TIME);
memcpy(q, &val, 4);
q += 4;
if (*slirp->client_hostname) {
val = strlen(slirp->client_hostname);
*q++ = RFC1533_HOSTNAME;
*q++ = val;
memcpy(q, slirp->client_hostname, val);
q += val;
}
} else {
static const char nak_msg[] = "requested address not available";
DPRINTF("nak'ed addr=%08x\n", ntohl(preq_addr->s_addr));
*q++ = RFC2132_MSG_TYPE;
*q++ = 1;
*q++ = DHCPNAK;
*q++ = RFC2132_MESSAGE;
*q++ = sizeof(nak_msg) - 1;
memcpy(q, nak_msg, sizeof(nak_msg) - 1);
q += sizeof(nak_msg) - 1;
}
*q = RFC1533_END;
daddr.sin_addr.s_addr = 0xffffffffu;
m->m_len = sizeof(struct bootp_t) -
sizeof(struct ip) - sizeof(struct udphdr);
udp_output2(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
}
void bootp_input(struct mbuf *m)
{
struct bootp_t *bp = mtod(m, struct bootp_t *);
if (bp->bp_op == BOOTP_REQUEST) {
bootp_reply(m->slirp, bp);
}
}

122
slirp/bootp.h Normal file
View file

@ -0,0 +1,122 @@
/* bootp/dhcp defines */
#define BOOTP_SERVER 67
#define BOOTP_CLIENT 68
#define BOOTP_REQUEST 1
#define BOOTP_REPLY 2
#define RFC1533_COOKIE 99, 130, 83, 99
#define RFC1533_PAD 0
#define RFC1533_NETMASK 1
#define RFC1533_TIMEOFFSET 2
#define RFC1533_GATEWAY 3
#define RFC1533_TIMESERVER 4
#define RFC1533_IEN116NS 5
#define RFC1533_DNS 6
#define RFC1533_LOGSERVER 7
#define RFC1533_COOKIESERVER 8
#define RFC1533_LPRSERVER 9
#define RFC1533_IMPRESSSERVER 10
#define RFC1533_RESOURCESERVER 11
#define RFC1533_HOSTNAME 12
#define RFC1533_BOOTFILESIZE 13
#define RFC1533_MERITDUMPFILE 14
#define RFC1533_DOMAINNAME 15
#define RFC1533_SWAPSERVER 16
#define RFC1533_ROOTPATH 17
#define RFC1533_EXTENSIONPATH 18
#define RFC1533_IPFORWARDING 19
#define RFC1533_IPSOURCEROUTING 20
#define RFC1533_IPPOLICYFILTER 21
#define RFC1533_IPMAXREASSEMBLY 22
#define RFC1533_IPTTL 23
#define RFC1533_IPMTU 24
#define RFC1533_IPMTUPLATEAU 25
#define RFC1533_INTMTU 26
#define RFC1533_INTLOCALSUBNETS 27
#define RFC1533_INTBROADCAST 28
#define RFC1533_INTICMPDISCOVER 29
#define RFC1533_INTICMPRESPOND 30
#define RFC1533_INTROUTEDISCOVER 31
#define RFC1533_INTROUTESOLICIT 32
#define RFC1533_INTSTATICROUTES 33
#define RFC1533_LLTRAILERENCAP 34
#define RFC1533_LLARPCACHETMO 35
#define RFC1533_LLETHERNETENCAP 36
#define RFC1533_TCPTTL 37
#define RFC1533_TCPKEEPALIVETMO 38
#define RFC1533_TCPKEEPALIVEGB 39
#define RFC1533_NISDOMAIN 40
#define RFC1533_NISSERVER 41
#define RFC1533_NTPSERVER 42
#define RFC1533_VENDOR 43
#define RFC1533_NBNS 44
#define RFC1533_NBDD 45
#define RFC1533_NBNT 46
#define RFC1533_NBSCOPE 47
#define RFC1533_XFS 48
#define RFC1533_XDM 49
#define RFC2132_REQ_ADDR 50
#define RFC2132_LEASE_TIME 51
#define RFC2132_MSG_TYPE 53
#define RFC2132_SRV_ID 54
#define RFC2132_PARAM_LIST 55
#define RFC2132_MESSAGE 56
#define RFC2132_MAX_SIZE 57
#define RFC2132_RENEWAL_TIME 58
#define RFC2132_REBIND_TIME 59
#define DHCPDISCOVER 1
#define DHCPOFFER 2
#define DHCPREQUEST 3
#define DHCPACK 5
#define DHCPNAK 6
#define RFC1533_VENDOR_MAJOR 0
#define RFC1533_VENDOR_MINOR 0
#define RFC1533_VENDOR_MAGIC 128
#define RFC1533_VENDOR_ADDPARM 129
#define RFC1533_VENDOR_ETHDEV 130
#define RFC1533_VENDOR_HOWTO 132
#define RFC1533_VENDOR_MNUOPTS 160
#define RFC1533_VENDOR_SELECTION 176
#define RFC1533_VENDOR_MOTD 184
#define RFC1533_VENDOR_NUMOFMOTD 8
#define RFC1533_VENDOR_IMG 192
#define RFC1533_VENDOR_NUMOFIMG 16
#define RFC1533_END 255
#define BOOTP_VENDOR_LEN 64
#define DHCP_OPT_LEN 312
struct bootp_t {
struct ip ip;
struct udphdr udp;
uint8_t bp_op;
uint8_t bp_htype;
uint8_t bp_hlen;
uint8_t bp_hops;
uint32_t bp_xid;
uint16_t bp_secs;
uint16_t unused;
struct in_addr bp_ciaddr;
struct in_addr bp_yiaddr;
struct in_addr bp_siaddr;
struct in_addr bp_giaddr;
uint8_t bp_hwaddr[16];
uint8_t bp_sname[64];
uint8_t bp_file[128];
uint8_t bp_vend[DHCP_OPT_LEN];
};
typedef struct {
uint16_t allocated;
uint8_t macaddr[6];
} BOOTPClient;
#define NB_BOOTP_CLIENTS 16
void bootp_input(struct mbuf *m);

139
slirp/cksum.c Normal file
View file

@ -0,0 +1,139 @@
/*
* Copyright (c) 1988, 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)in_cksum.c 8.1 (Berkeley) 6/10/93
* in_cksum.c,v 1.2 1994/08/02 07:48:16 davidg Exp
*/
#include "slirp.h"
/*
* Checksum routine for Internet Protocol family headers (Portable Version).
*
* This routine is very heavily used in the network
* code and should be modified for each CPU to be as fast as possible.
*
* XXX Since we will never span more than 1 mbuf, we can optimise this
*/
#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x)
#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; \
(void)ADDCARRY(sum);}
int cksum(struct mbuf *m, int len)
{
register uint16_t *w;
register int sum = 0;
register int mlen = 0;
int byte_swapped = 0;
union {
uint8_t c[2];
uint16_t s;
} s_util;
union {
uint16_t s[2];
uint32_t l;
} l_util;
if (m->m_len == 0)
goto cont;
w = mtod(m, uint16_t *);
mlen = m->m_len;
if (len < mlen)
mlen = len;
#ifdef DEBUG
len -= mlen;
#endif
/*
* Force to even boundary.
*/
if ((1 & (long) w) && (mlen > 0)) {
REDUCE;
sum <<= 8;
s_util.c[0] = *(uint8_t *)w;
w = (uint16_t *)((int8_t *)w + 1);
mlen--;
byte_swapped = 1;
}
/*
* Unroll the loop to make overhead from
* branches &c small.
*/
while ((mlen -= 32) >= 0) {
sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
w += 16;
}
mlen += 32;
while ((mlen -= 8) >= 0) {
sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
w += 4;
}
mlen += 8;
if (mlen == 0 && byte_swapped == 0)
goto cont;
REDUCE;
while ((mlen -= 2) >= 0) {
sum += *w++;
}
if (byte_swapped) {
REDUCE;
sum <<= 8;
if (mlen == -1) {
s_util.c[1] = *(uint8_t *)w;
sum += s_util.s;
mlen = 0;
} else
mlen = -1;
} else if (mlen == -1)
s_util.c[0] = *(uint8_t *)w;
cont:
#ifdef DEBUG
if (len) {
DEBUG_ERROR((dfd, "cksum: out of data\n"));
DEBUG_ERROR((dfd, " len = %d\n", len));
}
#endif
if (mlen == -1) {
/* The last mbuf has odd # of bytes. Follow the
standard (the odd byte may be shifted left by 8 bits
or not as determined by endian-ness of the machine) */
s_util.c[1] = 0;
sum += s_util.s;
}
REDUCE;
return (~sum & 0xffff);
}

34
slirp/debug.h Normal file
View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 1995 Danny Gasparovski.
*
* Please read the file COPYRIGHT for the
* terms and conditions of the copyright.
*/
//#define DEBUG 1
#ifdef DEBUG
#define DBG_CALL 0x1
#define DBG_MISC 0x2
#define DBG_ERROR 0x4
#define dfd stderr
extern int slirp_debug;
#define DEBUG_CALL(x) if (slirp_debug & DBG_CALL) { fprintf(dfd, "%s...\n", x); fflush(dfd); }
#define DEBUG_ARG(x, y) if (slirp_debug & DBG_CALL) { fputc(' ', dfd); fprintf(dfd, x, y); fputc('\n', dfd); fflush(dfd); }
#define DEBUG_ARGS(x) if (slirp_debug & DBG_CALL) { fprintf x ; fflush(dfd); }
#define DEBUG_MISC(x) if (slirp_debug & DBG_MISC) { fprintf x ; fflush(dfd); }
#define DEBUG_ERROR(x) if (slirp_debug & DBG_ERROR) {fprintf x ; fflush(dfd); }
#else
#define DEBUG_CALL(x)
#define DEBUG_ARG(x, y)
#define DEBUG_ARGS(x)
#define DEBUG_MISC(x)
#define DEBUG_ERROR(x)
#endif

209
slirp/if.c Normal file
View file

@ -0,0 +1,209 @@
/*
* Copyright (c) 1995 Danny Gasparovski.
*
* Please read the file COPYRIGHT for the
* terms and conditions of the copyright.
*/
#include "slirp.h"
#define ifs_init(ifm) ((ifm)->ifs_next = (ifm)->ifs_prev = (ifm))
static void
ifs_insque(struct mbuf *ifm, struct mbuf *ifmhead)
{
ifm->ifs_next = ifmhead->ifs_next;
ifmhead->ifs_next = ifm;
ifm->ifs_prev = ifmhead;
ifm->ifs_next->ifs_prev = ifm;
}
static void
ifs_remque(struct mbuf *ifm)
{
ifm->ifs_prev->ifs_next = ifm->ifs_next;
ifm->ifs_next->ifs_prev = ifm->ifs_prev;
}
void
if_init(Slirp *slirp)
{
slirp->if_fastq.ifq_next = slirp->if_fastq.ifq_prev = &slirp->if_fastq;
slirp->if_batchq.ifq_next = slirp->if_batchq.ifq_prev = &slirp->if_batchq;
slirp->next_m = &slirp->if_batchq;
}
/*
* if_output: Queue packet into an output queue.
* There are 2 output queue's, if_fastq and if_batchq.
* Each output queue is a doubly linked list of double linked lists
* of mbufs, each list belonging to one "session" (socket). This
* way, we can output packets fairly by sending one packet from each
* session, instead of all the packets from one session, then all packets
* from the next session, etc. Packets on the if_fastq get absolute
* priority, but if one session hogs the link, it gets "downgraded"
* to the batchq until it runs out of packets, then it'll return
* to the fastq (eg. if the user does an ls -alR in a telnet session,
* it'll temporarily get downgraded to the batchq)
*/
void
if_output(struct socket *so, struct mbuf *ifm)
{
Slirp *slirp = ifm->slirp;
struct mbuf *ifq;
int on_fastq = 1;
DEBUG_CALL("if_output");
DEBUG_ARG("so = %lx", (long)so);
DEBUG_ARG("ifm = %lx", (long)ifm);
/*
* First remove the mbuf from m_usedlist,
* since we're gonna use m_next and m_prev ourselves
* XXX Shouldn't need this, gotta change dtom() etc.
*/
if (ifm->m_flags & M_USEDLIST) {
remque(ifm);
ifm->m_flags &= ~M_USEDLIST;
}
/*
* See if there's already a batchq list for this session.
* This can include an interactive session, which should go on fastq,
* but gets too greedy... hence it'll be downgraded from fastq to batchq.
* We mustn't put this packet back on the fastq (or we'll send it out of order)
* XXX add cache here?
*/
for (ifq = slirp->if_batchq.ifq_prev; ifq != &slirp->if_batchq;
ifq = ifq->ifq_prev) {
if (so == ifq->ifq_so) {
/* A match! */
ifm->ifq_so = so;
ifs_insque(ifm, ifq->ifs_prev);
goto diddit;
}
}
/* No match, check which queue to put it on */
if (so && (so->so_iptos & IPTOS_LOWDELAY)) {
ifq = slirp->if_fastq.ifq_prev;
on_fastq = 1;
/*
* Check if this packet is a part of the last
* packet's session
*/
if (ifq->ifq_so == so) {
ifm->ifq_so = so;
ifs_insque(ifm, ifq->ifs_prev);
goto diddit;
}
} else
ifq = slirp->if_batchq.ifq_prev;
/* Create a new doubly linked list for this session */
ifm->ifq_so = so;
ifs_init(ifm);
insque(ifm, ifq);
diddit:
slirp->if_queued++;
if (so) {
/* Update *_queued */
so->so_queued++;
so->so_nqueued++;
/*
* Check if the interactive session should be downgraded to
* the batchq. A session is downgraded if it has queued 6
* packets without pausing, and at least 3 of those packets
* have been sent over the link
* (XXX These are arbitrary numbers, probably not optimal..)
*/
if (on_fastq && ((so->so_nqueued >= 6) &&
(so->so_nqueued - so->so_queued) >= 3)) {
/* Remove from current queue... */
remque(ifm->ifs_next);
/* ...And insert in the new. That'll teach ya! */
insque(ifm->ifs_next, &slirp->if_batchq);
}
}
#ifndef FULL_BOLT
/*
* This prevents us from malloc()ing too many mbufs
*/
if_start(ifm->slirp);
#endif
}
/*
* Send a packet
* We choose a packet based on it's position in the output queues;
* If there are packets on the fastq, they are sent FIFO, before
* everything else. Otherwise we choose the first packet from the
* batchq and send it. the next packet chosen will be from the session
* after this one, then the session after that one, and so on.. So,
* for example, if there are 3 ftp session's fighting for bandwidth,
* one packet will be sent from the first session, then one packet
* from the second session, then one packet from the third, then back
* to the first, etc. etc.
*/
void
if_start(Slirp *slirp)
{
struct mbuf *ifm, *ifqt;
DEBUG_CALL("if_start");
if (slirp->if_queued == 0)
return; /* Nothing to do */
again:
/* check if we can really output */
if (!slirp_can_output(slirp->opaque))
return;
/*
* See which queue to get next packet from
* If there's something in the fastq, select it immediately
*/
if (slirp->if_fastq.ifq_next != &slirp->if_fastq) {
ifm = slirp->if_fastq.ifq_next;
} else {
/* Nothing on fastq, see if next_m is valid */
if (slirp->next_m != &slirp->if_batchq)
ifm = slirp->next_m;
else
ifm = slirp->if_batchq.ifq_next;
/* Set which packet to send on next iteration */
slirp->next_m = ifm->ifq_next;
}
/* Remove it from the queue */
ifqt = ifm->ifq_prev;
remque(ifm);
slirp->if_queued--;
/* If there are more packets for this session, re-queue them */
if (ifm->ifs_next != /* ifm->ifs_prev != */ ifm) {
insque(ifm->ifs_next, ifqt);
ifs_remque(ifm);
}
/* Update so_queued */
if (ifm->ifq_so) {
if (--ifm->ifq_so->so_queued == 0)
/* If there's no more queued, reset nqueued */
ifm->ifq_so->so_nqueued = 0;
}
/* Encapsulate the packet for sending */
if_encap(slirp, (uint8_t *)ifm->m_data, ifm->m_len);
m_free(ifm);
if (slirp->if_queued)
goto again;
}

25
slirp/if.h Normal file
View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 1995 Danny Gasparovski.
*
* Please read the file COPYRIGHT for the
* terms and conditions of the copyright.
*/
#ifndef _IF_H_
#define _IF_H_
#define IF_COMPRESS 0x01 /* We want compression */
#define IF_NOCOMPRESS 0x02 /* Do not do compression */
#define IF_AUTOCOMP 0x04 /* Autodetect (default) */
#define IF_NOCIDCOMP 0x08 /* CID compression */
#define IF_MTU 1500
#define IF_MRU 1500
#define IF_COMP IF_AUTOCOMP /* Flags for compression */
/* 2 for alignment, 14 for ethernet, 40 for TCP/IP */
#define IF_MAXLINKHDR (2 + 14 + 40)
#define ifs_init(ifm) ((ifm)->ifs_next = (ifm)->ifs_prev = (ifm))
#endif

253
slirp/ip.h Normal file
View file

@ -0,0 +1,253 @@
/*
* Copyright (c) 1982, 1986, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ip.h 8.1 (Berkeley) 6/10/93
* ip.h,v 1.3 1994/08/21 05:27:30 paul Exp
*/
#ifndef _IP_H_
#define _IP_H_
#ifdef HOST_WORDS_BIGENDIAN
# ifndef NTOHL
# define NTOHL(d)
# endif
# ifndef NTOHS
# define NTOHS(d)
# endif
# ifndef HTONL
# define HTONL(d)
# endif
# ifndef HTONS
# define HTONS(d)
# endif
#else
# ifndef NTOHL
# define NTOHL(d) ((d) = ntohl((d)))
# endif
# ifndef NTOHS
# define NTOHS(d) ((d) = ntohs((uint16_t)(d)))
# endif
# ifndef HTONL
# define HTONL(d) ((d) = htonl((d)))
# endif
# ifndef HTONS
# define HTONS(d) ((d) = htons((uint16_t)(d)))
# endif
#endif
typedef uint32_t n_long; /* long as received from the net */
/*
* Definitions for internet protocol version 4.
* Per RFC 791, September 1981.
*/
#define IPVERSION 4
/*
* Structure of an internet header, naked of options.
*/
struct ip {
#ifdef HOST_WORDS_BIGENDIAN
u_int ip_v:4, /* version */
ip_hl:4; /* header length */
#else
u_int ip_hl:4, /* header length */
ip_v:4; /* version */
#endif
uint8_t ip_tos; /* type of service */
uint16_t ip_len; /* total length */
uint16_t ip_id; /* identification */
uint16_t ip_off; /* fragment offset field */
#define IP_DF 0x4000 /* don't fragment flag */
#define IP_MF 0x2000 /* more fragments flag */
#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
uint8_t ip_ttl; /* time to live */
uint8_t ip_p; /* protocol */
uint16_t ip_sum; /* checksum */
struct in_addr ip_src,ip_dst; /* source and dest address */
} __attribute__((packed));
#define IP_MAXPACKET 65535 /* maximum packet size */
/*
* Definitions for IP type of service (ip_tos)
*/
#define IPTOS_LOWDELAY 0x10
#define IPTOS_THROUGHPUT 0x08
#define IPTOS_RELIABILITY 0x04
/*
* Definitions for options.
*/
#define IPOPT_COPIED(o) ((o)&0x80)
#define IPOPT_CLASS(o) ((o)&0x60)
#define IPOPT_NUMBER(o) ((o)&0x1f)
#define IPOPT_CONTROL 0x00
#define IPOPT_RESERVED1 0x20
#define IPOPT_DEBMEAS 0x40
#define IPOPT_RESERVED2 0x60
#define IPOPT_EOL 0 /* end of option list */
#define IPOPT_NOP 1 /* no operation */
#define IPOPT_RR 7 /* record packet route */
#define IPOPT_TS 68 /* timestamp */
#define IPOPT_SECURITY 130 /* provide s,c,h,tcc */
#define IPOPT_LSRR 131 /* loose source route */
#define IPOPT_SATID 136 /* satnet id */
#define IPOPT_SSRR 137 /* strict source route */
/*
* Offsets to fields in options other than EOL and NOP.
*/
#define IPOPT_OPTVAL 0 /* option ID */
#define IPOPT_OLEN 1 /* option length */
#define IPOPT_OFFSET 2 /* offset within option */
#define IPOPT_MINOFF 4 /* min value of above */
/*
* Time stamp option structure.
*/
struct ip_timestamp {
uint8_t ipt_code; /* IPOPT_TS */
uint8_t ipt_len; /* size of structure (variable) */
uint8_t ipt_ptr; /* index of current entry */
#ifdef HOST_WORDS_BIGENDIAN
u_int ipt_oflw:4, /* overflow counter */
ipt_flg:4; /* flags, see below */
#else
u_int ipt_flg:4, /* flags, see below */
ipt_oflw:4; /* overflow counter */
#endif
union ipt_timestamp {
n_long ipt_time[1];
struct ipt_ta {
struct in_addr ipt_addr;
n_long ipt_time;
} ipt_ta[1];
} ipt_timestamp;
} __attribute__((packed));
/* flag bits for ipt_flg */
#define IPOPT_TS_TSONLY 0 /* timestamps only */
#define IPOPT_TS_TSANDADDR 1 /* timestamps and addresses */
#define IPOPT_TS_PRESPEC 3 /* specified modules only */
/* bits for security (not byte swapped) */
#define IPOPT_SECUR_UNCLASS 0x0000
#define IPOPT_SECUR_CONFID 0xf135
#define IPOPT_SECUR_EFTO 0x789a
#define IPOPT_SECUR_MMMM 0xbc4d
#define IPOPT_SECUR_RESTR 0xaf13
#define IPOPT_SECUR_SECRET 0xd788
#define IPOPT_SECUR_TOPSECRET 0x6bc5
/*
* Internet implementation parameters.
*/
#define MAXTTL 255 /* maximum time to live (seconds) */
#define IPDEFTTL 64 /* default ttl, from RFC 1340 */
#define IPFRAGTTL 60 /* time to live for frags, slowhz */
#define IPTTLDEC 1 /* subtracted when forwarding */
#define IP_MSS 576 /* default maximum segment size */
#if SIZEOF_CHAR_P == 4
struct mbuf_ptr {
struct mbuf *mptr;
uint32_t dummy;
} __attribute__((packed));
#else
struct mbuf_ptr {
struct mbuf *mptr;
} __attribute__((packed));
#endif
struct qlink {
void *next, *prev;
};
/*
* Overlay for ip header used by other protocols (tcp, udp).
*/
struct ipovly {
struct mbuf_ptr ih_mbuf; /* backpointer to mbuf */
uint8_t ih_x1; /* (unused) */
uint8_t ih_pr; /* protocol */
uint16_t ih_len; /* protocol length */
struct in_addr ih_src; /* source internet address */
struct in_addr ih_dst; /* destination internet address */
} __attribute__((packed));
/*
* Ip reassembly queue structure. Each fragment
* being reassembled is attached to one of these structures.
* They are timed out after ipq_ttl drops to 0, and may also
* be reclaimed if memory becomes tight.
* size 28 bytes
*/
struct ipq {
struct qlink frag_link; /* to ip headers of fragments */
struct qlink ip_link; /* to other reass headers */
uint8_t ipq_ttl; /* time for reass q to live */
uint8_t ipq_p; /* protocol of this fragment */
uint16_t ipq_id; /* sequence id for reassembly */
struct in_addr ipq_src,ipq_dst;
} __attribute__((packed));
/*
* Ip header, when holding a fragment.
*
* Note: ipf_link must be at same offset as frag_link above
*/
struct ipasfrag {
struct qlink ipf_link;
struct ip ipf_ip;
} __attribute__((packed));
#define ipf_off ipf_ip.ip_off
#define ipf_tos ipf_ip.ip_tos
#define ipf_len ipf_ip.ip_len
#define ipf_next ipf_link.next
#define ipf_prev ipf_link.prev
/*
* Structure stored in mbuf in inpcb.ip_options
* and passed to ip_output when ip options are in use.
* The actual length of the options (including ipopt_dst)
* is in m_len.
*/
#define MAX_IPOPTLEN 40
struct ipoption {
struct in_addr ipopt_dst; /* first-hop dst if source routed */
int8_t ipopt_list[MAX_IPOPTLEN]; /* options proper */
} __attribute__((packed));
#endif

351
slirp/ip_icmp.c Normal file
View file

@ -0,0 +1,351 @@
/*
* Copyright (c) 1982, 1986, 1988, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ip_icmp.c 8.2 (Berkeley) 1/4/94
* ip_icmp.c,v 1.7 1995/05/30 08:09:42 rgrimes Exp
*/
#include "slirp.h"
#include "ip_icmp.h"
/* The message sent when emulating PING */
/* Be nice and tell them it's just a pseudo-ping packet */
static const char icmp_ping_msg[] = "This is a pseudo-PING packet used by Slirp to emulate ICMP ECHO-REQUEST packets.\n";
/* list of actions for icmp_error() on RX of an icmp message */
static const int icmp_flush[19] = {
/* ECHO REPLY (0) */ 0,
1,
1,
/* DEST UNREACH (3) */ 1,
/* SOURCE QUENCH (4)*/ 1,
/* REDIRECT (5) */ 1,
1,
1,
/* ECHO (8) */ 0,
/* ROUTERADVERT (9) */ 1,
/* ROUTERSOLICIT (10) */ 1,
/* TIME EXCEEDED (11) */ 1,
/* PARAMETER PROBLEM (12) */ 1,
/* TIMESTAMP (13) */ 0,
/* TIMESTAMP REPLY (14) */ 0,
/* INFO (15) */ 0,
/* INFO REPLY (16) */ 0,
/* ADDR MASK (17) */ 0,
/* ADDR MASK REPLY (18) */ 0
};
/*
* Process a received ICMP message.
*/
void
icmp_input(struct mbuf *m, int hlen)
{
register struct icmp *icp;
register struct ip *ip=mtod(m, struct ip *);
int icmplen=ip->ip_len;
Slirp *slirp = m->slirp;
DEBUG_CALL("icmp_input");
DEBUG_ARG("m = %lx", (long )m);
DEBUG_ARG("m_len = %d", m->m_len);
/*
* Locate icmp structure in mbuf, and check
* that its not corrupted and of at least minimum length.
*/
if (icmplen < ICMP_MINLEN) { /* min 8 bytes payload */
freeit:
m_freem(m);
goto end_error;
}
m->m_len -= hlen;
m->m_data += hlen;
icp = mtod(m, struct icmp *);
if (cksum(m, icmplen)) {
goto freeit;
}
m->m_len += hlen;
m->m_data -= hlen;
DEBUG_ARG("icmp_type = %d", icp->icmp_type);
switch (icp->icmp_type) {
case ICMP_ECHO:
icp->icmp_type = ICMP_ECHOREPLY;
ip->ip_len += hlen; /* since ip_input subtracts this */
if (ip->ip_dst.s_addr == slirp->vhost_addr.s_addr) {
icmp_reflect(m);
} else {
struct socket *so;
struct sockaddr_in addr;
if ((so = socreate(slirp)) == NULL) goto freeit;
if(udp_attach(so) == -1) {
DEBUG_MISC((dfd,"icmp_input udp_attach errno = %d-%s\n",
errno,strerror(errno)));
sofree(so);
m_free(m);
goto end_error;
}
so->so_m = m;
so->so_faddr = ip->ip_dst;
so->so_fport = htons(7);
so->so_laddr = ip->ip_src;
so->so_lport = htons(9);
so->so_iptos = ip->ip_tos;
so->so_type = IPPROTO_ICMP;
so->so_state = SS_ISFCONNECTED;
/* Send the packet */
addr.sin_family = AF_INET;
if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
slirp->vnetwork_addr.s_addr) {
/* It's an alias */
if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) {
if (get_dns_addr(&addr.sin_addr) < 0)
addr.sin_addr = loopback_addr;
} else {
addr.sin_addr = loopback_addr;
}
} else {
addr.sin_addr = so->so_faddr;
}
addr.sin_port = so->so_fport;
if(sendto(so->s, icmp_ping_msg, strlen(icmp_ping_msg), 0,
(struct sockaddr *)&addr, sizeof(addr)) == -1) {
DEBUG_MISC((dfd,"icmp_input udp sendto tx errno = %d-%s\n",
errno,strerror(errno)));
icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno));
udp_detach(so);
}
} /* if ip->ip_dst.s_addr == alias_addr.s_addr */
break;
case ICMP_UNREACH:
/* XXX? report error? close socket? */
case ICMP_TIMXCEED:
case ICMP_PARAMPROB:
case ICMP_SOURCEQUENCH:
case ICMP_TSTAMP:
case ICMP_MASKREQ:
case ICMP_REDIRECT:
m_freem(m);
break;
default:
m_freem(m);
} /* swith */
end_error:
/* m is m_free()'d xor put in a socket xor or given to ip_send */
return;
}
/*
* Send an ICMP message in response to a situation
*
* RFC 1122: 3.2.2 MUST send at least the IP header and 8 bytes of header. MAY send more (we do).
* MUST NOT change this header information.
* MUST NOT reply to a multicast/broadcast IP address.
* MUST NOT reply to a multicast/broadcast MAC address.
* MUST reply to only the first fragment.
*/
/*
* Send ICMP_UNREACH back to the source regarding msrc.
* mbuf *msrc is used as a template, but is NOT m_free()'d.
* It is reported as the bad ip packet. The header should
* be fully correct and in host byte order.
* ICMP fragmentation is illegal. All machines must accept 576 bytes in one
* packet. The maximum payload is 576-20(ip hdr)-8(icmp hdr)=548
*/
#define ICMP_MAXDATALEN (IP_MSS-28)
void
icmp_error(struct mbuf *msrc, u_char type, u_char code, int minsize,
const char *message)
{
unsigned hlen, shlen, s_ip_len;
register struct ip *ip;
register struct icmp *icp;
register struct mbuf *m;
DEBUG_CALL("icmp_error");
DEBUG_ARG("msrc = %lx", (long )msrc);
DEBUG_ARG("msrc_len = %d", msrc->m_len);
if(type!=ICMP_UNREACH && type!=ICMP_TIMXCEED) goto end_error;
/* check msrc */
if(!msrc) goto end_error;
ip = mtod(msrc, struct ip *);
#ifdef DEBUG
{ char bufa[20], bufb[20];
strcpy(bufa, inet_ntoa(ip->ip_src));
strcpy(bufb, inet_ntoa(ip->ip_dst));
DEBUG_MISC((dfd, " %.16s to %.16s\n", bufa, bufb));
}
#endif
if(ip->ip_off & IP_OFFMASK) goto end_error; /* Only reply to fragment 0 */
shlen=ip->ip_hl << 2;
s_ip_len=ip->ip_len;
if(ip->ip_p == IPPROTO_ICMP) {
icp = (struct icmp *)((char *)ip + shlen);
/*
* Assume any unknown ICMP type is an error. This isn't
* specified by the RFC, but think about it..
*/
if(icp->icmp_type>18 || icmp_flush[icp->icmp_type]) goto end_error;
}
/* make a copy */
m = m_get(msrc->slirp);
if (!m) {
goto end_error;
}
{ int new_m_size;
new_m_size=sizeof(struct ip )+ICMP_MINLEN+msrc->m_len+ICMP_MAXDATALEN;
if(new_m_size>m->m_size) m_inc(m, new_m_size);
}
memcpy(m->m_data, msrc->m_data, msrc->m_len);
m->m_len = msrc->m_len; /* copy msrc to m */
/* make the header of the reply packet */
ip = mtod(m, struct ip *);
hlen= sizeof(struct ip ); /* no options in reply */
/* fill in icmp */
m->m_data += hlen;
m->m_len -= hlen;
icp = mtod(m, struct icmp *);
if(minsize) s_ip_len=shlen+ICMP_MINLEN; /* return header+8b only */
else if(s_ip_len>ICMP_MAXDATALEN) /* maximum size */
s_ip_len=ICMP_MAXDATALEN;
m->m_len=ICMP_MINLEN+s_ip_len; /* 8 bytes ICMP header */
/* min. size = 8+sizeof(struct ip)+8 */
icp->icmp_type = type;
icp->icmp_code = code;
icp->icmp_id = 0;
icp->icmp_seq = 0;
memcpy(&icp->icmp_ip, msrc->m_data, s_ip_len); /* report the ip packet */
HTONS(icp->icmp_ip.ip_len);
HTONS(icp->icmp_ip.ip_id);
HTONS(icp->icmp_ip.ip_off);
#ifdef DEBUG
if(message) { /* DEBUG : append message to ICMP packet */
int message_len;
char *cpnt;
message_len=strlen(message);
if(message_len>ICMP_MAXDATALEN) message_len=ICMP_MAXDATALEN;
cpnt=(char *)m->m_data+m->m_len;
memcpy(cpnt, message, message_len);
m->m_len+=message_len;
}
#endif
icp->icmp_cksum = 0;
icp->icmp_cksum = cksum(m, m->m_len);
m->m_data -= hlen;
m->m_len += hlen;
/* fill in ip */
ip->ip_hl = hlen >> 2;
ip->ip_len = m->m_len;
ip->ip_tos=((ip->ip_tos & 0x1E) | 0xC0); /* high priority for errors */
ip->ip_ttl = MAXTTL;
ip->ip_p = IPPROTO_ICMP;
ip->ip_dst = ip->ip_src; /* ip adresses */
ip->ip_src = m->slirp->vhost_addr;
(void ) ip_output((struct socket *)NULL, m);
end_error:
return;
}
#undef ICMP_MAXDATALEN
/*
* Reflect the ip packet back to the source
*/
void
icmp_reflect(struct mbuf *m)
{
register struct ip *ip = mtod(m, struct ip *);
int hlen = ip->ip_hl << 2;
int optlen = hlen - sizeof(struct ip );
register struct icmp *icp;
/*
* Send an icmp packet back to the ip level,
* after supplying a checksum.
*/
m->m_data += hlen;
m->m_len -= hlen;
icp = mtod(m, struct icmp *);
icp->icmp_cksum = 0;
icp->icmp_cksum = cksum(m, ip->ip_len - hlen);
m->m_data -= hlen;
m->m_len += hlen;
/* fill in ip */
if (optlen > 0) {
/*
* Strip out original options by copying rest of first
* mbuf's data back, and adjust the IP length.
*/
memmove((caddr_t)(ip + 1), (caddr_t)ip + hlen,
(unsigned )(m->m_len - hlen));
hlen -= optlen;
ip->ip_hl = hlen >> 2;
ip->ip_len -= optlen;
m->m_len -= optlen;
}
ip->ip_ttl = MAXTTL;
{ /* swap */
struct in_addr icmp_dst;
icmp_dst = ip->ip_dst;
ip->ip_dst = ip->ip_src;
ip->ip_src = icmp_dst;
}
(void ) ip_output((struct socket *)NULL, m);
}

161
slirp/ip_icmp.h Normal file
View file

@ -0,0 +1,161 @@
/*
* Copyright (c) 1982, 1986, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ip_icmp.h 8.1 (Berkeley) 6/10/93
* ip_icmp.h,v 1.4 1995/05/30 08:09:43 rgrimes Exp
*/
#ifndef _NETINET_IP_ICMP_H_
#define _NETINET_IP_ICMP_H_
/*
* Interface Control Message Protocol Definitions.
* Per RFC 792, September 1981.
*/
typedef uint32_t n_time;
/*
* Structure of an icmp header.
*/
struct icmp {
u_char icmp_type; /* type of message, see below */
u_char icmp_code; /* type sub code */
u_short icmp_cksum; /* ones complement cksum of struct */
union {
u_char ih_pptr; /* ICMP_PARAMPROB */
struct in_addr ih_gwaddr; /* ICMP_REDIRECT */
struct ih_idseq {
u_short icd_id;
u_short icd_seq;
} ih_idseq;
int ih_void;
/* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */
struct ih_pmtu {
u_short ipm_void;
u_short ipm_nextmtu;
} ih_pmtu;
} icmp_hun;
#define icmp_pptr icmp_hun.ih_pptr
#define icmp_gwaddr icmp_hun.ih_gwaddr
#define icmp_id icmp_hun.ih_idseq.icd_id
#define icmp_seq icmp_hun.ih_idseq.icd_seq
#define icmp_void icmp_hun.ih_void
#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void
#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu
union {
struct id_ts {
n_time its_otime;
n_time its_rtime;
n_time its_ttime;
} id_ts;
struct id_ip {
struct ip idi_ip;
/* options and then 64 bits of data */
} id_ip;
uint32_t id_mask;
char id_data[1];
} icmp_dun;
#define icmp_otime icmp_dun.id_ts.its_otime
#define icmp_rtime icmp_dun.id_ts.its_rtime
#define icmp_ttime icmp_dun.id_ts.its_ttime
#define icmp_ip icmp_dun.id_ip.idi_ip
#define icmp_mask icmp_dun.id_mask
#define icmp_data icmp_dun.id_data
};
/*
* Lower bounds on packet lengths for various types.
* For the error advice packets must first insure that the
* packet is large enought to contain the returned ip header.
* Only then can we do the check to see if 64 bits of packet
* data have been returned, since we need to check the returned
* ip header length.
*/
#define ICMP_MINLEN 8 /* abs minimum */
#define ICMP_TSLEN (8 + 3 * sizeof (n_time)) /* timestamp */
#define ICMP_MASKLEN 12 /* address mask */
#define ICMP_ADVLENMIN (8 + sizeof (struct ip) + 8) /* min */
#define ICMP_ADVLEN(p) (8 + ((p)->icmp_ip.ip_hl << 2) + 8)
/* N.B.: must separately check that ip_hl >= 5 */
/*
* Definition of type and code field values.
*/
#define ICMP_ECHOREPLY 0 /* echo reply */
#define ICMP_UNREACH 3 /* dest unreachable, codes: */
#define ICMP_UNREACH_NET 0 /* bad net */
#define ICMP_UNREACH_HOST 1 /* bad host */
#define ICMP_UNREACH_PROTOCOL 2 /* bad protocol */
#define ICMP_UNREACH_PORT 3 /* bad port */
#define ICMP_UNREACH_NEEDFRAG 4 /* IP_DF caused drop */
#define ICMP_UNREACH_SRCFAIL 5 /* src route failed */
#define ICMP_UNREACH_NET_UNKNOWN 6 /* unknown net */
#define ICMP_UNREACH_HOST_UNKNOWN 7 /* unknown host */
#define ICMP_UNREACH_ISOLATED 8 /* src host isolated */
#define ICMP_UNREACH_NET_PROHIB 9 /* prohibited access */
#define ICMP_UNREACH_HOST_PROHIB 10 /* ditto */
#define ICMP_UNREACH_TOSNET 11 /* bad tos for net */
#define ICMP_UNREACH_TOSHOST 12 /* bad tos for host */
#define ICMP_SOURCEQUENCH 4 /* packet lost, slow down */
#define ICMP_REDIRECT 5 /* shorter route, codes: */
#define ICMP_REDIRECT_NET 0 /* for network */
#define ICMP_REDIRECT_HOST 1 /* for host */
#define ICMP_REDIRECT_TOSNET 2 /* for tos and net */
#define ICMP_REDIRECT_TOSHOST 3 /* for tos and host */
#define ICMP_ECHO 8 /* echo service */
#define ICMP_ROUTERADVERT 9 /* router advertisement */
#define ICMP_ROUTERSOLICIT 10 /* router solicitation */
#define ICMP_TIMXCEED 11 /* time exceeded, code: */
#define ICMP_TIMXCEED_INTRANS 0 /* ttl==0 in transit */
#define ICMP_TIMXCEED_REASS 1 /* ttl==0 in reass */
#define ICMP_PARAMPROB 12 /* ip header bad */
#define ICMP_PARAMPROB_OPTABSENT 1 /* req. opt. absent */
#define ICMP_TSTAMP 13 /* timestamp request */
#define ICMP_TSTAMPREPLY 14 /* timestamp reply */
#define ICMP_IREQ 15 /* information request */
#define ICMP_IREQREPLY 16 /* information reply */
#define ICMP_MASKREQ 17 /* address mask request */
#define ICMP_MASKREPLY 18 /* address mask reply */
#define ICMP_MAXTYPE 18
#define ICMP_INFOTYPE(type) \
((type) == ICMP_ECHOREPLY || (type) == ICMP_ECHO || \
(type) == ICMP_ROUTERADVERT || (type) == ICMP_ROUTERSOLICIT || \
(type) == ICMP_TSTAMP || (type) == ICMP_TSTAMPREPLY || \
(type) == ICMP_IREQ || (type) == ICMP_IREQREPLY || \
(type) == ICMP_MASKREQ || (type) == ICMP_MASKREPLY)
void icmp_input(struct mbuf *, int);
void icmp_error(struct mbuf *msrc, u_char type, u_char code, int minsize,
const char *message);
void icmp_reflect(struct mbuf *);
#endif

685
slirp/ip_input.c Normal file
View file

@ -0,0 +1,685 @@
/*
* Copyright (c) 1982, 1986, 1988, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ip_input.c 8.2 (Berkeley) 1/4/94
* ip_input.c,v 1.11 1994/11/16 10:17:08 jkh Exp
*/
/*
* Changes and additions relating to SLiRP are
* Copyright (c) 1995 Danny Gasparovski.
*
* Please read the file COPYRIGHT for the
* terms and conditions of the copyright.
*/
#include "slirp.h"
#include "ip_icmp.h"
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
static struct ip *ip_reass(Slirp *slirp, struct ip *ip, struct ipq *fp);
static void ip_freef(Slirp *slirp, struct ipq *fp);
static void ip_enq(register struct ipasfrag *p,
register struct ipasfrag *prev);
static void ip_deq(register struct ipasfrag *p);
/*
* IP initialization: fill in IP protocol switch table.
* All protocols not implemented in kernel go to raw IP protocol handler.
*/
void
ip_init(Slirp *slirp)
{
slirp->ipq.ip_link.next = slirp->ipq.ip_link.prev = &slirp->ipq.ip_link;
udp_init(slirp);
tcp_init(slirp);
}
/*
* Ip input routine. Checksum and byte swap header. If fragmented
* try to reassemble. Process options. Pass to next level.
*/
void
ip_input(struct mbuf *m)
{
Slirp *slirp = m->slirp;
register struct ip *ip;
int hlen;
DEBUG_CALL("ip_input");
DEBUG_ARG("m = %lx", (long)m);
DEBUG_ARG("m_len = %d", m->m_len);
if (m->m_len < sizeof (struct ip)) {
return;
}
ip = mtod(m, struct ip *);
if (ip->ip_v != IPVERSION) {
goto bad;
}
hlen = ip->ip_hl << 2;
if (hlen<sizeof(struct ip ) || hlen>m->m_len) {/* min header length */
goto bad; /* or packet too short */
}
/* keep ip header intact for ICMP reply
* ip->ip_sum = cksum(m, hlen);
* if (ip->ip_sum) {
*/
if(cksum(m,hlen)) {
goto bad;
}
/*
* Convert fields to host representation.
*/
NTOHS(ip->ip_len);
if (ip->ip_len < hlen) {
goto bad;
}
NTOHS(ip->ip_id);
NTOHS(ip->ip_off);
/*
* Check that the amount of data in the buffers
* is as at least much as the IP header would have us expect.
* Trim mbufs if longer than we expect.
* Drop packet if shorter than we expect.
*/
if (m->m_len < ip->ip_len) {
goto bad;
}
if (slirp->restricted) {
if ((ip->ip_dst.s_addr & slirp->vnetwork_mask.s_addr) ==
slirp->vnetwork_addr.s_addr) {
if (ip->ip_dst.s_addr == 0xffffffff && ip->ip_p != IPPROTO_UDP)
goto bad;
} else {
uint32_t inv_mask = ~slirp->vnetwork_mask.s_addr;
struct ex_list *ex_ptr;
if ((ip->ip_dst.s_addr & inv_mask) == inv_mask) {
goto bad;
}
for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next)
if (ex_ptr->ex_addr.s_addr == ip->ip_dst.s_addr)
break;
if (!ex_ptr)
goto bad;
}
}
/* Should drop packet if mbuf too long? hmmm... */
if (m->m_len > ip->ip_len)
m_adj(m, ip->ip_len - m->m_len);
/* check ip_ttl for a correct ICMP reply */
if(ip->ip_ttl==0) {
icmp_error(m, ICMP_TIMXCEED,ICMP_TIMXCEED_INTRANS, 0,"ttl");
goto bad;
}
/*
* If offset or IP_MF are set, must reassemble.
* Otherwise, nothing need be done.
* (We could look in the reassembly queue to see
* if the packet was previously fragmented,
* but it's not worth the time; just let them time out.)
*
* XXX This should fail, don't fragment yet
*/
if (ip->ip_off &~ IP_DF) {
register struct ipq *fp;
struct qlink *l;
/*
* Look for queue of fragments
* of this datagram.
*/
for (l = slirp->ipq.ip_link.next; l != &slirp->ipq.ip_link;
l = l->next) {
fp = container_of(l, struct ipq, ip_link);
if (ip->ip_id == fp->ipq_id &&
ip->ip_src.s_addr == fp->ipq_src.s_addr &&
ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&
ip->ip_p == fp->ipq_p)
goto found;
}
fp = NULL;
found:
/*
* Adjust ip_len to not reflect header,
* set ip_mff if more fragments are expected,
* convert offset of this to bytes.
*/
ip->ip_len -= hlen;
if (ip->ip_off & IP_MF)
ip->ip_tos |= 1;
else
ip->ip_tos &= ~1;
ip->ip_off <<= 3;
/*
* If datagram marked as having more fragments
* or if this is not the first fragment,
* attempt reassembly; if it succeeds, proceed.
*/
if (ip->ip_tos & 1 || ip->ip_off) {
ip = ip_reass(slirp, ip, fp);
if (ip == NULL)
return;
m = dtom(slirp, ip);
} else
if (fp)
ip_freef(slirp, fp);
} else
ip->ip_len -= hlen;
/*
* Switch out to protocol's input routine.
*/
switch (ip->ip_p) {
case IPPROTO_TCP:
tcp_input(m, hlen, (struct socket *)NULL);
break;
case IPPROTO_UDP:
udp_input(m, hlen);
break;
case IPPROTO_ICMP:
icmp_input(m, hlen);
break;
default:
m_free(m);
}
return;
bad:
m_freem(m);
return;
}
#define iptofrag(P) ((struct ipasfrag *)(((char*)(P)) - sizeof(struct qlink)))
#define fragtoip(P) ((struct ip*)(((char*)(P)) + sizeof(struct qlink)))
/*
* Take incoming datagram fragment and try to
* reassemble it into whole datagram. If a chain for
* reassembly of this datagram already exists, then it
* is given as fp; otherwise have to make a chain.
*/
static struct ip *
ip_reass(Slirp *slirp, struct ip *ip, struct ipq *fp)
{
register struct mbuf *m = dtom(slirp, ip);
register struct ipasfrag *q;
int hlen = ip->ip_hl << 2;
int i, next;
DEBUG_CALL("ip_reass");
DEBUG_ARG("ip = %lx", (long)ip);
DEBUG_ARG("fp = %lx", (long)fp);
DEBUG_ARG("m = %lx", (long)m);
/*
* Presence of header sizes in mbufs
* would confuse code below.
* Fragment m_data is concatenated.
*/
m->m_data += hlen;
m->m_len -= hlen;
/*
* If first fragment to arrive, create a reassembly queue.
*/
if (fp == NULL) {
struct mbuf *t = m_get(slirp);
if (t == NULL) {
goto dropfrag;
}
fp = mtod(t, struct ipq *);
insque(&fp->ip_link, &slirp->ipq.ip_link);
fp->ipq_ttl = IPFRAGTTL;
fp->ipq_p = ip->ip_p;
fp->ipq_id = ip->ip_id;
fp->frag_link.next = fp->frag_link.prev = &fp->frag_link;
fp->ipq_src = ip->ip_src;
fp->ipq_dst = ip->ip_dst;
q = (struct ipasfrag *)fp;
goto insert;
}
/*
* Find a segment which begins after this one does.
*/
for (q = fp->frag_link.next; q != (struct ipasfrag *)&fp->frag_link;
q = q->ipf_next)
if (q->ipf_off > ip->ip_off)
break;
/*
* If there is a preceding segment, it may provide some of
* our data already. If so, drop the data from the incoming
* segment. If it provides all of our data, drop us.
*/
if (q->ipf_prev != &fp->frag_link) {
struct ipasfrag *pq = q->ipf_prev;
i = pq->ipf_off + pq->ipf_len - ip->ip_off;
if (i > 0) {
if (i >= ip->ip_len)
goto dropfrag;
m_adj(dtom(slirp, ip), i);
ip->ip_off += i;
ip->ip_len -= i;
}
}
/*
* While we overlap succeeding segments trim them or,
* if they are completely covered, dequeue them.
*/
while (q != (struct ipasfrag*)&fp->frag_link &&
ip->ip_off + ip->ip_len > q->ipf_off) {
i = (ip->ip_off + ip->ip_len) - q->ipf_off;
if (i < q->ipf_len) {
q->ipf_len -= i;
q->ipf_off += i;
m_adj(dtom(slirp, q), i);
break;
}
q = q->ipf_next;
m_freem(dtom(slirp, q->ipf_prev));
ip_deq(q->ipf_prev);
}
insert:
/*
* Stick new segment in its place;
* check for complete reassembly.
*/
ip_enq(iptofrag(ip), q->ipf_prev);
next = 0;
for (q = fp->frag_link.next; q != (struct ipasfrag*)&fp->frag_link;
q = q->ipf_next) {
if (q->ipf_off != next)
return NULL;
next += q->ipf_len;
}
if (((struct ipasfrag *)(q->ipf_prev))->ipf_tos & 1)
return NULL;
/*
* Reassembly is complete; concatenate fragments.
*/
q = fp->frag_link.next;
m = dtom(slirp, q);
q = (struct ipasfrag *) q->ipf_next;
while (q != (struct ipasfrag*)&fp->frag_link) {
struct mbuf *t = dtom(slirp, q);
q = (struct ipasfrag *) q->ipf_next;
m_cat(m, t);
}
/*
* Create header for new ip packet by
* modifying header of first packet;
* dequeue and discard fragment reassembly header.
* Make header visible.
*/
q = fp->frag_link.next;
/*
* If the fragments concatenated to an mbuf that's
* bigger than the total size of the fragment, then and
* m_ext buffer was alloced. But fp->ipq_next points to
* the old buffer (in the mbuf), so we must point ip
* into the new buffer.
*/
if (m->m_flags & M_EXT) {
int delta = (char *)q - m->m_dat;
q = (struct ipasfrag *)(m->m_ext + delta);
}
ip = fragtoip(q);
ip->ip_len = next;
ip->ip_tos &= ~1;
ip->ip_src = fp->ipq_src;
ip->ip_dst = fp->ipq_dst;
remque(&fp->ip_link);
(void) m_free(dtom(slirp, fp));
m->m_len += (ip->ip_hl << 2);
m->m_data -= (ip->ip_hl << 2);
return ip;
dropfrag:
m_freem(m);
return NULL;
}
/*
* Free a fragment reassembly header and all
* associated datagrams.
*/
static void
ip_freef(Slirp *slirp, struct ipq *fp)
{
register struct ipasfrag *q, *p;
for (q = fp->frag_link.next; q != (struct ipasfrag*)&fp->frag_link; q = p) {
p = q->ipf_next;
ip_deq(q);
m_freem(dtom(slirp, q));
}
remque(&fp->ip_link);
(void) m_free(dtom(slirp, fp));
}
/*
* Put an ip fragment on a reassembly chain.
* Like insque, but pointers in middle of structure.
*/
static void
ip_enq(register struct ipasfrag *p, register struct ipasfrag *prev)
{
DEBUG_CALL("ip_enq");
DEBUG_ARG("prev = %lx", (long)prev);
p->ipf_prev = prev;
p->ipf_next = prev->ipf_next;
((struct ipasfrag *)(prev->ipf_next))->ipf_prev = p;
prev->ipf_next = p;
}
/*
* To ip_enq as remque is to insque.
*/
static void
ip_deq(register struct ipasfrag *p)
{
((struct ipasfrag *)(p->ipf_prev))->ipf_next = p->ipf_next;
((struct ipasfrag *)(p->ipf_next))->ipf_prev = p->ipf_prev;
}
/*
* IP timer processing;
* if a timer expires on a reassembly
* queue, discard it.
*/
void
ip_slowtimo(Slirp *slirp)
{
struct qlink *l;
DEBUG_CALL("ip_slowtimo");
l = slirp->ipq.ip_link.next;
if (l == NULL)
return;
while (l != &slirp->ipq.ip_link) {
struct ipq *fp = container_of(l, struct ipq, ip_link);
l = l->next;
if (--fp->ipq_ttl == 0) {
ip_freef(slirp, fp);
}
}
}
/*
* Do option processing on a datagram,
* possibly discarding it if bad options are encountered,
* or forwarding it if source-routed.
* Returns 1 if packet has been forwarded/freed,
* 0 if the packet should be processed further.
*/
#ifdef notdef
int
ip_dooptions(m)
struct mbuf *m;
{
register struct ip *ip = mtod(m, struct ip *);
register u_char *cp;
register struct ip_timestamp *ipt;
register struct in_ifaddr *ia;
int opt, optlen, cnt, off, code, type, forward = 0;
struct in_addr *sin, dst;
typedef uint32_t n_time;
n_time ntime;
dst = ip->ip_dst;
cp = (u_char *)(ip + 1);
cnt = (ip->ip_hl << 2) - sizeof (struct ip);
for (; cnt > 0; cnt -= optlen, cp += optlen) {
opt = cp[IPOPT_OPTVAL];
if (opt == IPOPT_EOL)
break;
if (opt == IPOPT_NOP)
optlen = 1;
else {
optlen = cp[IPOPT_OLEN];
if (optlen <= 0 || optlen > cnt) {
code = &cp[IPOPT_OLEN] - (u_char *)ip;
goto bad;
}
}
switch (opt) {
default:
break;
/*
* Source routing with record.
* Find interface with current destination address.
* If none on this machine then drop if strictly routed,
* or do nothing if loosely routed.
* Record interface address and bring up next address
* component. If strictly routed make sure next
* address is on directly accessible net.
*/
case IPOPT_LSRR:
case IPOPT_SSRR:
if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
code = &cp[IPOPT_OFFSET] - (u_char *)ip;
goto bad;
}
ipaddr.sin_addr = ip->ip_dst;
ia = (struct in_ifaddr *)
ifa_ifwithaddr((struct sockaddr *)&ipaddr);
if (ia == 0) {
if (opt == IPOPT_SSRR) {
type = ICMP_UNREACH;
code = ICMP_UNREACH_SRCFAIL;
goto bad;
}
/*
* Loose routing, and not at next destination
* yet; nothing to do except forward.
*/
break;
}
off--; / * 0 origin * /
if (off > optlen - sizeof(struct in_addr)) {
/*
* End of source route. Should be for us.
*/
save_rte(cp, ip->ip_src);
break;
}
/*
* locate outgoing interface
*/
bcopy((caddr_t)(cp + off), (caddr_t)&ipaddr.sin_addr,
sizeof(ipaddr.sin_addr));
if (opt == IPOPT_SSRR) {
#define INA struct in_ifaddr *
#define SA struct sockaddr *
if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == 0)
ia = (INA)ifa_ifwithnet((SA)&ipaddr);
} else
ia = ip_rtaddr(ipaddr.sin_addr);
if (ia == 0) {
type = ICMP_UNREACH;
code = ICMP_UNREACH_SRCFAIL;
goto bad;
}
ip->ip_dst = ipaddr.sin_addr;
bcopy((caddr_t)&(IA_SIN(ia)->sin_addr),
(caddr_t)(cp + off), sizeof(struct in_addr));
cp[IPOPT_OFFSET] += sizeof(struct in_addr);
/*
* Let ip_intr's mcast routing check handle mcast pkts
*/
forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr));
break;
case IPOPT_RR:
if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
code = &cp[IPOPT_OFFSET] - (u_char *)ip;
goto bad;
}
/*
* If no space remains, ignore.
*/
off--; * 0 origin *
if (off > optlen - sizeof(struct in_addr))
break;
bcopy((caddr_t)(&ip->ip_dst), (caddr_t)&ipaddr.sin_addr,
sizeof(ipaddr.sin_addr));
/*
* locate outgoing interface; if we're the destination,
* use the incoming interface (should be same).
*/
if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == 0 &&
(ia = ip_rtaddr(ipaddr.sin_addr)) == 0) {
type = ICMP_UNREACH;
code = ICMP_UNREACH_HOST;
goto bad;
}
bcopy((caddr_t)&(IA_SIN(ia)->sin_addr),
(caddr_t)(cp + off), sizeof(struct in_addr));
cp[IPOPT_OFFSET] += sizeof(struct in_addr);
break;
case IPOPT_TS:
code = cp - (u_char *)ip;
ipt = (struct ip_timestamp *)cp;
if (ipt->ipt_len < 5)
goto bad;
if (ipt->ipt_ptr > ipt->ipt_len - sizeof (int32_t)) {
if (++ipt->ipt_oflw == 0)
goto bad;
break;
}
sin = (struct in_addr *)(cp + ipt->ipt_ptr - 1);
switch (ipt->ipt_flg) {
case IPOPT_TS_TSONLY:
break;
case IPOPT_TS_TSANDADDR:
if (ipt->ipt_ptr + sizeof(n_time) +
sizeof(struct in_addr) > ipt->ipt_len)
goto bad;
ipaddr.sin_addr = dst;
ia = (INA)ifaof_ i f p foraddr((SA)&ipaddr,
m->m_pkthdr.rcvif);
if (ia == 0)
continue;
bcopy((caddr_t)&IA_SIN(ia)->sin_addr,
(caddr_t)sin, sizeof(struct in_addr));
ipt->ipt_ptr += sizeof(struct in_addr);
break;
case IPOPT_TS_PRESPEC:
if (ipt->ipt_ptr + sizeof(n_time) +
sizeof(struct in_addr) > ipt->ipt_len)
goto bad;
bcopy((caddr_t)sin, (caddr_t)&ipaddr.sin_addr,
sizeof(struct in_addr));
if (ifa_ifwithaddr((SA)&ipaddr) == 0)
continue;
ipt->ipt_ptr += sizeof(struct in_addr);
break;
default:
goto bad;
}
ntime = iptime();
bcopy((caddr_t)&ntime, (caddr_t)cp + ipt->ipt_ptr - 1,
sizeof(n_time));
ipt->ipt_ptr += sizeof(n_time);
}
}
if (forward) {
ip_forward(m, 1);
return (1);
}
return (0);
bad:
icmp_error(m, type, code, 0, 0);
return (1);
}
#endif /* notdef */
/*
* Strip out IP options, at higher
* level protocol in the kernel.
* Second argument is buffer to which options
* will be moved, and return value is their length.
* (XXX) should be deleted; last arg currently ignored.
*/
void
ip_stripoptions(register struct mbuf *m, struct mbuf *mopt)
{
register int i;
struct ip *ip = mtod(m, struct ip *);
register caddr_t opts;
int olen;
olen = (ip->ip_hl<<2) - sizeof (struct ip);
opts = (caddr_t)(ip + 1);
i = m->m_len - (sizeof (struct ip) + olen);
memcpy(opts, opts + olen, (unsigned)i);
m->m_len -= olen;
ip->ip_hl = sizeof(struct ip) >> 2;
}

172
slirp/ip_output.c Normal file
View file

@ -0,0 +1,172 @@
/*
* Copyright (c) 1982, 1986, 1988, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ip_output.c 8.3 (Berkeley) 1/21/94
* ip_output.c,v 1.9 1994/11/16 10:17:10 jkh Exp
*/
/*
* Changes and additions relating to SLiRP are
* Copyright (c) 1995 Danny Gasparovski.
*
* Please read the file COPYRIGHT for the
* terms and conditions of the copyright.
*/
#include "slirp.h"
/* Number of packets queued before we start sending
* (to prevent allocing too many mbufs) */
#define IF_THRESH 10
/*
* IP output. The packet in mbuf chain m contains a skeletal IP
* header (with len, off, ttl, proto, tos, src, dst).
* The mbuf chain containing the packet will be freed.
* The mbuf opt, if present, will not be freed.
*/
int
ip_output(struct socket *so, struct mbuf *m0)
{
Slirp *slirp = m0->slirp;
register struct ip *ip;
register struct mbuf *m = m0;
register int hlen = sizeof(struct ip );
int len, off, error = 0;
DEBUG_CALL("ip_output");
DEBUG_ARG("so = %lx", (long)so);
DEBUG_ARG("m0 = %lx", (long)m0);
ip = mtod(m, struct ip *);
/*
* Fill in IP header.
*/
ip->ip_v = IPVERSION;
ip->ip_off &= IP_DF;
ip->ip_id = htons(slirp->ip_id++);
ip->ip_hl = hlen >> 2;
/*
* If small enough for interface, can just send directly.
*/
if ((uint16_t)ip->ip_len <= IF_MTU) {
ip->ip_len = htons((uint16_t)ip->ip_len);
ip->ip_off = htons((uint16_t)ip->ip_off);
ip->ip_sum = 0;
ip->ip_sum = cksum(m, hlen);
if_output(so, m);
goto done;
}
/*
* Too large for interface; fragment if possible.
* Must be able to put at least 8 bytes per fragment.
*/
if (ip->ip_off & IP_DF) {
error = -1;
goto bad;
}
len = (IF_MTU - hlen) &~ 7; /* ip databytes per packet */
if (len < 8) {
error = -1;
goto bad;
}
{
int mhlen, firstlen = len;
struct mbuf **mnext = &m->m_nextpkt;
/*
* Loop through length of segment after first fragment,
* make new header and copy data of each part and link onto chain.
*/
m0 = m;
mhlen = sizeof (struct ip);
for (off = hlen + len; off < (uint16_t)ip->ip_len; off += len) {
register struct ip *mhip;
m = m_get(slirp);
if (m == NULL) {
error = -1;
goto sendorfree;
}
m->m_data += IF_MAXLINKHDR;
mhip = mtod(m, struct ip *);
*mhip = *ip;
m->m_len = mhlen;
mhip->ip_off = ((off - hlen) >> 3) + (ip->ip_off & ~IP_MF);
if (ip->ip_off & IP_MF)
mhip->ip_off |= IP_MF;
if (off + len >= (uint16_t)ip->ip_len)
len = (uint16_t)ip->ip_len - off;
else
mhip->ip_off |= IP_MF;
mhip->ip_len = htons((uint16_t)(len + mhlen));
if (m_copy(m, m0, off, len) < 0) {
error = -1;
goto sendorfree;
}
mhip->ip_off = htons((uint16_t)mhip->ip_off);
mhip->ip_sum = 0;
mhip->ip_sum = cksum(m, mhlen);
*mnext = m;
mnext = &m->m_nextpkt;
}
/*
* Update first fragment by trimming what's been copied out
* and updating header, then send each fragment (in order).
*/
m = m0;
m_adj(m, hlen + firstlen - (uint16_t)ip->ip_len);
ip->ip_len = htons((uint16_t)m->m_len);
ip->ip_off = htons((uint16_t)(ip->ip_off | IP_MF));
ip->ip_sum = 0;
ip->ip_sum = cksum(m, hlen);
sendorfree:
for (m = m0; m; m = m0) {
m0 = m->m_nextpkt;
m->m_nextpkt = NULL;
if (error == 0)
if_output(so, m);
else
m_freem(m);
}
}
done:
return (error);
bad:
m_freem(m0);
goto done;
}

56
slirp/libslirp.h Normal file
View file

@ -0,0 +1,56 @@
#ifndef _LIBSLIRP_H
#define _LIBSLIRP_H
#ifdef CONFIG_SLIRP
#include <netinet/in.h>
struct Slirp;
typedef struct Slirp Slirp;
int get_dns_addr(struct in_addr *pdns_addr);
Slirp *slirp_init(int restricted, struct in_addr vnetwork,
struct in_addr vnetmask, struct in_addr vhost,
const char *vhostname, const char *tftp_path,
const char *bootfile, struct in_addr vdhcp_start,
struct in_addr vnameserver, void *opaque);
void slirp_cleanup(Slirp *slirp);
void slirp_select_fill(Slirp *slirp, int *pnfds,
fd_set *readfds, fd_set *writefds, fd_set *xfds);
void slirp_select_poll(Slirp *slirp,
fd_set *readfds, fd_set *writefds, fd_set *xfds,
int select_error);
void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len);
/* you must provide the following functions: */
int slirp_can_output(void *opaque);
void slirp_output(void *opaque, const uint8_t *pkt, int pkt_len);
int slirp_add_hostfwd(Slirp *slirp, int is_udp,
struct in_addr host_addr, int host_port,
struct in_addr guest_addr, int guest_port);
int slirp_remove_hostfwd(Slirp *slirp, int is_udp,
struct in_addr host_addr, int host_port);
int slirp_add_exec(Slirp *slirp, int do_pty, const void *args,
struct in_addr *guest_addr, int guest_port);
void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr,
int guest_port, const uint8_t *buf, int size);
size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr,
int guest_port);
int slirp_get_time_ms(void);
#else /* !CONFIG_SLIRP */
static inline void slirp_select_fill(int *pnfds, fd_set *readfds,
fd_set *writefds, fd_set *xfds) { }
static inline void slirp_select_poll(fd_set *readfds, fd_set *writefds,
fd_set *xfds, int select_error) { }
#endif /* !CONFIG_SLIRP */
#endif

46
slirp/main.h Normal file
View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 1995 Danny Gasparovski.
*
* Please read the file COPYRIGHT for the
* terms and conditions of the copyright.
*/
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#define TOWRITEMAX 512
extern int slirp_socket;
extern int slirp_socket_unit;
extern int slirp_socket_port;
extern uint32_t slirp_socket_addr;
extern char *slirp_socket_passwd;
extern int ctty_closed;
/*
* Get the difference in 2 times from updtim()
* Allow for wraparound times, "just in case"
* x is the greater of the 2 (current time) and y is
* what it's being compared against.
*/
#define TIME_DIFF(x,y) (x)-(y) < 0 ? ~0-(y)+(x) : (x)-(y)
extern char *slirp_tty;
extern char *exec_shell;
extern u_int curtime;
extern fd_set *global_readfds, *global_writefds, *global_xfds;
extern struct in_addr loopback_addr;
extern char *username;
extern char *socket_path;
extern int towrite_max;
extern int ppp_exit;
extern int tcp_keepintvl;
#define PROTO_SLIP 0x1
#ifdef USE_PPP
#define PROTO_PPP 0x2
#endif
void if_encap(Slirp *slirp, const uint8_t *ip_data, int ip_data_len);
ssize_t slirp_send(struct socket *so, const void *buf, size_t len, int flags);

218
slirp/mbuf.c Normal file
View file

@ -0,0 +1,218 @@
/*
* Copyright (c) 1995 Danny Gasparovski
*
* Please read the file COPYRIGHT for the
* terms and conditions of the copyright.
*/
/*
* mbuf's in SLiRP are much simpler than the real mbufs in
* FreeBSD. They are fixed size, determined by the MTU,
* so that one whole packet can fit. Mbuf's cannot be
* chained together. If there's more data than the mbuf
* could hold, an external malloced buffer is pointed to
* by m_ext (and the data pointers) and M_EXT is set in
* the flags
*/
#include "slirp.h"
#define MBUF_THRESH 30
/*
* Find a nice value for msize
* XXX if_maxlinkhdr already in mtu
*/
#define SLIRP_MSIZE (IF_MTU + IF_MAXLINKHDR + offsetof(struct mbuf, m_dat) + 6)
void
m_init(Slirp *slirp)
{
slirp->m_freelist.m_next = slirp->m_freelist.m_prev = &slirp->m_freelist;
slirp->m_usedlist.m_next = slirp->m_usedlist.m_prev = &slirp->m_usedlist;
}
/*
* Get an mbuf from the free list, if there are none
* malloc one
*
* Because fragmentation can occur if we alloc new mbufs and
* free old mbufs, we mark all mbufs above mbuf_thresh as M_DOFREE,
* which tells m_free to actually free() it
*/
struct mbuf *
m_get(Slirp *slirp)
{
register struct mbuf *m;
int flags = 0;
DEBUG_CALL("m_get");
if (slirp->m_freelist.m_next == &slirp->m_freelist) {
m = (struct mbuf *)malloc(SLIRP_MSIZE);
if (m == NULL) goto end_error;
slirp->mbuf_alloced++;
if (slirp->mbuf_alloced > MBUF_THRESH)
flags = M_DOFREE;
m->slirp = slirp;
} else {
m = slirp->m_freelist.m_next;
remque(m);
}
/* Insert it in the used list */
insque(m,&slirp->m_usedlist);
m->m_flags = (flags | M_USEDLIST);
/* Initialise it */
m->m_size = SLIRP_MSIZE - offsetof(struct mbuf, m_dat);
m->m_data = m->m_dat;
m->m_len = 0;
m->m_nextpkt = NULL;
m->m_prevpkt = NULL;
end_error:
DEBUG_ARG("m = %lx", (long )m);
return m;
}
void
m_free(struct mbuf *m)
{
DEBUG_CALL("m_free");
DEBUG_ARG("m = %lx", (long )m);
if(m) {
/* Remove from m_usedlist */
if (m->m_flags & M_USEDLIST)
remque(m);
/* If it's M_EXT, free() it */
if (m->m_flags & M_EXT)
free(m->m_ext);
/*
* Either free() it or put it on the free list
*/
if (m->m_flags & M_DOFREE) {
m->slirp->mbuf_alloced--;
free(m);
} else if ((m->m_flags & M_FREELIST) == 0) {
insque(m,&m->slirp->m_freelist);
m->m_flags = M_FREELIST; /* Clobber other flags */
}
} /* if(m) */
}
/*
* Copy data from one mbuf to the end of
* the other.. if result is too big for one mbuf, malloc()
* an M_EXT data segment
*/
void
m_cat(struct mbuf *m, struct mbuf *n)
{
/*
* If there's no room, realloc
*/
if (M_FREEROOM(m) < n->m_len)
m_inc(m,m->m_size+MINCSIZE);
memcpy(m->m_data+m->m_len, n->m_data, n->m_len);
m->m_len += n->m_len;
m_free(n);
}
/* make m size bytes large */
void
m_inc(struct mbuf *m, int size)
{
int datasize;
/* some compiles throw up on gotos. This one we can fake. */
if(m->m_size>size) return;
if (m->m_flags & M_EXT) {
datasize = m->m_data - m->m_ext;
m->m_ext = (char *)realloc(m->m_ext,size);
m->m_data = m->m_ext + datasize;
} else {
char *dat;
datasize = m->m_data - m->m_dat;
dat = (char *)malloc(size);
memcpy(dat, m->m_dat, m->m_size);
m->m_ext = dat;
m->m_data = m->m_ext + datasize;
m->m_flags |= M_EXT;
}
m->m_size = size;
}
void
m_adj(struct mbuf *m, int len)
{
if (m == NULL)
return;
if (len >= 0) {
/* Trim from head */
m->m_data += len;
m->m_len -= len;
} else {
/* Trim from tail */
len = -len;
m->m_len -= len;
}
}
/*
* Copy len bytes from m, starting off bytes into n
*/
int
m_copy(struct mbuf *n, struct mbuf *m, int off, int len)
{
if (len > M_FREEROOM(n))
return -1;
memcpy((n->m_data + n->m_len), (m->m_data + off), len);
n->m_len += len;
return 0;
}
/*
* Given a pointer into an mbuf, return the mbuf
* XXX This is a kludge, I should eliminate the need for it
* Fortunately, it's not used often
*/
struct mbuf *
dtom(Slirp *slirp, void *dat)
{
struct mbuf *m;
DEBUG_CALL("dtom");
DEBUG_ARG("dat = %lx", (long )dat);
/* bug corrected for M_EXT buffers */
for (m = slirp->m_usedlist.m_next; m != &slirp->m_usedlist;
m = m->m_next) {
if (m->m_flags & M_EXT) {
if( (char *)dat>=m->m_ext && (char *)dat<(m->m_ext + m->m_size) )
return m;
} else {
if( (char *)dat >= m->m_dat && (char *)dat<(m->m_dat + m->m_size) )
return m;
}
}
DEBUG_ERROR((dfd, "dtom failed"));
return (struct mbuf *)0;
}

127
slirp/mbuf.h Normal file
View file

@ -0,0 +1,127 @@
/*
* Copyright (c) 1982, 1986, 1988, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)mbuf.h 8.3 (Berkeley) 1/21/94
* mbuf.h,v 1.9 1994/11/14 13:54:20 bde Exp
*/
#ifndef _MBUF_H_
#define _MBUF_H_
#define m_freem m_free
#define MINCSIZE 4096 /* Amount to increase mbuf if too small */
/*
* Macros for type conversion
* mtod(m,t) - convert mbuf pointer to data pointer of correct type
*/
#define mtod(m,t) ((t)(m)->m_data)
/* XXX About mbufs for slirp:
* Only one mbuf is ever used in a chain, for each "cell" of data.
* m_nextpkt points to the next packet, if fragmented.
* If the data is too large, the M_EXT is used, and a larger block
* is alloced. Therefore, m_free[m] must check for M_EXT and if set
* free the m_ext. This is inefficient memory-wise, but who cares.
*/
/* XXX should union some of these! */
/* header at beginning of each mbuf: */
struct m_hdr {
struct mbuf *mh_next; /* Linked list of mbufs */
struct mbuf *mh_prev;
struct mbuf *mh_nextpkt; /* Next packet in queue/record */
struct mbuf *mh_prevpkt; /* Flags aren't used in the output queue */
int mh_flags; /* Misc flags */
int mh_size; /* Size of data */
struct socket *mh_so;
caddr_t mh_data; /* Location of data */
int mh_len; /* Amount of data in this mbuf */
};
/*
* How much room is in the mbuf, from m_data to the end of the mbuf
*/
#define M_ROOM(m) ((m->m_flags & M_EXT)? \
(((m)->m_ext + (m)->m_size) - (m)->m_data) \
: \
(((m)->m_dat + (m)->m_size) - (m)->m_data))
/*
* How much free room there is
*/
#define M_FREEROOM(m) (M_ROOM(m) - (m)->m_len)
#define M_TRAILINGSPACE M_FREEROOM
struct mbuf {
struct m_hdr m_hdr;
Slirp *slirp;
union M_dat {
char m_dat_[1]; /* ANSI don't like 0 sized arrays */
char *m_ext_;
} M_dat;
};
#define m_next m_hdr.mh_next
#define m_prev m_hdr.mh_prev
#define m_nextpkt m_hdr.mh_nextpkt
#define m_prevpkt m_hdr.mh_prevpkt
#define m_flags m_hdr.mh_flags
#define m_len m_hdr.mh_len
#define m_data m_hdr.mh_data
#define m_size m_hdr.mh_size
#define m_dat M_dat.m_dat_
#define m_ext M_dat.m_ext_
#define m_so m_hdr.mh_so
#define ifq_prev m_prev
#define ifq_next m_next
#define ifs_prev m_prevpkt
#define ifs_next m_nextpkt
#define ifq_so m_so
#define M_EXT 0x01 /* m_ext points to more (malloced) data */
#define M_FREELIST 0x02 /* mbuf is on free list */
#define M_USEDLIST 0x04 /* XXX mbuf is on used list (for dtom()) */
#define M_DOFREE 0x08 /* when m_free is called on the mbuf, free()
* it rather than putting it on the free list */
void m_init(Slirp *);
struct mbuf * m_get(Slirp *);
void m_free(struct mbuf *);
void m_cat(register struct mbuf *, register struct mbuf *);
void m_inc(struct mbuf *, int);
void m_adj(struct mbuf *, int);
int m_copy(struct mbuf *, struct mbuf *, int, int);
struct mbuf * dtom(Slirp *, void *);
#endif

403
slirp/misc.c Normal file
View file

@ -0,0 +1,403 @@
/*
* Copyright (c) 1995 Danny Gasparovski.
*
* Please read the file COPYRIGHT for the
* terms and conditions of the copyright.
*/
#include "slirp.h"
#ifdef DEBUG
int slirp_debug = DBG_CALL|DBG_MISC|DBG_ERROR;
#endif
struct quehead {
struct quehead *qh_link;
struct quehead *qh_rlink;
};
inline void
insque(void *a, void *b)
{
register struct quehead *element = (struct quehead *) a;
register struct quehead *head = (struct quehead *) b;
element->qh_link = head->qh_link;
head->qh_link = (struct quehead *)element;
element->qh_rlink = (struct quehead *)head;
((struct quehead *)(element->qh_link))->qh_rlink
= (struct quehead *)element;
}
inline void
remque(void *a)
{
register struct quehead *element = (struct quehead *) a;
((struct quehead *)(element->qh_link))->qh_rlink = element->qh_rlink;
((struct quehead *)(element->qh_rlink))->qh_link = element->qh_link;
element->qh_rlink = NULL;
}
int add_exec(struct ex_list **ex_ptr, int do_pty, char *exec,
struct in_addr addr, int port)
{
struct ex_list *tmp_ptr;
/* First, check if the port is "bound" */
for (tmp_ptr = *ex_ptr; tmp_ptr; tmp_ptr = tmp_ptr->ex_next) {
if (port == tmp_ptr->ex_fport &&
addr.s_addr == tmp_ptr->ex_addr.s_addr)
return -1;
}
tmp_ptr = *ex_ptr;
*ex_ptr = (struct ex_list *)malloc(sizeof(struct ex_list));
(*ex_ptr)->ex_fport = port;
(*ex_ptr)->ex_addr = addr;
(*ex_ptr)->ex_pty = do_pty;
(*ex_ptr)->ex_exec = (do_pty == 3) ? exec : strdup(exec);
(*ex_ptr)->ex_next = tmp_ptr;
return 0;
}
#ifndef HAVE_STRERROR
/*
* For systems with no strerror
*/
extern int sys_nerr;
extern char *sys_errlist[];
char *
strerror(error)
int error;
{
if (error < sys_nerr)
return sys_errlist[error];
else
return "Unknown error.";
}
#endif
int os_socket(int domain, int type, int protocol)
{
return socket(domain, type, protocol);
}
uint32_t os_get_time_ms(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec * 1000 +
(ts.tv_nsec / 1000000);
}
#if 1
int
fork_exec(struct socket *so, const char *ex, int do_pty)
{
/* not implemented */
return 0;
}
#else
/*
* XXX This is ugly
* We create and bind a socket, then fork off to another
* process, which connects to this socket, after which we
* exec the wanted program. If something (strange) happens,
* the accept() call could block us forever.
*
* do_pty = 0 Fork/exec inetd style
* do_pty = 1 Fork/exec using slirp.telnetd
* do_ptr = 2 Fork/exec using pty
*/
int
fork_exec(struct socket *so, const char *ex, int do_pty)
{
int s;
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
int opt;
int master = -1;
const char *argv[256];
/* don't want to clobber the original */
char *bptr;
const char *curarg;
int c, i, ret;
DEBUG_CALL("fork_exec");
DEBUG_ARG("so = %lx", (long)so);
DEBUG_ARG("ex = %lx", (long)ex);
DEBUG_ARG("do_pty = %lx", (long)do_pty);
if (do_pty == 2) {
return 0;
} else {
addr.sin_family = AF_INET;
addr.sin_port = 0;
addr.sin_addr.s_addr = INADDR_ANY;
if ((s = os_socket(AF_INET, SOCK_STREAM, 0)) < 0 ||
bind(s, (struct sockaddr *)&addr, addrlen) < 0 ||
listen(s, 1) < 0) {
lprint("Error: inet socket: %s\n", strerror(errno));
closesocket(s);
return 0;
}
}
switch(fork()) {
case -1:
lprint("Error: fork failed: %s\n", strerror(errno));
close(s);
if (do_pty == 2)
close(master);
return 0;
case 0:
/* Set the DISPLAY */
if (do_pty == 2) {
(void) close(master);
#ifdef TIOCSCTTY /* XXXXX */
(void) setsid();
ioctl(s, TIOCSCTTY, (char *)NULL);
#endif
} else {
getsockname(s, (struct sockaddr *)&addr, &addrlen);
close(s);
/*
* Connect to the socket
* XXX If any of these fail, we're in trouble!
*/
s = os_socket(AF_INET, SOCK_STREAM, 0);
addr.sin_addr = loopback_addr;
do {
ret = connect(s, (struct sockaddr *)&addr, addrlen);
} while (ret < 0 && errno == EINTR);
}
dup2(s, 0);
dup2(s, 1);
dup2(s, 2);
for (s = getdtablesize() - 1; s >= 3; s--)
close(s);
i = 0;
bptr = qemu_strdup(ex); /* No need to free() this */
if (do_pty == 1) {
/* Setup "slirp.telnetd -x" */
argv[i++] = "slirp.telnetd";
argv[i++] = "-x";
argv[i++] = bptr;
} else
do {
/* Change the string into argv[] */
curarg = bptr;
while (*bptr != ' ' && *bptr != (char)0)
bptr++;
c = *bptr;
*bptr++ = (char)0;
argv[i++] = strdup(curarg);
} while (c);
argv[i] = NULL;
execvp(argv[0], (char **)argv);
/* Ooops, failed, let's tell the user why */
fprintf(stderr, "Error: execvp of %s failed: %s\n",
argv[0], strerror(errno));
close(0); close(1); close(2); /* XXX */
exit(1);
default:
if (do_pty == 2) {
close(s);
so->s = master;
} else {
/*
* XXX this could block us...
* XXX Should set a timer here, and if accept() doesn't
* return after X seconds, declare it a failure
* The only reason this will block forever is if socket()
* of connect() fail in the child process
*/
do {
so->s = accept(s, (struct sockaddr *)&addr, &addrlen);
} while (so->s < 0 && errno == EINTR);
closesocket(s);
opt = 1;
setsockopt(so->s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int));
opt = 1;
setsockopt(so->s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(int));
}
fd_nonblock(so->s);
/* Append the telnet options now */
if (so->so_m != NULL && do_pty == 1) {
sbappend(so, so->so_m);
so->so_m = NULL;
}
return 1;
}
}
#endif
#ifndef HAVE_STRDUP
char *
strdup(str)
const char *str;
{
char *bptr;
bptr = (char *)malloc(strlen(str)+1);
strcpy(bptr, str);
return bptr;
}
#endif
void lprint(const char *format, ...)
{
va_list args;
va_start(args, format);
vprintf(format, args);
va_end(args);
}
/*
* Set fd blocking and non-blocking
*/
void
fd_nonblock(int fd)
{
#ifdef FIONBIO
#ifdef _WIN32
unsigned long opt = 1;
#else
int opt = 1;
#endif
ioctlsocket(fd, FIONBIO, &opt);
#else
int opt;
opt = fcntl(fd, F_GETFL, 0);
opt |= O_NONBLOCK;
fcntl(fd, F_SETFL, opt);
#endif
}
void
fd_block(int fd)
{
#ifdef FIONBIO
#ifdef _WIN32
unsigned long opt = 0;
#else
int opt = 0;
#endif
ioctlsocket(fd, FIONBIO, &opt);
#else
int opt;
opt = fcntl(fd, F_GETFL, 0);
opt &= ~O_NONBLOCK;
fcntl(fd, F_SETFL, opt);
#endif
}
#if 0
void slirp_connection_info(Slirp *slirp, Monitor *mon)
{
const char * const tcpstates[] = {
[TCPS_CLOSED] = "CLOSED",
[TCPS_LISTEN] = "LISTEN",
[TCPS_SYN_SENT] = "SYN_SENT",
[TCPS_SYN_RECEIVED] = "SYN_RCVD",
[TCPS_ESTABLISHED] = "ESTABLISHED",
[TCPS_CLOSE_WAIT] = "CLOSE_WAIT",
[TCPS_FIN_WAIT_1] = "FIN_WAIT_1",
[TCPS_CLOSING] = "CLOSING",
[TCPS_LAST_ACK] = "LAST_ACK",
[TCPS_FIN_WAIT_2] = "FIN_WAIT_2",
[TCPS_TIME_WAIT] = "TIME_WAIT",
};
struct in_addr dst_addr;
struct sockaddr_in src;
socklen_t src_len;
uint16_t dst_port;
struct socket *so;
const char *state;
char buf[20];
int n;
monitor_printf(mon, " Protocol[State] FD Source Address Port "
"Dest. Address Port RecvQ SendQ\n");
for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so->so_next) {
if (so->so_state & SS_HOSTFWD) {
state = "HOST_FORWARD";
} else if (so->so_tcpcb) {
state = tcpstates[so->so_tcpcb->t_state];
} else {
state = "NONE";
}
if (so->so_state & (SS_HOSTFWD | SS_INCOMING)) {
src_len = sizeof(src);
getsockname(so->s, (struct sockaddr *)&src, &src_len);
dst_addr = so->so_laddr;
dst_port = so->so_lport;
} else {
src.sin_addr = so->so_laddr;
src.sin_port = so->so_lport;
dst_addr = so->so_faddr;
dst_port = so->so_fport;
}
n = snprintf(buf, sizeof(buf), " TCP[%s]", state);
memset(&buf[n], ' ', 19 - n);
buf[19] = 0;
monitor_printf(mon, "%s %3d %15s %5d ", buf, so->s,
src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : "*",
ntohs(src.sin_port));
monitor_printf(mon, "%15s %5d %5d %5d\n",
inet_ntoa(dst_addr), ntohs(dst_port),
so->so_rcv.sb_cc, so->so_snd.sb_cc);
}
for (so = slirp->udb.so_next; so != &slirp->udb; so = so->so_next) {
if (so->so_state & SS_HOSTFWD) {
n = snprintf(buf, sizeof(buf), " UDP[HOST_FORWARD]");
src_len = sizeof(src);
getsockname(so->s, (struct sockaddr *)&src, &src_len);
dst_addr = so->so_laddr;
dst_port = so->so_lport;
} else {
n = snprintf(buf, sizeof(buf), " UDP[%d sec]",
(so->so_expire - curtime) / 1000);
src.sin_addr = so->so_laddr;
src.sin_port = so->so_lport;
dst_addr = so->so_faddr;
dst_port = so->so_fport;
}
memset(&buf[n], ' ', 19 - n);
buf[19] = 0;
monitor_printf(mon, "%s %3d %15s %5d ", buf, so->s,
src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : "*",
ntohs(src.sin_port));
monitor_printf(mon, "%15s %5d %5d %5d\n",
inet_ntoa(dst_addr), ntohs(dst_port),
so->so_rcv.sb_cc, so->so_snd.sb_cc);
}
}
#endif

73
slirp/misc.h Normal file
View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 1995 Danny Gasparovski.
*
* Please read the file COPYRIGHT for the
* terms and conditions of the copyright.
*/
#ifndef _MISC_H_
#define _MISC_H_
struct ex_list {
int ex_pty; /* Do we want a pty? */
struct in_addr ex_addr; /* Server address */
int ex_fport; /* Port to telnet to */
const char *ex_exec; /* Command line of what to exec */
struct ex_list *ex_next;
};
#ifndef HAVE_STRDUP
char *strdup(const char *);
#endif
void do_wait(int);
#define EMU_NONE 0x0
/* TCP emulations */
#define EMU_CTL 0x1
#define EMU_FTP 0x2
#define EMU_KSH 0x3
#define EMU_IRC 0x4
#define EMU_REALAUDIO 0x5
#define EMU_RLOGIN 0x6
#define EMU_IDENT 0x7
#define EMU_RSH 0x8
#define EMU_NOCONNECT 0x10 /* Don't connect */
struct tos_t {
uint16_t lport;
uint16_t fport;
uint8_t tos;
uint8_t emu;
};
struct emu_t {
uint16_t lport;
uint16_t fport;
uint8_t tos;
uint8_t emu;
struct emu_t *next;
};
extern int x_port, x_server, x_display;
int show_x(char *, struct socket *);
void redir_x(uint32_t, int, int, int);
void slirp_insque(void *, void *);
void slirp_remque(void *);
int add_exec(struct ex_list **, int, char *, struct in_addr, int);
int slirp_openpty(int *, int *);
int fork_exec(struct socket *so, const char *ex, int do_pty);
void snooze_hup(int);
void snooze(void);
void relay(int);
void add_emu(char *);
void fd_nonblock(int);
void fd_block(int);
int rsh_exec(struct socket *, struct socket *, char *, char *, char *);
int os_socket(int domain, int type, int protocol);
uint32_t os_get_time_ms(void);
#endif

181
slirp/sbuf.c Normal file
View file

@ -0,0 +1,181 @@
/*
* Copyright (c) 1995 Danny Gasparovski.
*
* Please read the file COPYRIGHT for the
* terms and conditions of the copyright.
*/
#include "slirp.h"
static void sbappendsb(struct sbuf *sb, struct mbuf *m);
void
sbfree(struct sbuf *sb)
{
free(sb->sb_data);
}
void
sbdrop(struct sbuf *sb, int num)
{
/*
* We can only drop how much we have
* This should never succeed
*/
if(num > sb->sb_cc)
num = sb->sb_cc;
sb->sb_cc -= num;
sb->sb_rptr += num;
if(sb->sb_rptr >= sb->sb_data + sb->sb_datalen)
sb->sb_rptr -= sb->sb_datalen;
}
void
sbreserve(struct sbuf *sb, int size)
{
if (sb->sb_data) {
/* Already alloced, realloc if necessary */
if (sb->sb_datalen != size) {
sb->sb_wptr = sb->sb_rptr = sb->sb_data = (char *)realloc(sb->sb_data, size);
sb->sb_cc = 0;
if (sb->sb_wptr)
sb->sb_datalen = size;
else
sb->sb_datalen = 0;
}
} else {
sb->sb_wptr = sb->sb_rptr = sb->sb_data = (char *)malloc(size);
sb->sb_cc = 0;
if (sb->sb_wptr)
sb->sb_datalen = size;
else
sb->sb_datalen = 0;
}
}
/*
* Try and write() to the socket, whatever doesn't get written
* append to the buffer... for a host with a fast net connection,
* this prevents an unnecessary copy of the data
* (the socket is non-blocking, so we won't hang)
*/
void
sbappend(struct socket *so, struct mbuf *m)
{
int ret = 0;
DEBUG_CALL("sbappend");
DEBUG_ARG("so = %lx", (long)so);
DEBUG_ARG("m = %lx", (long)m);
DEBUG_ARG("m->m_len = %d", m->m_len);
/* Shouldn't happen, but... e.g. foreign host closes connection */
if (m->m_len <= 0) {
m_free(m);
return;
}
/*
* If there is urgent data, call sosendoob
* if not all was sent, sowrite will take care of the rest
* (The rest of this function is just an optimisation)
*/
if (so->so_urgc) {
sbappendsb(&so->so_rcv, m);
m_free(m);
sosendoob(so);
return;
}
/*
* We only write if there's nothing in the buffer,
* ottherwise it'll arrive out of order, and hence corrupt
*/
if (!so->so_rcv.sb_cc)
ret = slirp_send(so, m->m_data, m->m_len, 0);
if (ret <= 0) {
/*
* Nothing was written
* It's possible that the socket has closed, but
* we don't need to check because if it has closed,
* it will be detected in the normal way by soread()
*/
sbappendsb(&so->so_rcv, m);
} else if (ret != m->m_len) {
/*
* Something was written, but not everything..
* sbappendsb the rest
*/
m->m_len -= ret;
m->m_data += ret;
sbappendsb(&so->so_rcv, m);
} /* else */
/* Whatever happened, we free the mbuf */
m_free(m);
}
/*
* Copy the data from m into sb
* The caller is responsible to make sure there's enough room
*/
static void
sbappendsb(struct sbuf *sb, struct mbuf *m)
{
int len, n, nn;
len = m->m_len;
if (sb->sb_wptr < sb->sb_rptr) {
n = sb->sb_rptr - sb->sb_wptr;
if (n > len) n = len;
memcpy(sb->sb_wptr, m->m_data, n);
} else {
/* Do the right edge first */
n = sb->sb_data + sb->sb_datalen - sb->sb_wptr;
if (n > len) n = len;
memcpy(sb->sb_wptr, m->m_data, n);
len -= n;
if (len) {
/* Now the left edge */
nn = sb->sb_rptr - sb->sb_data;
if (nn > len) nn = len;
memcpy(sb->sb_data,m->m_data+n,nn);
n += nn;
}
}
sb->sb_cc += n;
sb->sb_wptr += n;
if (sb->sb_wptr >= sb->sb_data + sb->sb_datalen)
sb->sb_wptr -= sb->sb_datalen;
}
/*
* Copy data from sbuf to a normal, straight buffer
* Don't update the sbuf rptr, this will be
* done in sbdrop when the data is acked
*/
void
sbcopy(struct sbuf *sb, int off, int len, char *to)
{
char *from;
from = sb->sb_rptr + off;
if (from >= sb->sb_data + sb->sb_datalen)
from -= sb->sb_datalen;
if (from < sb->sb_wptr) {
if (len > sb->sb_cc) len = sb->sb_cc;
memcpy(to,from,len);
} else {
/* re-use off */
off = (sb->sb_data + sb->sb_datalen) - from;
if (off > len) off = len;
memcpy(to,from,off);
len -= off;
if (len)
memcpy(to+off,sb->sb_data,len);
}
}

30
slirp/sbuf.h Normal file
View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 1995 Danny Gasparovski.
*
* Please read the file COPYRIGHT for the
* terms and conditions of the copyright.
*/
#ifndef _SBUF_H_
#define _SBUF_H_
#define sbflush(sb) sbdrop((sb),(sb)->sb_cc)
#define sbspace(sb) ((sb)->sb_datalen - (sb)->sb_cc)
struct sbuf {
u_int sb_cc; /* actual chars in buffer */
u_int sb_datalen; /* Length of data */
char *sb_wptr; /* write pointer. points to where the next
* bytes should be written in the sbuf */
char *sb_rptr; /* read pointer. points to where the next
* byte should be read from the sbuf */
char *sb_data; /* Actual data */
};
void sbfree(struct sbuf *);
void sbdrop(struct sbuf *, int);
void sbreserve(struct sbuf *, int);
void sbappend(struct socket *, struct mbuf *);
void sbcopy(struct sbuf *, int, int, char *);
#endif

828
slirp/slirp.c Normal file
View file

@ -0,0 +1,828 @@
/*
* libslirp glue
*
* Copyright (c) 2004-2008 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "slirp.h"
/* host loopback address */
struct in_addr loopback_addr;
/* emulated hosts use the MAC addr 52:55:IP:IP:IP:IP */
static const uint8_t special_ethaddr[6] = {
0x52, 0x55, 0x00, 0x00, 0x00, 0x00
};
static const uint8_t zero_ethaddr[6] = { 0, 0, 0, 0, 0, 0 };
/* XXX: suppress those select globals */
fd_set *global_readfds, *global_writefds, *global_xfds;
u_int curtime;
static u_int time_fasttimo, last_slowtimo;
static int do_slowtimo;
static struct in_addr dns_addr;
static u_int dns_addr_time;
#ifdef _WIN32
int get_dns_addr(struct in_addr *pdns_addr)
{
FIXED_INFO *FixedInfo=NULL;
ULONG BufLen;
DWORD ret;
IP_ADDR_STRING *pIPAddr;
struct in_addr tmp_addr;
if (dns_addr.s_addr != 0 && (curtime - dns_addr_time) < 1000) {
*pdns_addr = dns_addr;
return 0;
}
FixedInfo = (FIXED_INFO *)GlobalAlloc(GPTR, sizeof(FIXED_INFO));
BufLen = sizeof(FIXED_INFO);
if (ERROR_BUFFER_OVERFLOW == GetNetworkParams(FixedInfo, &BufLen)) {
if (FixedInfo) {
GlobalFree(FixedInfo);
FixedInfo = NULL;
}
FixedInfo = GlobalAlloc(GPTR, BufLen);
}
if ((ret = GetNetworkParams(FixedInfo, &BufLen)) != ERROR_SUCCESS) {
printf("GetNetworkParams failed. ret = %08x\n", (u_int)ret );
if (FixedInfo) {
GlobalFree(FixedInfo);
FixedInfo = NULL;
}
return -1;
}
pIPAddr = &(FixedInfo->DnsServerList);
inet_aton(pIPAddr->IpAddress.String, &tmp_addr);
*pdns_addr = tmp_addr;
dns_addr = tmp_addr;
dns_addr_time = curtime;
if (FixedInfo) {
GlobalFree(FixedInfo);
FixedInfo = NULL;
}
return 0;
}
static void winsock_cleanup(void)
{
WSACleanup();
}
#else
static struct stat dns_addr_stat;
int get_dns_addr(struct in_addr *pdns_addr)
{
char buff[512];
char buff2[257];
FILE *f;
int found = 0;
struct in_addr tmp_addr;
if (dns_addr.s_addr != 0) {
struct stat old_stat;
if ((curtime - dns_addr_time) < 1000) {
*pdns_addr = dns_addr;
return 0;
}
old_stat = dns_addr_stat;
if (stat("/etc/resolv.conf", &dns_addr_stat) != 0)
return -1;
if ((dns_addr_stat.st_dev == old_stat.st_dev)
&& (dns_addr_stat.st_ino == old_stat.st_ino)
&& (dns_addr_stat.st_size == old_stat.st_size)
&& (dns_addr_stat.st_mtime == old_stat.st_mtime)) {
*pdns_addr = dns_addr;
return 0;
}
}
f = fopen("/etc/resolv.conf", "r");
if (!f)
return -1;
#ifdef DEBUG
lprint("IP address of your DNS(s): ");
#endif
while (fgets(buff, 512, f) != NULL) {
if (sscanf(buff, "nameserver%*[ \t]%256s", buff2) == 1) {
if (!inet_aton(buff2, &tmp_addr))
continue;
/* If it's the first one, set it to dns_addr */
if (!found) {
*pdns_addr = tmp_addr;
dns_addr = tmp_addr;
dns_addr_time = curtime;
}
#ifdef DEBUG
else
lprint(", ");
#endif
if (++found > 3) {
#ifdef DEBUG
lprint("(more)");
#endif
break;
}
#ifdef DEBUG
else
lprint("%s", inet_ntoa(tmp_addr));
#endif
}
}
fclose(f);
if (!found)
return -1;
return 0;
}
#endif
static void slirp_init_once(void)
{
static int initialized;
#ifdef _WIN32
WSADATA Data;
#endif
if (initialized) {
return;
}
initialized = 1;
#ifdef _WIN32
WSAStartup(MAKEWORD(2,0), &Data);
atexit(winsock_cleanup);
#endif
loopback_addr.s_addr = htonl(INADDR_LOOPBACK);
}
Slirp *slirp_init(int restricted, struct in_addr vnetwork,
struct in_addr vnetmask, struct in_addr vhost,
const char *vhostname, const char *tftp_path,
const char *bootfile, struct in_addr vdhcp_start,
struct in_addr vnameserver, void *opaque)
{
Slirp *slirp = mallocz(sizeof(Slirp));
slirp_init_once();
slirp->restricted = restricted;
if_init(slirp);
ip_init(slirp);
/* Initialise mbufs *after* setting the MTU */
m_init(slirp);
slirp->vnetwork_addr = vnetwork;
slirp->vnetwork_mask = vnetmask;
slirp->vhost_addr = vhost;
if (vhostname) {
pstrcpy(slirp->client_hostname, sizeof(slirp->client_hostname),
vhostname);
}
if (tftp_path) {
slirp->tftp_prefix = strdup(tftp_path);
}
if (bootfile) {
slirp->bootp_filename = strdup(bootfile);
}
slirp->vdhcp_startaddr = vdhcp_start;
slirp->vnameserver_addr = vnameserver;
slirp->opaque = opaque;
return slirp;
}
void slirp_cleanup(Slirp *slirp)
{
free(slirp->tftp_prefix);
free(slirp->bootp_filename);
free(slirp);
}
#define CONN_CANFSEND(so) (((so)->so_state & (SS_FCANTSENDMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
#define CONN_CANFRCV(so) (((so)->so_state & (SS_FCANTRCVMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
#define UPD_NFDS(x) if (nfds < (x)) nfds = (x)
void slirp_select_fill(Slirp *slirp, int *pnfds,
fd_set *readfds, fd_set *writefds, fd_set *xfds)
{
struct socket *so, *so_next;
int nfds;
/* fail safe */
global_readfds = NULL;
global_writefds = NULL;
global_xfds = NULL;
nfds = *pnfds;
/*
* First, TCP sockets
*/
do_slowtimo = 0;
{
/*
* *_slowtimo needs calling if there are IP fragments
* in the fragment queue, or there are TCP connections active
*/
do_slowtimo |= ((slirp->tcb.so_next != &slirp->tcb) ||
(&slirp->ipq.ip_link != slirp->ipq.ip_link.next));
for (so = slirp->tcb.so_next; so != &slirp->tcb;
so = so_next) {
so_next = so->so_next;
/*
* See if we need a tcp_fasttimo
*/
if (time_fasttimo == 0 && so->so_tcpcb->t_flags & TF_DELACK)
time_fasttimo = curtime; /* Flag when we want a fasttimo */
/*
* NOFDREF can include still connecting to local-host,
* newly socreated() sockets etc. Don't want to select these.
*/
if (so->so_state & SS_NOFDREF || so->s == -1)
continue;
/*
* Set for reading sockets which are accepting
*/
if (so->so_state & SS_FACCEPTCONN) {
FD_SET(so->s, readfds);
UPD_NFDS(so->s);
continue;
}
/*
* Set for writing sockets which are connecting
*/
if (so->so_state & SS_ISFCONNECTING) {
FD_SET(so->s, writefds);
UPD_NFDS(so->s);
continue;
}
/*
* Set for writing if we are connected, can send more, and
* we have something to send
*/
if (CONN_CANFSEND(so) && so->so_rcv.sb_cc) {
FD_SET(so->s, writefds);
UPD_NFDS(so->s);
}
/*
* Set for reading (and urgent data) if we are connected, can
* receive more, and we have room for it XXX /2 ?
*/
if (CONN_CANFRCV(so) && (so->so_snd.sb_cc < (so->so_snd.sb_datalen/2))) {
FD_SET(so->s, readfds);
FD_SET(so->s, xfds);
UPD_NFDS(so->s);
}
}
/*
* UDP sockets
*/
for (so = slirp->udb.so_next; so != &slirp->udb;
so = so_next) {
so_next = so->so_next;
/*
* See if it's timed out
*/
if (so->so_expire) {
if (so->so_expire <= curtime) {
udp_detach(so);
continue;
} else
do_slowtimo = 1; /* Let socket expire */
}
/*
* When UDP packets are received from over the
* link, they're sendto()'d straight away, so
* no need for setting for writing
* Limit the number of packets queued by this session
* to 4. Note that even though we try and limit this
* to 4 packets, the session could have more queued
* if the packets needed to be fragmented
* (XXX <= 4 ?)
*/
if ((so->so_state & SS_ISFCONNECTED) && so->so_queued <= 4) {
FD_SET(so->s, readfds);
UPD_NFDS(so->s);
}
}
}
*pnfds = nfds;
}
void slirp_select_poll(Slirp *slirp,
fd_set *readfds, fd_set *writefds, fd_set *xfds,
int select_error)
{
struct socket *so, *so_next;
int ret;
global_readfds = readfds;
global_writefds = writefds;
global_xfds = xfds;
curtime = os_get_time_ms();
{
/*
* See if anything has timed out
*/
if (time_fasttimo && ((curtime - time_fasttimo) >= 2)) {
tcp_fasttimo(slirp);
time_fasttimo = 0;
}
if (do_slowtimo && ((curtime - last_slowtimo) >= 499)) {
ip_slowtimo(slirp);
tcp_slowtimo(slirp);
last_slowtimo = curtime;
}
/*
* Check sockets
*/
if (!select_error) {
/*
* Check TCP sockets
*/
for (so = slirp->tcb.so_next; so != &slirp->tcb;
so = so_next) {
so_next = so->so_next;
/*
* FD_ISSET is meaningless on these sockets
* (and they can crash the program)
*/
if (so->so_state & SS_NOFDREF || so->s == -1)
continue;
/*
* Check for URG data
* This will soread as well, so no need to
* test for readfds below if this succeeds
*/
if (FD_ISSET(so->s, xfds))
sorecvoob(so);
/*
* Check sockets for reading
*/
else if (FD_ISSET(so->s, readfds)) {
/*
* Check for incoming connections
*/
if (so->so_state & SS_FACCEPTCONN) {
tcp_connect(so);
continue;
} /* else */
ret = soread(so);
/* Output it if we read something */
if (ret > 0)
tcp_output(sototcpcb(so));
}
/*
* Check sockets for writing
*/
if (FD_ISSET(so->s, writefds)) {
/*
* Check for non-blocking, still-connecting sockets
*/
if (so->so_state & SS_ISFCONNECTING) {
/* Connected */
so->so_state &= ~SS_ISFCONNECTING;
ret = send(so->s, (const void *) &ret, 0, 0);
if (ret < 0) {
/* XXXXX Must fix, zero bytes is a NOP */
if (errno == EAGAIN || errno == EWOULDBLOCK ||
errno == EINPROGRESS || errno == ENOTCONN)
continue;
/* else failed */
so->so_state &= SS_PERSISTENT_MASK;
so->so_state |= SS_NOFDREF;
}
/* else so->so_state &= ~SS_ISFCONNECTING; */
/*
* Continue tcp_input
*/
tcp_input((struct mbuf *)NULL, sizeof(struct ip), so);
/* continue; */
} else
ret = sowrite(so);
/*
* XXXXX If we wrote something (a lot), there
* could be a need for a window update.
* In the worst case, the remote will send
* a window probe to get things going again
*/
}
/*
* Probe a still-connecting, non-blocking socket
* to check if it's still alive
*/
#ifdef PROBE_CONN
if (so->so_state & SS_ISFCONNECTING) {
ret = recv(so->s, (char *)&ret, 0,0);
if (ret < 0) {
/* XXX */
if (errno == EAGAIN || errno == EWOULDBLOCK ||
errno == EINPROGRESS || errno == ENOTCONN)
continue; /* Still connecting, continue */
/* else failed */
so->so_state &= SS_PERSISTENT_MASK;
so->so_state |= SS_NOFDREF;
/* tcp_input will take care of it */
} else {
ret = send(so->s, &ret, 0,0);
if (ret < 0) {
/* XXX */
if (errno == EAGAIN || errno == EWOULDBLOCK ||
errno == EINPROGRESS || errno == ENOTCONN)
continue;
/* else failed */
so->so_state &= SS_PERSISTENT_MASK;
so->so_state |= SS_NOFDREF;
} else
so->so_state &= ~SS_ISFCONNECTING;
}
tcp_input((struct mbuf *)NULL, sizeof(struct ip),so);
} /* SS_ISFCONNECTING */
#endif
}
/*
* Now UDP sockets.
* Incoming packets are sent straight away, they're not buffered.
* Incoming UDP data isn't buffered either.
*/
for (so = slirp->udb.so_next; so != &slirp->udb;
so = so_next) {
so_next = so->so_next;
if (so->s != -1 && FD_ISSET(so->s, readfds)) {
sorecvfrom(so);
}
}
}
/*
* See if we can start outputting
*/
if (slirp->if_queued) {
if_start(slirp);
}
}
/* clear global file descriptor sets.
* these reside on the stack in vl.c
* so they're unusable if we're not in
* slirp_select_fill or slirp_select_poll.
*/
global_readfds = NULL;
global_writefds = NULL;
global_xfds = NULL;
}
#define ETH_ALEN 6
#define ETH_HLEN 14
#define ETH_P_IP 0x0800 /* Internet Protocol packet */
#define ETH_P_ARP 0x0806 /* Address Resolution packet */
#define ARPOP_REQUEST 1 /* ARP request */
#define ARPOP_REPLY 2 /* ARP reply */
struct ethhdr
{
unsigned char h_dest[ETH_ALEN]; /* destination eth addr */
unsigned char h_source[ETH_ALEN]; /* source ether addr */
unsigned short h_proto; /* packet type ID field */
};
struct arphdr
{
unsigned short ar_hrd; /* format of hardware address */
unsigned short ar_pro; /* format of protocol address */
unsigned char ar_hln; /* length of hardware address */
unsigned char ar_pln; /* length of protocol address */
unsigned short ar_op; /* ARP opcode (command) */
/*
* Ethernet looks like this : This bit is variable sized however...
*/
unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */
uint32_t ar_sip; /* sender IP address */
unsigned char ar_tha[ETH_ALEN]; /* target hardware address */
uint32_t ar_tip ; /* target IP address */
} __attribute__((packed));
static void arp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len)
{
struct ethhdr *eh = (struct ethhdr *)pkt;
struct arphdr *ah = (struct arphdr *)(pkt + ETH_HLEN);
uint8_t arp_reply[max(ETH_HLEN + sizeof(struct arphdr), 64)];
struct ethhdr *reh = (struct ethhdr *)arp_reply;
struct arphdr *rah = (struct arphdr *)(arp_reply + ETH_HLEN);
int ar_op;
struct ex_list *ex_ptr;
ar_op = ntohs(ah->ar_op);
switch(ar_op) {
case ARPOP_REQUEST:
if ((ah->ar_tip & slirp->vnetwork_mask.s_addr) ==
slirp->vnetwork_addr.s_addr) {
if (ah->ar_tip == slirp->vnameserver_addr.s_addr ||
ah->ar_tip == slirp->vhost_addr.s_addr)
goto arp_ok;
for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) {
if (ex_ptr->ex_addr.s_addr == ah->ar_tip)
goto arp_ok;
}
return;
arp_ok:
memset(arp_reply, 0, sizeof(arp_reply));
/* XXX: make an ARP request to have the client address */
memcpy(slirp->client_ethaddr, eh->h_source, ETH_ALEN);
/* ARP request for alias/dns mac address */
memcpy(reh->h_dest, pkt + ETH_ALEN, ETH_ALEN);
memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 4);
memcpy(&reh->h_source[2], &ah->ar_tip, 4);
reh->h_proto = htons(ETH_P_ARP);
rah->ar_hrd = htons(1);
rah->ar_pro = htons(ETH_P_IP);
rah->ar_hln = ETH_ALEN;
rah->ar_pln = 4;
rah->ar_op = htons(ARPOP_REPLY);
memcpy(rah->ar_sha, reh->h_source, ETH_ALEN);
rah->ar_sip = ah->ar_tip;
memcpy(rah->ar_tha, ah->ar_sha, ETH_ALEN);
rah->ar_tip = ah->ar_sip;
slirp_output(slirp->opaque, arp_reply, sizeof(arp_reply));
}
break;
case ARPOP_REPLY:
/* reply to request of client mac address ? */
if (!memcmp(slirp->client_ethaddr, zero_ethaddr, ETH_ALEN) &&
ah->ar_sip == slirp->client_ipaddr.s_addr) {
memcpy(slirp->client_ethaddr, ah->ar_sha, ETH_ALEN);
}
break;
default:
break;
}
}
void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len)
{
struct mbuf *m;
int proto;
if (pkt_len < ETH_HLEN)
return;
proto = ntohs(*(uint16_t *)(pkt + 12));
switch(proto) {
case ETH_P_ARP:
arp_input(slirp, pkt, pkt_len);
break;
case ETH_P_IP:
m = m_get(slirp);
if (!m)
return;
/* Note: we add to align the IP header */
if (M_FREEROOM(m) < pkt_len + 2) {
m_inc(m, pkt_len + 2);
}
m->m_len = pkt_len + 2;
memcpy(m->m_data + 2, pkt, pkt_len);
m->m_data += 2 + ETH_HLEN;
m->m_len -= 2 + ETH_HLEN;
ip_input(m);
break;
default:
break;
}
}
/* output the IP packet to the ethernet device */
void if_encap(Slirp *slirp, const uint8_t *ip_data, int ip_data_len)
{
uint8_t buf[1600];
struct ethhdr *eh = (struct ethhdr *)buf;
if (ip_data_len + ETH_HLEN > sizeof(buf))
return;
if (!memcmp(slirp->client_ethaddr, zero_ethaddr, ETH_ALEN)) {
uint8_t arp_req[ETH_HLEN + sizeof(struct arphdr)];
struct ethhdr *reh = (struct ethhdr *)arp_req;
struct arphdr *rah = (struct arphdr *)(arp_req + ETH_HLEN);
const struct ip *iph = (const struct ip *)ip_data;
/* If the client addr is not known, there is no point in
sending the packet to it. Normally the sender should have
done an ARP request to get its MAC address. Here we do it
in place of sending the packet and we hope that the sender
will retry sending its packet. */
memset(reh->h_dest, 0xff, ETH_ALEN);
memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 4);
memcpy(&reh->h_source[2], &slirp->vhost_addr, 4);
reh->h_proto = htons(ETH_P_ARP);
rah->ar_hrd = htons(1);
rah->ar_pro = htons(ETH_P_IP);
rah->ar_hln = ETH_ALEN;
rah->ar_pln = 4;
rah->ar_op = htons(ARPOP_REQUEST);
/* source hw addr */
memcpy(rah->ar_sha, special_ethaddr, ETH_ALEN - 4);
memcpy(&rah->ar_sha[2], &slirp->vhost_addr, 4);
/* source IP */
rah->ar_sip = slirp->vhost_addr.s_addr;
/* target hw addr (none) */
memset(rah->ar_tha, 0, ETH_ALEN);
/* target IP */
rah->ar_tip = iph->ip_dst.s_addr;
slirp->client_ipaddr = iph->ip_dst;
slirp_output(slirp->opaque, arp_req, sizeof(arp_req));
} else {
memcpy(eh->h_dest, slirp->client_ethaddr, ETH_ALEN);
memcpy(eh->h_source, special_ethaddr, ETH_ALEN - 4);
/* XXX: not correct */
memcpy(&eh->h_source[2], &slirp->vhost_addr, 4);
eh->h_proto = htons(ETH_P_IP);
memcpy(buf + sizeof(struct ethhdr), ip_data, ip_data_len);
slirp_output(slirp->opaque, buf, ip_data_len + ETH_HLEN);
}
}
/* Drop host forwarding rule, return 0 if found. */
int slirp_remove_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
int host_port)
{
struct socket *so;
struct socket *head = (is_udp ? &slirp->udb : &slirp->tcb);
struct sockaddr_in addr;
int port = htons(host_port);
socklen_t addr_len;
for (so = head->so_next; so != head; so = so->so_next) {
addr_len = sizeof(addr);
if ((so->so_state & SS_HOSTFWD) &&
getsockname(so->s, (struct sockaddr *)&addr, &addr_len) == 0 &&
addr.sin_addr.s_addr == host_addr.s_addr &&
addr.sin_port == port) {
close(so->s);
sofree(so);
return 0;
}
}
return -1;
}
int slirp_add_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,
int host_port, struct in_addr guest_addr, int guest_port)
{
if (!guest_addr.s_addr) {
guest_addr = slirp->vdhcp_startaddr;
}
if (is_udp) {
if (!udp_listen(slirp, host_addr.s_addr, htons(host_port),
guest_addr.s_addr, htons(guest_port), SS_HOSTFWD))
return -1;
} else {
if (!tcp_listen(slirp, host_addr.s_addr, htons(host_port),
guest_addr.s_addr, htons(guest_port), SS_HOSTFWD))
return -1;
}
return 0;
}
int slirp_add_exec(Slirp *slirp, int do_pty, const void *args,
struct in_addr *guest_addr, int guest_port)
{
if (!guest_addr->s_addr) {
guest_addr->s_addr = slirp->vnetwork_addr.s_addr |
(htonl(0x0204) & ~slirp->vnetwork_mask.s_addr);
}
if ((guest_addr->s_addr & slirp->vnetwork_mask.s_addr) !=
slirp->vnetwork_addr.s_addr ||
guest_addr->s_addr == slirp->vhost_addr.s_addr ||
guest_addr->s_addr == slirp->vnameserver_addr.s_addr) {
return -1;
}
return add_exec(&slirp->exec_list, do_pty, (char *)args, *guest_addr,
htons(guest_port));
}
ssize_t slirp_send(struct socket *so, const void *buf, size_t len, int flags)
{
#if 0
if (so->s == -1 && so->extra) {
qemu_chr_write(so->extra, buf, len);
return len;
}
#endif
return send(so->s, buf, len, flags);
}
static struct socket *
slirp_find_ctl_socket(Slirp *slirp, struct in_addr guest_addr, int guest_port)
{
struct socket *so;
for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so->so_next) {
if (so->so_faddr.s_addr == guest_addr.s_addr &&
htons(so->so_fport) == guest_port) {
return so;
}
}
return NULL;
}
size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr,
int guest_port)
{
struct iovec iov[2];
struct socket *so;
so = slirp_find_ctl_socket(slirp, guest_addr, guest_port);
if (!so || so->so_state & SS_NOFDREF)
return 0;
if (!CONN_CANFRCV(so) || so->so_snd.sb_cc >= (so->so_snd.sb_datalen/2))
return 0;
return sopreprbuf(so, iov, NULL);
}
void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr, int guest_port,
const uint8_t *buf, int size)
{
int ret;
struct socket *so = slirp_find_ctl_socket(slirp, guest_addr, guest_port);
if (!so)
return;
ret = soreadbuf(so, (const char *)buf, size);
if (ret > 0)
tcp_output(sototcpcb(so));
}

313
slirp/slirp.h Normal file
View file

@ -0,0 +1,313 @@
#ifndef __COMMON_H__
#define __COMMON_H__
#include <stdlib.h>
#include "../cutils.h"
#include "slirp_config.h"
#ifdef _WIN32
# include <inttypes.h>
typedef char *caddr_t;
# include <windows.h>
# include <winsock2.h>
# include <ws2tcpip.h>
# include <sys/timeb.h>
# include <iphlpapi.h>
# define EWOULDBLOCK WSAEWOULDBLOCK
# define EINPROGRESS WSAEINPROGRESS
# define ENOTCONN WSAENOTCONN
# define EHOSTUNREACH WSAEHOSTUNREACH
# define ENETUNREACH WSAENETUNREACH
# define ECONNREFUSED WSAECONNREFUSED
#else
# define ioctlsocket ioctl
# define closesocket(s) close(s)
# if !defined(__HAIKU__)
# define O_BINARY 0
# endif
#endif
#include <sys/types.h>
#ifdef HAVE_SYS_BITYPES_H
# include <sys/bitypes.h>
#endif
#include <sys/time.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif
#include <stdio.h>
#include <errno.h>
#ifndef HAVE_MEMMOVE
#define memmove(x, y, z) bcopy(y, x, z)
#endif
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#else
# include <strings.h>
#endif
#ifndef _WIN32
#include <sys/uio.h>
#endif
#ifndef _WIN32
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
/* Systems lacking strdup() definition in <string.h>. */
#if defined(ultrix)
char *strdup(const char *);
#endif
/* Systems lacking malloc() definition in <stdlib.h>. */
#if defined(ultrix) || defined(hcx)
void *malloc(size_t arg);
void free(void *ptr);
#endif
#ifndef HAVE_INET_ATON
int inet_aton(const char *cp, struct in_addr *ia);
#endif
#include <fcntl.h>
#ifndef NO_UNIX_SOCKETS
#include <sys/un.h>
#endif
#include <signal.h>
#ifdef HAVE_SYS_SIGNAL_H
# include <sys/signal.h>
#endif
#ifndef _WIN32
#include <sys/socket.h>
#endif
#if defined(HAVE_SYS_IOCTL_H)
# include <sys/ioctl.h>
#endif
#ifdef HAVE_SYS_SELECT_H
# include <sys/select.h>
#endif
#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
#ifdef HAVE_SYS_FILIO_H
# include <sys/filio.h>
#endif
#ifdef USE_PPP
#include <ppp/slirppp.h>
#endif
#ifdef __STDC__
#include <stdarg.h>
#else
#include <varargs.h>
#endif
#include <sys/stat.h>
/* Avoid conflicting with the libc insque() and remque(), which
have different prototypes. */
#define insque slirp_insque
#define remque slirp_remque
#ifdef HAVE_SYS_STROPTS_H
#include <sys/stropts.h>
#endif
#include "debug.h"
#include "libslirp.h"
#include "ip.h"
#include "tcp.h"
#include "tcp_timer.h"
#include "tcp_var.h"
#include "tcpip.h"
#include "udp.h"
#include "mbuf.h"
#include "sbuf.h"
#include "socket.h"
#include "if.h"
#include "main.h"
#include "misc.h"
#ifdef USE_PPP
#include "ppp/pppd.h"
#include "ppp/ppp.h"
#endif
#include "bootp.h"
#include "tftp.h"
struct Slirp {
/* virtual network configuration */
struct in_addr vnetwork_addr;
struct in_addr vnetwork_mask;
struct in_addr vhost_addr;
struct in_addr vdhcp_startaddr;
struct in_addr vnameserver_addr;
/* ARP cache for the guest IP addresses (XXX: allow many entries) */
uint8_t client_ethaddr[6];
struct in_addr client_ipaddr;
char client_hostname[33];
int restricted;
struct timeval tt;
struct ex_list *exec_list;
/* mbuf states */
struct mbuf m_freelist, m_usedlist;
int mbuf_alloced;
/* if states */
int if_queued; /* number of packets queued so far */
struct mbuf if_fastq; /* fast queue (for interactive data) */
struct mbuf if_batchq; /* queue for non-interactive data */
struct mbuf *next_m; /* pointer to next mbuf to output */
/* ip states */
struct ipq ipq; /* ip reass. queue */
uint16_t ip_id; /* ip packet ctr, for ids */
/* bootp/dhcp states */
BOOTPClient bootp_clients[NB_BOOTP_CLIENTS];
char *bootp_filename;
/* tcp states */
struct socket tcb;
struct socket *tcp_last_so;
tcp_seq tcp_iss; /* tcp initial send seq # */
uint32_t tcp_now; /* for RFC 1323 timestamps */
/* udp states */
struct socket udb;
struct socket *udp_last_so;
/* tftp states */
char *tftp_prefix;
struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX];
void *opaque;
};
extern Slirp *slirp_instance;
#ifndef NULL
#define NULL (void *)0
#endif
#ifndef FULL_BOLT
void if_start(Slirp *);
#else
void if_start(struct ttys *);
#endif
#ifndef HAVE_STRERROR
char *strerror(int error);
#endif
#ifndef HAVE_INDEX
char *index(const char *, int);
#endif
#ifndef HAVE_GETHOSTID
long gethostid(void);
#endif
void lprint(const char *, ...) __attribute__((format(printf, 1, 2)));
#ifndef _WIN32
#include <netdb.h>
#endif
#define DEFAULT_BAUD 115200
#define SO_OPTIONS DO_KEEPALIVE
#define TCP_MAXIDLE (TCPTV_KEEPCNT * TCPTV_KEEPINTVL)
/* cksum.c */
int cksum(struct mbuf *m, int len);
/* if.c */
void if_init(Slirp *);
void if_output(struct socket *, struct mbuf *);
/* ip_input.c */
void ip_init(Slirp *);
void ip_input(struct mbuf *);
void ip_slowtimo(Slirp *);
void ip_stripoptions(register struct mbuf *, struct mbuf *);
/* ip_output.c */
int ip_output(struct socket *, struct mbuf *);
/* tcp_input.c */
void tcp_input(register struct mbuf *, int, struct socket *);
int tcp_mss(register struct tcpcb *, u_int);
/* tcp_output.c */
int tcp_output(register struct tcpcb *);
void tcp_setpersist(register struct tcpcb *);
/* tcp_subr.c */
void tcp_init(Slirp *);
void tcp_template(struct tcpcb *);
void tcp_respond(struct tcpcb *, register struct tcpiphdr *, register struct mbuf *, tcp_seq, tcp_seq, int);
struct tcpcb * tcp_newtcpcb(struct socket *);
struct tcpcb * tcp_close(register struct tcpcb *);
void tcp_sockclosed(struct tcpcb *);
int tcp_fconnect(struct socket *);
void tcp_connect(struct socket *);
int tcp_attach(struct socket *);
uint8_t tcp_tos(struct socket *);
int tcp_emu(struct socket *, struct mbuf *);
int tcp_ctl(struct socket *);
struct tcpcb *tcp_drop(struct tcpcb *tp, int err);
#ifdef USE_PPP
#define MIN_MRU MINMRU
#define MAX_MRU MAXMRU
#else
#define MIN_MRU 128
#define MAX_MRU 16384
#endif
#ifndef _WIN32
#define min(x,y) ((x) < (y) ? (x) : (y))
#define max(x,y) ((x) > (y) ? (x) : (y))
#endif
#ifdef _WIN32
#undef errno
#define errno (WSAGetLastError())
#endif
#endif

188
slirp/slirp_config.h Normal file
View file

@ -0,0 +1,188 @@
/*
* User definable configuration options
*/
/* Define if you want the connection to be probed */
/* XXX Not working yet, so ignore this for now */
#undef PROBE_CONN
/* Define to 1 if you want KEEPALIVE timers */
#define DO_KEEPALIVE 0
/* Define to MAX interfaces you expect to use at once */
/* MAX_INTERFACES determines the max. TOTAL number of interfaces (SLIP and PPP) */
/* MAX_PPP_INTERFACES determines max. number of PPP interfaces */
#define MAX_INTERFACES 1
#define MAX_PPP_INTERFACES 1
/* Define if you want slirp's socket in /tmp */
/* XXXXXX Do this in ./configure */
#undef USE_TMPSOCKET
/* Define if you want slirp to use cfsetXspeed() on the terminal */
#undef DO_CFSETSPEED
/* Define this if you want slirp to write to the tty as fast as it can */
/* This should only be set if you are using load-balancing, slirp does a */
/* pretty good job on single modems already, and seting this will make */
/* interactive sessions less responsive */
/* XXXXX Talk about having fast modem as unit 0 */
#undef FULL_BOLT
/*
* Define if you want slirp to use less CPU
* You will notice a small lag in interactive sessions, but it's not that bad
* Things like Netscape/ftp/etc. are completely unaffected
* This is mainly for sysadmins who have many slirp users
*/
#undef USE_LOWCPU
/* Define this if your compiler doesn't like prototypes */
#ifndef __STDC__
#define NO_PROTOTYPES
#endif
/*********************************************************/
/*
* Autoconf defined configuration options
* You shouldn't need to touch any of these
*/
/* Ignore this */
#undef DUMMY_PPP
/* Define if you have unistd.h */
#define HAVE_UNISTD_H
/* Define if you have stdlib.h */
#define HAVE_STDLIB_H
/* Define if you have sys/ioctl.h */
#undef HAVE_SYS_IOCTL_H
#ifndef _WIN32
#define HAVE_SYS_IOCTL_H
#endif
/* Define if you have sys/filio.h */
#undef HAVE_SYS_FILIO_H
#ifdef __APPLE__
#define HAVE_SYS_FILIO_H
#endif
/* Define if you have strerror */
#define HAVE_STRERROR
/* Define if you have strdup() */
#define HAVE_STRDUP
/* Define according to how time.h should be included */
#define TIME_WITH_SYS_TIME 0
#undef HAVE_SYS_TIME_H
/* Define if you have sys/bitypes.h */
#undef HAVE_SYS_BITYPES_H
/* Define if the machine is big endian */
//#undef HOST_WORDS_BIGENDIAN
/* Define if you have readv */
#undef HAVE_READV
/* Define if iovec needs to be declared */
#undef DECLARE_IOVEC
#ifdef _WIN32
#define DECLARE_IOVEC
#endif
/* Define if you have a POSIX.1 sys/wait.h */
#undef HAVE_SYS_WAIT_H
/* Define if you have sys/select.h */
#undef HAVE_SYS_SELECT_H
#ifndef _WIN32
#define HAVE_SYS_SELECT_H
#endif
/* Define if you have strings.h */
#define HAVE_STRING_H
/* Define if you have arpa/inet.h */
#undef HAVE_ARPA_INET_H
#ifndef _WIN32
#define HAVE_ARPA_INET_H
#endif
/* Define if you have sys/signal.h */
#undef HAVE_SYS_SIGNAL_H
/* Define if you have sys/stropts.h */
#undef HAVE_SYS_STROPTS_H
/* Define to whatever your compiler thinks inline should be */
//#define inline inline
/* Define to whatever your compiler thinks const should be */
//#define const const
/* Define if your compiler doesn't like prototypes */
#undef NO_PROTOTYPES
/* Define to sizeof(char) */
#define SIZEOF_CHAR 1
/* Define to sizeof(short) */
#define SIZEOF_SHORT 2
/* Define to sizeof(int) */
#define SIZEOF_INT 4
/* Define to sizeof(char *) */
#define SIZEOF_CHAR_P (HOST_LONG_BITS / 8)
/* Define if you have random() */
#undef HAVE_RANDOM
/* Define if you have srandom() */
#undef HAVE_SRANDOM
/* Define if you have inet_aton */
#undef HAVE_INET_ATON
#ifndef _WIN32
#define HAVE_INET_ATON
#endif
/* Define if you have setenv */
#undef HAVE_SETENV
/* Define if you have index() */
#define HAVE_INDEX
/* Define if you have bcmp() */
#undef HAVE_BCMP
/* Define if you have drand48 */
#undef HAVE_DRAND48
/* Define if you have memmove */
#define HAVE_MEMMOVE
/* Define if you have gethostid */
#define HAVE_GETHOSTID
/* Define if you DON'T have unix-domain sockets */
#undef NO_UNIX_SOCKETS
#ifdef _WIN32
#define NO_UNIX_SOCKETS
#endif
/* Define if you have revoke() */
#undef HAVE_REVOKE
/* Define if you have the sysv method of opening pty's (/dev/ptmx, etc.) */
#undef HAVE_GRANTPT
/* Define if you have fchmod */
#undef HAVE_FCHMOD
/* Define if you have <sys/type32.h> */
#undef HAVE_SYS_TYPES32_H

722
slirp/socket.c Normal file
View file

@ -0,0 +1,722 @@
/*
* Copyright (c) 1995 Danny Gasparovski.
*
* Please read the file COPYRIGHT for the
* terms and conditions of the copyright.
*/
#include "slirp.h"
#include "ip_icmp.h"
static void sofcantrcvmore(struct socket *so);
static void sofcantsendmore(struct socket *so);
struct socket *
solookup(struct socket *head, struct in_addr laddr, u_int lport,
struct in_addr faddr, u_int fport)
{
struct socket *so;
for (so = head->so_next; so != head; so = so->so_next) {
if (so->so_lport == lport &&
so->so_laddr.s_addr == laddr.s_addr &&
so->so_faddr.s_addr == faddr.s_addr &&
so->so_fport == fport)
break;
}
if (so == head)
return (struct socket *)NULL;
return so;
}
/*
* Create a new socket, initialise the fields
* It is the responsibility of the caller to
* insque() it into the correct linked-list
*/
struct socket *
socreate(Slirp *slirp)
{
struct socket *so;
so = (struct socket *)malloc(sizeof(struct socket));
if(so) {
memset(so, 0, sizeof(struct socket));
so->so_state = SS_NOFDREF;
so->s = -1;
so->slirp = slirp;
}
return(so);
}
/*
* remque and free a socket, clobber cache
*/
void
sofree(struct socket *so)
{
Slirp *slirp = so->slirp;
if (so->so_emu==EMU_RSH && so->extra) {
sofree(so->extra);
so->extra=NULL;
}
if (so == slirp->tcp_last_so) {
slirp->tcp_last_so = &slirp->tcb;
} else if (so == slirp->udp_last_so) {
slirp->udp_last_so = &slirp->udb;
}
m_free(so->so_m);
if(so->so_next && so->so_prev)
remque(so); /* crashes if so is not in a queue */
free(so);
}
size_t sopreprbuf(struct socket *so, struct iovec *iov, int *np)
{
int n, lss, total;
struct sbuf *sb = &so->so_snd;
int len = sb->sb_datalen - sb->sb_cc;
int mss = so->so_tcpcb->t_maxseg;
DEBUG_CALL("sopreprbuf");
DEBUG_ARG("so = %lx", (long )so);
if (len <= 0)
return 0;
iov[0].iov_base = sb->sb_wptr;
iov[1].iov_base = NULL;
iov[1].iov_len = 0;
if (sb->sb_wptr < sb->sb_rptr) {
iov[0].iov_len = sb->sb_rptr - sb->sb_wptr;
/* Should never succeed, but... */
if (iov[0].iov_len > len)
iov[0].iov_len = len;
if (iov[0].iov_len > mss)
iov[0].iov_len -= iov[0].iov_len%mss;
n = 1;
} else {
iov[0].iov_len = (sb->sb_data + sb->sb_datalen) - sb->sb_wptr;
/* Should never succeed, but... */
if (iov[0].iov_len > len) iov[0].iov_len = len;
len -= iov[0].iov_len;
if (len) {
iov[1].iov_base = sb->sb_data;
iov[1].iov_len = sb->sb_rptr - sb->sb_data;
if(iov[1].iov_len > len)
iov[1].iov_len = len;
total = iov[0].iov_len + iov[1].iov_len;
if (total > mss) {
lss = total%mss;
if (iov[1].iov_len > lss) {
iov[1].iov_len -= lss;
n = 2;
} else {
lss -= iov[1].iov_len;
iov[0].iov_len -= lss;
n = 1;
}
} else
n = 2;
} else {
if (iov[0].iov_len > mss)
iov[0].iov_len -= iov[0].iov_len%mss;
n = 1;
}
}
if (np)
*np = n;
return iov[0].iov_len + (n - 1) * iov[1].iov_len;
}
/*
* Read from so's socket into sb_snd, updating all relevant sbuf fields
* NOTE: This will only be called if it is select()ed for reading, so
* a read() of 0 (or less) means it's disconnected
*/
int
soread(struct socket *so)
{
int n, nn;
struct sbuf *sb = &so->so_snd;
struct iovec iov[2];
DEBUG_CALL("soread");
DEBUG_ARG("so = %lx", (long )so);
/*
* No need to check if there's enough room to read.
* soread wouldn't have been called if there weren't
*/
sopreprbuf(so, iov, &n);
#ifdef HAVE_READV
nn = readv(so->s, (struct iovec *)iov, n);
DEBUG_MISC((dfd, " ... read nn = %d bytes\n", nn));
#else
nn = recv(so->s, iov[0].iov_base, iov[0].iov_len,0);
#endif
if (nn <= 0) {
if (nn < 0 && (errno == EINTR || errno == EAGAIN))
return 0;
else {
DEBUG_MISC((dfd, " --- soread() disconnected, nn = %d, errno = %d-%s\n", nn, errno,strerror(errno)));
sofcantrcvmore(so);
tcp_sockclosed(sototcpcb(so));
return -1;
}
}
#ifndef HAVE_READV
/*
* If there was no error, try and read the second time round
* We read again if n = 2 (ie, there's another part of the buffer)
* and we read as much as we could in the first read
* We don't test for <= 0 this time, because there legitimately
* might not be any more data (since the socket is non-blocking),
* a close will be detected on next iteration.
* A return of -1 wont (shouldn't) happen, since it didn't happen above
*/
if (n == 2 && nn == iov[0].iov_len) {
int ret;
ret = recv(so->s, iov[1].iov_base, iov[1].iov_len,0);
if (ret > 0)
nn += ret;
}
DEBUG_MISC((dfd, " ... read nn = %d bytes\n", nn));
#endif
/* Update fields */
sb->sb_cc += nn;
sb->sb_wptr += nn;
if (sb->sb_wptr >= (sb->sb_data + sb->sb_datalen))
sb->sb_wptr -= sb->sb_datalen;
return nn;
}
int soreadbuf(struct socket *so, const char *buf, int size)
{
int n, nn, copy = size;
struct sbuf *sb = &so->so_snd;
struct iovec iov[2];
DEBUG_CALL("soreadbuf");
DEBUG_ARG("so = %lx", (long )so);
/*
* No need to check if there's enough room to read.
* soread wouldn't have been called if there weren't
*/
if (sopreprbuf(so, iov, &n) < size)
goto err;
nn = min(iov[0].iov_len, copy);
memcpy(iov[0].iov_base, buf, nn);
copy -= nn;
buf += nn;
if (copy == 0)
goto done;
memcpy(iov[1].iov_base, buf, copy);
done:
/* Update fields */
sb->sb_cc += size;
sb->sb_wptr += size;
if (sb->sb_wptr >= (sb->sb_data + sb->sb_datalen))
sb->sb_wptr -= sb->sb_datalen;
return size;
err:
sofcantrcvmore(so);
tcp_sockclosed(sototcpcb(so));
fprintf(stderr, "soreadbuf buffer to small");
return -1;
}
/*
* Get urgent data
*
* When the socket is created, we set it SO_OOBINLINE,
* so when OOB data arrives, we soread() it and everything
* in the send buffer is sent as urgent data
*/
void
sorecvoob(struct socket *so)
{
struct tcpcb *tp = sototcpcb(so);
DEBUG_CALL("sorecvoob");
DEBUG_ARG("so = %lx", (long)so);
/*
* We take a guess at how much urgent data has arrived.
* In most situations, when urgent data arrives, the next
* read() should get all the urgent data. This guess will
* be wrong however if more data arrives just after the
* urgent data, or the read() doesn't return all the
* urgent data.
*/
soread(so);
tp->snd_up = tp->snd_una + so->so_snd.sb_cc;
tp->t_force = 1;
tcp_output(tp);
tp->t_force = 0;
}
/*
* Send urgent data
* There's a lot duplicated code here, but...
*/
int
sosendoob(struct socket *so)
{
struct sbuf *sb = &so->so_rcv;
char buff[2048]; /* XXX Shouldn't be sending more oob data than this */
int n, len;
DEBUG_CALL("sosendoob");
DEBUG_ARG("so = %lx", (long)so);
DEBUG_ARG("sb->sb_cc = %d", sb->sb_cc);
if (so->so_urgc > 2048)
so->so_urgc = 2048; /* XXXX */
if (sb->sb_rptr < sb->sb_wptr) {
/* We can send it directly */
n = slirp_send(so, sb->sb_rptr, so->so_urgc, (MSG_OOB)); /* |MSG_DONTWAIT)); */
so->so_urgc -= n;
DEBUG_MISC((dfd, " --- sent %d bytes urgent data, %d urgent bytes left\n", n, so->so_urgc));
} else {
/*
* Since there's no sendv or sendtov like writev,
* we must copy all data to a linear buffer then
* send it all
*/
len = (sb->sb_data + sb->sb_datalen) - sb->sb_rptr;
if (len > so->so_urgc) len = so->so_urgc;
memcpy(buff, sb->sb_rptr, len);
so->so_urgc -= len;
if (so->so_urgc) {
n = sb->sb_wptr - sb->sb_data;
if (n > so->so_urgc) n = so->so_urgc;
memcpy((buff + len), sb->sb_data, n);
so->so_urgc -= n;
len += n;
}
n = slirp_send(so, buff, len, (MSG_OOB)); /* |MSG_DONTWAIT)); */
#ifdef DEBUG
if (n != len)
DEBUG_ERROR((dfd, "Didn't send all data urgently XXXXX\n"));
#endif
DEBUG_MISC((dfd, " ---2 sent %d bytes urgent data, %d urgent bytes left\n", n, so->so_urgc));
}
sb->sb_cc -= n;
sb->sb_rptr += n;
if (sb->sb_rptr >= (sb->sb_data + sb->sb_datalen))
sb->sb_rptr -= sb->sb_datalen;
return n;
}
/*
* Write data from so_rcv to so's socket,
* updating all sbuf field as necessary
*/
int
sowrite(struct socket *so)
{
int n,nn;
struct sbuf *sb = &so->so_rcv;
int len = sb->sb_cc;
struct iovec iov[2];
DEBUG_CALL("sowrite");
DEBUG_ARG("so = %lx", (long)so);
if (so->so_urgc) {
sosendoob(so);
if (sb->sb_cc == 0)
return 0;
}
/*
* No need to check if there's something to write,
* sowrite wouldn't have been called otherwise
*/
iov[0].iov_base = sb->sb_rptr;
iov[1].iov_base = NULL;
iov[1].iov_len = 0;
if (sb->sb_rptr < sb->sb_wptr) {
iov[0].iov_len = sb->sb_wptr - sb->sb_rptr;
/* Should never succeed, but... */
if (iov[0].iov_len > len) iov[0].iov_len = len;
n = 1;
} else {
iov[0].iov_len = (sb->sb_data + sb->sb_datalen) - sb->sb_rptr;
if (iov[0].iov_len > len) iov[0].iov_len = len;
len -= iov[0].iov_len;
if (len) {
iov[1].iov_base = sb->sb_data;
iov[1].iov_len = sb->sb_wptr - sb->sb_data;
if (iov[1].iov_len > len) iov[1].iov_len = len;
n = 2;
} else
n = 1;
}
/* Check if there's urgent data to send, and if so, send it */
#ifdef HAVE_READV
nn = writev(so->s, (const struct iovec *)iov, n);
DEBUG_MISC((dfd, " ... wrote nn = %d bytes\n", nn));
#else
nn = slirp_send(so, iov[0].iov_base, iov[0].iov_len,0);
#endif
/* This should never happen, but people tell me it does *shrug* */
if (nn < 0 && (errno == EAGAIN || errno == EINTR))
return 0;
if (nn <= 0) {
DEBUG_MISC((dfd, " --- sowrite disconnected, so->so_state = %x, errno = %d\n",
so->so_state, errno));
sofcantsendmore(so);
tcp_sockclosed(sototcpcb(so));
return -1;
}
#ifndef HAVE_READV
if (n == 2 && nn == iov[0].iov_len) {
int ret;
ret = slirp_send(so, iov[1].iov_base, iov[1].iov_len,0);
if (ret > 0)
nn += ret;
}
DEBUG_MISC((dfd, " ... wrote nn = %d bytes\n", nn));
#endif
/* Update sbuf */
sb->sb_cc -= nn;
sb->sb_rptr += nn;
if (sb->sb_rptr >= (sb->sb_data + sb->sb_datalen))
sb->sb_rptr -= sb->sb_datalen;
/*
* If in DRAIN mode, and there's no more data, set
* it CANTSENDMORE
*/
if ((so->so_state & SS_FWDRAIN) && sb->sb_cc == 0)
sofcantsendmore(so);
return nn;
}
/*
* recvfrom() a UDP socket
*/
void
sorecvfrom(struct socket *so)
{
struct sockaddr_in addr;
socklen_t addrlen = sizeof(struct sockaddr_in);
DEBUG_CALL("sorecvfrom");
DEBUG_ARG("so = %lx", (long)so);
if (so->so_type == IPPROTO_ICMP) { /* This is a "ping" reply */
char buff[256];
int len;
len = recvfrom(so->s, buff, 256, 0,
(struct sockaddr *)&addr, &addrlen);
/* XXX Check if reply is "correct"? */
if(len == -1 || len == 0) {
u_char code=ICMP_UNREACH_PORT;
if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST;
else if(errno == ENETUNREACH) code=ICMP_UNREACH_NET;
DEBUG_MISC((dfd," udp icmp rx errno = %d-%s\n",
errno,strerror(errno)));
icmp_error(so->so_m, ICMP_UNREACH,code, 0,strerror(errno));
} else {
icmp_reflect(so->so_m);
so->so_m = NULL; /* Don't m_free() it again! */
}
/* No need for this socket anymore, udp_detach it */
udp_detach(so);
} else { /* A "normal" UDP packet */
struct mbuf *m;
int len;
#ifdef _WIN32
unsigned long n;
#else
int n;
#endif
m = m_get(so->slirp);
if (!m) {
return;
}
m->m_data += IF_MAXLINKHDR;
/*
* XXX Shouldn't FIONREAD packets destined for port 53,
* but I don't know the max packet size for DNS lookups
*/
len = M_FREEROOM(m);
/* if (so->so_fport != htons(53)) { */
ioctlsocket(so->s, FIONREAD, &n);
if (n > len) {
n = (m->m_data - m->m_dat) + m->m_len + n + 1;
m_inc(m, n);
len = M_FREEROOM(m);
}
/* } */
m->m_len = recvfrom(so->s, m->m_data, len, 0,
(struct sockaddr *)&addr, &addrlen);
DEBUG_MISC((dfd, " did recvfrom %d, errno = %d-%s\n",
m->m_len, errno,strerror(errno)));
if(m->m_len<0) {
u_char code=ICMP_UNREACH_PORT;
if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST;
else if(errno == ENETUNREACH) code=ICMP_UNREACH_NET;
DEBUG_MISC((dfd," rx error, tx icmp ICMP_UNREACH:%i\n", code));
icmp_error(so->so_m, ICMP_UNREACH,code, 0,strerror(errno));
m_free(m);
} else {
/*
* Hack: domain name lookup will be used the most for UDP,
* and since they'll only be used once there's no need
* for the 4 minute (or whatever) timeout... So we time them
* out much quicker (10 seconds for now...)
*/
if (so->so_expire) {
if (so->so_fport == htons(53))
so->so_expire = curtime + SO_EXPIREFAST;
else
so->so_expire = curtime + SO_EXPIRE;
}
/*
* If this packet was destined for CTL_ADDR,
* make it look like that's where it came from, done by udp_output
*/
udp_output(so, m, &addr);
} /* rx error */
} /* if ping packet */
}
/*
* sendto() a socket
*/
int
sosendto(struct socket *so, struct mbuf *m)
{
Slirp *slirp = so->slirp;
int ret;
struct sockaddr_in addr;
DEBUG_CALL("sosendto");
DEBUG_ARG("so = %lx", (long)so);
DEBUG_ARG("m = %lx", (long)m);
addr.sin_family = AF_INET;
if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
slirp->vnetwork_addr.s_addr) {
/* It's an alias */
if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) {
if (get_dns_addr(&addr.sin_addr) < 0)
addr.sin_addr = loopback_addr;
} else {
addr.sin_addr = loopback_addr;
}
} else
addr.sin_addr = so->so_faddr;
addr.sin_port = so->so_fport;
DEBUG_MISC((dfd, " sendto()ing, addr.sin_port=%d, addr.sin_addr.s_addr=%.16s\n", ntohs(addr.sin_port), inet_ntoa(addr.sin_addr)));
/* Don't care what port we get */
ret = sendto(so->s, m->m_data, m->m_len, 0,
(struct sockaddr *)&addr, sizeof (struct sockaddr));
if (ret < 0)
return -1;
/*
* Kill the socket if there's no reply in 4 minutes,
* but only if it's an expirable socket
*/
if (so->so_expire)
so->so_expire = curtime + SO_EXPIRE;
so->so_state &= SS_PERSISTENT_MASK;
so->so_state |= SS_ISFCONNECTED; /* So that it gets select()ed */
return 0;
}
/*
* Listen for incoming TCP connections
*/
struct socket *
tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr,
u_int lport, int flags)
{
struct sockaddr_in addr;
struct socket *so;
int s, opt = 1;
socklen_t addrlen = sizeof(addr);
memset(&addr, 0, addrlen);
DEBUG_CALL("tcp_listen");
DEBUG_ARG("haddr = %x", haddr);
DEBUG_ARG("hport = %d", hport);
DEBUG_ARG("laddr = %x", laddr);
DEBUG_ARG("lport = %d", lport);
DEBUG_ARG("flags = %x", flags);
so = socreate(slirp);
if (!so) {
return NULL;
}
/* Don't tcp_attach... we don't need so_snd nor so_rcv */
if ((so->so_tcpcb = tcp_newtcpcb(so)) == NULL) {
free(so);
return NULL;
}
insque(so, &slirp->tcb);
/*
* SS_FACCEPTONCE sockets must time out.
*/
if (flags & SS_FACCEPTONCE)
so->so_tcpcb->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT*2;
so->so_state &= SS_PERSISTENT_MASK;
so->so_state |= (SS_FACCEPTCONN | flags);
so->so_lport = lport; /* Kept in network format */
so->so_laddr.s_addr = laddr; /* Ditto */
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = haddr;
addr.sin_port = hport;
if (((s = os_socket(AF_INET,SOCK_STREAM,0)) < 0) ||
(setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int)) < 0) ||
(bind(s,(struct sockaddr *)&addr, sizeof(addr)) < 0) ||
(listen(s,1) < 0)) {
int tmperrno = errno; /* Don't clobber the real reason we failed */
close(s);
sofree(so);
/* Restore the real errno */
#ifdef _WIN32
WSASetLastError(tmperrno);
#else
errno = tmperrno;
#endif
return NULL;
}
setsockopt(s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(int));
getsockname(s,(struct sockaddr *)&addr,&addrlen);
so->so_fport = addr.sin_port;
if (addr.sin_addr.s_addr == 0 || addr.sin_addr.s_addr == loopback_addr.s_addr)
so->so_faddr = slirp->vhost_addr;
else
so->so_faddr = addr.sin_addr;
so->s = s;
return so;
}
/*
* Various session state calls
* XXX Should be #define's
* The socket state stuff needs work, these often get call 2 or 3
* times each when only 1 was needed
*/
void
soisfconnecting(struct socket *so)
{
so->so_state &= ~(SS_NOFDREF|SS_ISFCONNECTED|SS_FCANTRCVMORE|
SS_FCANTSENDMORE|SS_FWDRAIN);
so->so_state |= SS_ISFCONNECTING; /* Clobber other states */
}
void
soisfconnected(struct socket *so)
{
so->so_state &= ~(SS_ISFCONNECTING|SS_FWDRAIN|SS_NOFDREF);
so->so_state |= SS_ISFCONNECTED; /* Clobber other states */
}
static void
sofcantrcvmore(struct socket *so)
{
if ((so->so_state & SS_NOFDREF) == 0) {
shutdown(so->s,0);
if(global_writefds) {
FD_CLR(so->s,global_writefds);
}
}
so->so_state &= ~(SS_ISFCONNECTING);
if (so->so_state & SS_FCANTSENDMORE) {
so->so_state &= SS_PERSISTENT_MASK;
so->so_state |= SS_NOFDREF; /* Don't select it */
} else {
so->so_state |= SS_FCANTRCVMORE;
}
}
static void
sofcantsendmore(struct socket *so)
{
if ((so->so_state & SS_NOFDREF) == 0) {
shutdown(so->s,1); /* send FIN to fhost */
if (global_readfds) {
FD_CLR(so->s,global_readfds);
}
if (global_xfds) {
FD_CLR(so->s,global_xfds);
}
}
so->so_state &= ~(SS_ISFCONNECTING);
if (so->so_state & SS_FCANTRCVMORE) {
so->so_state &= SS_PERSISTENT_MASK;
so->so_state |= SS_NOFDREF; /* as above */
} else {
so->so_state |= SS_FCANTSENDMORE;
}
}
/*
* Set write drain mode
* Set CANTSENDMORE once all data has been write()n
*/
void
sofwdrain(struct socket *so)
{
if (so->so_rcv.sb_cc)
so->so_state |= SS_FWDRAIN;
else
sofcantsendmore(so);
}

95
slirp/socket.h Normal file
View file

@ -0,0 +1,95 @@
/*
* Copyright (c) 1995 Danny Gasparovski.
*
* Please read the file COPYRIGHT for the
* terms and conditions of the copyright.
*/
#ifndef _SLIRP_SOCKET_H_
#define _SLIRP_SOCKET_H_
#define SO_EXPIRE 240000
#define SO_EXPIREFAST 10000
/*
* Our socket structure
*/
struct socket {
struct socket *so_next,*so_prev; /* For a linked list of sockets */
int s; /* The actual socket */
Slirp *slirp; /* managing slirp instance */
/* XXX union these with not-yet-used sbuf params */
struct mbuf *so_m; /* Pointer to the original SYN packet,
* for non-blocking connect()'s, and
* PING reply's */
struct tcpiphdr *so_ti; /* Pointer to the original ti within
* so_mconn, for non-blocking connections */
int so_urgc;
struct in_addr so_faddr; /* foreign host table entry */
struct in_addr so_laddr; /* local host table entry */
uint16_t so_fport; /* foreign port */
uint16_t so_lport; /* local port */
uint8_t so_iptos; /* Type of service */
uint8_t so_emu; /* Is the socket emulated? */
u_char so_type; /* Type of socket, UDP or TCP */
int so_state; /* internal state flags SS_*, below */
struct tcpcb *so_tcpcb; /* pointer to TCP protocol control block */
u_int so_expire; /* When the socket will expire */
int so_queued; /* Number of packets queued from this socket */
int so_nqueued; /* Number of packets queued in a row
* Used to determine when to "downgrade" a session
* from fastq to batchq */
struct sbuf so_rcv; /* Receive buffer */
struct sbuf so_snd; /* Send buffer */
void * extra; /* Extra pointer */
};
/*
* Socket state bits. (peer means the host on the Internet,
* local host means the host on the other end of the modem)
*/
#define SS_NOFDREF 0x001 /* No fd reference */
#define SS_ISFCONNECTING 0x002 /* Socket is connecting to peer (non-blocking connect()'s) */
#define SS_ISFCONNECTED 0x004 /* Socket is connected to peer */
#define SS_FCANTRCVMORE 0x008 /* Socket can't receive more from peer (for half-closes) */
#define SS_FCANTSENDMORE 0x010 /* Socket can't send more to peer (for half-closes) */
#define SS_FWDRAIN 0x040 /* We received a FIN, drain data and set SS_FCANTSENDMORE */
#define SS_CTL 0x080
#define SS_FACCEPTCONN 0x100 /* Socket is accepting connections from a host on the internet */
#define SS_FACCEPTONCE 0x200 /* If set, the SS_FACCEPTCONN socket will die after one accept */
#define SS_PERSISTENT_MASK 0xf000 /* Unremovable state bits */
#define SS_HOSTFWD 0x1000 /* Socket describes host->guest forwarding */
#define SS_INCOMING 0x2000 /* Connection was initiated by a host on the internet */
struct socket * solookup(struct socket *, struct in_addr, u_int, struct in_addr, u_int);
struct socket * socreate(Slirp *);
void sofree(struct socket *);
int soread(struct socket *);
void sorecvoob(struct socket *);
int sosendoob(struct socket *);
int sowrite(struct socket *);
void sorecvfrom(struct socket *);
int sosendto(struct socket *, struct mbuf *);
struct socket * tcp_listen(Slirp *, uint32_t, u_int, uint32_t, u_int,
int);
void soisfconnecting(register struct socket *);
void soisfconnected(register struct socket *);
void sofwdrain(struct socket *);
struct iovec; /* For win32 */
size_t sopreprbuf(struct socket *so, struct iovec *iov, int *np);
int soreadbuf(struct socket *so, const char *buf, int size);
#endif /* _SOCKET_H_ */

164
slirp/tcp.h Normal file
View file

@ -0,0 +1,164 @@
/*
* Copyright (c) 1982, 1986, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)tcp.h 8.1 (Berkeley) 6/10/93
* tcp.h,v 1.3 1994/08/21 05:27:34 paul Exp
*/
#ifndef _TCP_H_
#define _TCP_H_
typedef uint32_t tcp_seq;
#define PR_SLOWHZ 2 /* 2 slow timeouts per second (approx) */
#define PR_FASTHZ 5 /* 5 fast timeouts per second (not important) */
#define TCP_SNDSPACE 8192
#define TCP_RCVSPACE 8192
/*
* TCP header.
* Per RFC 793, September, 1981.
*/
struct tcphdr {
uint16_t th_sport; /* source port */
uint16_t th_dport; /* destination port */
tcp_seq th_seq; /* sequence number */
tcp_seq th_ack; /* acknowledgement number */
#ifdef HOST_WORDS_BIGENDIAN
u_int th_off:4, /* data offset */
th_x2:4; /* (unused) */
#else
u_int th_x2:4, /* (unused) */
th_off:4; /* data offset */
#endif
uint8_t th_flags;
#define TH_FIN 0x01
#define TH_SYN 0x02
#define TH_RST 0x04
#define TH_PUSH 0x08
#define TH_ACK 0x10
#define TH_URG 0x20
uint16_t th_win; /* window */
uint16_t th_sum; /* checksum */
uint16_t th_urp; /* urgent pointer */
};
#include "tcp_var.h"
#define TCPOPT_EOL 0
#define TCPOPT_NOP 1
#define TCPOPT_MAXSEG 2
#define TCPOLEN_MAXSEG 4
#define TCPOPT_WINDOW 3
#define TCPOLEN_WINDOW 3
#define TCPOPT_SACK_PERMITTED 4 /* Experimental */
#define TCPOLEN_SACK_PERMITTED 2
#define TCPOPT_SACK 5 /* Experimental */
#define TCPOPT_TIMESTAMP 8
#define TCPOLEN_TIMESTAMP 10
#define TCPOLEN_TSTAMP_APPA (TCPOLEN_TIMESTAMP+2) /* appendix A */
#define TCPOPT_TSTAMP_HDR \
(TCPOPT_NOP<<24|TCPOPT_NOP<<16|TCPOPT_TIMESTAMP<<8|TCPOLEN_TIMESTAMP)
/*
* Default maximum segment size for TCP.
* With an IP MSS of 576, this is 536,
* but 512 is probably more convenient.
* This should be defined as MIN(512, IP_MSS - sizeof (struct tcpiphdr)).
*
* We make this 1460 because we only care about Ethernet in the qemu context.
*/
#define TCP_MSS 1460
#define TCP_MAXWIN 65535 /* largest value for (unscaled) window */
#define TCP_MAX_WINSHIFT 14 /* maximum window shift */
/*
* User-settable options (used with setsockopt).
*
* We don't use the system headers on unix because we have conflicting
* local structures. We can't avoid the system definitions on Windows,
* so we undefine them.
*/
#undef TCP_NODELAY
#define TCP_NODELAY 0x01 /* don't delay send to coalesce packets */
#undef TCP_MAXSEG
/*
* TCP FSM state definitions.
* Per RFC793, September, 1981.
*/
#define TCP_NSTATES 11
#define TCPS_CLOSED 0 /* closed */
#define TCPS_LISTEN 1 /* listening for connection */
#define TCPS_SYN_SENT 2 /* active, have sent syn */
#define TCPS_SYN_RECEIVED 3 /* have send and received syn */
/* states < TCPS_ESTABLISHED are those where connections not established */
#define TCPS_ESTABLISHED 4 /* established */
#define TCPS_CLOSE_WAIT 5 /* rcvd fin, waiting for close */
/* states > TCPS_CLOSE_WAIT are those where user has closed */
#define TCPS_FIN_WAIT_1 6 /* have closed, sent fin */
#define TCPS_CLOSING 7 /* closed xchd FIN; await FIN ACK */
#define TCPS_LAST_ACK 8 /* had fin and close; await FIN ACK */
/* states > TCPS_CLOSE_WAIT && < TCPS_FIN_WAIT_2 await ACK of FIN */
#define TCPS_FIN_WAIT_2 9 /* have closed, fin is acked */
#define TCPS_TIME_WAIT 10 /* in 2*msl quiet wait after close */
#define TCPS_HAVERCVDSYN(s) ((s) >= TCPS_SYN_RECEIVED)
#define TCPS_HAVEESTABLISHED(s) ((s) >= TCPS_ESTABLISHED)
#define TCPS_HAVERCVDFIN(s) ((s) >= TCPS_TIME_WAIT)
/*
* TCP sequence numbers are 32 bit integers operated
* on with modular arithmetic. These macros can be
* used to compare such integers.
*/
#define SEQ_LT(a,b) ((int)((a)-(b)) < 0)
#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
#define SEQ_GT(a,b) ((int)((a)-(b)) > 0)
#define SEQ_GEQ(a,b) ((int)((a)-(b)) >= 0)
/*
* Macros to initialize tcp sequence numbers for
* send and receive from initial send and receive
* sequence numbers.
*/
#define tcp_rcvseqinit(tp) \
(tp)->rcv_adv = (tp)->rcv_nxt = (tp)->irs + 1
#define tcp_sendseqinit(tp) \
(tp)->snd_una = (tp)->snd_nxt = (tp)->snd_max = (tp)->snd_up = (tp)->iss
#define TCP_ISSINCR (125*1024) /* increment for tcp_iss each second */
#endif

1487
slirp/tcp_input.c Normal file

File diff suppressed because it is too large Load diff

492
slirp/tcp_output.c Normal file
View file

@ -0,0 +1,492 @@
/*
* Copyright (c) 1982, 1986, 1988, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)tcp_output.c 8.3 (Berkeley) 12/30/93
* tcp_output.c,v 1.3 1994/09/15 10:36:55 davidg Exp
*/
/*
* Changes and additions relating to SLiRP
* Copyright (c) 1995 Danny Gasparovski.
*
* Please read the file COPYRIGHT for the
* terms and conditions of the copyright.
*/
#include "slirp.h"
static const u_char tcp_outflags[TCP_NSTATES] = {
TH_RST|TH_ACK, 0, TH_SYN, TH_SYN|TH_ACK,
TH_ACK, TH_ACK, TH_FIN|TH_ACK, TH_FIN|TH_ACK,
TH_FIN|TH_ACK, TH_ACK, TH_ACK,
};
#define MAX_TCPOPTLEN 32 /* max # bytes that go in options */
/*
* Tcp output routine: figure out what should be sent and send it.
*/
int
tcp_output(struct tcpcb *tp)
{
register struct socket *so = tp->t_socket;
register long len, win;
int off, flags, error;
register struct mbuf *m;
register struct tcpiphdr *ti;
u_char opt[MAX_TCPOPTLEN];
unsigned optlen, hdrlen;
int idle, sendalot;
DEBUG_CALL("tcp_output");
DEBUG_ARG("tp = %lx", (long )tp);
/*
* Determine length of data that should be transmitted,
* and flags that will be used.
* If there is some data or critical controls (SYN, RST)
* to send, then transmit; otherwise, investigate further.
*/
idle = (tp->snd_max == tp->snd_una);
if (idle && tp->t_idle >= tp->t_rxtcur)
/*
* We have been idle for "a while" and no acks are
* expected to clock out any data we send --
* slow start to get ack "clock" running again.
*/
tp->snd_cwnd = tp->t_maxseg;
again:
sendalot = 0;
off = tp->snd_nxt - tp->snd_una;
win = min(tp->snd_wnd, tp->snd_cwnd);
flags = tcp_outflags[tp->t_state];
DEBUG_MISC((dfd, " --- tcp_output flags = 0x%x\n",flags));
/*
* If in persist timeout with window of 0, send 1 byte.
* Otherwise, if window is small but nonzero
* and timer expired, we will send what we can
* and go to transmit state.
*/
if (tp->t_force) {
if (win == 0) {
/*
* If we still have some data to send, then
* clear the FIN bit. Usually this would
* happen below when it realizes that we
* aren't sending all the data. However,
* if we have exactly 1 byte of unset data,
* then it won't clear the FIN bit below,
* and if we are in persist state, we wind
* up sending the packet without recording
* that we sent the FIN bit.
*
* We can't just blindly clear the FIN bit,
* because if we don't have any more data
* to send then the probe will be the FIN
* itself.
*/
if (off < so->so_snd.sb_cc)
flags &= ~TH_FIN;
win = 1;
} else {
tp->t_timer[TCPT_PERSIST] = 0;
tp->t_rxtshift = 0;
}
}
len = min(so->so_snd.sb_cc, win) - off;
if (len < 0) {
/*
* If FIN has been sent but not acked,
* but we haven't been called to retransmit,
* len will be -1. Otherwise, window shrank
* after we sent into it. If window shrank to 0,
* cancel pending retransmit and pull snd_nxt
* back to (closed) window. We will enter persist
* state below. If the window didn't close completely,
* just wait for an ACK.
*/
len = 0;
if (win == 0) {
tp->t_timer[TCPT_REXMT] = 0;
tp->snd_nxt = tp->snd_una;
}
}
if (len > tp->t_maxseg) {
len = tp->t_maxseg;
sendalot = 1;
}
if (SEQ_LT(tp->snd_nxt + len, tp->snd_una + so->so_snd.sb_cc))
flags &= ~TH_FIN;
win = sbspace(&so->so_rcv);
/*
* Sender silly window avoidance. If connection is idle
* and can send all data, a maximum segment,
* at least a maximum default-size segment do it,
* or are forced, do it; otherwise don't bother.
* If peer's buffer is tiny, then send
* when window is at least half open.
* If retransmitting (possibly after persist timer forced us
* to send into a small window), then must resend.
*/
if (len) {
if (len == tp->t_maxseg)
goto send;
if ((1 || idle || tp->t_flags & TF_NODELAY) &&
len + off >= so->so_snd.sb_cc)
goto send;
if (tp->t_force)
goto send;
if (len >= tp->max_sndwnd / 2 && tp->max_sndwnd > 0)
goto send;
if (SEQ_LT(tp->snd_nxt, tp->snd_max))
goto send;
}
/*
* Compare available window to amount of window
* known to peer (as advertised window less
* next expected input). If the difference is at least two
* max size segments, or at least 50% of the maximum possible
* window, then want to send a window update to peer.
*/
if (win > 0) {
/*
* "adv" is the amount we can increase the window,
* taking into account that we are limited by
* TCP_MAXWIN << tp->rcv_scale.
*/
long adv = min(win, (long)TCP_MAXWIN << tp->rcv_scale) -
(tp->rcv_adv - tp->rcv_nxt);
if (adv >= (long) (2 * tp->t_maxseg))
goto send;
if (2 * adv >= (long) so->so_rcv.sb_datalen)
goto send;
}
/*
* Send if we owe peer an ACK.
*/
if (tp->t_flags & TF_ACKNOW)
goto send;
if (flags & (TH_SYN|TH_RST))
goto send;
if (SEQ_GT(tp->snd_up, tp->snd_una))
goto send;
/*
* If our state indicates that FIN should be sent
* and we have not yet done so, or we're retransmitting the FIN,
* then we need to send.
*/
if (flags & TH_FIN &&
((tp->t_flags & TF_SENTFIN) == 0 || tp->snd_nxt == tp->snd_una))
goto send;
/*
* TCP window updates are not reliable, rather a polling protocol
* using ``persist'' packets is used to insure receipt of window
* updates. The three ``states'' for the output side are:
* idle not doing retransmits or persists
* persisting to move a small or zero window
* (re)transmitting and thereby not persisting
*
* tp->t_timer[TCPT_PERSIST]
* is set when we are in persist state.
* tp->t_force
* is set when we are called to send a persist packet.
* tp->t_timer[TCPT_REXMT]
* is set when we are retransmitting
* The output side is idle when both timers are zero.
*
* If send window is too small, there is data to transmit, and no
* retransmit or persist is pending, then go to persist state.
* If nothing happens soon, send when timer expires:
* if window is nonzero, transmit what we can,
* otherwise force out a byte.
*/
if (so->so_snd.sb_cc && tp->t_timer[TCPT_REXMT] == 0 &&
tp->t_timer[TCPT_PERSIST] == 0) {
tp->t_rxtshift = 0;
tcp_setpersist(tp);
}
/*
* No reason to send a segment, just return.
*/
return (0);
send:
/*
* Before ESTABLISHED, force sending of initial options
* unless TCP set not to do any options.
* NOTE: we assume that the IP/TCP header plus TCP options
* always fit in a single mbuf, leaving room for a maximum
* link header, i.e.
* max_linkhdr + sizeof (struct tcpiphdr) + optlen <= MHLEN
*/
optlen = 0;
hdrlen = sizeof (struct tcpiphdr);
if (flags & TH_SYN) {
tp->snd_nxt = tp->iss;
if ((tp->t_flags & TF_NOOPT) == 0) {
uint16_t mss;
opt[0] = TCPOPT_MAXSEG;
opt[1] = 4;
mss = htons((uint16_t) tcp_mss(tp, 0));
memcpy((caddr_t)(opt + 2), (caddr_t)&mss, sizeof(mss));
optlen = 4;
}
}
hdrlen += optlen;
/*
* Adjust data length if insertion of options will
* bump the packet length beyond the t_maxseg length.
*/
if (len > tp->t_maxseg - optlen) {
len = tp->t_maxseg - optlen;
sendalot = 1;
}
/*
* Grab a header mbuf, attaching a copy of data to
* be transmitted, and initialize the header from
* the template for sends on this connection.
*/
if (len) {
m = m_get(so->slirp);
if (m == NULL) {
error = 1;
goto out;
}
m->m_data += IF_MAXLINKHDR;
m->m_len = hdrlen;
sbcopy(&so->so_snd, off, (int) len, mtod(m, caddr_t) + hdrlen);
m->m_len += len;
/*
* If we're sending everything we've got, set PUSH.
* (This will keep happy those implementations which only
* give data to the user when a buffer fills or
* a PUSH comes in.)
*/
if (off + len == so->so_snd.sb_cc)
flags |= TH_PUSH;
} else {
m = m_get(so->slirp);
if (m == NULL) {
error = 1;
goto out;
}
m->m_data += IF_MAXLINKHDR;
m->m_len = hdrlen;
}
ti = mtod(m, struct tcpiphdr *);
memcpy((caddr_t)ti, &tp->t_template, sizeof (struct tcpiphdr));
/*
* Fill in fields, remembering maximum advertised
* window for use in delaying messages about window sizes.
* If resending a FIN, be sure not to use a new sequence number.
*/
if (flags & TH_FIN && tp->t_flags & TF_SENTFIN &&
tp->snd_nxt == tp->snd_max)
tp->snd_nxt--;
/*
* If we are doing retransmissions, then snd_nxt will
* not reflect the first unsent octet. For ACK only
* packets, we do not want the sequence number of the
* retransmitted packet, we want the sequence number
* of the next unsent octet. So, if there is no data
* (and no SYN or FIN), use snd_max instead of snd_nxt
* when filling in ti_seq. But if we are in persist
* state, snd_max might reflect one byte beyond the
* right edge of the window, so use snd_nxt in that
* case, since we know we aren't doing a retransmission.
* (retransmit and persist are mutually exclusive...)
*/
if (len || (flags & (TH_SYN|TH_FIN)) || tp->t_timer[TCPT_PERSIST])
ti->ti_seq = htonl(tp->snd_nxt);
else
ti->ti_seq = htonl(tp->snd_max);
ti->ti_ack = htonl(tp->rcv_nxt);
if (optlen) {
memcpy((caddr_t)(ti + 1), (caddr_t)opt, optlen);
ti->ti_off = (sizeof (struct tcphdr) + optlen) >> 2;
}
ti->ti_flags = flags;
/*
* Calculate receive window. Don't shrink window,
* but avoid silly window syndrome.
*/
if (win < (long)(so->so_rcv.sb_datalen / 4) && win < (long)tp->t_maxseg)
win = 0;
if (win > (long)TCP_MAXWIN << tp->rcv_scale)
win = (long)TCP_MAXWIN << tp->rcv_scale;
if (win < (long)(tp->rcv_adv - tp->rcv_nxt))
win = (long)(tp->rcv_adv - tp->rcv_nxt);
ti->ti_win = htons((uint16_t) (win>>tp->rcv_scale));
if (SEQ_GT(tp->snd_up, tp->snd_una)) {
ti->ti_urp = htons((uint16_t)(tp->snd_up - ntohl(ti->ti_seq)));
ti->ti_flags |= TH_URG;
} else
/*
* If no urgent pointer to send, then we pull
* the urgent pointer to the left edge of the send window
* so that it doesn't drift into the send window on sequence
* number wraparound.
*/
tp->snd_up = tp->snd_una; /* drag it along */
/*
* Put TCP length in extended header, and then
* checksum extended header and data.
*/
if (len + optlen)
ti->ti_len = htons((uint16_t)(sizeof (struct tcphdr) +
optlen + len));
ti->ti_sum = cksum(m, (int)(hdrlen + len));
/*
* In transmit state, time the transmission and arrange for
* the retransmit. In persist state, just set snd_max.
*/
if (tp->t_force == 0 || tp->t_timer[TCPT_PERSIST] == 0) {
tcp_seq startseq = tp->snd_nxt;
/*
* Advance snd_nxt over sequence space of this segment.
*/
if (flags & (TH_SYN|TH_FIN)) {
if (flags & TH_SYN)
tp->snd_nxt++;
if (flags & TH_FIN) {
tp->snd_nxt++;
tp->t_flags |= TF_SENTFIN;
}
}
tp->snd_nxt += len;
if (SEQ_GT(tp->snd_nxt, tp->snd_max)) {
tp->snd_max = tp->snd_nxt;
/*
* Time this transmission if not a retransmission and
* not currently timing anything.
*/
if (tp->t_rtt == 0) {
tp->t_rtt = 1;
tp->t_rtseq = startseq;
}
}
/*
* Set retransmit timer if not currently set,
* and not doing an ack or a keep-alive probe.
* Initial value for retransmit timer is smoothed
* round-trip time + 2 * round-trip time variance.
* Initialize shift counter which is used for backoff
* of retransmit time.
*/
if (tp->t_timer[TCPT_REXMT] == 0 &&
tp->snd_nxt != tp->snd_una) {
tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
if (tp->t_timer[TCPT_PERSIST]) {
tp->t_timer[TCPT_PERSIST] = 0;
tp->t_rxtshift = 0;
}
}
} else
if (SEQ_GT(tp->snd_nxt + len, tp->snd_max))
tp->snd_max = tp->snd_nxt + len;
/*
* Fill in IP length and desired time to live and
* send to IP level. There should be a better way
* to handle ttl and tos; we could keep them in
* the template, but need a way to checksum without them.
*/
m->m_len = hdrlen + len; /* XXX Needed? m_len should be correct */
{
((struct ip *)ti)->ip_len = m->m_len;
((struct ip *)ti)->ip_ttl = IPDEFTTL;
((struct ip *)ti)->ip_tos = so->so_iptos;
error = ip_output(so, m);
}
if (error) {
out:
return (error);
}
/*
* Data sent (as far as we can tell).
* If this advertises a larger window than any other segment,
* then remember the size of the advertised window.
* Any pending ACK has now been sent.
*/
if (win > 0 && SEQ_GT(tp->rcv_nxt+win, tp->rcv_adv))
tp->rcv_adv = tp->rcv_nxt + win;
tp->last_ack_sent = tp->rcv_nxt;
tp->t_flags &= ~(TF_ACKNOW|TF_DELACK);
if (sendalot)
goto again;
return (0);
}
void
tcp_setpersist(struct tcpcb *tp)
{
int t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1;
/*
* Start/restart persistence timer.
*/
TCPT_RANGESET(tp->t_timer[TCPT_PERSIST],
t * tcp_backoff[tp->t_rxtshift],
TCPTV_PERSMIN, TCPTV_PERSMAX);
if (tp->t_rxtshift < TCP_MAXRXTSHIFT)
tp->t_rxtshift++;
}

915
slirp/tcp_subr.c Normal file
View file

@ -0,0 +1,915 @@
/*
* Copyright (c) 1982, 1986, 1988, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)tcp_subr.c 8.1 (Berkeley) 6/10/93
* tcp_subr.c,v 1.5 1994/10/08 22:39:58 phk Exp
*/
/*
* Changes and additions relating to SLiRP
* Copyright (c) 1995 Danny Gasparovski.
*
* Please read the file COPYRIGHT for the
* terms and conditions of the copyright.
*/
#include "slirp.h"
/* patchable/settable parameters for tcp */
/* Don't do rfc1323 performance enhancements */
#define TCP_DO_RFC1323 0
/*
* Tcp initialization
*/
void
tcp_init(Slirp *slirp)
{
slirp->tcp_iss = 1; /* wrong */
slirp->tcb.so_next = slirp->tcb.so_prev = &slirp->tcb;
slirp->tcp_last_so = &slirp->tcb;
}
/*
* Create template to be used to send tcp packets on a connection.
* Call after host entry created, fills
* in a skeletal tcp/ip header, minimizing the amount of work
* necessary when the connection is used.
*/
void
tcp_template(struct tcpcb *tp)
{
struct socket *so = tp->t_socket;
register struct tcpiphdr *n = &tp->t_template;
n->ti_mbuf = NULL;
n->ti_x1 = 0;
n->ti_pr = IPPROTO_TCP;
n->ti_len = htons(sizeof (struct tcpiphdr) - sizeof (struct ip));
n->ti_src = so->so_faddr;
n->ti_dst = so->so_laddr;
n->ti_sport = so->so_fport;
n->ti_dport = so->so_lport;
n->ti_seq = 0;
n->ti_ack = 0;
n->ti_x2 = 0;
n->ti_off = 5;
n->ti_flags = 0;
n->ti_win = 0;
n->ti_sum = 0;
n->ti_urp = 0;
}
/*
* Send a single message to the TCP at address specified by
* the given TCP/IP header. If m == 0, then we make a copy
* of the tcpiphdr at ti and send directly to the addressed host.
* This is used to force keep alive messages out using the TCP
* template for a connection tp->t_template. If flags are given
* then we send a message back to the TCP which originated the
* segment ti, and discard the mbuf containing it and any other
* attached mbufs.
*
* In any case the ack and sequence number of the transmitted
* segment are as specified by the parameters.
*/
void
tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m,
tcp_seq ack, tcp_seq seq, int flags)
{
register int tlen;
int win = 0;
DEBUG_CALL("tcp_respond");
DEBUG_ARG("tp = %lx", (long)tp);
DEBUG_ARG("ti = %lx", (long)ti);
DEBUG_ARG("m = %lx", (long)m);
DEBUG_ARG("ack = %u", ack);
DEBUG_ARG("seq = %u", seq);
DEBUG_ARG("flags = %x", flags);
if (tp)
win = sbspace(&tp->t_socket->so_rcv);
if (m == NULL) {
if ((m = m_get(tp->t_socket->slirp)) == NULL)
return;
tlen = 0;
m->m_data += IF_MAXLINKHDR;
*mtod(m, struct tcpiphdr *) = *ti;
ti = mtod(m, struct tcpiphdr *);
flags = TH_ACK;
} else {
/*
* ti points into m so the next line is just making
* the mbuf point to ti
*/
m->m_data = (caddr_t)ti;
m->m_len = sizeof (struct tcpiphdr);
tlen = 0;
#define xchg(a,b,type) { type t; t=a; a=b; b=t; }
xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, uint32_t);
xchg(ti->ti_dport, ti->ti_sport, uint16_t);
#undef xchg
}
ti->ti_len = htons((u_short)(sizeof (struct tcphdr) + tlen));
tlen += sizeof (struct tcpiphdr);
m->m_len = tlen;
ti->ti_mbuf = NULL;
ti->ti_x1 = 0;
ti->ti_seq = htonl(seq);
ti->ti_ack = htonl(ack);
ti->ti_x2 = 0;
ti->ti_off = sizeof (struct tcphdr) >> 2;
ti->ti_flags = flags;
if (tp)
ti->ti_win = htons((uint16_t) (win >> tp->rcv_scale));
else
ti->ti_win = htons((uint16_t)win);
ti->ti_urp = 0;
ti->ti_sum = 0;
ti->ti_sum = cksum(m, tlen);
((struct ip *)ti)->ip_len = tlen;
if(flags & TH_RST)
((struct ip *)ti)->ip_ttl = MAXTTL;
else
((struct ip *)ti)->ip_ttl = IPDEFTTL;
(void) ip_output((struct socket *)0, m);
}
/*
* Create a new TCP control block, making an
* empty reassembly queue and hooking it to the argument
* protocol control block.
*/
struct tcpcb *
tcp_newtcpcb(struct socket *so)
{
register struct tcpcb *tp;
tp = (struct tcpcb *)malloc(sizeof(*tp));
if (tp == NULL)
return ((struct tcpcb *)0);
memset((char *) tp, 0, sizeof(struct tcpcb));
tp->seg_next = tp->seg_prev = (struct tcpiphdr*)tp;
tp->t_maxseg = TCP_MSS;
tp->t_flags = TCP_DO_RFC1323 ? (TF_REQ_SCALE|TF_REQ_TSTMP) : 0;
tp->t_socket = so;
/*
* Init srtt to TCPTV_SRTTBASE (0), so we can tell that we have no
* rtt estimate. Set rttvar so that srtt + 2 * rttvar gives
* reasonable initial retransmit time.
*/
tp->t_srtt = TCPTV_SRTTBASE;
tp->t_rttvar = TCPTV_SRTTDFLT << 2;
tp->t_rttmin = TCPTV_MIN;
TCPT_RANGESET(tp->t_rxtcur,
((TCPTV_SRTTBASE >> 2) + (TCPTV_SRTTDFLT << 2)) >> 1,
TCPTV_MIN, TCPTV_REXMTMAX);
tp->snd_cwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT;
tp->snd_ssthresh = TCP_MAXWIN << TCP_MAX_WINSHIFT;
tp->t_state = TCPS_CLOSED;
so->so_tcpcb = tp;
return (tp);
}
/*
* Drop a TCP connection, reporting
* the specified error. If connection is synchronized,
* then send a RST to peer.
*/
struct tcpcb *tcp_drop(struct tcpcb *tp, int err)
{
DEBUG_CALL("tcp_drop");
DEBUG_ARG("tp = %lx", (long)tp);
DEBUG_ARG("errno = %d", errno);
if (TCPS_HAVERCVDSYN(tp->t_state)) {
tp->t_state = TCPS_CLOSED;
(void) tcp_output(tp);
}
return (tcp_close(tp));
}
/*
* Close a TCP control block:
* discard all space held by the tcp
* discard internet protocol block
* wake up any sleepers
*/
struct tcpcb *
tcp_close(struct tcpcb *tp)
{
register struct tcpiphdr *t;
struct socket *so = tp->t_socket;
Slirp *slirp = so->slirp;
register struct mbuf *m;
DEBUG_CALL("tcp_close");
DEBUG_ARG("tp = %lx", (long )tp);
/* free the reassembly queue, if any */
t = tcpfrag_list_first(tp);
while (!tcpfrag_list_end(t, tp)) {
t = tcpiphdr_next(t);
m = tcpiphdr_prev(t)->ti_mbuf;
remque(tcpiphdr2qlink(tcpiphdr_prev(t)));
m_freem(m);
}
free(tp);
so->so_tcpcb = NULL;
/* clobber input socket cache if we're closing the cached connection */
if (so == slirp->tcp_last_so)
slirp->tcp_last_so = &slirp->tcb;
closesocket(so->s);
sbfree(&so->so_rcv);
sbfree(&so->so_snd);
sofree(so);
return ((struct tcpcb *)0);
}
/*
* TCP protocol interface to socket abstraction.
*/
/*
* User issued close, and wish to trail through shutdown states:
* if never received SYN, just forget it. If got a SYN from peer,
* but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN.
* If already got a FIN from peer, then almost done; go to LAST_ACK
* state. In all other cases, have already sent FIN to peer (e.g.
* after PRU_SHUTDOWN), and just have to play tedious game waiting
* for peer to send FIN or not respond to keep-alives, etc.
* We can let the user exit from the close as soon as the FIN is acked.
*/
void
tcp_sockclosed(struct tcpcb *tp)
{
DEBUG_CALL("tcp_sockclosed");
DEBUG_ARG("tp = %lx", (long)tp);
switch (tp->t_state) {
case TCPS_CLOSED:
case TCPS_LISTEN:
case TCPS_SYN_SENT:
tp->t_state = TCPS_CLOSED;
tp = tcp_close(tp);
break;
case TCPS_SYN_RECEIVED:
case TCPS_ESTABLISHED:
tp->t_state = TCPS_FIN_WAIT_1;
break;
case TCPS_CLOSE_WAIT:
tp->t_state = TCPS_LAST_ACK;
break;
}
if (tp)
tcp_output(tp);
}
/*
* Connect to a host on the Internet
* Called by tcp_input
* Only do a connect, the tcp fields will be set in tcp_input
* return 0 if there's a result of the connect,
* else return -1 means we're still connecting
* The return value is almost always -1 since the socket is
* nonblocking. Connect returns after the SYN is sent, and does
* not wait for ACK+SYN.
*/
int tcp_fconnect(struct socket *so)
{
Slirp *slirp = so->slirp;
int ret=0;
DEBUG_CALL("tcp_fconnect");
DEBUG_ARG("so = %lx", (long )so);
if( (ret = so->s = os_socket(AF_INET,SOCK_STREAM,0)) >= 0) {
int opt, s=so->s;
struct sockaddr_in addr;
fd_nonblock(s);
opt = 1;
setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(opt ));
opt = 1;
setsockopt(s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(opt ));
addr.sin_family = AF_INET;
if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
slirp->vnetwork_addr.s_addr) {
/* It's an alias */
if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) {
if (get_dns_addr(&addr.sin_addr) < 0)
addr.sin_addr = loopback_addr;
} else {
addr.sin_addr = loopback_addr;
}
} else
addr.sin_addr = so->so_faddr;
addr.sin_port = so->so_fport;
DEBUG_MISC((dfd, " connect()ing, addr.sin_port=%d, "
"addr.sin_addr.s_addr=%.16s\n",
ntohs(addr.sin_port), inet_ntoa(addr.sin_addr)));
/* We don't care what port we get */
ret = connect(s,(struct sockaddr *)&addr,sizeof (addr));
/*
* If it's not in progress, it failed, so we just return 0,
* without clearing SS_NOFDREF
*/
soisfconnecting(so);
}
return(ret);
}
/*
* Accept the socket and connect to the local-host
*
* We have a problem. The correct thing to do would be
* to first connect to the local-host, and only if the
* connection is accepted, then do an accept() here.
* But, a) we need to know who's trying to connect
* to the socket to be able to SYN the local-host, and
* b) we are already connected to the foreign host by
* the time it gets to accept(), so... We simply accept
* here and SYN the local-host.
*/
void
tcp_connect(struct socket *inso)
{
Slirp *slirp = inso->slirp;
struct socket *so;
struct sockaddr_in addr;
socklen_t addrlen = sizeof(struct sockaddr_in);
struct tcpcb *tp;
int s, opt;
DEBUG_CALL("tcp_connect");
DEBUG_ARG("inso = %lx", (long)inso);
/*
* If it's an SS_ACCEPTONCE socket, no need to socreate()
* another socket, just use the accept() socket.
*/
if (inso->so_state & SS_FACCEPTONCE) {
/* FACCEPTONCE already have a tcpcb */
so = inso;
} else {
if ((so = socreate(slirp)) == NULL) {
/* If it failed, get rid of the pending connection */
closesocket(accept(inso->s,(struct sockaddr *)&addr,&addrlen));
return;
}
if (tcp_attach(so) < 0) {
free(so); /* NOT sofree */
return;
}
so->so_laddr = inso->so_laddr;
so->so_lport = inso->so_lport;
}
(void) tcp_mss(sototcpcb(so), 0);
if ((s = accept(inso->s,(struct sockaddr *)&addr,&addrlen)) < 0) {
tcp_close(sototcpcb(so)); /* This will sofree() as well */
return;
}
fd_nonblock(s);
opt = 1;
setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int));
opt = 1;
setsockopt(s,SOL_SOCKET,SO_OOBINLINE,(char *)&opt,sizeof(int));
opt = 1;
setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&opt,sizeof(int));
so->so_fport = addr.sin_port;
so->so_faddr = addr.sin_addr;
/* Translate connections from localhost to the real hostname */
if (so->so_faddr.s_addr == 0 || so->so_faddr.s_addr == loopback_addr.s_addr)
so->so_faddr = slirp->vhost_addr;
/* Close the accept() socket, set right state */
if (inso->so_state & SS_FACCEPTONCE) {
closesocket(so->s); /* If we only accept once, close the accept() socket */
so->so_state = SS_NOFDREF; /* Don't select it yet, even though we have an FD */
/* if it's not FACCEPTONCE, it's already NOFDREF */
}
so->s = s;
so->so_state |= SS_INCOMING;
so->so_iptos = tcp_tos(so);
tp = sototcpcb(so);
tcp_template(tp);
tp->t_state = TCPS_SYN_SENT;
tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
tp->iss = slirp->tcp_iss;
slirp->tcp_iss += TCP_ISSINCR/2;
tcp_sendseqinit(tp);
tcp_output(tp);
}
/*
* Attach a TCPCB to a socket.
*/
int
tcp_attach(struct socket *so)
{
if ((so->so_tcpcb = tcp_newtcpcb(so)) == NULL)
return -1;
insque(so, &so->slirp->tcb);
return 0;
}
/*
* Set the socket's type of service field
*/
static const struct tos_t tcptos[] = {
{0, 20, IPTOS_THROUGHPUT, 0}, /* ftp data */
{21, 21, IPTOS_LOWDELAY, EMU_FTP}, /* ftp control */
{0, 23, IPTOS_LOWDELAY, 0}, /* telnet */
{0, 80, IPTOS_THROUGHPUT, 0}, /* WWW */
{0, 513, IPTOS_LOWDELAY, EMU_RLOGIN|EMU_NOCONNECT}, /* rlogin */
{0, 514, IPTOS_LOWDELAY, EMU_RSH|EMU_NOCONNECT}, /* shell */
{0, 544, IPTOS_LOWDELAY, EMU_KSH}, /* kshell */
{0, 543, IPTOS_LOWDELAY, 0}, /* klogin */
{0, 6667, IPTOS_THROUGHPUT, EMU_IRC}, /* IRC */
{0, 6668, IPTOS_THROUGHPUT, EMU_IRC}, /* IRC undernet */
{0, 7070, IPTOS_LOWDELAY, EMU_REALAUDIO }, /* RealAudio control */
{0, 113, IPTOS_LOWDELAY, EMU_IDENT }, /* identd protocol */
{0, 0, 0, 0}
};
static struct emu_t *tcpemu = NULL;
/*
* Return TOS according to the above table
*/
uint8_t
tcp_tos(struct socket *so)
{
int i = 0;
struct emu_t *emup;
while(tcptos[i].tos) {
if ((tcptos[i].fport && (ntohs(so->so_fport) == tcptos[i].fport)) ||
(tcptos[i].lport && (ntohs(so->so_lport) == tcptos[i].lport))) {
so->so_emu = tcptos[i].emu;
return tcptos[i].tos;
}
i++;
}
/* Nope, lets see if there's a user-added one */
for (emup = tcpemu; emup; emup = emup->next) {
if ((emup->fport && (ntohs(so->so_fport) == emup->fport)) ||
(emup->lport && (ntohs(so->so_lport) == emup->lport))) {
so->so_emu = emup->emu;
return emup->tos;
}
}
return 0;
}
/*
* Emulate programs that try and connect to us
* This includes ftp (the data connection is
* initiated by the server) and IRC (DCC CHAT and
* DCC SEND) for now
*
* NOTE: It's possible to crash SLiRP by sending it
* unstandard strings to emulate... if this is a problem,
* more checks are needed here
*
* XXX Assumes the whole command came in one packet
*
* XXX Some ftp clients will have their TOS set to
* LOWDELAY and so Nagel will kick in. Because of this,
* we'll get the first letter, followed by the rest, so
* we simply scan for ORT instead of PORT...
* DCC doesn't have this problem because there's other stuff
* in the packet before the DCC command.
*
* Return 1 if the mbuf m is still valid and should be
* sbappend()ed
*
* NOTE: if you return 0 you MUST m_free() the mbuf!
*/
int
tcp_emu(struct socket *so, struct mbuf *m)
{
Slirp *slirp = so->slirp;
u_int n1, n2, n3, n4, n5, n6;
char buff[257];
uint32_t laddr;
u_int lport;
char *bptr;
DEBUG_CALL("tcp_emu");
DEBUG_ARG("so = %lx", (long)so);
DEBUG_ARG("m = %lx", (long)m);
switch(so->so_emu) {
int x, i;
case EMU_IDENT:
/*
* Identification protocol as per rfc-1413
*/
{
struct socket *tmpso;
struct sockaddr_in addr;
socklen_t addrlen = sizeof(struct sockaddr_in);
struct sbuf *so_rcv = &so->so_rcv;
memcpy(so_rcv->sb_wptr, m->m_data, m->m_len);
so_rcv->sb_wptr += m->m_len;
so_rcv->sb_rptr += m->m_len;
m->m_data[m->m_len] = 0; /* NULL terminate */
if (strchr(m->m_data, '\r') || strchr(m->m_data, '\n')) {
if (sscanf(so_rcv->sb_data, "%u%*[ ,]%u", &n1, &n2) == 2) {
HTONS(n1);
HTONS(n2);
/* n2 is the one on our host */
for (tmpso = slirp->tcb.so_next;
tmpso != &slirp->tcb;
tmpso = tmpso->so_next) {
if (tmpso->so_laddr.s_addr == so->so_laddr.s_addr &&
tmpso->so_lport == n2 &&
tmpso->so_faddr.s_addr == so->so_faddr.s_addr &&
tmpso->so_fport == n1) {
if (getsockname(tmpso->s,
(struct sockaddr *)&addr, &addrlen) == 0)
n2 = ntohs(addr.sin_port);
break;
}
}
}
so_rcv->sb_cc = snprintf(so_rcv->sb_data,
so_rcv->sb_datalen,
"%d,%d\r\n", n1, n2);
so_rcv->sb_rptr = so_rcv->sb_data;
so_rcv->sb_wptr = so_rcv->sb_data + so_rcv->sb_cc;
}
m_free(m);
return 0;
}
case EMU_FTP: /* ftp */
*(m->m_data+m->m_len) = 0; /* NUL terminate for strstr */
if ((bptr = (char *)strstr(m->m_data, "ORT")) != NULL) {
/*
* Need to emulate the PORT command
*/
x = sscanf(bptr, "ORT %u,%u,%u,%u,%u,%u\r\n%256[^\177]",
&n1, &n2, &n3, &n4, &n5, &n6, buff);
if (x < 6)
return 1;
laddr = htonl((n1 << 24) | (n2 << 16) | (n3 << 8) | (n4));
lport = htons((n5 << 8) | (n6));
if ((so = tcp_listen(slirp, INADDR_ANY, 0, laddr,
lport, SS_FACCEPTONCE)) == NULL) {
return 1;
}
n6 = ntohs(so->so_fport);
n5 = (n6 >> 8) & 0xff;
n6 &= 0xff;
laddr = ntohl(so->so_faddr.s_addr);
n1 = ((laddr >> 24) & 0xff);
n2 = ((laddr >> 16) & 0xff);
n3 = ((laddr >> 8) & 0xff);
n4 = (laddr & 0xff);
m->m_len = bptr - m->m_data; /* Adjust length */
m->m_len += snprintf(bptr, m->m_hdr.mh_size - m->m_len,
"ORT %d,%d,%d,%d,%d,%d\r\n%s",
n1, n2, n3, n4, n5, n6, x==7?buff:"");
return 1;
} else if ((bptr = (char *)strstr(m->m_data, "27 Entering")) != NULL) {
/*
* Need to emulate the PASV response
*/
x = sscanf(bptr, "27 Entering Passive Mode (%u,%u,%u,%u,%u,%u)\r\n%256[^\177]",
&n1, &n2, &n3, &n4, &n5, &n6, buff);
if (x < 6)
return 1;
laddr = htonl((n1 << 24) | (n2 << 16) | (n3 << 8) | (n4));
lport = htons((n5 << 8) | (n6));
if ((so = tcp_listen(slirp, INADDR_ANY, 0, laddr,
lport, SS_FACCEPTONCE)) == NULL) {
return 1;
}
n6 = ntohs(so->so_fport);
n5 = (n6 >> 8) & 0xff;
n6 &= 0xff;
laddr = ntohl(so->so_faddr.s_addr);
n1 = ((laddr >> 24) & 0xff);
n2 = ((laddr >> 16) & 0xff);
n3 = ((laddr >> 8) & 0xff);
n4 = (laddr & 0xff);
m->m_len = bptr - m->m_data; /* Adjust length */
m->m_len += snprintf(bptr, m->m_hdr.mh_size - m->m_len,
"27 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n%s",
n1, n2, n3, n4, n5, n6, x==7?buff:"");
return 1;
}
return 1;
case EMU_KSH:
/*
* The kshell (Kerberos rsh) and shell services both pass
* a local port port number to carry signals to the server
* and stderr to the client. It is passed at the beginning
* of the connection as a NUL-terminated decimal ASCII string.
*/
so->so_emu = 0;
for (lport = 0, i = 0; i < m->m_len-1; ++i) {
if (m->m_data[i] < '0' || m->m_data[i] > '9')
return 1; /* invalid number */
lport *= 10;
lport += m->m_data[i] - '0';
}
if (m->m_data[m->m_len-1] == '\0' && lport != 0 &&
(so = tcp_listen(slirp, INADDR_ANY, 0, so->so_laddr.s_addr,
htons(lport), SS_FACCEPTONCE)) != NULL)
m->m_len = snprintf(m->m_data, m->m_hdr.mh_size, "%d",
ntohs(so->so_fport)) + 1;
return 1;
case EMU_IRC:
/*
* Need to emulate DCC CHAT, DCC SEND and DCC MOVE
*/
*(m->m_data+m->m_len) = 0; /* NULL terminate the string for strstr */
if ((bptr = (char *)strstr(m->m_data, "DCC")) == NULL)
return 1;
/* The %256s is for the broken mIRC */
if (sscanf(bptr, "DCC CHAT %256s %u %u", buff, &laddr, &lport) == 3) {
if ((so = tcp_listen(slirp, INADDR_ANY, 0,
htonl(laddr), htons(lport),
SS_FACCEPTONCE)) == NULL) {
return 1;
}
m->m_len = bptr - m->m_data; /* Adjust length */
m->m_len += snprintf(bptr, m->m_hdr.mh_size,
"DCC CHAT chat %lu %u%c\n",
(unsigned long)ntohl(so->so_faddr.s_addr),
ntohs(so->so_fport), 1);
} else if (sscanf(bptr, "DCC SEND %256s %u %u %u", buff, &laddr, &lport, &n1) == 4) {
if ((so = tcp_listen(slirp, INADDR_ANY, 0,
htonl(laddr), htons(lport),
SS_FACCEPTONCE)) == NULL) {
return 1;
}
m->m_len = bptr - m->m_data; /* Adjust length */
m->m_len += snprintf(bptr, m->m_hdr.mh_size,
"DCC SEND %s %lu %u %u%c\n", buff,
(unsigned long)ntohl(so->so_faddr.s_addr),
ntohs(so->so_fport), n1, 1);
} else if (sscanf(bptr, "DCC MOVE %256s %u %u %u", buff, &laddr, &lport, &n1) == 4) {
if ((so = tcp_listen(slirp, INADDR_ANY, 0,
htonl(laddr), htons(lport),
SS_FACCEPTONCE)) == NULL) {
return 1;
}
m->m_len = bptr - m->m_data; /* Adjust length */
m->m_len += snprintf(bptr, m->m_hdr.mh_size,
"DCC MOVE %s %lu %u %u%c\n", buff,
(unsigned long)ntohl(so->so_faddr.s_addr),
ntohs(so->so_fport), n1, 1);
}
return 1;
case EMU_REALAUDIO:
/*
* RealAudio emulation - JP. We must try to parse the incoming
* data and try to find the two characters that contain the
* port number. Then we redirect an udp port and replace the
* number with the real port we got.
*
* The 1.0 beta versions of the player are not supported
* any more.
*
* A typical packet for player version 1.0 (release version):
*
* 0000:50 4E 41 00 05
* 0000:00 01 00 02 1B D7 00 00 67 E6 6C DC 63 00 12 50 ........g.l.c..P
* 0010:4E 43 4C 49 45 4E 54 20 31 30 31 20 41 4C 50 48 NCLIENT 101 ALPH
* 0020:41 6C 00 00 52 00 17 72 61 66 69 6C 65 73 2F 76 Al..R..rafiles/v
* 0030:6F 61 2F 65 6E 67 6C 69 73 68 5F 2E 72 61 79 42 oa/english_.rayB
*
* Now the port number 0x1BD7 is found at offset 0x04 of the
* Now the port number 0x1BD7 is found at offset 0x04 of the
* second packet. This time we received five bytes first and
* then the rest. You never know how many bytes you get.
*
* A typical packet for player version 2.0 (beta):
*
* 0000:50 4E 41 00 06 00 02 00 00 00 01 00 02 1B C1 00 PNA.............
* 0010:00 67 75 78 F5 63 00 0A 57 69 6E 32 2E 30 2E 30 .gux.c..Win2.0.0
* 0020:2E 35 6C 00 00 52 00 1C 72 61 66 69 6C 65 73 2F .5l..R..rafiles/
* 0030:77 65 62 73 69 74 65 2F 32 30 72 65 6C 65 61 73 website/20releas
* 0040:65 2E 72 61 79 53 00 00 06 36 42 e.rayS...6B
*
* Port number 0x1BC1 is found at offset 0x0d.
*
* This is just a horrible switch statement. Variable ra tells
* us where we're going.
*/
bptr = m->m_data;
while (bptr < m->m_data + m->m_len) {
u_short p;
static int ra = 0;
char ra_tbl[4];
ra_tbl[0] = 0x50;
ra_tbl[1] = 0x4e;
ra_tbl[2] = 0x41;
ra_tbl[3] = 0;
switch (ra) {
case 0:
case 2:
case 3:
if (*bptr++ != ra_tbl[ra]) {
ra = 0;
continue;
}
break;
case 1:
/*
* We may get 0x50 several times, ignore them
*/
if (*bptr == 0x50) {
ra = 1;
bptr++;
continue;
} else if (*bptr++ != ra_tbl[ra]) {
ra = 0;
continue;
}
break;
case 4:
/*
* skip version number
*/
bptr++;
break;
case 5:
/*
* The difference between versions 1.0 and
* 2.0 is here. For future versions of
* the player this may need to be modified.
*/
if (*(bptr + 1) == 0x02)
bptr += 8;
else
bptr += 4;
break;
case 6:
/* This is the field containing the port
* number that RA-player is listening to.
*/
lport = (((u_char*)bptr)[0] << 8)
+ ((u_char *)bptr)[1];
if (lport < 6970)
lport += 256; /* don't know why */
if (lport < 6970 || lport > 7170)
return 1; /* failed */
/* try to get udp port between 6970 - 7170 */
for (p = 6970; p < 7071; p++) {
if (udp_listen(slirp, INADDR_ANY,
htons(p),
so->so_laddr.s_addr,
htons(lport),
SS_FACCEPTONCE)) {
break;
}
}
if (p == 7071)
p = 0;
*(u_char *)bptr++ = (p >> 8) & 0xff;
*(u_char *)bptr = p & 0xff;
ra = 0;
return 1; /* port redirected, we're done */
break;
default:
ra = 0;
}
ra++;
}
return 1;
default:
/* Ooops, not emulated, won't call tcp_emu again */
so->so_emu = 0;
return 1;
}
}
/*
* Do misc. config of SLiRP while its running.
* Return 0 if this connections is to be closed, 1 otherwise,
* return 2 if this is a command-line connection
*/
int tcp_ctl(struct socket *so)
{
Slirp *slirp = so->slirp;
struct sbuf *sb = &so->so_snd;
struct ex_list *ex_ptr;
int do_pty;
DEBUG_CALL("tcp_ctl");
DEBUG_ARG("so = %lx", (long )so);
if (so->so_faddr.s_addr != slirp->vhost_addr.s_addr) {
/* Check if it's pty_exec */
for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) {
if (ex_ptr->ex_fport == so->so_fport &&
so->so_faddr.s_addr == ex_ptr->ex_addr.s_addr) {
if (ex_ptr->ex_pty == 3) {
so->s = -1;
so->extra = (void *)ex_ptr->ex_exec;
return 1;
}
do_pty = ex_ptr->ex_pty;
DEBUG_MISC((dfd, " executing %s \n",ex_ptr->ex_exec));
return fork_exec(so, ex_ptr->ex_exec, do_pty);
}
}
}
sb->sb_cc =
snprintf(sb->sb_wptr, sb->sb_datalen - (sb->sb_wptr - sb->sb_data),
"Error: No application configured.\r\n");
sb->sb_wptr += sb->sb_cc;
return 0;
}

292
slirp/tcp_timer.c Normal file
View file

@ -0,0 +1,292 @@
/*
* Copyright (c) 1982, 1986, 1988, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)tcp_timer.c 8.1 (Berkeley) 6/10/93
* tcp_timer.c,v 1.2 1994/08/02 07:49:10 davidg Exp
*/
#include "slirp.h"
static struct tcpcb *tcp_timers(register struct tcpcb *tp, int timer);
/*
* Fast timeout routine for processing delayed acks
*/
void
tcp_fasttimo(Slirp *slirp)
{
register struct socket *so;
register struct tcpcb *tp;
DEBUG_CALL("tcp_fasttimo");
so = slirp->tcb.so_next;
if (so)
for (; so != &slirp->tcb; so = so->so_next)
if ((tp = (struct tcpcb *)so->so_tcpcb) &&
(tp->t_flags & TF_DELACK)) {
tp->t_flags &= ~TF_DELACK;
tp->t_flags |= TF_ACKNOW;
(void) tcp_output(tp);
}
}
/*
* Tcp protocol timeout routine called every 500 ms.
* Updates the timers in all active tcb's and
* causes finite state machine actions if timers expire.
*/
void
tcp_slowtimo(Slirp *slirp)
{
register struct socket *ip, *ipnxt;
register struct tcpcb *tp;
register int i;
DEBUG_CALL("tcp_slowtimo");
/*
* Search through tcb's and update active timers.
*/
ip = slirp->tcb.so_next;
if (ip == NULL) {
return;
}
for (; ip != &slirp->tcb; ip = ipnxt) {
ipnxt = ip->so_next;
tp = sototcpcb(ip);
if (tp == NULL) {
continue;
}
for (i = 0; i < TCPT_NTIMERS; i++) {
if (tp->t_timer[i] && --tp->t_timer[i] == 0) {
tcp_timers(tp,i);
if (ipnxt->so_prev != ip)
goto tpgone;
}
}
tp->t_idle++;
if (tp->t_rtt)
tp->t_rtt++;
tpgone:
;
}
slirp->tcp_iss += TCP_ISSINCR/PR_SLOWHZ; /* increment iss */
slirp->tcp_now++; /* for timestamps */
}
/*
* Cancel all timers for TCP tp.
*/
void
tcp_canceltimers(struct tcpcb *tp)
{
register int i;
for (i = 0; i < TCPT_NTIMERS; i++)
tp->t_timer[i] = 0;
}
const int tcp_backoff[TCP_MAXRXTSHIFT + 1] =
{ 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
/*
* TCP timer processing.
*/
static struct tcpcb *
tcp_timers(register struct tcpcb *tp, int timer)
{
register int rexmt;
DEBUG_CALL("tcp_timers");
switch (timer) {
/*
* 2 MSL timeout in shutdown went off. If we're closed but
* still waiting for peer to close and connection has been idle
* too long, or if 2MSL time is up from TIME_WAIT, delete connection
* control block. Otherwise, check again in a bit.
*/
case TCPT_2MSL:
if (tp->t_state != TCPS_TIME_WAIT &&
tp->t_idle <= TCP_MAXIDLE)
tp->t_timer[TCPT_2MSL] = TCPTV_KEEPINTVL;
else
tp = tcp_close(tp);
break;
/*
* Retransmission timer went off. Message has not
* been acked within retransmit interval. Back off
* to a longer retransmit interval and retransmit one segment.
*/
case TCPT_REXMT:
/*
* XXXXX If a packet has timed out, then remove all the queued
* packets for that session.
*/
if (++tp->t_rxtshift > TCP_MAXRXTSHIFT) {
/*
* This is a hack to suit our terminal server here at the uni of canberra
* since they have trouble with zeroes... It usually lets them through
* unharmed, but under some conditions, it'll eat the zeros. If we
* keep retransmitting it, it'll keep eating the zeroes, so we keep
* retransmitting, and eventually the connection dies...
* (this only happens on incoming data)
*
* So, if we were gonna drop the connection from too many retransmits,
* don't... instead halve the t_maxseg, which might break up the NULLs and
* let them through
*
* *sigh*
*/
tp->t_maxseg >>= 1;
if (tp->t_maxseg < 32) {
/*
* We tried our best, now the connection must die!
*/
tp->t_rxtshift = TCP_MAXRXTSHIFT;
tp = tcp_drop(tp, tp->t_softerror);
/* tp->t_softerror : ETIMEDOUT); */ /* XXX */
return (tp); /* XXX */
}
/*
* Set rxtshift to 6, which is still at the maximum
* backoff time
*/
tp->t_rxtshift = 6;
}
rexmt = TCP_REXMTVAL(tp) * tcp_backoff[tp->t_rxtshift];
TCPT_RANGESET(tp->t_rxtcur, rexmt,
(short)tp->t_rttmin, TCPTV_REXMTMAX); /* XXX */
tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
/*
* If losing, let the lower level know and try for
* a better route. Also, if we backed off this far,
* our srtt estimate is probably bogus. Clobber it
* so we'll take the next rtt measurement as our srtt;
* move the current srtt into rttvar to keep the current
* retransmit times until then.
*/
if (tp->t_rxtshift > TCP_MAXRXTSHIFT / 4) {
tp->t_rttvar += (tp->t_srtt >> TCP_RTT_SHIFT);
tp->t_srtt = 0;
}
tp->snd_nxt = tp->snd_una;
/*
* If timing a segment in this window, stop the timer.
*/
tp->t_rtt = 0;
/*
* Close the congestion window down to one segment
* (we'll open it by one segment for each ack we get).
* Since we probably have a window's worth of unacked
* data accumulated, this "slow start" keeps us from
* dumping all that data as back-to-back packets (which
* might overwhelm an intermediate gateway).
*
* There are two phases to the opening: Initially we
* open by one mss on each ack. This makes the window
* size increase exponentially with time. If the
* window is larger than the path can handle, this
* exponential growth results in dropped packet(s)
* almost immediately. To get more time between
* drops but still "push" the network to take advantage
* of improving conditions, we switch from exponential
* to linear window opening at some threshold size.
* For a threshold, we use half the current window
* size, truncated to a multiple of the mss.
*
* (the minimum cwnd that will give us exponential
* growth is 2 mss. We don't allow the threshold
* to go below this.)
*/
{
u_int win = min(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->t_maxseg;
if (win < 2)
win = 2;
tp->snd_cwnd = tp->t_maxseg;
tp->snd_ssthresh = win * tp->t_maxseg;
tp->t_dupacks = 0;
}
(void) tcp_output(tp);
break;
/*
* Persistence timer into zero window.
* Force a byte to be output, if possible.
*/
case TCPT_PERSIST:
tcp_setpersist(tp);
tp->t_force = 1;
(void) tcp_output(tp);
tp->t_force = 0;
break;
/*
* Keep-alive timer went off; send something
* or drop connection if idle for too long.
*/
case TCPT_KEEP:
if (tp->t_state < TCPS_ESTABLISHED)
goto dropit;
if ((SO_OPTIONS) && tp->t_state <= TCPS_CLOSE_WAIT) {
if (tp->t_idle >= TCPTV_KEEP_IDLE + TCP_MAXIDLE)
goto dropit;
/*
* Send a packet designed to force a response
* if the peer is up and reachable:
* either an ACK if the connection is still alive,
* or an RST if the peer has closed the connection
* due to timeout or reboot.
* Using sequence number tp->snd_una-1
* causes the transmitted zero-length segment
* to lie outside the receive window;
* by the protocol spec, this requires the
* correspondent TCP to respond.
*/
tcp_respond(tp, &tp->t_template, (struct mbuf *)NULL,
tp->rcv_nxt, tp->snd_una - 1, 0);
tp->t_timer[TCPT_KEEP] = TCPTV_KEEPINTVL;
} else
tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_IDLE;
break;
dropit:
tp = tcp_drop(tp, 0);
break;
}
return (tp);
}

127
slirp/tcp_timer.h Normal file
View file

@ -0,0 +1,127 @@
/*
* Copyright (c) 1982, 1986, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)tcp_timer.h 8.1 (Berkeley) 6/10/93
* tcp_timer.h,v 1.4 1994/08/21 05:27:38 paul Exp
*/
#ifndef _TCP_TIMER_H_
#define _TCP_TIMER_H_
/*
* Definitions of the TCP timers. These timers are counted
* down PR_SLOWHZ times a second.
*/
#define TCPT_NTIMERS 4
#define TCPT_REXMT 0 /* retransmit */
#define TCPT_PERSIST 1 /* retransmit persistence */
#define TCPT_KEEP 2 /* keep alive */
#define TCPT_2MSL 3 /* 2*msl quiet time timer */
/*
* The TCPT_REXMT timer is used to force retransmissions.
* The TCP has the TCPT_REXMT timer set whenever segments
* have been sent for which ACKs are expected but not yet
* received. If an ACK is received which advances tp->snd_una,
* then the retransmit timer is cleared (if there are no more
* outstanding segments) or reset to the base value (if there
* are more ACKs expected). Whenever the retransmit timer goes off,
* we retransmit one unacknowledged segment, and do a backoff
* on the retransmit timer.
*
* The TCPT_PERSIST timer is used to keep window size information
* flowing even if the window goes shut. If all previous transmissions
* have been acknowledged (so that there are no retransmissions in progress),
* and the window is too small to bother sending anything, then we start
* the TCPT_PERSIST timer. When it expires, if the window is nonzero,
* we go to transmit state. Otherwise, at intervals send a single byte
* into the peer's window to force him to update our window information.
* We do this at most as often as TCPT_PERSMIN time intervals,
* but no more frequently than the current estimate of round-trip
* packet time. The TCPT_PERSIST timer is cleared whenever we receive
* a window update from the peer.
*
* The TCPT_KEEP timer is used to keep connections alive. If an
* connection is idle (no segments received) for TCPTV_KEEP_INIT amount of time,
* but not yet established, then we drop the connection. Once the connection
* is established, if the connection is idle for TCPTV_KEEP_IDLE time
* (and keepalives have been enabled on the socket), we begin to probe
* the connection. We force the peer to send us a segment by sending:
* <SEQ=SND.UNA-1><ACK=RCV.NXT><CTL=ACK>
* This segment is (deliberately) outside the window, and should elicit
* an ack segment in response from the peer. If, despite the TCPT_KEEP
* initiated segments we cannot elicit a response from a peer in TCPT_MAXIDLE
* amount of time probing, then we drop the connection.
*/
/*
* Time constants.
*/
#define TCPTV_MSL ( 5*PR_SLOWHZ) /* max seg lifetime (hah!) */
#define TCPTV_SRTTBASE 0 /* base roundtrip time;
if 0, no idea yet */
#define TCPTV_SRTTDFLT ( 3*PR_SLOWHZ) /* assumed RTT if no info */
#define TCPTV_PERSMIN ( 5*PR_SLOWHZ) /* retransmit persistence */
#define TCPTV_PERSMAX ( 60*PR_SLOWHZ) /* maximum persist interval */
#define TCPTV_KEEP_INIT ( 75*PR_SLOWHZ) /* initial connect keep alive */
#define TCPTV_KEEP_IDLE (120*60*PR_SLOWHZ) /* dflt time before probing */
#define TCPTV_KEEPINTVL ( 75*PR_SLOWHZ) /* default probe interval */
#define TCPTV_KEEPCNT 8 /* max probes before drop */
#define TCPTV_MIN ( 1*PR_SLOWHZ) /* minimum allowable value */
#define TCPTV_REXMTMAX ( 12*PR_SLOWHZ) /* max allowable REXMT value */
#define TCP_LINGERTIME 120 /* linger at most 2 minutes */
#define TCP_MAXRXTSHIFT 12 /* maximum retransmits */
/*
* Force a time value to be in a certain range.
*/
#define TCPT_RANGESET(tv, value, tvmin, tvmax) { \
(tv) = (value); \
if ((tv) < (tvmin)) \
(tv) = (tvmin); \
else if ((tv) > (tvmax)) \
(tv) = (tvmax); \
}
extern const int tcp_backoff[];
struct tcpcb;
void tcp_fasttimo(Slirp *);
void tcp_slowtimo(Slirp *);
void tcp_canceltimers(struct tcpcb *);
#endif

161
slirp/tcp_var.h Normal file
View file

@ -0,0 +1,161 @@
/*
* Copyright (c) 1982, 1986, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)tcp_var.h 8.3 (Berkeley) 4/10/94
* tcp_var.h,v 1.3 1994/08/21 05:27:39 paul Exp
*/
#ifndef _TCP_VAR_H_
#define _TCP_VAR_H_
#include "tcpip.h"
#include "tcp_timer.h"
/*
* Tcp control block, one per tcp; fields:
*/
struct tcpcb {
struct tcpiphdr *seg_next; /* sequencing queue */
struct tcpiphdr *seg_prev;
short t_state; /* state of this connection */
short t_timer[TCPT_NTIMERS]; /* tcp timers */
short t_rxtshift; /* log(2) of rexmt exp. backoff */
short t_rxtcur; /* current retransmit value */
short t_dupacks; /* consecutive dup acks recd */
u_short t_maxseg; /* maximum segment size */
char t_force; /* 1 if forcing out a byte */
u_short t_flags;
#define TF_ACKNOW 0x0001 /* ack peer immediately */
#define TF_DELACK 0x0002 /* ack, but try to delay it */
#define TF_NODELAY 0x0004 /* don't delay packets to coalesce */
#define TF_NOOPT 0x0008 /* don't use tcp options */
#define TF_SENTFIN 0x0010 /* have sent FIN */
#define TF_REQ_SCALE 0x0020 /* have/will request window scaling */
#define TF_RCVD_SCALE 0x0040 /* other side has requested scaling */
#define TF_REQ_TSTMP 0x0080 /* have/will request timestamps */
#define TF_RCVD_TSTMP 0x0100 /* a timestamp was received in SYN */
#define TF_SACK_PERMIT 0x0200 /* other side said I could SACK */
struct tcpiphdr t_template; /* static skeletal packet for xmit */
struct socket *t_socket; /* back pointer to socket */
/*
* The following fields are used as in the protocol specification.
* See RFC783, Dec. 1981, page 21.
*/
/* send sequence variables */
tcp_seq snd_una; /* send unacknowledged */
tcp_seq snd_nxt; /* send next */
tcp_seq snd_up; /* send urgent pointer */
tcp_seq snd_wl1; /* window update seg seq number */
tcp_seq snd_wl2; /* window update seg ack number */
tcp_seq iss; /* initial send sequence number */
uint32_t snd_wnd; /* send window */
/* receive sequence variables */
uint32_t rcv_wnd; /* receive window */
tcp_seq rcv_nxt; /* receive next */
tcp_seq rcv_up; /* receive urgent pointer */
tcp_seq irs; /* initial receive sequence number */
/*
* Additional variables for this implementation.
*/
/* receive variables */
tcp_seq rcv_adv; /* advertised window */
/* retransmit variables */
tcp_seq snd_max; /* highest sequence number sent;
* used to recognize retransmits
*/
/* congestion control (for slow start, source quench, retransmit after loss) */
uint32_t snd_cwnd; /* congestion-controlled window */
uint32_t snd_ssthresh; /* snd_cwnd size threshold for
* for slow start exponential to
* linear switch
*/
/*
* transmit timing stuff. See below for scale of srtt and rttvar.
* "Variance" is actually smoothed difference.
*/
short t_idle; /* inactivity time */
short t_rtt; /* round trip time */
tcp_seq t_rtseq; /* sequence number being timed */
short t_srtt; /* smoothed round-trip time */
short t_rttvar; /* variance in round-trip time */
u_short t_rttmin; /* minimum rtt allowed */
uint32_t max_sndwnd; /* largest window peer has offered */
/* out-of-band data */
char t_oobflags; /* have some */
char t_iobc; /* input character */
#define TCPOOB_HAVEDATA 0x01
#define TCPOOB_HADDATA 0x02
short t_softerror; /* possible error not yet reported */
/* RFC 1323 variables */
u_char snd_scale; /* window scaling for send window */
u_char rcv_scale; /* window scaling for recv window */
u_char request_r_scale; /* pending window scaling */
u_char requested_s_scale;
uint32_t ts_recent; /* timestamp echo data */
uint32_t ts_recent_age; /* when last updated */
tcp_seq last_ack_sent;
};
#define sototcpcb(so) ((so)->so_tcpcb)
/*
* The smoothed round-trip time and estimated variance
* are stored as fixed point numbers scaled by the values below.
* For convenience, these scales are also used in smoothing the average
* (smoothed = (1/scale)sample + ((scale-1)/scale)smoothed).
* With these scales, srtt has 3 bits to the right of the binary point,
* and thus an "ALPHA" of 0.875. rttvar has 2 bits to the right of the
* binary point, and is smoothed with an ALPHA of 0.75.
*/
#define TCP_RTT_SCALE 8 /* multiplier for srtt; 3 bits frac. */
#define TCP_RTT_SHIFT 3 /* shift for srtt; 3 bits frac. */
#define TCP_RTTVAR_SCALE 4 /* multiplier for rttvar; 2 bits */
#define TCP_RTTVAR_SHIFT 2 /* multiplier for rttvar; 2 bits */
/*
* The initial retransmission should happen at rtt + 4 * rttvar.
* Because of the way we do the smoothing, srtt and rttvar
* will each average +1/2 tick of bias. When we compute
* the retransmit timer, we want 1/2 tick of rounding and
* 1 extra tick because of +-1/2 tick uncertainty in the
* firing of the timer. The bias will give us exactly the
* 1.5 tick we need. But, because the bias is
* statistical, we have to test that we don't drop below
* the minimum feasible timer (which is 2 ticks).
* This macro assumes that the value of TCP_RTTVAR_SCALE
* is the same as the multiplier for rttvar.
*/
#define TCP_REXMTVAL(tp) \
(((tp)->t_srtt >> TCP_RTT_SHIFT) + (tp)->t_rttvar)
#endif

77
slirp/tcpip.h Normal file
View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 1982, 1986, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)tcpip.h 8.1 (Berkeley) 6/10/93
* tcpip.h,v 1.3 1994/08/21 05:27:40 paul Exp
*/
#ifndef _TCPIP_H_
#define _TCPIP_H_
/*
* Tcp+ip header, after ip options removed.
*/
struct tcpiphdr {
struct ipovly ti_i; /* overlaid ip structure */
struct tcphdr ti_t; /* tcp header */
};
#define ti_mbuf ti_i.ih_mbuf.mptr
#define ti_x1 ti_i.ih_x1
#define ti_pr ti_i.ih_pr
#define ti_len ti_i.ih_len
#define ti_src ti_i.ih_src
#define ti_dst ti_i.ih_dst
#define ti_sport ti_t.th_sport
#define ti_dport ti_t.th_dport
#define ti_seq ti_t.th_seq
#define ti_ack ti_t.th_ack
#define ti_x2 ti_t.th_x2
#define ti_off ti_t.th_off
#define ti_flags ti_t.th_flags
#define ti_win ti_t.th_win
#define ti_sum ti_t.th_sum
#define ti_urp ti_t.th_urp
#define tcpiphdr2qlink(T) ((struct qlink*)(((char*)(T)) - sizeof(struct qlink)))
#define qlink2tcpiphdr(Q) ((struct tcpiphdr*)(((char*)(Q)) + sizeof(struct qlink)))
#define tcpiphdr_next(T) qlink2tcpiphdr(tcpiphdr2qlink(T)->next)
#define tcpiphdr_prev(T) qlink2tcpiphdr(tcpiphdr2qlink(T)->prev)
#define tcpfrag_list_first(T) qlink2tcpiphdr((T)->seg_next)
#define tcpfrag_list_end(F, T) (tcpiphdr2qlink(F) == (struct qlink*)(T))
#define tcpfrag_list_empty(T) ((T)->seg_next == (struct tcpiphdr*)(T))
/*
* Just a clean way to get to the first byte
* of the packet
*/
struct tcpiphdr_2 {
struct tcpiphdr dummy;
char first_char;
};
#endif

43
slirp/tftp.h Normal file
View file

@ -0,0 +1,43 @@
/* tftp defines */
#define TFTP_SESSIONS_MAX 3
#define TFTP_SERVER 69
#define TFTP_RRQ 1
#define TFTP_WRQ 2
#define TFTP_DATA 3
#define TFTP_ACK 4
#define TFTP_ERROR 5
#define TFTP_OACK 6
#define TFTP_FILENAME_MAX 512
struct tftp_t {
struct ip ip;
struct udphdr udp;
uint16_t tp_op;
union {
struct {
uint16_t tp_block_nr;
uint8_t tp_buf[512];
} tp_data;
struct {
uint16_t tp_error_code;
uint8_t tp_msg[512];
} tp_error;
uint8_t tp_buf[512 + 2];
} x;
};
struct tftp_session {
Slirp *slirp;
char *filename;
struct in_addr client_ip;
uint16_t client_port;
int timestamp;
};
void tftp_input(struct mbuf *m);

386
slirp/udp.c Normal file
View file

@ -0,0 +1,386 @@
/*
* Copyright (c) 1982, 1986, 1988, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)udp_usrreq.c 8.4 (Berkeley) 1/21/94
* udp_usrreq.c,v 1.4 1994/10/02 17:48:45 phk Exp
*/
/*
* Changes and additions relating to SLiRP
* Copyright (c) 1995 Danny Gasparovski.
*
* Please read the file COPYRIGHT for the
* terms and conditions of the copyright.
*/
#include "slirp.h"
#include "ip_icmp.h"
static uint8_t udp_tos(struct socket *so);
void
udp_init(Slirp *slirp)
{
slirp->udb.so_next = slirp->udb.so_prev = &slirp->udb;
slirp->udp_last_so = &slirp->udb;
}
/* m->m_data points at ip packet header
* m->m_len length ip packet
* ip->ip_len length data (IPDU)
*/
void
udp_input(register struct mbuf *m, int iphlen)
{
Slirp *slirp = m->slirp;
register struct ip *ip;
register struct udphdr *uh;
int len;
struct ip save_ip;
struct socket *so;
DEBUG_CALL("udp_input");
DEBUG_ARG("m = %lx", (long)m);
DEBUG_ARG("iphlen = %d", iphlen);
/*
* Strip IP options, if any; should skip this,
* make available to user, and use on returned packets,
* but we don't yet have a way to check the checksum
* with options still present.
*/
if(iphlen > sizeof(struct ip)) {
ip_stripoptions(m, (struct mbuf *)0);
iphlen = sizeof(struct ip);
}
/*
* Get IP and UDP header together in first mbuf.
*/
ip = mtod(m, struct ip *);
uh = (struct udphdr *)((caddr_t)ip + iphlen);
/*
* Make mbuf data length reflect UDP length.
* If not enough data to reflect UDP length, drop.
*/
len = ntohs((uint16_t)uh->uh_ulen);
if (ip->ip_len != len) {
if (len > ip->ip_len) {
goto bad;
}
m_adj(m, len - ip->ip_len);
ip->ip_len = len;
}
/*
* Save a copy of the IP header in case we want restore it
* for sending an ICMP error message in response.
*/
save_ip = *ip;
save_ip.ip_len+= iphlen; /* tcp_input subtracts this */
/*
* Checksum extended UDP header and data.
*/
if (uh->uh_sum) {
memset(&((struct ipovly *)ip)->ih_mbuf, 0, sizeof(struct mbuf_ptr));
((struct ipovly *)ip)->ih_x1 = 0;
((struct ipovly *)ip)->ih_len = uh->uh_ulen;
if(cksum(m, len + sizeof(struct ip))) {
goto bad;
}
}
/*
* handle DHCP/BOOTP
*/
if (ntohs(uh->uh_dport) == BOOTP_SERVER) {
bootp_input(m);
goto bad;
}
if (slirp->restricted) {
goto bad;
}
#if 0
/*
* handle TFTP
*/
if (ntohs(uh->uh_dport) == TFTP_SERVER) {
tftp_input(m);
goto bad;
}
#endif
/*
* Locate pcb for datagram.
*/
so = slirp->udp_last_so;
if (so->so_lport != uh->uh_sport ||
so->so_laddr.s_addr != ip->ip_src.s_addr) {
struct socket *tmp;
for (tmp = slirp->udb.so_next; tmp != &slirp->udb;
tmp = tmp->so_next) {
if (tmp->so_lport == uh->uh_sport &&
tmp->so_laddr.s_addr == ip->ip_src.s_addr) {
so = tmp;
break;
}
}
if (tmp == &slirp->udb) {
so = NULL;
} else {
slirp->udp_last_so = so;
}
}
if (so == NULL) {
/*
* If there's no socket for this packet,
* create one
*/
so = socreate(slirp);
if (!so) {
goto bad;
}
if(udp_attach(so) == -1) {
DEBUG_MISC((dfd," udp_attach errno = %d-%s\n",
errno,strerror(errno)));
sofree(so);
goto bad;
}
/*
* Setup fields
*/
so->so_laddr = ip->ip_src;
so->so_lport = uh->uh_sport;
if ((so->so_iptos = udp_tos(so)) == 0)
so->so_iptos = ip->ip_tos;
/*
* XXXXX Here, check if it's in udpexec_list,
* and if it is, do the fork_exec() etc.
*/
}
so->so_faddr = ip->ip_dst; /* XXX */
so->so_fport = uh->uh_dport; /* XXX */
iphlen += sizeof(struct udphdr);
m->m_len -= iphlen;
m->m_data += iphlen;
/*
* Now we sendto() the packet.
*/
if(sosendto(so,m) == -1) {
m->m_len += iphlen;
m->m_data -= iphlen;
*ip=save_ip;
DEBUG_MISC((dfd,"udp tx errno = %d-%s\n",errno,strerror(errno)));
icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,strerror(errno));
}
m_free(so->so_m); /* used for ICMP if error on sorecvfrom */
/* restore the orig mbuf packet */
m->m_len += iphlen;
m->m_data -= iphlen;
*ip=save_ip;
so->so_m=m; /* ICMP backup */
return;
bad:
m_freem(m);
return;
}
int udp_output2(struct socket *so, struct mbuf *m,
struct sockaddr_in *saddr, struct sockaddr_in *daddr,
int iptos)
{
register struct udpiphdr *ui;
int error = 0;
DEBUG_CALL("udp_output");
DEBUG_ARG("so = %lx", (long)so);
DEBUG_ARG("m = %lx", (long)m);
DEBUG_ARG("saddr = %lx", (long)saddr->sin_addr.s_addr);
DEBUG_ARG("daddr = %lx", (long)daddr->sin_addr.s_addr);
/*
* Adjust for header
*/
m->m_data -= sizeof(struct udpiphdr);
m->m_len += sizeof(struct udpiphdr);
/*
* Fill in mbuf with extended UDP header
* and addresses and length put into network format.
*/
ui = mtod(m, struct udpiphdr *);
memset(&ui->ui_i.ih_mbuf, 0 , sizeof(struct mbuf_ptr));
ui->ui_x1 = 0;
ui->ui_pr = IPPROTO_UDP;
ui->ui_len = htons(m->m_len - sizeof(struct ip));
/* XXXXX Check for from-one-location sockets, or from-any-location sockets */
ui->ui_src = saddr->sin_addr;
ui->ui_dst = daddr->sin_addr;
ui->ui_sport = saddr->sin_port;
ui->ui_dport = daddr->sin_port;
ui->ui_ulen = ui->ui_len;
/*
* Stuff checksum and output datagram.
*/
ui->ui_sum = 0;
if ((ui->ui_sum = cksum(m, m->m_len)) == 0)
ui->ui_sum = 0xffff;
((struct ip *)ui)->ip_len = m->m_len;
((struct ip *)ui)->ip_ttl = IPDEFTTL;
((struct ip *)ui)->ip_tos = iptos;
error = ip_output(so, m);
return (error);
}
int udp_output(struct socket *so, struct mbuf *m,
struct sockaddr_in *addr)
{
Slirp *slirp = so->slirp;
struct sockaddr_in saddr, daddr;
saddr = *addr;
if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
slirp->vnetwork_addr.s_addr) {
uint32_t inv_mask = ~slirp->vnetwork_mask.s_addr;
if ((so->so_faddr.s_addr & inv_mask) == inv_mask) {
saddr.sin_addr = slirp->vhost_addr;
} else if (addr->sin_addr.s_addr == loopback_addr.s_addr ||
so->so_faddr.s_addr != slirp->vhost_addr.s_addr) {
saddr.sin_addr = so->so_faddr;
}
}
daddr.sin_addr = so->so_laddr;
daddr.sin_port = so->so_lport;
return udp_output2(so, m, &saddr, &daddr, so->so_iptos);
}
int
udp_attach(struct socket *so)
{
if((so->s = os_socket(AF_INET,SOCK_DGRAM,0)) != -1) {
so->so_expire = curtime + SO_EXPIRE;
insque(so, &so->slirp->udb);
}
return(so->s);
}
void
udp_detach(struct socket *so)
{
closesocket(so->s);
sofree(so);
}
static const struct tos_t udptos[] = {
{0, 53, IPTOS_LOWDELAY, 0}, /* DNS */
{0, 0, 0, 0}
};
static uint8_t
udp_tos(struct socket *so)
{
int i = 0;
while(udptos[i].tos) {
if ((udptos[i].fport && ntohs(so->so_fport) == udptos[i].fport) ||
(udptos[i].lport && ntohs(so->so_lport) == udptos[i].lport)) {
so->so_emu = udptos[i].emu;
return udptos[i].tos;
}
i++;
}
return 0;
}
struct socket *
udp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr,
u_int lport, int flags)
{
struct sockaddr_in addr;
struct socket *so;
socklen_t addrlen = sizeof(struct sockaddr_in), opt = 1;
so = socreate(slirp);
if (!so) {
return NULL;
}
so->s = os_socket(AF_INET,SOCK_DGRAM,0);
so->so_expire = curtime + SO_EXPIRE;
insque(so, &slirp->udb);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = haddr;
addr.sin_port = hport;
if (bind(so->s,(struct sockaddr *)&addr, addrlen) < 0) {
udp_detach(so);
return NULL;
}
setsockopt(so->s,SOL_SOCKET,SO_REUSEADDR,(char *)&opt,sizeof(int));
getsockname(so->s,(struct sockaddr *)&addr,&addrlen);
so->so_fport = addr.sin_port;
if (addr.sin_addr.s_addr == 0 ||
addr.sin_addr.s_addr == loopback_addr.s_addr) {
so->so_faddr = slirp->vhost_addr;
} else {
so->so_faddr = addr.sin_addr;
}
so->so_lport = lport;
so->so_laddr.s_addr = laddr;
if (flags != SS_FACCEPTONCE)
so->so_expire = 0;
so->so_state &= SS_PERSISTENT_MASK;
so->so_state |= SS_ISFCONNECTED | flags;
return so;
}

86
slirp/udp.h Normal file
View file

@ -0,0 +1,86 @@
/*
* Copyright (c) 1982, 1986, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)udp.h 8.1 (Berkeley) 6/10/93
* udp.h,v 1.3 1994/08/21 05:27:41 paul Exp
*/
#ifndef _UDP_H_
#define _UDP_H_
#define UDP_TTL 0x60
#define UDP_UDPDATALEN 16192
/*
* Udp protocol header.
* Per RFC 768, September, 1981.
*/
struct udphdr {
uint16_t uh_sport; /* source port */
uint16_t uh_dport; /* destination port */
int16_t uh_ulen; /* udp length */
uint16_t uh_sum; /* udp checksum */
};
/*
* UDP kernel structures and variables.
*/
struct udpiphdr {
struct ipovly ui_i; /* overlaid ip structure */
struct udphdr ui_u; /* udp header */
};
#define ui_mbuf ui_i.ih_mbuf.mptr
#define ui_x1 ui_i.ih_x1
#define ui_pr ui_i.ih_pr
#define ui_len ui_i.ih_len
#define ui_src ui_i.ih_src
#define ui_dst ui_i.ih_dst
#define ui_sport ui_u.uh_sport
#define ui_dport ui_u.uh_dport
#define ui_ulen ui_u.uh_ulen
#define ui_sum ui_u.uh_sum
/*
* Names for UDP sysctl objects
*/
#define UDPCTL_CHECKSUM 1 /* checksum UDP packets */
#define UDPCTL_MAXID 2
struct mbuf;
void udp_init(Slirp *);
void udp_input(register struct mbuf *, int);
int udp_output(struct socket *, struct mbuf *, struct sockaddr_in *);
int udp_attach(struct socket *);
void udp_detach(struct socket *);
struct socket * udp_listen(Slirp *, uint32_t, u_int, uint32_t, u_int,
int);
int udp_output2(struct socket *so, struct mbuf *m,
struct sockaddr_in *saddr, struct sockaddr_in *daddr,
int iptos);
#endif

87
softfp.c Normal file
View file

@ -0,0 +1,87 @@
/*
* SoftFP Library
*
* Copyright (c) 2016 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include "cutils.h"
#include "softfp.h"
static inline int clz32(uint32_t a)
{
int r;
if (a == 0) {
r = 32;
} else {
r = __builtin_clz(a);
}
return r;
}
static inline int clz64(uint64_t a)
{
int r;
if (a == 0) {
r = 64;
} else
{
r = __builtin_clzll(a);
}
return r;
}
#ifdef HAVE_INT128
static inline int clz128(uint128_t a)
{
int r;
if (a == 0) {
r = 128;
} else
{
uint64_t ah, al;
ah = a >> 64;
al = a;
if (ah != 0)
r = __builtin_clzll(ah);
else
r = __builtin_clzll(al) + 64;
}
return r;
}
#endif
#define F_SIZE 32
#include "softfp_template.h"
#define F_SIZE 64
#include "softfp_template.h"
#ifdef HAVE_INT128
#define F_SIZE 128
#include "softfp_template.h"
#endif

181
softfp.h Normal file
View file

@ -0,0 +1,181 @@
/*
* SoftFP Library
*
* Copyright (c) 2016 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef SOFTFP_H
#define SOFTFP_H
#include <inttypes.h>
#include "cutils.h"
typedef enum {
RM_RNE, /* Round to Nearest, ties to Even */
RM_RTZ, /* Round towards Zero */
RM_RDN, /* Round Down */
RM_RUP, /* Round Up */
RM_RMM, /* Round to Nearest, ties to Max Magnitude */
} RoundingModeEnum;
#define FFLAG_INVALID_OP (1 << 4)
#define FFLAG_DIVIDE_ZERO (1 << 3)
#define FFLAG_OVERFLOW (1 << 2)
#define FFLAG_UNDERFLOW (1 << 1)
#define FFLAG_INEXACT (1 << 0)
#define FCLASS_NINF (1 << 0)
#define FCLASS_NNORMAL (1 << 1)
#define FCLASS_NSUBNORMAL (1 << 2)
#define FCLASS_NZERO (1 << 3)
#define FCLASS_PZERO (1 << 4)
#define FCLASS_PSUBNORMAL (1 << 5)
#define FCLASS_PNORMAL (1 << 6)
#define FCLASS_PINF (1 << 7)
#define FCLASS_SNAN (1 << 8)
#define FCLASS_QNAN (1 << 9)
typedef enum {
FMINMAX_PROP, /* min(1, qNaN/sNaN) -> qNaN */
FMINMAX_IEEE754_2008, /* min(1, qNaN) -> 1, min(1, sNaN) -> qNaN */
FMINMAX_IEEE754_201X, /* min(1, qNaN/sNaN) -> 1 */
} SoftFPMinMaxTypeEnum;
typedef uint32_t sfloat32;
typedef uint64_t sfloat64;
#ifdef HAVE_INT128
typedef uint128_t sfloat128;
#endif
/* 32 bit floats */
#define FSIGN_MASK32 (1 << 31)
sfloat32 add_sf32(sfloat32 a, sfloat32 b, RoundingModeEnum rm, uint32_t *pfflags);
sfloat32 sub_sf32(sfloat32 a, sfloat32 b, RoundingModeEnum rm, uint32_t *pfflags);
sfloat32 mul_sf32(sfloat32 a, sfloat32 b, RoundingModeEnum rm, uint32_t *pfflags);
sfloat32 div_sf32(sfloat32 a, sfloat32 b, RoundingModeEnum rm, uint32_t *pfflags);
sfloat32 sqrt_sf32(sfloat32 a, RoundingModeEnum rm, uint32_t *pfflags);
sfloat32 fma_sf32(sfloat32 a, sfloat32 b, sfloat32 c, RoundingModeEnum rm, uint32_t *pfflags);
sfloat32 min_sf32(sfloat32 a, sfloat32 b, uint32_t *pfflags, SoftFPMinMaxTypeEnum minmax_type);
sfloat32 max_sf32(sfloat32 a, sfloat32 b, uint32_t *pfflags, SoftFPMinMaxTypeEnum minmax_type);
int eq_quiet_sf32(sfloat32 a, sfloat32 b, uint32_t *pfflags);
int le_sf32(sfloat32 a, sfloat32 b, uint32_t *pfflags);
int lt_sf32(sfloat32 a, sfloat32 b, uint32_t *pfflags);
uint32_t fclass_sf32(sfloat32 a);
sfloat64 cvt_sf32_sf64(sfloat32 a, uint32_t *pfflags);
sfloat32 cvt_sf64_sf32(sfloat64 a, RoundingModeEnum rm, uint32_t *pfflags);
int32_t cvt_sf32_i32(sfloat32 a, RoundingModeEnum rm, uint32_t *pfflags);
uint32_t cvt_sf32_u32(sfloat32 a, RoundingModeEnum rm, uint32_t *pfflags);
int64_t cvt_sf32_i64(sfloat32 a, RoundingModeEnum rm, uint32_t *pfflags);
uint64_t cvt_sf32_u64(sfloat32 a, RoundingModeEnum rm, uint32_t *pfflags);
#ifdef HAVE_INT128
int128_t cvt_sf32_i128(sfloat32 a, RoundingModeEnum rm, uint32_t *pfflags);
uint128_t cvt_sf32_u128(sfloat32 a, RoundingModeEnum rm, uint32_t *pfflags);
#endif
sfloat32 cvt_i32_sf32(int32_t a, RoundingModeEnum rm, uint32_t *pfflags);
sfloat32 cvt_u32_sf32(uint32_t a, RoundingModeEnum rm, uint32_t *pfflags);
sfloat32 cvt_i64_sf32(int64_t a, RoundingModeEnum rm, uint32_t *pfflags);
sfloat32 cvt_u64_sf32(uint64_t a, RoundingModeEnum rm, uint32_t *pfflags);
#ifdef HAVE_INT128
sfloat32 cvt_i128_sf32(int128_t a, RoundingModeEnum rm, uint32_t *pfflags);
sfloat32 cvt_u128_sf32(uint128_t a, RoundingModeEnum rm, uint32_t *pfflags);
#endif
/* 64 bit floats */
#define FSIGN_MASK64 ((uint64_t)1 << 63)
sfloat64 add_sf64(sfloat64 a, sfloat64 b, RoundingModeEnum rm, uint32_t *pfflags);
sfloat64 sub_sf64(sfloat64 a, sfloat64 b, RoundingModeEnum rm, uint32_t *pfflags);
sfloat64 mul_sf64(sfloat64 a, sfloat64 b, RoundingModeEnum rm, uint32_t *pfflags);
sfloat64 div_sf64(sfloat64 a, sfloat64 b, RoundingModeEnum rm, uint32_t *pfflags);
sfloat64 sqrt_sf64(sfloat64 a, RoundingModeEnum rm, uint32_t *pfflags);
sfloat64 fma_sf64(sfloat64 a, sfloat64 b, sfloat64 c, RoundingModeEnum rm, uint32_t *pfflags);
sfloat64 min_sf64(sfloat64 a, sfloat64 b, uint32_t *pfflags, SoftFPMinMaxTypeEnum minmax_type);
sfloat64 max_sf64(sfloat64 a, sfloat64 b, uint32_t *pfflags, SoftFPMinMaxTypeEnum minmax_type);
int eq_quiet_sf64(sfloat64 a, sfloat64 b, uint32_t *pfflags);
int le_sf64(sfloat64 a, sfloat64 b, uint32_t *pfflags);
int lt_sf64(sfloat64 a, sfloat64 b, uint32_t *pfflags);
uint32_t fclass_sf64(sfloat64 a);
sfloat64 cvt_sf32_sf64(sfloat32 a, uint32_t *pfflags);
sfloat32 cvt_sf64_sf32(sfloat64 a, RoundingModeEnum rm, uint32_t *pfflags);
int32_t cvt_sf64_i32(sfloat64 a, RoundingModeEnum rm, uint32_t *pfflags);
uint32_t cvt_sf64_u32(sfloat64 a, RoundingModeEnum rm, uint32_t *pfflags);
int64_t cvt_sf64_i64(sfloat64 a, RoundingModeEnum rm, uint32_t *pfflags);
uint64_t cvt_sf64_u64(sfloat64 a, RoundingModeEnum rm, uint32_t *pfflags);
#ifdef HAVE_INT128
int128_t cvt_sf64_i128(sfloat64 a, RoundingModeEnum rm, uint32_t *pfflags);
uint128_t cvt_sf64_u128(sfloat64 a, RoundingModeEnum rm, uint32_t *pfflags);
#endif
sfloat64 cvt_i32_sf64(int32_t a, RoundingModeEnum rm, uint32_t *pfflags);
sfloat64 cvt_u32_sf64(uint32_t a, RoundingModeEnum rm, uint32_t *pfflags);
sfloat64 cvt_i64_sf64(int64_t a, RoundingModeEnum rm, uint32_t *pfflags);
sfloat64 cvt_u64_sf64(uint64_t a, RoundingModeEnum rm, uint32_t *pfflags);
#ifdef HAVE_INT128
sfloat64 cvt_i128_sf64(int128_t a, RoundingModeEnum rm, uint32_t *pfflags);
sfloat64 cvt_u128_sf64(uint128_t a, RoundingModeEnum rm, uint32_t *pfflags);
#endif
/* 128 bit floats */
#ifdef HAVE_INT128
#define FSIGN_MASK128 ((uint128_t)1 << 127)
sfloat128 add_sf128(sfloat128 a, sfloat128 b, RoundingModeEnum rm, uint32_t *pfflags);
sfloat128 sub_sf128(sfloat128 a, sfloat128 b, RoundingModeEnum rm, uint32_t *pfflags);
sfloat128 mul_sf128(sfloat128 a, sfloat128 b, RoundingModeEnum rm, uint32_t *pfflags);
sfloat128 div_sf128(sfloat128 a, sfloat128 b, RoundingModeEnum rm, uint32_t *pfflags);
sfloat128 sqrt_sf128(sfloat128 a, RoundingModeEnum rm, uint32_t *pfflags);
sfloat128 fma_sf128(sfloat128 a, sfloat128 b, sfloat128 c, RoundingModeEnum rm, uint32_t *pfflags);
sfloat128 min_sf128(sfloat128 a, sfloat128 b, uint32_t *pfflags, SoftFPMinMaxTypeEnum minmax_type);
sfloat128 max_sf128(sfloat128 a, sfloat128 b, uint32_t *pfflags, SoftFPMinMaxTypeEnum minmax_type);
int eq_quiet_sf128(sfloat128 a, sfloat128 b, uint32_t *pfflags);
int le_sf128(sfloat128 a, sfloat128 b, uint32_t *pfflags);
int lt_sf128(sfloat128 a, sfloat128 b, uint32_t *pfflags);
uint32_t fclass_sf128(sfloat128 a);
sfloat128 cvt_sf32_sf128(sfloat32 a, uint32_t *pfflags);
sfloat32 cvt_sf128_sf32(sfloat128 a, RoundingModeEnum rm, uint32_t *pfflags);
sfloat128 cvt_sf64_sf128(sfloat64 a, uint32_t *pfflags);
sfloat64 cvt_sf128_sf64(sfloat128 a, RoundingModeEnum rm, uint32_t *pfflags);
int32_t cvt_sf128_i32(sfloat128 a, RoundingModeEnum rm, uint32_t *pfflags);
uint32_t cvt_sf128_u32(sfloat128 a, RoundingModeEnum rm, uint32_t *pfflags);
int64_t cvt_sf128_i64(sfloat128 a, RoundingModeEnum rm, uint32_t *pfflags);
uint64_t cvt_sf128_u64(sfloat128 a, RoundingModeEnum rm, uint32_t *pfflags);
int128_t cvt_sf128_i128(sfloat128 a, RoundingModeEnum rm, uint32_t *pfflags);
uint128_t cvt_sf128_u128(sfloat128 a, RoundingModeEnum rm, uint32_t *pfflags);
sfloat128 cvt_i32_sf128(int32_t a, RoundingModeEnum rm, uint32_t *pfflags);
sfloat128 cvt_u32_sf128(uint32_t a, RoundingModeEnum rm, uint32_t *pfflags);
sfloat128 cvt_i64_sf128(int64_t a, RoundingModeEnum rm, uint32_t *pfflags);
sfloat128 cvt_u64_sf128(uint64_t a, RoundingModeEnum rm, uint32_t *pfflags);
sfloat128 cvt_i128_sf128(int128_t a, RoundingModeEnum rm, uint32_t *pfflags);
sfloat128 cvt_u128_sf128(uint128_t a, RoundingModeEnum rm, uint32_t *pfflags);
#endif
#endif /* SOFTFP_H */

1129
softfp_template.h Normal file

File diff suppressed because it is too large Load diff

171
softfp_template_icvt.h Normal file
View file

@ -0,0 +1,171 @@
/*
* SoftFP Library
*
* Copyright (c) 2016 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#if ICVT_SIZE == 32
#define ICVT_UINT uint32_t
#define ICVT_INT int32_t
#elif ICVT_SIZE == 64
#define ICVT_UINT uint64_t
#define ICVT_INT int64_t
#elif ICVT_SIZE == 128
#define ICVT_UINT uint128_t
#define ICVT_INT int128_t
#else
#error unsupported icvt
#endif
/* conversions between float and integers */
static ICVT_INT glue(glue(glue(internal_cvt_sf, F_SIZE), _i), ICVT_SIZE)(F_UINT a, RoundingModeEnum rm,
uint32_t *pfflags, BOOL is_unsigned)
{
uint32_t a_sign, addend, rnd_bits;
int32_t a_exp;
F_UINT a_mant;
ICVT_UINT r, r_max;
a_sign = a >> (F_SIZE - 1);
a_exp = (a >> MANT_SIZE) & EXP_MASK;
a_mant = a & MANT_MASK;
if (a_exp == EXP_MASK && a_mant != 0)
a_sign = 0; /* NaN is like +infinity */
if (a_exp == 0) {
a_exp = 1;
} else {
a_mant |= (F_UINT)1 << MANT_SIZE;
}
a_mant <<= RND_SIZE;
a_exp = a_exp - (EXP_MASK / 2) - MANT_SIZE;
if (is_unsigned)
r_max = (ICVT_UINT)a_sign - 1;
else
r_max = ((ICVT_UINT)1 << (ICVT_SIZE - 1)) - (ICVT_UINT)(a_sign ^ 1);
if (a_exp >= 0) {
if (a_exp <= (ICVT_SIZE - 1 - MANT_SIZE)) {
r = (ICVT_UINT)(a_mant >> RND_SIZE) << a_exp;
if (r > r_max)
goto overflow;
} else {
overflow:
*pfflags |= FFLAG_INVALID_OP;
return r_max;
}
} else {
a_mant = rshift_rnd(a_mant, -a_exp);
switch(rm) {
case RM_RNE:
case RM_RMM:
addend = (1 << (RND_SIZE - 1));
break;
case RM_RTZ:
addend = 0;
break;
default:
case RM_RDN:
case RM_RUP:
if (a_sign ^ (rm & 1))
addend = (1 << RND_SIZE) - 1;
else
addend = 0;
break;
}
rnd_bits = a_mant & ((1 << RND_SIZE ) - 1);
a_mant = (a_mant + addend) >> RND_SIZE;
/* half way: select even result */
if (rm == RM_RNE && rnd_bits == (1 << (RND_SIZE - 1)))
a_mant &= ~1;
if (a_mant > r_max)
goto overflow;
r = a_mant;
if (rnd_bits != 0)
*pfflags |= FFLAG_INEXACT;
}
if (a_sign)
r = -r;
return r;
}
ICVT_INT glue(glue(glue(cvt_sf, F_SIZE), _i), ICVT_SIZE)(F_UINT a, RoundingModeEnum rm,
uint32_t *pfflags)
{
return glue(glue(glue(internal_cvt_sf, F_SIZE), _i), ICVT_SIZE)(a, rm,
pfflags, FALSE);
}
ICVT_UINT glue(glue(glue(cvt_sf, F_SIZE), _u), ICVT_SIZE)(F_UINT a, RoundingModeEnum rm,
uint32_t *pfflags)
{
return glue(glue(glue(internal_cvt_sf, F_SIZE), _i), ICVT_SIZE) (a, rm,
pfflags, TRUE);
}
/* conversions between float and integers */
static F_UINT glue(glue(glue(internal_cvt_i, ICVT_SIZE), _sf), F_SIZE)(ICVT_INT a,
RoundingModeEnum rm,
uint32_t *pfflags,
BOOL is_unsigned)
{
uint32_t a_sign;
int32_t a_exp;
F_UINT a_mant;
ICVT_UINT r, mask;
int l;
if (!is_unsigned && a < 0) {
a_sign = 1;
r = -(ICVT_UINT)a;
} else {
a_sign = 0;
r = a;
}
a_exp = (EXP_MASK / 2) + F_SIZE - 2;
/* need to reduce range before generic float normalization */
l = ICVT_SIZE - glue(clz, ICVT_SIZE)(r) - (F_SIZE - 1);
if (l > 0) {
mask = r & (((ICVT_UINT)1 << l) - 1);
r = (r >> l) | ((r & mask) != 0);
a_exp += l;
}
a_mant = r;
return normalize_sf(a_sign, a_exp, a_mant, rm, pfflags);
}
F_UINT glue(glue(glue(cvt_i, ICVT_SIZE), _sf), F_SIZE)(ICVT_INT a,
RoundingModeEnum rm,
uint32_t *pfflags)
{
return glue(glue(glue(internal_cvt_i, ICVT_SIZE), _sf), F_SIZE)(a, rm, pfflags, FALSE);
}
F_UINT glue(glue(glue(cvt_u, ICVT_SIZE), _sf), F_SIZE)(ICVT_UINT a,
RoundingModeEnum rm,
uint32_t *pfflags)
{
return glue(glue(glue(internal_cvt_i, ICVT_SIZE), _sf), F_SIZE)(a, rm, pfflags, TRUE);
}
#undef ICVT_SIZE
#undef ICVT_INT
#undef ICVT_UINT

102
splitimg.c Normal file
View file

@ -0,0 +1,102 @@
/*
* Disk image splitter
*
* Copyright (c) 2016 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include <string.h>
#include <ctype.h>
#include <getopt.h>
int main(int argc, char **argv)
{
int blocksize, ret, i;
const char *infilename, *outpath;
FILE *f, *fo;
char buf1[1024];
uint8_t *buf;
if ((optind + 1) >= argc) {
printf("splitimg version " CONFIG_VERSION ", Copyright (c) 2011-2016 Fabrice Bellard\n"
"usage: splitimg infile outpath [blocksize]\n"
"Create a multi-file disk image for the RISCVEMU HTTP block device\n"
"\n"
"outpath must be a directory\n"
"blocksize is the block size in KB\n");
exit(1);
}
infilename = argv[optind++];
outpath = argv[optind++];
blocksize = 256;
if (optind < argc)
blocksize = strtol(argv[optind++], NULL, 0);
blocksize *= 1024;
buf = malloc(blocksize);
f = fopen(infilename, "rb");
if (!f) {
perror(infilename);
exit(1);
}
i = 0;
for(;;) {
ret = fread(buf, 1, blocksize, f);
if (ret < 0) {
perror("fread");
exit(1);
}
if (ret == 0)
break;
if (ret < blocksize) {
printf("warning: last block is not full\n");
memset(buf + ret, 0, blocksize - ret);
}
snprintf(buf1, sizeof(buf1), "%s/blk%09u.bin", outpath, i);
fo = fopen(buf1, "wb");
if (!fo) {
perror(buf1);
exit(1);
}
fwrite(buf, 1, blocksize, fo);
fclose(fo);
i++;
}
fclose(f);
printf("%d blocks\n", i);
snprintf(buf1, sizeof(buf1), "%s/blk.txt", outpath);
fo = fopen(buf1, "wb");
if (!fo) {
perror(buf1);
exit(1);
}
fprintf(fo, "{\n");
fprintf(fo, " block_size: %d,\n", blocksize / 1024);
fprintf(fo, " n_block: %d,\n", i);
fprintf(fo, "}\n");
fclose(fo);
return 0;
}

835
temu.c Normal file
View file

@ -0,0 +1,835 @@
/*
* TinyEMU
*
* Copyright (c) 2016-2018 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <getopt.h>
#ifndef _WIN32
#include <termios.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <linux/if_tun.h>
#endif
#include <sys/stat.h>
#include <signal.h>
#include "cutils.h"
#include "iomem.h"
#include "virtio.h"
#include "machine.h"
#ifdef CONFIG_FS_NET
#include "fs_utils.h"
#include "fs_wget.h"
#endif
#ifdef CONFIG_SLIRP
#include "slirp/libslirp.h"
#endif
#ifndef _WIN32
typedef struct {
int stdin_fd;
int console_esc_state;
BOOL resize_pending;
} STDIODevice;
static struct termios oldtty;
static int old_fd0_flags;
static STDIODevice *global_stdio_device;
static void term_exit(void)
{
tcsetattr (0, TCSANOW, &oldtty);
fcntl(0, F_SETFL, old_fd0_flags);
}
static void term_init(BOOL allow_ctrlc)
{
struct termios tty;
memset(&tty, 0, sizeof(tty));
tcgetattr (0, &tty);
oldtty = tty;
old_fd0_flags = fcntl(0, F_GETFL);
tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
|INLCR|IGNCR|ICRNL|IXON);
tty.c_oflag |= OPOST;
tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
if (!allow_ctrlc)
tty.c_lflag &= ~ISIG;
tty.c_cflag &= ~(CSIZE|PARENB);
tty.c_cflag |= CS8;
tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 0;
tcsetattr (0, TCSANOW, &tty);
atexit(term_exit);
}
static void console_write(void *opaque, const uint8_t *buf, int len)
{
fwrite(buf, 1, len, stdout);
fflush(stdout);
}
static int console_read(void *opaque, uint8_t *buf, int len)
{
STDIODevice *s = opaque;
int ret, i, j;
uint8_t ch;
if (len <= 0)
return 0;
ret = read(s->stdin_fd, buf, len);
if (ret < 0)
return 0;
if (ret == 0) {
/* EOF */
exit(1);
}
j = 0;
for(i = 0; i < ret; i++) {
ch = buf[i];
if (s->console_esc_state) {
s->console_esc_state = 0;
switch(ch) {
case 'x':
printf("Terminated\n");
exit(0);
case 'h':
printf("\n"
"C-a h print this help\n"
"C-a x exit emulator\n"
"C-a C-a send C-a\n"
);
break;
case 1:
goto output_char;
default:
break;
}
} else {
if (ch == 1) {
s->console_esc_state = 1;
} else {
output_char:
buf[j++] = ch;
}
}
}
return j;
}
static void term_resize_handler(int sig)
{
if (global_stdio_device)
global_stdio_device->resize_pending = TRUE;
}
static void console_get_size(STDIODevice *s, int *pw, int *ph)
{
struct winsize ws;
int width, height;
/* default values */
width = 80;
height = 25;
if (ioctl(s->stdin_fd, TIOCGWINSZ, &ws) == 0 &&
ws.ws_col >= 4 && ws.ws_row >= 4) {
width = ws.ws_col;
height = ws.ws_row;
}
*pw = width;
*ph = height;
}
CharacterDevice *console_init(BOOL allow_ctrlc)
{
CharacterDevice *dev;
STDIODevice *s;
struct sigaction sig;
term_init(allow_ctrlc);
dev = mallocz(sizeof(*dev));
s = mallocz(sizeof(*s));
s->stdin_fd = 0;
/* Note: the glibc does not properly tests the return value of
write() in printf, so some messages on stdout may be lost */
fcntl(s->stdin_fd, F_SETFL, O_NONBLOCK);
s->resize_pending = TRUE;
global_stdio_device = s;
/* use a signal to get the host terminal resize events */
sig.sa_handler = term_resize_handler;
sigemptyset(&sig.sa_mask);
sig.sa_flags = 0;
sigaction(SIGWINCH, &sig, NULL);
dev->opaque = s;
dev->write_data = console_write;
dev->read_data = console_read;
return dev;
}
#endif /* !_WIN32 */
typedef enum {
BF_MODE_RO,
BF_MODE_RW,
BF_MODE_SNAPSHOT,
} BlockDeviceModeEnum;
#define SECTOR_SIZE 512
typedef struct BlockDeviceFile {
FILE *f;
int64_t nb_sectors;
BlockDeviceModeEnum mode;
uint8_t **sector_table;
} BlockDeviceFile;
static int64_t bf_get_sector_count(BlockDevice *bs)
{
BlockDeviceFile *bf = bs->opaque;
return bf->nb_sectors;
}
//#define DUMP_BLOCK_READ
static int bf_read_async(BlockDevice *bs,
uint64_t sector_num, uint8_t *buf, int n,
BlockDeviceCompletionFunc *cb, void *opaque)
{
BlockDeviceFile *bf = bs->opaque;
// printf("bf_read_async: sector_num=%" PRId64 " n=%d\n", sector_num, n);
#ifdef DUMP_BLOCK_READ
{
static FILE *f;
if (!f)
f = fopen("/tmp/read_sect.txt", "wb");
fprintf(f, "%" PRId64 " %d\n", sector_num, n);
}
#endif
if (!bf->f)
return -1;
if (bf->mode == BF_MODE_SNAPSHOT) {
int i;
for(i = 0; i < n; i++) {
if (!bf->sector_table[sector_num]) {
fseek(bf->f, sector_num * SECTOR_SIZE, SEEK_SET);
fread(buf, 1, SECTOR_SIZE, bf->f);
} else {
memcpy(buf, bf->sector_table[sector_num], SECTOR_SIZE);
}
sector_num++;
buf += SECTOR_SIZE;
}
} else {
fseek(bf->f, sector_num * SECTOR_SIZE, SEEK_SET);
fread(buf, 1, n * SECTOR_SIZE, bf->f);
}
/* synchronous read */
return 0;
}
static int bf_write_async(BlockDevice *bs,
uint64_t sector_num, const uint8_t *buf, int n,
BlockDeviceCompletionFunc *cb, void *opaque)
{
BlockDeviceFile *bf = bs->opaque;
int ret;
switch(bf->mode) {
case BF_MODE_RO:
ret = -1; /* error */
break;
case BF_MODE_RW:
fseek(bf->f, sector_num * SECTOR_SIZE, SEEK_SET);
fwrite(buf, 1, n * SECTOR_SIZE, bf->f);
ret = 0;
break;
case BF_MODE_SNAPSHOT:
{
int i;
if ((sector_num + n) > bf->nb_sectors)
return -1;
for(i = 0; i < n; i++) {
if (!bf->sector_table[sector_num]) {
bf->sector_table[sector_num] = malloc(SECTOR_SIZE);
}
memcpy(bf->sector_table[sector_num], buf, SECTOR_SIZE);
sector_num++;
buf += SECTOR_SIZE;
}
ret = 0;
}
break;
default:
abort();
}
return ret;
}
static BlockDevice *block_device_init(const char *filename,
BlockDeviceModeEnum mode)
{
BlockDevice *bs;
BlockDeviceFile *bf;
int64_t file_size;
FILE *f;
const char *mode_str;
if (mode == BF_MODE_RW) {
mode_str = "r+b";
} else {
mode_str = "rb";
}
f = fopen(filename, mode_str);
if (!f) {
perror(filename);
exit(1);
}
fseek(f, 0, SEEK_END);
file_size = ftello(f);
bs = mallocz(sizeof(*bs));
bf = mallocz(sizeof(*bf));
bf->mode = mode;
bf->nb_sectors = file_size / 512;
bf->f = f;
if (mode == BF_MODE_SNAPSHOT) {
bf->sector_table = mallocz(sizeof(bf->sector_table[0]) *
bf->nb_sectors);
}
bs->opaque = bf;
bs->get_sector_count = bf_get_sector_count;
bs->read_async = bf_read_async;
bs->write_async = bf_write_async;
return bs;
}
#ifndef _WIN32
typedef struct {
int fd;
BOOL select_filled;
} TunState;
static void tun_write_packet(EthernetDevice *net,
const uint8_t *buf, int len)
{
TunState *s = net->opaque;
write(s->fd, buf, len);
}
static void tun_select_fill(EthernetDevice *net, int *pfd_max,
fd_set *rfds, fd_set *wfds, fd_set *efds,
int *pdelay)
{
TunState *s = net->opaque;
int net_fd = s->fd;
s->select_filled = net->device_can_write_packet(net);
if (s->select_filled) {
FD_SET(net_fd, rfds);
*pfd_max = max_int(*pfd_max, net_fd);
}
}
static void tun_select_poll(EthernetDevice *net,
fd_set *rfds, fd_set *wfds, fd_set *efds,
int select_ret)
{
TunState *s = net->opaque;
int net_fd = s->fd;
uint8_t buf[2048];
int ret;
if (select_ret <= 0)
return;
if (s->select_filled && FD_ISSET(net_fd, rfds)) {
ret = read(net_fd, buf, sizeof(buf));
if (ret > 0)
net->device_write_packet(net, buf, ret);
}
}
/* configure with:
# bridge configuration (connect tap0 to bridge interface br0)
ip link add br0 type bridge
ip tuntap add dev tap0 mode tap [user x] [group x]
ip link set tap0 master br0
ip link set dev br0 up
ip link set dev tap0 up
# NAT configuration (eth1 is the interface connected to internet)
ifconfig br0 192.168.3.1
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -D FORWARD 1
iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE
In the VM:
ifconfig eth0 192.168.3.2
route add -net 0.0.0.0 netmask 0.0.0.0 gw 192.168.3.1
*/
static EthernetDevice *tun_open(const char *ifname)
{
struct ifreq ifr;
int fd, ret;
EthernetDevice *net;
TunState *s;
fd = open("/dev/net/tun", O_RDWR);
if (fd < 0) {
fprintf(stderr, "Error: could not open /dev/net/tun\n");
return NULL;
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
pstrcpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
ret = ioctl(fd, TUNSETIFF, (void *) &ifr);
if (ret != 0) {
fprintf(stderr, "Error: could not configure /dev/net/tun\n");
close(fd);
return NULL;
}
fcntl(fd, F_SETFL, O_NONBLOCK);
net = mallocz(sizeof(*net));
net->mac_addr[0] = 0x02;
net->mac_addr[1] = 0x00;
net->mac_addr[2] = 0x00;
net->mac_addr[3] = 0x00;
net->mac_addr[4] = 0x00;
net->mac_addr[5] = 0x01;
s = mallocz(sizeof(*s));
s->fd = fd;
net->opaque = s;
net->write_packet = tun_write_packet;
net->select_fill = tun_select_fill;
net->select_poll = tun_select_poll;
return net;
}
#endif /* !_WIN32 */
#ifdef CONFIG_SLIRP
/*******************************************************/
/* slirp */
static Slirp *slirp_state;
static void slirp_write_packet(EthernetDevice *net,
const uint8_t *buf, int len)
{
Slirp *slirp_state = net->opaque;
slirp_input(slirp_state, buf, len);
}
int slirp_can_output(void *opaque)
{
EthernetDevice *net = opaque;
return net->device_can_write_packet(net);
}
void slirp_output(void *opaque, const uint8_t *pkt, int pkt_len)
{
EthernetDevice *net = opaque;
return net->device_write_packet(net, pkt, pkt_len);
}
static void slirp_select_fill1(EthernetDevice *net, int *pfd_max,
fd_set *rfds, fd_set *wfds, fd_set *efds,
int *pdelay)
{
Slirp *slirp_state = net->opaque;
slirp_select_fill(slirp_state, pfd_max, rfds, wfds, efds);
}
static void slirp_select_poll1(EthernetDevice *net,
fd_set *rfds, fd_set *wfds, fd_set *efds,
int select_ret)
{
Slirp *slirp_state = net->opaque;
slirp_select_poll(slirp_state, rfds, wfds, efds, (select_ret <= 0));
}
static EthernetDevice *slirp_open(void)
{
EthernetDevice *net;
struct in_addr net_addr = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */
struct in_addr mask = { .s_addr = htonl(0xffffff00) }; /* 255.255.255.0 */
struct in_addr host = { .s_addr = htonl(0x0a000202) }; /* 10.0.2.2 */
struct in_addr dhcp = { .s_addr = htonl(0x0a00020f) }; /* 10.0.2.15 */
struct in_addr dns = { .s_addr = htonl(0x0a000203) }; /* 10.0.2.3 */
const char *bootfile = NULL;
const char *vhostname = NULL;
int restricted = 0;
if (slirp_state) {
fprintf(stderr, "Only a single slirp instance is allowed\n");
return NULL;
}
net = mallocz(sizeof(*net));
slirp_state = slirp_init(restricted, net_addr, mask, host, vhostname,
"", bootfile, dhcp, dns, net);
net->mac_addr[0] = 0x02;
net->mac_addr[1] = 0x00;
net->mac_addr[2] = 0x00;
net->mac_addr[3] = 0x00;
net->mac_addr[4] = 0x00;
net->mac_addr[5] = 0x01;
net->opaque = slirp_state;
net->write_packet = slirp_write_packet;
net->select_fill = slirp_select_fill1;
net->select_poll = slirp_select_poll1;
return net;
}
#endif /* CONFIG_SLIRP */
#define MAX_EXEC_CYCLE 500000
#define MAX_SLEEP_TIME 10 /* in ms */
void virt_machine_run(VirtMachine *m)
{
fd_set rfds, wfds, efds;
int fd_max, ret, delay;
struct timeval tv;
#ifndef _WIN32
int stdin_fd;
#endif
delay = virt_machine_get_sleep_duration(m, MAX_SLEEP_TIME);
/* wait for an event */
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_ZERO(&efds);
fd_max = -1;
#ifndef _WIN32
if (m->console_dev && virtio_console_can_write_data(m->console_dev)) {
STDIODevice *s = m->console->opaque;
stdin_fd = s->stdin_fd;
FD_SET(stdin_fd, &rfds);
fd_max = stdin_fd;
if (s->resize_pending) {
int width, height;
console_get_size(s, &width, &height);
virtio_console_resize_event(m->console_dev, width, height);
s->resize_pending = FALSE;
}
}
#endif
if (m->net) {
m->net->select_fill(m->net, &fd_max, &rfds, &wfds, &efds, &delay);
}
#ifdef CONFIG_FS_NET
fs_net_set_fdset(&fd_max, &rfds, &wfds, &efds, &delay);
#endif
tv.tv_sec = delay / 1000;
tv.tv_usec = (delay % 1000) * 1000;
ret = select(fd_max + 1, &rfds, &wfds, &efds, &tv);
if (m->net) {
m->net->select_poll(m->net, &rfds, &wfds, &efds, ret);
}
if (ret > 0) {
#ifndef _WIN32
if (m->console_dev && FD_ISSET(stdin_fd, &rfds)) {
uint8_t buf[128];
int ret, len;
len = virtio_console_get_write_len(m->console_dev);
len = min_int(len, sizeof(buf));
ret = m->console->read_data(m->console->opaque, buf, len);
if (ret > 0) {
virtio_console_write_data(m->console_dev, buf, ret);
}
}
#endif
}
#ifdef CONFIG_SDL
sdl_refresh(m);
#endif
virt_machine_interp(m, MAX_EXEC_CYCLE);
}
/*******************************************************/
static struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "ctrlc", no_argument },
{ "rw", no_argument },
{ "ro", no_argument },
{ "append", required_argument },
{ "no-accel", no_argument },
{ "build-preload", required_argument },
{ NULL },
};
void help(void)
{
printf("temu version " CONFIG_VERSION ", Copyright (c) 2016-2018 Fabrice Bellard\n"
"usage: riscvemu [options] config_file\n"
"options are:\n"
"-m ram_size set the RAM size in MB\n"
"-rw allow write access to the disk image (default=snapshot)\n"
"-ctrlc the C-c key stops the emulator instead of being sent to the\n"
" emulated software\n"
"-append cmdline append cmdline to the kernel command line\n"
"-no-accel disable VM acceleration (KVM, x86 machine only)\n"
"\n"
"Console keys:\n"
"Press C-a x to exit the emulator, C-a h to get some help.\n");
exit(1);
}
#ifdef CONFIG_FS_NET
static BOOL net_completed;
static void net_start_cb(void *arg)
{
net_completed = TRUE;
}
static BOOL net_poll_cb(void *arg)
{
return net_completed;
}
#endif
int main(int argc, char **argv)
{
VirtMachine *s;
const char *path, *cmdline, *build_preload_file;
int c, option_index, i, ram_size, accel_enable;
BOOL allow_ctrlc;
BlockDeviceModeEnum drive_mode;
VirtMachineParams p_s, *p = &p_s;
ram_size = -1;
allow_ctrlc = FALSE;
(void)allow_ctrlc;
drive_mode = BF_MODE_SNAPSHOT;
accel_enable = -1;
cmdline = NULL;
build_preload_file = NULL;
for(;;) {
c = getopt_long_only(argc, argv, "hm:", options, &option_index);
if (c == -1)
break;
switch(c) {
case 0:
switch(option_index) {
case 1: /* ctrlc */
allow_ctrlc = TRUE;
break;
case 2: /* rw */
drive_mode = BF_MODE_RW;
break;
case 3: /* ro */
drive_mode = BF_MODE_RO;
break;
case 4: /* append */
cmdline = optarg;
break;
case 5: /* no-accel */
accel_enable = FALSE;
break;
case 6: /* build-preload */
build_preload_file = optarg;
break;
default:
fprintf(stderr, "unknown option index: %d\n", option_index);
exit(1);
}
break;
case 'h':
help();
break;
case 'm':
ram_size = strtoul(optarg, NULL, 0);
break;
default:
exit(1);
}
}
if (optind >= argc) {
help();
}
path = argv[optind++];
virt_machine_set_defaults(p);
#ifdef CONFIG_FS_NET
fs_wget_init();
#endif
virt_machine_load_config_file(p, path, NULL, NULL);
#ifdef CONFIG_FS_NET
fs_net_event_loop(NULL, NULL);
#endif
/* override some config parameters */
if (ram_size > 0) {
p->ram_size = (uint64_t)ram_size << 20;
}
if (accel_enable != -1)
p->accel_enable = accel_enable;
if (cmdline) {
vm_add_cmdline(p, cmdline);
}
/* open the files & devices */
for(i = 0; i < p->drive_count; i++) {
BlockDevice *drive;
char *fname;
fname = get_file_path(p->cfg_filename, p->tab_drive[i].filename);
#ifdef CONFIG_FS_NET
if (is_url(fname)) {
net_completed = FALSE;
drive = block_device_init_http(fname, 128 * 1024,
net_start_cb, NULL);
/* wait until the drive is initialized */
fs_net_event_loop(net_poll_cb, NULL);
} else
#endif
{
drive = block_device_init(fname, drive_mode);
}
free(fname);
p->tab_drive[i].block_dev = drive;
}
for(i = 0; i < p->fs_count; i++) {
FSDevice *fs;
const char *path;
path = p->tab_fs[i].filename;
#ifdef CONFIG_FS_NET
if (is_url(path)) {
fs = fs_net_init(path, NULL, NULL);
if (!fs)
exit(1);
if (build_preload_file)
fs_dump_cache_load(fs, build_preload_file);
fs_net_event_loop(NULL, NULL);
} else
#endif
{
#ifdef _WIN32
fprintf(stderr, "Filesystem access not supported yet\n");
exit(1);
#else
char *fname;
fname = get_file_path(p->cfg_filename, path);
fs = fs_disk_init(fname);
if (!fs) {
fprintf(stderr, "%s: must be a directory\n", fname);
exit(1);
}
free(fname);
#endif
}
p->tab_fs[i].fs_dev = fs;
}
for(i = 0; i < p->eth_count; i++) {
#ifdef CONFIG_SLIRP
if (!strcmp(p->tab_eth[i].driver, "user")) {
p->tab_eth[i].net = slirp_open();
if (!p->tab_eth[i].net)
exit(1);
} else
#endif
#ifndef _WIN32
if (!strcmp(p->tab_eth[i].driver, "tap")) {
p->tab_eth[i].net = tun_open(p->tab_eth[i].ifname);
if (!p->tab_eth[i].net)
exit(1);
} else
#endif
{
fprintf(stderr, "Unsupported network driver '%s'\n",
p->tab_eth[i].driver);
exit(1);
}
}
#ifdef CONFIG_SDL
if (p->display_device) {
sdl_init(p->width, p->height);
} else
#endif
{
#ifdef _WIN32
fprintf(stderr, "Console not supported yet\n");
exit(1);
#else
p->console = console_init(allow_ctrlc);
#endif
}
p->rtc_real_time = TRUE;
s = virt_machine_init(p);
if (!s)
exit(1);
virt_machine_free_config(p);
if (s->net) {
s->net->device_set_carrier(s->net, TRUE);
}
for(;;) {
virt_machine_run(s);
}
virt_machine_end(s);
return 0;
}

804
vga.c Normal file
View file

@ -0,0 +1,804 @@
/*
* Dummy VGA device
*
* Copyright (c) 2003-2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include "cutils.h"
#include "iomem.h"
#include "virtio.h"
#include "machine.h"
//#define DEBUG_VBE
#define MSR_COLOR_EMULATION 0x01
#define MSR_PAGE_SELECT 0x20
#define ST01_V_RETRACE 0x08
#define ST01_DISP_ENABLE 0x01
#define VBE_DISPI_INDEX_ID 0x0
#define VBE_DISPI_INDEX_XRES 0x1
#define VBE_DISPI_INDEX_YRES 0x2
#define VBE_DISPI_INDEX_BPP 0x3
#define VBE_DISPI_INDEX_ENABLE 0x4
#define VBE_DISPI_INDEX_BANK 0x5
#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6
#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7
#define VBE_DISPI_INDEX_X_OFFSET 0x8
#define VBE_DISPI_INDEX_Y_OFFSET 0x9
#define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xa
#define VBE_DISPI_INDEX_NB 0xb
#define VBE_DISPI_ID0 0xB0C0
#define VBE_DISPI_ID1 0xB0C1
#define VBE_DISPI_ID2 0xB0C2
#define VBE_DISPI_ID3 0xB0C3
#define VBE_DISPI_ID4 0xB0C4
#define VBE_DISPI_ID5 0xB0C5
#define VBE_DISPI_DISABLED 0x00
#define VBE_DISPI_ENABLED 0x01
#define VBE_DISPI_GETCAPS 0x02
#define VBE_DISPI_8BIT_DAC 0x20
#define VBE_DISPI_LFB_ENABLED 0x40
#define VBE_DISPI_NOCLEARMEM 0x80
#define FB_ALLOC_ALIGN (1 << 20)
#define MAX_TEXT_WIDTH 132
#define MAX_TEXT_HEIGHT 60
struct VGAState {
FBDevice *fb_dev;
int fb_page_count;
PhysMemoryRange *mem_range;
PhysMemoryRange *mem_range2;
PCIDevice *pci_dev;
PhysMemoryRange *rom_range;
uint8_t *vga_ram; /* 128K at 0xa0000 */
uint8_t sr_index;
uint8_t sr[8];
uint8_t gr_index;
uint8_t gr[16];
uint8_t ar_index;
uint8_t ar[21];
int ar_flip_flop;
uint8_t cr_index;
uint8_t cr[256]; /* CRT registers */
uint8_t msr; /* Misc Output Register */
uint8_t fcr; /* Feature Control Register */
uint8_t st00; /* status 0 */
uint8_t st01; /* status 1 */
uint8_t dac_state;
uint8_t dac_sub_index;
uint8_t dac_read_index;
uint8_t dac_write_index;
uint8_t dac_cache[3]; /* used when writing */
uint8_t palette[768];
/* text mode state */
uint32_t last_palette[16];
uint16_t last_ch_attr[MAX_TEXT_WIDTH * MAX_TEXT_HEIGHT];
uint32_t last_width;
uint32_t last_height;
uint16_t last_line_offset;
uint16_t last_start_addr;
uint16_t last_cursor_offset;
uint8_t last_cursor_start;
uint8_t last_cursor_end;
/* VBE extension */
uint16_t vbe_index;
uint16_t vbe_regs[VBE_DISPI_INDEX_NB];
};
static void vga_draw_glyph8(uint8_t *d, int linesize,
const uint8_t *font_ptr, int h,
uint32_t fgcol, uint32_t bgcol)
{
uint32_t font_data, xorcol;
xorcol = bgcol ^ fgcol;
do {
font_data = font_ptr[0];
((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
font_ptr++;
d += linesize;
} while (--h);
}
static void vga_draw_glyph9(uint8_t *d, int linesize,
const uint8_t *font_ptr, int h,
uint32_t fgcol, uint32_t bgcol,
int dup9)
{
uint32_t font_data, xorcol, v;
xorcol = bgcol ^ fgcol;
do {
font_data = font_ptr[0];
((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
v = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
((uint32_t *)d)[7] = v;
if (dup9)
((uint32_t *)d)[8] = v;
else
((uint32_t *)d)[8] = bgcol;
font_ptr++;
d += linesize;
} while (--h);
}
static const uint8_t cursor_glyph[32] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
};
static inline int c6_to_8(int v)
{
int b;
v &= 0x3f;
b = v & 1;
return (v << 2) | (b << 1) | b;
}
static int update_palette16(VGAState *s, uint32_t *palette)
{
int full_update, i;
uint32_t v, col;
full_update = 0;
for(i = 0; i < 16; i++) {
v = s->ar[i];
if (s->ar[0x10] & 0x80)
v = ((s->ar[0x14] & 0xf) << 4) | (v & 0xf);
else
v = ((s->ar[0x14] & 0xc) << 4) | (v & 0x3f);
v = v * 3;
col = (c6_to_8(s->palette[v]) << 16) |
(c6_to_8(s->palette[v + 1]) << 8) |
c6_to_8(s->palette[v + 2]);
if (col != palette[i]) {
full_update = 1;
palette[i] = col;
}
}
return full_update;
}
/* the text refresh is just for debugging and initial boot message, so
it is very incomplete */
static void vga_text_refresh(VGAState *s,
SimpleFBDrawFunc *redraw_func, void *opaque)
{
FBDevice *fb_dev = s->fb_dev;
int width, height, cwidth, cheight, cy, cx, x1, y1, width1, height1;
int cx_min, cx_max, dup9;
uint32_t ch_attr, line_offset, start_addr, ch_addr, ch_addr1, ch, cattr;
uint8_t *vga_ram, *font_ptr, *dst;
uint32_t fgcol, bgcol, cursor_offset, cursor_start, cursor_end;
BOOL full_update;
full_update = update_palette16(s, s->last_palette);
vga_ram = s->vga_ram;
line_offset = s->cr[0x13];
line_offset <<= 3;
line_offset >>= 1;
start_addr = s->cr[0x0d] | (s->cr[0x0c] << 8);
cheight = (s->cr[9] & 0x1f) + 1;
cwidth = 8;
if (!(s->sr[1] & 0x01))
cwidth++;
width = (s->cr[0x01] + 1);
height = s->cr[0x12] |
((s->cr[0x07] & 0x02) << 7) |
((s->cr[0x07] & 0x40) << 3);
height = (height + 1) / cheight;
width1 = width * cwidth;
height1 = height * cheight;
if (fb_dev->width < width1 || fb_dev->height < height1 ||
width > MAX_TEXT_WIDTH || height > MAX_TEXT_HEIGHT)
return; /* not enough space */
if (s->last_line_offset != line_offset ||
s->last_start_addr != start_addr ||
s->last_width != width ||
s->last_height != height) {
s->last_line_offset = line_offset;
s->last_start_addr = start_addr;
s->last_width = width;
s->last_height = height;
full_update = TRUE;
}
/* update cursor position */
cursor_offset = ((s->cr[0x0e] << 8) | s->cr[0x0f]) - start_addr;
cursor_start = s->cr[0xa];
cursor_end = s->cr[0xb];
if (cursor_offset != s->last_cursor_offset ||
cursor_start != s->last_cursor_start ||
cursor_end != s->last_cursor_end) {
/* force refresh of characters with the cursor */
if (s->last_cursor_offset < MAX_TEXT_WIDTH * MAX_TEXT_HEIGHT)
s->last_ch_attr[s->last_cursor_offset] = -1;
if (cursor_offset < MAX_TEXT_WIDTH * MAX_TEXT_HEIGHT)
s->last_ch_attr[cursor_offset] = -1;
s->last_cursor_offset = cursor_offset;
s->last_cursor_start = cursor_start;
s->last_cursor_end = cursor_end;
}
ch_addr1 = 0x18000 + (start_addr * 2);
cursor_offset = 0x18000 + (start_addr + cursor_offset) * 2;
x1 = (fb_dev->width - width1) / 2;
y1 = (fb_dev->height - height1) / 2;
#if 0
printf("text refresh %dx%d font=%dx%d start_addr=0x%x line_offset=0x%x\n",
width, height, cwidth, cheight, start_addr, line_offset);
#endif
for(cy = 0; cy < height; cy++) {
ch_addr = ch_addr1;
dst = fb_dev->fb_data + (y1 + cy * cheight) * fb_dev->stride + x1 * 4;
cx_min = width;
cx_max = -1;
for(cx = 0; cx < width; cx++) {
ch_attr = *(uint16_t *)(vga_ram + (ch_addr & 0x1fffe));
if (full_update || ch_attr != s->last_ch_attr[cy * width + cx]) {
s->last_ch_attr[cy * width + cx] = ch_attr;
cx_min = min_int(cx_min, cx);
cx_max = max_int(cx_max, cx);
ch = ch_attr & 0xff;
cattr = ch_attr >> 8;
font_ptr = vga_ram + 32 * ch;
bgcol = s->last_palette[cattr >> 4];
fgcol = s->last_palette[cattr & 0x0f];
if (cwidth == 8) {
vga_draw_glyph8(dst, fb_dev->stride, font_ptr, cheight,
fgcol, bgcol);
} else {
dup9 = 0;
if (ch >= 0xb0 && ch <= 0xdf && (s->ar[0x10] & 0x04))
dup9 = 1;
vga_draw_glyph9(dst, fb_dev->stride, font_ptr, cheight,
fgcol, bgcol, dup9);
}
/* cursor display */
if (cursor_offset == ch_addr && !(cursor_start & 0x20)) {
int line_start, line_last, h;
uint8_t *dst1;
line_start = cursor_start & 0x1f;
line_last = min_int(cursor_end & 0x1f, cheight - 1);
if (line_last >= line_start && line_start < cheight) {
h = line_last - line_start + 1;
dst1 = dst + fb_dev->stride * line_start;
if (cwidth == 8) {
vga_draw_glyph8(dst1, fb_dev->stride,
cursor_glyph,
h, fgcol, bgcol);
} else {
vga_draw_glyph9(dst1, fb_dev->stride,
cursor_glyph,
h, fgcol, bgcol, 1);
}
}
}
}
ch_addr += 2;
dst += 4 * cwidth;
}
if (cx_max >= cx_min) {
// printf("redraw %d %d %d\n", cy, cx_min, cx_max);
redraw_func(fb_dev, opaque,
x1 + cx_min * cwidth, y1 + cy * cheight,
(cx_max - cx_min + 1) * cwidth, cheight);
}
ch_addr1 += line_offset;
}
}
static void vga_refresh(FBDevice *fb_dev,
SimpleFBDrawFunc *redraw_func, void *opaque)
{
VGAState *s = fb_dev->device_opaque;
if (!(s->ar_index & 0x20)) {
/* blank */
} else if (s->gr[0x06] & 1) {
/* graphic mode (VBE) */
simplefb_refresh(fb_dev, redraw_func, opaque, s->mem_range, s->fb_page_count);
} else {
/* text mode */
vga_text_refresh(s, redraw_func, opaque);
}
}
/* force some bits to zero */
static const uint8_t sr_mask[8] = {
(uint8_t)~0xfc,
(uint8_t)~0xc2,
(uint8_t)~0xf0,
(uint8_t)~0xc0,
(uint8_t)~0xf1,
(uint8_t)~0xff,
(uint8_t)~0xff,
(uint8_t)~0x00,
};
static const uint8_t gr_mask[16] = {
(uint8_t)~0xf0, /* 0x00 */
(uint8_t)~0xf0, /* 0x01 */
(uint8_t)~0xf0, /* 0x02 */
(uint8_t)~0xe0, /* 0x03 */
(uint8_t)~0xfc, /* 0x04 */
(uint8_t)~0x84, /* 0x05 */
(uint8_t)~0xf0, /* 0x06 */
(uint8_t)~0xf0, /* 0x07 */
(uint8_t)~0x00, /* 0x08 */
(uint8_t)~0xff, /* 0x09 */
(uint8_t)~0xff, /* 0x0a */
(uint8_t)~0xff, /* 0x0b */
(uint8_t)~0xff, /* 0x0c */
(uint8_t)~0xff, /* 0x0d */
(uint8_t)~0xff, /* 0x0e */
(uint8_t)~0xff, /* 0x0f */
};
static uint32_t vga_ioport_read(VGAState *s, uint32_t addr)
{
int val, index;
/* check port range access depending on color/monochrome mode */
if ((addr >= 0x3b0 && addr <= 0x3bf && (s->msr & MSR_COLOR_EMULATION)) ||
(addr >= 0x3d0 && addr <= 0x3df && !(s->msr & MSR_COLOR_EMULATION))) {
val = 0xff;
} else {
switch(addr) {
case 0x3c0:
if (s->ar_flip_flop == 0) {
val = s->ar_index;
} else {
val = 0;
}
break;
case 0x3c1:
index = s->ar_index & 0x1f;
if (index < 21)
val = s->ar[index];
else
val = 0;
break;
case 0x3c2:
val = s->st00;
break;
case 0x3c4:
val = s->sr_index;
break;
case 0x3c5:
val = s->sr[s->sr_index];
#ifdef DEBUG_VGA_REG
printf("vga: read SR%x = 0x%02x\n", s->sr_index, val);
#endif
break;
case 0x3c7:
val = s->dac_state;
break;
case 0x3c8:
val = s->dac_write_index;
break;
case 0x3c9:
val = s->palette[s->dac_read_index * 3 + s->dac_sub_index];
if (++s->dac_sub_index == 3) {
s->dac_sub_index = 0;
s->dac_read_index++;
}
break;
case 0x3ca:
val = s->fcr;
break;
case 0x3cc:
val = s->msr;
break;
case 0x3ce:
val = s->gr_index;
break;
case 0x3cf:
val = s->gr[s->gr_index];
#ifdef DEBUG_VGA_REG
printf("vga: read GR%x = 0x%02x\n", s->gr_index, val);
#endif
break;
case 0x3b4:
case 0x3d4:
val = s->cr_index;
break;
case 0x3b5:
case 0x3d5:
val = s->cr[s->cr_index];
#ifdef DEBUG_VGA_REG
printf("vga: read CR%x = 0x%02x\n", s->cr_index, val);
#endif
break;
case 0x3ba:
case 0x3da:
/* just toggle to fool polling */
s->st01 ^= ST01_V_RETRACE | ST01_DISP_ENABLE;
val = s->st01;
s->ar_flip_flop = 0;
break;
default:
val = 0x00;
break;
}
}
#if defined(DEBUG_VGA)
printf("VGA: read addr=0x%04x data=0x%02x\n", addr, val);
#endif
return val;
}
static void vga_ioport_write(VGAState *s, uint32_t addr, uint32_t val)
{
int index;
/* check port range access depending on color/monochrome mode */
if ((addr >= 0x3b0 && addr <= 0x3bf && (s->msr & MSR_COLOR_EMULATION)) ||
(addr >= 0x3d0 && addr <= 0x3df && !(s->msr & MSR_COLOR_EMULATION)))
return;
#ifdef DEBUG_VGA
printf("VGA: write addr=0x%04x data=0x%02x\n", addr, val);
#endif
switch(addr) {
case 0x3c0:
if (s->ar_flip_flop == 0) {
val &= 0x3f;
s->ar_index = val;
} else {
index = s->ar_index & 0x1f;
switch(index) {
case 0x00 ... 0x0f:
s->ar[index] = val & 0x3f;
break;
case 0x10:
s->ar[index] = val & ~0x10;
break;
case 0x11:
s->ar[index] = val;
break;
case 0x12:
s->ar[index] = val & ~0xc0;
break;
case 0x13:
s->ar[index] = val & ~0xf0;
break;
case 0x14:
s->ar[index] = val & ~0xf0;
break;
default:
break;
}
}
s->ar_flip_flop ^= 1;
break;
case 0x3c2:
s->msr = val & ~0x10;
break;
case 0x3c4:
s->sr_index = val & 7;
break;
case 0x3c5:
#ifdef DEBUG_VGA_REG
printf("vga: write SR%x = 0x%02x\n", s->sr_index, val);
#endif
s->sr[s->sr_index] = val & sr_mask[s->sr_index];
break;
case 0x3c7:
s->dac_read_index = val;
s->dac_sub_index = 0;
s->dac_state = 3;
break;
case 0x3c8:
s->dac_write_index = val;
s->dac_sub_index = 0;
s->dac_state = 0;
break;
case 0x3c9:
s->dac_cache[s->dac_sub_index] = val;
if (++s->dac_sub_index == 3) {
memcpy(&s->palette[s->dac_write_index * 3], s->dac_cache, 3);
s->dac_sub_index = 0;
s->dac_write_index++;
}
break;
case 0x3ce:
s->gr_index = val & 0x0f;
break;
case 0x3cf:
#ifdef DEBUG_VGA_REG
printf("vga: write GR%x = 0x%02x\n", s->gr_index, val);
#endif
s->gr[s->gr_index] = val & gr_mask[s->gr_index];
break;
case 0x3b4:
case 0x3d4:
s->cr_index = val;
break;
case 0x3b5:
case 0x3d5:
#ifdef DEBUG_VGA_REG
printf("vga: write CR%x = 0x%02x\n", s->cr_index, val);
#endif
/* handle CR0-7 protection */
if ((s->cr[0x11] & 0x80) && s->cr_index <= 7) {
/* can always write bit 4 of CR7 */
if (s->cr_index == 7)
s->cr[7] = (s->cr[7] & ~0x10) | (val & 0x10);
return;
}
switch(s->cr_index) {
case 0x01: /* horizontal display end */
case 0x07:
case 0x09:
case 0x0c:
case 0x0d:
case 0x12: /* vertical display end */
s->cr[s->cr_index] = val;
break;
default:
s->cr[s->cr_index] = val;
break;
}
break;
case 0x3ba:
case 0x3da:
s->fcr = val & 0x10;
break;
}
}
#define VGA_IO(base) \
static uint32_t vga_read_ ## base(void *opaque, uint32_t addr, int size_log2)\
{\
return vga_ioport_read(opaque, base + addr);\
}\
static void vga_write_ ## base(void *opaque, uint32_t addr, uint32_t val, int size_log2)\
{\
return vga_ioport_write(opaque, base + addr, val);\
}
VGA_IO(0x3c0)
VGA_IO(0x3b4)
VGA_IO(0x3d4)
VGA_IO(0x3ba)
VGA_IO(0x3da)
static void vbe_write(void *opaque, uint32_t offset,
uint32_t val, int size_log2)
{
VGAState *s = opaque;
FBDevice *fb_dev = s->fb_dev;
if (offset == 0) {
s->vbe_index = val;
} else {
#ifdef DEBUG_VBE
printf("VBE write: index=0x%04x val=0x%04x\n", s->vbe_index, val);
#endif
switch(s->vbe_index) {
case VBE_DISPI_INDEX_ID:
if (val >= VBE_DISPI_ID0 && val <= VBE_DISPI_ID5)
s->vbe_regs[s->vbe_index] = val;
break;
case VBE_DISPI_INDEX_ENABLE:
if ((val & VBE_DISPI_ENABLED) &&
!(s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) {
/* set graphic mode */
/* XXX: resolution change not really supported */
if (s->vbe_regs[VBE_DISPI_INDEX_XRES] <= 4096 &&
s->vbe_regs[VBE_DISPI_INDEX_YRES] <= 4096 &&
(s->vbe_regs[VBE_DISPI_INDEX_XRES] * 4 *
s->vbe_regs[VBE_DISPI_INDEX_YRES]) <= fb_dev->fb_size) {
fb_dev->width = s->vbe_regs[VBE_DISPI_INDEX_XRES];
fb_dev->height = s->vbe_regs[VBE_DISPI_INDEX_YRES];
fb_dev->stride = fb_dev->width * 4;
}
s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] =
s->vbe_regs[VBE_DISPI_INDEX_XRES];
s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] =
s->vbe_regs[VBE_DISPI_INDEX_YRES];
s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0;
s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0;
}
s->vbe_regs[s->vbe_index] = val;
break;
case VBE_DISPI_INDEX_XRES:
case VBE_DISPI_INDEX_YRES:
case VBE_DISPI_INDEX_BPP:
case VBE_DISPI_INDEX_BANK:
case VBE_DISPI_INDEX_VIRT_WIDTH:
case VBE_DISPI_INDEX_VIRT_HEIGHT:
case VBE_DISPI_INDEX_X_OFFSET:
case VBE_DISPI_INDEX_Y_OFFSET:
s->vbe_regs[s->vbe_index] = val;
break;
}
}
}
static uint32_t vbe_read(void *opaque, uint32_t offset, int size_log2)
{
VGAState *s = opaque;
uint32_t val;
if (offset == 0) {
val = s->vbe_index;
} else {
if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_GETCAPS) {
switch(s->vbe_index) {
case VBE_DISPI_INDEX_XRES:
val = s->fb_dev->width;
break;
case VBE_DISPI_INDEX_YRES:
val = s->fb_dev->height;
break;
case VBE_DISPI_INDEX_BPP:
val = 32;
break;
default:
goto read_reg;
}
} else {
read_reg:
if (s->vbe_index < VBE_DISPI_INDEX_NB)
val = s->vbe_regs[s->vbe_index];
else
val = 0;
}
#ifdef DEBUG_VBE
printf("VBE read: index=0x%04x val=0x%04x\n", s->vbe_index, val);
#endif
}
return val;
}
static void simplefb_bar_set(void *opaque, int bar_num,
uint32_t addr, BOOL enabled)
{
VGAState *s = opaque;
if (bar_num == 0)
phys_mem_set_addr(s->mem_range, addr, enabled);
else
phys_mem_set_addr(s->rom_range, addr, enabled);
}
VGAState *pci_vga_init(PCIBus *bus, FBDevice *fb_dev,
int width, int height,
const uint8_t *vga_rom_buf, int vga_rom_size)
{
VGAState *s;
PCIDevice *d;
uint32_t bar_size;
PhysMemoryMap *mem_map, *port_map;
d = pci_register_device(bus, "VGA", -1, 0x1234, 0x1111, 0x00, 0x0300);
mem_map = pci_device_get_mem_map(d);
port_map = pci_device_get_port_map(d);
s = mallocz(sizeof(*s));
s->fb_dev = fb_dev;
fb_dev->width = width;
fb_dev->height = height;
fb_dev->stride = width * 4;
fb_dev->fb_size = (height * fb_dev->stride + FB_ALLOC_ALIGN - 1) & ~(FB_ALLOC_ALIGN - 1);
s->fb_page_count = fb_dev->fb_size >> DEVRAM_PAGE_SIZE_LOG2;
s->mem_range =
cpu_register_ram(mem_map, 0, fb_dev->fb_size,
DEVRAM_FLAG_DIRTY_BITS | DEVRAM_FLAG_DISABLED);
fb_dev->fb_data = s->mem_range->phys_mem;
s->pci_dev = d;
bar_size = 1;
while (bar_size < fb_dev->fb_size)
bar_size <<= 1;
pci_register_bar(d, 0, bar_size, PCI_ADDRESS_SPACE_MEM, s,
simplefb_bar_set);
if (vga_rom_size > 0) {
int rom_size;
/* align to page size */
rom_size = (vga_rom_size + DEVRAM_PAGE_SIZE - 1) & ~(DEVRAM_PAGE_SIZE - 1);
s->rom_range = cpu_register_ram(mem_map, 0, rom_size,
DEVRAM_FLAG_ROM | DEVRAM_FLAG_DISABLED);
memcpy(s->rom_range->phys_mem, vga_rom_buf, vga_rom_size);
bar_size = 1;
while (bar_size < rom_size)
bar_size <<= 1;
pci_register_bar(d, PCI_ROM_SLOT, bar_size, PCI_ADDRESS_SPACE_MEM, s,
simplefb_bar_set);
}
/* VGA memory (for simple text mode no need for callbacks) */
s->mem_range2 = cpu_register_ram(mem_map, 0xa0000, 0x20000, 0);
s->vga_ram = s->mem_range2->phys_mem;
/* standard VGA ports */
cpu_register_device(port_map, 0x3c0, 16, s, vga_read_0x3c0, vga_write_0x3c0,
DEVIO_SIZE8);
cpu_register_device(port_map, 0x3b4, 2, s, vga_read_0x3b4, vga_write_0x3b4,
DEVIO_SIZE8);
cpu_register_device(port_map, 0x3d4, 2, s, vga_read_0x3d4, vga_write_0x3d4,
DEVIO_SIZE8);
cpu_register_device(port_map, 0x3ba, 1, s, vga_read_0x3ba, vga_write_0x3ba,
DEVIO_SIZE8);
cpu_register_device(port_map, 0x3da, 1, s, vga_read_0x3da, vga_write_0x3da,
DEVIO_SIZE8);
/* VBE extension */
cpu_register_device(port_map, 0x1ce, 2, s, vbe_read, vbe_write,
DEVIO_SIZE16);
s->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID5;
s->vbe_regs[VBE_DISPI_INDEX_VIDEO_MEMORY_64K] = fb_dev->fb_size >> 16;
fb_dev->device_opaque = s;
fb_dev->refresh = vga_refresh;
return s;
}

2650
virtio.c Normal file

File diff suppressed because it is too large Load diff

146
virtio.h Normal file
View file

@ -0,0 +1,146 @@
/*
* VIRTIO driver
*
* Copyright (c) 2016 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef VIRTIO_H
#define VIRTIO_H
#include <sys/select.h>
#include "iomem.h"
#include "pci.h"
#define VIRTIO_PAGE_SIZE 4096
#if defined(EMSCRIPTEN)
#define VIRTIO_ADDR_BITS 32
#else
#define VIRTIO_ADDR_BITS 64
#endif
#if VIRTIO_ADDR_BITS == 64
typedef uint64_t virtio_phys_addr_t;
#else
typedef uint32_t virtio_phys_addr_t;
#endif
typedef struct {
/* PCI only: */
PCIBus *pci_bus;
/* MMIO only: */
PhysMemoryMap *mem_map;
uint64_t addr;
IRQSignal *irq;
} VIRTIOBusDef;
typedef struct VIRTIODevice VIRTIODevice;
#define VIRTIO_DEBUG_IO (1 << 0)
#define VIRTIO_DEBUG_9P (1 << 1)
void virtio_set_debug(VIRTIODevice *s, int debug_flags);
/* block device */
typedef void BlockDeviceCompletionFunc(void *opaque, int ret);
typedef struct BlockDevice BlockDevice;
struct BlockDevice {
int64_t (*get_sector_count)(BlockDevice *bs);
int (*read_async)(BlockDevice *bs,
uint64_t sector_num, uint8_t *buf, int n,
BlockDeviceCompletionFunc *cb, void *opaque);
int (*write_async)(BlockDevice *bs,
uint64_t sector_num, const uint8_t *buf, int n,
BlockDeviceCompletionFunc *cb, void *opaque);
void *opaque;
};
VIRTIODevice *virtio_block_init(VIRTIOBusDef *bus, BlockDevice *bs);
/* network device */
typedef struct EthernetDevice EthernetDevice;
struct EthernetDevice {
uint8_t mac_addr[6]; /* mac address of the interface */
void (*write_packet)(EthernetDevice *net,
const uint8_t *buf, int len);
void *opaque;
#if !defined(EMSCRIPTEN)
void (*select_fill)(EthernetDevice *net, int *pfd_max,
fd_set *rfds, fd_set *wfds, fd_set *efds,
int *pdelay);
void (*select_poll)(EthernetDevice *net,
fd_set *rfds, fd_set *wfds, fd_set *efds,
int select_ret);
#endif
/* the following is set by the device */
void *device_opaque;
BOOL (*device_can_write_packet)(EthernetDevice *net);
void (*device_write_packet)(EthernetDevice *net,
const uint8_t *buf, int len);
void (*device_set_carrier)(EthernetDevice *net, BOOL carrier_state);
};
VIRTIODevice *virtio_net_init(VIRTIOBusDef *bus, EthernetDevice *es);
/* console device */
typedef struct {
void *opaque;
void (*write_data)(void *opaque, const uint8_t *buf, int len);
int (*read_data)(void *opaque, uint8_t *buf, int len);
} CharacterDevice;
VIRTIODevice *virtio_console_init(VIRTIOBusDef *bus, CharacterDevice *cs);
BOOL virtio_console_can_write_data(VIRTIODevice *s);
int virtio_console_get_write_len(VIRTIODevice *s);
int virtio_console_write_data(VIRTIODevice *s, const uint8_t *buf, int buf_len);
void virtio_console_resize_event(VIRTIODevice *s, int width, int height);
/* input device */
typedef enum {
VIRTIO_INPUT_TYPE_KEYBOARD,
VIRTIO_INPUT_TYPE_MOUSE,
VIRTIO_INPUT_TYPE_TABLET,
} VirtioInputTypeEnum;
#define VIRTIO_INPUT_ABS_SCALE 32768
int virtio_input_send_key_event(VIRTIODevice *s, BOOL is_down,
uint16_t key_code);
int virtio_input_send_mouse_event(VIRTIODevice *s, int dx, int dy, int dz,
unsigned int buttons);
VIRTIODevice *virtio_input_init(VIRTIOBusDef *bus, VirtioInputTypeEnum type);
/* 9p filesystem device */
#include "fs.h"
VIRTIODevice *virtio_9p_init(VIRTIOBusDef *bus, FSDevice *fs,
const char *mount_tag);
#endif /* VIRTIO_H */

162
vmmouse.c Normal file
View file

@ -0,0 +1,162 @@
/*
* VM mouse emulation
*
* Copyright (c) 2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include "cutils.h"
#include "iomem.h"
#include "ps2.h"
#define VMPORT_MAGIC 0x564D5868
#define REG_EAX 0
#define REG_EBX 1
#define REG_ECX 2
#define REG_EDX 3
#define REG_ESI 4
#define REG_EDI 5
#define FIFO_SIZE (4 * 16)
struct VMMouseState {
PS2MouseState *ps2_mouse;
int fifo_count, fifo_rindex, fifo_windex;
BOOL enabled;
BOOL absolute;
uint32_t fifo_buf[FIFO_SIZE];
};
static void put_queue(VMMouseState *s, uint32_t val)
{
if (s->fifo_count >= FIFO_SIZE)
return;
s->fifo_buf[s->fifo_windex] = val;
if (++s->fifo_windex == FIFO_SIZE)
s->fifo_windex = 0;
s->fifo_count++;
}
static void read_data(VMMouseState *s, uint32_t *regs, int size)
{
int i;
if (size > 6 || size > s->fifo_count) {
// printf("vmmouse: read error req=%d count=%d\n", size, s->fifo_count);
s->enabled = FALSE;
return;
}
for(i = 0; i < size; i++) {
regs[i] = s->fifo_buf[s->fifo_rindex];
if (++s->fifo_rindex == FIFO_SIZE)
s->fifo_rindex = 0;
}
s->fifo_count -= size;
}
void vmmouse_send_mouse_event(VMMouseState *s, int x, int y, int dz,
int buttons)
{
int state;
if (!s->enabled) {
ps2_mouse_event(s->ps2_mouse, x, y, dz, buttons);
return;
}
if ((s->fifo_count + 4) > FIFO_SIZE)
return;
state = 0;
if (buttons & 1)
state |= 0x20;
if (buttons & 2)
state |= 0x10;
if (buttons & 4)
state |= 0x08;
if (s->absolute) {
/* range = 0 ... 65535 */
x *= 2;
y *= 2;
}
put_queue(s, state);
put_queue(s, x);
put_queue(s, y);
put_queue(s, -dz);
/* send PS/2 mouse event */
ps2_mouse_event(s->ps2_mouse, 1, 0, 0, 0);
}
void vmmouse_handler(VMMouseState *s, uint32_t *regs)
{
uint32_t cmd;
cmd = regs[REG_ECX] & 0xff;
switch(cmd) {
case 10: /* get version */
regs[REG_EBX] = VMPORT_MAGIC;
break;
case 39: /* VMMOUSE_DATA */
read_data(s, regs, regs[REG_EBX]);
break;
case 40: /* VMMOUSE_STATUS */
regs[REG_EAX] = ((s->enabled ? 0 : 0xffff) << 16) | s->fifo_count;
break;
case 41: /* VMMOUSE_COMMAND */
switch(regs[REG_EBX]) {
case 0x45414552: /* read id */
if (s->fifo_count < FIFO_SIZE) {
put_queue(s, 0x3442554a);
s->enabled = TRUE;
}
break;
case 0x000000f5: /* disable */
s->enabled = FALSE;
break;
case 0x4c455252: /* set relative */
s->absolute = 0;
break;
case 0x53424152: /* set absolute */
s->absolute = 1;
break;
}
break;
}
}
BOOL vmmouse_is_absolute(VMMouseState *s)
{
return s->absolute;
}
VMMouseState *vmmouse_init(PS2MouseState *ps2_mouse)
{
VMMouseState *s;
s = mallocz(sizeof(*s));
s->ps2_mouse = ps2_mouse;
return s;
}

96
x86_cpu.c Normal file
View file

@ -0,0 +1,96 @@
/*
* x86 CPU emulator stub
*
* Copyright (c) 2011-2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include "cutils.h"
#include "x86_cpu.h"
X86CPUState *x86_cpu_init(PhysMemoryMap *mem_map)
{
fprintf(stderr, "x86 emulator is not supported\n");
exit(1);
}
void x86_cpu_end(X86CPUState *s)
{
}
void x86_cpu_interp(X86CPUState *s, int max_cycles1)
{
}
void x86_cpu_set_irq(X86CPUState *s, BOOL set)
{
}
void x86_cpu_set_reg(X86CPUState *s, int reg, uint32_t val)
{
}
uint32_t x86_cpu_get_reg(X86CPUState *s, int reg)
{
return 0;
}
void x86_cpu_set_seg(X86CPUState *s, int seg, const X86CPUSeg *sd)
{
}
void x86_cpu_set_get_hard_intno(X86CPUState *s,
int (*get_hard_intno)(void *opaque),
void *opaque)
{
}
void x86_cpu_set_get_tsc(X86CPUState *s,
uint64_t (*get_tsc)(void *opaque),
void *opaque)
{
}
void x86_cpu_set_port_io(X86CPUState *s,
DeviceReadFunc *port_read, DeviceWriteFunc *port_write,
void *opaque)
{
}
int64_t x86_cpu_get_cycles(X86CPUState *s)
{
return 0;
}
BOOL x86_cpu_get_power_down(X86CPUState *s)
{
return FALSE;
}
void x86_cpu_flush_tlb_write_range_ram(X86CPUState *s,
uint8_t *ram_ptr, size_t ram_size)
{
}

70
x86_cpu.h Normal file
View file

@ -0,0 +1,70 @@
/*
* x86 CPU emulator
*
* Copyright (c) 2011-2017 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "iomem.h"
typedef struct X86CPUState X86CPUState;
/* get_reg/set_reg additional constants */
#define X86_CPU_REG_EIP 8
#define X86_CPU_REG_CR0 9
#define X86_CPU_REG_CR2 10
#define X86_CPU_SEG_ES 0
#define X86_CPU_SEG_CS 1
#define X86_CPU_SEG_SS 2
#define X86_CPU_SEG_DS 3
#define X86_CPU_SEG_FS 4
#define X86_CPU_SEG_GS 5
#define X86_CPU_SEG_LDT 6
#define X86_CPU_SEG_TR 7
#define X86_CPU_SEG_GDT 8
#define X86_CPU_SEG_IDT 9
typedef struct {
uint16_t sel;
uint16_t flags;
uint32_t base;
uint32_t limit;
} X86CPUSeg;
X86CPUState *x86_cpu_init(PhysMemoryMap *mem_map);
void x86_cpu_end(X86CPUState *s);
void x86_cpu_interp(X86CPUState *s, int max_cycles1);
void x86_cpu_set_irq(X86CPUState *s, BOOL set);
void x86_cpu_set_reg(X86CPUState *s, int reg, uint32_t val);
uint32_t x86_cpu_get_reg(X86CPUState *s, int reg);
void x86_cpu_set_seg(X86CPUState *s, int seg, const X86CPUSeg *sd);
void x86_cpu_set_get_hard_intno(X86CPUState *s,
int (*get_hard_intno)(void *opaque),
void *opaque);
void x86_cpu_set_get_tsc(X86CPUState *s,
uint64_t (*get_tsc)(void *opaque),
void *opaque);
void x86_cpu_set_port_io(X86CPUState *s,
DeviceReadFunc *port_read, DeviceWriteFunc *port_write,
void *opaque);
int64_t x86_cpu_get_cycles(X86CPUState *s);
BOOL x86_cpu_get_power_down(X86CPUState *s);
void x86_cpu_flush_tlb_write_range_ram(X86CPUState *s,
uint8_t *ram_ptr, size_t ram_size);

2569
x86_machine.c Normal file

File diff suppressed because it is too large Load diff