Exemplo n.º 1
0
status_t
load_kernel(stage2_args* args, BootVolume& volume)
{
	const char *name;
	int fd = find_kernel(volume, &name);
	if (fd < B_OK)
		return fd;

	dprintf("load kernel %s...\n", name);

	elf_init();
	preloaded_image *image;
	status_t status = elf_load_image(fd, &image);

	close(fd);

	if (status < B_OK) {
		dprintf("loading kernel failed: %lx!\n", status);
		return status;
	}

	gKernelArgs.kernel_image = image;

	status = elf_relocate_image(gKernelArgs.kernel_image);
	if (status < B_OK) {
		dprintf("relocating kernel failed: %lx!\n", status);
		return status;
	}

	gKernelArgs.kernel_image->name = kernel_args_strdup(name);

	return B_OK;
}
Exemplo n.º 2
0
void findKernelCodePageByCr3(unsigned startVirtualAddr, Mem * mem, int pageSize, unsigned cr3Pages[]) {
	unsigned startVirtual = startVirtualAddr;
	int cr3PageIndex = 0;
	for (; startVirtual > startVirtualAddr - 1; startVirtual += 0x1000) {
		//	for (; startVirtual < 0x818f0000; startVirtual += 0x1000) {
		unsigned vAddr = startVirtual;

		int rw = 0; //read or write
		int us = 0; //use or system
		int g = 0; //global(no move out of TLB) or not global
		int ps = 0; //page size
		unsigned pAddr = vtopPageProperty(mem->mem, mem->mem_size, mem->pgd, vAddr, &rw, &us, &g,
				&ps);

		// IS PHYSICAL ADDRESS VALID?
		if (pAddr == -1 || pAddr > mem->mem_size)
			continue;

		//collect pages which are system access, and global pages
		if (us == 0 && g == 256) {
//			printf("r only page %x\n", vAddr);
			if (find_kernel(mem, vAddr, pageSize) == 0) {
				//record kernel address
				cr3Pages[cr3PageIndex++] = vAddr;
				printf("kernel start at %x\n", vAddr);
			}
		}
	}
}
Exemplo n.º 3
0
bool
is_bootable(Directory *volume)
{
	if (volume->IsEmpty())
		return false;

	// check for the existance of a kernel (for our platform)
	int fd = find_kernel(volume);
	if (fd < B_OK)
		return false;

	close(fd);

	return true;
}
Exemplo n.º 4
0
static struct kvm *kvm_cmd_run_init(int argc, const char **argv)
{
	static char real_cmdline[2048], default_name[20];
	unsigned int nr_online_cpus;
	struct sigaction sa;
	struct kvm *kvm = kvm__new();

	if (IS_ERR(kvm))
		return kvm;

	sa.sa_flags = SA_SIGINFO;
	sa.sa_sigaction = handle_sigalrm;
	sigemptyset(&sa.sa_mask);
	sigaction(SIGALRM, &sa, NULL);

	nr_online_cpus = sysconf(_SC_NPROCESSORS_ONLN);
	kvm->cfg.custom_rootfs_name = "default";

	while (argc != 0) {
		BUILD_OPTIONS(options, &kvm->cfg, kvm);
		argc = parse_options(argc, argv, options, run_usage,
				PARSE_OPT_STOP_AT_NON_OPTION |
				PARSE_OPT_KEEP_DASHDASH);
		if (argc != 0) {
			/* Cusrom options, should have been handled elsewhere */
			if (strcmp(argv[0], "--") == 0) {
				if (kvm_run_wrapper == KVM_RUN_SANDBOX) {
					kvm->cfg.sandbox = DEFAULT_SANDBOX_FILENAME;
					kvm_run_write_sandbox_cmd(kvm, argv+1, argc-1);
					break;
				}
			}

			if ((kvm_run_wrapper == KVM_RUN_DEFAULT && kvm->cfg.kernel_filename) ||
				(kvm_run_wrapper == KVM_RUN_SANDBOX && kvm->cfg.sandbox)) {
				fprintf(stderr, "Cannot handle parameter: "
						"%s\n", argv[0]);
				usage_with_options(run_usage, options);
				free(kvm);
				return ERR_PTR(-EINVAL);
			}
			if (kvm_run_wrapper == KVM_RUN_SANDBOX) {
				/*
				 * first unhandled parameter is treated as
				 * sandbox command
				 */
				kvm->cfg.sandbox = DEFAULT_SANDBOX_FILENAME;
				kvm_run_write_sandbox_cmd(kvm, argv, argc);
			} else {
				/*
				 * first unhandled parameter is treated as a kernel
				 * image
				 */
				kvm->cfg.kernel_filename = argv[0];
			}
			argv++;
			argc--;
		}

	}

