├── README.md ├── cpuid.py ├── ioctl_opt.py ├── iodev.py ├── iodev_acpi.py ├── iodev_pc.py ├── iodev_pci.py ├── iodev_qemu.py ├── iodev_tpm.py ├── iodev_virtio.py ├── kvm.py ├── kvmapi.py ├── kvmo.py ├── memmgr.py ├── scsi.py ├── vmm.py └── x86.py /README.md: -------------------------------------------------------------------------------- 1 | # kvmtest 2 | 3 | An experimental VMM for KVM written in Python. This is simply an experimental 4 | proof of concept which was hacked together enough to be able to boot OVMF, then 5 | install Linux on a disk and boot it. 6 | 7 | Mainly I wrote this to demonstrate how surprisingly easy the KVM API is to use. 8 | 9 | Features: 10 | 11 | - Can boot OVMF UEFI 12 | - virtio-scsi interface featuring minimal block device and optical device emulation 13 | - SDL2 framebuffer (qxl) (install PySDL2) 14 | - PS/2 keyboard 15 | - Serial port 16 | 17 | If you have any questions, don't hesitate to [contact 18 | me](https://www.devever.net/~hl/contact) via IRC or email. 19 | 20 | [**See article for introduction and demo video.**](https://www.devever.net/~hl/kvm) 21 | 22 | ## Example usage 23 | 24 | ### Nix users 25 | 26 | Example usage for those with Nix installed, booting a [Debian 27 | DVD](https://cdimage.debian.org/debian-cd/current/amd64/iso-dvd/debian-11.7.0-amd64-DVD-1.iso): 28 | ``` 29 | $ nix-shell -p python3Packages.pysdl2 OVMF.fd 30 | $ OVMF="$(cat "$buildInputs" | tr ' ' '\n' | grep OVMF)" 31 | $ cp "$OVMF/FV/OVMF_VARS.fd" ./ 32 | $ chmod +w OVMF_VARS.fd 33 | $ qemu-img create -f raw test.bin 8G 34 | $ ./kvm.py \ 35 | -fwcode $OVMF/FV/OVMF_CODE.fd \ 36 | -fwvars OVMF_VARS.fd \ 37 | -optical debian-11.7.0-amd64-DVD-1.iso \ 38 | -disk test.bin 39 | ``` 40 | 41 | ### Other users 42 | 43 | If you don't have Nix installed, you can get a [prebuilt demo OVMF image from 44 | here to play with](https://www.devever.net/~hl/f/OVMF-Demo-Image.tar.gz). Pass 45 | the paths to `OVMF_CODE.fd` and `OVMF_VARS.fd` to `kvm.py`, ensuring that 46 | `OVMF_VARS.fd` is writable. You will also need Python 3 and PySDL2 installed. 47 | You will also need an optical media image to boot, [such as a Debian 48 | DVD.](https://cdimage.debian.org/debian-cd/current/amd64/iso-dvd/debian-11.7.0-amd64-DVD-1.iso) 49 | ``` 50 | $ qemu-img create -f raw test.bin 8G 51 | $ ./kvm.py \ 52 | -fwcode $OVMF/FV/OVMF_CODE.fd \ 53 | -fwvars OVMF_VARS.fd \ 54 | -optical debian-11.7.0-amd64-DVD-1.iso \ 55 | -disk test.bin 56 | ``` 57 | 58 | ## Known issues 59 | 60 | This is just a demo of the KVM API. It was hacked together to demonstrate the 61 | KVM API, so don't expect the code to be very clean. 62 | 63 | - Arch Linux won't detect the PS/2 keyboard in the live ISO due to lack of working 64 | ACPI. Pass `earlymodules=i8042,atkbd` on the kernel command line. 65 | 66 | - Debian 10's installer works (in text mode), until it tries to install 67 | grub-uefi, where it fails for whatever reason. 68 | 69 | - Debian 10's graphical installer won't work currently. 70 | 71 | - No network devices are implemented. 72 | 73 | ## Licence 74 | 75 | ``` 76 | 2021 Hugo Landau 77 | ``` 78 | 79 | Files not otherwise marked are licenced under the MIT License. 80 | -------------------------------------------------------------------------------- /cpuid.py: -------------------------------------------------------------------------------- 1 | # From https://github.com/flababah/cpuid.py/blob/master/cpuid.py 2 | import os, ctypes 3 | 4 | class CPUID(ctypes.Structure): 5 | _fields_ = [ 6 | ('eax', ctypes.c_uint32), 7 | ('ebx', ctypes.c_uint32), 8 | ('ecx', ctypes.c_uint32), 9 | ('edx', ctypes.c_uint32) 10 | ] 11 | 12 | def get_cpuid(): 13 | _POSIX_64_OPC = [ 14 | 0x53, # push %rbx 15 | 0x89, 0xf0, # mov %esi, %eax 16 | 0x89, 0xd1, # mov %edx, %ecx 17 | 0x0f, 0xa2, # cpuid 18 | 0x89, 0x07, # mov %eax, (%rdi) 19 | 0x89, 0x5f, 0x04, # mov %ebx, 0x4(%rdi) 20 | 0x89, 0x4f, 0x08, # mov %ecx, 0x8(%rdi) 21 | 0x89, 0x57, 0x0c, # mov %edx, 0xC(%rdi) 22 | 0x5b, # pop %rbx 23 | 0xc3, # retq 24 | ] 25 | 26 | code = (ctypes.c_ubyte*len(_POSIX_64_OPC))(*_POSIX_64_OPC) 27 | 28 | _libc = ctypes.cdll.LoadLibrary(None) 29 | _libc.valloc.restype = ctypes.c_void_p 30 | _libc.valloc.argtypes = [ctypes.c_size_t] 31 | addr = _libc.valloc(len(_POSIX_64_OPC)) 32 | if not addr: 33 | raise Exception("allocation failure") 34 | 35 | _libc.mprotect.restype = ctypes.c_int 36 | _libc.mprotect.argtypes = [ctypes.c_void_p, ctypes.c_size_t, ctypes.c_int] 37 | ret = _libc.mprotect(addr, len(_POSIX_64_OPC), 1|2|4) 38 | if ret: 39 | raise Exception("mprotect failure") 40 | 41 | ctypes.memmove(addr, code, len(_POSIX_64_OPC)) 42 | 43 | func_type = ctypes.CFUNCTYPE(None, ctypes.POINTER(CPUID), ctypes.c_uint32, ctypes.c_uint32) 44 | f = func_type(addr) 45 | def g(eax, ecx): 46 | s = CPUID() 47 | f(s, eax, ecx) 48 | return s 49 | 50 | return g 51 | 52 | get_cpuid = get_cpuid() 53 | -------------------------------------------------------------------------------- /ioctl_opt.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2013-2018 Vincent Pelletier 2 | # 3 | # This library is free software; you can redistribute it and/or 4 | # modify it under the terms of the GNU Lesser General Public 5 | # License as published by the Free Software Foundation; either 6 | # version 2 of the License, or (at your option) any later version. 7 | # 8 | # This library is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 | # Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public 14 | # License along with this library; if not, write to the Free Software 15 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 16 | """ 17 | Pythonified linux asm-generic/ioctl.h . 18 | 19 | Produce IOCTL command numbers from their individual components, simplifying 20 | C header conversion to python (keeping magic constants and differences to 21 | C code to a minimum). 22 | 23 | Common parameter meanings: 24 | type (8-bits unsigned integer) 25 | Driver-imposed ioctl number. 26 | nr (8-bits unsigned integer) 27 | Driver-imposed ioctl function number. 28 | """ 29 | import ctypes 30 | 31 | _IOC_NRBITS = 8 32 | _IOC_TYPEBITS = 8 33 | _IOC_SIZEBITS = 14 34 | _IOC_DIRBITS = 2 35 | 36 | _IOC_NRMASK = (1 << _IOC_NRBITS) - 1 37 | _IOC_TYPEMASK = (1 << _IOC_TYPEBITS) - 1 38 | _IOC_SIZEMASK = (1 << _IOC_SIZEBITS) - 1 39 | _IOC_DIRMASK = (1 << _IOC_DIRBITS) - 1 40 | 41 | _IOC_NRSHIFT = 0 42 | _IOC_TYPESHIFT = _IOC_NRSHIFT + _IOC_NRBITS 43 | _IOC_SIZESHIFT = _IOC_TYPESHIFT + _IOC_TYPEBITS 44 | _IOC_DIRSHIFT = _IOC_SIZESHIFT + _IOC_SIZEBITS 45 | 46 | IOC_NONE = 0 47 | IOC_WRITE = 1 48 | IOC_READ = 2 49 | 50 | def IOC(dir, type, nr, size): 51 | """ 52 | dir 53 | One of IOC_NONE, IOC_WRITE, IOC_READ, or IOC_READ|IOC_WRITE. 54 | Direction is from the application's point of view, not kernel's. 55 | size (14-bits unsigned integer) 56 | Size of the buffer passed to ioctl's "arg" argument. 57 | """ 58 | assert dir <= _IOC_DIRMASK, dir 59 | assert type <= _IOC_TYPEMASK, type 60 | assert nr <= _IOC_NRMASK, nr 61 | assert size <= _IOC_SIZEMASK, size 62 | return (dir << _IOC_DIRSHIFT) | (type << _IOC_TYPESHIFT) | (nr << _IOC_NRSHIFT) | (size << _IOC_SIZESHIFT) 63 | 64 | def IOC_TYPECHECK(t): 65 | """ 66 | Returns the size of given type, and check its suitability for use in an 67 | ioctl command number. 68 | """ 69 | result = ctypes.sizeof(t) 70 | assert result <= _IOC_SIZEMASK, result 71 | return result 72 | 73 | def IO(type, nr): 74 | """ 75 | An ioctl with no parameters. 76 | """ 77 | return IOC(IOC_NONE, type, nr, 0) 78 | 79 | def IOR(type, nr, size): 80 | """ 81 | An ioctl with read parameters. 82 | 83 | size (ctype type or instance) 84 | Type/structure of the argument passed to ioctl's "arg" argument. 85 | """ 86 | return IOC(IOC_READ, type, nr, IOC_TYPECHECK(size)) 87 | 88 | def IOW(type, nr, size): 89 | """ 90 | An ioctl with write parameters. 91 | 92 | size (ctype type or instance) 93 | Type/structure of the argument passed to ioctl's "arg" argument. 94 | """ 95 | return IOC(IOC_WRITE, type, nr, IOC_TYPECHECK(size)) 96 | 97 | def IOWR(type, nr, size): 98 | """ 99 | An ioctl with both read an writes parameters. 100 | 101 | size (ctype type or instance) 102 | Type/structure of the argument passed to ioctl's "arg" argument. 103 | """ 104 | return IOC(IOC_READ | IOC_WRITE, type, nr, IOC_TYPECHECK(size)) 105 | 106 | def IOC_DIR(nr): 107 | """ 108 | Extract direction from an ioctl command number. 109 | """ 110 | return (nr >> _IOC_DIRSHIFT) & _IOC_DIRMASK 111 | 112 | def IOC_TYPE(nr): 113 | """ 114 | Extract type from an ioctl command number. 115 | """ 116 | return (nr >> _IOC_TYPESHIFT) & _IOC_TYPEMASK 117 | 118 | def IOC_NR(nr): 119 | """ 120 | Extract nr from an ioctl command number. 121 | """ 122 | return (nr >> _IOC_NRSHIFT) & _IOC_NRMASK 123 | 124 | def IOC_SIZE(nr): 125 | """ 126 | Extract size from an ioctl command number. 127 | """ 128 | return (nr >> _IOC_SIZESHIFT) & _IOC_SIZEMASK 129 | 130 | IOC_IN = IOC_WRITE << _IOC_DIRSHIFT 131 | IOC_OUT = IOC_READ << _IOC_DIRSHIFT 132 | IOC_INOUT = (IOC_WRITE | IOC_READ) << _IOC_DIRSHIFT 133 | IOCSIZE_MASK = _IOC_SIZEMASK << _IOC_SIZESHIFT 134 | IOCSIZE_SHIFT = _IOC_SIZESHIFT 135 | 136 | if __name__ == '__main__': 137 | print('Sanity checks...') 138 | # hid.h 139 | HID_MAX_DESCRIPTOR_SIZE = 4096 140 | 141 | # hidraw.h 142 | class hidraw_report_descriptor(ctypes.Structure): 143 | _fields_ = [ 144 | ('size', ctypes.c_uint), 145 | ('value', ctypes.c_ubyte * HID_MAX_DESCRIPTOR_SIZE), 146 | ] 147 | 148 | class hidraw_devinfo(ctypes.Structure): 149 | _fields_ = [ 150 | ('bustype', ctypes.c_uint), 151 | ('vendor', ctypes.c_short), 152 | ('product', ctypes.c_short), 153 | ] 154 | 155 | HIDIOCGRDESCSIZE = IOR(ord('H'), 0x01, ctypes.c_int) 156 | HIDIOCGRDESC = IOR(ord('H'), 0x02, hidraw_report_descriptor) 157 | HIDIOCGRAWINFO = IOR(ord('H'), 0x03, hidraw_devinfo) 158 | HIDIOCGRAWNAME = lambda len: IOC(IOC_READ, ord('H'), 0x04, len) 159 | HIDIOCGRAWPHYS = lambda len: IOC(IOC_READ, ord('H'), 0x05, len) 160 | HIDIOCSFEATURE = lambda len: IOC(IOC_WRITE|IOC_READ, ord('H'), 0x06, len) 161 | HIDIOCGFEATURE = lambda len: IOC(IOC_WRITE|IOC_READ, ord('H'), 0x07, len) 162 | HIDIOCGRAWNAME(0) 163 | HIDIOCGRAWPHYS(1) 164 | HIDIOCGRAWPHYS(_IOC_SIZEMASK) 165 | HIDIOCGFEATURE(_IOC_SIZEMASK) 166 | -------------------------------------------------------------------------------- /iodev.py: -------------------------------------------------------------------------------- 1 | # Abstract interface for objects which can handle memory accesses. "Memory" in 2 | # this context can also mean e.g. I/O ports and the scope being accessed must 3 | # be understood from the context. 4 | class MemoryHandler: 5 | # (addr: u64) → u8 / u16 / u32 / u64 6 | def read8(self, addr): 7 | raise NotImplementedError("%s: read u8(0x%x)" % (self, addr)) 8 | def read16(self, addr): 9 | raise NotImplementedError("%s: read u16(0x%x)" % (self, addr)) 10 | def read32(self, addr): 11 | raise NotImplementedError("%s: read u32(0x%x)" % (self, addr)) 12 | def read64(self, addr): 13 | raise NotImplementedError("%s: read u64(0x%x)" % (self, addr)) 14 | 15 | # (addr: u64, v: u8 / u16 / u32 / u64) → () 16 | def write8(self, addr, v): 17 | raise NotImplementedError("%s: write u8(0x%x): 0x%x" % (self, addr, v)) 18 | def write16(self, addr, v): 19 | raise NotImplementedError("%s: write u16(0x%x): 0x%x" % (self, addr, v)) 20 | def write32(self, addr, v): 21 | raise NotImplementedError("%s: write u32(0x%x): 0x%x" % (self, addr, v)) 22 | def write64(self, addr, v): 23 | raise NotImplementedError("%s: write u64(0x%x): 0x%x" % (self, addr, v)) 24 | 25 | # MemoryHandler which can dispatch to other MemoryHandlers by range. 26 | class AddressSpace(MemoryHandler): 27 | def __init__(self): 28 | self.mappings = [] 29 | 30 | # Mount a new memory handler. The handler should contain attributes .base and 31 | # .len, which should designate the range of addresses handled [base, 32 | # base+len). Also returns the handler. 33 | def mount(self, handler): 34 | self.mappings.append(handler) 35 | return handler 36 | 37 | # Determines the memory handler which handles the given address and returns it. 38 | # Throws an exception if no memory handler for the address can be found. 39 | def resolve(self, addr): 40 | for handler in self.mappings: 41 | if addr >= handler.base and addr < (handler.base + handler.len): 42 | return handler 43 | 44 | raise Exception("%s: no mapping found for address 0x%x" % (self, addr)) 45 | 46 | def read8(self, addr): 47 | return self.resolve(addr).read8(addr) 48 | def read16(self, addr): 49 | return self.resolve(addr).read16(addr) 50 | def read32(self, addr): 51 | return self.resolve(addr).read32(addr) 52 | def read64(self, addr): 53 | return self.resolve(addr).read64(addr) 54 | def write8(self, addr, v): 55 | return self.resolve(addr).write8(addr, v) 56 | def write16(self, addr, v): 57 | return self.resolve(addr).write16(addr, v) 58 | def write32(self, addr, v): 59 | return self.resolve(addr).write32(addr, v) 60 | def write64(self, addr, v): 61 | return self.resolve(addr).write64(addr, v) 62 | 63 | # 64 | def registerDevice(): 65 | def f(cls): 66 | cls.__registers__ = [] 67 | cls.__registersByOffset__ = {} 68 | for x in dir(cls): 69 | if x.startswith('_'): 70 | continue 71 | a = getattr(cls, x) 72 | if not isinstance(a, Register): 73 | continue 74 | 75 | a.key = x 76 | a.cls = cls 77 | cls.__registers__.append(a) 78 | for i in range((a.mapWidth+8)//8-1): 79 | cls.__registersByOffset__[a.offset+i] = a 80 | 81 | oldInit = None 82 | if hasattr(cls, '__init__'): 83 | oldInit = cls.__init__ 84 | 85 | def init(self, *args, **kwargs): 86 | if not hasattr(self, '__registerInitDone__'): 87 | base = kwargs.get('base') 88 | if base is not None: 89 | self.base = base 90 | 91 | rs = [] 92 | rbo = {} 93 | for r in self.__registers__: 94 | ri = r.instantiateOn(self) 95 | rs.append(ri) 96 | for i in range((r.mapWidth+8)//8-1): 97 | rbo[ri.offset+i] = ri 98 | setattr(self, r.key, ri) 99 | 100 | self.__registers__ = rs 101 | self.__registersByOffset__ = rbo 102 | self.__registerInitDone__ = True 103 | 104 | if oldInit is not None: 105 | oldInit(self, *args, **kwargs) 106 | 107 | init.isRegisterDeviceInit = True 108 | 109 | def read(self, addr, width): 110 | # A single read can read multiple actual registers (for example, a 32-bit 111 | # read of four 8-bit registers). 112 | curOffset = addr - self.base 113 | bitCount = 0 # Number of bits retrieved so far 114 | v = 0 115 | while bitCount < width: 116 | ri = self.__registersByOffset__.get(curOffset) 117 | if ri is None: 118 | if hasattr(self, 'onUnknownRead'): 119 | return self.onUnknownRead(addr, width) 120 | raise Exception("%s: unknown register read: 0x%x (+0x%x) u%s (%x)" % (self, addr, addr - self.base, width, curOffset)) 121 | 122 | relOffset = curOffset - ri.offset # Offset in bytes into this register 123 | assert relOffset >= 0 124 | rw = width 125 | while rw > ri.width - relOffset*8: 126 | rw = rw // 2 127 | 128 | assert rw >= 8 129 | vv = ri.read(relOffset, rw) 130 | v = v | (vv << bitCount) 131 | 132 | curOffset += rw//8 133 | bitCount += rw 134 | 135 | return v 136 | 137 | ri = self.__registersByOffset__.get(addr - self.base) 138 | if ri is None: 139 | raise Exception("%s: unknown register read: 0x%x (+0x%x) u%s" % (self, addr, addr-self.base, width)) 140 | 141 | if ri.width < width: 142 | pass 143 | 144 | return ri.read(addr - self.base - ri.offset, width) 145 | 146 | def write(self, addr, v, width): 147 | # A single write can write multiple actual registers (for example, a 32-bit 148 | # write of four 8-bit registers). 149 | curOffset = addr - self.base 150 | bitCount = 0 151 | vv = v 152 | oneSuccess = False 153 | while bitCount < width: 154 | ri = self.__registersByOffset__.get(curOffset) 155 | if ri is None: 156 | if hasattr(self, 'onUnknownWrite'): 157 | return self.onUnknownWrite(addr, v, width) 158 | raise Exception("%s: unknown register write: 0x%x (+0x%x) u%s = 0x%x (0x%x)" % (self, addr, addr-self.base, width, v, curOffset)) 159 | 160 | relOffset = curOffset - ri.offset # Offset into bytes into this register 161 | assert relOffset >= 0 162 | rw = width 163 | while rw > ri.width - relOffset*8: 164 | rw = rw // 2 165 | 166 | assert rw >= 8 167 | 168 | if not ri.reg.ro: 169 | ri.write(relOffset, vv & bits0(rw-1), rw) 170 | oneSuccess = True 171 | 172 | vv = vv >> rw 173 | 174 | curOffset += rw//8 175 | bitCount += rw 176 | 177 | if not oneSuccess: 178 | raise Exception("%s: all registers written were read-only: 0x%x (+0x%x) u%s = 0x%x" % (self, addr, addr-self.base, width, v)) 179 | 180 | def read8(self, addr): 181 | return read(self, addr, 8) 182 | def read16(self, addr): 183 | return read(self, addr, 16) 184 | def read32(self, addr): 185 | return read(self, addr, 32) 186 | def read64(self, addr): 187 | return read(self, addr, 64) 188 | 189 | def write8(self, addr, v): 190 | return write(self, addr, v, 8) 191 | def write16(self, addr, v): 192 | return write(self, addr, v, 16) 193 | def write32(self, addr, v): 194 | return write(self, addr, v, 32) 195 | def write64(self, addr, v): 196 | return write(self, addr, v, 64) 197 | 198 | if not hasattr(cls, '__init__') or not hasattr(cls.__init__, 'isRegisterDeviceInit'): 199 | cls.__init__ = init 200 | 201 | cls.read8 = read8 202 | cls.read16 = read16 203 | cls.read32 = read32 204 | cls.read64 = read64 205 | cls.write8 = write8 206 | cls.write16 = write16 207 | cls.write32 = write32 208 | cls.write64 = write64 209 | return cls 210 | 211 | return f 212 | 213 | class Register: 214 | def __init__(self, offset, abbr=None, *, set=None, get=None, afterSet=None, title=None, initial=0, ro=False, noOffset=False): 215 | self.offset = offset 216 | self.set = set # unbound function: (v) → () 217 | self.get = get # unbound function: () → (v) 218 | self.afterSet = afterSet # unbound function: (v) → () 219 | self.abbr = abbr 220 | self.title = title 221 | self.initial = initial 222 | self.ro = ro 223 | self.noOffset = noOffset 224 | if self.noOffset: 225 | self.mapWidth = 8 226 | else: 227 | self.mapWidth = self.width 228 | assert self.width 229 | 230 | def getter(self, f): 231 | self.get = f # unbound function 232 | return f 233 | 234 | def setter(self, f): 235 | self.set = f # unbound function 236 | return f 237 | 238 | def afterSet(self, f): 239 | self.afterSet = f # unbound function 240 | return f 241 | 242 | def instantiateOn(self, obj): 243 | return RegisterInstance(self, obj, self.initial) 244 | 245 | def __repr__(self): 246 | return f"Register({self.key}: u{self.width})" 247 | 248 | class Register8(Register): 249 | width = 8 250 | class Register16(Register): 251 | width = 16 252 | class Register32(Register): 253 | width = 32 254 | class Register64(Register): 255 | width = 64 256 | 257 | # bits 0:n 258 | def bits0(n): 259 | return (1< 0: 266 | v = v ^ bits0(n-1) 267 | return v 268 | 269 | class RegisterInstance: 270 | def __init__(self, reg, obj, value=0): 271 | self.reg = reg 272 | self.obj = obj 273 | self.value = value 274 | 275 | @property 276 | def offset(self): 277 | return self.reg.offset 278 | 279 | @property 280 | def width(self): 281 | return self.reg.width 282 | 283 | @property 284 | def mapWidth(self): 285 | return self.reg.mapWidth 286 | 287 | def read(self, offset, width): 288 | assert offset >= 0 and offset + width//8 <= self.reg.width//8 289 | 290 | if self.reg.get is not None: 291 | v = self.reg.get(self.obj) 292 | else: 293 | v = self.value 294 | 295 | return (v >> (offset*8)) & bits0(width-1) 296 | 297 | def write(self, offset, v, width): 298 | if self.reg.ro: 299 | raise Exception("cannot write read-only register: %s: +0x%x: u%s(0x%x)" % (self, offset, width, v)) 300 | 301 | rw = self.reg.width 302 | assert offset >= 0 and offset + width//8 <= rw//8 303 | 304 | if width < rw: 305 | oldv = self.read(0, rw) 306 | 307 | wmask = bits0(width-1) 308 | oshift = offset*8 309 | vv = (oldv & ~(wmask<> 8 109 | else: 110 | return self.ier.value 111 | 112 | @ier.setter 113 | def _(self, v): 114 | if self.lcr.value & 0x80: 115 | self._div = (self._div & 0xFF) | (v<<8) 116 | else: 117 | self.ier.value = v 118 | 119 | def _outputChar(self, v): 120 | self._buf.append(v) 121 | if v == 10: 122 | sys.stdout.write('COM%s: %s' % (self._n+1, re_ansiEsc.sub('', bytes(self._buf).decode('ascii', errors='ignore')))) 123 | sys.stdout.flush() 124 | self._buf = [] 125 | 126 | class PS2Device: 127 | def reset(self): 128 | pass 129 | 130 | def poll(self): 131 | return False 132 | 133 | def read(self): 134 | return None 135 | 136 | def write(self, v): 137 | pass 138 | 139 | PS2_KEYBOARD_STATE__NORMAL = 0 140 | PS2_KEYBOARD_STATE__SET_SCANCODE = 1 141 | PS2_KEYBOARD_STATE__SET_REPEAT = 2 142 | PS2_KEYBOARD_STATE__SET_LED = 3 143 | 144 | _usbToScancodeSet2 = { 145 | 0x04: (b'\x1C', b'\xF0\x1C'), # 'A' 146 | 0x05: (b'\x32', b'\xF0\x32'), # 'B' 147 | 0x06: (b'\x21', b'\xF0\x21'), # 'C' 148 | 0x07: (b'\x23', b'\xF0\x23'), # 'D' 149 | 0x08: (b'\x24', b'\xF0\x24'), # 'E' 150 | 0x09: (b'\x2B', b'\xF0\x2B'), # 'F' 151 | 0x0A: (b'\x34', b'\xF0\x34'), # 'G' 152 | 0x0B: (b'\x33', b'\xF0\x33'), # 'H' 153 | 0x0C: (b'\x43', b'\xF0\x43'), # 'I' 154 | 0x0D: (b'\x3B', b'\xF0\x3B'), # 'J' 155 | 0x0E: (b'\x42', b'\xF0\x42'), # 'K' 156 | 0x0F: (b'\x4B', b'\xF0\x4B'), # 'L' 157 | 0x10: (b'\x3A', b'\xF0\x3A'), # 'M' 158 | 0x11: (b'\x31', b'\xF0\x31'), # 'N' 159 | 0x12: (b'\x44', b'\xF0\x44'), # 'O' 160 | 0x13: (b'\x4D', b'\xF0\x4D'), # 'P' 161 | 0x14: (b'\x15', b'\xF0\x15'), # 'Q' 162 | 0x15: (b'\x2D', b'\xF0\x2D'), # 'R' 163 | 0x16: (b'\x1B', b'\xF0\x1B'), # 'S' 164 | 0x17: (b'\x2C', b'\xF0\x2C'), # 'T' 165 | 0x18: (b'\x3C', b'\xF0\x3C'), # 'U' 166 | 0x19: (b'\x2A', b'\xF0\x2A'), # 'V' 167 | 0x1A: (b'\x1D', b'\xF0\x1D'), # 'W' 168 | 0x1B: (b'\x22', b'\xF0\x22'), # 'X' 169 | 0x1C: (b'\x35', b'\xF0\x35'), # 'Y' 170 | 0x1D: (b'\x1A', b'\xF0\x1A'), # 'Z' 171 | 172 | 0x1E: (b'\x16', b'\xF0\x16'), # '1' 173 | 0x1F: (b'\x1E', b'\xF0\x1E'), # '2' 174 | 0x20: (b'\x26', b'\xF0\x26'), # '3' 175 | 0x21: (b'\x25', b'\xF0\x25'), # '4' 176 | 0x22: (b'\x2E', b'\xF0\x2E'), # '5' 177 | 0x23: (b'\x36', b'\xF0\x36'), # '6' 178 | 0x24: (b'\x3D', b'\xF0\x3D'), # '7' 179 | 0x25: (b'\x3E', b'\xF0\x3E'), # '8' 180 | 0x26: (b'\x46', b'\xF0\x46'), # '9' 181 | 0x27: (b'\x45', b'\xF0\x45'), # '0' 182 | 0x28: (b'\x5A', b'\xF0\x5A'), # '' 183 | 0x29: (b'\x76', b'\xF0\x76'), # '' 184 | 0x2A: (b'\x66', b'\xF0\x66'), # '' 185 | 0x2B: (b'\x0D', b'\xF0\x0D'), # '' 186 | 0x2C: (b'\x29', b'\xF0\x29'), # '' 187 | 0x2D: (b'\x4E', b'\xF0\x4E'), # '-' 188 | 0x2E: (b'\x55', b'\xF0\x55'), # '=' 189 | 0x2F: (b'\x54', b'\xF0\x54'), # '[' 190 | 0x30: (b'\x5B', b'\xF0\x5B'), # ']' 191 | 0x31: (b'\x5D', b'\xF0\x5D'), # '\' 192 | 0x32: (b'\x5D', b'\xF0\x5D'), # 193 | 0x33: (b'\x4C', b'\xF0\x4C'), # ';' 194 | 0x34: (b'\x52', b'\xF0\x52'), # '\'' 195 | 0x35: (b'\x0E', b'\xF0\x0E'), # '`' 196 | 0x36: (b'\x41', b'\xF0\x41'), # ',' 197 | 0x37: (b'\x49', b'\xF0\x49'), # '.' 198 | 0x38: (b'\x4a', b'\xF0\x4a'), # '/' 199 | 0x39: (b'\x58', b'\xF0\x58'), # '' 200 | 0x3a: (b'\x05', b'\xF0\x05'), # '' 201 | 0x3b: (b'\x06', b'\xF0\x06'), # '' 202 | 0x3c: (b'\x04', b'\xF0\x04'), # '' 203 | 0x3d: (b'\x0c', b'\xF0\x0c'), # '' 204 | 0x3e: (b'\x03', b'\xF0\x03'), # '' 205 | 0x3f: (b'\x0b', b'\xF0\x0b'), # '' 206 | 0x40: (b'\x83', b'\xF0\x83'), # '' 207 | 0x41: (b'\x0a', b'\xF0\x0a'), # '' 208 | 0x42: (b'\x01', b'\xF0\x01'), # '' 209 | 0x43: (b'\x09', b'\xF0\x09'), # '' 210 | 0x44: (b'\x78', b'\xF0\x78'), # '' 211 | 0x45: (b'\x07', b'\xF0\x07'), # '' 212 | 213 | 0x4f: (b'\xE0\x74', b'\xE0\xF0\x74'), # '' 214 | 0x50: (b'\xE0\x6B', b'\xE0\xF0\x6B'), # '' 215 | 0x51: (b'\xE0\x72', b'\xE0\xF0\x72'), # '' 216 | 0x52: (b'\xE0\x75', b'\xE0\xF0\x75'), # '' 217 | 218 | 0xE0: (b'\x14', b'\xF0\x14'), # '' 219 | 0xE1: (b'\x12', b'\xF0\x12'), # '' 220 | 0xE2: (b'\x11', b'\xF0\x11'), # '' 221 | 0xE3: (b'\xE0\x1F', b'\xE0\xF0\x1F'), # '' 222 | 0xE4: (b'\xE0\x14', b'\xE0\xF0\x14'), # '' 223 | 0xE5: (b'\x59', b'\xF0\x59'), # '' 224 | 0xE6: (b'\xE0\x11', b'\xE0\xF0\x11'), # '' 225 | 0xE7: (b'\xE0\x27', b'\xE0\xF0\x27'), # '' 226 | } 227 | 228 | _scancodeTranslators = (None, _usbToScancodeSet2, None) 229 | 230 | class PS2Keyboard(PS2Device): 231 | def __init__(self): 232 | self.notifyFunc = None 233 | self.reset() 234 | 235 | def reset(self, includeAck=False): 236 | self._ledState = 7 237 | self._scancodeSetNo = 1 238 | self._state = PS2_KEYBOARD_STATE__NORMAL 239 | if includeAck: 240 | self._outputBuf = [0xFA,0xAA] 241 | else: 242 | self._outputBuf = [0xAA] 243 | 244 | self._notify() 245 | 246 | def poll(self): 247 | return len(self._outputBuf) > 0 248 | 249 | def read(self): 250 | if len(self._outputBuf) > 0: 251 | v = self._outputBuf[0] 252 | self._outputBuf = self._outputBuf[1:] 253 | return v 254 | 255 | return None 256 | 257 | def write(self, v): 258 | if self._state == PS2_KEYBOARD_STATE__NORMAL: 259 | if v == 0xED: # Update LEDs 260 | self._queueOutputByte(0xFA) 261 | self._state = PS2_KEYBOARD_STATE__SET_LED 262 | elif v == 0xF2: # Read keyboard ID 263 | self._queueOutputByte(0xFA) 264 | elif v == 0xF4: # Enable scanning 265 | self._queueOutputByte(0xFA) 266 | elif v == 0xF0: # Select scancode 267 | self._queueOutputByte(0xFA) 268 | self._state = PS2_KEYBOARD_STATE__SET_SCANCODE 269 | elif v == 0xF3: # Set repeat rate and delay 270 | self._queueOutputByte(0xFA) 271 | self._state = PS2_KEYBOARD_STATE__SET_REPEAT 272 | elif v == 0xF6: # Reset keyboard, clear output buffer, switch off LEDs, reset repeat rate and delay to defaults 273 | self._ledState = 0 274 | self._queueOutputByte(0xFA) 275 | elif v == 0xFF: # Reset and self test 276 | self.reset(True) 277 | else: 278 | print('@PS2: Keyboard: unknown command: 0x%x' % v) 279 | elif self._state == PS2_KEYBOARD_STATE__SET_SCANCODE: 280 | self._selectScancodeSet(v) 281 | self._queueOutputByte(0xFA) 282 | self._state = PS2_KEYBOARD_STATE__NORMAL 283 | elif self._state == PS2_KEYBOARD_STATE__SET_REPEAT: 284 | print('@PS2: Keyboard: setting repeat 0x%x' % v) 285 | self._queueOutputByte(0xFA) 286 | self._state = PS2_KEYBOARD_STATE__NORMAL 287 | elif self._state == PS2_KEYBOARD_STATE__SET_LED: 288 | print('@PS2: Keyboard: setting LED state: 0x%x' % v) 289 | self._ledState = v 290 | self._queueOutputByte(0xFA) 291 | self._state = PS2_KEYBOARD_STATE__NORMAL 292 | else: 293 | assert False 294 | 295 | # Queues a key down event to be reported to the host, as though typed on this keyboard. 296 | # x: a USB scancode. 297 | # (x: int) → () 298 | def keyDown(self, x): 299 | sc = self._toScancode(x) 300 | if sc is None: 301 | return 302 | 303 | makeCode, breakCode = sc 304 | return self._queueCode(makeCode) 305 | 306 | # Queues a key up event to be reported to the host, as though typed on this keyboard. 307 | # x: a USB scancode. 308 | # (x: int) → () 309 | def keyUp(self, x): 310 | sc = self._toScancode(x) 311 | if sc is None: 312 | return 313 | 314 | makeCode, breakCode = sc 315 | return self._queueCode(breakCode) 316 | 317 | def _selectScancodeSet(self, scancodeSetNo): 318 | if scancodeSetNo < 1 or scancodeSetNo > 3: 319 | scancodeSetNo = 2 320 | 321 | self._scancodeSetNo = scancodeSetNo 322 | print('@PS2: Keyboard: selecting scancode set no. %s' % scancodeSetNo) 323 | 324 | def _toScancode(self, v): 325 | sct = _scancodeTranslators[self._scancodeSetNo-1] 326 | if sct is None: 327 | print('@PS2: Keyboard: scancode set %s is not supported' % (self._scancodeSetNo)) 328 | return None 329 | 330 | sc = sct.get(v) 331 | if sc is None: 332 | print('@PS2: Keyboard: warning: unmappable USB keycode: 0x%x' % v) 333 | 334 | return sc 335 | 336 | def _queueCode(self, code): 337 | for b in code: 338 | self._outputBuf.append(b) 339 | 340 | self._notify() 341 | 342 | def _queueOutputByte(self, b): 343 | self._outputBuf.append(b) 344 | self._notify() 345 | 346 | def _notify(self): 347 | if self.notifyFunc: 348 | self.notifyFunc() 349 | 350 | PS2_CTL_STATE__NORMAL = 0 351 | PS2_CTL_STATE__CFG_RAM_WRITE = 2 352 | 353 | _ps2TranslationTable = [ 354 | 0xFF, 0x43, 0x41, 0x3F, 0x3D, 0x3B, 0x3C, 0x58, 0x64, 0x44, 0x42, 0x40, 0x3E, 0x0F, 0x29, 0x59, 355 | 0x65, 0x38, 0x2A, 0x70, 0x1D, 0x10, 0x02, 0x5A, 0x66, 0x71, 0x2C, 0x1F, 0x1E, 0x11, 0x03, 0x5B, 356 | 0x67, 0x2E, 0x2D, 0x20, 0x12, 0x05, 0x04, 0x5C, 0x68, 0x39, 0x2F, 0x21, 0x14, 0x13, 0x06, 0x5D, 357 | 0x69, 0x31, 0x30, 0x23, 0x22, 0x15, 0x07, 0x5e, 0x6a, 0x72, 0x32, 0x24, 0x16, 0x08, 0x09, 0x5f, 358 | 0x6b, 0x33, 0x25, 0x17, 0x18, 0x0b, 0x0a, 0x60, 0x6c, 0x34, 0x35, 0x26, 0x27, 0x19, 0x0c, 0x61, 359 | 0x6d, 0x73, 0x28, 0x74, 0x1a, 0x0d, 0x62, 0x6e, 0x3a, 0x36, 0x1c, 0x1b, 0x75, 0x2b, 0x63, 0x76, 360 | 0x55, 0x56, 0x77, 0x78, 0x79, 0x7a, 0x0e, 0x7b, 0x7c, 0x4f, 0x7d, 0x4b, 0x47, 0x7e, 0x7f, 0x6f, 361 | 0x52, 0x53, 0x50, 0x4c, 0x4d, 0x48, 0x01, 0x45, 0x57, 0x4e, 0x51, 0x4a, 0x37, 0x49, 0x46, 0x54, 362 | 0x80, 0x81, 0x82, 0x41, 0x54, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 363 | 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 364 | 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 365 | 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 366 | 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 367 | 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 368 | 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xee, 0xee, 0xef, 369 | None, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xff, 0xfe, 0xff, 370 | ] 371 | 372 | @registerDevice() 373 | class PS2Io(MemoryHandler): 374 | base = 0x60 375 | len = 8 376 | 377 | r60 = Register8(0) 378 | r61 = Register8(1) 379 | r64 = Register8(4) 380 | 381 | def __init__(self, vm, sysResetFunc=None): 382 | self._vm = vm 383 | self._inputBuf = [] 384 | self._ctlCfgRam = [0x00]*32 385 | self._ctlState = PS2_CTL_STATE__NORMAL 386 | self._ctlCfgRamOffset = 0 387 | self._device = PS2Keyboard() 388 | self._device.notifyFunc = self._onNotify 389 | self._lastIntrStatus = False 390 | self.sysResetFunc = sysResetFunc 391 | 392 | @property 393 | def keyboard(self): 394 | return self._device 395 | 396 | # Data Register 397 | @r60.getter 398 | def _(self): 399 | if self._ctlState == PS2_CTL_STATE__NORMAL: 400 | if len(self._inputBuf) > 0: 401 | v = self._inputBuf[0] 402 | self._inputBuf = self._inputBuf[1:] 403 | #print('@PS2: Data get cmd response 0x%x' % v) 404 | self._updateIntr() 405 | return v 406 | 407 | v = self._device.read() 408 | self._updateIntr() 409 | if v is None: 410 | print('@PS2: No data') 411 | return 0 412 | 413 | oldv = v 414 | v = self._translate(v) 415 | if v is None: 416 | v = self._translate(self._device.read()) 417 | self._updateIntr() 418 | assert v is not None 419 | v = v | 0x80 420 | 421 | #print('@PS2: Data get device input 0x%x' % v) 422 | return v 423 | else: 424 | print('@PS2: Warning: unexpected state when reading from data port') 425 | self._ctlState = PS2_CTL_STATE__NORMAL 426 | return 0 427 | 428 | @r60.setter 429 | def _(self, v): 430 | if self._ctlState == PS2_CTL_STATE__NORMAL: 431 | #print('@PS2: To keyboard: 0x%x' % v) 432 | self._device.write(v) 433 | elif self._ctlState == PS2_CTL_STATE__CFG_RAM_WRITE: 434 | #print('@PS2: ram set [0x%x] = 0x%x' % (self._ctlCfgRamOffset, v)) 435 | self._ctlCfgRam[self._ctlCfgRamOffset] = v 436 | self._updateIntr() 437 | self._ctlState = PS2_CTL_STATE__NORMAL 438 | else: 439 | print('@PS2: Warning: unexpected state when writing to data port: 0x%x' % v) 440 | self._ctlState = PS2_CTL_STATE__NORMAL 441 | 442 | # Status Register 443 | @r64.getter 444 | def _(self): 445 | flags = 0 446 | if len(self._inputBuf) != 0 or self._device.poll(): 447 | flags = flags | (1<<0) # At least one byte in Keyboard to Host buffer 448 | if self._ctlCfgRam[0] & (1<<2): 449 | flags = flags | (1<<2) # System Flag 450 | if self._ctlState == PS2_CTL_STATE__CFG_RAM_WRITE: 451 | flags = flags | (1<<3) # Command? 452 | #flags = flags | (1<<1) # Host to Keyboard buffer full 453 | return flags 454 | 455 | # Command Register 456 | @r64.setter 457 | def _(self, v): 458 | if v >= 0x20 and v <= 0x3F: 459 | offset = v & 0x1F 460 | self._inputBuf.append(self._ctlCfgRam[offset]) 461 | elif v >= 0x60 and v <= 0x7F: 462 | offset = v & 0x1F 463 | self._ctlState = PS2_CTL_STATE__CFG_RAM_WRITE 464 | self._ctlCfgRamOffset = offset 465 | elif v == 0xAD: # Disable PS/2 port A (keyboard) 466 | self._ctlCfgRam[0] |= (1<<4) 467 | elif v == 0xAE: # Enable PS/2 port A (keyboard) 468 | self._ctlCfgRam[0] &= ~(1<<4) 469 | elif v == 0xA7: # Disable PS/2 port B (mouse) 470 | self._ctlCfgRam[0] |= (1<<5) 471 | elif v == 0xA8: # Enable PS/2 port B (mouse) 472 | self._ctlCfgRam[0] &= ~(1<<5) 473 | elif v == 0xAA: # Test PS/2 Controller 474 | self._inputBuf.append(0x55) 475 | elif v == 0xAB: # Test PS/2 port A (keyboard) 476 | self._inputBuf.append(0x00) 477 | elif v == 0xA9: # Test PS/2 port B (mouse) 478 | self._inputBuf.append(0x06) 479 | elif v & 0xF0 == 0xF0: 480 | # Pulse output lines low for 6ms 481 | doReset = False 482 | for i in range(4): 483 | if (v & (1< 0 or self._device.poll()) 520 | #if self._lastIntrStatus != newIntrStatus: 521 | self._lastIntrStatus = newIntrStatus 522 | self._vm.setIrqLine(1, False) 523 | if newIntrStatus: 524 | self._vm.setIrqLine(1, newIntrStatus) 525 | -------------------------------------------------------------------------------- /iodev_pci.py: -------------------------------------------------------------------------------- 1 | from iodev import * 2 | 3 | # A PCI bus-device-function value. This is a 16-bit integer of a format 4 | # commonly used by PCI-related code and is formatted as follows: 5 | # 6 | # bbbb bbbb dddd dfff 7 | # 8 | # where b is the bus number, d is the device number and f is the function 9 | # number. 10 | # 11 | # The most common presentation format for this is as follows: 12 | # 13 | # BB:DD.F 14 | # 15 | # where BB, DD and F are hexadecimal representations of the bus, device and 16 | # function numbers, respectively. 17 | # 18 | # To instantiate this class, pass a (bus, device, function) tuple or an integer 19 | # containing the encoded BDF. 20 | class BDF: 21 | __slots__ = ('_bdf',) 22 | 23 | def __new__(cls, bdf): 24 | if isinstance(bdf, BDF): 25 | return bdf 26 | 27 | return object.__new__(cls) 28 | 29 | def __init__(self, bdf): 30 | if type(bdf) == tuple: 31 | busNo, devNo, funcNo = bdf 32 | if busNo > 0xFF or devNo > 0x1F or funcNo > 0x7: 33 | raise Exception("bus, device or function number does not have a valid range: 0x%x, 0x%x, 0x%x" % (busNo, devNo, funcNo)) 34 | 35 | self._bdf = (busNo<<8) | (devNo<<3) | funcNo 36 | 37 | elif type(bdf) == int: 38 | if bdf < 0 or bdf > 0xFFFF: 39 | raise Exception("not a valid BDF value: 0x%x" % bdf) 40 | 41 | self._bdf = bdf 42 | 43 | else: 44 | raise Exception("BDF argument must be a tuple or integer: %s" % (bdf,)) 45 | 46 | @property 47 | def int(self): 48 | return self._bdf 49 | 50 | @property 51 | def tuple(self): 52 | funcNo = self._bdf & 0x07 53 | devNo = (self._bdf >> 3) & 0x1F 54 | busNo = (self._bdf >> 8) & 0xFF 55 | return (busNo, devNo, funcNo) 56 | 57 | @property 58 | def busNo(self): 59 | return self.tuple[0] 60 | 61 | @property 62 | def devNo(self): 63 | return self.tuple[1] 64 | 65 | @property 66 | def funcNo(self): 67 | return self.tuple[2] 68 | 69 | def __repr__(self): 70 | return "%02x:%02x.%x" % self.tuple 71 | 72 | # An entire PCI subsystem (PCI domain), which contains a set of PCI functions 73 | # identified by their BDF. 74 | class PciSubsystem: 75 | def __init__(self): 76 | self.bdfs = {} 77 | 78 | def insert(self, dev, bdf=None): 79 | if bdf is None: 80 | bdf = dev.bdf 81 | 82 | assert bdf is not None 83 | bdf = BDF(bdf) 84 | self.bdfs[bdf.int] = dev 85 | dev.pciSubsystem = self 86 | return dev 87 | 88 | def getConfig(self, bdf): 89 | dev = self.bdfs.get(bdf) 90 | if dev is None: 91 | return None 92 | return dev.config 93 | 94 | def cfgRead(self, bdf, reg): 95 | dev = self.bdfs.get(bdf) 96 | if dev is None: 97 | print("Warning: access to nonexistent PCI device %s (reg 0x%x)" % (BDF(bdf), reg)) 98 | return 0xFFFFFFFF 99 | return dev.cfgRead(reg) 100 | 101 | def cfgWrite(self, bdf, reg, v): 102 | dev = self.bdfs.get(bdf) 103 | if dev is None: 104 | return 105 | #raise Exception("access to nonexistent PCI device %s (reg 0x%x)" % (self.formatBdf(bdf), reg)) 106 | dev.cfgWrite(reg, v) 107 | 108 | # A PCI function which can be made part of a PCI subsystem. 109 | class PciFunctionBase: 110 | # Read a 32-bit PCI configuration register. The argument is the register 111 | # offset within the configuration space for the given BDF. Returns the result. 112 | # 113 | # (reg: u12) → (v: u32) 114 | def cfgRead(self, reg): 115 | raise NotImplementedError("%s does not support reading configuration register 0x%x" % reg) 116 | 117 | # Writes a 32-bit PCI configuration register. The argument is the register 118 | # offset within the configuration space for the given BDF. 119 | def cfgWrite(self, reg, v): 120 | raise NotImplementedError("%s does not support writing configuration register 0x%x (0x%x)" % (reg, v)) 121 | 122 | @registerDevice() 123 | class PciConfig: 124 | base = 0 125 | len = 4096 126 | 127 | def __init__(self, device): 128 | self.device = device 129 | 130 | vendorID = Register16(0x00, get=lambda self: self.device.vendorID, ro=True) 131 | deviceID = Register16(0x02, get=lambda self: self.device.deviceID, ro=True) 132 | command = Register16(0x04) 133 | status = Register16(0x06, ro=True) 134 | revision = Register8 (0x08, get=lambda self: self.device.rev, ro=True) 135 | progIf = Register8 (0x09, get=lambda self: self.device.progIf, ro=True) 136 | subclass = Register8 (0x0A, get=lambda self: self.device.subClass, ro=True) 137 | classCode = Register8 (0x0B, get=lambda self: self.device.classCode, ro=True) 138 | cacheLineSize = Register8 (0x0C) 139 | latencyTimer = Register8 (0x0D, ro=True) 140 | headerType = Register8 (0x0E, ro=True) 141 | bist = Register8 (0x0F, ro=True) 142 | bar0 = Register32(0x10, set=lambda self, v: self._setBar(0, v)) 143 | bar1 = Register32(0x14, set=lambda self, v: self._setBar(1, v)) 144 | bar2 = Register32(0x18, set=lambda self, v: self._setBar(2, v)) 145 | bar3 = Register32(0x1C, set=lambda self, v: self._setBar(3, v)) 146 | bar4 = Register32(0x20, set=lambda self, v: self._setBar(4, v)) 147 | bar5 = Register32(0x24, set=lambda self, v: self._setBar(5, v)) 148 | cardbusCisPtr = Register32(0x28, ro=True) 149 | subsystemVendorID = Register16(0x2C, ro=True, get=lambda self: self.device.subsystemVendorID) 150 | subsystemID = Register16(0x2E, ro=True, get=lambda self: self.device.subsystemID) 151 | expansionRomBaseAddr = Register32(0x30) 152 | capPtr = Register8 (0x34, ro=True) 153 | rsvd35 = Register8 (0x35, ro=True) 154 | rsvd36 = Register16(0x36, ro=True) 155 | rsvd38 = Register32(0x38, ro=True) 156 | intrLine = Register8 (0x3C) 157 | intrPin = Register8 (0x3D, ro=True) 158 | minGrant = Register8 (0x3E, ro=True) 159 | maxLatency = Register8 (0x3F, ro=True) 160 | 161 | def __repr__(self): 162 | return f"PciConfig(for device {repr(self.device)})" 163 | 164 | def _setBar(self, barNo, v): 165 | bars = self.device.bars 166 | if barNo >= len(bars) or bars[barNo] is None: 167 | return 168 | 169 | L, kind = bars[barNo] 170 | if kind == 'io': 171 | vv = (v & 0xFFFF_FFFC) 172 | vv2 = vv | 1 173 | elif kind == 'm32': 174 | vv = (v & 0xFFFF_FFF0) 175 | vv = vv & ~(L-1) 176 | vv2 = vv 177 | else: 178 | raise NotImplementedError() 179 | 180 | getattr(self, 'bar%s' % barNo).value = vv2 181 | self.device.cfgBarChanged(barNo, vv) 182 | 183 | class PciBar(MemoryHandler): 184 | base = 0xFFFFFFFF 185 | 186 | class PciRamBar(PciBar): 187 | def __init__(self, device): 188 | self._device = device 189 | self._slot = None 190 | self._lock = None 191 | 192 | def onBarUpdate(self): 193 | if self._lock: 194 | self._lock.acquire() 195 | 196 | if self._slot: 197 | self._slot.teardown() 198 | self._slot = self._device._memoryManager.mapNew(self.base, self.len) 199 | 200 | if self._lock: 201 | self._lock.release() 202 | 203 | # A PCI function with a Type 0 header which implements basic functionality for 204 | # standard configuration registers. 205 | class PciFunction(PciFunctionBase): 206 | configClass = PciConfig 207 | bars = (None, None, None, None, None, None) 208 | barHandlers = (None, None, None, None, None, None) 209 | subsystemID = 0 210 | subsystemVendorID = 0 211 | 212 | def __init__(self): 213 | self.config = self.configClass(self) 214 | 215 | def cfgRead(self, reg): 216 | return self.config.read32(reg) 217 | 218 | def cfgWrite(self, reg, v): 219 | return self.config.write32(reg, v) 220 | 221 | def cfgBarChanged(self, barNo, v): 222 | if self.barHandlers[barNo]: 223 | self.barHandlers[barNo].base = v 224 | if hasattr(self.barHandlers[barNo], 'onBarUpdate'): 225 | self.barHandlers[barNo].onBarUpdate() 226 | 227 | def addBarM32(self, barNo, handler): 228 | if type(self.bars) == tuple: 229 | self.bars = list(self.bars) 230 | self.barHandlers = list(self.barHandlers) 231 | 232 | self.bars[barNo] = (handler.len, 'm32') 233 | self.barHandlers[barNo] = handler 234 | return handler 235 | 236 | # An I/O port handler which implements the 0xCF8 and 0xCFC I/O ports for access 237 | # to PCI configuration space. Must be given a PCI subsystem to work with. 238 | @registerDevice() 239 | class PciIoCfgDev(MemoryHandler): 240 | base = 0xCF8 241 | len = 8 242 | 243 | cf8 = Register32(0, title='Address Port') 244 | cfc = Register32(4, title='Data Port') 245 | 246 | def __init__(self, pciSubsystem): 247 | self.pciSubsystem = pciSubsystem 248 | 249 | @cfc.getter 250 | def _(self): 251 | cf8 = self.cf8.value & 0x7FFF_FFFF 252 | bdf = cf8 >> 8 253 | reg = cf8 & 0xFF 254 | return self.pciSubsystem.cfgRead(bdf, reg) 255 | 256 | @cfc.setter 257 | def _(self, v): 258 | cf8 = self.cf8.value & 0x7FFF_FFFF 259 | bdf = cf8 >> 8 260 | reg = cf8 & 0xFF 261 | return self.pciSubsystem.cfgWrite(bdf, reg, v) 262 | 263 | # A memory handler which implements the PCIe memory-mapped expanded 264 | # configuration space. 265 | class PciMmioCfgDev(MemoryHandler): 266 | base = 0xB000_0000 267 | len = 0x1000_0000 268 | 269 | def __init__(self, pciSubsystem): 270 | self.pciSubsystem = pciSubsystem 271 | 272 | def split(self, addr): 273 | bdf = (addr>>12) & 0xFFFF 274 | reg = addr & 0xFFF 275 | config = self.pciSubsystem.getConfig(bdf) 276 | return config, reg 277 | 278 | def read32(self, addr): 279 | config, reg = self.split(addr) 280 | if config is None: 281 | return 0xFFFF_FFFF 282 | 283 | return config.read32(reg) 284 | 285 | def write32(self, addr, v): 286 | config, reg = self.split(addr) 287 | if config is None: 288 | return 289 | 290 | return config.write32(reg, v) 291 | 292 | def read16(self, addr): 293 | config, reg = self.split(addr) 294 | if config is None: 295 | return 0xFFFF 296 | 297 | return config.read16(reg) 298 | 299 | def write16(self, addr, v): 300 | config, reg = self.split(addr) 301 | if config is None: 302 | return 303 | 304 | return config.write16(reg, v) 305 | 306 | def read8(self, addr): 307 | config, reg = self.split(addr) 308 | if config is None: 309 | return 0xFF 310 | 311 | return config.read8(reg) 312 | 313 | def write8(self, addr, v): 314 | config, reg = self.split(addr) 315 | if config is None: 316 | return 317 | 318 | return config.write8(reg, v) 319 | -------------------------------------------------------------------------------- /iodev_qemu.py: -------------------------------------------------------------------------------- 1 | import sys, struct 2 | from iodev import * 3 | from iodev_pci import * 4 | from iodev_pc import * 5 | from iodev_acpi import * 6 | from iodev_tpm import * 7 | from iodev_virtio import * 8 | from memmgr import * 9 | from scsi import * 10 | import sdl2 11 | 12 | @registerDevice() 13 | class QemuDebugOutputDev(MemoryHandler): 14 | base = 0x402 15 | len = 1 16 | 17 | r402 = Register8(0, initial=0xE9) 18 | 19 | def __init__(self): 20 | self._dbgStr = [] 21 | 22 | @r402.setter 23 | def _(self, v): 24 | self._dbgStr.append(v & 0xFF) 25 | if v == 10: # NL 26 | sys.stdout.write('DBG: %s' % bytes(self._dbgStr).decode('utf-8')) 27 | sys.stdout.flush() 28 | self._dbgStr = [] 29 | 30 | @registerDevice() 31 | class QemuFwCfg(MemoryHandler): 32 | base = 0x510 33 | len = 2 34 | 35 | sel = Register16(0, noOffset=True, afterSet=lambda self, v: self._changeBuffer(v)) 36 | data = Register8 (1, ro=True, get=lambda self: self._readBuffer()) 37 | 38 | def _changeBuffer(self, v): 39 | print('Changing to buffer 0x%x' % v) 40 | self._buf = self._genBuffer(v) 41 | 42 | def _genBuffer(self, sel): 43 | return b'' 44 | 45 | def _readBuffer(self): 46 | if len(self._buf) == 0: 47 | return 0 48 | 49 | v = self._buf[0] 50 | self._buf = self._buf[1:] 51 | return v 52 | 53 | @registerDevice() 54 | class Q35PciIch9Config(PciConfig): 55 | pciexbarLo = Register32(0x60) 56 | pciexbarHi = Register32(0x64) 57 | pam1 = Register8 (0x91) 58 | 59 | class Q35PciIch9(PciFunction): 60 | configClass = Q35PciIch9Config 61 | bdf = (0,0,0) 62 | vendorID = 0x8086 63 | deviceID = 0x29c0 # INTEL_Q35_MCH_DEVICE_ID 64 | classCode = 0 65 | subClass = 0 66 | progIf = 0 67 | rev = 0 68 | 69 | @registerDevice() 70 | class Q35PciD31F0Config(PciConfig): 71 | pmbase = Register32(0x40, afterSet=lambda self, v: print('PMBASE=0x%x' % v)) 72 | acpiCntl = Register32(0x44, afterSet=lambda self, v: print('ACPICTL=0x%x' % v)) 73 | rcr1 = Register32(0x60) 74 | rcr2 = Register32(0x68) 75 | rcba = Register32(0xF0) 76 | 77 | class Q35PciD31F0(PciFunction): 78 | configClass = Q35PciD31F0Config 79 | bdf = (0,31,0) 80 | vendorID = 0x8086 81 | deviceID = 0x7000 82 | classCode = 0x06 # Bridge: ISA Bridge 83 | subClass = 0x01 # 84 | progIf = 0x00 # 85 | rev = 0x55 86 | 87 | @registerDevice() 88 | class QxlRegs(MemoryHandler): 89 | base = 0 90 | len = 0xFFFF 91 | 92 | id = Register16(0x00, initial=0xB0C0, ro=True, noOffset=True) 93 | xRes = Register16(0x01, noOffset=True, afterSet=lambda self,v : self.onModeChange()) 94 | yRes = Register16(0x02, noOffset=True, afterSet=lambda self, v: self.onModeChange()) 95 | bpp = Register16(0x03, noOffset=True, afterSet=lambda self, v: self.onModeChange()) 96 | enable = Register16(0x04, noOffset=True, afterSet=lambda self, v: self.onModeChange()) 97 | bank = Register16(0x05, noOffset=True, afterSet=lambda self, v: self.onModeChange()) 98 | virtWidth = Register16(0x06, noOffset=True, afterSet=lambda self, v: self.onModeChange()) 99 | virtHeight= Register16(0x07, noOffset=True, afterSet=lambda self, v: self.onModeChange()) 100 | xOffset = Register16(0x08, noOffset=True, afterSet=lambda self, v: self.onModeChange()) 101 | yOffset = Register16(0x09, noOffset=True, afterSet=lambda self, v: self.onModeChange()) 102 | 103 | def __init__(self, qxl): 104 | self._qxl = qxl 105 | 106 | def onModeChange(self): 107 | e = sdl2.SDL_Event() 108 | e.type = getSdlSyncEventNo() 109 | print('@ModeChange:Event 0x%x' % e.type) 110 | sdl2.SDL_PushEvent(e) 111 | 112 | @registerDevice() 113 | class QxlIo(MemoryHandler): 114 | base = 0x1CE 115 | len = 4 116 | 117 | addr = Register16(0) 118 | data = Register16(2) 119 | 120 | def __init__(self, qxl): 121 | self._qxl = qxl 122 | 123 | @data.getter 124 | def _(self): 125 | return self._qxl._regs.read16(self.addr.value) 126 | 127 | @data.setter 128 | def _(self, v): 129 | return self._qxl._regs.write16(self.addr.value, v) 130 | 131 | @registerDevice() 132 | class QxlVgaIo(MemoryHandler): 133 | base = 0x3C0 134 | len = 17 135 | 136 | def __init__(self, qxl): 137 | self._qxl = qxl 138 | 139 | attAddr = Register8(0) 140 | paletteIdx = Register8(0x8) 141 | paletteData = Register8(0x9) 142 | 143 | class QxlBar0(PciRamBar): 144 | len = 16*1024*1024 145 | 146 | @registerDevice() 147 | class QxlBar2(MemoryHandler): 148 | base = 0xFFFFFFFF 149 | len = 8*1024 150 | 151 | def __init__(self, device): 152 | self._device = device 153 | 154 | magic = Register32(0x00, ro=True, initial=0x4f52_5851) 155 | drawStart = Register32(0x24, ro=True) 156 | availableFBSize = Register32(0x28, ro=True, initial=(16*1024*1024)) 157 | 158 | def getSdlSyncEventNo(): 159 | v = None 160 | def f(): 161 | nonlocal v 162 | if v is None: 163 | v = sdl2.SDL_RegisterEvents(1) 164 | return v 165 | return f 166 | 167 | getSdlSyncEventNo = getSdlSyncEventNo() 168 | 169 | class Qxl(PciFunction): 170 | bdf = (0,1,0) 171 | vendorID = 0x1B36 172 | deviceID = 0x0100 173 | classCode = 0x03 # Display 174 | subClass = 0x00 # VGA-compatible 175 | progIf = 0x00 176 | rev = 0 177 | 178 | def __init__(self, memoryManager): 179 | super().__init__() 180 | self._memoryManager = memoryManager 181 | self._regs = QxlRegs(self) 182 | self.io = QxlIo(self) 183 | self.vgaIo = QxlVgaIo(self) 184 | self.b0h = self.addBarM32(0, QxlBar0(self)) 185 | self.b2h = self.addBarM32(2, QxlBar2(self)) 186 | self.keyEventHandler = None 187 | 188 | self._startVisualizer() 189 | 190 | def teardown(self): 191 | self._visRun = False 192 | self._visExitLock.acquire() 193 | 194 | def _startVisualizer(self): 195 | import threading, sdl2, ctypes 196 | 197 | self._visRun = True 198 | 199 | lock = threading.Lock() 200 | self.b0h._lock = lock 201 | 202 | exitLock = threading.Lock() 203 | exitLock.acquire() 204 | self._visExitLock = exitLock 205 | 206 | def f(): 207 | sdl2.SDL_Init(sdl2.SDL_INIT_VIDEO) 208 | 209 | w = sdl2.SDL_CreateWindow(b"Framebuffer", sdl2.SDL_WINDOWPOS_UNDEFINED, sdl2.SDL_WINDOWPOS_UNDEFINED, 800, 600, sdl2.SDL_WINDOW_SHOWN) 210 | s = None 211 | curUserspaceAddr = None 212 | 213 | def onModeChange(): 214 | nonlocal s, curUserspaceAddr 215 | if s is not None: 216 | sdl2.SDL_FreeSurface(s) 217 | s = None 218 | 219 | if self.b0h._slot is None or self._regs.virtWidth.value == 0 or self._regs.virtHeight.value == 0: 220 | print('@Mode Change: None (%s, %s, %s)' % (self.b0h._slot, self._regs.virtWidth.value, self._regs.virtHeight.value)) 221 | sdl2.SDL_SetWindowTitle(w, b'Framebuffer') 222 | curUserspaceAddr = None 223 | return 224 | 225 | print('@Mode Change: w=%s, h=%s, bpp=%s' % (self._regs.virtWidth.value, self._regs.virtHeight.value, self._regs.bpp.value)) 226 | 227 | sdl2.SDL_SetWindowSize(w, self._regs.virtWidth.value, self._regs.virtHeight.value) 228 | sdl2.SDL_SetWindowTitle(w, ("Framebuffer (%sx%sx%s)" % (self._regs.virtWidth.value, self._regs.virtHeight.value, self._regs.bpp.value)).encode('utf-8')) 229 | curUserspaceAddr = self.b0h._slot.userspaceAddr 230 | s = sdl2.SDL_CreateRGBSurfaceFrom(curUserspaceAddr, 231 | self._regs.virtWidth.value, 232 | self._regs.virtHeight.value, 233 | self._regs.bpp.value, 234 | (self._regs.virtWidth.value * self._regs.bpp.value) // 8, 235 | 0x00FF_0000, 0x0000_FF00, 0x0000_00FF, 0x0000_0000) 236 | 237 | lock.acquire() 238 | onModeChange() 239 | lock.release() 240 | 241 | e = sdl2.SDL_Event() 242 | def handleEvent(): 243 | if e.type == getSdlSyncEventNo(): 244 | lock.acquire() 245 | onModeChange() 246 | lock.release() 247 | elif e.type == sdl2.SDL_KEYDOWN or e.type == sdl2.SDL_KEYUP: 248 | if self.keyEventHandler: 249 | self.keyEventHandler(e) 250 | 251 | while self._visRun: 252 | if sdl2.SDL_WaitEventTimeout(ctypes.byref(e), 16) != 0: 253 | handleEvent() 254 | while sdl2.SDL_PollEvent(ctypes.byref(e)) != 0: 255 | handleEvent() 256 | 257 | lock.acquire() 258 | if (curUserspaceAddr and self.b0h._slot is None) or (self.b0h._slot and curUserspaceAddr != self.b0h._slot.userspaceAddr): 259 | onModeChange() 260 | 261 | dsts = sdl2.SDL_GetWindowSurface(w) 262 | if s is not None and dsts is not None: 263 | sdl2.SDL_BlitSurface(s, None, dsts, None) 264 | 265 | lock.release() 266 | sdl2.SDL_UpdateWindowSurface(w) 267 | 268 | if s: 269 | sdl2.SDL_FreeSurface(s) 270 | s = None 271 | 272 | sdl2.SDL_DestroyWindow(w) 273 | sdl2.SDL_Quit() 274 | exitLock.release() 275 | 276 | self._visThread = threading.Thread(target=f, name='QxlVis') 277 | self._visThread.daemon = True 278 | self._visThread.start() 279 | 280 | class Q35PciSubsystem(PciSubsystem): 281 | def __init__(self, memoryManager, vm, scsiSubsystem): 282 | super().__init__() 283 | self.ich9 = self.insert(Q35PciIch9()) 284 | self.ich9d31f0 = self.insert(Q35PciD31F0()) 285 | self.qxl = self.insert(Qxl(memoryManager)) 286 | self.vioScsi = self.insert(VirtioScsi(memoryManager, scsiSubsystem)) 287 | self._vm = vm 288 | 289 | class Q35IOAddressSpace(AddressSpace): 290 | def __init__(self, platform, pciSubsystem, vm): 291 | super().__init__() 292 | 293 | self.qemuDebugOut = self.mount(QemuDebugOutputDev()) 294 | self.qemuFwCfg = self.mount(QemuFwCfg()) 295 | self.pciCfgAccess = self.mount(PciIoCfgDev(pciSubsystem)) 296 | self.rtc = self.mount(Rtc()) 297 | self.port92 = self.mount(Port92()) 298 | self.pm = self.mount(Q35PmIo()) 299 | self.com1 = self.mount(SerialIo(0)) 300 | self.com2 = self.mount(SerialIo(1)) 301 | self.com3 = self.mount(SerialIo(2)) 302 | self.com4 = self.mount(SerialIo(3)) 303 | self.ps2 = self.mount(PS2Io(vm, sysResetFunc=platform.sysReset)) 304 | self.qxl = self.mount(pciSubsystem.qxl.io) 305 | self.vga = self.mount(pciSubsystem.qxl.vgaIo) 306 | self.port80 = self.mount(Port80()) 307 | 308 | class Ram(MemoryHandler): 309 | base = 0 310 | len = 0 311 | 312 | def __init__(self, memoryManager, firmwarePath): 313 | self._memoryManager = memoryManager 314 | 315 | data = open(firmwarePath, 'rb').read() 316 | dataShortLen = min(len(data), 128*1024) 317 | assert len(data) <= 4*1024*1024 318 | assert (len(data) % 4096) == 0 319 | 320 | self._slot = memoryManager.mapNew(0, 1*1024*1024*1024) 321 | self._fwSlot = memoryManager.mapNew(4*1024*1024*1024 - len(data), len(data), ro=True) 322 | 323 | self._memoryManager.write(0x10_0000 - dataShortLen, data[-dataShortLen:]) 324 | self._memoryManager.write(4*1024*1024*1024 - len(data), data) 325 | 326 | SYS_FLASH_STATE__NORMAL = 0 327 | SYS_FLASH_STATE__READ_STATUS_REG = 1 328 | SYS_FLASH_STATE__SINGLE_BYTE_PROGRAM = 2 329 | 330 | class SysFlash(MemoryHandler): 331 | base = 0 332 | len = 0 333 | 334 | def __init__(self, memoryManager, firmwareVarsPath): 335 | self._f = open(firmwareVarsPath, 'r+b') 336 | self._data = bytearray(self._f.read()) 337 | self.base = 0xFFC0_0000 338 | self.len = len(self._data) 339 | self._state = SYS_FLASH_STATE__NORMAL 340 | self._wstate = 0 341 | self._status = 0 342 | 343 | def read8(self, addr): 344 | addr -= self.base 345 | print('@Flash read u8 0x%x' % addr) 346 | if self._state == SYS_FLASH_STATE__NORMAL: 347 | return self._data[addr] 348 | elif self._state == SYS_FLASH_STATE__READ_STATUS_REG: 349 | return self._status & 0xFF 350 | else: 351 | assert False 352 | 353 | def read16(self, addr): 354 | addr -= self.base 355 | print('@Flash read u16 0x%x' % addr) 356 | if self._state == SYS_FLASH_STATE__NORMAL: 357 | return struct.unpack('= len(self._queueLens): 157 | return 0 158 | 159 | return self._queueLens[queueNo] 160 | 161 | @comQueueLen.setter 162 | def _(self, v): 163 | queueNo = self.comQueueSel.value 164 | if queueNo >= len(self._queueLens): 165 | return 166 | 167 | self._queueLens[queueNo] = min(v, self._maxQueueLens[queueNo]) 168 | 169 | @comQueueEnable.getter 170 | def _(self): 171 | queueNo = self.comQueueSel.value 172 | if queueNo >= len(self._queueEnables): 173 | return 0 174 | 175 | return int(self._queueEnables[queueNo]) 176 | 177 | @comQueueEnable.setter 178 | def _(self, v): 179 | queueNo = self.comQueueSel.value 180 | if queueNo >= len(self._queueEnables): 181 | return 182 | 183 | self._queueEnables[queueNo] = bool(v) 184 | 185 | @comQueueDesc.getter 186 | def _(self): 187 | queueNo = self.comQueueSel.value 188 | if queueNo >= len(self._queueEnables): 189 | return 190 | 191 | return self._queueDescriptorAreas[queueNo] 192 | 193 | @comQueueDesc.setter 194 | def _(self, v): 195 | queueNo = self.comQueueSel.value 196 | if queueNo >= len(self._queueEnables): 197 | return 198 | 199 | self._setQueueDescriptorArea(queueNo, v) 200 | 201 | @comQueueDrv.getter 202 | def _(self): 203 | queueNo = self.comQueueSel.value 204 | if queueNo >= len(self._queueEnables): 205 | return 206 | 207 | return self._queueDriverAreas[queueNo] 208 | 209 | @comQueueDrv.setter 210 | def _(self, v): 211 | queueNo = self.comQueueSel.value 212 | if queueNo >= len(self._queueEnables): 213 | return 214 | 215 | self._setQueueDriverArea(queueNo, v) 216 | 217 | @comQueueDev.getter 218 | def _(self): 219 | queueNo = self.comQueueSel.value 220 | if queueNo >= len(self._queueEnables): 221 | return 222 | 223 | return self._queueDeviceAreas[queueNo] 224 | 225 | @comQueueDev.setter 226 | def _(self, v): 227 | queueNo = self.comQueueSel.value 228 | if queueNo >= len(self._queueEnables): 229 | return 230 | 231 | self._setQueueDeviceArea(queueNo, v) 232 | 233 | @isrStatus.getter 234 | def _(self): 235 | v = self.isrStatus.value 236 | self.isrStatus.value = 0 237 | self._updateIntr() 238 | return v 239 | 240 | def _assertQueueIntr(self): 241 | self.isrStatus.value = self.isrStatus.value | (1<<0) 242 | self._updateIntr() 243 | 244 | def _updateIntr(self): 245 | self._device.pciSubsystem._vm.setIrqLine(self._device.config.intrLine.value, bool(self.isrStatus.value)) 246 | 247 | def _onNotify(self, queueIdx): 248 | if queueIdx >= len(self._queueLens): 249 | return 250 | 251 | self._syncProcessAvail(queueIdx) 252 | 253 | def _setQueueDescriptorArea(self, queueNo, v): 254 | self._queueDescriptorAreas[queueNo] = v 255 | 256 | def _setQueueDriverArea(self, queueNo, v): 257 | self._queueDriverAreas[queueNo] = v 258 | 259 | def _setQueueDeviceArea(self, queueNo, v): 260 | self._queueDeviceAreas[queueNo] = v 261 | 262 | def _syncProcessAvail(self, queueNo): 263 | queueLen = self._queueLens[queueNo] 264 | pAvailRing = self._queueDriverAreas[queueNo] 265 | 266 | avails = self._device._memoryManager.read(pAvailRing, 2+2+2*queueLen+2) 267 | if avails is None: 268 | print('@Virtio: cannot get buffer for avail ring 0x%x' % pAvailRing) 269 | return 270 | 271 | availFlags, availIdx = struct.unpack('= queueLen: 291 | print('@Virtio: invalid descriptor index 0x%x' % headDescIdx) 292 | return 293 | 294 | descBuf = self._device._memoryManager.read(pDescriptors + 16*(curDescIdx%queueLen), 16) 295 | if descBuf is None: 296 | print('@Virtio: cannot get buffer for desc 0x%x' % headDescIdx) 297 | return 298 | 299 | dAddr, dLen, dFlags, dNext = struct.unpack('Q', req[0:8])[0] 346 | id, taskAttr, priority, crn = struct.unpack('= dataInBuf.len: 375 | break 376 | d = dataInBuf.read(min(8192, dataInBuf.len - L)) 377 | if d == b'': 378 | break 379 | wbuf.write(d) 380 | L += len(d) 381 | #print('@Virtio: writing %s bytes to data-in: %s (rem 0x%x)' % (len(d),d, wbuf.remaining)) 382 | except Exception as e: 383 | print('@Virtio: SCSI exception: %s' % e) 384 | wbuf.write(struct.pack('= 0) 18 | 19 | def getSupportedCpuid(self): 20 | n = 128 21 | while True: 22 | try: 23 | info = kvmapi.makeKvmCpuid2(n)() 24 | info.nent = n 25 | fcntl.ioctl(self._fd, kvmapi.KVM_GET_SUPPORTED_CPUID, info) 26 | return info.entries[0:info.nent] 27 | except OSError as e: 28 | if e.errno == errno.E2BIG: 29 | n = 2*n 30 | continue 31 | else: 32 | raise 33 | 34 | def getMsrs(self, msrNumList): 35 | msrs = kvmapi.makeKvmMsrs(len(msrNumList))() 36 | msrs.nmsrs = len(msrNumList) 37 | for i, x in enumerate(msrNumList): 38 | msrs.entries[i].index = x 39 | msrs.entries[i].data = 0x55555555 40 | 41 | L = fcntl.ioctl(self._fd, kvmapi.KVM_GET_MSRS, msrs) 42 | return msrs.entries[0:L] 43 | 44 | def _getMsrIndexList(self, op): 45 | L = 1 46 | while True: 47 | msrList = kvmapi.makeKvmMsrList(L)() 48 | msrList.nmsrs = L 49 | try: 50 | fcntl.ioctl(self._fd, op, msrList) 51 | return msrList.indices[0:msrList.nmsrs] 52 | except OSError as e: 53 | if e.errno == errno.E2BIG: 54 | L = msrList.nmsrs 55 | continue 56 | else: 57 | raise 58 | 59 | def getMsrIndexList(self): 60 | return self._getMsrIndexList(kvmapi.KVM_GET_MSR_INDEX_LIST) 61 | 62 | def getMsrFeatureIndexList(self): 63 | return self._getMsrIndexList(kvmapi.KVM_GET_MSR_FEATURE_INDEX_LIST) 64 | 65 | @property 66 | def fd(self): 67 | return self._fd 68 | 69 | class VM: 70 | def __init__(self, kvm): 71 | assert isinstance(kvm, Kvm) 72 | 73 | self._kvm = kvm 74 | self._fd = fcntl.ioctl(self._kvm._fd, kvmapi.KVM_CREATE_VM, 0) 75 | 76 | def createVcpu(self, *args, **kwargs): 77 | return Vcpu(self, *args, **kwargs) 78 | 79 | def setUserMemoryRegion(self, rgn): 80 | assert isinstance(rgn, kvmapi.KvmUserSpaceMemoryRegion) 81 | fcntl.ioctl(self._fd, kvmapi.KVM_SET_USER_MEMORY_REGION, rgn) 82 | 83 | def setTssAddr(self, addr): 84 | addr = struct.unpack('i', struct.pack('I', addr))[0] 85 | fcntl.ioctl(self._fd, kvmapi.KVM_SET_TSS_ADDR, addr) 86 | 87 | def createPit2(self, cfg): 88 | assert isinstance(cfg, kvmapi.KvmPitConfig) 89 | fcntl.ioctl(self._fd, kvmapi.KVM_CREATE_PIT2, cfg) 90 | 91 | def createIrqChip(self): 92 | fcntl.ioctl(self._fd, kvmapi.KVM_CREATE_IRQCHIP) 93 | 94 | def setIrqLine(self, irq, level): 95 | args = kvmapi.KvmIrqLevel(irq, int(level)) 96 | fcntl.ioctl(self._fd, kvmapi.KVM_IRQ_LINE, args) 97 | 98 | @property 99 | def kvm(self): 100 | return self._kvm 101 | 102 | @property 103 | def fd(self): 104 | return self._fd 105 | 106 | class Vcpu: 107 | def __init__(self, vm, cpuNum=0): 108 | assert isinstance(vm, VM) 109 | self._vm = vm 110 | self._fd = fcntl.ioctl(self._vm._fd, kvmapi.KVM_CREATE_VCPU, cpuNum) 111 | self._runBase = kvmapi.mmap(-1, self._vm._kvm._mapLen, mmap.PROT_READ | mmap.PROT_WRITE, mmap.MAP_SHARED, self._fd, 0) 112 | self._run = kvmapi.KvmRun.from_address(self._runBase) 113 | self._runBuf = (ctypes.c_uint8 * self._vm._kvm._mapLen).from_address(self._runBase) 114 | #self._runBase = mmap.mmap(self._fd, self._vm._kvm._mapLen) 115 | #self._run = kvmapi.KvmRun.from_buffer(self._runBase) 116 | 117 | def teardown(self): 118 | self._run = None 119 | self._runBuf = None 120 | kvmapi.munmap(self._runBase, self._vm._kvm._mapLen) 121 | #self._runBase.close() 122 | os.close(self._fd) 123 | 124 | @property 125 | def regs(self): 126 | regs = kvmapi.KvmRegs() 127 | fcntl.ioctl(self._fd, kvmapi.KVM_GET_REGS, regs) 128 | return regs 129 | 130 | @regs.setter 131 | def regs(self, regs): 132 | assert isinstance(regs, kvmapi.KvmRegs) 133 | fcntl.ioctl(self._fd, kvmapi.KVM_SET_REGS, regs) 134 | 135 | @property 136 | def sregs(self): 137 | sregs = kvmapi.KvmSregs() 138 | fcntl.ioctl(self._fd, kvmapi.KVM_GET_SREGS, sregs) 139 | return sregs 140 | 141 | @sregs.setter 142 | def sregs(self, sregs): 143 | assert isinstance(sregs, kvmapi.KvmSregs) 144 | fcntl.ioctl(self._fd, kvmapi.KVM_SET_SREGS, sregs) 145 | 146 | @property 147 | def fpu(self): 148 | fpu = kvmapi.KvmFpu() 149 | fcntl.ioctl(self._fd, kvmapi.KVM_GET_FPU, fpu) 150 | return fpu 151 | 152 | @fpu.setter 153 | def fpu(self, fpu): 154 | assert isinstance(fpu, kvmapi.KvmFpu) 155 | fcntl.ioctl(self._fd, kvmapi.KVM_SET_FPU, fpu) 156 | 157 | @property 158 | def lapic(self): 159 | lapic = kvmapi.LocalApic() 160 | fcntl.ioctl(self._fd, kvmapi.KVM_GET_LAPIC, lapic) 161 | return lapic 162 | 163 | @lapic.setter 164 | def lapic(self, lapic): 165 | assert isinstance(lapic, kvmapi.LocalApic) 166 | fcntl.ioctl(self._fd, kvmapi.KVM_SET_LAPIC, lapic) 167 | 168 | def setCpuid2(self, cpuid): 169 | if isinstance(cpuid, list): 170 | cpuid2 = kvmapi.makeKvmCpuid2(len(cpuid))() 171 | cpuid2.nent = len(cpuid) 172 | for i in range(len(cpuid)): 173 | cpuid2.entries[i] = cpuid[i] 174 | cpuid = cpuid2 175 | 176 | assert isinstance(cpuid.entries[0], kvmapi.KvmCpuidEntry2) 177 | fcntl.ioctl(self._fd, kvmapi.KVM_SET_CPUID2, cpuid) 178 | 179 | def getMsrs(self, msrNumList): 180 | msrs = kvmapi.makeKvmMsrs(len(msrNumList))() 181 | msrs.nmsrs = len(msrNumList) 182 | for i, x in enumerate(msrNumList): 183 | msrs.entries[i].index = x 184 | msrs.entries[i].data = 0x55555555 185 | 186 | L = fcntl.ioctl(self._fd, kvmapi.KVM_GET_MSRS, msrs) 187 | return msrs.entries[0:L] 188 | 189 | def setMsrs(self, msrs): 190 | if isinstance(msrs, list): 191 | msrs2 = kvmapi.makeKvmMsrs(len(msrs))() 192 | msrs2.nmsrs = len(msrs) 193 | for i in range(len(msrs)): 194 | msrs2.entries[i] = msrs[i] 195 | msrs = msrs2 196 | 197 | assert isinstance(msrs.entries[0], kvmapi.KvmMsrEntry) 198 | fcntl.ioctl(self._fd, kvmapi.KVM_SET_MSRS, msrs) 199 | 200 | def setDebug(self, debugs): 201 | assert isinstance(debugs, kvmapi.KvmGuestDebug) 202 | fcntl.ioctl(self._fd, kvmapi.KVM_SET_GUEST_DEBUG, debugs) 203 | 204 | def runOnce(self): 205 | fcntl.ioctl(self._fd, kvmapi.KVM_RUN, 0) 206 | 207 | @property 208 | def reason(self): 209 | return kvmapi.KvmExitReason(self._run.exitReason) 210 | 211 | @property 212 | def runData(self): 213 | return self._run 214 | 215 | @property 216 | def runBase(self): 217 | return self._runBase 218 | 219 | @property 220 | def runBuf(self): 221 | return self._runBuf 222 | 223 | @property 224 | def vm(self): 225 | return self._vm 226 | 227 | @property 228 | def fd(self): 229 | return self._fd 230 | -------------------------------------------------------------------------------- /memmgr.py: -------------------------------------------------------------------------------- 1 | import kvmapi, mmap, ctypes 2 | 3 | MAP_NORESERVE = 0x4000 4 | 5 | def copyToRam(base, data): 6 | buf = (ctypes.c_ubyte*len(data)).from_address(base) 7 | for i, x in enumerate(data): 8 | buf[i] = x 9 | 10 | class MemoryExtent: 11 | def __init__(self, base, len): 12 | self.base = base 13 | self.len = len 14 | 15 | def __repr__(self): 16 | return "MemoryExtent[0x%x,0x%x)" % (self.base, self.base+self.len) 17 | 18 | def __len__(self): 19 | return self.len 20 | 21 | def __getitem__(self, idx): 22 | if isinstance(idx, slice): 23 | newBase = self.base 24 | if idx.start is not None: 25 | assert idx.start >= 0 26 | newBase += idx.start 27 | 28 | newLen = self.len 29 | newLen = min(newLen, self.len - (newBase - self.base)) 30 | if idx.stop is not None: 31 | if idx.stop < 0: 32 | newLen -= -idx.stop 33 | else: 34 | newLen = idx.stop - (idx.start or 0) 35 | 36 | newLen = min(newLen, self.len - (newBase - self.base)) 37 | newLen = max(newLen, 0) 38 | return MemoryExtent(newBase, newLen) 39 | else: 40 | raise NotImplementedError() 41 | 42 | def copyFrom(self, buf=None): 43 | if buf is None: 44 | assert self.len < 16*1024*1024 45 | buf = bytearray(self.len) 46 | 47 | rbuf = (ctypes.c_ubyte*self.len).from_address(self.base) 48 | for i in range(min(len(buf), self.len)): 49 | buf[i] = rbuf[i] 50 | 51 | return buf 52 | 53 | def copyTo(self, srcBuf): 54 | dstBuf = (ctypes.c_ubyte*self.len).from_address(self.base) 55 | for i, x in enumerate(srcBuf): 56 | dstBuf[i] = x 57 | 58 | class MemorySlot: 59 | def __init__(self, mgr, slotNo, guestPhysAddr, userspaceAddr, len, ro): 60 | self._mgr = mgr 61 | self.slotNo = slotNo 62 | self.guestPhysAddr = guestPhysAddr 63 | self.userspaceAddr = userspaceAddr 64 | self.len = len 65 | self.ro = ro 66 | self._wasAllocated = False 67 | self._destroyed = False 68 | 69 | def teardown(self): 70 | if self._destroyed: 71 | return 72 | 73 | oldLen = self.len 74 | self.len = 0 75 | self.update() 76 | 77 | if self._wasAllocated: 78 | print('@MemMgr: UNMAP (0x%x, 0x%x)' % (self.userspaceAddr, oldLen)) 79 | kvmapi.munmap(self.userspaceAddr, oldLen) 80 | 81 | del self._mgr._slots[self.slotNo] 82 | self._mgr._freeSlots.add(self.slotNo) 83 | self._destroyed = True 84 | 85 | def update(self): 86 | assert not self._destroyed 87 | 88 | flags = 0 89 | if self.ro: 90 | flags = kvmapi.KVM_MEM_READONLY 91 | 92 | try: 93 | self._mgr._vmm.vm.setUserMemoryRegion(kvmapi.KvmUserSpaceMemoryRegion(self.slotNo, flags, self.guestPhysAddr, self.len, self.userspaceAddr)) 94 | print('@MemMgr: MAPPED (0x%x, 0x%x)' % (self.userspaceAddr, self.len)) 95 | except Exception as e: 96 | print('Warning: failed to update memory region: %s' % e) 97 | 98 | def toExtent(self): 99 | return MemoryExtent(self.userspaceAddr, self.len) 100 | 101 | class MemoryManager: 102 | def __init__(self, vmm): 103 | self._vmm = vmm 104 | self._nextSlotNo = 0 105 | self._freeSlots = set() 106 | self._slots = {} 107 | 108 | def mapExisting(self, guestPhysAddr, userspaceAddr, len, ro=False): 109 | slotNo = self._allocateSlotNo() 110 | slot = MemorySlot(self, slotNo, guestPhysAddr, userspaceAddr, len, ro) 111 | slot.update() 112 | self._slots[slotNo] = slot 113 | return slot 114 | 115 | def mapNew(self, guestPhysAddr, len, ro=False): 116 | p = kvmapi.mmap(-1, len, mmap.PROT_READ | mmap.PROT_WRITE, mmap.MAP_ANON | mmap.MAP_PRIVATE | MAP_NORESERVE, -1, 0) 117 | if p == 0xFFFFFFFF_FFFFFFFF: 118 | raise Exception("failed to map RAM") 119 | 120 | slot = self.mapExisting(guestPhysAddr, p, len, ro) 121 | slot._wasAllocated = True 122 | return slot 123 | 124 | def clear(self): 125 | for s in list(self._slots.values()): 126 | s.teardown() 127 | 128 | assert len(self._slots) == 0 129 | self._nextSlotNo = 0 130 | self._freeSlots = set() 131 | 132 | def resolveSlot(self, guestPhysAddr): 133 | for slot in self._slots.values(): 134 | if guestPhysAddr >= slot.guestPhysAddr and guestPhysAddr < slot.guestPhysAddr + slot.len: 135 | return slot 136 | 137 | return None 138 | 139 | def resolveExtent(self, guestPhysAddr): 140 | slot = self.resolveSlot(guestPhysAddr) 141 | if slot is None: 142 | return None 143 | 144 | ex = slot.toExtent()[guestPhysAddr - slot.guestPhysAddr:] 145 | assert ex.base >= slot.userspaceAddr 146 | assert ex.base + ex.len <= slot.userspaceAddr + slot.len 147 | return ex 148 | 149 | def resolveExtents(self, guestPhysAddr, len): 150 | bufs = [] 151 | while len: 152 | extent = self.resolveExtent(guestPhysAddr) 153 | if extent is None: 154 | return None 155 | 156 | L = min(len, extent.len) 157 | bufs.append(extent[0:L]) 158 | len -= L 159 | guestPhysAddr += L 160 | 161 | return bufs 162 | 163 | def read(self, guestPhysAddr, bufLen): 164 | extents = self.resolveExtents(guestPhysAddr, bufLen) 165 | if extents is None: 166 | return None 167 | 168 | b = b'' 169 | while len(b) < bufLen: 170 | b += MultiReadBuffer(extents).read(bufLen - len(b)) 171 | 172 | return b 173 | 174 | def write(self, guestPhysAddr, buf): 175 | extents = self.resolveExtents(guestPhysAddr, len(buf)) 176 | if extents is None: 177 | return None 178 | 179 | return MultiWriteBuffer(extents).write(buf) 180 | 181 | def _allocateSlotNo(self): 182 | if len(self._freeSlots): 183 | return self._freeSlots.pop() 184 | slotNo = self._nextSlotNo 185 | self._nextSlotNo += 1 186 | return slotNo 187 | 188 | class MultiReadBuffer: 189 | # (bufs: [MemoryExtent...]) 190 | def __init__(self, extents): 191 | self._extents = extents 192 | 193 | @property 194 | def remaining(self): 195 | L = 0 196 | for extent in self._extents: 197 | L += extent.len 198 | return L 199 | 200 | def read(self, n): 201 | if len(self._extents) == 0: 202 | return b'' 203 | 204 | extent = self._extents[0] 205 | n = min(n, extent.len) 206 | b = bytes(extent[:n].copyFrom()) 207 | if extent.len - n == 0: 208 | self._extents = self._extents[1:] 209 | else: 210 | self._extents[0] = self._extents[0][n:] 211 | 212 | return b 213 | 214 | class MultiWriteBuffer: 215 | # (bufs: [MemoryExtent...]) 216 | def __init__(self, extents): 217 | self._extents = extents 218 | 219 | @property 220 | def remaining(self): 221 | L = 0 222 | for extent in self._extents: 223 | L += extent.len 224 | return L 225 | 226 | def write(self, b): 227 | c = 0 228 | while len(b): 229 | if len(self._extents) == 0: 230 | return c 231 | 232 | extent = self._extents[0] 233 | n = min(len(b), extent.len) 234 | extent.copyTo(b[:n]) 235 | c += n 236 | b = b[n:] 237 | 238 | if extent.len - n == 0: 239 | self._extents = self._extents[1:] 240 | else: 241 | self._extents[0] = self._extents[0][n:] 242 | 243 | return c 244 | -------------------------------------------------------------------------------- /scsi.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | SCSI_TASK_ATTR__SIMPLE = 0 4 | SCSI_TASK_ATTR__ORDERED = 1 5 | SCSI_TASK_ATTR__HEAD_OF_QUEUE = 2 6 | SCSI_TASK_ATTR__ACA = 3 7 | 8 | SCSI_STATUS__GOOD = 0x00 9 | SCSI_STATUS__CHECK_CONDITION = 0x02 10 | SCSI_STATUS__CONDITION_MET = 0x04 11 | SCSI_STATUS__BUSY = 0x08 12 | SCSI_STATUS__RESERVATION_CONFLICT = 0x18 13 | SCSI_STATUS__TASK_SET_FULL = 0x28 14 | SCSI_STATUS__ACA_ACTIVE = 0x30 15 | SCSI_STATUS__TASK_ABORTED = 0x40 16 | 17 | SCSI_TMF_RES__FUNCTION_COMPLETE = 0 18 | SCSI_TMF_RES__FUNCTION_SUCCEEDED = 1 19 | SCSI_TMF_RES__FUNCTION_REJECTED = 2 20 | SCSI_TMF_RES__INCORRECT_LUN = 3 21 | 22 | SCSI_SENSE_KEY__NO_SENSE = 0x0 23 | SCSI_SENSE_KEY__HW_ERROR = 0x4 24 | SCSI_SENSE_KEY__ILLEGAL_REQ = 0x5 25 | 26 | class ScsiException(Exception): 27 | pass 28 | 29 | class ScsiServiceDeliveryOrTargetFailureException(ScsiException): 30 | pass 31 | 32 | # Represents the arguments to the SCSI Execute Command procedure call. 33 | class ScsiCmd: 34 | # dataOutBuf, if given, is a buffer object supporting seeks and reads. 35 | # It must also have a special attribute .len indicating the total 36 | # amount which can be read from it in bytes. 37 | # 38 | # dataInBuf, if given, is a buffer object supporting seeks and writes. 39 | # It must also have a special attribute .len indicating the total 40 | # amount which can be written to it in bytes. 41 | # 42 | # (lun: u64, id: u64, cdb: bytes, taskAttr: TASk_ATTR__*, crn: u8?, 43 | # priority: u4?, dataOutBuf: buffer?, dataInBuf: buffer?) → ScsiCmd 44 | def __init__(self, *, lun, id, cdb, taskAttr=SCSI_TASK_ATTR__SIMPLE, crn=None, priority=0, dataOutBuf=None, dataInBuf=None): 45 | assert len(cdb) >= 6 46 | assert taskAttr in (SCSI_TASK_ATTR__SIMPLE, SCSI_TASK_ATTR__ORDERED, 47 | SCSI_TASK_ATTR__HEAD_OF_QUEUE, SCSI_TASK_ATTR__ACA) 48 | assert priority >= 0 and priority <= 0xF 49 | self.lun = lun 50 | self.id = id 51 | self.cdb = cdb 52 | print('@Virtio: new cmd, cdb=%s' % self.cdb) 53 | self.taskAttr = taskAttr 54 | self.crn = crn 55 | self.priority = priority 56 | self.dataOutBuf = dataOutBuf 57 | self.dataInBuf = dataInBuf 58 | 59 | def __repr__(self): 60 | return f"ScsiCmd(lun=0x%x, id=0x%x, cdbOpcode=0x%02x)" % (self.lun, self.id, self.cdb[0]) 61 | 62 | # Represents a category of SCSI sense response which can be used to generate 63 | # SCSI sense data. 64 | class ScsiSenseTemplate: 65 | def __init__(self, key, asc, ascq): 66 | self.key = key 67 | self.asc = asc 68 | self.ascq = ascq 69 | 70 | def make(self): 71 | return struct.pack('!BBBIBIBBBBBB', 0x70, 0, 72 | self.key & 0xF, 73 | 0, # info 74 | 0, # additionalLen 75 | 0, # csInfo 76 | self.asc, # ASC 77 | self.ascq, # ASCQ 78 | 0, # FRU Code 79 | 0, # Sense Key Specific 0 80 | 0, # Sense Key Specific 1 81 | 0) # Sense Key Specific 2 82 | 83 | SCSI_ST__INVALID_COMMAND_OPERATION_CODE = ScsiSenseTemplate(SCSI_SENSE_KEY__ILLEGAL_REQ, 0x20, 0x00) 84 | SCSI_ST__LBA_OUT_OF_RANGE = ScsiSenseTemplate(SCSI_SENSE_KEY__ILLEGAL_REQ, 0x21, 0x00) 85 | SCSI_ST__INVALID_FIELD_IN_CDB = ScsiSenseTemplate(SCSI_SENSE_KEY__ILLEGAL_REQ, 0x24, 0x00) 86 | SCSI_ST__LOGICAL_UNIT_NOT_SUPPORTED = ScsiSenseTemplate(SCSI_SENSE_KEY__ILLEGAL_REQ, 0x25, 0x00) 87 | SCSI_ST__LOGICAL_UNIT_FAILURE = ScsiSenseTemplate(SCSI_SENSE_KEY__HW_ERROR, 0x3E, 0x01) 88 | SCSI_ST__INTERNAL_TARGET_FAILURE = ScsiSenseTemplate(SCSI_SENSE_KEY__HW_ERROR, 0x44, 0x00) 89 | SCSI_ST__NONE = ScsiSenseTemplate(SCSI_SENSE_KEY__NO_SENSE, 0x00, 0x00) 90 | 91 | # Represents the results of a successful SCSI Execute Command procedure call. 92 | class ScsiResult: 93 | # (senseData: bytes, status: u8, statusQualifier: u16?) → ScsiResult 94 | def __init__(self, *, senseData, status, statusQualifier=None): 95 | self.senseData = senseData 96 | self.status = status 97 | self.statusQualifier = statusQualifier 98 | 99 | def __repr__(self): 100 | return f"ScsiResult(status=0x%x/0x%x)" % (self.status, self.statusQualifier) 101 | 102 | @classmethod 103 | def good(cls): 104 | return ScsiResult(senseData=None, status=SCSI_STATUS__GOOD) 105 | 106 | @classmethod 107 | def checkCondition(cls, senseData, **kwargs): 108 | if isinstance(senseData, ScsiSenseTemplate): 109 | senseData = senseData.make(**kwargs) 110 | elif not isinstance(senseData, bytes): 111 | raise Exception("sense data must be a ScsiSenseTemplate or bytes object: %s" % senseData) 112 | 113 | return ScsiResult(senseData=senseData, status=SCSI_STATUS__CHECK_CONDITION) 114 | 115 | # Abstract representation of an SCSI Service Delivery Subsystem as defined by 116 | # SAM-4. 117 | class IScsiSubsystem: 118 | # Abstract definition of the Execute Command procedure call as defined in SAM-4. 119 | # The arguments defined in SAM-4 are encapsulated in a ScsiCmd object. 120 | # The I_T_L_Q nexus is represented a (LUN, Command ID) tuple. 121 | # 122 | # Either a ScsiResult is returned (corresponding to a service response of 123 | # Command Complete) or a ServiceDeliveryOrTargetFailureException is thrown. 124 | # 125 | # (req: ScsiCmd) → ScsiResult | throw ScsiServiceDeliveryOrTargetFailureException 126 | def executeCommand(self, req): 127 | raise NotImplementedError() 128 | 129 | # The following functions implement the different task management function 130 | # procedure calls as defined by SAM-4. The nexus is given by the lun or (lun, 131 | # id) arguments. 132 | # 133 | # The service responses of Function Complete, Function Succeeded, Function Rejected 134 | # and Incorrect Logical Unit Number are represented by returning TMF_RES__*. 135 | # A ScsiServiceDeliveryOrTargetFailureException corresponds to a service 136 | # response of Service Delivery or Target Failure. 137 | # 138 | # (lun: u64, id: u64) → () 139 | def abortTask(self, lun, id): 140 | raise NotImplementedError() 141 | 142 | def abortTaskSet(self, lun): 143 | raise NotImplementedError() 144 | 145 | def clearAca(self, lun): 146 | raise NotImplementedError() 147 | 148 | def clearTaskSet(self, lun): 149 | raise NotImplementedError() 150 | 151 | def itNexusReset(self): 152 | raise NotImplementedError() 153 | 154 | def luReset(self, lun): 155 | raise NotImplementedError() 156 | 157 | def queryTask(self, lun, id): 158 | raise NotImplementedError() 159 | 160 | def queryTaskSet(self, lun): 161 | raise NotImplementedError() 162 | 163 | def queryAsyncEvent(self, lun): 164 | raise NotImplementedError() 165 | 166 | # The following functions correspond to the event notification SCSI transport 167 | # protocol services given in SAM-4. 168 | 169 | # () → () 170 | def nexusLoss(self): 171 | pass 172 | 173 | # () → () 174 | def transportReset(self): 175 | pass 176 | 177 | # () → () 178 | def powerLossExpected(self): 179 | pass 180 | 181 | class ScsiDevice(IScsiSubsystem): 182 | def __init__(self, subsystem): 183 | self.subsystem = subsystem 184 | 185 | def executeCommand(self, req): 186 | r = self._executeCommand(req) 187 | self._lastSenseData = r.senseData 188 | return r 189 | 190 | def _executeCommand(self, req): 191 | opcode = req.cdb[0] 192 | if opcode == 0x00: # TEST UNIT READY 193 | return self._handleTEST_UNIT_READY(req) 194 | elif opcode == 0x03: # REQUEST SENSE 195 | return self._handleREQUEST_SENSE(req) 196 | elif opcode == 0x12: # INQUIRY 197 | print('@Virtio: as inquiry') 198 | return self._handleINQUIRY(req) 199 | else: 200 | print('@Virtio: as unhandled 0x%x' % req.cdb[0]) 201 | return ScsiResult.checkCondition(SCSI_ST__INVALID_COMMAND_OPERATION_CODE) 202 | 203 | @property 204 | def peripheralDeviceType(self): 205 | raise NotImplementedError("must set periperalDeviceType") 206 | 207 | @property 208 | def t10VendorID(self): 209 | raise NotImplementedError("must set T10 vendor ID to a bytestring not exceeding eight bytes") 210 | 211 | @property 212 | def t10VendorSubID(self): 213 | raise NotImplementedError("must set T10 vendor ID to a bytestring") 214 | 215 | @property 216 | def eui64(self): 217 | raise NotImplementedError("must set eui64 to an EUI-64 unique ID") 218 | 219 | version = 0x04 # SPC-2 220 | 221 | @property 222 | def vendorID(self): 223 | raise NotImplementedError("must set vendorID to bytestring not exceeding eight bytes") 224 | 225 | @property 226 | def productID(self): 227 | raise NotImplementedError("must set productID to a bytestring not exceeding sixteen bytes") 228 | 229 | @property 230 | def productRev(self): 231 | raise NotImplementedError("must set productRev to a bytestring not exceeding four bytes") 232 | 233 | @property 234 | def versionDescriptors(self): 235 | raise NotImplementedError("must set versionDescriptors to a tuple of shorts") 236 | 237 | def _handleTEST_UNIT_READY(self, req): 238 | return ScsiResult.good() 239 | 240 | def _handleREQUEST_SENSE(self, req): 241 | maxLen = req.cdb[4] 242 | useDesc = req.cdb[1] & 1 243 | if useDesc: 244 | return ScsiResult.checkCondition(SCSI_ST__INVALID_FIELD_IN_CDB) 245 | 246 | if self._lastSenseData is None: 247 | req.dataInBuf.write(SCSI_ST__NONE.make()[:maxLen]) 248 | else: 249 | req.dataInBuf.write(self._lastSenseData[:maxLen]) 250 | 251 | return ScsiResult.good() 252 | 253 | def _handleINQUIRY(self, req): 254 | cmdDt = req.cdb[1] & 2 255 | evpd = req.cdb[1] & 1 256 | page = req.cdb[2] 257 | allocLen = req.cdb[4] 258 | control = req.cdb[5] 259 | 260 | periQual = 0 261 | periDeviceType = self.peripheralDeviceType 262 | 263 | if evpd: 264 | print('@Virtio: evpd req') 265 | if cmdDt: 266 | return ScsiResult.checkCondition(SCSI_ST__INVALID_FIELD_IN_CDB) 267 | if page == 0x00: # Supported VPD Pages 268 | pageList = [0x00, 0x83] 269 | pageData = struct.pack('!BBBB', periDeviceType | (periQual<<5), 0x00, 0, len(pageList)) + bytes(pageList) 270 | req.dataInBuf.write(pageData) 271 | return ScsiResult.good() 272 | elif page == 0x83: # Device Identification 273 | t10VendorID = self.t10VendorID.ljust(8, b' ') 274 | if len(t10VendorID) > 8: 275 | raise Exception("T10 vendor ID must not exceed 8 characters: %r" % t10VendorID) 276 | 277 | t10VendorSubID = self.t10VendorSubID 278 | ident = t10VendorID + t10VendorSubID 279 | ident2 = self.eui64 280 | pageBody = struct.pack('!BBBB', 2, 1, 0, len(ident)) + ident 281 | pageBody += struct.pack('!BBBB', 1, 2, 0, len(ident2)) + ident2 282 | pageData = struct.pack('!BBBB', periDeviceType | (periQual<<5), 0x83, 0, len(pageBody)) + pageBody 283 | req.dataInBuf.write(pageData) 284 | return ScsiResult.good() 285 | else: 286 | return ScsiResult.checkCondition(SCSI_ST__INVALID_FIELD_IN_CDB) 287 | 288 | if cmdDt or page: 289 | print('@Virtio: bad page') 290 | return ScsiResult.checkCondition(SCSI_ST__INVALID_FIELD_IN_CDB) 291 | 292 | version = self.version 293 | 294 | vendorID = self.vendorID.ljust(8, b' ') 295 | if len(vendorID) > 8: 296 | raise Exception("vendor ID must not exceed 8 characters: %r", vendorID) 297 | 298 | productID = self.productID.ljust(16, b' ') 299 | if len(productID) > 16: 300 | raise Exception("product ID must not exceed 16 characters: %r", productID) 301 | 302 | productRev = self.productRev.ljust(4, b' ') 303 | if len(productRev) > 8: 304 | raise Exception("product revision must not exceed 4 characters: %r", productRev) 305 | 306 | additionalLen = 0 307 | 308 | versionDescriptors = list(self.versionDescriptors) 309 | if len(versionDescriptors) > 8: 310 | raise Exception("cannot have more than eight version descriptors") 311 | while len(versionDescriptors) < 8: 312 | versionDescriptors.append(0) 313 | 314 | inquiryData = struct.pack('!8B8s16s4s20sBB8H22s', 315 | periDeviceType | (periQual << 5), 316 | 0, 317 | version, 318 | 2, 319 | additionalLen, 320 | 0, 321 | 0, 322 | 0, 323 | vendorID, 324 | productID, 325 | productRev, 326 | b'\0'*20, 327 | 0, 328 | 0, 329 | versionDescriptors[0], versionDescriptors[1], versionDescriptors[2], versionDescriptors[3], 330 | versionDescriptors[4], versionDescriptors[5], versionDescriptors[6], versionDescriptors[7], 331 | b'\0'*22) 332 | 333 | if len(inquiryData) > req.dataInBuf.len: 334 | inquiryData = inquiryData[0:req.dataInBuf.len] 335 | 336 | print('@Virtio: inquiry handled normally') 337 | req.dataInBuf.write(inquiryData) 338 | return ScsiResult.good() 339 | 340 | class ScsiBlockDeviceBase(ScsiDevice): 341 | peripheralDeviceType= 0x00 # SBC 342 | t10VendorID = b'DEVEVER' 343 | t10VendorSubID = b'BLKDEV' 344 | eui64 = b'\x11\x22\x33\x44\x11\x22\x33\x44' 345 | vendorID = b'DEVEVER' 346 | productID = b'BLKDEV' 347 | productRev = b'0' 348 | versionDescriptors = (0x0080, 0x0600) # SAM-4, SBC-4 349 | blockSize = 512 350 | _openMode = 'rb' 351 | 352 | def __init__(self, subsystem, fn): 353 | super().__init__(subsystem) 354 | self._f = open(fn, self._openMode) 355 | self._f.seek(0, 2) 356 | self._capacity = self._f.tell() 357 | 358 | def _executeCommand(self, req): 359 | opcode = req.cdb[0] 360 | if opcode == 0x25: # READ CAPACITY (10) 361 | return self._handleREAD_CAPACITY_10(req) 362 | elif opcode == 0x28: # READ (10) 363 | return self._handleREAD_10(req) 364 | # 0xA0 REPORT LUNS 365 | # 0x1A MODE SENSE(6) 366 | elif opcode == 0x1A: # MODE SENSE (6) 367 | return self._handleMODE_SENSE_6(req) 368 | else: 369 | return super()._executeCommand(req) 370 | 371 | def _handleREAD_CAPACITY_10(self, req): 372 | if len(req.cdb) < 10: 373 | return ScsiResult.checkCondition(SCSI_ST__INVALID_FIELD_IN_CDB) 374 | 375 | lba = struct.unpack('>I', req.cdb[2:6])[0] 376 | if lba or req.cdb[8] & 1: 377 | return ScsiResult.checkCondition(SCSI_ST__INVALID_FIELD_IN_CDB) 378 | 379 | bytesPerLba = self.blockSize 380 | numLba = self._capacity//bytesPerLba 381 | 382 | if numLba > 0xFFFF_FFFF: 383 | numLba = 0xFFFF_FFFF 384 | 385 | req.dataInBuf.write(struct.pack('>II', numLba, bytesPerLba)) 386 | return ScsiResult.good() 387 | 388 | def _handleREAD_10(self, req): 389 | if len(req.cdb) < 10: 390 | return ScsiResult.checkCondition(SCSI_ST__INVALID_FIELD_IN_CDB) 391 | 392 | lba, groupNo, xferLen = struct.unpack('>IBH', req.cdb[2:9]) 393 | print('@Virtio: reading LBA %s, count %s' % (lba, xferLen)) 394 | self._f.seek(lba*self.blockSize) 395 | for i in range(xferLen): 396 | d = self._f.read(self.blockSize) 397 | req.dataInBuf.write(d) 398 | return ScsiResult.good() 399 | 400 | def _handleMODE_SENSE_6(self, req): 401 | pageCode = req.cdb[2] 402 | pc = pageCode>>6 403 | pageCode = pageCode & 0x3F 404 | subPageCode = req.cdb[3] 405 | print('@Virtio: unhandled MODE SENSE (6) pc=0x%x pageCode=0x%x subPageCode=0x%x' % (pc, pageCode, subPageCode)) 406 | return ScsiResult.checkCondition(SCSI_ST__INVALID_COMMAND_OPERATION_CODE) 407 | 408 | class ScsiBlockDevice(ScsiBlockDeviceBase): 409 | peripheralDeviceType = 0x00 # SBC 410 | _openMode = 'r+b' 411 | 412 | def _executeCommand(self, req): 413 | opcode = req.cdb[0] 414 | if opcode == 0x2A: # WRITE (10) 415 | return self._handleWRITE_10(req) 416 | elif opcode == 0x41: # WRITE SAME (10) 417 | return self._handleWRITE_SAME_10(req) 418 | else: 419 | return super()._executeCommand(req) 420 | 421 | def _handleWRITE_10(self, req): 422 | if len(req.cdb) < 10: 423 | return ScsiResult.checkCondition(SCSI_ST__INVALID_FIELD_IN_CDB) 424 | 425 | lba, groupNo, xferLen = struct.unpack('>IBH', req.cdb[2:9]) 426 | print('@Virtio: writing LBA %s, count %s' % (lba, xferLen)) 427 | self._f.seek(lba*self.blockSize) 428 | for i in range(xferLen): 429 | d = req.dataOutBuf.read(self.blockSize) 430 | assert len(d) == self.blockSize 431 | self._f.write(d) 432 | 433 | return ScsiResult.good() 434 | 435 | def _handleWRITE_SAME_10(self, req): 436 | if len(req.cdb) < 10: 437 | return ScsiResult.checkCondition(SCSI_ST__INVALID_FIELD_IN_CDB) 438 | 439 | lba, groupNo, xferLen = struct.unpack('>IBH', req.cdb[2:9]) 440 | print('@Virtio: writing-same LBA %s, count %s' % (lba, xferLen)) 441 | 442 | lbdata = req.cdb[1] & (1<<1) 443 | pbdata = req.cdb[1] & (1<<2) 444 | if lbdata or pbdata: 445 | return ScsiResult.checkCondition(SCSI_ST__INVALID_FIELD_IN_CDB) 446 | 447 | d = req.dataOutBuf.read(self.blockSize) 448 | assert len(d) == self.blockSize 449 | 450 | self._f.seek(lba*self.blockSize) 451 | for i in range(xferLen): 452 | self._f.write(d) 453 | 454 | return ScsiResult.good() 455 | 456 | class ScsiOpticalDevice(ScsiBlockDeviceBase): 457 | peripheralDeviceType = 0x05 # MMC 458 | blockSize = 2048 459 | 460 | def _executeCommand(self, req): 461 | opcode = req.cdb[0] 462 | # We must be able to generate DISK_EJECT_REQUEST="", 463 | # ID_CDROM_MEDIA_TRACK_COUNT_DATA="?*", 464 | # ID_CDROM_MEDIA_SESSION_LAST_OFFSET="?*" or Arch Linux ISOs will not boot 465 | # because udev will not recognise the optical drive as having media 466 | # inserted according to its default rules and not run the blkid scanner, 467 | # which in turn means the label of the ISO's filesystem is not determined 468 | # and /dev/disk/by-label/... is not created, which is what the initrd 469 | # expects to mount as the root FS. 470 | # 471 | # In order to get ID_CDROM_MEDIA_TRACK_COUNT_DATA, we need to implement 472 | # READ TOC/PMA/ATIP(Format=0 "TOC", Track/Session Number=1) and 473 | # READ TOC/PMA/ATIP(Format=0 "TOC", Track/Session Number=X). 474 | # In order to get ID_CDROM_MEDIA_SESSION_LAST_OFFSET, we need to implement 475 | # Perhaps also READ TOC/PMA/ATIP(Format=1 "Session Info") 476 | # 477 | # udev's scanner utility, cdrom_id, will not issue READ TOC/PMA/ATIP 478 | # however unless GET CONFIGURATION(StartingFeature=0, RT=0) is implemented, 479 | # so we need to implement that too. 480 | if opcode == 0x43: # READ TOC/PMA/ATIP 481 | return self._handleREAD_TOC_PMA_ATIP(req) 482 | #elif opcode == 0x51: # READ DISC INFORMATION 483 | # pass 484 | elif opcode == 0x46: # GET CONFIGURATION 485 | return self._handleGET_CONFIGURATION(req) 486 | else: 487 | return super()._executeCommand(req) 488 | # 0x4A GET EVENT STATUS NOTIFICATION 489 | # 0x43 READ TOC/PMA/ATIP 490 | # 0x51 READ DISC INFORMATION 491 | # 0x46 GET CONFIGURATION * 492 | 493 | def _handleREAD_TOC_PMA_ATIP(self, req): 494 | format = req.cdb[2] & 0xF 495 | trackSessionNo = req.cdb[6] 496 | 497 | if format == 0: # TOC 498 | return self._handleREAD_TOC_PMA_ATIP__TOC(req, trackSessionNo) 499 | elif format == 1: # Session Info 500 | return self._handleREAD_TOC_PMA_ATIP__SessionInfo(req, trackSessionNo) 501 | else: 502 | return ScsiResult.checkCondition(SCSI_ST__INVALID_FIELD_IN_CDB) 503 | 504 | def _handleREAD_TOC_PMA_ATIP__TOC(self, req, trackSessionNo): 505 | # From qemu hw/block/cdrom.c 506 | firstTrack = 1 507 | lastTrack = 1 508 | 509 | if trackSessionNo > 1 and trackSessionNo != 0xAA: 510 | return ScsiResult.checkCondition(SCSI_ST__INVALID_FIELD_IN_CDB) 511 | 512 | data = b'' 513 | if trackSessionNo <= 1: 514 | adrCtrl = 0x14 515 | trackNo = 1 516 | lba = 0 517 | data += struct.pack('>BBBBI', 0, adrCtrl, trackNo, 0, lba) 518 | 519 | adrCtrl = 0x16 520 | trackNo = 0xAA 521 | lba = self._capacity//self.blockSize 522 | data += struct.pack('>BBBBI', 0, adrCtrl, trackNo, 0, lba) 523 | 524 | b = struct.pack('>HBB', len(data), firstTrack, lastTrack) + data 525 | req.dataInBuf.write(b) 526 | return ScsiResult.good() 527 | 528 | def _handleREAD_TOC_PMA_ATIP__SessionInfo(self, req, trackSessionNo): 529 | firstSession = 1 530 | lastSession = 1 531 | 532 | adrCtrl = 0b0100 # Data track recorded uninterrupted 533 | trackNo = 1 534 | lba = 0 535 | 536 | data = struct.pack('>BBBBI', 0, adrCtrl, trackNo, 0, lba) 537 | 538 | b = struct.pack('>HBB', len(data), firstSession, lastSession) + data 539 | req.dataInBuf.write(b) 540 | return ScsiResult.good() 541 | 542 | def _handleGET_CONFIGURATION(self, req): 543 | curProfile = 0x40 # BD-ROM 544 | data = struct.pack('>HH', 0, curProfile) 545 | 546 | features = ( 547 | (0x0000,0x3), # Profile List 548 | (0x0001,0xB), # Core 549 | (0x0040,0x5), # BD Read 550 | ) 551 | 552 | for featureCode, flags in features: 553 | additionalData = b'' 554 | if featureCode == 0x0000: # Profile List 555 | profileNo = 0x0040 # BD-ROM 556 | additionalData = struct.pack('>HBB', profileNo, 1, 0) 557 | elif featureCode == 0x0001: # Core 558 | # N.B. Implementing this requires one to support 559 | # GET CONFIGURATION, GET EVENT STATUS NOTIFICATION, 560 | # MODE SELECT (10), MODE SENSE (10) 561 | physIntfStd = 1 # SCSI Family 562 | coreFlags = 1 563 | additionalData = struct.pack('>IBBBB', physIntfStd, coreFlags, 0, 0, 0) 564 | elif featureCode == 0x0040: # BD Read 565 | flags0 = 0 566 | flags1 = (1<<1) | (1<<2) # Support reading BD-RE 567 | flags2 = (1<<1) # Support reading BD-R 568 | flags3 = (1<<1) # Support reading BD-ROM 569 | additionalData = struct.pack('>B4xB2xB2xBx', flags0, flags1, flags2, flags3) 570 | 571 | data += struct.pack('>HBB', featureCode, flags, len(additionalData)) + additionalData 572 | 573 | data = struct.pack('>I', len(data)) + data 574 | req.dataInBuf.write(data) 575 | return ScsiResult.good() 576 | 577 | class ScsiReportLunsLU(IScsiSubsystem): 578 | def __init__(self, subsystem): 579 | self.subsystem = subsystem 580 | 581 | def executeCommand(self, req): 582 | opcode = req.cdb[0] 583 | if opcode == 0xA0: # REPORT LUNS 584 | return self._handleREPORT_LUNS(req) 585 | elif opcode == 0x12: # INQUIRY 586 | return self._handleINQUIRY(req) 587 | else: 588 | return ScsiResult.checkCondition(SCSI_ST__INVALID_COMMAND_OPERATION_CODE) 589 | 590 | # A SCSI subsystem which routes via LUN. 591 | class ScsiSubsystem(IScsiSubsystem): 592 | def __init__(self, *, diskPath=None, opticalPath=None): 593 | self._luns = {} 594 | #self.registerLun(0xC101_0000_0000_0000, ScsiReportLunsLU(self)) 595 | if opticalPath: 596 | self.blk0 = self.registerLun(0x0100_4000_0000_0000, ScsiOpticalDevice(self, fn=opticalPath)) 597 | if diskPath: 598 | self.blk1 = self.registerLun(0x0100_4001_0000_0000, ScsiBlockDevice(self, fn=diskPath)) 599 | 600 | def registerLun(self, id, lun): 601 | self._luns[id] = lun 602 | return lun 603 | 604 | def executeCommand(self, req): 605 | lun = self._luns.get(req.lun) 606 | if lun: 607 | return lun.executeCommand(req) 608 | 609 | print('@Virtio: LU not supported') 610 | return ScsiResult.checkCondition(SCSI_ST__LOGICAL_UNIT_NOT_SUPPORTED) 611 | -------------------------------------------------------------------------------- /vmm.py: -------------------------------------------------------------------------------- 1 | import mmap, ctypes, struct 2 | import kvmo, kvmapi 3 | from cpuid import * 4 | from x86 import * 5 | from iodev_qemu import * 6 | from memmgr import * 7 | 8 | MAP_NORESERVE = 0x4000 9 | 10 | class VMM: 11 | def __init__(self, platformFunc, firmwarePath, firmwareVarsPath, opticalPath=None, diskPath=None): 12 | self.kvm = kvmo.Kvm() 13 | 14 | for e in ( 15 | kvmapi.KvmCapability.KVM_CAP_COALESCED_MMIO, 16 | kvmapi.KvmCapability.KVM_CAP_SET_TSS_ADDR, 17 | kvmapi.KvmCapability.KVM_CAP_PIT2, 18 | kvmapi.KvmCapability.KVM_CAP_USER_MEMORY, 19 | kvmapi.KvmCapability.KVM_CAP_IRQ_ROUTING, 20 | kvmapi.KvmCapability.KVM_CAP_IRQCHIP, 21 | kvmapi.KvmCapability.KVM_CAP_HLT, 22 | kvmapi.KvmCapability.KVM_CAP_IRQ_INJECT_STATUS, 23 | kvmapi.KvmCapability.KVM_CAP_EXT_CPUID): 24 | self.kvm.checkExtension(e) 25 | 26 | self.cpuids = self.kvm.getSupportedCpuid() 27 | 28 | self._firmwarePath = firmwarePath 29 | self._firmwareVarsPath = firmwareVarsPath 30 | self._initVM() 31 | self._initVcpu() 32 | self._resetVcpu() 33 | self.i = 0 34 | self._memMgr = MemoryManager(self) 35 | self._platform = platformFunc(memoryManager=self._memMgr, firmwarePath=self._firmwarePath, firmwareVarsPath=self._firmwareVarsPath, vm=self.vm, sysResetFunc=self.onSysReset, opticalPath=opticalPath, diskPath=diskPath) 36 | 37 | def _initVM(self): 38 | self.vm = self.kvm.createVM() 39 | #self.vm.setTssAddr(0xFFFBD000) 40 | self.vm.createPit2(kvmapi.KvmPitConfig()) 41 | self.vm.createIrqChip() 42 | 43 | def _initVcpu(self): 44 | self.vcpu = self.vm.createVcpu() 45 | self._vcpuOrigRegs = self.vcpu.regs 46 | self._vcpuOrigSregs = self.vcpu.sregs 47 | self._vcpuOrigFpu = self.vcpu.fpu 48 | 49 | self.virtCpuids = [] 50 | for c in self.cpuids: 51 | #if c.function == 0: 52 | # self.virtCpuids.append(kvmapi.KvmCpuidEntry2(0, 0, 0, c.eax, 0x44444444, 0x44444444, 0x44444444)) 53 | # #self.virtCpuids.append(kvmapi.KvmCpuidEntry2(0, 0, 0, c.eax, ebx, ecx, edx)) 54 | # #self.virtCpuids.append(kvmapi.KvmCpuidEntry2(0, 0, 0, c.eax, 0x42413938, 0x37363534, 0x33323130)) 55 | if c.function == 1: 56 | r = get_cpuid(0x1, 0) 57 | c.eax = r.eax 58 | c.ebx = r.ebx 59 | c.ecx = r.ecx 60 | c.edx = r.edx 61 | #c.ecx = 0x37363534 62 | #c.edx = 0x078bfbff 63 | #c.ecx |= (1<<31) 64 | #c.ecx |= (1<<0) | (1<<1) | (1<<9) | (1<<28) | (1<<19) | (1<<20) | (1<<23) # SSE3 PCLMULDQ SSSE3 AVX SSE4.1 SSE4.2 POPCNT 65 | c.ecx |= (1<<31) 66 | self.virtCpuids.append(c) 67 | elif c.function == 2: 68 | r = get_cpuid(0x2, 0) 69 | c.eax = r.eax 70 | c.ebx = r.ebx 71 | c.ecx = r.ecx 72 | c.edx = r.edx 73 | self.virtCpuids.append(c) 74 | elif c.function == 7: 75 | r = get_cpuid(0x7, 0) 76 | c.eax = r.eax 77 | c.ebx = r.ebx 78 | c.ecx = r.ecx 79 | c.edx = r.edx 80 | self.virtCpuids.append(c) 81 | elif c.function == 0x80000001: 82 | r = get_cpuid(0x80000001, 0) 83 | c.eax = r.eax 84 | c.ebx = r.ebx 85 | c.ecx = r.ecx 86 | c.edx = r.edx 87 | self.virtCpuids.append(c) 88 | elif c.function == 0x80000005: 89 | r = get_cpuid(0x80000005, 0) 90 | self.virtCpuids.append(kvmapi.KvmCpuidEntry2(0x80000005, 0, 0, r.eax, r.ebx, r.ecx, r.edx)) 91 | elif c.function == 0x80000006: 92 | r = get_cpuid(0x80000006, 0) 93 | self.virtCpuids.append(kvmapi.KvmCpuidEntry2(0x80000006, 0, 0, r.eax, r.ebx, r.ecx, r.edx)) 94 | elif c.function == 0x80000008: 95 | r = get_cpuid(0x80000008, 0) 96 | self.virtCpuids.append(kvmapi.KvmCpuidEntry2(0x80000008, 0, 0, r.eax, r.ebx, r.ecx, r.edx)) 97 | else: 98 | self.virtCpuids.append(c) 99 | 100 | cpuid = b'KVMKVMKVM\0\0\0' 101 | ebx, ecx, edx = struct.unpack('> 8)&0x07 201 | devNo = (cf8>>11)&0x1F 202 | busNo = (cf8>>16)&0xFF 203 | return '(%s:%s.%s) + 0x%02x' % (busNo,devNo,funcNo,regNo) 204 | 205 | def _handleIoRead(self, addr, width): 206 | if width == 1: 207 | return self._platform.iospace.read8(addr) 208 | elif width == 2: 209 | return self._platform.iospace.read16(addr) 210 | elif width == 4: 211 | return self._platform.iospace.read32(addr) 212 | else: 213 | raise Exception("invalid width") 214 | 215 | def _handleIoWrite(self, addr, v, width): 216 | if width == 1: 217 | self._platform.iospace.write8(addr, v) 218 | elif width == 2: 219 | self._platform.iospace.write16(addr, v) 220 | elif width == 4: 221 | self._platform.iospace.write32(addr, v) 222 | else: 223 | raise Exception("invalid width") 224 | 225 | def _handleMmioRead(self, addr, width): 226 | if width == 1: 227 | return self._platform.mspace.read8(addr) 228 | elif width == 2: 229 | return self._platform.mspace.read16(addr) 230 | elif width == 4: 231 | return self._platform.mspace.read32(addr) 232 | elif width == 8: 233 | return self._platform.mspace.read64(addr) 234 | else: 235 | raise Exception("invalid width") 236 | 237 | def _handleMmioWrite(self, addr, v, width): 238 | if width == 1: 239 | self._platform.mspace.write8(addr, v) 240 | elif width == 2: 241 | self._platform.mspace.write16(addr, v) 242 | elif width == 4: 243 | self._platform.mspace.write32(addr, v) 244 | elif width == 8: 245 | self._platform.mspace.write64(addr, v) 246 | else: 247 | raise Exception("invalid width") 248 | 249 | def runOnce(self): 250 | try: 251 | self.vcpu.runOnce() 252 | except InterruptedError as e: 253 | print("Interrupted") 254 | self._dumpRegs() 255 | return True 256 | except KeyboardInterrupt: 257 | print("Keyboard Interrupt") 258 | self._dumpRegs() 259 | self.i += 1 260 | return self.i < 2 261 | 262 | reason = self.vcpu.reason 263 | if reason == kvmapi.KvmExitReason.KVM_EXIT_UNKNOWN: 264 | print("unknown exit reason") 265 | elif reason == kvmapi.KvmExitReason.KVM_EXIT_DEBUG: 266 | dbg = self.vcpu.runData.exitReasons.debug 267 | print("exit debug: exc=0x%x pc=0x%x" % (dbg.exception, dbg.pc)) 268 | self._dumpRegs() 269 | elif reason == kvmapi.KvmExitReason.KVM_EXIT_IO: 270 | io = self.vcpu.runData.exitReasons.io 271 | if io.direction: # out 272 | #r = self.vcpu.runBuf.seek(io.dataOffset) 273 | #r = self.vcpu.runBuf.read(io.size) 274 | r = bytearray(io.size) 275 | for i in range(io.size): 276 | r[i] = self.vcpu.runBuf[io.dataOffset+i] 277 | if io.size == 1: 278 | v = struct.unpack(' u%s: 0x%x" % (io.port, io.size*8, result)) 305 | 306 | elif reason == kvmapi.KvmExitReason.KVM_EXIT_MMIO: #direction size port count dataOffset 307 | mmio = self.vcpu.runData.exitReasons.mmio 308 | if mmio.isWrite: 309 | if mmio.len == 1: 310 | v = mmio.data[0] #struct.unpack(' u%d" % (mmio.physAddr, mmio.len*8)) 339 | 340 | elif reason == kvmapi.KvmExitReason.KVM_EXIT_INTR: 341 | print("exit INTR") 342 | elif reason == kvmapi.KvmExitReason.KVM_EXIT_SHUTDOWN: 343 | print("exit shutdown") 344 | return False 345 | elif reason == kvmapi.KvmExitReason.KVM_EXIT_SYSTEM_EVENT: 346 | print("exit system event") 347 | elif reason == kvmapi.KvmExitReason.KVM_EXIT_HLT: 348 | print("exit halt") 349 | return False 350 | else: 351 | print("other exit reason") 352 | 353 | return True 354 | 355 | def run(self): 356 | while True: 357 | if not self.runOnce(): 358 | break 359 | -------------------------------------------------------------------------------- /x86.py: -------------------------------------------------------------------------------- 1 | # x86 MSRs. 2 | MSR_TSC = 0x10 3 | MSR_IA32_SYSENTER_CS = 0x174 4 | MSR_IA32_SYSENTER_ESP = 0x175 5 | MSR_IA32_SYSENTER_EIP = 0x176 6 | MSR_STAR = 0xC0000081 7 | MSR_LSTAR = 0xC0000082 8 | MSR_CSTAR = 0xC0000083 9 | MSR_SYSCALL_MASK = 0xC0000084 10 | MSR_KERNEL_GS_BASE = 0xC0000102 11 | MSR_IA32_TSC = 0xC0000010 12 | MSR_IA32_MISC_ENABLE = 0xC00001A0 13 | MSR_IA32_MISC_ENABLE__FAST_STRING = (1<<0) 14 | MSR_EFER = 0xC0000080 15 | --------------------------------------------------------------------------------