Browse Source

Add basic PMC support.

Giovanni Mascellani 5 years ago
parent
commit
3d351a557d
8 changed files with 273 additions and 14 deletions
  1. 1 1
      Makefile
  2. 112 10
      asmasm/asmasm.asm
  3. 21 0
      asmg/asm_opcodes.g
  4. 23 3
      asmg/fasm.g
  5. 16 0
      asmg/kernel-asmg.asm
  6. 4 0
      asmg/utils.g
  7. 93 0
      lib/pmc.asm
  8. 3 0
      test/test.asm

+ 1 - 1
Makefile

@@ -124,7 +124,7 @@ build/boot_empty.x86: build/bootloader.x86.mbr build/bootloader.x86.stage2 build
 build/script.g:
 	bash -c "echo -n" > $@
 
-build/full-asmg.asm: lib/multiboot.asm lib/kernel.asm lib/shutdown.asm lib/ar.asm lib/library.asm lib/setjmp.asm asmg/asmg.asm asmg/kernel-asmg.asm lib/top.asm
+build/full-asmg.asm: lib/multiboot.asm lib/kernel.asm lib/shutdown.asm lib/ar.asm lib/library.asm lib/setjmp.asm lib/pmc.asm asmg/asmg.asm asmg/kernel-asmg.asm lib/top.asm
 	cat $^ | grep -v "^ *section " > $@
 
 build/initrd-asmg.ar: asmg/*.g build/script.g test/test.hex2 test/test.m1 test/test_mes.c test/test.asm build/END

+ 112 - 10
asmasm/asmasm.asm

@@ -56,6 +56,10 @@
   OP_XOR equ 38
   OP_TEST equ 39
   OP_HLT equ 40
+  OP_RDPMC equ 41
+  OP_RDMSR equ 42
+  OP_WRMSR equ 43
+  OP_CPUID equ 44
 
   section .data
 
@@ -142,6 +146,14 @@ opcode_names:
   db 0
   db 'hlt'
   db 0
+  db 'rdpmc'
+  db 0
+  db 'rdmsr'
+  db 0
+  db 'wrmsr'
+  db 0
+  db 'cpuid'
+  db 0
   db 0
 
 opcode_funcs:
@@ -176,7 +188,7 @@ opcode_funcs:
   dd process_jmp_like    ; OP_MUL
   dd process_jmp_like    ; OP_IMUL
   dd process_int         ; OP_INT
-  dd process_ret         ; OP_RET
+  dd process_ret_like    ; OP_RET
   dd process_in_like     ; OP_IN
   dd process_in_like     ; OP_OUT
   dd process_jmp_like    ; OP_DIV
@@ -186,6 +198,57 @@ opcode_funcs:
   dd process_add_like    ; OP_XOR
   dd process_add_like    ; OP_TEST
   dd process_hlt         ; OP_HLT
+  dd process_ret_like    ; OP_RDPMC
+  dd process_ret_like    ; OP_RDMSR
+  dd process_ret_like    ; OP_WRMSR
+  dd process_ret_like    ; OP_CPUID
+
+empty_opcode:
+  dd 0xf0    ; OP_PUSH
+  dd 0xf0    ; OP_POP
+  dd 0xf0    ; OP_ADD
+  dd 0xf0    ; OP_SUB
+  dd 0xf0    ; OP_MOV
+  dd 0xf0    ; OP_CMP
+  dd 0xf0    ; OP_AND
+  dd 0xf0    ; OP_OR
+  dd 0xf0    ; OP_JMP
+  dd 0xf0    ; OP_CALL
+  dd 0xf0    ; OP_JE
+  dd 0xf0    ; OP_JNE
+  dd 0xf0    ; OP_JA
+  dd 0xf0    ; OP_JNA
+  dd 0xf0    ; OP_JAE
+  dd 0xf0    ; OP_JNAE
+  dd 0xf0    ; OP_JB
+  dd 0xf0    ; OP_JNB
+  dd 0xf0    ; OP_JBE
+  dd 0xf0    ; OP_JNBE
+  dd 0xf0    ; OP_JG
+  dd 0xf0    ; OP_JNG
+  dd 0xf0    ; OP_JGE
+  dd 0xf0    ; OP_JNGE
+  dd 0xf0    ; OP_JL
+  dd 0xf0    ; OP_JNL
+  dd 0xf0    ; OP_JLE
+  dd 0xf0    ; OP_JNLE
+  dd 0xf0    ; OP_MUL
+  dd 0xf0    ; OP_IMUL
+  dd 0xf0    ; OP_INT
+  dd 0xc3    ; OP_RET
+  dd 0xf0    ; OP_IN
+  dd 0xf0    ; OP_OUT
+  dd 0xf0    ; OP_DIV
+  dd 0xf0    ; OP_IDIV
+  dd 0xf0    ; OP_NEG
+  dd 0xf0    ; OP_NOT
+  dd 0xf0    ; OP_XOR
+  dd 0xf0    ; OP_TEST
+  dd 0xf0    ; OP_HLT
+  dd 0x1330f ; OP_RDPMC
+  dd 0x1320f ; OP_RDMSR
+  dd 0x1300f ; OP_WRMSR
+  dd 0x1a20f ; OP_CPUID
 
 rm32_opcode:
   dd 0x06ff  ; OP_PUSH
@@ -229,6 +292,10 @@ rm32_opcode:
   dd 0xf0    ; OP_XOR
   dd 0xf0    ; OP_TEST
   dd 0xf0    ; OP_HLT
+  dd 0xf0    ; OP_RDPMC
+  dd 0xf0    ; OP_RDMSR
+  dd 0xf0    ; OP_WRMSR
+  dd 0xf0    ; OP_CPUID
 
 imm32_opcode:
   dd 0xf0    ; OP_PUSH
@@ -272,6 +339,10 @@ imm32_opcode:
   dd 0xf0    ; OP_XOR
   dd 0xf0    ; OP_TEST
   dd 0xf0    ; OP_HLT
+  dd 0xf0    ; OP_RDPMC
+  dd 0xf0    ; OP_RDMSR
+  dd 0xf0    ; OP_WRMSR
+  dd 0xf0    ; OP_CPUID
 
 r8rm8_opcode:
   dd 0xf0    ; OP_PUSH
@@ -315,6 +386,10 @@ r8rm8_opcode:
   dd 0x32    ; OP_XOR
   dd 0x84    ; OP_TEST
   dd 0xf0    ; OP_HLT
+  dd 0xf0    ; OP_RDPMC
+  dd 0xf0    ; OP_RDMSR
+  dd 0xf0    ; OP_WRMSR
+  dd 0xf0    ; OP_CPUID
 
 r32rm32_opcode:
   dd 0xf0    ; OP_PUSH
@@ -358,6 +433,10 @@ r32rm32_opcode:
   dd 0x33    ; OP_XOR
   dd 0x85    ; OP_TEST
   dd 0xf0    ; OP_HLT
+  dd 0xf0    ; OP_RDPMC
+  dd 0xf0    ; OP_RDMSR
+  dd 0xf0    ; OP_WRMSR
+  dd 0xf0    ; OP_CPUID
 
 rm8r8_opcode:
   dd 0xf0    ; OP_PUSH
@@ -401,6 +480,10 @@ rm8r8_opcode:
   dd 0x30    ; OP_XOR
   dd 0x84    ; OP_TEST
   dd 0xf0    ; OP_HLT
+  dd 0xf0    ; OP_RDPMC
+  dd 0xf0    ; OP_RDMSR
+  dd 0xf0    ; OP_WRMSR
+  dd 0xf0    ; OP_CPUID
 
 rm32r32_opcode:
   dd 0xf0    ; OP_PUSH
@@ -444,6 +527,10 @@ rm32r32_opcode:
   dd 0x31    ; OP_XOR
   dd 0x85    ; OP_TEST
   dd 0xf0    ; OP_HLT
+  dd 0xf0    ; OP_RDPMC
+  dd 0xf0    ; OP_RDMSR
+  dd 0xf0    ; OP_WRMSR
+  dd 0xf0    ; OP_CPUID
 
 rm8imm8_opcode:
   dd 0xf0    ; OP_PUSH
@@ -487,6 +574,10 @@ rm8imm8_opcode:
   dd 0x0680  ; OP_XOR
   dd 0x00f6  ; OP_TEST
   dd 0xf0    ; OP_HLT
+  dd 0xf0    ; OP_RDPMC
+  dd 0xf0    ; OP_RDMSR
+  dd 0xf0    ; OP_WRMSR
+  dd 0xf0    ; OP_CPUID
 
 rm32imm32_opcode:
   dd 0xf0    ; OP_PUSH
@@ -530,6 +621,10 @@ rm32imm32_opcode:
   dd 0x0681  ; OP_XOR
   dd 0x00f7  ; OP_TEST
   dd 0xf0    ; OP_HLT
+  dd 0xf0    ; OP_RDPMC
+  dd 0xf0    ; OP_RDMSR
+  dd 0xf0    ; OP_WRMSR
+  dd 0xf0    ; OP_CPUID
 
 
 reg_eax:
@@ -2190,21 +2285,28 @@ process_int:
   ret
 
 
-  global process_ret
-process_ret:
-  ;; Check the operation is actually a ret
-  cmp DWORD [esp+4], OP_RET
-  jne platform_panic
+  global process_ret_like
+process_ret_like:
+  ;; Get the opcode data
+  mov eax, [esp+4]
+  mov edx, 4
+  mul edx
+  add eax, empty_opcode
+  mov ecx, [eax]
 
   ;; Check that data is empty
   mov edx, [esp+8]
   cmp BYTE [edx], 0
   jne platform_panic
 
-  ;; Call emit
-  push 0xc3
-  call emit
-  add esp, 4
+  ;; Call emit_helper
+  push 0
+  push 0xffffffff
+  push 0xffffffff
+  push 1
+  push ecx
+  call emit_helper
+  add esp, 20
 
   ret
 

+ 21 - 0
asmg/asm_opcodes.g

@@ -1991,6 +1991,27 @@ fun build_opcode_map 0 {
   opcode OPCODE_NO_OPERAND take_addr 0x0000c301 = ;
   opcode_map name opcode map_set ;
 
+  @name "rdpmc" = ;
+  @opcode SIZEOF_OPCODE malloc = ;
+  opcode OPCODE_ARG_NUM take_addr 0 = ;
+  opcode OPCODE_HANDLER take_addr @ret_like_handler = ;
+  opcode OPCODE_NO_OPERAND take_addr 0x00330f02 = ;
+  opcode_map name opcode map_set ;
+
+  @name "rdmsr" = ;
+  @opcode SIZEOF_OPCODE malloc = ;
+  opcode OPCODE_ARG_NUM take_addr 0 = ;
+  opcode OPCODE_HANDLER take_addr @ret_like_handler = ;
+  opcode OPCODE_NO_OPERAND take_addr 0x00320f02 = ;
+  opcode_map name opcode map_set ;
+
+  @name "wrmsr" = ;
+  @opcode SIZEOF_OPCODE malloc = ;
+  opcode OPCODE_ARG_NUM take_addr 0 = ;
+  opcode OPCODE_HANDLER take_addr @ret_like_handler = ;
+  opcode OPCODE_NO_OPERAND take_addr 0x00300f02 = ;
+  opcode_map name opcode map_set ;
+
   @name "clc" = ;
   @opcode SIZEOF_OPCODE malloc = ;
   opcode OPCODE_ARG_NUM take_addr 0 = ;

+ 23 - 3
asmg/fasm.g

@@ -150,8 +150,12 @@ fun fasm_display_block 2 {
   }
 }
 
+$ret_instr_enter
+
 fun error_additional_handler 0 {
-  "Additional handler!\n" 1 platform_log ;
+  "Fault happened after executing " 1 platform_log ;
+  read_ret_instr ret_instr_enter - itoa 1 platform_log ;
+  " instructions.\n" 1 platform_log ;
 }
 
 fun compile_fasm 0 {
@@ -160,6 +164,11 @@ fun compile_fasm 0 {
 
   0x10000 @error_additional_handler = ;
 
+  @ret_instr_enter read_ret_instr = ;
+  "Retired instruction counter before compiling fasm: " 1 platform_log ;
+  read_ret_instr itoa 1 platform_log ;
+  "\n" 1 platform_log ;
+
   # Compile fasm
   $ctx
   @ctx asmctx_init = ;
@@ -171,7 +180,7 @@ fun compile_fasm 0 {
   ctx asmctx_compile ;
   fd vfs_close ;
 
-  # Run fasm
+  # Prepare fasm argument list
   $input_file
   $output_file
   @input_file "/disk1/fasm/test.asm" = ;
@@ -196,13 +205,24 @@ fun compile_fasm 0 {
   handles @fasm_fatal_error vector_push_back ;
   handles @fasm_assembler_error vector_push_back ;
   handles @fasm_display_block vector_push_back ;
+
+  @ret_instr_enter read_ret_instr = ;
+  "Retired instruction counter before entering fasm: " 1 platform_log ;
+  read_ret_instr itoa 1 platform_log ;
+  "\n" 1 platform_log ;
+
+  # Run fasm
   $res
   @res handles vector_data main_addr \1 = ;
-  handles vector_destroy ;
+
+  "Retired instruction counter after exiting fasm: " 1 platform_log ;
+  read_ret_instr itoa 1 platform_log ;
+  "\n" 1 platform_log ;
 
   "fasm returned " 1 platform_log ;
   res itoa 1 platform_log ;
   "\n" 1 platform_log ;
 
+  handles vector_destroy ;
   ctx asmctx_destroy ;
 }

+ 16 - 0
asmg/kernel-asmg.asm

@@ -218,6 +218,9 @@ start:
   push ebp
   mov ebp, 0
 
+  ;; Enable PMC
+  call enable_pmc
+
   ;; Call main
   push 0
   push str_main
@@ -596,6 +599,13 @@ symbol_arities_func:
   mov eax, [eax]
   ret
 
+str_ret_instr:
+  db '__ret_instr'
+  db 0
+ret_instr:
+  call read_ret_instr
+  ret
+
 
 init_g_operations:
   push 2
@@ -838,4 +848,10 @@ init_g_operations:
   call add_symbol
   add esp, 12
 
+  push 0
+  push ret_instr
+  push str_ret_instr
+  call add_symbol
+  add esp, 12
+
   ret

+ 4 - 0
asmg/utils.g

@@ -78,6 +78,10 @@ fun dump_stacktrace 0 {
   frame_ptr dump_frame ;
 }
 
+fun read_ret_instr 0 {
+  __ret_instr ret ;
+}
+
 $assert_pos
 
 fun set_assert_pos 1 {

+ 93 - 0
lib/pmc.asm

@@ -0,0 +1,93 @@
+;; This file is part of asmc, a bootstrapping OS with minimal seed
+;; Copyright (C) 2018 Giovanni Mascellani <gio@debian.org>
+;; https://gitlab.com/giomasce/asmc
+
+;; This program is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+pmc_supported:
+  dd 0
+
+
+query_pmc:
+  ;; Call CPUID
+  mov eax, 0xa
+  cpuid
+  mov edx, pmc_supported
+  cmp al, 2
+  jb query_pmc_ret_false
+  mov DWORD [edx], 1
+
+  ;; Log
+  push str_pmc_avail
+  push 1
+  call platform_log
+  add esp, 8
+
+  ret
+
+query_pmc_ret_false:
+  mov DWORD [edx], 0
+
+  ;; Log
+  push str_pmc_unavail
+  push 1
+  call platform_log
+  add esp, 8
+
+  ret
+
+
+enable_pmc:
+  ;; Query PMC availability and return if they are not available
+  call query_pmc
+  mov edx, pmc_supported
+  cmp DWORD [edx], 0
+  je enable_pmc_ret
+
+  ;; Enable retired instruction counder (only PMC supported so far)
+  mov ecx, 0x38d
+  rdmsr
+  or eax, 0x1
+  mov ecx, 0x38d
+  wrmsr
+
+enable_pmc_ret:
+  ret
+
+
+read_ret_instr:
+  ;; Return 0 is PMC are not available
+  mov edx, pmc_supported
+  cmp DWORD [edx], 0
+  je read_ret_instr_ret0
+
+  ;; Read retiret instruction counter
+  mov ecx, 0x40000000
+  rdpmc
+  ret
+
+read_ret_instr_ret0:
+  mov eax, 0
+  mov edx, 0
+  ret
+
+
+str_pmc_avail:
+  db 'Performance counters are available and enabled!'
+  db NEWLINE
+  db 0
+str_pmc_unavail:
+  db 'Performance counters are not available...'
+  db NEWLINE
+  db 0

+ 3 - 0
test/test.asm

@@ -143,6 +143,9 @@ there:
   bsf eax, [edi]
   bsr eax, [edi]
 
+  mov ecx, 0x10000000
+  rdpmc
+
   dd there-here
   dw there-here,10,11,there-here
   dd 0x11223344