Skip to content
Snippets Groups Projects
loader.cc 7.36 KiB
Newer Older
#include "drivers/isa-serial.hh"
#include "fs/fs.hh"
#include <boost/format.hpp>
#include <cctype>
#include "elf.hh"
#include "tls.hh"
#include "msr.hh"
Avi Kivity's avatar
Avi Kivity committed
#include "exceptions.hh"
#include "debug.hh"
#include "drivers/pci.hh"
#include "drivers/device-factory.hh"
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "drivers/virtio-net.hh"
#include "drivers/virtio-blk.hh"
#include "drivers/driver-factory.hh"
Avi Kivity's avatar
Avi Kivity committed
#include "sched.hh"
Avi Kivity's avatar
Avi Kivity committed
#include "drivers/clock.hh"
Avi Kivity's avatar
Avi Kivity committed
#include "drivers/clockevent.hh"
#include "barrier.hh"
Avi Kivity's avatar
Avi Kivity committed
asm(".pushsection \".debug_gdb_scripts\", \"MS\",@progbits,1 \n"
    ".byte 1 \n"
    ".asciz \"scripts/loader.py\" \n"
    ".popsection \n");

namespace {

    void test_locale()
    {
	auto loc = std::locale();
	auto &fac = std::use_facet<std::ctype<char>>(loc);
	bool ok = fac.is(std::ctype_base::digit, '3')
	    && !fac.is(std::ctype_base::digit, 'x');
	debug(ok ? "locale works" : "locale fails");
Avi Kivity's avatar
Avi Kivity committed
elf::Elf64_Ehdr* elf_header;
void setup_tls(elf::init_table inittab)
{
    static char tcb0[1 << 15] __attribute__((aligned(4096)));
    assert(inittab.tls_size + sizeof(thread_control_block) <= sizeof(tcb0));
    memcpy(tcb0, inittab.tls, inittab.tls_size);
    auto p = reinterpret_cast<thread_control_block*>(tcb0 + inittab.tls_size);
    p->self = p;
    processor::wrmsr(msr::IA32_FS_BASE, reinterpret_cast<uint64_t>(p));
}

extern "C" {
    void premain();
    void vfs_init(void);
    void ramdisk_init(void);
}
void premain()
{
    auto inittab = elf::get_init(elf_header);
    setup_tls(inittab);
    for (auto init = inittab.start; init < inittab.start + inittab.count; ++init) {
        (*init)();
    }
}

Avi Kivity's avatar
Avi Kivity committed
void disable_pic()
{
    outb(0xff, 0x21);
    outb(0xff, 0xa1);
}

Avi Kivity's avatar
Avi Kivity committed
static int test_ctr;

using sched::thread;

struct test_threads_data {
    thread* main;
    thread* t1;
    bool t1ok;
Avi Kivity's avatar
Avi Kivity committed
    thread* t2;
    bool t2ok;
Avi Kivity's avatar
Avi Kivity committed
};

void test_thread_1(test_threads_data& tt)
{
    while (test_ctr < 1000) {
        thread::wait_until([&] { return (test_ctr % 2) == 0; });
        ++test_ctr;
        if (tt.t2ok) {
Avi Kivity's avatar
Avi Kivity committed
            tt.t2->wake();
        }
    }
    tt.t1ok = false;
Avi Kivity's avatar
Avi Kivity committed
    tt.main->wake();
}

void test_thread_2(test_threads_data& tt)
{
    while (test_ctr < 1000) {
        thread::wait_until([&] { return (test_ctr % 2) == 1; });
        ++test_ctr;
        if (tt.t1ok) {
Avi Kivity's avatar
Avi Kivity committed
            tt.t1->wake();
        }
    }
    tt.t2ok = false;
Avi Kivity's avatar
Avi Kivity committed
    tt.main->wake();
}

void test_threads()
{
    test_threads_data tt;
    tt.main = thread::current();
    char stk1[10000], stk2[10000];
    tt.t1ok = tt.t2ok = true;
    tt.t1 = new thread([&] { test_thread_1(tt); }, { stk1, 10000 });
    tt.t2 = new thread([&] { test_thread_2(tt); }, { stk2, 10000 });
Avi Kivity's avatar
Avi Kivity committed
    thread::wait_until([&] { return test_ctr >= 1000; });
    delete tt.t1;
    delete tt.t2;
Avi Kivity's avatar
Avi Kivity committed
    debug("threading test succeeded");
}

Avi Kivity's avatar
Avi Kivity committed
int main(int ac, char **av)
{
    debug("Loader Copyright 2013 Unnamed");
Avi Kivity's avatar
Avi Kivity committed
    idt.load_on_cpu();
    vfs_init();

    filesystem fs;
Avi Kivity's avatar
Avi Kivity committed
    disable_pic();
Avi Kivity's avatar
Avi Kivity committed
    processor::sti();
Avi Kivity's avatar
Avi Kivity committed

#if 1
    if (std::isdigit('1'))
	debug("isgidit(1) = ok");
	debug("isgidit(1) = bad");
    if (!std::isdigit('x'))
	debug("isgidit(x) = ok");
	debug("isgidit(x) = bad");
#if 0
    auto &fac = std::use_facet<std::ctype<char> >(std::locale("C"));
    if (fac.is(std::ctype<char>::digit, '1'))
	debug("facet works");
	debug("facet !works");
    prog = new elf::program(fs);
    sched::init(*prog);
    static char main_stack[64*1024];
    void main_thread(int ac, char** av);
    new thread([&] { main_thread(ac, av); }, { main_stack, sizeof main_stack }, true);
Avi Kivity's avatar
Avi Kivity committed
void test_clock_events()
{
    struct test_callback : public clock_event_callback {
        test_callback() : n() {}
        virtual void fired() { t[n++] = clock::get()->time(); }
        unsigned n;
        u64 t[20];
    };
    test_callback t;
    clock_event_callback* old_callback = clock_event->callback();
    clock_event->set_callback(&t);
    for (unsigned i = 0; i < 10; ++i) {
        clock_event->set(clock::get()->time() + 1000000);
        while (t.n == i) {
            barrier();
        }
    }
    clock_event->set_callback(nullptr);
    for (unsigned i = 0; i < 10; ++i) {
        debug(fmt("clock_event: %d") % t.t[i]);
    }
    clock_event->set_callback(old_callback);
}

struct argblock {
    int ac;
    char** av;
};

void load_test(elf::program *prog, char *path)
    printf("running %s\n", path);

        = prog->lookup_function<int (int, const char **)>("main");
    std::string str = "test";
    const char *name = str.c_str();
    int ret = test_main(1, &name);
Christoph Hellwig's avatar
Christoph Hellwig committed
    if (ret)
        printf("failed.\n");
    else
        printf("ok.\n");
    prog->remove_object(path);
int load_tests(elf::program *prog, struct argblock *args)
{
#define TESTDIR		"/tests"
    DIR *dir = opendir(TESTDIR);
    char path[PATH_MAX];
    struct dirent *d;
    struct stat st;

    if (!dir) {
        perror("failed to open testdir");
        return EXIT_FAILURE;
    }

    while ((d = readdir(dir))) {
        if (strcmp(d->d_name, ".") == 0 ||
            strcmp(d->d_name, "..") == 0)
           continue;

        snprintf(path, PATH_MAX, "%s/%s", TESTDIR, d->d_name);
        if (__xstat(1, path, &st) < 0) {
            printf("failed to stat %s\n", path);
            continue;
        }
        if (!S_ISREG(st.st_mode)) {
            printf("ignoring %s, not a regular file\n", path);
            continue;
        }
        load_test(prog, path);
    }
    if (closedir(dir) < 0) {
        perror("failed to close testdir");
        return EXIT_FAILURE;
    }

    return 0;
void run_main(elf::program *prog, struct argblock *args)
{
    auto av = args->av;
    auto ac = args->ac;
    prog->add_object(av[0]);
    ++av, --ac;
    auto main = prog->lookup_function<void (int, char**)>("main");
    main(ac, av);
}
void* do_main_thread(void *_args)
    auto args = static_cast<argblock*>(_args);
Avi Kivity's avatar
Avi Kivity committed
    test_clock_events();
    pci::pci_devices_print();
    pci::pci_device_enumeration();
    DeviceFactory::Instance()->DumpDevices();
    Driver *vnet = new virtio::virtio_net();
    DriverFactory::Instance()->RegisterDriver(vnet);

    virtio::virtio_blk *vblk = new virtio::virtio_blk();
    DriverFactory::Instance()->RegisterDriver(vblk);

    DeviceFactory::Instance()->InitializeDrivers();

    DriverFactory::Instance()->Destroy();
Avi Kivity's avatar
Avi Kivity committed
    auto t1 = clock::get()->time();
    auto t2 = clock::get()->time();
    debug(fmt("clock@t1 %1%") % t1);
    debug(fmt("clock@t2 %1%") % t2);

    timespec ts = {};
    ts.tv_nsec = 100;
    t1 = clock::get()->time();
    nanosleep(&ts, nullptr);
    t2 = clock::get()->time();
    debug(fmt("nanosleep(100) -> %d") % (t2 - t1));
    ts.tv_nsec = 100000;
    t1 = clock::get()->time();
    nanosleep(&ts, nullptr);
    t2 = clock::get()->time();
    debug(fmt("nanosleep(100000) -> %d") % (t2 - t1));

//    load_tests(prog, args);
    run_main(prog, args);
Avi Kivity's avatar
Avi Kivity committed
    while (true)
	;
void main_thread(int ac, char **av)
{
    pthread_t pthread;
    // run the payload in a pthread, so pthread_self() etc. work
    argblock args{ ac, av };
    pthread_create(&pthread, nullptr, do_main_thread, &args);
    sched::thread::wait_until([] { return false; });