6 Commits

Author SHA1 Message Date
Travis Geiselbrecht
804de7cec2 WIP telnetd 2022-03-19 14:46:01 -07:00
Travis Geiselbrecht
8b81805b0e WIP start of IRC app 2022-03-19 14:46:01 -07:00
Travis Geiselbrecht
fe28bd8a95 [lib][minip] add a mechanism to wait for the stack to be configured
Configured in this case means an ip address assigned and a nic
installed.
2022-03-19 14:46:01 -07:00
Travis Geiselbrecht
9caf62273c [lib][minip] start of TCP connect state machine
A few miscellaneous tweaks outside of that.
2022-03-19 14:43:55 -07:00
Travis Geiselbrecht
dc09bac586 [lib][elf] tweak to a printf to be more useful 2022-03-13 13:49:28 -07:00
Travis Geiselbrecht
ce9f4f6db2 [arch][riscv] using a new trick utilize real instructions to zero fpu state
Previously was hard coding the instructions to work around a limitation
of the assembler that did not allow using fpu instructions when the code
was being compiled without support. Move the zeroing routine into a
separate assembly file and override the architure at the top.
2022-03-12 17:39:00 -08:00
30 changed files with 916 additions and 142 deletions

338
app/irc/irc.cpp Normal file
View File

@@ -0,0 +1,338 @@
/*
* Copyright (c) 2021 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#include <string.h>
#include <stdio.h>
#include <lk/debug.h>
#include <lk/err.h>
#include <lk/trace.h>
#include <app.h>
#include <lib/minip.h>
#include <kernel/thread.h>
#include <kernel/mutex.h>
#include "lktl.h"
#define LOCAL_TRACE 1
//#define IRC_SERVER IPV4(176,58,122,119) // irc.libera.chat
#define IRC_SERVER IPV4(88,99,244,30) // irc.sortix.org
//#define IRC_SERVER IPV4(192,168,1,110) // localhost
#define IRC_PORT 6667
#define IRC_USER "geist"
#define IRC_NICK "geist-lk"
#define IRC_CHAN "#sortix"
//#define IRC_CHAN "#osdev"
class irc_client {
public:
irc_client();
~irc_client();
void init();
void shutdown();
status_t connect();
status_t handshake();
status_t read_loop();
status_t console_input_line(const char *line, bool &exit);
void set_server(uint32_t server) { server_ip_ = server; }
void set_server_port(uint16_t port) { server_port_ = port; }
private:
mutex_t lock_ = MUTEX_INITIAL_VALUE(lock_);
tcp_socket_t *sock_ = nullptr;
uint32_t server_ip_ = 0;
uint16_t server_port_ = 0;
enum {
INITIAL,
CONNECTED,
HANDSHOOK,
} state_ = INITIAL;
};
irc_client::irc_client() = default;
irc_client::~irc_client() = default;
void irc_client::init() {
}
void irc_client::shutdown() {
lktl::auto_lock al(&lock_);
if (sock_) {
if (state_ == HANDSHOOK) {
// send quit
tcp_write(sock_, "QUIT\r\n", strlen("QUIT\r\n"));
// XXX flush socket
thread_sleep(1000);
}
tcp_close(sock_);
sock_ = nullptr;
}
}
status_t irc_client::connect() {
lktl::auto_lock al(&lock_);
if (server_ip_ == 0 || server_port_ == 0) {
return ERR_NOT_CONFIGURED;
}
auto err = tcp_connect(&sock_, server_ip_, server_port_);
if (err < 0) {
printf("err %d connecting to server\n", err);
return ERR_CHANNEL_CLOSED; // TODO: better one
}
state_ = CONNECTED;
return NO_ERROR;
}
status_t irc_client::read_loop() {
char line[1024];
int pos = 0;
for (;;) {
char c;
ssize_t r = tcp_read(sock_, &c, sizeof(c));
if (r < 0) {
return r;
}
if (r == 0) {
TRACEF("tcp_read returns 0?\n");
return ERR_GENERIC;
}
// TODO: make sure we dont overwrite the line
// append the char to our accumulated line
if (c == '\r') {
// consume \r
continue;
}
// store the char
line[pos++] = c;
if (c != '\n') {
// we're done, loop around
continue;
}
// we've completed a line, process it
line[pos] = 0; // terminate the string
pos = 0; // next time around we start over
lktl::auto_lock al(&lock_);
if (strncmp(line, "PING", strlen("PING"))== 0) {
// handle a PONG
tcp_write(sock_, "PONG\r\n", strlen("PONG\r\n"));
printf("%s", line);
printf("PING/PONG\n");
} else {
printf("%s", line);
}
}
return NO_ERROR;
}
status_t irc_client::console_input_line(const char *line, bool &exit) {
//printf("CONSOLE LINE '%s'\n", line);
if (strlen(line) == 0) {
return NO_ERROR;
}
// see if it starts with /
if (line[0] == '/') {
if (line[1] == 0) {
// malformed command
return NO_ERROR;
}
// look for quit command
if (strncmp(&line[1], "quit", strlen("quit")) == 0) {
shutdown();
exit = true;
return NO_ERROR;
} else {
printf("bad command\n");
return NO_ERROR;
}
}
lktl::auto_lock al(&lock_);
// send it as a privmsg
char buf[256];
snprintf(buf, sizeof(buf), "PRIVMSG #sortix :%s\r\n", line);
status_t err = tcp_write(sock_, buf, strlen(buf));
return err;
}
status_t irc_client::handshake() {
lktl::auto_lock al(&lock_);
// send USER and NICK
char buf[128];
snprintf(buf, sizeof(buf), "USER %s host server :geist\r\n", IRC_USER);
status_t err = tcp_write(sock_, buf, strlen(buf));
if (err < 0) {
printf("error %d writing to server\n", err);
return err;
}
snprintf(buf, sizeof(buf), "NICK %s\r\n", IRC_NICK);
err = tcp_write(sock_, buf, strlen(buf));
if (err < 0) {
printf("error %d writing to server\n", err);
return err;
}
snprintf(buf, sizeof(buf), "JOIN %s\r\n", IRC_CHAN);
err = tcp_write(sock_, buf, strlen(buf));
if (err < 0) {
printf("error %d writing to server\n", err);
return err;
}
state_ = HANDSHOOK;
return NO_ERROR;
}
// console worker thread
static int console_thread_worker(void *arg) {
irc_client *irc = static_cast<irc_client *>(arg);
LTRACEF("top of console thread\n");
// read a line from the console, giving it to the irc client object at EOL
status_t err;
char line[256];
int pos = 0;
bool exit = false;
while (!exit) {
int c = getchar();
if (c <= 0) {
break;
}
if (pos == sizeof(line)) {
printf("line too long, discarding\n");
pos = 0;
}
//printf("char %c (%d)\n", c, c);
switch (c) {
case '\n':
case '\r':
if (pos > 0) {
putchar('\n');
// end of a line with characters in it, feed it to the irc client
line[pos++] = 0; // null terminate
err = irc->console_input_line(line, exit);
if (err < 0) {
exit = true;
}
pos = 0;
}
break;
case '\b': // backspace
case 127: // DEL
if (pos > 0) {
pos--;
fputs("\b \b", stdout); // wipe out a character
}
break;
default:
line[pos++] = (char)c;
putchar(c);
break;
}
}
LTRACEF("console thread exiting\n");
return 0;
};
static void irc_app_entry(const struct app_descriptor *app, void *args) {
LTRACE_ENTRY;
printf("welcome to IRC!\n");
// create a local state object
irc_client *irc = new irc_client();
if (!irc) {
return;
}
status_t err;
irc->init();
// clean up and delete the object on the way out
auto cleanup = [&irc]() {
printf("cleaning up IRC\n");
irc->shutdown();
delete irc;
};
auto ac = lktl::make_auto_call(cleanup);
// configure the parameters
irc->set_server(IRC_SERVER);
irc->set_server_port(IRC_PORT);
err = irc->connect();
if (err < 0) {
return;
}
err = irc->handshake();
if (err < 0) {
return;
}
// start two threads
thread_t *console_thread;
thread_t *server_thread;
console_thread = thread_create("irc console", console_thread_worker, irc, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
thread_resume(console_thread);
auto server_thread_worker = [](void *arg) -> int {
irc_client *_irc = static_cast<irc_client *>(arg);
printf("top of server thread\n");
_irc->read_loop();
return 0;
};
server_thread = thread_create("irc socket", server_thread_worker, irc, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
thread_resume(server_thread);
thread_join(server_thread, nullptr, INFINITE_TIME);
thread_join(console_thread, nullptr, INFINITE_TIME);
LTRACE_EXIT;
}
APP_START(irc)
.init = nullptr,
.entry = irc_app_entry,
.flags = APP_FLAG_NO_AUTOSTART,
.stack_size = 0,
APP_END

92
app/irc/lktl.h Normal file
View File

@@ -0,0 +1,92 @@
/*
* Copyright (c) 2021 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <kernel/thread.h>
#include <kernel/mutex.h>
// Macro used to simplify the task of deleting all of the default copy
// constructors and assignment operators.
#define DISALLOW_COPY_ASSIGN_AND_MOVE(_class_name) \
_class_name(const _class_name&) = delete; \
_class_name(_class_name&&) = delete; \
_class_name& operator=(const _class_name&) = delete; \
_class_name& operator=(_class_name&&) = delete
// Macro used to simplify the task of deleting the non rvalue reference copy
// constructors and assignment operators. (IOW - forcing move semantics)
#define DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(_class_name) \
_class_name(const _class_name&) = delete; \
_class_name& operator=(const _class_name&) = delete
// Macro used to simplify the task of deleting the new and new[]
// operators. (IOW - disallow heap allocations)
#define DISALLOW_NEW \
static void* operator new(size_t) = delete; \
static void* operator new[](size_t) = delete
#pragma once
namespace lktl {
// call a routine when the object goes out of scope
template <typename T>
class auto_call {
public:
constexpr explicit auto_call(T call) : call_(call) {}
~auto_call() {
if (armed_) {
call_();
}
}
// move
auto_call(auto_call &&ac) : armed_(ac.armed_), call_(ac.call_) {
ac.cancel();
}
void cancel() {
armed_ = false;
}
private:
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(auto_call);
bool armed_ = true;
T call_;
};
// create an auto caller with implicit template specialization.
//
// example:
// auto ac = make_auto_call([]() { printf("a lambda!\n"); });
template <typename T>
inline auto_call<T> make_auto_call(T c) {
return auto_call<T>(c);
}
// auto mutex scope guard
class auto_lock {
public:
explicit auto_lock(mutex_t *m) : m_(m) {
mutex_acquire(m_);
}
~auto_lock() {
mutex_release(m_);
}
private:
DISALLOW_COPY_ASSIGN_AND_MOVE(auto_lock);
mutex_t *m_ = nullptr;
};
} // namespace lktl

12
app/irc/rules.mk Normal file
View File

@@ -0,0 +1,12 @@
LOCAL_DIR := $(GET_LOCAL_DIR)
MODULE := $(LOCAL_DIR)
MODULE_SRCS += \
$(LOCAL_DIR)/irc.cpp \
MODULE_DEPS := \
lib/libcpp \
lib/minip
include make/module.mk

12
app/telnetd/rules.mk Normal file
View File

@@ -0,0 +1,12 @@
LOCAL_DIR := $(GET_LOCAL_DIR)
MODULE := $(LOCAL_DIR)
MODULE_SRCS += $(LOCAL_DIR)/telnetd.cpp
MODULE_DEPS := \
lib/cksum \
lib/libcpp \
lib/minip
include make/module.mk

72
app/telnetd/telnetd.cpp Normal file
View File

@@ -0,0 +1,72 @@
/*
* Copyright (c) 2014 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#include <app.h>
#include <lk/err.h>
#include <lk/debug.h>
#include <lk/trace.h>
#include <stdio.h>
#include <stdlib.h>
#include <lk/compiler.h>
#include <kernel/thread.h>
#include <lib/minip.h>
#include <lib/tftp.h>
#include <lib/cksum.h>
#include <platform.h>
#define LOCAL_TRACE 1
static int telnet_worker(void *arg) {
tcp_socket_t *s = static_cast<tcp_socket_t *>(arg);
char buf[128];
for (;;) {
ssize_t err = tcp_read(s, buf, sizeof(buf));
if (err < 0) {
printf("TELENT: error from read, exiting\n");
return err;
}
hexdump8(buf, err);
}
return 0;
}
static void telnetd_entry(const struct app_descriptor *app, void *args) {
printf("TELNET: waiting for network configuration\n");
minip_wait_for_configured(INFINITE_TIME);
printf("TELNET: starting telnet server\n");
// starting telnet stack
tcp_socket_t *listen_socket;
status_t err = tcp_open_listen(&listen_socket, 23);
if (err < 0) {
printf("tcp_open_listen returns %d\n", err);
}
for (;;) {
tcp_socket_t *accept_socket;
err = tcp_accept(listen_socket, &accept_socket);
LTRACEF("tcp_accept returns returns %d, handle %p\n", err, accept_socket);
if (err < 0) {
TRACEF("error accepting socket, retrying\n");
continue;
}
printf("TELNET: starting worker\n");
thread_detach_and_resume(thread_create("chargen_worker", &telnet_worker, accept_socket, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
}
}
APP_START(inetsrv)
.init = nullptr,
.entry = telnetd_entry,
.flags = 0,
.stack_size = 0,
APP_END

View File

@@ -172,14 +172,15 @@ static void zynq_common_target_init(uint level) {
sysparam_read("net0.ip_mask", &ip_mask, sizeof(ip_mask));
sysparam_read("net0.ip_gateway", &ip_gateway, sizeof(ip_gateway));
minip_set_macaddr(mac_addr);
gem_set_macaddr(mac_addr);
minip_set_eth(gem_send_raw_pkt, NULL, mac_addr);
if (!use_dhcp && ip_addr != IPV4_NONE) {
minip_init(gem_send_raw_pkt, NULL, ip_addr, ip_mask, ip_gateway);
minip_start_static(ip_addr, ip_mask, ip_gateway);
} else {
/* Configure IP stack and hook to the driver */
minip_init_dhcp(gem_send_raw_pkt, NULL);
minip_start_dhcp();
}
gem_set_callback(minip_rx_driver_callback);
#endif

