Commit d3b3e663 authored by Paras Garg's avatar Paras Garg

changes

parents
kvm-hello-world
*.o
*.img
David Wragg
Ben Noordhuis
Andreas Neubaum
Copyright (c) 2016,2018 David Wragg
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
CFLAGS = -Wall -Wextra -Werror -O2
.PHONY: run
run: kvm-hello-world
./kvm-hello-world
./kvm-hello-world -s
./kvm-hello-world -p
./kvm-hello-world -l
kvm-hello-world: kvm-hello-world.o payload.o
$(CC) $^ -o $@
payload.o: payload.ld guest16.o guest32.img.o guest64.img.o
$(LD) -T $< -o $@
guest64.o: guest.c
$(CC) $(CFLAGS) -m64 -ffreestanding -fno-pic -c -o $@ $^
guest64.img: guest64.o
$(LD) -T guest.ld $^ -o $@
guest32.o: guest.c
$(CC) $(CFLAGS) -m32 -ffreestanding -fno-pic -c -o $@ $^
guest32.img: guest32.o
$(LD) -T guest.ld -m elf_i386 $^ -o $@
%.img.o: %.img
$(LD) -b binary -r $^ -o $@
.PHONY: clean
clean:
$(RM) kvm-hello-world kvm-hello-world.o payload.o guest16.o \
guest32.o guest32.img guest32.img.o \
guest64.o guest64.img guest64.img.o
# A minimal KVM example
kvm-hello-world is a very simple example program to demonstrate the
use of the KVM API provided by the Linux kernel. It acts as a very
simple VM host, and runs a trivial program in a VM. I tested it on
Intel processors with the VMX hardware virtualization extensions. It
*might* work on AMD processors with AMD-V, but that hasn't been
tested.
## Background
[KVM](https://en.wikipedia.org/wiki/Kernel-based_Virtual_Machine) is
the Linux kernel subsystem that provides access to hardware
virtualization features of the processor. On x86, this means Intel's
VMX or AMD's AMD-V. VMX is also known as
[VT-x](https://en.wikipedia.org/wiki/VT-x); VT-x seems to be the
marketing term, whereas VMX is used in the Intel x86 manual set.
In practice, KVM is m often employed via
[qemu](http://wiki.qemu.org/). In that case, KVM provides
virtualization of the CPU and a few other key hardware components
intimately associated with the CPU, such as the interrupt controller.
qemu emulates all the devices making up the rest of a typical x86
system. qemu predates KVM, and can also operate independently of it,
performing CPU virtualization in software instead.
But if you want to learn about the details of KVM, qemu is not a great
resource. It's a big project with a lot of features and support for
emulating many devices.
There's another project that is much more approachable:
[kvmtool](https://github.com/kvmtool/kvmtool). Like qemu, kvmtool does
full-system emulation. unlike qemu, it is deliberately minimal,
emulating just a few devices. But while kvmtool is impressive
demonstration of how simple and clean a KVM-based full-system emulator
can be, it's still far more than a bare-bones example.
So, as no such example seems to exist, I wrote one by studying api.txt
and the kvmtool sources. (Update: When I wrote this, I had overlooked
https://github.com/soulxu/kvmsample).
## Notes
The code is straightforward. It:
* Opens `/dev/kvm` and checks the version.
* Makes a `KVM_CREATE_VM` call to creates a VM.
* Uses mmap to allocate some memory for the VM.
* Makes a `KVM_CREATE_VCPU` call to creates a VCPU within the VM, and
mmaps its control area.
* Sets the FLAGS and CS:IP registers of the VCPU.
* Copies a few bytes of code into the VM memory.
* Makes a `KVM_RUN` call to execute the VCPU.
* Checks that the VCPU execution had the expected result.
A couple of aspects are worth noting:
Note that the Intel VMX extensions did not initially implement support
for real mode. In fact, they restricted VMX guests to paged protected
mode. VM hosts were expected to emulate the unsupported modes in
software, only employing VMX when a guest had entered paged protected
mode (KVM does not implement such emulation support; I assume it is
delegated to qemu). Later VMX implementations (since Westmere aka
Nehalem-C in 2010) include *Unrestricted Guest Mode*: support for
virtualization of all x86 modes in hardware.
The code run in the VM code exits with a HLT instruction. There are
many ways to cause a VM exit, so why use a HLT instruction? The most
obvious way might be the VMCALL (or VMMCALL on AMD) instruction, which
it specifically intended to call out to the hypervisor. But it turns
out the KVM reserves VMCALL/VMMCALL for its internal hypercall
mechanism, without notifying the userspace VM host program of the VM
exits caused by these instructions. So we need some other way to
trigger a VM exit. HLT is convenient because it is a single-byte
instruction.
/* IO PORT USED*/
#include<unistd.h>
#include<sys/types.h>
#define HYPERCALL 0X80
#define PRINT_STRING_PORT 0xE5
#define NUM_EXIT_PORT 0XE6
#define PRINT_VAL_PORT 0XE7
#define CHAR_PORT 0xE9
#define HC_OPEN (HYPERCALL | 1)
#define HC_READ (HYPERCALL | 2)
#define HC_WRITE (HYPERCALL | 3)
#define HC_CLOSE (HYPERCALL | 4)
#define HC_LSEEK (HYPERCALL | 5)
#define HC_EXIT (HYPERCALL | 6 )
#define HC_PRINT_STRING (HYPERCALL | 7)
#define HC_PRINT_VALUE (HYPERCALL | 8)
#define HC_NUM_EXIT (HYPERCALL | 9)
#define HC_O (HYPERCALL | 10)
/*struct used for file system calls */
struct open_data{
uint32_t fileoffset;
int flags;
mode_t mode;
int fd;
int errno_hc;
};
struct close_data{
int close_fd;
int result;
int errno_hc;
};
struct read_write_data{
int fd;
uint32_t buffer_offset;
size_t count;
ssize_t result;
int errno_hc;
};
struct lseek_data{
int fd;
off_t offset;
int whence;
off_t result;
int errno_hc;
};
#include <stddef.h>
#include <stdint.h>
#include"filesystem.h"
#include<fcntl.h>
#include<errno.h>
#include<stdarg.h>
int errno_hc;
static void outb8(uint16_t port, uint8_t value) {
asm("outb %0,%1" : /* empty */ : "a" (value), "Nd" (port) : "memory");
}
/*
__attribut__ are used to allow compiler optimization in code generated
here noreturn means function will not return any value
In function for loop will travesrse trough each character and output to file
*/
static inline void printVal(uint32_t value) {
asm("out %0,%1" : /* empty */ : "a" (value), "Nd" (HC_PRINT_VALUE) : "memory");
}
void display(char *str){
int offset=str-(char*)0X0;
asm("out %0, %1": :"a"(offset),"Nd"(HC_PRINT_STRING) :"memory");
}
uint32_t getNumExits(){
uint32_t exits=0;
asm("in %1,%0" : "=a" (exits): "Nd" (HC_NUM_EXIT) : "memory");
return exits;
}
int open_hc( char *pathname,int flags,...){
va_list ap;
va_start(ap,flags);
struct open_data data[1];
data[0].flags=flags;
data[0].mode=va_arg(ap,mode_t);
printVal(data[0].mode);
data[0].fileoffset=pathname-(char*)0X0;
data[0].fd=-1;
va_end(ap);
int offset=(char*)&data-(char*)0X0;
asm("outl %0, %1" : : "a"(offset),"Nd"(HC_OPEN) : "memory" );
errno_hc=data[0].errno_hc;
return data[0].fd;
}
int close_hc(int close_fd){
struct close_data data[1];
data[0].close_fd=close_fd;
uint32_t offset=(char*)data-(char*)0X0;
asm("outl %0, %1" : : "a"(offset),"Nd"(HC_CLOSE) : "memory" );
errno_hc=data[0].errno_hc;
return data[0].result;
}
ssize_t read_hc(int fd,void* buf,int count){
struct read_write_data data[1];
data[0].fd=fd;
data[0].count=count;
data[0].buffer_offset=(char*)buf-(char*)0X0;
uint32_t offset=(char*)data-(char*)0X0;
asm("outl %0, %1" : : "a"(offset),"Nd"(HC_READ) : "memory" );
errno_hc=data[0].errno_hc;
return data[0].result;
}
ssize_t write_hc(int fd,void* buf,int count){
struct read_write_data data[1];
data[0].fd=fd;
data[0].count=count;
data[0].buffer_offset=(char*)buf-(char*)0X0;
uint32_t offset=(char*)data-(char*)0X0;
asm("outl %0, %1" : : "a"(offset),"Nd"(HC_WRITE) : "memory" );
errno_hc=data[0].errno_hc;
return data[0].result;
}
off_t lseek_hc(int fd,off_t offset,int whence){
struct lseek_data data[1];
data[0].fd=fd;
data[0].offset=offset;
data[0].whence=whence;
uint32_t _offset=(char*)data-(char*)0X0;
asm("outl %0, %1" : : "a"(_offset),"Nd"(HC_LSEEK) : "memory" );
errno_hc=data[0].errno_hc;
return data[0].result;
}
void
__attribute__((noreturn))
__attribute__((section(".start")))
_start(void) {
display("*******************************GUEST OS BOOTED********************************\n");
uint32_t numExits = getNumExits();
char *str="Number of exits required to print this string \0";
display(str);
numExits = getNumExits()-numExits;
printVal(numExits);
display("\n");
//char str1[]={'t','e','s','t','.','t','x','t','\0'};
display("checking open \n");
int fd=open_hc("test.txt",O_RDWR|O_APPEND,S_IRWXU);
//int fd=open_hc("test4.txt",O_CREAT|O_RDWR|O_TRUNC,S_IRWXG);
write_hc(1,"Paras Garg",11);
// int fd=open_hc("test2.txt",O_RDWR|O_APPEND,S_IRWXU);
if(fd!=-1){
lseek_hc(fd,1,SEEK_CUR);
char read_array[10];
int n_read=read_hc(fd,read_array,10);
if(n_read!=-1){
read_array[n_read-1]='\0';
display(read_array);
display("\n");
}
char write_array[]={'h','c','r','m','n'};
int n_write=write_hc(fd,write_array,5);
display("redff \n");
n_write=write_hc(fd,write_array,5);
if(n_write!=-1){
write_array[n_write-1]='\0';
display("written : ");
display(write_array);
display("\n");
}
fd=close_hc(fd);
}
const char *p= "Hello, world!\n";
for (; *p; ++p)
outb8(CHAR_PORT, *p);
display("********************************SENDING HALT**********************************\n");
*(long *) 0x400 = 42;
for (;;)
asm("hlt" : /* empty */ : "a" (42) : "memory");
}
OUTPUT_FORMAT(binary)
SECTIONS
{
.start : { *(.start) }
.text : { *(.text*) }
.rodata : { *(.rodata) }
.data : { *(.data) }
}
.code16
.global code16, code16_end
guest16:
movw $42, %ax
movw %ax, 0x400
hlt
guest16_end:
This diff is collapsed.
SECTIONS
{
.payload16 0 : {
guest16 = .;
guest16.o(.text)
guest16_end = .;
}
.payload32 0 : AT(LOADADDR(.payload16)+SIZEOF(.payload16)) {
guest32 = .;
guest32.img.o
guest32_end = .;
}
.payload64 0 : AT(LOADADDR(.payload32)+SIZEOF(.payload32)) {
guest64 = .;
guest64.img.o
guest64_end = .;
}
}
hcrmnhcrmnhcrmnhcrmnhcrmnhcrmnhcrmnhcrmnhcrmnhcrmn
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment