commit 73f52a86d867dfb3b3fbce039bab2055d335c72d Author: Armin Friedl Date: Sun Feb 25 23:51:20 2024 +0100 Some arch native calls with zig diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96d00e4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,166 @@ +# Created by https://www.toptal.com/developers/gitignore/api/zig,emacs,linux,jetbrains+all +# Edit at https://www.toptal.com/developers/gitignore?templates=zig,emacs,linux,jetbrains+all + +### Emacs ### +# -*- mode: gitignore; -*- +*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* + +# Org-mode +.org-id-locations +*_archive + +# flymake-mode +*_flymake.* + +# eshell files +/eshell/history +/eshell/lastdir + +# elpa packages +/elpa/ + +# reftex files +*.rel + +# AUCTeX auto folder +/auto/ + +# cask packages +.cask/ +dist/ + +# Flycheck +flycheck_*.el + +# server auth directory +/server/ + +# projectiles files +.projectile + +# directory configuration +.dir-locals.el + +# network security +/network-security.data + + +### JetBrains+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### JetBrains+all Patch ### +# Ignore everything but code style settings and run configurations +# that are supposed to be shared within teams. + +.idea/* + +!.idea/codeStyles +!.idea/runConfigurations + +### Linux ### + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### zig ### +# Zig programming language + +zig-cache/ +zig-out/ +build/ +build-*/ +docgen_tmp/ + +# End of https://www.toptal.com/developers/gitignore/api/zig,emacs,linux,jetbrains+all diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..305dc11 --- /dev/null +++ b/build.zig @@ -0,0 +1,79 @@ +const std = @import("std"); + +// Although this function looks imperative, note that its job is to +// declaratively construct a build graph that will be executed by an external +// runner. +pub fn build(b: *std.Build) void { + // Standard target options allows the person running `zig build` to choose + // what target to build for. Here we do not override the defaults, which + // means any target is allowed, and the default is native. Other options + // for restricting supported target set are available. + const target = b.standardTargetOptions(.{}); + + // Standard optimization options allow the person running `zig build` to select + // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not + // set a preferred release mode, allowing the user to decide how to optimize. + const optimize = b.standardOptimizeOption(.{}); + + const exe = b.addExecutable(.{ + .name = "rand", + .root_source_file = .{ .path = "src/main.zig" }, + .target = target, + .optimize = optimize, + }); + + exe.addCSourceFile(.{ + .file = .{ + .path = "src/arch/rand.c", + }, + .flags = &[_][]const u8{}, + }); + + exe.addIncludePath(.{ + .path = "src/arch/", + }); + + exe.linkLibC(); + + // This declares intent for the executable to be installed into the + // standard location when the user invokes the "install" step (the default + // step when running `zig build`). + b.installArtifact(exe); + + // This *creates* a Run step in the build graph, to be executed when another + // step is evaluated that depends on it. The next line below will establish + // such a dependency. + const run_cmd = b.addRunArtifact(exe); + + // By making the run step depend on the install step, it will be run from the + // installation directory rather than directly from within the cache directory. + // This is not necessary, however, if the application depends on other installed + // files, this ensures they will be present and in the expected location. + run_cmd.step.dependOn(b.getInstallStep()); + + // This allows the user to pass arguments to the application in the build + // command itself, like this: `zig build run -- arg1 arg2 etc` + if (b.args) |args| { + run_cmd.addArgs(args); + } + + // This creates a build step. It will be visible in the `zig build --help` menu, + // and can be selected like this: `zig build run` + // This will evaluate the `run` step rather than the default, which is "install". + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); + + const exe_unit_tests = b.addTest(.{ + .root_source_file = .{ .path = "src/main.zig" }, + .target = target, + .optimize = optimize, + }); + + const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); + + // Similar to creating the run step earlier, this exposes a `test` step to + // the `zig build --help` menu, providing a way for the user to request + // running the unit tests. + const test_step = b.step("test", "Run unit tests"); + test_step.dependOn(&run_exe_unit_tests.step); +} diff --git a/build.zig.zon b/build.zig.zon new file mode 100644 index 0000000..e22af6a --- /dev/null +++ b/build.zig.zon @@ -0,0 +1,62 @@ +.{ + .name = "untitled", + // This is a [Semantic Version](https://semver.org/). + // In a future version of Zig it will be used for package deduplication. + .version = "0.0.0", + + // This field is optional. + // This is currently advisory only; Zig does not yet do anything + // with this value. + //.minimum_zig_version = "0.11.0", + + // This field is optional. + // Each dependency must either provide a `url` and `hash`, or a `path`. + // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. + // Once all dependencies are fetched, `zig build` no longer requires + // internet connectivity. + .dependencies = .{ + // See `zig fetch --save ` for a command-line interface for adding dependencies. + //.example = .{ + // // When updating this field to a new URL, be sure to delete the corresponding + // // `hash`, otherwise you are communicating that you expect to find the old hash at + // // the new URL. + // .url = "https://example.com/foo.tar.gz", + // + // // This is computed from the file contents of the directory of files that is + // // obtained after fetching `url` and applying the inclusion rules given by + // // `paths`. + // // + // // This field is the source of truth; packages do not come from a `url`; they + // // come from a `hash`. `url` is just one of many possible mirrors for how to + // // obtain a package matching this `hash`. + // // + // // Uses the [multihash](https://multiformats.io/multihash/) format. + // .hash = "...", + // + // // When this is provided, the package is found in a directory relative to the + // // build root. In this case the package's hash is irrelevant and therefore not + // // computed. This field and `url` are mutually exclusive. + // .path = "foo", + //}, + }, + + // Specifies the set of files and directories that are included in this package. + // Only files and directories listed here are included in the `hash` that + // is computed for this package. + // Paths are relative to the build root. Use the empty string (``) to refer to + // the build root itself. + // A directory listed here means that all files within, recursively, are included. + .paths = .{ + // This makes *all* files, recursively, included in this package. It is generally + // better to explicitly list the files and directories instead, to insure that + // fetching from tarballs, file system paths, and version control all result + // in the same contents hash. + "", + // For example... + //"build.zig", + //"build.zig.zon", + //"src", + //"LICENSE", + //"README.md", + }, +} \ No newline at end of file diff --git a/src/arch/rand.c b/src/arch/rand.c new file mode 100644 index 0000000..59b1b32 --- /dev/null +++ b/src/arch/rand.c @@ -0,0 +1,31 @@ +#include "rand.h" + +bool rdrand16_step(uint16_t *rand) { + unsigned char ok; + + asm volatile("rdrand %0;" + "setc %1" + :"=r" (*rand), "=qm" (ok)); + + return ok != 0; +} + +bool rdrand32_step(uint32_t *rand) { + unsigned char ok; + + asm volatile("rdrand %0;" + "setc %1" + : "=r" (*rand), "=qm" (ok)); + + return ok != 0; +} + +bool rdrand64_step(uint64_t *rand) { + unsigned char ok; + + asm volatile("rdrand %0;" + "setc %1" + : "=r" (*rand), "=qm" (ok)); + + return ok != 0; +} diff --git a/src/arch/rand.h b/src/arch/rand.h new file mode 100644 index 0000000..11bd30b --- /dev/null +++ b/src/arch/rand.h @@ -0,0 +1,11 @@ +#ifndef RAND_H_ +#define RAND_H_ + +#include +#include + +bool rdrand16_step(uint16_t *rand); +bool rdrand32_step(uint32_t *rand); +bool rdrand64_step(uint64_t *rand); + +#endif // RAND_H_ diff --git a/src/lib.zig b/src/lib.zig new file mode 100644 index 0000000..3c205c6 --- /dev/null +++ b/src/lib.zig @@ -0,0 +1,29 @@ +const rnd = @cImport( + {@cInclude("rand.h");} +); + +const RndError = error{InsufficientEntropy}; + +pub fn cpuid(eax_input: u32, ecx_input: u32) [4]u32 { + var eax: u32 = undefined; + var ebx: u32 = undefined; + var ecx: u32 = undefined; + var edx: u32 = undefined; + + asm volatile ( + "cpuid" + : [_] "={eax}" (eax), [_] "={ebx}" (ebx), [_] "={ecx}" (ecx), [_] "={edx}" (edx) + : [_] "{eax}" (eax_input), [_] "{ecx}" (ecx_input) + ); + + return .{eax, ebx, ecx, edx}; +} + +pub fn rdrand() !u64 { + var x: u64 = 0; + var ok: bool = rnd.rdrand64_step(&x); + + if(!ok) return error.InsufficientEntropy; + + return x; +} diff --git a/src/main.zig b/src/main.zig new file mode 100644 index 0000000..93e049f --- /dev/null +++ b/src/main.zig @@ -0,0 +1,26 @@ +const std = @import("std"); +const lib = @import("lib.zig"); + + +pub fn main() !void { + for(0..100) |_| { + var res = try lib.rdrand(); + std.debug.print("{d}\n", .{res}); + } + + const vendor_id = struct { + fn vendor() [12]u8 { + var buffer: [12]u8 = undefined; + const cpuinfo = lib.cpuid(0, 0); + + std.mem.writeIntNative(u32, buffer[0..4], cpuinfo[1]); + std.mem.writeIntNative(u32, buffer[4..8], cpuinfo[3]); + std.mem.writeIntNative(u32, buffer[8..], cpuinfo[2]); + + return buffer; + } + }.vendor(); + + std.debug.print("CPU Vendor ID: {s}\n", .{vendor_id}); + +}