	kvm->nr_disks = kvm->cfg.image_count;

	if (!kvm->cfg.kernel_filename)
		kvm->cfg.kernel_filename = find_kernel();

	if (!kvm->cfg.kernel_filename) {
		kernel_usage_with_options();
		return ERR_PTR(-EINVAL);
	}

	kvm->cfg.vmlinux_filename = find_vmlinux();
	kvm->vmlinux = kvm->cfg.vmlinux_filename;

	if (kvm->cfg.nrcpus == 0)
		kvm->cfg.nrcpus = nr_online_cpus;

	if (!kvm->cfg.ram_size)
		kvm->cfg.ram_size = get_ram_size(kvm->cfg.nrcpus);

	if (kvm->cfg.ram_size < MIN_RAM_SIZE_MB)
		die("Not enough memory specified: %lluMB (min %lluMB)",
		    (unsigned long long)kvm->cfg.ram_size, MIN_RAM_SIZE_MB);

	if (kvm->cfg.ram_size > host_ram_size())
		pr_warning("Guest memory size %lluMB exceeds host physical RAM size %lluMB",
			   (unsigned long long)kvm->cfg.ram_size, (unsigned long long)host_ram_size());

	kvm->cfg.ram_size <<= MB_SHIFT;

	if (!kvm->cfg.dev)
		kvm->cfg.dev = DEFAULT_KVM_DEV;

	if (!kvm->cfg.console)
		kvm->cfg.console = DEFAULT_CONSOLE;

	if (!strncmp(kvm->cfg.console, "virtio", 6))
		kvm->cfg.active_console  = CONSOLE_VIRTIO;
	else if (!strncmp(kvm->cfg.console, "serial", 6))
		kvm->cfg.active_console  = CONSOLE_8250;
	else if (!strncmp(kvm->cfg.console, "hv", 2))
		kvm->cfg.active_console = CONSOLE_HV;
	else
		pr_warning("No console!");

	if (!kvm->cfg.host_ip)
		kvm->cfg.host_ip = DEFAULT_HOST_ADDR;

	if (!kvm->cfg.guest_ip)
		kvm->cfg.guest_ip = DEFAULT_GUEST_ADDR;

	if (!kvm->cfg.guest_mac)
		kvm->cfg.guest_mac = DEFAULT_GUEST_MAC;

	if (!kvm->cfg.host_mac)
		kvm->cfg.host_mac = DEFAULT_HOST_MAC;

	if (!kvm->cfg.script)
		kvm->cfg.script = DEFAULT_SCRIPT;

	if (!kvm->cfg.network)
                kvm->cfg.network = DEFAULT_NETWORK;

	memset(real_cmdline, 0, sizeof(real_cmdline));
	kvm__arch_set_cmdline(real_cmdline, kvm->cfg.vnc || kvm->cfg.sdl);

	if (strlen(real_cmdline) > 0)
		strcat(real_cmdline, " ");

	if (kvm->cfg.kernel_cmdline)
		strlcat(real_cmdline, kvm->cfg.kernel_cmdline, sizeof(real_cmdline));

	if (!kvm->cfg.guest_name) {
		if (kvm->cfg.custom_rootfs) {
			kvm->cfg.guest_name = kvm->cfg.custom_rootfs_name;
		} else {
			sprintf(default_name, "guest-%u", getpid());
			kvm->cfg.guest_name = default_name;
		}
	}

