This commit is contained in:
commit
b6b7d72717
96 changed files with 37739 additions and 0 deletions
45
Changelog
Normal file
45
Changelog
Normal 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
19
MIT-LICENSE.txt
Normal 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
135
Makefile
Normal 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
65
Makefile.js
Normal 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
1
VERSION
Normal file
|
|
@ -0,0 +1 @@
|
|||
2019-12-21
|
||||
49
aes.h
Normal file
49
aes.h
Normal 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
531
block_net.c
Normal 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
311
build_filelist.c
Normal 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
120
cutils.c
Normal 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
194
cutils.h
Normal 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
22
fbuf.h
Normal 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
104
fs.c
Normal 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
211
fs.h
Normal 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
659
fs_disk.c
Normal 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;
|
||||
}
|
||||
370
fs_utils.c
Normal file
370
fs_utils.c
Normal 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
95
fs_utils.h
Normal 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
625
fs_wget.c
Normal 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
93
fs_wget.h
Normal 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
835
ide.c
Normal 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
32
ide.h
Normal 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
264
iomem.c
Normal 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
148
iomem.h
Normal 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
280
js/lib.js
Normal 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
349
jsemu.c
Normal 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
464
json.c
Normal 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
132
json.h
Normal 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
94
list.h
Normal 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
640
machine.c
Normal 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
196
machine.h
Normal 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
43
netinit.sh
Executable 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
588
pci.c
Normal 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
81
pci.h
Normal 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
342
pckbd.c
Normal 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
489
ps2.c
Normal 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
34
ps2.h
Normal 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
204
readme.txt
Normal 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
1377
riscv_cpu.c
Normal file
File diff suppressed because it is too large
Load diff
118
riscv_cpu.h
Normal file
118
riscv_cpu.h
Normal 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
304
riscv_cpu_fp_template.h
Normal 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
293
riscv_cpu_priv.h
Normal 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
1739
riscv_cpu_template.h
Normal file
File diff suppressed because it is too large
Load diff
1053
riscv_machine.c
Normal file
1053
riscv_machine.c
Normal file
File diff suppressed because it is too large
Load diff
275
sdl.c
Normal file
275
sdl.c
Normal 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
341
sha256.c
Normal 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
40
sha256.h
Normal 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
127
simplefb.c
Normal 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
314
slirp/bootp.c
Normal 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
122
slirp/bootp.h
Normal 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
139
slirp/cksum.c
Normal 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
34
slirp/debug.h
Normal 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
209
slirp/if.c
Normal 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
25
slirp/if.h
Normal 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
253
slirp/ip.h
Normal 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
351
slirp/ip_icmp.c
Normal 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
161
slirp/ip_icmp.h
Normal 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
685
slirp/ip_input.c
Normal 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
172
slirp/ip_output.c
Normal 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
56
slirp/libslirp.h
Normal 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
46
slirp/main.h
Normal 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
218
slirp/mbuf.c
Normal 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
127
slirp/mbuf.h
Normal 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
403
slirp/misc.c
Normal 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
73
slirp/misc.h
Normal 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
181
slirp/sbuf.c
Normal 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
30
slirp/sbuf.h
Normal 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
828
slirp/slirp.c
Normal 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
313
slirp/slirp.h
Normal 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
188
slirp/slirp_config.h
Normal 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
722
slirp/socket.c
Normal 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
95
slirp/socket.h
Normal 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
164
slirp/tcp.h
Normal 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
1487
slirp/tcp_input.c
Normal file
File diff suppressed because it is too large
Load diff
492
slirp/tcp_output.c
Normal file
492
slirp/tcp_output.c
Normal 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
915
slirp/tcp_subr.c
Normal 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
292
slirp/tcp_timer.c
Normal 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
127
slirp/tcp_timer.h
Normal 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
161
slirp/tcp_var.h
Normal 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
77
slirp/tcpip.h
Normal 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
43
slirp/tftp.h
Normal 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
386
slirp/udp.c
Normal 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
86
slirp/udp.h
Normal 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
87
softfp.c
Normal 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
181
softfp.h
Normal 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
1129
softfp_template.h
Normal file
File diff suppressed because it is too large
Load diff
171
softfp_template_icvt.h
Normal file
171
softfp_template_icvt.h
Normal 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
102
splitimg.c
Normal 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
835
temu.c
Normal 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
804
vga.c
Normal 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;
|
||||
}
|
||||
146
virtio.h
Normal file
146
virtio.h
Normal 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
162
vmmouse.c
Normal 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
96
x86_cpu.c
Normal 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
70
x86_cpu.h
Normal 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
2569
x86_machine.c
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue