Skip to content

Commit

Permalink
✨131 CPU 功能检测
Browse files Browse the repository at this point in the history
  • Loading branch information
StevenBaby committed Apr 15, 2023
1 parent 23235f1 commit e637407
Show file tree
Hide file tree
Showing 7 changed files with 226 additions and 30 deletions.
37 changes: 37 additions & 0 deletions docs/09 设备驱动/131 CPU 功能检测.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# CPU 功能检测

## CPUID

`CPUID` [^osdev] 指令可以用于获取很多 CPU 的信息,比如供应商(Vendor) 字符串,和模型数,内部缓存大小,以及很多其他有趣的东西。

EFLAGS 位图:

![](../04%20中断和时钟/images/eflags.drawio.svg)

## 功能检测

在执行 `CPUID` 指令之前,首先应该检测处理器是否支持该指令,如果 EFLAGS 的 ID 位可修改,那么表示处理器支持 `CPUID` 指令。

## 基础用法

执行 `CPUID` 指令前,将参数 `0` 传入 `EAX`,不同的参数将返回不同的信息。

### 供应商 ID

`EAX=0` 时,CPUID 将返回供应商 ID 字符串到 `EBX`, `EDX``ECX`,将它们写入内存将得到长度 12 的字符串。这个字符串表示了供应商的 ID。下面是典型的供应商 ID:

```c++
#define CPUID_VENDOR_AMD "AuthenticAMD"
#define CPUID_VENDOR_INTEL "GenuineIntel"
```
`EAX` 中返回最大的输入数;
### CPU 功能
使用参数 `EAX` = 1 调用 CPUID,返回一个位图存储在 `EDX` 和 `ECX` 中 [^info]。注意不同品牌的 CPU 可能有不同的含义。
## 参考
[^osdev]: https://wiki.osdev.org/CPUID
[^info]: Intel® 64 and IA-32 Architectures Software Developer's Manual
4 changes: 4 additions & 0 deletions src/builtin/osh.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ void builtin_logo()

void builtin_test(int argc, char *argv[])
{
test();
}

void builtin_pwd()
Expand Down Expand Up @@ -556,6 +557,9 @@ static int signal_handler(int sig)