	if (!kvm->cfg.using_rootfs && !kvm->cfg.disk_image[0].filename && !kvm->cfg.initrd_filename) {
		char tmp[PATH_MAX];

		kvm_setup_create_new(kvm->cfg.custom_rootfs_name);
		kvm_setup_resolv(kvm->cfg.custom_rootfs_name);

		snprintf(tmp, PATH_MAX, "%s%s", kvm__get_dir(), "default");
		if (virtio_9p__register(kvm, tmp, "/dev/root") < 0)
			die("Unable to initialize virtio 9p");
		if (virtio_9p__register(kvm, "/", "hostfs") < 0)
			die("Unable to initialize virtio 9p");
		kvm->cfg.using_rootfs = kvm->cfg.custom_rootfs = 1;
	}
#ifndef CONFIG_MIPS
	if (kvm->cfg.using_rootfs) {
		strcat(real_cmdline, " root=/dev/root rw rootflags=rw,trans=virtio,version=9p2000.L rootfstype=9p");
		if (kvm->cfg.custom_rootfs) {
			kvm_run_set_sandbox(kvm);

			strcat(real_cmdline, " init=/virt/init");

			if (!kvm->cfg.no_dhcp)
				strcat(real_cmdline, "  ip=dhcp");
			if (kvm_setup_guest_init(kvm))
				die("Failed to setup init for guest.");
		}
	} else
#endif
		if (!strstr(real_cmdline, "root=")) {
		strlcat(real_cmdline, " root=/dev/vda rw ", sizeof(real_cmdline));
	}

	kvm->cfg.real_cmdline = real_cmdline;

	printf("  # %s run -k %s -m %u -c %d --name %s\n", KVM_BINARY_NAME,
	       kvm->cfg.kernel_filename, (unsigned)(kvm->cfg.ram_size / 1024 / 1024), kvm->cfg.nrcpus, kvm->cfg.guest_name);

	if (init_list__init(kvm) < 0)
		die ("Initialisation failed");

	return kvm;
}
Exemplo n.º 5
0
/* determine version of OS by mem
 * 1.To get signature of multiply versions of os, which is the md5 of kernel code
 * 2.Compared by a decision tree.
 * 3.Done!*/