View File

@@ -147,50 +147,13 @@ void arch_enter_uspace(vaddr_t entry_point, vaddr_t user_stack_top) {
riscv_csr_write(sstatus, status);
riscv_csr_write(sepc, entry_point);
riscv_csr_write(sscratch, kernel_stack_top);
riscv_fpu_zero();
// put the current tp (percpu pointer) just below the top of the stack
// the exception code will recover it when coming from user space
((uintptr_t *)kernel_stack_top)[-1] = (uintptr_t)riscv_get_percpu();
asm volatile(
#if RISCV_FPU
// zero out the fpu state
"csrw fcsr, 0\n"
// TODO: figure out how to do this more cleanly
// without 'fd' in the march line the assembler wont let us emit a direct
// fpu opcode. Tried unsuccessfully to use the .insn operand. below is a
// series of fmv.d.x fN, zero instructions to wipe out the complete state.
".word 0xf2000053\n" // fmv.d.x f0, zero
".word 0xf20000d3\n" // fmv.d.x f1, zero
".word 0xf2000153\n" // ...
".word 0xf20001d3\n"
".word 0xf2000253\n"
".word 0xf20002d3\n"
".word 0xf2000353\n"
".word 0xf20003d3\n"
".word 0xf2000453\n"
".word 0xf20004d3\n"
".word 0xf2000553\n"
".word 0xf20005d3\n"
".word 0xf2000653\n"
".word 0xf20006d3\n"
".word 0xf2000753\n"
".word 0xf20007d3\n"
".word 0xf2000853\n"
".word 0xf20008d3\n"
".word 0xf2000953\n"
".word 0xf20009d3\n"
".word 0xf2000a53\n"
".word 0xf2000ad3\n"
".word 0xf2000b53\n"
".word 0xf2000bd3\n"
".word 0xf2000c53\n"
".word 0xf2000cd3\n"
".word 0xf2000d53\n"
".word 0xf2000dd3\n"
".word 0xf2000e53\n"
".word 0xf2000ed3\n"
".word 0xf2000f53\n"
".word 0xf2000fd3\n" // fmv.d.x f31, zero
#endif
// set the user stack pointer
"mv sp, %0\n"
// zero out the rest of the integer state

72
arch/riscv/fpu_asm.S Normal file
View File

@@ -0,0 +1,72 @@
/*
* Copyright (c) 2022 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#include <lk/asm.h>
#include <arch/riscv.h>
#include <arch/riscv/asm.h>
// enable full use of all of the fpu instructions
#if __riscv_xlen == 32
.attribute arch, "rv32imafdc"
#elif __riscv_xlen == 64
.attribute arch, "rv64imafdc"
#else
#error unknown xlen
#endif
// conditionally use fcvt or fmv based on 32 or 64bit ISA
.macro ZERO_FPU_REG reg, width
#if __riscv_xlen == 32
fcvt.\width\().w \reg, zero
#elif __riscv_xlen == 64
fmv.\width\().x \reg, zero
#endif
.endm
// called just before entering user space for the first time.
// must not use the stack and is okay to be called with interrupts disabled.
FUNCTION(riscv_fpu_zero)
#if RISCV_FPU
// zero out the fpu state
// TODO: handle single precision implementations
csrw fcsr, zero
ZERO_FPU_REG f0, d
ZERO_FPU_REG f1, d
ZERO_FPU_REG f2, d
ZERO_FPU_REG f3, d
ZERO_FPU_REG f4, d
ZERO_FPU_REG f5, d
ZERO_FPU_REG f6, d
ZERO_FPU_REG f7, d
ZERO_FPU_REG f8, d
ZERO_FPU_REG f9, d
ZERO_FPU_REG f10, d
ZERO_FPU_REG f11, d
ZERO_FPU_REG f12, d
ZERO_FPU_REG f13, d
ZERO_FPU_REG f14, d
ZERO_FPU_REG f15, d
ZERO_FPU_REG f16, d
ZERO_FPU_REG f17, d
ZERO_FPU_REG f18, d
ZERO_FPU_REG f19, d
ZERO_FPU_REG f20, d
ZERO_FPU_REG f21, d
ZERO_FPU_REG f22, d
ZERO_FPU_REG f23, d
ZERO_FPU_REG f24, d
ZERO_FPU_REG f25, d
ZERO_FPU_REG f26, d
ZERO_FPU_REG f27, d
ZERO_FPU_REG f28, d
ZERO_FPU_REG f29, d
ZERO_FPU_REG f30, d
ZERO_FPU_REG f31, d
#endif
ret
END_FUNCTION(riscv_fpu_zero)

View File

@@ -214,6 +214,9 @@ enum handler_return riscv_software_exception(void);
enum handler_return riscv_platform_irq(void);
void riscv_syscall_handler(struct riscv_short_iframe *frame);
// initialize the fpu state to zero
void riscv_fpu_zero(void);
// If using S mode, time seems to be implemented in clint.h
// TODO: clean up by moving into its own header
#if RISCV_S_MODE

View File

@@ -6,6 +6,7 @@ MODULE_SRCS += $(LOCAL_DIR)/start.S
MODULE_SRCS += $(LOCAL_DIR)/arch.c
MODULE_SRCS += $(LOCAL_DIR)/asm.S
MODULE_SRCS += $(LOCAL_DIR)/exceptions.c
MODULE_SRCS += $(LOCAL_DIR)/fpu_asm.S
MODULE_SRCS += $(LOCAL_DIR)/thread.c
MODULE_SRCS += $(LOCAL_DIR)/mmu.cpp
MODULE_SRCS += $(LOCAL_DIR)/mp.c

View File

@@ -60,6 +60,8 @@ public:
bool is_e1000e() const { return id_feat_->e1000e; }
const uint8_t *mac_addr() const { return mac_addr_; }
private:
static const size_t rxring_len = 64;
static const size_t txring_len = 64;
@@ -428,10 +430,6 @@ status_t e1000::init_device(pci_location_t loc, const e1000_id_features *id) {
}
LTRACEF("IRQ number %#x\n", irq_base);
// set up minip's macaddr
// TODO: move to something smarter
minip_set_macaddr(mac_addr_);
unmask_interrupt(irq_base);
// set up the rx ring
@@ -513,9 +511,7 @@ status_t e1000::init_device(pci_location_t loc, const e1000_id_features *id) {
return NO_ERROR;
}
// XXX REMOVE HACK
extern "C"
int e1000_tx(pktbuf_t *p) {
static int e1000_tx(pktbuf_t *p) {
if (the_e) {
the_e->tx(p);
}
@@ -523,6 +519,16 @@ int e1000_tx(pktbuf_t *p) {
return NO_ERROR;
}
extern "C"
status_t e1000_register_with_minip() {
if (the_e) {
minip_set_eth(e1000_tx, the_e, the_e->mac_addr());
return NO_ERROR;
}
return ERR_NOT_FOUND;
}
static void e1000_init(uint level) {
LTRACE_ENTRY;

11
docs/lk_tap.txt Normal file
View File

@@ -0,0 +1,11 @@
# Setting up a tun/tap network for qemu
sudo ip tuntap add mode tap user $USER name qemu0
sudo ip link set qemu0 up
# manually making bridge
sudo brctl addbr br0
sudo brctl addif br0 qemu0
sudo brctl addif br0 <physical nic>
sudo ip link set br0 up
# alternatively using the network config to create the bridge and merge it with particular vlan

View File

@@ -224,6 +224,7 @@ status_t elf_load(elf_handle_t *handle) {
void *ptr = (void *)(uintptr_t)pheader->p_vaddr;
if (handle->mem_alloc_hook) {
// TODO: pass flags re: X bit, etc
status_t err = handle->mem_alloc_hook(handle, &ptr, pheader->p_memsz, load_count, 0);
if (err < 0) {
LTRACEF("mem hook failed, abort\n");
@@ -233,7 +234,7 @@ status_t elf_load(elf_handle_t *handle) {
}
// read the file portion of the segment into memory at vaddr
LTRACEF("reading segment at offset " ELF_OFF_PRINT_U " to address %p\n", pheader->p_offset, ptr);
LTRACEF("reading segment at offset 0x" ELF_OFF_PRINT_X " to address %p\n", pheader->p_offset, ptr);
readerr = handle->read_hook(handle, ptr, pheader->p_offset, pheader->p_filesz);
if (readerr < (ssize_t)pheader->p_filesz) {
LTRACEF("error %ld reading program header %u\n", readerr, i);

View File

@@ -16,26 +16,19 @@
#include <kernel/mutex.h>
#include <lk/trace.h>
typedef union {
uint32_t u;
uint8_t b[4];
} ipv4_t;
#define LOCAL_TRACE 0
static struct list_node arp_list;
typedef struct {
struct list_node node;
uint32_t addr;
uint8_t mac[6];
} arp_entry_t;
static struct list_node arp_list = LIST_INITIAL_VALUE(arp_list);
static mutex_t arp_mutex = MUTEX_INITIAL_VALUE(arp_mutex);
void arp_cache_init(void) {
list_initialize(&arp_list);
}
void arp_cache_init(void) {}
static inline void mru_update(struct list_node *entry) {
static void mru_update(struct list_node *entry) {
if (arp_list.next == entry)
return;
@@ -77,7 +70,7 @@ void arp_cache_update(uint32_t addr, const uint8_t mac[6]) {
}
arp->addr = addr;
memcpy(arp->mac, mac, sizeof(arp->mac));
mac_addr_copy(arp->mac, mac);
list_add_head(&arp_list, &arp->node);
}

View File

@@ -253,6 +253,9 @@ void dhcp::udp_callback(void *data, size_t sz, uint32_t srcip, uint16_t srcport)
#endif
sz -= sizeof(dhcp_msg_t);
opt = msg->options;
#if TRACE_DHCP
printf("\toptions: ");
#endif
while (sz >= 2) {
sz -= 2;
if (opt[1] > sz) {
@@ -319,6 +322,9 @@ done:
}
state_ = CONFIGURED;
configured_ = true;
// signal that minip is ready to be used
minip_set_configured();
}
}
}
@@ -372,9 +378,7 @@ status_t dhcp::start() {
} // anonymous namespace
void minip_init_dhcp(tx_func_t tx_func, void *tx_arg) {
minip_init(tx_func, tx_arg, IPV4_NONE, IPV4_NONE, IPV4_NONE);
void minip_start_dhcp() {
static dhcp d;
d.start();
}

View File

@@ -8,15 +8,14 @@
*/
#pragma once
#include <lk/compiler.h>
#include <endian.h>
#include <iovec.h>
#include <lib/pktbuf.h>
#include <lk/compiler.h>
#include <lk/list.h>
#include <stdint.h>
#include <iovec.h>
#include <sys/types.h>
#include <lib/pktbuf.h>
__BEGIN_CDECLS
#define IPV4(a,b,c,d) (((a)&0xFF)|(((b)&0xFF)<<8)|(((c)&0xFF)<<16)|(((d)&0xFF)<<24))
@@ -29,12 +28,21 @@ typedef int (*tx_func_t)(pktbuf_t *p);
typedef void (*udp_callback_t)(void *data, size_t len,
uint32_t srcaddr, uint16_t srcport, void *arg);
/* initialize minip with static configuration */
void minip_init(tx_func_t tx_func, void *tx_arg,
uint32_t ip, uint32_t netmask, uint32_t gateway);
/* initialize and start minip with static configuration */
void minip_start_static(uint32_t ip, uint32_t netmask, uint32_t gateway);
/* initialize minip with DHCP configuration */
void minip_init_dhcp(tx_func_t tx_func, void *tx_arg);
/* initialize and start minip with DHCP configuration
* note: may take a while to have an ip address assigned, check
* for configuration with minip_is_configured()
*/
void minip_start_dhcp(void);
/* ethernet driver install hook */
void minip_set_eth(tx_func_t tx_handler, void *tx_arg, const uint8_t *macaddr);
/* check or wait for minip to be configured */
bool minip_is_configured(void);
status_t minip_wait_for_configured(lk_time_t timeout);
/* packet rx hook to hand to ethernet driver */
void minip_rx_driver_callback(pktbuf_t *p);
@@ -45,17 +53,14 @@ void minip_set_macaddr(const uint8_t *addr);
uint32_t minip_get_ipaddr(void);
void minip_set_ipaddr(const uint32_t addr);
uint32_t minip_get_netmask(void);
void minip_set_netmask(const uint32_t mask);
uint32_t minip_get_broadcast(void); // computed from ipaddr & netmask
uint32_t minip_get_gateway(void);
void minip_set_gateway(const uint32_t addr);
void minip_set_hostname(const char *name);
const char *minip_get_hostname(void);
uint32_t minip_get_broadcast(void);
uint32_t minip_get_netmask(void);
void minip_set_netmask(const uint32_t netmask);
uint32_t minip_get_gateway(void);
void minip_set_gateway(const uint32_t gateway);
void minip_set_configured(void); // set by dhcp or static init to signal minip is ready to be used
/* udp */
typedef struct udp_socket udp_socket_t;
@@ -69,6 +74,7 @@ status_t udp_close(udp_socket_t *handle);
/* tcp */
typedef struct tcp_socket tcp_socket_t;
status_t tcp_connect(tcp_socket_t **handle, uint32_t addr, uint16_t port);
status_t tcp_open_listen(tcp_socket_t **handle, uint16_t port);
status_t tcp_accept_timeout(tcp_socket_t *listen_socket, tcp_socket_t **accept_socket, lk_time_t timeout);
status_t tcp_close(tcp_socket_t *socket);
@@ -81,6 +87,7 @@ static inline status_t tcp_accept(tcp_socket_t *listen_socket, tcp_socket_t **ac
/* utilities */
void gen_random_mac_address(uint8_t *mac_addr);
uint32_t minip_parse_ipaddr(const char *addr, size_t len);
uint32_t minip_parse_ipaddr(const char *addr, size_t len);
void printip(uint32_t x);

View File

@@ -89,7 +89,13 @@ enum {
extern tx_func_t minip_tx_handler;
typedef struct udp_hdr udp_hdr_t;
static const uint8_t bcast_mac[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
typedef uint32_t ipv4_addr;
typedef union {
uint32_t u;
uint8_t b[4];
} ipv4_t;
// ARP cache
void arp_cache_init(void);
void arp_cache_update(uint32_t addr, const uint8_t mac[6]);
uint8_t *arp_cache_lookup(uint32_t addr);
@@ -101,7 +107,7 @@ uint16_t rfc1701_chksum(const uint8_t *buf, size_t len);
uint16_t rfc768_chksum(struct ipv4_hdr *ipv4, udp_hdr_t *udp);
uint16_t ones_sum16(uint32_t sum, const void *_buf, int len);
/* Helper methods for building headers */
// Helper methods for building headers
void minip_build_mac_hdr(struct eth_hdr *pkt, const uint8_t *dst, uint16_t type);
void minip_build_ipv4_hdr(struct ipv4_hdr *ipv4, uint32_t dst, uint8_t proto, uint16_t len);

View File

@@ -9,6 +9,7 @@
#include "minip-internal.h"
#include <assert.h>
#include <lk/err.h>
#include <stdio.h>
#include <lk/debug.h>
@@ -20,10 +21,10 @@
#include <lk/trace.h>
#include <malloc.h>
#include <lk/list.h>
#include <lk/init.h>
#include <kernel/event.h>
#include <kernel/thread.h>
static struct list_node arp_list = LIST_INITIAL_VALUE(arp_list);
// TODO
// 1. Tear endian code out into something that flips words before/after tx/rx calls
@@ -38,11 +39,37 @@ static uint8_t minip_mac[6] = {0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC};
static char minip_hostname[32] = "";
static volatile bool minip_configured = false;
static event_t minip_configured_event = EVENT_INITIAL_VALUE(minip_configured_event, false, 0);
/* This function is called by minip to send packets */
tx_func_t minip_tx_handler;
void *minip_tx_arg;
static void dump_mac_address(const uint8_t *mac);
static void dump_ipv4_addr(uint32_t addr);
/* if all the important configuration bits are set, signal that we're configured */
static void check_and_set_configured(void) {
if (minip_ip == IPV4_NONE) return;
if (minip_netmask == IPV4_NONE) return;
if (minip_broadcast == IPV4_BCAST) return;
// minip_gateway doesn't have to be set
if (minip_mac[0] == 0xcc &&
minip_mac[1] == 0xcc &&
minip_mac[2] == 0xcc &&
minip_mac[3] == 0xcc &&
minip_mac[4] == 0xcc &&
minip_mac[5] == 0xcc) return;
// we're configured
printf("MINIP: setting configured state\n");
minip_set_configured();
}
void minip_set_hostname(const char *name) {
strlcpy(minip_hostname, name, sizeof(minip_hostname));
check_and_set_configured();
}
const char *minip_get_hostname(void) {
@@ -57,10 +84,6 @@ void minip_get_macaddr(uint8_t *addr) {
mac_addr_copy(addr, minip_mac);
}
void minip_set_macaddr(const uint8_t *addr) {
mac_addr_copy(minip_mac, addr);
}
uint32_t minip_get_ipaddr(void) {
return minip_ip;
}
@@ -68,6 +91,7 @@ uint32_t minip_get_ipaddr(void) {
void minip_set_ipaddr(const uint32_t addr) {
minip_ip = addr;
compute_broadcast_address();
check_and_set_configured();
}
uint32_t minip_get_broadcast(void) {
@@ -81,14 +105,30 @@ uint32_t minip_get_netmask(void) {
void minip_set_netmask(const uint32_t netmask) {
minip_netmask = netmask;
compute_broadcast_address();
check_and_set_configured();
}
uint32_t minip_get_gateway(void) {
return minip_gateway;
}
void minip_set_gateway(const uint32_t gateway) {
minip_gateway = gateway;
void minip_set_gateway(const uint32_t addr) {
minip_gateway = addr;
// TODO: check that it is reacheable on local network
check_and_set_configured();
}
void minip_set_configured(void) {
minip_configured = true;
event_signal(&minip_configured_event, true);
}
bool minip_is_configured(void) {
return minip_configured;
}
status_t minip_wait_for_configured(lk_time_t timeout) {
return event_wait_timeout(&minip_configured_event, timeout);
}
void gen_random_mac_address(uint8_t *mac_addr) {
@@ -100,22 +140,24 @@ void gen_random_mac_address(uint8_t *mac_addr) {
mac_addr[0] |= (1<<1);
}
/* This function is called by minip to send packets */
tx_func_t minip_tx_handler;
void *minip_tx_arg;
void minip_start_static(uint32_t ip, uint32_t mask, uint32_t gateway) {
minip_set_ipaddr(ip);
minip_set_netmask(mask);
minip_set_gateway(gateway);
}
void minip_init(tx_func_t tx_handler, void *tx_arg,
uint32_t ip, uint32_t mask, uint32_t gateway) {
void minip_set_eth(tx_func_t tx_handler, void *tx_arg, const uint8_t *macaddr) {
LTRACEF("handler %p, arg %p, macaddr %p\n", tx_handler, tx_arg, macaddr);
DEBUG_ASSERT(minip_tx_handler == NULL);
DEBUG_ASSERT(minip_tx_arg == NULL);
// TODO: assert mac address is not already set
// set up the low level driver handler
minip_tx_handler = tx_handler;
minip_tx_arg = tx_arg;
minip_ip = ip;
minip_netmask = mask;
minip_gateway = gateway;
compute_broadcast_address();
arp_cache_init();
net_timer_init();
mac_addr_copy(minip_mac, macaddr);
}
static uint16_t ipv4_payload_len(struct ipv4_hdr *pkt) {
@@ -209,13 +251,24 @@ status_t minip_ipv4_send(pktbuf_t *p, uint32_t dest_addr, uint8_t proto) {
struct ipv4_hdr *ip = pktbuf_prepend(p, sizeof(struct ipv4_hdr));
struct eth_hdr *eth = pktbuf_prepend(p, sizeof(struct eth_hdr));
// are we sending a broadcast packet?
if (dest_addr == IPV4_BCAST || dest_addr == minip_broadcast) {
dst_mac = bcast_mac;
goto ready;
}
dst_mac = get_dest_mac(dest_addr);
// is this a local subnet packet or do we need to send to the router?
uint32_t target_addr = dest_addr;
if ((dest_addr & minip_netmask) != (minip_ip & minip_netmask)) {
// need to use the gateway
if (minip_gateway == IPV4_NONE) {
return ERR_NOT_FOUND; // TODO: better error code
}
target_addr = minip_gateway;
}
dst_mac = arp_get_dest_mac(target_addr);
if (!dst_mac) {
pktbuf_free(p, true);
ret = -EHOSTUNREACH;
@@ -499,3 +552,12 @@ void printip_named(const char *s, uint32_t x) {
printf("%s ", s);
printip(x);
}
// run static initialization
static void minip_init(uint level) {
arp_cache_init();
net_timer_init();
}
LK_INIT_HOOK(minip, minip_init, LK_INIT_LEVEL_THREADING);

View File

@@ -25,8 +25,6 @@
#define LOCAL_TRACE 0
typedef uint32_t ipv4_addr;
typedef struct tcp_header {
uint16_t source_port;
uint16_t dest_port;
@@ -114,6 +112,9 @@ typedef struct tcp_socket {
struct tcp_socket *accepted;
net_timer_t time_wait_timer;
/* connect waiting */
event_t connect_event;
} tcp_socket_t;
#define DEFAULT_MSS (1460)
@@ -216,7 +217,7 @@ static void dump_socket(tcp_socket_t *s) {
}
static tcp_socket_t *lookup_socket(ipv4_addr remote_ip, ipv4_addr local_ip, uint16_t remote_port, uint16_t local_port) {
LTRACEF("remote ip 0x%x local ip 0x%x remote port %u local port %u\n", remote_ip, local_ip, remote_port, local_port);
LTRACEF_LEVEL(2, "remote ip 0x%x local ip 0x%x remote port %u local port %u\n", remote_ip, local_ip, remote_port, local_port);
mutex_acquire(&tcp_socket_list_lock);
@@ -286,7 +287,7 @@ static void inc_socket_ref(tcp_socket_t *s) {
DEBUG_ASSERT(s);
__UNUSED int oldval = atomic_add(&s->ref, 1);
LTRACEF("caller %p, thread %p, socket %p, ref now %d\n", __GET_CALLER(), get_current_thread(), s, oldval + 1);
LTRACEF_LEVEL(2, "caller %p, thread %p, socket %p, ref now %d\n", __GET_CALLER(), get_current_thread(), s, oldval + 1);
DEBUG_ASSERT(oldval > 0);
}
@@ -294,12 +295,13 @@ static bool dec_socket_ref(tcp_socket_t *s) {
DEBUG_ASSERT(s);
int oldval = atomic_add(&s->ref, -1);
LTRACEF("caller %p, thread %p, socket %p, ref now %d\n", __GET_CALLER(), get_current_thread(), s, oldval - 1);
LTRACEF_LEVEL(2, "caller %p, thread %p, socket %p, ref now %d\n", __GET_CALLER(), get_current_thread(), s, oldval - 1);
if (oldval == 1) {
LTRACEF("destroying socket\n");
event_destroy(&s->tx_event);
event_destroy(&s->rx_event);
event_destroy(&s->connect_event);
free(s->rx_buffer_raw);
free(s->tx_buffer);
@@ -482,6 +484,44 @@ void tcp_input(pktbuf_t *p, uint32_t src_ip, uint32_t dst_ip) {
break;
/* active connection state */
case STATE_SYN_SENT:
if ((packet_flags & PKT_SYN) == 0) {
// we got data on the packet without a syn, reset
goto send_reset;
}
if ((packet_flags & PKT_ACK) == 0) {
// simultaneous SYN/ACK
// TODO: handle
goto send_reset;
}
LTRACEF("ack num %d tx win_low %d\n", header->ack_num, s->tx_win_low);
if (header->ack_num != s->tx_win_low + 1) {
// they didn't ack our syn
goto send_reset;
}
// remember their sequence
s->rx_win_low = header->seq_num + 1;
s->rx_win_high = s->rx_win_low + s->rx_win_size - 1;
s->tx_win_low++;
s->tx_win_high = s->tx_win_low + header->win_size;
s->tx_highest_seq = s->tx_win_low;
s->state = STATE_ESTABLISHED;
send_ack(s);
event_signal(&s->connect_event, true);
break;
/* established state */
case STATE_ESTABLISHED:
if (packet_flags & PKT_ACK) {
/* they're acking us */
@@ -565,9 +605,6 @@ fin_wait_2:
case STATE_TIME_WAIT:
/* /dev/null of packets */
break;
case STATE_SYN_SENT:
PANIC_UNIMPLEMENTED;
}
done:
@@ -887,6 +924,7 @@ static void tcp_wakeup_waiters(tcp_socket_t *s) {
// wake up any waiters
event_signal(&s->rx_event, true);
event_signal(&s->tx_event, true);
event_signal(&s->connect_event, true);
}
static void tcp_remote_close(tcp_socket_t *s) {
@@ -938,11 +976,72 @@ static tcp_socket_t *create_tcp_socket(bool alloc_buffers) {
}
sem_init(&s->accept_sem, 0);
event_init(&s->connect_event, false, 0);
return s;
}
/* user api */
status_t tcp_connect(tcp_socket_t **handle, uint32_t addr, uint16_t port) {
tcp_socket_t *s;
if (!handle)
return ERR_INVALID_ARGS;
s = create_tcp_socket(true);
if (!s)
return ERR_NO_MEMORY;
// XXX add some entropy to try to better randomize things
lk_bigtime_t t = current_time_hires();
printf("%lld\n", t);
rand_add_entropy(&t, sizeof(t));
// set up the socket for outgoing connections
s->local_ip = minip_get_ipaddr();
s->local_port = (rand() + 1024) & 0xffff; // TODO: allocate sanely
DEBUG_ASSERT(s->local_port <= 0xffff);
s->remote_ip = addr;
s->remote_port = port;
if (LOCAL_TRACE) {
dump_socket(s);
}
// send a SYN packet
mutex_acquire(&s->lock);
s->state = STATE_SYN_SENT;
add_socket_to_list(s);
/* set up a mss option for sending back */
tcp_mss_option_t mss_option;
mss_option.kind = 0x2;
mss_option.len = 0x4;
mss_option.mss = ntohs(s->mss);
tcp_send(s->remote_ip, s->remote_port, s->local_ip, s->local_port, NULL, 0, PKT_SYN, &mss_option, 0x4, 0, s->tx_win_low, s->rx_win_size);
// TODO: handle retransmit
mutex_release(&s->lock);
// block to wait for a successful connection
if (event_wait(&s->connect_event) == ERR_TIMED_OUT) {
return ERR_TIMED_OUT;
}
status_t err = NO_ERROR;
mutex_acquire(&s->lock);
if (s->state != STATE_ESTABLISHED) {
err = ERR_CHANNEL_CLOSED;
}
mutex_release(&s->lock);
*handle = s;
return err;
}
status_t tcp_open_listen(tcp_socket_t **handle, uint16_t port) {
tcp_socket_t *s;
@@ -1150,6 +1249,7 @@ status_t tcp_close(tcp_socket_t *socket) {
// XXX set up fin retransmit timer here
break;
case STATE_SYN_SENT:
case STATE_FIN_WAIT_1:
case STATE_FIN_WAIT_2:
case STATE_CLOSING:

View File

@@ -9,6 +9,7 @@
*/
#include <lk/err.h>
#include <lk/init.h>
#include <lk/trace.h>
#include <arch/x86/mmu.h>
#include <platform.h>
@@ -300,9 +301,16 @@ void platform_init(void) {
#endif
platform_init_mmu_mappings();
}
#if WITH_LIB_MINIP
extern int e1000_tx(pktbuf_t *p);
minip_init_dhcp(e1000_tx, 0);
#endif
void _start_minip(uint level) {
extern status_t e1000_register_with_minip(void);
status_t err = e1000_register_with_minip();
if (err == NO_ERROR) {
minip_start_dhcp();
}
}
LK_INIT_HOOK(start_minip, _start_minip, LK_INIT_LEVEL_APPS - 1);
#endif

View File

@@ -226,16 +226,16 @@ void platform_init(void) {
TRACEF("found virtio networking interface\n");
/* start minip */
minip_set_macaddr(mac_addr);
minip_set_eth(virtio_net_send_minip_pkt, NULL, mac_addr);
__UNUSED uint32_t ip_addr = IPV4(192, 168, 0, 99);
__UNUSED uint32_t ip_mask = IPV4(255, 255, 255, 0);
__UNUSED uint32_t ip_gateway = IPV4_NONE;
//minip_init(virtio_net_send_minip_pkt, NULL, ip_addr, ip_mask, ip_gateway);
minip_init_dhcp(virtio_net_send_minip_pkt, NULL);
virtio_net_start();
//minip_start_static(ip_addr, ip_mask, ip_gateway);
minip_start_dhcp();
}
#endif
}

View File

@@ -106,16 +106,16 @@ void platform_init(void) {
TRACEF("found virtio networking interface\n");
/* start minip */
minip_set_macaddr(mac_addr);
minip_set_eth(virtio_net_send_minip_pkt, NULL, mac_addr);
__UNUSED uint32_t ip_addr = IPV4(192, 168, 0, 99);
__UNUSED uint32_t ip_mask = IPV4(255, 255, 255, 0);
__UNUSED uint32_t ip_gateway = IPV4_NONE;
//minip_init(virtio_net_send_minip_pkt, NULL, ip_addr, ip_mask, ip_gateway);
minip_init_dhcp(virtio_net_send_minip_pkt, NULL);
virtio_net_start();
//minip_start_static(ip_addr, ip_mask, ip_gateway);
minip_start_dhcp();
}
#endif
}

View File

@@ -198,16 +198,19 @@ void platform_init(void) {
TRACEF("found virtio networking interface\n");
/* start minip */
minip_set_macaddr(mac_addr);
minip_set_eth(virtio_net_send_minip_pkt, NULL, mac_addr);
virtio_net_start();
#if 0
__UNUSED uint32_t ip_addr = IPV4(192, 168, 0, 99);
__UNUSED uint32_t ip_mask = IPV4(255, 255, 255, 0);
__UNUSED uint32_t ip_gateway = IPV4_NONE;
//minip_init(virtio_net_send_minip_pkt, NULL, ip_addr, ip_mask, ip_gateway);
minip_init_dhcp(virtio_net_send_minip_pkt, NULL);
virtio_net_start();
minip_start_static(ip_addr, ip_mask, ip_gateway);
#else
minip_start_dhcp();
#endif
}
#endif
}

View File

@@ -1,11 +1,12 @@
# main project for qemu-riscv64-supervisor
MODULES += \
app/irc \
app/shell
SUBARCH := 64
RISCV_MODE := supervisor
include project/virtual/test.mk
include project/virtual/fs.mk
include project/virtual/minip.mk
include project/virtual/inetapps.mk
include project/target/qemu-virt-riscv.mk

View File

@@ -0,0 +1,8 @@
# some internet apps that depend on minip
MODULES += \
app/inetsrv \
app/irc \
app/telnetd \
include project/virtual/minip.mk

View File

@@ -1,6 +1,4 @@
# modules related to the minip stack
MODULES += \
lib/minip \
app/inetsrv
lib/minip

View File

@@ -104,7 +104,7 @@ fi
BLOCK_ARGS=" -drive if=none,file=blk.bin,id=blk,format=raw -device virtio-blk-device,drive=blk"
NET_ARGS=" -netdev user,id=vmnic,hostname=qemu -device virtio-net-device,netdev=vmnic"
NET_TAP_ARGS=" -netdev tap,id=vmnic -device virtio-net-device,netdev=vmnic"
NET_TAP_ARGS=" -netdev tap,id=vmnic,ifname=qemu0,script=no -device virtio-net-device,netdev=vmnic"
NO_DISPLAY_ARGS=" -nographic"
DISPLAY_ARGS=" -device virtio-gpu-device -serial stdio"
@@ -130,7 +130,7 @@ if (( $DO_NET )); then
fi
if (( $DO_NET_TAP )); then
ARGS+=$NET_TAP_ARGS
SUDO="sudo "
#SUDO="sudo "
fi
if (( $DO_DISPLAY )); then
ARGS+=$DISPLAY_ARGS

View File

@@ -123,13 +123,13 @@ void target_init(void) {
eth_init(mac_addr, PHY_KSZ8721);
/* start minip */
minip_set_macaddr(mac_addr);
minip_set_eth(stm32_eth_send_minip_pkt, NULL, mac_addr);
uint32_t ip_addr = IPV4(192, 168, 0, 98);
uint32_t ip_mask = IPV4(255, 255, 255, 0);
uint32_t ip_gateway = IPV4_NONE;
minip_init(stm32_eth_send_minip_pkt, NULL, ip_addr, ip_mask, ip_gateway);
minip_start_static(ip_addr, ip_mask, ip_gateway);
#endif
#if WITH_LIB_FS_SPIFS

View File

@@ -67,13 +67,13 @@ void target_init(void) {
eth_init(mac_addr, PHY_DP83848);
/* start minip */
minip_set_macaddr(mac_addr);
minip_set_eth(stm32_eth_send_minip_pkt, NULL, mac_addr);
uint32_t ip_addr = IPV4(192, 168, 0, 99);
uint32_t ip_mask = IPV4(255, 255, 255, 0);
uint32_t ip_gateway = IPV4_NONE;
minip_init(stm32_eth_send_minip_pkt, NULL, ip_addr, ip_mask, ip_gateway);
minip_start_static(ip_addr, ip_mask, ip_gateway);
#endif
TRACE_EXIT;

View File

@@ -64,13 +64,13 @@ void target_init(void) {
eth_init(mac_addr, PHY_LAN8742A);
/* start minip */
minip_set_macaddr(mac_addr);
minip_set_eth(stm32_eth_send_minip_pkt, NULL, mac_addr);
uint32_t ip_addr = IPV4(192, 168, 0, 98);
uint32_t ip_mask = IPV4(255, 255, 255, 0);
uint32_t ip_gateway = IPV4_NONE;
minip_init(stm32_eth_send_minip_pkt, NULL, ip_addr, ip_mask, ip_gateway);
minip_start_static(ip_addr, ip_mask, ip_gateway);
#endif
// start usb