新增rust ffi (#77)

* 引入cargo

* 取消对Cargo.lock的跟踪

* 解决vscode报错问题

* new: rust的代码能够调用c语言的printk_color

* 1、将原本run.sh的工作拆解,变为几个不同的make命令
2、在docker镜像中编译rust

* 更改workflow

* update workflow

* new: 解决workflow无法通过编译的问题
This commit is contained in:
login
2022-11-11 15:35:37 +08:00
committed by GitHub
parent 5e023cf791
commit 2813126e31
271 changed files with 609 additions and 307 deletions

View File

@ -0,0 +1,290 @@
#include "HPET.h"
#include <common/kprint.h>
#include <common/compiler.h>
#include <mm/mm.h>
#include <driver/interrupt/apic/apic.h>
#include <exception/softirq.h>
#include <time/timer.h>
#include <process/process.h>
#include <sched/sched.h>
#include <smp/ipi.h>
#include <driver/video/video.h>
#include <driver/interrupt/apic/apic_timer.h>
#include <common/spinlock.h>
#pragma GCC push_options
#pragma GCC optimize("O0")
static struct acpi_HPET_description_table_t *hpet_table;
static uint64_t HPET_REG_BASE = 0;
static uint32_t HPET_COUNTER_CLK_PERIOD = 0; // 主计数器时间精度(单位:飞秒)
static uint64_t HPET_freq = 0; // 主计时器频率
static uint8_t HPET_NUM_TIM_CAP = 0; // 定时器数量
static char measure_apic_timer_flag; // 初始化apic时钟时所用到的标志变量
// 测定tsc频率的临时变量
static uint64_t test_tsc_start = 0;
static uint64_t test_tsc_end = 0;
extern uint64_t Cpu_tsc_freq; // 导出自cpu.c
extern struct rtc_time_t rtc_now; // 导出全局墙上时钟
enum
{
GCAP_ID = 0x00,
GEN_CONF = 0x10,
GINTR_STA = 0x20,
MAIN_CNT = 0xf0,
TIM0_CONF = 0x100,
TIM0_COMP = 0x108,
TIM1_CONF = 0x120,
TIM1_COMP = 0x128,
TIM2_CONF = 0x140,
TIM2_COMP = 0x148,
TIM3_CONF = 0x160,
TIM3_COMP = 0x168,
TIM4_CONF = 0x180,
TIM4_COMP = 0x188,
TIM5_CONF = 0x1a0,
TIM5_COMP = 0x1a8,
TIM6_CONF = 0x1c0,
TIM6_COMP = 0x1c8,
TIM7_CONF = 0x1e0,
TIM7_COMP = 0x1e8,
};
hardware_intr_controller HPET_intr_controller =
{
.enable = apic_ioapic_enable,
.disable = apic_ioapic_disable,
.install = apic_ioapic_install,
.uninstall = apic_ioapic_uninstall,
.ack = apic_ioapic_edge_ack,
};
void HPET_handler(uint64_t number, uint64_t param, struct pt_regs *regs)
{
// printk("(HPET)");
switch (param)
{
case 0: // 定时器0中断
timer_jiffies += HPET0_INTERVAL;
/*
// 将HEPT中断消息转发到ap:1处理器
ipi_send_IPI(DEST_PHYSICAL, IDLE, ICR_LEVEL_DE_ASSERT, EDGE_TRIGGER, 0xc8,
ICR_APIC_FIXED, ICR_ALL_EXCLUDE_Self, true, 0);
*/
// 若当前时间比定时任务的时间间隔大,则进入中断下半部
if (container_of(list_next(&timer_func_head.list), struct timer_func_list_t, list)->expire_jiffies <= timer_jiffies)
raise_softirq(TIMER_SIRQ);
// 当时间到了,或进程发生切换时,刷新帧缓冲区
if (timer_jiffies >= video_refresh_expire_jiffies || (video_last_refresh_pid != current_pcb->pid))
{
raise_softirq(VIDEO_REFRESH_SIRQ);
// 超过130ms仍未刷新完成则重新发起刷新(防止由于进程异常退出导致的屏幕无法刷新)
if (unlikely(timer_jiffies >= (video_refresh_expire_jiffies + (1 << 17))))
{
video_refresh_expire_jiffies = timer_jiffies + (1 << 20);
clear_softirq_pending(VIDEO_REFRESH_SIRQ);
}
}
break;
default:
kwarn("Unsupported HPET irq: %d.", number);
break;
}
}
/**
* @brief 测定apic定时器以及tsc的频率的中断回调函数
*
*/
void HPET_measure_handler(uint64_t number, uint64_t param, struct pt_regs *regs)
{
test_tsc_end = rdtsc();
// 停止apic定时器
// 写入每1ms的ticks
apic_timer_stop();
apic_timer_ticks_result = 0xFFFFFFFF - apic_timer_get_current();
measure_apic_timer_flag = true;
}
/**
* @brief 测定apic定时器以及tsc的频率
*
*/
void HPET_measure_freq()
{
kinfo("Measuring local APIC timer's frequency...");
const uint64_t interval = APIC_TIMER_INTERVAL; // 测量给定时间内的计数
struct apic_IO_APIC_RTE_entry entry;
// 使用I/O APIC 的IRQ2接收hpet定时器0的中断
apic_make_rte_entry(&entry, 34, IO_APIC_FIXED, DEST_PHYSICAL, IDLE, POLARITY_HIGH, IRR_RESET, EDGE_TRIGGER, MASKED, 0);
// 计算HPET0间隔多少个时钟周期触发一次中断
uint64_t clks_to_intr = 0.001 * interval * HPET_freq;
// kdebug("clks_to_intr=%#ld", clks_to_intr);
if (clks_to_intr <= 0 || clks_to_intr > (HPET_freq * 8))
{
kBUG("HPET0: Numof clocks to generate interrupt is INVALID! value=%lld", clks_to_intr);
while (1)
hlt();
}
__write8b(HPET_REG_BASE + MAIN_CNT, 0);
io_mfence();
__write8b((HPET_REG_BASE + TIM0_CONF), 0x0044); // 设置定时器0为非周期边沿触发默认投递到IO APIC的2号引脚
io_mfence();
__write8b(HPET_REG_BASE + TIM0_COMP, clks_to_intr);
io_mfence();
measure_apic_timer_flag = false;
// 注册中断
irq_register(34, &entry, &HPET_measure_handler, 0, &HPET_intr_controller, "HPET0 measure");
sti();
// 设置div16
apic_timer_stop();
apic_timer_set_div(APIC_TIMER_DIVISOR);
// 设置初始计数
apic_timer_set_init_cnt(0xFFFFFFFF);
// 启动apic定时器
apic_timer_set_LVT(151, 0, APIC_LVT_Timer_One_Shot);
__write8b(HPET_REG_BASE + GEN_CONF, 3); // 置位旧设备中断路由兼容标志位、定时器组使能标志位,开始计时
// 顺便测定tsc频率
test_tsc_start = rdtsc();
io_mfence();
while (measure_apic_timer_flag == false)
;
irq_unregister(34);
*(uint64_t *)(HPET_REG_BASE + GEN_CONF) = 0; // 停用HPET定时器
io_mfence();
kinfo("Local APIC timer's freq: %d ticks/ms.", apic_timer_ticks_result);
// 计算tsc频率
Cpu_tsc_freq = (test_tsc_end - test_tsc_start) * (1000UL / interval);
kinfo("TSC frequency: %ldMHz", Cpu_tsc_freq / 1000000);
}
/**
* @brief 启用HPET周期中断5ms
*
*/
void HPET_enable()
{
struct apic_IO_APIC_RTE_entry entry;
// 使用I/O APIC 的IRQ2接收hpet定时器0的中断
apic_make_rte_entry(&entry, 34, IO_APIC_FIXED, DEST_PHYSICAL, IDLE, POLARITY_HIGH, IRR_RESET, EDGE_TRIGGER, MASKED, 0);
// 计算HPET0间隔多少个时钟周期触发一次中断
uint64_t clks_to_intr = 0.000001 * HPET0_INTERVAL * HPET_freq;
// kdebug("clks_to_intr=%#ld", clks_to_intr);
if (clks_to_intr <= 0 || clks_to_intr > (HPET_freq * 8))
{
kBUG("HPET0: Numof clocks to generate interrupt is INVALID! value=%lld", clks_to_intr);
while (1)
hlt();
}
// kdebug("[HPET0] conf register=%#018lx conf register[63:32]=%#06lx", (*(uint64_t *)(HPET_REG_BASE + TIM0_CONF)), ((*(uint64_t *)(HPET_REG_BASE + TIM0_CONF))>>32)&0xffffffff);
__write8b(HPET_REG_BASE + MAIN_CNT, 0);
io_mfence();
__write8b(HPET_REG_BASE + TIM0_CONF, 0x004c); // 设置定时器0为周期定时边沿触发默认投递到IO APIC的2号引脚(看conf寄存器的高32bit哪一位被置1则可以投递到哪一个I/O apic引脚)
io_mfence();
__write8b(HPET_REG_BASE + TIM0_COMP, clks_to_intr);
io_mfence();
// kdebug("[HPET0] conf register after modify=%#018lx", ((*(uint64_t *)(HPET_REG_BASE + TIM0_CONF))));
// kdebug("[HPET1] conf register =%#018lx", ((*(uint64_t *)(HPET_REG_BASE + TIM1_CONF))));
rtc_get_cmos_time(&rtc_now);
kinfo("HPET0 enabled.");
__write8b(HPET_REG_BASE + GEN_CONF, 3); // 置位旧设备中断路由兼容标志位、定时器组使能标志位
io_mfence();
// 注册中断
irq_register(34, &entry, &HPET_handler, 0, &HPET_intr_controller, "HPET0");
}
int HPET_init()
{
kinfo("Initializing HPET...");
// 从acpi获取hpet结构体
ul hpet_table_addr = 0;
acpi_iter_SDT(acpi_get_HPET, &hpet_table_addr);
// ACPI表没有HPET尝试读HPTC
if (hpet_table_addr == 0)
{
kwarn("ACPI: HPET Table Not Found On This Computer!");
if (RCBA_vaddr != 0)
{
kerror("NO HPET found on this computer!");
uint64_t hptc_vaddr = (RCBA_vaddr + 0x3404UL);
// enable HPET
io_mfence();
// 读取HPET配置寄存器地址
switch (__read4b(hptc_vaddr) & 0x3)
{
case 0:
HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + 0xfed00000;
break;
case 1:
HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + 0xfed01000;
break;
case 2:
HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + 0xfed02000;
break;
case 3:
HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + 0xfed03000;
break;
default:
break;
}
// enable HPET
__write4b(hptc_vaddr, 0x80);
io_mfence();
}
else
{
// 没有RCBA寄存器采用默认值
HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + 0xfed00000;
kwarn("There is no RCBA register on this computer, and HPET regs base use default value.");
}
}
else // ACPI表中有HPET表
{
hpet_table = (struct acpi_HPET_description_table_t *)hpet_table_addr;
// kdebug("hpet_table_addr=%#018lx", hpet_table_addr);
// 由于这段内存与io/apic的映射在同一物理页内因此不需要重复映射
HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + hpet_table->address;
kdebug("hpet_table->address=%#018lx", hpet_table->address);
}
kdebug("HPET_REG_BASE=%#018lx", HPET_REG_BASE);
// 读取计时精度并计算频率
uint64_t tmp;
tmp = __read8b(HPET_REG_BASE + GCAP_ID);
HPET_COUNTER_CLK_PERIOD = (tmp >> 32) & 0xffffffff;
HPET_freq = 1e15 / HPET_COUNTER_CLK_PERIOD;
HPET_NUM_TIM_CAP = (tmp >> 8) & 0x1f; // 读取计时器数量
kdebug("HPET_COUNTER_CLK_PERIOD=%#018lx", HPET_COUNTER_CLK_PERIOD);
kinfo("Total HPET timers: %d", HPET_NUM_TIM_CAP);
kinfo("HPET driver Initialized.");
}
#pragma GCC pop_options

View File

@ -0,0 +1,22 @@
#pragma once
#include <common/glib.h>
#include <driver/acpi/acpi.h>
#include <driver/timers/rtc/rtc.h>
#define E_HPET_INIT_FAILED 1
#define HPET0_INTERVAL 500 // HPET0定时器的中断间隔为500us
int HPET_init();
/**
* @brief 测定apic定时器以及tsc的频率
*
*/
void HPET_measure_freq();
/**
* @brief 启用HPET周期中断5ms
*
*/
void HPET_enable();

View File

@ -0,0 +1,10 @@
all: rtc.o HPET.o
CFLAGS += -I .
rtc.o: rtc/rtc.c
$(CC) $(CFLAGS) -c rtc/rtc.c -o rtc/rtc.o
HPET.o: HPET/HPET.c
$(CC) $(CFLAGS) -c HPET/HPET.c -o HPET/HPET.o

View File

@ -0,0 +1,58 @@
#include "rtc.h"
#include <common/kprint.h>
/*置位0x70的第7位禁止不可屏蔽中断*/
#define read_cmos(addr) ({ \
io_out8(0x70, 0x80 | addr); \
io_in8(0x71); \
})
enum CMOSTimeSelector
{
T_SECOND = 0x0,
T_MINUTE = 0x2,
T_HOUR = 0x4,
T_DAY = 0x7,
T_MONTH = 0x8,
T_YEAR = 0x9,
};
int rtc_get_cmos_time(struct rtc_time_t *t)
{
// 为防止中断请求打断该过程,需要先关中断
cli();
uint8_t status_register_B = read_cmos(0x0B); // 读取状态寄存器B
bool is_24h = ((status_register_B & 0x02) ? true : false); // 判断是否启用24小时模式
bool is_binary = ((status_register_B & 0x04) ? true : false); // 判断是否为二进制码
do
{
t->year = read_cmos(0x09);
t->month = read_cmos(0x08);
t->day = read_cmos(0x07);
t->hour = read_cmos(0x04);
t->minute = read_cmos(0x02);
t->second = read_cmos(0x00);
} while (t->second != read_cmos(0x00)); // 若读取时间过程中时间发生跳变则重新读取
// 使能NMI中断
io_out8(0x70, 0x00);
if (!is_binary) // 把BCD转为二进制
{
t->second = (t->second & 0xf) + (t->second >> 4) * 10;
t->minute = (t->minute & 0xf) + (t->minute >> 4) * 10;
t->hour = ((t->hour & 0xf) + ((t->hour & 0x70) >> 4) * 10) | (t->hour & 0x80);
t->month = (t->month & 0xf) + (t->month >> 4) * 10;
t->year = (t->year & 0xf) + (t->year >> 4) * 10;
}
t->year += 2000;
if ((!is_24h) && t->hour & 0x80) // 将十二小时制转为24小时
t->hour = ((t->hour & 0x7f) + 12) % 24;
sti();
return 0;
}

View File

@ -0,0 +1,21 @@
#pragma once
#include <common/glib.h>
struct rtc_time_t
{
int second;
int minute;
int hour;
int day;
int month;
int year;
}rtc_now; // rtc_now为墙上时钟由HPET定时器0维护
/**
* @brief 从主板cmos中获取时间
*
* @param t time结构体
* @return int 成功则为0
*/
int rtc_get_cmos_time(struct rtc_time_t*t);
void rtc_init();