void determineOsVersion(Mem * mem)
{
    int i;
    int pageSize = 4 * 1024;    //4k
    int totalPageNumber = mem->mem_size / (4 * 1024);   //assume that every page has 4k

    unsigned codePageNo = 0;
    //record when two page have different page index and the same sharp
    int calledPages[totalPageNumber];
    int dsmPages[totalPageNumber];
    //record virtual address
    unsigned virtualAddrs[totalPageNumber];
    for (i = 0; i < totalPageNumber; i++) {
        calledPages[i] = 0;
        dsmPages[i] = 0;
        virtualAddrs[i] = 0;
    }

    //start address
    unsigned startVirtualAddr = KERNEL_START_ADDRESS;

    //with dissemble or not
    int withDissemble = 1;

    int cr3PageIndex = 0;
    unsigned cr3Pages[20];
    for (i = 0; i < 20; i++) {
        cr3Pages[i] = 0;
    }

    struct timeval earlier;
    struct timeval later;
    if (gettimeofday(&earlier, NULL)) {
        perror("gettimeofday() error");
        exit(1);
    }
    //generate step 1 clusters
    clusters[0].end = 0;
    int pre_rw = -1;            //read or write
    int pre_us = -1;            //use or system
    int pre_g = -1;             //global, no move out of TLB
    int pre_ps = -1;            //page size
    unsigned cluster_index = 0;
    newstart = 1;

    unsigned vAddr = startVirtualAddr;
    for (; vAddr > KERNEL_START_ADDRESS - 1; vAddr += 0x1000) {
        //printf("startvirtual %x:\n",startVirtualAddr);

        int rw = 0;             //read or write
        int us = 0;             //use or system
        int g = 0;              //global, no move out of TLB
        int ps = 0;             //page size 4M or 4k
        unsigned pAddr =
            vtopPageProperty(mem->mem, mem->mem_size, mem->pgd, vAddr, &rw,
                             &us, &g, &ps);

        //if PHYSICAL ADDRESS is not VALID, then start a new cluster
        if (pAddr < 0 || pAddr > mem->mem_size || us != 0 || g != 256) {
            if (newstart == 0) {
                clusters[cluster_index].end = vAddr - 1;
                //printf("err address end is %x %x\n", vAddr, ranges[range_index].end);
                newstart = 1;
            }
            continue;
        }
        //if any property changes, then start a new cluster
        if (rw != pre_rw || us != pre_us || g != pre_g || ps != pre_ps) {
            if (newstart == 0) {
                clusters[cluster_index].end = vAddr - 1;
                //printf("property change end is %x %x\n", vAddr, ranges[range_index].end);
                newstart = 1;
            }
        }
        //update pre properties
        pre_rw = rw;
        pre_us = us;
        pre_g = g;
        pre_ps = ps;

        //collect pages  with continuous properties;
        if (newstart) {
            clusters[++cluster_index].start = vAddr;
            clusters[cluster_index].end = vAddr + pageSize - 1;
            newstart = 0;
        } else
            clusters[cluster_index].end = vAddr + pageSize - 1;
    }

    if (gettimeofday(&later, NULL)) {
        perror("gettimeofday() error");
        exit(1);
    }
    printf("step1, cluster: %d,time cost is %d milliseconds\n",
           cluster_index, timeval_diff(NULL, &later, &earlier) / 1000);

    //step2. kernel code page clusters with cr3
    if (gettimeofday(&earlier, NULL)) {
        perror("gettimeofday() error");
        exit(1);
    }

    unsigned startVirtual = startVirtualAddr;
    for (; startVirtual > startVirtualAddr - 1; startVirtual += 0x1000) {
        //      for (; startVirtual < 0x818f0000; startVirtual += 0x1000) {
        unsigned vAddr = startVirtual;

        int rw = 0;             //read or write
        int us = 0;             //use or system
        int g = 0;              //global(no move out of TLB) or not global
        int ps = 0;             //page size
        unsigned pAddr =
            vtopPageProperty(mem->mem, mem->mem_size, mem->pgd, vAddr, &rw,
                             &us, &g, &ps);

        // IS PHYSICAL ADDRESS VALID?
        if (pAddr == -1 || pAddr > mem->mem_size)
            continue;

        //collect pages which are system access, and global pages
        if (us == 0 && g == 256) {
//                      printf("r only page %x\n", vAddr);
            if (find_kernel(mem, vAddr, pageSize) == 0) {
                //record kernel address
                cr3Pages[cr3PageIndex++] = vAddr;
                printf("kernel start at %x\n", vAddr);
            }
        }
    }

    if (gettimeofday(&later, NULL)) {
        perror("gettimeofday() error");
        exit(1);
    }
    printf("step2 time cost is %d milliseconds\n",
           timeval_diff(NULL, &later, &earlier) / 1000);

    //step 3. clusters
    if (gettimeofday(&earlier, NULL)) {
        perror("gettimeofday() error");
        exit(1);
    }
    int cr3PagesNo = 0;
    ranges[0].end = 0;
    newstart = 1;
    for (i = 1; i <= cluster_index; i++) {
//:w            printf("%x %x\n", clusters[i].start, clusters[i].end);
        if (containKernelAddres(clusters[i], cr3Pages) == -1) {
            continue;
        }
        cr3PagesNo++;
        unsigned vAddr = clusters[i].start;
        //      printf("%x %x\n", clusters[i].start, clusters[i].end);
        newstart = 1;
        for (; vAddr < clusters[i].end; vAddr += 0x1000) {
            unsigned pAddr =
                vtop(mem->mem, mem->mem_size, mem->pgd, vAddr);
            if (vAddr == out_pc)
                code_init(mem, vAddr, pageSize, dsmPages, virtualAddrs, 1,
                          calledPages, &codePageNo);
            else
                code_init(mem, vAddr, pageSize, dsmPages, virtualAddrs, 0,
                          calledPages, &codePageNo);
        }
        ranges[range_index].end = clusters[i].end;
    }
    if (gettimeofday(&later, NULL)) {
        perror("gettimeofday() error");
        exit(1);
    }
    printf("step2, cluster: %d\n", cr3PagesNo);

    printf("step3, cluster: %d,time cost is %d milliseconds\n",
           range_index, timeval_diff(NULL, &later, &earlier) / 1000);

    //3.find the kernel core code page cluster, and print it
    int osNumber = initDb();

    if (gettimeofday(&earlier, NULL)) {
        perror("gettimeofday() error");
        exit(1);
    }

    int max_len = 0, max_index = 0;
    for (i = 1; i <= range_index; i++) {
//              printf("start:%x, end:%x: len:%x kernel\n", ranges[i].start, ranges[i].end, ranges[i].len);
        if (ranges[i].len > max_len) {
            max_index = i;
            max_len = ranges[i].len;
        }
    }

    //4.print md5 of pages that can be disassembled
    int availableOs[FINGERPRINT_NO], matchCounts[FINGERPRINT_NO];
    for (i = 0; i < FINGERPRINT_NO; i++) {
        availableOs[i] = 1;
        matchCounts[i] = 0;
    }
    startVirtualAddr = ranges[max_index].start;
    unsigned disasPageNo = 0;
    unsigned totalPageNo = 0;
    for (; startVirtualAddr <= ranges[max_index].end;
         startVirtualAddr += 0x1000) {
        totalPageNo++;
        unsigned pAddr =
            vtop(mem->mem, mem->mem_size, mem->pgd, startVirtualAddr);
        if (pAddr == -1 || pAddr > mem->mem_size)
            continue;
        int pageIndex = pAddr / pageSize;
        if (dsmPages[pageIndex] == 1) {
            int offset =
                (startVirtualAddr - ranges[max_index].start) / 4096;
            void *startAdress =
                (void *) ((unsigned) mem->mem + pageIndex * pageSize);
            unsigned char md5digest[16];
            MDMem(startAdress, pageSize, md5digest);
            //      printf("%x ", vaddr); //print vaddr
            MDPrint(md5digest);
            printf("\n");

            //search hash table
            int ret =
                matchByIndex(osNumber, md5digest, offset, availableOs,
                             matchCounts);

//                      genMd5WithOffset(startAdress, pageSize, startVirtualAddr, offset);
            disasPageNo++;
        }
    }

    if (gettimeofday(&later, NULL)) {
        perror("gettimeofday() error");
        exit(1);
    }
    printf("step4.time cost is %d milliseconds\n",
           timeval_diff(NULL, &later, &earlier) / 1000);

    int maxIndex = -1;
    int maxMatch = 0;
    for (i = 0; i < FINGERPRINT_NO; i++) {
        if (matchCounts[i] > maxMatch) {
            maxIndex = i;
            maxMatch = matchCounts[i];
        }
    }
    if (maxMatch > 0)
        printf("Os is %s, match count is %d\n",
               fingerprints[maxIndex].osVersion, maxMatch);
    else
        puts("Unknown OS!");

    return;
}
Exemplo n.º 6
0
int kvm_cmd_run(int argc, const char **argv, const char *prefix)
{
	struct virtio_net_parameters net_params;
	static char real_cmdline[2048];
	struct framebuffer *fb = NULL;
	unsigned int nr_online_cpus;
	int exit_code = 0;
	int max_cpus;
	char *hi;
	int i;
	void *ret;

	signal(SIGALRM, handle_sigalrm);
	signal(SIGQUIT, handle_sigquit);
	signal(SIGUSR1, handle_sigusr1);
	signal(SIGUSR2, handle_sigusr2);

	nr_online_cpus = sysconf(_SC_NPROCESSORS_ONLN);

	while (argc != 0) {
		argc = parse_options(argc, argv, options, run_usage,
				PARSE_OPT_STOP_AT_NON_OPTION);
		if (argc != 0) {
			if (kernel_filename) {
				fprintf(stderr, "Cannot handle parameter: "
						"%s\n", argv[0]);
				usage_with_options(run_usage, options);
				return EINVAL;
			}
			/* first unhandled parameter is treated as a kernel
			   image
			 */
			kernel_filename = argv[0];
			argv++;
			argc--;
		}

	}

	if (!kernel_filename)
		kernel_filename = find_kernel();

	if (!kernel_filename) {
		kernel_usage_with_options();
		return EINVAL;
	}

	vmlinux_filename = find_vmlinux();

	if (nrcpus == 0)
		nrcpus = nr_online_cpus;
	else if (nrcpus < 1 || nrcpus > KVM_NR_CPUS)
		die("Number of CPUs %d is out of [1;%d] range", nrcpus, KVM_NR_CPUS);

	if (!ram_size)
		ram_size	= get_ram_size(nrcpus);

	if (ram_size < MIN_RAM_SIZE_MB)
		die("Not enough memory specified: %lluMB (min %lluMB)", ram_size, MIN_RAM_SIZE_MB);

	if (ram_size > host_ram_size())
		pr_warning("Guest memory size %lluMB exceeds host physical RAM size %lluMB", ram_size, host_ram_size());

	ram_size <<= MB_SHIFT;

	if (!kvm_dev)
		kvm_dev = DEFAULT_KVM_DEV;

	if (!console)
		console = DEFAULT_CONSOLE;

	if (!strncmp(console, "virtio", 6))
		active_console  = CONSOLE_VIRTIO;
	else
		active_console  = CONSOLE_8250;

	if (!host_ip_addr)
		host_ip_addr = DEFAULT_HOST_ADDR;

	if (!guest_mac)
		guest_mac = DEFAULT_GUEST_MAC;

	if (!script)
		script = DEFAULT_SCRIPT;

	if (virtio_9p_dir) {
		char tmp[PATH_MAX];

		if (realpath(virtio_9p_dir, tmp))
			virtio_9p__init(kvm, tmp);
		else
			die("Failed resolving 9p path");
	}

	symbol__init(vmlinux_filename);

	term_init();

	kvm = kvm__init(kvm_dev, ram_size);

	ioeventfd__init();

	max_cpus = kvm__max_cpus(kvm);

	if (nrcpus > max_cpus) {
		printf("  # Limit the number of CPUs to %d\n", max_cpus);
		kvm->nrcpus	= max_cpus;
	}

	kvm->nrcpus = nrcpus;

	/*
	 * vidmode should be either specified
	 * either set by default
	 */
	if (vnc || sdl) {
		if (vidmode == -1)
			vidmode = 0x312;
	} else
		vidmode = 0;

	memset(real_cmdline, 0, sizeof(real_cmdline));
	strcpy(real_cmdline, "notsc noapic noacpi pci=conf1");
	if (vnc || sdl) {
		strcat(real_cmdline, " video=vesafb console=tty0");
	} else
		strcat(real_cmdline, " console=ttyS0 earlyprintk=serial");
	strcat(real_cmdline, " ");
	if (kernel_cmdline)
		strlcat(real_cmdline, kernel_cmdline, sizeof(real_cmdline));

	hi = NULL;
	if (!image_filename[0]) {
		hi = host_image(real_cmdline, sizeof(real_cmdline));
		if (hi) {
			image_filename[0] = hi;
			readonly_image[0] = true;
			image_count++;
		}
	}

	if (!strstr(real_cmdline, "root="))
		strlcat(real_cmdline, " root=/dev/vda rw ", sizeof(real_cmdline));

	if (image_count) {
		kvm->nr_disks = image_count;
		kvm->disks    = disk_image__open_all(image_filename, readonly_image, image_count);
		if (!kvm->disks)
			die("Unable to load all disk images.");

		virtio_blk__init_all(kvm);
	}

	free(hi);

	printf("  # kvm run -k %s -m %Lu -c %d\n", kernel_filename, ram_size / 1024 / 1024, nrcpus);

	if (!kvm__load_kernel(kvm, kernel_filename, initrd_filename,
				real_cmdline, vidmode))
		die("unable to load kernel %s", kernel_filename);

	kvm->vmlinux		= vmlinux_filename;

	ioport__setup_legacy();

	rtc__init();

	serial8250__init(kvm);

	pci__init();

	if (active_console == CONSOLE_VIRTIO)
		virtio_console__init(kvm);

	if (virtio_rng)
		while (virtio_rng--)
			virtio_rng__init(kvm);

	if (!network)
		network = DEFAULT_NETWORK;

	if (!strncmp(network, "virtio", 6)) {
		net_params = (struct virtio_net_parameters) {
			.host_ip = host_ip_addr,
			.kvm = kvm,
			.script = script
		};
		sscanf(guest_mac,	"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
							net_params.guest_mac,
							net_params.guest_mac+1,
							net_params.guest_mac+2,
							net_params.guest_mac+3,
							net_params.guest_mac+4,
							net_params.guest_mac+5);

		virtio_net__init(&net_params);
	}
Exemplo n.º 7
0
/**
 * Set command.
 */
void command_set(const char* line) {

	char cmd[MAX_BUFFER];
	char key[MAX_BUFFER];
	char func[MAX_BUFFER];
	char arg1[MAX_BUFFER];
	char arg2[MAX_BUFFER];

	int argc = sscanf(line, "%s %s = %s %s %s", cmd, key, func, arg1, arg2);
	if (argc < 3) {
		puts("invalid arguments");
		return;
	}

	float* matrix = NULL;

	switch (argc) {
		case 3:
			if (strcasecmp(func, "identity") == 0) {
				matrix = identity_matrix();
			} else {
				goto invalid;
			}
			break;

		case 4:
			if (strcasecmp(func, "random") == 0) {
				int seed = atol(arg1);
				matrix = random_matrix(seed);
			} else if (strcasecmp(func, "uniform") == 0) {
				float value = atof(arg1);
				matrix = uniform_matrix(value);
			} else if (strcasecmp(func, "cloned") == 0) {
				MATRIX_GUARD(arg1);
				matrix = cloned(m);
			} else if (strcasecmp(func, "sorted") == 0) {
				MATRIX_GUARD(arg1);
				matrix = sorted(m);
			} else if (strcasecmp(func, "rotated") == 0) {
				MATRIX_GUARD(arg1);
				matrix = rotated(m);
			} else if (strcasecmp(func, "reversed") == 0) {
				MATRIX_GUARD(arg1);
				matrix = reversed(m);
			} else if (strcasecmp(func, "transposed") == 0) {
				MATRIX_GUARD(arg1);
				matrix = transposed(m);
			} else {
				goto invalid;
			}
			break;

		case 5:
			if (strcasecmp(func, "sequence") == 0) {
				float start = atof(arg1);
				float step = atof(arg2);
				matrix = sequence_matrix(start, step);
			} else if (strcasecmp(func, "scalar.add") == 0) {
				MATRIX_GUARD(arg1);
				float value = atof(arg2);
				matrix = scalar_add(m, value);
			} else if (strcasecmp(func, "scalar.mul") == 0) {
				MATRIX_GUARD(arg1);
				float value = atof(arg2);
				matrix = scalar_mul(m, value);
			} else if (strcasecmp(func, "matrix.add") == 0) {
				MATRIX_GUARD_PAIR(arg1, arg2);
				matrix = matrix_add(m1, m2);
			} else if (strcasecmp(func, "matrix.mul") == 0) {
				MATRIX_GUARD_PAIR(arg1, arg2);
				matrix = matrix_mul(m1, m2);
			} else if (strcasecmp(func, "matrix.pow") == 0) {
				MATRIX_GUARD(arg1);
				float exponent = atof(arg2);
				matrix = matrix_pow(m, exponent);
			} else if (strcasecmp(func, "matrix.conv") == 0) {
				MATRIX_GUARD(arg1);
				const float* kernel = find_kernel(arg2);
				if (kernel == NULL) {
					puts("no such kernel");
				}
				matrix = matrix_conv(m, kernel);
			} else {
				goto invalid;
			}
			break;
	}

	entry* e = find_entry(key);
	if (e == NULL) {
		e = add_entry(key);
	} else {
		free(e->matrix);
	}

	e->matrix = matrix;

	puts("ok");
	return;

invalid:
	puts("invalid arguments");
}