int main()
{
// 方便调试,直接调用测试
builtin_test(0, NULL);

// 注册信号 CTRL + C
signal(SIGINT, (int)signal_handler);

Expand Down
108 changes: 108 additions & 0 deletions src/include/onix/cpu.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#ifndef ONIX_CPU_H
#define ONIX_CPU_H

#include <onix/types.h>

// Vendor strings from CPUs.
#define CPUID_VENDOR_AMD "AuthenticAMD"
#define CPUID_VENDOR_INTEL "GenuineIntel"

bool cpu_check_cpuid();

typedef struct cpu_vendor_t
{
u32 max_value;
char info[13];
} _packed cpu_vendor_t;

void cpu_vendor_id(cpu_vendor_t *item);

typedef struct cpu_version_t
{
u8 stepping : 4;
u8 model : 4;
u8 family : 4;
u8 type : 2;
u8 RESERVED : 2;
u8 emodel : 4;
u8 efamily0 : 4;
u8 efamily1 : 4;
u8 RESERVED : 4;

u8 brand_index;
u8 clflush;
u8 max_num;
u8 apic_id;

// ECX;
u8 SSE3 : 1; // 0
u8 PCLMULQDQ : 1; // 1
u8 DTES64 : 1; // 2
u8 MONITOR : 1; // 3
u8 DS_CPL : 1; // 4
u8 VMX : 1; // 5
u8 SMX : 1; // 6
u8 EIST : 1; // 7
u8 TM2 : 1; // 8
u8 SSSE3 : 1; // 9
u8 CNXT_ID : 1; // 10
u8 SDBG : 1; // 11
u8 FMA : 1; // 12
u8 CMPXCHG16B : 1; // 13
u8 xTPR : 1; // 14
u8 PDCM : 1; // 15
u8 RESERVED : 1; // 16
u8 PCID : 1; // 17
u8 DCA : 1; // 18
u8 SSE4_1 : 1; // 19
u8 SSE4_2 : 1; // 20
u8 x2APIC : 1; // 21
u8 MOVBE : 1; // 22
u8 POPCNT : 1; // 23
u8 TSCD : 1; // 24
u8 AESNI : 1; // 25
u8 XSAVE : 1; // 26
u8 OSXSAVE : 1; // 27
u8 AVX : 1; // 28
u8 F16C : 1; // 29
u8 RDRAND : 1; // 30
u8 RESERVED : 1; // 31

// EDX
u8 FPU : 1; // 0 x87 FPU on Chip
u8 VME : 1; // 1 Virtual-8086 Mode Enhancement
u8 DE : 1; // 2 Debugging Extensions
u8 PSE : 1; // 3 Page Size Extensions
u8 TSC : 1; // 4 Time Stamp Counter
u8 MSR : 1; // 5 RDMSR and WRMSR Support
u8 PAE : 1; // 6 Physical Address Extensions
u8 MCE : 1; // 7 Machine Check Exception
u8 CX8 : 1; // 8 CMPXCHG8B Inst.
u8 APIC : 1; // 9 APIC on Chip
u8 RESERVED : 1; // 10
u8 SEP : 1; // 11 SYSENTER and SYSEXIT
u8 MTRR : 1; // 12 Memory Type Range Registers
u8 PGE : 1; // 13 PTE Global Bit
u8 MCA : 1; // 14 Machine Check Architecture
u8 CMOV : 1; // 15 Conditional Move/Compare Instruction
u8 PAT : 1; // 16 Page Attribute Table
u8 PSE36 : 1; // 17 Page Size Extension
u8 PSN : 1; // 18 Processor Serial Number
u8 CLFSH : 1; // 19 CLFLUSH instruction
u8 RESERVED : 1; // 20
u8 DS : 1; // 21 Debug Store
u8 ACPI : 1; // 22 Thermal Monitor and Clock Ctrl
u8 MMX : 1; // 23 MMX Technology
u8 FXSR : 1; // 24 FXSAVE/FXRSTOR
u8 SSE : 1; // 25 SSE Extensions
u8 SSE2 : 1; // 26 SSE2 Extensions
u8 SS : 1; // 27 Self Snoop
u8 HTT : 1; // 28 Multi-threading
u8 TM : 1; // 29 Therm. Monitor
u8 RESERVED : 1; // 30
u8 PBE : 1; // 31 Pend. Brk. EN.
} _packed cpu_version_t;

void cpu_version(cpu_version_t *ver);

#endif
47 changes: 47 additions & 0 deletions src/kernel/cpu.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include <onix/cpu.h>

// 检测是否支持 cpuid 指令
bool cpu_check_cpuid()
{
bool ret;
asm volatile(
"pushfl \n" // 保存 eflags

"pushfl \n" // 得到 eflags
"xorl $0x00200000, (%%esp)\n" // 反转 ID 位
"popfl\n" // 写入 eflags

"pushfl\n" // 得到 eflags
"popl %%eax\n" // 写入 eax
"xorl (%%esp), %%eax\n" // 将写入的值与原值比较
"andl $0x00200000, %%eax\n" // 得到 ID 位
"shrl $21, %%eax\n" // 右移 21 位,得到是否支持

"popfl\n" // 恢复 eflags
: "=a"(ret));
return ret;
}

// 得到供应商 ID 字符串
void cpu_vendor_id(cpu_vendor_t *item)
{
asm volatile(
"cpuid \n"
: "=a"(*((u32 *)item + 0)),
"=b"(*((u32 *)item + 1)),
"=d"(*((u32 *)item + 2)),
"=c"(*((u32 *)item + 3))
: "a"(0));
item->info[12] = 0;
}

void cpu_version(cpu_version_t *item)
{
asm volatile(
"cpuid \n"
: "=a"(*((u32 *)item + 0)),
"=b"(*((u32 *)item + 1)),
"=c"(*((u32 *)item + 2)),
"=d"(*((u32 *)item + 3))
: "a"(1));
}
33 changes: 3 additions & 30 deletions src/kernel/gate.c
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
#include <onix/interrupt.h>
#include <onix/task.h>
#include <onix/syscall.h>
#include <onix/assert.h>
#include <onix/debug.h>
#include <onix/syscall.h>
#include <onix/task.h>
#include <onix/console.h>
#include <onix/memory.h>
#include <onix/device.h>
#include <onix/string.h>
#include <onix/buffer.h>
#include <onix/fs.h>

#define LOGK(fmt, args...) DEBUGK(fmt, ##args)

Expand All @@ -29,28 +23,7 @@ static void sys_default()
panic("syscall not implemented!!!");
}

static u32 sys_test()
{
char ch;
device_t *device;

device_t *serial = device_find(DEV_SERIAL, 0);
assert(serial);

device_t *keyboard = device_find(DEV_KEYBOARD, 0);
assert(keyboard);

device_t *console = device_find(DEV_CONSOLE, 0);
assert(console);

device_read(serial->dev, &ch, 1, 0, 0);
// device_read(keyboard->dev, &ch, 1, 0, 0);

device_write(serial->dev, &ch, 1, 0, 0);
device_write(console->dev, &ch, 1, 0, 0);

return 255;
}
extern int sys_test();

extern void sys_execve();
extern int sys_kill();
Expand Down
25 changes: 25 additions & 0 deletions src/kernel/test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include <onix/types.h>
#include <onix/cpu.h>
#include <onix/printk.h>
#include <onix/debug.h>
#include <onix/errno.h>

#define LOGK(fmt, args...) DEBUGK(fmt, ##args)

err_t sys_test()
{
LOGK("test syscall...\n");

cpu_vendor_t vendor;

cpu_vendor_id(&vendor);
printk("CPU vendor id: %s\n", vendor.info);
printk("CPU max value: 0x%X\n", vendor.max_value);

cpu_version_t ver;

cpu_version(&ver);
printk("FPU support state: %d\n", ver.FPU);
printk("APIC support state: %d\n", ver.APIC);
return EOK;
}
2 changes: 2 additions & 0 deletions src/makefile
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ $(BUILD)/kernel.bin: \
$(BUILD)/kernel/execve.o \
$(BUILD)/kernel/signal.o \
$(BUILD)/kernel/alarm.o \
$(BUILD)/kernel/cpu.o \
$(BUILD)/kernel/test.o \
$(BUILD)/fs/super.o \
$(BUILD)/fs/bmap.o \
$(BUILD)/fs/inode.o \
Expand Down

0 comments on commit e637407

Please sign in to comment.