#include <sys/types.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/cons.h>
#include <sys/random.h>
#include <sys/vkernel.h>
#include <sys/tls.h>
#include <sys/reboot.h>
#include <sys/proc.h>
#include <sys/msgbuf.h>
#include <sys/vmspace.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/sysctl.h>
#include <sys/un.h>
#include <vm/vm_page.h>
#include <vm/vm_map.h>
#include <sys/mplock2.h>
#include <sys/wait.h>
#include <machine/cpu.h>
#include <machine/globaldata.h>
#include <machine/tls.h>
#include <machine/md_var.h>
#include <machine/vmparam.h>
#include <cpu/specialreg.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <net/ethernet.h>
#include <net/bridge/if_bridgevar.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if_var.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <err.h>
#include <errno.h>
#include <assert.h>
#include <sysexits.h>
#include <pthread.h>
#define EX_VKERNEL_REBOOT 32
vm_phystable_t phys_avail[16];
vm_paddr_t Maxmem;
vm_paddr_t Maxmem_bytes;
long physmem;
int MemImageFd = -1;
struct vkdisk_info DiskInfo[VKDISK_MAX];
int DiskNum;
struct vknetif_info NetifInfo[VKNETIF_MAX];
int NetifNum;
char *pid_file;
vm_offset_t KvaStart;
vm_offset_t KvaEnd;
vm_offset_t KvaSize;
vm_offset_t virtual_start;
vm_offset_t virtual_end;
vm_offset_t virtual2_start;
vm_offset_t virtual2_end;
vm_offset_t kernel_vm_end;
vm_offset_t crashdumpmap;
vm_offset_t clean_sva;
vm_offset_t clean_eva;
struct msgbuf *msgbufp;
caddr_t ptvmmap;
vpte_t *KernelPTD;
vpte_t *KernelPTA;
void *dmap_min_address;
void *vkernel_stack;
u_int cpu_feature;
u_int cpu_feature2;
int tsc_present;
int tsc_invariant;
int tsc_mpsync;
int optcpus;
int cpu_bits;
int lwp_cpu_lock;
int real_ncpus;
int next_cpu;
int vkernel_b_arg;
int vkernel_B_arg;
int use_precise_timer = 0;
struct privatespace *CPU_prvspace;
tsc_uclock_t tsc_frequency;
tsc_uclock_t tsc_oneus_approx;
extern uint64_t KPML4phys;
static struct trapframe proc0_tf;
static void *proc0paddr;
static void init_sys_memory(char *imageFile);
static void init_kern_memory(void);
static void init_globaldata(void);
static void init_vkernel(void);
static void init_disk(char **diskExp, int *diskFlags, int diskFileNum, enum vkdisk_type type);
static void init_netif(char *netifExp[], int netifFileNum);
static void writepid(void);
static void cleanpid(void);
static int unix_connect(const char *path);
static void usage_err(const char *ctl, ...) __printflike(1, 2);
static void usage_help(_Bool);
static void init_locks(void);
static void handle_term(int);
pid_t childpid;
static int save_ac;
static int prezeromem;
static char **save_av;
int
main(int ac, char **av)
{
char *memImageFile = NULL;
char *netifFile[VKNETIF_MAX];
char *diskFile[VKDISK_MAX];
char *cdFile[VKDISK_MAX];
char *suffix;
char *endp;
char *tmp;
char *tok;
int diskFlags[VKDISK_MAX];
int netifFileNum = 0;
int diskFileNum = 0;
int cdFileNum = 0;
int bootOnDisk = -1;
int c;
int i;
int j;
int n;
int isq;
int pos;
int eflag;
int real_vkernel_enable;
int supports_sse;
size_t vsize;
size_t msize;
size_t kenv_size;
size_t kenv_size2;
int status;
struct sigaction sa;
if (getenv("LD_SHAREDLIB_BASE") == NULL) {
setenv("LD_SHAREDLIB_BASE", "0x10000000", 1);
execv(av[0], av);
fprintf(stderr, "Must run %s with full path\n", av[0]);
exit(1);
}
while ((childpid = fork()) != 0) {
bzero(&sa, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_handler = SIG_IGN;
sigaction(SIGINT, &sa, NULL);
sigaction(SIGQUIT, &sa, NULL);
sigaction(SIGHUP, &sa, NULL);
sa.sa_handler = handle_term;
sigaction(SIGTERM, &sa, NULL);
while (waitpid(childpid, &status, 0) != childpid) {
if (errno == ECHILD)
exit(1);
}
if (WEXITSTATUS(status) != EX_VKERNEL_REBOOT)
return 0;
}
save_ac = ac;
save_av = av;
eflag = 0;
pos = 0;
kenv_size = 0;
kernel_mem_readonly = 1;
optcpus = 2;
cpu_bits = 1;
vkernel_b_arg = 0;
vkernel_B_arg = 0;
lwp_cpu_lock = LCL_NONE;
real_vkernel_enable = 0;
vsize = sizeof(real_vkernel_enable);
sysctlbyname("vm.vkernel_enable", &real_vkernel_enable, &vsize, NULL,0);
if (real_vkernel_enable == 0) {
errx(1, "vm.vkernel_enable is 0, must be set "
"to 1 to execute a vkernel!");
}
real_ncpus = 1;
vsize = sizeof(real_ncpus);
sysctlbyname("hw.ncpu", &real_ncpus, &vsize, NULL, 0);
if (ac < 2)
usage_help(false);
while ((c = getopt(ac, av, "c:hsvztTl:m:n:r:R:e:i:p:I:U")) != -1) {
switch(c) {
case 'e':
n = strlen(optarg);
isq = 0;
if (eflag == 0) {
kenv_size = n + 2;
kern_envp = malloc(kenv_size);
if (kern_envp == NULL)
errx(1, "Couldn't allocate %zd bytes for kern_envp", kenv_size);
} else {
kenv_size2 = kenv_size + n + 1;
pos = kenv_size - 1;
if ((tmp = realloc(kern_envp, kenv_size2)) == NULL)
errx(1, "Couldn't reallocate %zd bytes for kern_envp", kenv_size2);
kern_envp = tmp;
kenv_size = kenv_size2;
}
for (i = 0, j = pos; i < n; ++i) {
if (optarg[i] == '"')
isq ^= 1;
else if (optarg[i] == '\'')
isq ^= 2;
else if (isq == 0 && optarg[i] == ':')
kern_envp[j++] = 0;
else
kern_envp[j++] = optarg[i];
}
kern_envp[j++] = 0;
kern_envp[j++] = 0;
eflag++;
break;
case 's':
boothowto |= RB_SINGLE;
break;
case 't':
use_precise_timer = 1;
break;
case 'v':
bootverbose = 1;
break;
case 'i':
memImageFile = optarg;
break;
case 'I':
if (netifFileNum < VKNETIF_MAX)
netifFile[netifFileNum++] = strdup(optarg);
break;
case 'r':
case 'R':
if (bootOnDisk < 0)
bootOnDisk = 1;
if (diskFileNum + cdFileNum < VKDISK_MAX) {
diskFile[diskFileNum] = strdup(optarg);
diskFlags[diskFileNum] = (c == 'R');
++diskFileNum;
}
break;
case 'c':
if (bootOnDisk < 0)
bootOnDisk = 0;
if (diskFileNum + cdFileNum < VKDISK_MAX)
cdFile[cdFileNum++] = strdup(optarg);
break;
case 'm':
Maxmem_bytes = strtoull(optarg, &suffix, 0);
if (suffix) {
switch(*suffix) {
case 'g':
case 'G':
Maxmem_bytes <<= 30;
break;
case 'm':
case 'M':
Maxmem_bytes <<= 20;
break;
case 'k':
case 'K':
Maxmem_bytes <<= 10;
break;
default:
Maxmem_bytes = 0;
usage_err("Bad maxmem option");
break;
}
}
break;
case 'l':
next_cpu = -1;
if (strncmp("map", optarg, 3) == 0) {
lwp_cpu_lock = LCL_PER_CPU;
if (optarg[3] == ',') {
next_cpu = strtol(optarg+4, &endp, 0);
if (*endp != '\0')
usage_err("Bad target CPU number at '%s'", endp);
} else {
next_cpu = 0;
}
if (next_cpu < 0 || next_cpu > real_ncpus - 1)
usage_err("Bad target CPU, valid range is 0-%d", real_ncpus - 1);
} else if (strncmp("any", optarg, 3) == 0) {
lwp_cpu_lock = LCL_NONE;
} else {
lwp_cpu_lock = LCL_SINGLE_CPU;
next_cpu = strtol(optarg, &endp, 0);
if (*endp != '\0')
usage_err("Bad target CPU number at '%s'", endp);
if (next_cpu < 0 || next_cpu > real_ncpus - 1)
usage_err("Bad target CPU, valid range is 0-%d", real_ncpus - 1);
}
break;
case 'n':
tok = strtok(optarg, ":");
optcpus = strtol(tok, NULL, 0);
if (optcpus < 1 || optcpus > MAXCPU)
usage_err("Bad ncpus, valid range is 1-%d", MAXCPU);
cpu_bits = 1;
while ((1 << cpu_bits) < optcpus)
++cpu_bits;
vkernel_b_arg = 1;
vkernel_B_arg = cpu_bits - vkernel_b_arg;
tok = strtok(NULL, ":");
if (tok != NULL) {
vkernel_b_arg = strtol(tok, NULL, 0);
vkernel_B_arg = cpu_bits - vkernel_b_arg;
tok = strtok(NULL, ":");
if (tok != NULL) {
vkernel_B_arg = strtol(tok, NULL, 0);
}
}
break;
case 'p':
pid_file = optarg;
break;
case 'U':
kernel_mem_readonly = 0;
break;
case 'h':
usage_help(true);
break;
case 'z':
prezeromem = 1;
break;
default:
usage_help(false);
}
}
writepid();
cpu_disable_intr();
init_sys_memory(memImageFile);
init_kern_memory();
init_globaldata();
init_vkernel();
setrealcpu();
init_kqueue();
vmm_guest = VMM_GUEST_VKERNEL;
vsize = sizeof(tsc_present);
sysctlbyname("hw.tsc_present", &tsc_present, &vsize, NULL, 0);
vsize = sizeof(tsc_invariant);
sysctlbyname("hw.tsc_invariant", &tsc_invariant, &vsize, NULL, 0);
vsize = sizeof(tsc_mpsync);
sysctlbyname("hw.tsc_mpsync", &tsc_mpsync, &vsize, NULL, 0);
vsize = sizeof(tsc_frequency);
sysctlbyname("hw.tsc_frequency", &tsc_frequency, &vsize, NULL, 0);
if (tsc_present)
cpu_feature |= CPUID_TSC;
tsc_oneus_approx = ((tsc_frequency|1) + 999999) / 1000000;
vsize = sizeof(supports_sse);
supports_sse = 0;
sysctlbyname("hw.instruction_sse", &supports_sse, &vsize, NULL, 0);
msize = sizeof(npx_mxcsr_mask);
sysctlbyname("hw.mxcsr_mask", &npx_mxcsr_mask, &msize, NULL, 0);
init_fpu(supports_sse);
if (supports_sse)
cpu_feature |= CPUID_SSE | CPUID_FXSR;
if (bootOnDisk == 1) {
init_disk(diskFile, diskFlags, diskFileNum, VKD_DISK);
init_disk(cdFile, NULL, cdFileNum, VKD_CD);
} else {
init_disk(cdFile, NULL, cdFileNum, VKD_CD);
init_disk(diskFile, diskFlags, diskFileNum, VKD_DISK);
}
init_netif(netifFile, netifFileNum);
init_exceptions();
mi_startup();
exit(EX_SOFTWARE);
}
static
void
handle_term(int sig)
{
kill(childpid, sig);
}
static
void
init_sys_memory(char *imageFile)
{
int fd;
char *tmpfile;
if (imageFile != NULL) {
fprintf(stderr,
"WARNING: -i option is deprecated and ignored.\n"
" Memory is now always anonymous (unlinked file).\n");
}
if (Maxmem_bytes == 0) {
errx(1, "System memory size must be specified with -m");
}
if (Maxmem_bytes < 64 * 1024 * 1024 || (Maxmem_bytes & SEG_MASK)) {
errx(1, "Bad maxmem specification: 64MB minimum, "
"multiples of %dMB only",
SEG_SIZE / 1024 / 1024);
}
asprintf(&tmpfile, "/var/vkernel/.memimg.%d", (int)getpid());
fd = open(tmpfile, O_RDWR|O_CREAT|O_EXCL, 0600);
if (fd < 0)
err(1, "Unable to create %s", tmpfile);
unlink(tmpfile);
free(tmpfile);
fprintf(stderr, "Using anonymous memory (%llu MB)\n",
(unsigned long long)Maxmem_bytes / (1024 * 1024));
if (ftruncate(fd, Maxmem_bytes) < 0) {
err(1, "Unable to size memory backing file");
}
MemImageFd = fd;
Maxmem = Maxmem_bytes >> PAGE_SHIFT;
physmem = Maxmem;
}
static
void
init_kern_memory(void)
{
void *base;
int i;
void *firstfree;
base = mmap((void*)KERNEL_KVA_START, KERNEL_KVA_SIZE,
PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_FILE|MAP_SHARED|MAP_VPAGETABLE|MAP_FIXED|MAP_TRYFIXED,
MemImageFd, (off_t)KERNEL_KVA_START);
if (base == MAP_FAILED) {
err(1, "Unable to mmap() kernel virtual memory!");
}
madvise(base, KERNEL_KVA_SIZE, MADV_NOSYNC);
KvaStart = (vm_offset_t)base;
KvaSize = KERNEL_KVA_SIZE;
KvaEnd = KvaStart + KvaSize;
printf("KVM mapped at %p-%p\n", (void *)KvaStart, (void *)KvaEnd);
dmap_min_address = mmap(0, DMAP_SIZE, PROT_READ|PROT_WRITE,
MAP_NOCORE|MAP_NOSYNC|MAP_SHARED,
MemImageFd, 0);
if (dmap_min_address == MAP_FAILED) {
err(1, "Unable to mmap() kernel DMAP region!");
}
if (prezeromem)
bzero(dmap_min_address, Maxmem_bytes);
firstfree = NULL;
pmap_bootstrap((vm_paddr_t *)&firstfree, (int64_t)base);
mcontrol(base, KERNEL_KVA_SIZE, MADV_SETMAP,
0 | VPTE_RW | VPTE_V);
phys_avail[0].phys_beg = (vm_paddr_t)firstfree;
phys_avail[0].phys_beg = (phys_avail[0].phys_beg + PAGE_MASK) &
~(vm_paddr_t)PAGE_MASK;
phys_avail[0].phys_end = Maxmem_bytes;
#if 0
virtual_start = KvaStart + (long)firstfree;
virtual_start = (virtual_start + PAGE_MASK) & ~(vm_offset_t)PAGE_MASK;
virtual_end = KvaStart + KERNEL_KVA_SIZE;
#endif
kernel_vm_end = 0;
proc0paddr = (void *)virtual_start;
for (i = 0; i < UPAGES; ++i) {
pmap_kenter_quick(virtual_start, phys_avail[0].phys_beg);
virtual_start += PAGE_SIZE;
phys_avail[0].phys_beg += PAGE_SIZE;
}
crashdumpmap = virtual_start;
virtual_start += MAXDUMPPGS * PAGE_SIZE;
assert((MSGBUF_SIZE & PAGE_MASK) == 0);
msgbufp = (void *)virtual_start;
for (i = 0; i < (MSGBUF_SIZE >> PAGE_SHIFT); ++i) {
pmap_kenter_quick(virtual_start, phys_avail[0].phys_beg);
virtual_start += PAGE_SIZE;
phys_avail[0].phys_beg += PAGE_SIZE;
}
msgbufinit(msgbufp, MSGBUF_SIZE);
ptvmmap = (caddr_t)virtual_start;
virtual_start += PAGE_SIZE;
}
static
void
init_globaldata(void)
{
int i;
vm_paddr_t pa;
vm_offset_t va;
virtual_start = (virtual_start + SEG_MASK) & ~(vm_offset_t)SEG_MASK;
CPU_prvspace = (void *)virtual_start;
virtual_start += sizeof(struct privatespace) * SMP_MAXCPU;
for (i = 0; i < sizeof(struct mdglobaldata); i += PAGE_SIZE) {
pa = phys_avail[0].phys_beg;
va = (vm_offset_t)&CPU_prvspace[0].mdglobaldata + i;
pmap_kenter_quick(va, pa);
phys_avail[0].phys_beg += PAGE_SIZE;
}
for (i = 0; i < sizeof(CPU_prvspace[0].idlestack); i += PAGE_SIZE) {
pa = phys_avail[0].phys_beg;
va = (vm_offset_t)&CPU_prvspace[0].idlestack + i;
pmap_kenter_quick(va, pa);
phys_avail[0].phys_beg += PAGE_SIZE;
}
tls_set_gs(&CPU_prvspace[0], sizeof(struct privatespace));
}
static void
init_locks(void)
{
cpu_get_initial_mplock();
lwkt_token_pool_init();
}
static
void
init_vkernel(void)
{
struct mdglobaldata *gd;
gd = &CPU_prvspace[0].mdglobaldata;
bzero(gd, sizeof(*gd));
gd->mi.gd_curthread = &thread0;
thread0.td_gd = &gd->mi;
ncpus = 1;
ncpus_fit = 1;
init_param1();
gd->mi.gd_prvspace = &CPU_prvspace[0];
mi_gdinit(&gd->mi, 0);
cpu_gdinit(gd, 0);
mi_proc0init(&gd->mi, proc0paddr);
lwp0.lwp_md.md_regs = &proc0_tf;
init_locks();
cninit();
rand_initialize();
#if 0
kdb_init();
if (boothowto & RB_KDB)
Debugger("Boot flags requested debugger");
#endif
identcpu();
#if 0
initializecpu();
#endif
init_param2((phys_avail[0].phys_end -
phys_avail[0].phys_beg) / PAGE_SIZE);
#if 0
for (off = 0; off < round_page(MSGBUF_SIZE); off += PAGE_SIZE)
pmap_kenter((vm_offset_t)msgbufp + off, avail_end + off);
msgbufinit(msgbufp, MSGBUF_SIZE);
#endif
#if 0
thread0.td_pcb_cr3 ... MMU
lwp0.lwp_md.md_regs = &proc0_tf;
#endif
}
static
void
init_disk(char **diskExp, int *diskFlags, int diskFileNum, enum vkdisk_type type)
{
char *serno;
int i;
if (diskFileNum == 0)
return;
for (i=0; i < diskFileNum; i++){
char *fname;
fname = diskExp[i];
if (fname == NULL) {
warnx("Invalid argument to '-r'");
continue;
}
serno = fname;
strsep(&serno, ":");
if (DiskNum < VKDISK_MAX) {
struct stat st;
struct vkdisk_info *info = NULL;
int fd;
size_t l = 0;
if (type == VKD_DISK)
fd = open(fname, O_RDWR|O_DIRECT, 0644);
else
fd = open(fname, O_RDONLY|O_DIRECT, 0644);
if (fd < 0 || fstat(fd, &st) < 0) {
err(1, "Unable to open/create %s", fname);
}
if (S_ISREG(st.st_mode) && (diskFlags[i] & 1) == 0) {
if (flock(fd, LOCK_EX|LOCK_NB) < 0) {
errx(1, "Disk image %s is already "
"in use\n", fname);
}
}
info = &DiskInfo[DiskNum];
l = strlen(fname);
info->unit = i;
info->fd = fd;
info->type = type;
info->flags = diskFlags[i];
memcpy(info->fname, fname, l);
info->serno = NULL;
if (serno) {
if ((info->serno = malloc(SERNOLEN)) != NULL)
strlcpy(info->serno, serno, SERNOLEN);
else
warnx("Couldn't allocate memory for the operation");
}
if (DiskNum == 0) {
if (type == VKD_CD) {
rootdevnames[0] = "cd9660:vcd0";
} else if (type == VKD_DISK) {
rootdevnames[0] = "ufs:vkd0s0a";
rootdevnames[1] = "ufs:vkd0s1a";
}
}
DiskNum++;
} else {
warnx("vkd%d (%s) > VKDISK_MAX", DiskNum, fname);
continue;
}
}
}
static
int
netif_set_tapflags(int tap_unit, int f, int s)
{
struct ifreq ifr;
int flags;
bzero(&ifr, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "tap%d", tap_unit);
if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) {
warn("tap%d: ioctl(SIOCGIFFLAGS) failed", tap_unit);
return -1;
}
flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16);
if (f < 0) {
f = -f;
if ((flags & f) == 0)
return 0;
flags &= ~f;
} else {
if (flags & f)
return 0;
flags |= f;
}
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "tap%d", tap_unit);
ifr.ifr_flags = flags & 0xffff;
ifr.ifr_flagshigh = flags >> 16;
if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) {
warn("tap%d: ioctl(SIOCSIFFLAGS) failed", tap_unit);
return -1;
}
return 0;
}
static
int
netif_set_tapaddr(int tap_unit, in_addr_t addr, in_addr_t mask, int s)
{
struct ifaliasreq ifra;
struct sockaddr_in *in;
bzero(&ifra, sizeof(ifra));
snprintf(ifra.ifra_name, sizeof(ifra.ifra_name), "tap%d", tap_unit);
in = (struct sockaddr_in *)&ifra.ifra_addr;
in->sin_family = AF_INET;
in->sin_len = sizeof(*in);
in->sin_addr.s_addr = addr;
if (mask != 0) {
in = (struct sockaddr_in *)&ifra.ifra_mask;
in->sin_len = sizeof(*in);
in->sin_addr.s_addr = mask;
}
if (ioctl(s, SIOCAIFADDR, &ifra) < 0) {
warn("tap%d: ioctl(SIOCAIFADDR) failed", tap_unit);
return -1;
}
return 0;
}
static
int
netif_add_tap2brg(int tap_unit, const char *ifbridge, int s)
{
struct ifbreq ifbr;
struct ifdrv ifd;
bzero(&ifbr, sizeof(ifbr));
snprintf(ifbr.ifbr_ifsname, sizeof(ifbr.ifbr_ifsname),
"tap%d", tap_unit);
bzero(&ifd, sizeof(ifd));
strlcpy(ifd.ifd_name, ifbridge, sizeof(ifd.ifd_name));
ifd.ifd_cmd = BRDGADD;
ifd.ifd_len = sizeof(ifbr);
ifd.ifd_data = &ifbr;
if (ioctl(s, SIOCSDRVSPEC, &ifd) < 0) {
if (errno != EEXIST) {
warn("ioctl(%s, SIOCSDRVSPEC) failed", ifbridge);
return -1;
}
}
return 0;
}
#define TAPDEV_OFLAGS (O_RDWR | O_NONBLOCK)
static
int
netif_open_tap(const char *netif, int *tap_unit, int s)
{
char tap_dev[MAXPATHLEN];
int tap_fd, failed;
struct stat st;
char *dname;
*tap_unit = -1;
if (strcmp(netif, "auto") == 0) {
tap_fd = open("/dev/tap", TAPDEV_OFLAGS);
if (tap_fd < 0) {
warnc(errno, "Unable to find a free tap(4)");
return -1;
}
} else {
if (netif[0] == '/')
strlcpy(tap_dev, netif, sizeof(tap_dev));
else
snprintf(tap_dev, sizeof(tap_dev), "/dev/%s", netif);
tap_fd = open(tap_dev, TAPDEV_OFLAGS);
if (tap_fd < 0)
tap_fd = unix_connect(tap_dev);
if (tap_fd < 0) {
warn("Unable to open %s", tap_dev);
return -1;
}
}
if (fstat(tap_fd, &st) < 0) {
failed = 1;
} else if (S_ISCHR(st.st_mode)) {
dname = fdevname(tap_fd);
if (dname)
dname = strstr(dname, "tap");
if (dname) {
*tap_unit = strtol(dname + 3, NULL, 10);
printf("TAP UNIT %d\n", *tap_unit);
if (netif_set_tapflags(*tap_unit, IFF_UP, s) == 0)
failed = 0;
else
failed = 1;
} else {
failed = 1;
}
} else if (S_ISSOCK(st.st_mode)) {
failed = 0;
} else {
failed = 1;
}
if (failed) {
warnx("%s is not a tap(4) device or socket", tap_dev);
close(tap_fd);
tap_fd = -1;
*tap_unit = -1;
}
return tap_fd;
}
static int
unix_connect(const char *path)
{
struct sockaddr_un sunx;
int len;
int net_fd;
int sndbuf = 262144;
struct stat st;
snprintf(sunx.sun_path, sizeof(sunx.sun_path), "%s", path);
len = offsetof(struct sockaddr_un, sun_path[strlen(sunx.sun_path)]);
++len;
sunx.sun_family = AF_UNIX;
sunx.sun_len = len;
net_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (net_fd < 0)
return(-1);
if (connect(net_fd, (void *)&sunx, len) < 0) {
close(net_fd);
return(-1);
}
setsockopt(net_fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));
if (fstat(net_fd, &st) == 0)
printf("Network socket buffer: %ld bytes\n", st.st_blksize);
fcntl(net_fd, F_SETFL, O_NONBLOCK);
return(net_fd);
}
#undef TAPDEV_MAJOR
#undef TAPDEV_MINOR
#undef TAPDEV_OFLAGS
static
int
netif_init_tap(int tap_unit, in_addr_t *addr, in_addr_t *mask, int s)
{
in_addr_t tap_addr, netmask, netif_addr;
int next_netif_addr;
char *tok, *masklen_str, *ifbridge;
*addr = 0;
*mask = 0;
tok = strtok(NULL, ":/");
if (tok == NULL) {
return 0;
}
if (inet_pton(AF_INET, tok, &tap_addr) > 0) {
ifbridge = NULL;
next_netif_addr = 0;
} else {
tap_addr = 0;
ifbridge = tok;
next_netif_addr = 1;
}
netmask = netif_addr = 0;
tok = strtok(NULL, ":/");
if (tok == NULL)
goto back;
if (inet_pton(AF_INET, tok, &netif_addr) <= 0) {
if (next_netif_addr) {
warnx("Invalid pseudo netif address: %s", tok);
return -1;
}
netif_addr = 0;
masklen_str = tok;
} else {
masklen_str = strtok(NULL, "/");
}
if (masklen_str != NULL) {
u_long masklen;
masklen = strtoul(masklen_str, NULL, 10);
if (masklen < 32 && masklen > 0) {
netmask =
htonl(rounddown2(0xffffffff, 1LL << (32 - masklen)));
} else {
warnx("Invalid netmask len: %lu", masklen);
return -1;
}
}
if (strtok(NULL, ":/") != NULL) {
warnx("Invalid argument to '-I'");
return -1;
}
back:
if (tap_unit < 0) {
} else if (ifbridge == NULL) {
if (netif_set_tapaddr(tap_unit, tap_addr, netmask, s) < 0)
return -1;
} else {
if (netif_add_tap2brg(tap_unit, ifbridge, s) < 0)
return -1;
}
*addr = netif_addr;
*mask = netmask;
return 0;
}
static
void
init_netif(char *netifExp[], int netifExpNum)
{
int i, s;
char *tmp;
if (netifExpNum == 0)
return;
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s < 0)
return;
for (i = 0; i < netifExpNum; ++i) {
struct vknetif_info *info;
in_addr_t netif_addr, netif_mask;
int tap_fd, tap_unit;
char *netif;
tmp = netifExp[i];
strsep(&tmp, "=");
netif = strtok(netifExp[i], ":");
if (netif == NULL) {
warnx("Invalid argument to '-I'");
continue;
}
tap_fd = netif_open_tap(netif, &tap_unit, s);
if (tap_fd < 0)
continue;
if (netif_init_tap(tap_unit, &netif_addr, &netif_mask, s) < 0) {
close(tap_fd);
continue;
}
info = &NetifInfo[NetifNum];
bzero(info, sizeof(*info));
info->tap_fd = tap_fd;
info->tap_unit = tap_unit;
info->netif_addr = netif_addr;
info->netif_mask = netif_mask;
if (tmp != NULL) {
if ((info->enaddr = malloc(ETHER_ADDR_LEN)) == NULL)
warnx("Couldn't allocate memory for the operation");
else {
if ((kether_aton(tmp, info->enaddr)) == NULL) {
free(info->enaddr);
info->enaddr = NULL;
}
}
}
NetifNum++;
if (NetifNum >= VKNETIF_MAX)
break;
}
close(s);
}
static
void
writepid(void)
{
char buf[32];
int fd;
if (pid_file != NULL) {
snprintf(buf, sizeof(buf), "%ld\n", (long)getpid());
fd = open(pid_file, O_RDWR|O_CREAT|O_EXLOCK|O_NONBLOCK, 0666);
if (fd < 0) {
if (errno == EWOULDBLOCK) {
perror("Failed to lock pidfile, "
"vkernel already running");
} else {
perror("Failed to create pidfile");
}
exit(EX_SOFTWARE);
}
ftruncate(fd, 0);
write(fd, buf, strlen(buf));
}
}
static
void
cleanpid( void )
{
if (pid_file != NULL) {
if (unlink(pid_file) < 0)
perror("Warning: couldn't remove pidfile");
}
}
static
void
usage_err(const char *ctl, ...)
{
va_list va;
va_start(va, ctl);
vfprintf(stderr, ctl, va);
va_end(va);
fprintf(stderr, "\n");
exit(EX_USAGE);
}
static
void
usage_help(_Bool help)
{
fprintf(stderr, "Usage: %s [-hsUvdt] [-c file] [-e name=value:name=value:...]\n"
"\t[-i file] [-I interface[:address1[:address2][/netmask]]] [-l cpulock]\n"
"\t[-m size] [-n numcpus[:lbits[:cbits]]]\n"
"\t[-p file] [-r file]\n", save_av[0]);
if (help)
fprintf(stderr, "\nArguments:\n"
"\t-c\tSpecify a readonly CD-ROM image file to be used by the kernel.\n"
"\t-e\tSpecify an environment to be used by the kernel.\n"
"\t-h\tThis list of options.\n"
"\t-i\t(DEPRECATED) Memory is now always anonymous.\n"
"\t-I\tCreate a virtual network device.\n"
"\t-l\tSpecify which, if any, real CPUs to lock virtual CPUs to.\n"
"\t-m\tSpecify the amount of memory to be used by the kernel in bytes (required).\n"
"\t-n\tSpecify the number of CPUs and the topology you wish to emulate:\n"
"\t\t\tnumcpus - number of cpus\n"
"\t\t\tlbits - specify the number of bits within APICID(=CPUID)\n"
"\t\t\t needed for representing the logical ID.\n"
"\t\t\t Controls the number of threads/core:\n"
"\t\t\t (0 bits - 1 thread, 1 bit - 2 threads).\n"
"\t\t\tcbits - specify the number of bits within APICID(=CPUID)\n"
"\t\t\t needed for representing the core ID.\n"
"\t\t\t Controls the number of cores/package:\n"
"\t\t\t (0 bits - 1 core, 1 bit - 2 cores).\n"
"\t-p\tSpecify a file in which to store the process ID.\n"
"\t-r\tSpecify a R/W disk image file, iterates vkd0..n\n"
"\t-R\tSpecify a COW disk image file, iterates vkd0..n\n"
"\t-s\tBoot into single-user mode.\n"
"\t-t\tUse a precise host timer when calculating clock values.\n"
"\t-U\tEnable writing to kernel memory and module loading.\n"
"\t-v\tTurn on verbose booting.\n");
exit(EX_USAGE);
}
void
cpu_smp_stopped(void)
{
}
void
cpu_reset(void)
{
kprintf("cpu reset, rebooting vkernel\n");
closefrom(3);
cleanpid();
exit(EX_VKERNEL_REBOOT);
}
void
cpu_halt(void)
{
kprintf("cpu halt, exiting vkernel\n");
cleanpid();
exit(EX_OK);
}
void
setrealcpu(void)
{
switch(lwp_cpu_lock) {
case LCL_PER_CPU:
if (bootverbose)
kprintf("Locking CPU%d to real cpu %d\n",
mycpuid, next_cpu);
usched_set(getpid(), USCHED_SET_CPU, &next_cpu, sizeof(next_cpu));
next_cpu++;
if (next_cpu >= real_ncpus)
next_cpu = 0;
break;
case LCL_SINGLE_CPU:
if (bootverbose)
kprintf("Locking CPU%d to real cpu %d\n",
mycpuid, next_cpu);
usched_set(getpid(), USCHED_SET_CPU, &next_cpu, sizeof(next_cpu));
break;
default:
break;
}
}
int
vkernel_module_memory_alloc(vm_offset_t *basep, size_t bytes)
{
#if 1
size_t xtra;
xtra = (PAGE_SIZE - (vm_offset_t)sbrk(0)) & PAGE_MASK;
*basep = (vm_offset_t)sbrk(xtra + bytes) + xtra;
bzero((void *)*basep, bytes);
#else
*basep = (vm_offset_t)mmap((void *)0x000000000, bytes,
PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_ANON|MAP_SHARED, -1, 0);
if ((void *)*basep == MAP_FAILED)
return ENOMEM;
#endif
return 0;
}
void
vkernel_module_memory_free(vm_offset_t base, size_t bytes)
{
#if 0
#if 0
munmap((void *)base, bytes);
#endif
#endif
}
void
vkernel_yield(void)
{
pthread_yield();
}