Search code examples
goassembly

Get value from RDPMC using Go


I am running an Ubuntu server VM on VMware on my Fedora system and I would like to retrieve my host's TSC value. According to VMware, I can do that by executing RDPMC 0x10000. This works using inline assembly in C++ but now I need to do the same in go.

Basically, the logic of my code resides in two files. In a .s file I have this:

#include "textflag.h"

// func rdPmcTsc() uint64
TEXT ·rdPmcTsc(SB), NOSPLIT, $0
    MOVL $0x10000, CX
    RDPMC
    RET

Which I link into a .go file like so:

//go:nosplit
func rdPmcTsc() uint64

I have been debugging this on VScode and, looking at the values in the AX and DX registers, it seems the assembly code works as intended. However, once I need to use the value in Go, it transforms values around 25Ti into others around 8000 and other similar errors. I have been looking around for long, but I haven't found any reasonable explanation as to why this could happen. And, to my best knowledge, it seems that the way I am trying to get the RDPMC 0x10000 value is how it should be done in Go.

If anyone knows how to get the correct return value in Go, knows why it may not be working or anything of that sort, it would be great.


Solution

  • Managed to fix it. It turns out the result needs to be stored before RET in order for Go to retrieve it. Also, the 32 bits of rax and rdx had to be combined to get the full TSC result. The following assembler code fixed it:

    #include "textflag.h"
    
    // func rdPmcTsc() uint64
    TEXT ·rdPmcTsc(SB), NOSPLIT, $0
        MOVL $0x10000, CX
        RDPMC
        SHLQ $32, DX       // Shift the higher bits in rdx
        ORQ DX, AX         // Combine the 2 registers for the 64 bits result
        MOVQ AX, ret+0(FP) // Store the result
        RET