284 lines
7.7 KiB
Zig
284 lines
7.7 KiB
Zig
const std = @import("std");
|
|
|
|
const msr = @import("msr.zig");
|
|
|
|
inline fn bit(value: var, comptime pos: @IntType(false, std.math.log2_int(u16, @typeInfo(@TypeOf(value)).Int.bits))) bool {
|
|
return (value & (1 << pos)) != 0;
|
|
}
|
|
|
|
pub const CpuId = struct {
|
|
const Self = @This();
|
|
|
|
a: u32,
|
|
b: u32,
|
|
c: u32,
|
|
d: u32,
|
|
|
|
pub fn instructionSupport() InstructionSupport {
|
|
const standard = cpuid(0x00000001);
|
|
const extended = cpuid(0x80000001);
|
|
const structured = cpuid(0x00000007);
|
|
|
|
return InstructionSupport{
|
|
// TODO All flags
|
|
.sse = bit(standard.d, 25),
|
|
.sse2 = bit(standard.d, 26),
|
|
.sse3 = bit(standard.c, 0),
|
|
.avx = bit(standard.c, 28),
|
|
.aes = bit(standard.c, 25),
|
|
.sha = bit(structured.b, 29),
|
|
};
|
|
}
|
|
|
|
pub fn signature() Signature {
|
|
const sign = cpuid(1);
|
|
return Signature{
|
|
.value = sign.a,
|
|
};
|
|
}
|
|
|
|
pub fn vendor() [12]u8 {
|
|
var result = [_]u8{0} ** 12;
|
|
const name = cpuid(0);
|
|
|
|
// bitCast crashes the compiler
|
|
|
|
comptime var i = 0;
|
|
comptime const shifts = [_]u5{ 0, 8, 16, 24 };
|
|
inline for (shifts) |s| {
|
|
result[i] = @intCast(u8, name.b >> s & 255);
|
|
i += 1;
|
|
}
|
|
inline for (shifts) |s| {
|
|
result[i] = @intCast(u8, name.d >> s & 255);
|
|
i += 1;
|
|
}
|
|
inline for (shifts) |s| {
|
|
result[i] = @intCast(u8, name.c >> s & 255);
|
|
i += 1;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
pub const InstructionSupport = packed struct {
|
|
clfsh: bool = false,
|
|
cmpxchg8b: bool = false,
|
|
cmpxchg16b: bool = false,
|
|
cmov: bool = false,
|
|
msr: bool = false,
|
|
tsc: bool = false,
|
|
rdtscp: bool = false,
|
|
syscallsysret: bool = false,
|
|
sysentersysexit: bool = false,
|
|
fpu: bool = false,
|
|
x87_and_cmov: bool = false,
|
|
mmx: bool = false,
|
|
_3dnow: bool = false,
|
|
mmxext: bool = false,
|
|
_3dnowext: bool = false,
|
|
_3dnowprefetch: bool = false,
|
|
sse: bool = false,
|
|
sse2: bool = false,
|
|
sse3: bool = false,
|
|
ssse3: bool = false,
|
|
sse4a: bool = false,
|
|
sse41: bool = false,
|
|
sse42: bool = false,
|
|
lm: bool = false,
|
|
svm: bool = false,
|
|
avx: bool = false,
|
|
avx2: bool = false,
|
|
xop: bool = false,
|
|
aes: bool = false,
|
|
fma: bool = false,
|
|
fma4: bool = false,
|
|
f16c: bool = false,
|
|
rdrand: bool = false,
|
|
abm: bool = false,
|
|
bmi1: bool = false,
|
|
bmi2: bool = false,
|
|
popcnt: bool = false,
|
|
tbm: bool = false,
|
|
movbe: bool = false,
|
|
monitor: bool = false,
|
|
monitorx: bool = false,
|
|
pclmulqdq: bool = false,
|
|
fxsr: bool = false,
|
|
skinit: bool = false,
|
|
lahfsahf: bool = false,
|
|
fsgsbase: bool = false,
|
|
sha: bool = false,
|
|
clflopt: bool = false,
|
|
smap: bool = false,
|
|
adx: bool = false,
|
|
rdseed: bool = false,
|
|
sme: bool = false,
|
|
sev: bool = false,
|
|
pageflushmsr: bool = false,
|
|
es: bool = false,
|
|
clzero: bool = false,
|
|
instruction_retired_counter: bool = false,
|
|
error_pointer_zero_restore: bool = false,
|
|
xsaveopt: bool = false,
|
|
xsavec_with_ecx1: bool = false,
|
|
xgetbv: bool = false,
|
|
xsaves_xrstors: bool = false,
|
|
xsave: bool = false,
|
|
};
|
|
|
|
pub const Signature = struct {
|
|
value: u32,
|
|
|
|
pub inline fn stepping(self: Signature) u4 {
|
|
return @intCast(u4, self.value & 0xf);
|
|
}
|
|
|
|
pub inline fn baseModel(self: Signature) u4 {
|
|
return @intCast(u4, (self.value & 0xf << 4) >> 4);
|
|
}
|
|
|
|
pub inline fn baseFamily(self: Signature) u4 {
|
|
return @intCast(u4, (self.value & 0xf << 8) >> 8);
|
|
}
|
|
|
|
pub inline fn extModel(self: Signature) u4 {
|
|
return @intCast(u4, (self.value & 0xf << 16) >> 16);
|
|
}
|
|
|
|
pub inline fn extFamily(self: Signature) u8 {
|
|
return @intCast(u4, (self.value & 0xf << 20) >> 20);
|
|
}
|
|
|
|
pub fn model(self: Signature) u8 {
|
|
const base = @intCast(u8, self.baseModel());
|
|
return base + if (base == 0xf) (@intCast(u8, self.extModel()) << 4) else 0;
|
|
}
|
|
|
|
pub fn family(self: Signature) u8 {
|
|
const base = @intCast(u8, self.baseFamily());
|
|
return base + if (base == 0xf) self.extFamily() else 0;
|
|
}
|
|
|
|
pub fn format(self: *const @This(), comptime fmt: []const u8, options: std.fmt.FormatOptions, context: var, comptime Errors: type, output: fn (@TypeOf(context), []const u8) Errors!void) Errors!void {
|
|
try std.fmt.format(context, Errors, output, "Family {x}h model {} stepping {} - {x}", .{ self.family(), self.model(), self.stepping(), self.value });
|
|
}
|
|
};
|
|
};
|
|
|
|
pub fn cpuid(function: u32) CpuId {
|
|
var a: u32 = undefined;
|
|
var b: u32 = undefined;
|
|
var c: u32 = undefined;
|
|
var d: u32 = undefined;
|
|
asm volatile ("cpuid"
|
|
: [a] "={eax}" (a),
|
|
[b] "={ebx}" (b),
|
|
[c] "={ecx}" (c),
|
|
[d] "={edx}" (d)
|
|
: [function] "{eax}" (function)
|
|
);
|
|
|
|
return CpuId{
|
|
.a = a,
|
|
.b = b,
|
|
.c = c,
|
|
.d = d,
|
|
};
|
|
}
|
|
|
|
pub const Capabilities = struct {
|
|
physical_address_width: u8 = 32,
|
|
virtual_address_width: u8 = 32,
|
|
gigabyte_pages: bool = false,
|
|
no_execute: bool = false,
|
|
|
|
pub fn initialize(self: *@This()) void {
|
|
const addr_width = cpuid(0x80000008);
|
|
self.physical_address_width = @intCast(u8, addr_width.a & 255);
|
|
self.virtual_address_width = @intCast(u8, addr_width.a >> 8 & 255);
|
|
|
|
const caps = cpuid(0x80000001);
|
|
if (bit(caps.d, 20) or bit(caps.d, 29)) { // EFER available
|
|
var extended_features_enables = rdmsr(msr.ia32_efer);
|
|
extended_features_enables |= 1 << 11; // Set no execute
|
|
wrmsr(msr.ia32_efer, extended_features_enables);
|
|
|
|
extended_features_enables = rdmsr(msr.ia32_efer);
|
|
if (bit(extended_features_enables, 11))
|
|
self.no_execute = true;
|
|
}
|
|
if (bit(caps.d, 26)) {
|
|
self.gigabyte_pages = true;
|
|
}
|
|
}
|
|
};
|
|
|
|
pub inline fn hlt() void {
|
|
asm volatile ("hlt");
|
|
}
|
|
|
|
pub inline fn lgdt(addr: u64) void {
|
|
asm volatile ("lgdt %[addr]"
|
|
: [addr] "r" (addr)
|
|
);
|
|
}
|
|
|
|
pub inline fn lidt(addr: u64) void {
|
|
asm volatile ("lidt %[addr]"
|
|
: [addr] "r" (addr)
|
|
);
|
|
}
|
|
|
|
pub inline fn outb(port: u16, data: u8) void {
|
|
asm volatile ("out %[data], %[port]"
|
|
:
|
|
: [port] "{dx}" (port),
|
|
[data] "{al}" (data)
|
|
);
|
|
}
|
|
|
|
pub inline fn rdmsr(reg: u32) u64 {
|
|
var value_low: u32 = undefined;
|
|
var value_high: u32 = undefined;
|
|
|
|
asm volatile ("rdmsr"
|
|
: [ret_low] "={eax}" (value_low),
|
|
[ret_high] "={edx}" (value_high)
|
|
: [reg] "{ecx}" (reg)
|
|
);
|
|
|
|
return @intCast(u64, value_high) << 32 | value_low;
|
|
}
|
|
|
|
pub inline fn wrmsr(reg: u32, value: u64) void {
|
|
asm volatile ("wrmsr"
|
|
:
|
|
: [reg] "{ecx}" (reg),
|
|
[value_low] "{eax}" (@intCast(u32, value & 0xffff)),
|
|
[value_high] "{edx}" (@intCast(u32, (value & 0xffff0000) >> 32))
|
|
);
|
|
}
|
|
|
|
pub const register = struct {
|
|
pub inline fn cr3() u64 {
|
|
return asm volatile ("movq %%cr3, %%rax"
|
|
: [ret] "={rax}" (-> u64)
|
|
);
|
|
}
|
|
|
|
pub inline fn cr3Set(addr: u64) void {
|
|
return asm volatile ("movq %[addr], %%cr3"
|
|
:
|
|
: [addr] "r" (addr)
|
|
: "memory"
|
|
);
|
|
}
|
|
|
|
pub inline fn sp() u64 {
|
|
return asm volatile (""
|
|
: [ret] "={rsp}" (-> u64)
|
|
);
|
|
}
|
|
};
|