Re: [PATCH v6 7/9] hv: vrtc calibrate function
Junjie Mao
Yuanyuan Zhao <yuanyuan.zhao@...> writes:
For TSC's precision (20~100ppm) is lower than physical RTC (less than 20ppm),Reviewed-by: Junjie Mao <junjie.mao@...> ---
|
|
Re: [PATCH v6 4/9] hv: add vRTC reg_b and reg_c support
Junjie Mao
Yuanyuan Zhao <yuanyuan.zhao@...> writes:
During setting RTC time, driver will halt RTC update. So supportReviewed-by: Junjie Mao <junjie.mao@...> -- Best Regards Junjie Mao ---
|
|
[PATCH v3] misc: add vUART channel for s5 feature
chenli.wei
"S5" is one of the ACPI sleep states which means the function to shut
down the VMs. On ACRN, the User VM must be shut down before powering off the Service VM, so we need a vUART channel to communicate between the Service VM and User VMs. This patch adds a vUART channel for each User VM connect to Service VM v2-->v3: 1.split the define of MAX_VUART_NUM_PER_VM to another patch, this patch will focus on the S5 feature only v1-->v2: 1.move the define of MAX_VUART_NUM_PER_VM to offline tool Tracked-On: #8782 Signed-off-by: Chenli Wei <chenli.wei@...> --- .../service_vm_config/serial_config.py | 30 +++++------ misc/config_tools/static_allocators/intx.py | 30 +++++++++-- .../xforms/vm_configurations.c.xsl | 51 +++++++++++++++++-- 3 files changed, 88 insertions(+), 23 deletions(-) diff --git a/misc/config_tools/service_vm_config/serial_config.py b/misc/config_tools/service_vm_config/serial_config.py index fda214c07..3c53c42c1 100644 --- a/misc/config_tools/service_vm_config/serial_config.py +++ b/misc/config_tools/service_vm_config/serial_config.py @@ -17,7 +17,7 @@ VUART_DEV_NAME_NUM = 8 stadard_uart_port = {'0x3F8', '0x2F8', '0x3E8', '0x2E8'} UART_IRQ_BAUD = " irq 0 uart 16550A baud_base 115200" -def find_non_standard_uart(vm, scenario_etree): +def find_non_standard_uart(vm, scenario_etree, allocation_etree): uart_list = [] vmname = common.get_node("./name/text()", vm) @@ -30,7 +30,17 @@ def find_non_standard_uart(vm, scenario_etree): port = common.get_node(f".//endpoint[vm_name = '{vmname}']/io_port/text()", connection) if port not in stadard_uart_port: - uart_list.append(connection) + target_vm_name = common.get_node(f".//endpoint[vm_name != '{vmname}']/vm_name/text()", connection) + target_vm_id = common.get_node(f"//vm[name = '{target_vm_name}']/@id", scenario_etree) + uart_list.append({"io_port" : port, "target_vm_id" : target_vm_id}) + + legacy_uart_list = allocation_etree.xpath(f"//vm[load_order = 'SERVICE_VM']/legacy_vuart") + for legacy_uart in legacy_uart_list: + port = common.get_node(f"./addr.port_base/text()", legacy_uart) + if port is None: + continue + elif port not in stadard_uart_port: + uart_list.append({"io_port" : port, "target_vm_id": common.get_node(f"./t_vuart.vm_id/text()", legacy_uart)}) return uart_list @@ -45,23 +55,13 @@ def main(args): vm_list = scenario_etree.xpath("//vm[load_order = 'SERVICE_VM']") for vm in vm_list: - vmname = common.get_node("./name/text()", vm) - for connection in scenario_etree.xpath(f"//vuart_connection[endpoint/vm_name = '{vmname}']"): - vm_name = common.get_node(f".//endpoint[vm_name != '{vmname}']/vm_name/text()", connection) - for target_vm in scenario_etree.xpath(f"//vm[name = '{vm_name}']"): - vuart_target_vmid[connection.find('name').text] = target_vm.attrib["id"] - - vm_list = scenario_etree.xpath("//vm[load_order = 'SERVICE_VM']") - for vm in vm_list: - vuart_list = find_non_standard_uart(vm, scenario_etree) + vuart_list = find_non_standard_uart(vm, scenario_etree, allocation_etree) vmname = common.get_node("./name/text()", vm) if len(vuart_list) != 0: with open(args.out, "w+") as config_f: for uart_start_num, vuart in enumerate(vuart_list, start=START_VUART_DEV_NAME_NO): - port = common.get_node(f".//endpoint[vm_name = '{vmname}']/io_port/text()", vuart) - base = " port " + str(port) - connection_name = vuart.find('name').text - vm_id_note = "# User_VM_id: " + str(vuart_target_vmid[connection_name]) + '\n' + base = " port " + vuart["io_port"] + vm_id_note = "# User_VM_id: " + str(vuart["target_vm_id"])+ '\n' config_f.write(vm_id_note) conf = "/dev/ttyS" + str(uart_start_num) + base + UART_IRQ_BAUD + '\n' config_f.write(conf) diff --git a/misc/config_tools/static_allocators/intx.py b/misc/config_tools/static_allocators/intx.py index b36d89eb0..559ebe86d 100644 --- a/misc/config_tools/static_allocators/intx.py +++ b/misc/config_tools/static_allocators/intx.py @@ -55,11 +55,33 @@ def alloc_vuart_connection_irqs(board_etree, scenario_etree, allocation_etree): hv_debug_console = lib.lib.parse_hv_console(scenario_etree) vm_node_list = scenario_etree.xpath("//vm") + user_vm_list = scenario_etree.xpath("//vm[load_order != 'SERVICE_VM']/name/text()") + service_vm_id = common.get_node(f"//vm[load_order = 'SERVICE_VM']/@id", scenario_etree) + + native_irq_list = get_native_valid_irq() + for index in range(0, len(user_vm_list)): + vuart_id = index + 1 + + legacy_vuart_irq = alloc_irq(native_irq_list) + create_vuart_irq_node(allocation_etree, service_vm_id, "SERVICE_VM", str(vuart_id), legacy_vuart_irq) + for vm_node in vm_node_list: load_order = common.get_node("./load_order/text()", vm_node) - irq_list = get_native_valid_irq() if load_order == "SERVICE_VM" else [f"{d}" for d in list(range(1,15))] - vuart_id = '1' + irq_list = native_irq_list if load_order == "SERVICE_VM" else [f"{d}" for d in list(range(1,15))] + + if load_order != "SERVICE_VM": + vuart_id = 1 + else: + vuart_id = 1 + len(user_vm_list) + vmname = common.get_node("./name/text()", vm_node) + + #Allocate irq for S5 vuart + if load_order != "SERVICE_VM": + legacy_vuart_irq = alloc_irq(irq_list) + create_vuart_irq_node(allocation_etree, common.get_node("./@id", vm_node), load_order, str(vuart_id), legacy_vuart_irq) + vuart_id = vuart_id + 1 + vuart_connections = scenario_etree.xpath("//vuart_connection") for connection in vuart_connections: endpoint_list = connection.xpath(".//endpoint") @@ -67,8 +89,8 @@ def alloc_vuart_connection_irqs(board_etree, scenario_etree, allocation_etree): vm_name = common.get_node("./vm_name/text()",endpoint) if vm_name == vmname: legacy_vuart_irq = alloc_irq(irq_list) - create_vuart_irq_node(allocation_etree, common.get_node("./@id", vm_node), load_order, vuart_id, legacy_vuart_irq) - vuart_id = str(int(vuart_id) + 1) + create_vuart_irq_node(allocation_etree, common.get_node("./@id", vm_node), load_order, str(vuart_id), legacy_vuart_irq) + vuart_id = vuart_id + 1 def get_irqs_of_device(device_node): irqs = set() diff --git a/misc/config_tools/xforms/vm_configurations.c.xsl b/misc/config_tools/xforms/vm_configurations.c.xsl index b16565961..539f0f23e 100644 --- a/misc/config_tools/xforms/vm_configurations.c.xsl +++ b/misc/config_tools/xforms/vm_configurations.c.xsl @@ -117,6 +117,7 @@ <xsl:apply-templates select="os_config" /> <xsl:call-template name="acpi_config" /> <xsl:apply-templates select="console_vuart" /> + <xsl:call-template name="s5_vuart" /> <xsl:call-template name="vuart_connection" /> <xsl:call-template name="pci_dev_num" /> <xsl:call-template name="pci_devs" /> @@ -282,13 +283,55 @@ <xsl:value-of select="$newline" /> </xsl:template> + <xsl:template name="s5_vuart"> + <xsl:variable name="vm_id" select="@id" /> + <xsl:variable name="vm_name" select="name/text()" /> + <xsl:variable name="load_order" select="load_order/text()" /> + <xsl:choose> + <xsl:when test="acrn:is-service-vm($load_order)"> + <xsl:for-each select="//vm[load_order != 'SERVICE_VM']/name/text()"> + <xsl:variable name="vuart_id" select="position()" /> + <xsl:value-of select="acrn:initializer(concat('vuart[', $vuart_id, ']'), '{', true())" /> + <xsl:value-of select="acrn:initializer('type', 'VUART_LEGACY_PIO')" /> + <xsl:value-of select="acrn:initializer('irq', concat(//allocation-data/acrn-config/vm[@id=$vm_id]/legacy_vuart[@id=$vuart_id]/irq, 'U'))" /> + <xsl:value-of select="acrn:initializer('addr.port_base', concat(//allocation-data/acrn-config/vm[@id=$vm_id]/legacy_vuart[@id=$vuart_id]/addr.port_base, 'U'))" /> + <xsl:value-of select="acrn:initializer('t_vuart.vm_id', concat(//allocation-data/acrn-config/vm[@id=$vm_id]/legacy_vuart[@id=$vuart_id]/t_vuart.vm_id, 'U'))" /> + <xsl:value-of select="acrn:initializer('t_vuart.vuart_id', concat(//allocation-data/acrn-config/vm[@id=$vm_id]/legacy_vuart[@id=$vuart_id]/t_vuart.vuart_id, 'U'))" /> + <xsl:text>},</xsl:text> + </xsl:for-each> + </xsl:when> + <xsl:otherwise> + <xsl:variable name="vuart_id" select="1" /> + <xsl:value-of select="acrn:initializer(concat('vuart[', $vuart_id, ']'), '{', true())" /> + <xsl:value-of select="acrn:initializer('type', 'VUART_LEGACY_PIO')" /> + <xsl:value-of select="acrn:initializer('irq', concat(//allocation-data/acrn-config/vm[@id=$vm_id]/legacy_vuart[@id=$vuart_id]/irq, 'U'))" /> + <xsl:value-of select="acrn:initializer('addr.port_base', concat(//allocation-data/acrn-config/vm[@id=$vm_id]/legacy_vuart[@id=$vuart_id]/addr.port_base, 'U'))" /> + <xsl:value-of select="acrn:initializer('t_vuart.vm_id', concat(//allocation-data/acrn-config/vm[@id=$vm_id]/legacy_vuart[@id=$vuart_id]/t_vuart.vm_id, 'U'))" /> + <xsl:value-of select="acrn:initializer('t_vuart.vuart_id', concat(//allocation-data/acrn-config/vm[@id=$vm_id]/legacy_vuart[@id=$vuart_id]/t_vuart.vuart_id, 'U'))" /> + <xsl:text>},</xsl:text> + </xsl:otherwise> + </xsl:choose> + </xsl:template> + <xsl:template name="vuart_connection"> <xsl:variable name="vm_id" select="@id" /> - <xsl:variable name="vmname" select="name/text()" /> - <xsl:for-each select="//vuart_connection[endpoint/vm_name = $vmname]"> + <xsl:variable name="vm_name" select="name/text()" /> + <xsl:variable name="load_order" select="load_order/text()" /> + <xsl:for-each select="//vuart_connection[endpoint/vm_name = $vm_name]"> <xsl:variable name="connection_name" select="name/text()" /> <xsl:variable name="type" select="type/text()" /> - <xsl:variable name="vuart_id" select="position()"/> + <xsl:variable name="vuart_id"> + <xsl:choose> + <xsl:when test="acrn:is-service-vm($load_order)"> + <xsl:variable name="pre_launched_num" select="count(//vm[load_order = 'PRE_LAUNCHED_VM']/name/text())" /> + <xsl:variable name="post_launched_num" select="count(//vm[load_order = 'POST_LAUNCHED_VM']/name/text())" /> + <xsl:value-of select="position() + $pre_launched_num + $post_launched_num" /> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="position() + 1" /> + </xsl:otherwise> + </xsl:choose> + </xsl:variable> <xsl:value-of select="acrn:initializer(concat('vuart[', $vuart_id, ']'), '{', true())" /> <xsl:choose> <xsl:when test="$type = 'legacy'"> @@ -301,7 +344,7 @@ <xsl:value-of select="acrn:initializer('irq', concat(//allocation-data/acrn-config/vm[@id=$vm_id]/legacy_vuart[@id=$vuart_id]/irq, 'U'))" /> <xsl:for-each select="endpoint"> <xsl:choose> - <xsl:when test="vm_name = $vmname"> + <xsl:when test="vm_name = $vm_name"> <xsl:choose> <xsl:when test="$type = 'legacy'"> <xsl:value-of select="acrn:initializer('addr.port_base', concat(io_port, 'U'))" /> -- 2.17.1
|
|
[PATCH v6 6/9] hv: add time modification of vrtc
Zhao, Yuanyuan
VRTC for hv used to ignore writes to vRTC register.
This patch add time modification to vRTC. Add base RTC time and TSC offset to get the current time. Convert current time to vRTC register values (`struct rtcdev`). Then modify a register, and calculate a new time. Get RTC offset by substrcting new time from current time. Thereafter, add RTC offset also when get current time. Then user can get the modified time. Signed-off-by: Yuanyuan Zhao <yuanyuan.zhao@...> Reviewed-by: Junjie Mao <junjie.mao@...> --- hypervisor/dm/vrtc.c | 14 +++++++++++++- hypervisor/include/dm/vrtc.h | 2 ++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/hypervisor/dm/vrtc.c b/hypervisor/dm/vrtc.c index cb1de7423..9157a8fc2 100644 --- a/hypervisor/dm/vrtc.c +++ b/hypervisor/dm/vrtc.c @@ -383,7 +383,7 @@ static time_t vrtc_get_current_time(struct acrn_vrtc *vrtc) if (vrtc->base_rtctime > 0) { offset = (cpu_ticks() - vrtc->base_tsc) / (get_tsc_khz() * 1000U); - second = vrtc->base_rtctime + (time_t)offset; + second = vrtc->base_rtctime + vrtc->offset_rtctime + (time_t)offset; } return second; } @@ -531,6 +531,7 @@ static bool vrtc_read(struct acrn_vcpu *vcpu, uint16_t addr, __unused size_t wid static bool vrtc_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width, uint32_t value) { + time_t current, after; struct acrn_vrtc *vrtc = &vcpu->vm->vrtc; if ((width == 1U) && (addr == CMOS_ADDR_PORT)) { @@ -556,7 +557,18 @@ static bool vrtc_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width, *((uint8_t *)&vrtc->rtcdev + vrtc->addr) = (uint8_t)(value & 0x7FU); RTC_DEBUG("RTC alarm reg(%d) set to %#x (ignored)\n", vrtc->addr, value); break; + case RTC_SEC: + /* + * High order bit of 'seconds' is readonly. + */ + value &= 0x7f; + /* FALLTHRU */ default: + RTC_DEBUG("RTC offset %#x set to %#x\n", vrtc->addr, value); + *((uint8_t *)&vrtc->rtcdev + vrtc->addr) = (uint8_t)(value & 0xFFU); + current = vrtc_get_current_time(vrtc); + after = rtc_to_secs(vrtc); + vrtc->offset_rtctime += after - current; break; } } diff --git a/hypervisor/include/dm/vrtc.h b/hypervisor/include/dm/vrtc.h index fff2559d2..b760ce445 100644 --- a/hypervisor/include/dm/vrtc.h +++ b/hypervisor/include/dm/vrtc.h @@ -34,6 +34,8 @@ struct acrn_vrtc { uint32_t addr; /* RTC register to read or write */ time_t base_rtctime; /* Base time calulated from physical rtc register. */ + time_t offset_rtctime; /* RTC offset against base time. */ + uint64_t base_tsc; /* Base tsc value */ struct rtcdev rtcdev; /* RTC register */ -- 2.25.1
|
|
[PATCH v6 0/9] add vRTC write emulate
Zhao, Yuanyuan
Now ACRN would emulate vRTC for non-RT pre-launched VM, RTVM and
Service VM. For vRTC read, ACRN would only return the the physical RTC register value; for vRTC write, ACRN would ignore it. New implement is: 1. Service OS read and write physical RTC. 2. The initial vRTC of each VM will is same with physical RTC, i.e. the RTC of service VM. 3. Guest VM can re-program its RTC (thu NTP etc.), but the update of guest RTC will be emulated by Hypervisor through 'offset_rtctime'. 4. Service VM will update physical RTC. So, if the system shuts down, hypervisor can rely on #2 for each VM to get right initial RTC (sync with real NTP), without saving the “offset_rtctime”. 5. A calibration of vrtc will trigger by timer every 3 hours to keep precision. 6. When write the physical RTC, will reset the 'offset_rtctime' of each guest VM. v6->v5 1. Add a new patch to ensure monotonicity. 2. Alarm interrupts commits. v4->v5 1. [1/8] 'vrtc_set_basetime' change byte array to 'struct rtcdev' ptr. 2. [3/8] Remove space. 3. [4/8][5/8] Commit message. 4. [6/8] 'vrtc_read' switch case '0' to 'RTC_SEC'. 5. [7/8] 'calibrate_lock' --> 'vrtc_rebase_lock'. v3->v4: 1. Add a calibrate_lock. 2. Split reg_b and reg_c patch. 3. Other commits in v3. v2->v3: 1. Remove Servcie VM pass through, add read & write for it in HV. 2. Add periodical calibation of vrtc. 3. When set RTC time, reset offset of vrtc. v1->v2: 1. Change name of 'mc146818rtc.h'. 2. Add cover letter. 3. Use unsigned int in time transform. 4. Split patches in a new way. Yuanyuan Zhao (9): hv: add `rtcdev` emulate vrtc hv: support vrtc BCD data mode hv: support 12 hour mode hv: add vRTC reg_b and reg_c support hv: add Service VM write physical RTC register hv: add time modification of vrtc hv: vrtc calibrate function hv: calibrate vrtc when modify physical time hv: vRTC monotonic growth hypervisor/dm/vrtc.c | 647 ++++++++++++++++++++- hypervisor/include/arch/x86/asm/guest/vm.h | 3 +- hypervisor/include/dm/mc146818rtc.h | 72 +++ hypervisor/include/dm/vrtc.h | 45 ++ 4 files changed, 755 insertions(+), 12 deletions(-) create mode 100644 hypervisor/include/dm/mc146818rtc.h create mode 100644 hypervisor/include/dm/vrtc.h -- 2.25.1
|
|
[PATCH v6 8/9] hv: calibrate vrtc when modify physical time
Zhao, Yuanyuan
For Service VM modify physical rtc time and vrtc will calibrate
time by read physical rtc. So when Service VM modify physical time, calibrate all vrtc. Signed-off-by: Yuanyuan Zhao <yuanyuan.zhao@...> Reviewed-by: Junjie Mao <junjie.mao@...> --- hypervisor/dm/vrtc.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/hypervisor/dm/vrtc.c b/hypervisor/dm/vrtc.c index 3a21f219f..fda4e332d 100644 --- a/hypervisor/dm/vrtc.c +++ b/hypervisor/dm/vrtc.c @@ -41,6 +41,9 @@ #endif # define RTC_ERR pr_err +static time_t vrtc_get_physical_rtc_time(struct acrn_vrtc *vrtc); +static void vrtc_update_basetime(time_t physical_time, time_t offset); + struct clktime { uint32_t year; /* year (4 digit year) */ uint32_t mon; /* month (1 - 12) */ @@ -528,6 +531,12 @@ static bool vrtc_read(struct acrn_vcpu *vcpu, uint16_t addr, __unused size_t wid return ret; } +static inline bool vrtc_is_time_register(uint32_t offset) +{ + return (offset == RTC_SEC || offset == RTC_MIN || offset == RTC_HRS || offset == RTC_DAY + || offset == RTC_MONTH || offset == RTC_YEAR || offset == RTC_CENTURY); +} + /** * @pre vcpu != NULL * @pre vcpu->vm != NULL @@ -537,12 +546,22 @@ static bool vrtc_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width, { time_t current, after; struct acrn_vrtc *vrtc = &vcpu->vm->vrtc; + struct acrn_vrtc temp_vrtc; + bool is_time_register; if ((width == 1U) && (addr == CMOS_ADDR_PORT)) { vrtc->addr = (uint8_t)(value & 0x7FU); } else { if (is_service_vm(vcpu->vm)) { - cmos_set_reg_val(vrtc->addr, (uint8_t)(value & 0xFFU)); + is_time_register = vrtc_is_time_register(vrtc->addr); + if (is_time_register) { + current = vrtc_get_physical_rtc_time(&temp_vrtc); + } + cmos_set_reg_val(vcpu->vm->vrtc.addr, (uint8_t)(value & 0xFFU)); + if (is_time_register) { + after = vrtc_get_physical_rtc_time(&temp_vrtc); + vrtc_update_basetime(after, current - after); + } } else { switch (vrtc->addr) { case RTC_STATUSA: -- 2.25.1
|
|
[PATCH v6 2/9] hv: support vrtc BCD data mode
Zhao, Yuanyuan
Add judging of the vRTC data mode. If BCD data mode is inuse,
make conversion of data. Signed-off-by: Yuanyuan Zhao <yuanyuan.zhao@...> Reviewed-by: Junjie Mao <junjie.mao@...> --- hypervisor/dm/vrtc.c | 79 ++++++++++++++++++++++++----- hypervisor/include/dm/mc146818rtc.h | 2 + 2 files changed, 67 insertions(+), 14 deletions(-) diff --git a/hypervisor/dm/vrtc.c b/hypervisor/dm/vrtc.c index 9a62bf9cc..b9c9d1f4d 100644 --- a/hypervisor/dm/vrtc.c +++ b/hypervisor/dm/vrtc.c @@ -104,6 +104,51 @@ static inline uint32_t day_of_week(uint32_t days) return ((days) + 4U) % 7U; } +uint8_t const bin2bcd_data[] = { + 0x00U, 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x06U, 0x07U, 0x08U, 0x09U, + 0x10U, 0x11U, 0x12U, 0x13U, 0x14U, 0x15U, 0x16U, 0x17U, 0x18U, 0x19U, + 0x20U, 0x21U, 0x22U, 0x23U, 0x24U, 0x25U, 0x26U, 0x27U, 0x28U, 0x29U, + 0x30U, 0x31U, 0x32U, 0x33U, 0x34U, 0x35U, 0x36U, 0x37U, 0x38U, 0x39U, + 0x40U, 0x41U, 0x42U, 0x43U, 0x44U, 0x45U, 0x46U, 0x47U, 0x48U, 0x49U, + 0x50U, 0x51U, 0x52U, 0x53U, 0x54U, 0x55U, 0x56U, 0x57U, 0x58U, 0x59U, + 0x60U, 0x61U, 0x62U, 0x63U, 0x64U, 0x65U, 0x66U, 0x67U, 0x68U, 0x69U, + 0x70U, 0x71U, 0x72U, 0x73U, 0x74U, 0x75U, 0x76U, 0x77U, 0x78U, 0x79U, + 0x80U, 0x81U, 0x82U, 0x83U, 0x84U, 0x85U, 0x86U, 0x87U, 0x88U, 0x89U, + 0x90U, 0x91U, 0x92U, 0x93U, 0x94U, 0x95U, 0x96U, 0x97U, 0x98U, 0x99U +}; + +/* + * @pre val < 100 + */ +static inline uint8_t rtcset(struct rtcdev *rtc, uint32_t val) +{ + return ((rtc->reg_b & RTCSB_BCD) ? val : bin2bcd_data[val]); +} + +/* + * Get rtc time register binary value. + * If BCD data mode is enabled, translate BCD to binary. + */ +static int32_t rtcget(const struct rtcdev *rtc, int32_t val, uint32_t *retval) +{ + uint8_t upper, lower; + int32_t errno = 0; + + if (rtc->reg_b & RTCSB_BCD) { + *retval = val; + } else { + lower = val & 0xfU; + upper = (val >> 4) & 0xfU; + + if (lower > 9U || upper > 9U) { + errno = -EINVAL; + } else { + *retval = upper * 10U + lower; + } + } + return errno; +} + /* * Translate clktime (such as year, month, day) to time_t. */ @@ -195,13 +240,19 @@ static time_t rtc_to_secs(const struct acrn_vrtc *vrtc) time_t second = VRTC_BROKEN_TIME; const struct rtcdev *rtc= &vrtc->rtcdev; int32_t err = 0; + uint32_t century = 0, year = 0; do { - ct.sec = rtc->sec; - ct.min = rtc->min; - ct.hour = rtc->hour; - ct.day = rtc->day_of_month; - ct.mon = rtc->month; + if (rtcget(rtc, rtc->sec, &ct.sec) < 0 || rtcget(rtc, rtc->min, &ct.min) < 0 || + rtcget(rtc, rtc->hour, &ct.hour) < 0 || rtcget(rtc, rtc->day_of_month, &ct.day) < 0 || + rtcget(rtc, rtc->month, &ct.mon) < 0 || rtcget(rtc, rtc->year, &year) < 0 || + rtcget(rtc, rtc->century, ¢ury) < 0) { + RTC_ERR("Invalid RTC sec %#x hour %#x day %#x mon %#x year %#x century %#x\n", + rtc->sec, rtc->min, rtc->day_of_month, rtc->month, + rtc->year, rtc->century); + err = -1; + break; + } /* * Ignore 'rtc->dow' because some guests like Linux don't bother @@ -211,7 +262,7 @@ static time_t rtc_to_secs(const struct acrn_vrtc *vrtc) */ ct.dow = -1; - ct.year = rtc->century * 100 + rtc->year; + ct.year = century * 100 + year; if (ct.year < POSIX_BASE_YEAR) { RTC_ERR("Invalid RTC century %#x/%d\n", rtc->century, ct.year); @@ -241,14 +292,14 @@ static void secs_to_rtc(time_t rtctime, struct acrn_vrtc *vrtc) if (rtctime > 0 && clk_ts_to_ct(rtctime, &ct) == 0) { rtc = &vrtc->rtcdev; - rtc->sec = ct.sec; - rtc->min = ct.min; - rtc->hour = ct.hour; - rtc->day_of_week = ct.dow + 1; - rtc->day_of_month = ct.day; - rtc->month = ct.mon; - rtc->year = ct.year % 100; - rtc->century = ct.year / 100; + rtc->sec = rtcset(rtc, ct.sec); + rtc->min = rtcset(rtc, ct.min); + rtc->hour = rtcset(rtc, ct.hour); + rtc->day_of_week = rtcset(rtc, ct.dow + 1); + rtc->day_of_month = rtcset(rtc, ct.day); + rtc->month = rtcset(rtc, ct.mon); + rtc->year = rtcset(rtc, ct.year % 100); + rtc->century = rtcset(rtc, ct.year / 100); } } diff --git a/hypervisor/include/dm/mc146818rtc.h b/hypervisor/include/dm/mc146818rtc.h index 023bbe599..4baf9de69 100644 --- a/hypervisor/include/dm/mc146818rtc.h +++ b/hypervisor/include/dm/mc146818rtc.h @@ -58,6 +58,8 @@ #define RTCSA_TUP 0x80 /* time update, don't look now */ #define RTC_STATUSB 0x0b /* status register B */ +#define RTCSB_BCD 0x04 /* 0 = BCD, 1 = Binary coded time */ + #define RTC_INTR 0x0c /* status register C (R) interrupt source */ #define RTC_STATUSD 0x0d /* status register D (R) Lost Power */ -- 2.25.1
|
|
[PATCH v6 1/9] hv: add `rtcdev` emulate vrtc
Zhao, Yuanyuan
Current code would read physical RTC register and return it directly to guest.
This patch would read a base physical RTC time and a base physical TSC time at initialize stage. Then when guest tries to read vRTC time, ACRN HV would read the real TSC time and use the TSC offset to calculate the real RTC time. This patch only support BIN data mode and 24 hour mode. BCD data mode and 12 hour mode will add in other patch. The accuracy of clock provided by this patch is limited by TSC, and will be improved in a following patch also. Signed-off-by: Yuanyuan Zhao <yuanyuan.zhao@...> Reviewed-by: Junjie Mao <junjie.mao@...> --- hypervisor/dm/vrtc.c | 321 ++++++++++++++++++++- hypervisor/include/arch/x86/asm/guest/vm.h | 3 +- hypervisor/include/dm/mc146818rtc.h | 64 ++++ hypervisor/include/dm/vrtc.h | 42 +++ 4 files changed, 418 insertions(+), 12 deletions(-) diff --git a/hypervisor/dm/vrtc.c b/hypervisor/dm/vrtc.c index 23fb978c2..9a62bf9cc 100644 --- a/hypervisor/dm/vrtc.c +++ b/hypervisor/dm/vrtc.c @@ -1,18 +1,275 @@ /* - * Copyright (C) 2018 Intel Corporation. + * Copyright (c) 2014, Neel Natu (neel@...) + * Copyright (c) 2022 Intel Corporation + * All rights reserved. * - * SPDX-License-Identifier: BSD-3-Clause + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <asm/guest/vm.h> #include <asm/io.h> +#include <asm/tsc.h> +#include <vrtc.h> +#include <logmsg.h> + +#include "mc146818rtc.h" + +/* #define DEBUG_RTC */ +#ifdef DEBUG_RTC +# define RTC_DEBUG pr_info +#else +# define RTC_DEBUG(format, ...) do { } while (false) +#endif +# define RTC_ERR pr_err + +struct clktime { + uint32_t year; /* year (4 digit year) */ + uint32_t mon; /* month (1 - 12) */ + uint32_t day; /* day (1 - 31) */ + uint32_t hour; /* hour (0 - 23) */ + uint32_t min; /* minute (0 - 59) */ + uint32_t sec; /* second (0 - 59) */ + uint32_t dow; /* day of week (0 - 6; 0 = Sunday) */ +}; + +#define POSIX_BASE_YEAR 1970 +#define SECDAY (24 * 60 * 60) +#define SECYR (SECDAY * 365) +#define VRTC_BROKEN_TIME ((time_t)-1) + +#define FEBRUARY 2U + +static const uint32_t month_days[12] = { + 31U, 28U, 31U, 30U, 31U, 30U, 31U, 31U, 30U, 31U, 30U, 31U +}; + +/* + * This inline avoids some unnecessary modulo operations + * as compared with the usual macro: + * ( ((year % 4) == 0 && + * (year % 100) != 0) || + * ((year % 400) == 0) ) + * It is otherwise equivalent. + */ +static inline uint32_t leapyear(uint32_t year) +{ + uint32_t rv = 0U; + + if ((year & 3U) == 0) { + rv = 1U; + if ((year % 100U) == 0) { + rv = 0U; + if ((year % 400U) == 0) { + rv = 1U; + } + } + } + return rv; +} + +static inline uint32_t days_in_year(uint32_t year) +{ + return leapyear(year) ? 366U : 365U; +} + +static inline uint32_t days_in_month(uint32_t year, uint32_t month) +{ + return month_days[(month) - 1U] + (month == FEBRUARY ? leapyear(year) : 0U); +} + +/* + * Day of week. Days are counted from 1/1/1970, which was a Thursday. + */ +static inline uint32_t day_of_week(uint32_t days) +{ + return ((days) + 4U) % 7U; +} + +/* + * Translate clktime (such as year, month, day) to time_t. + */ +static int32_t clk_ct_to_ts(struct clktime *ct, time_t *sec) +{ + uint32_t i, year, days; + int32_t err = 0; + + year = ct->year; + + /* Sanity checks. */ + if (ct->mon < 1U || ct->mon > 12U || ct->day < 1U || + ct->day > days_in_month(year, ct->mon) || + ct->hour > 23U || ct->min > 59U || ct->sec > 59U || + year < POSIX_BASE_YEAR || year > 2037U) { + /* time_t overflow */ + err = -EINVAL; + } else { + /* + * Compute days since start of time + * First from years, then from months. + */ + days = 0U; + for (i = POSIX_BASE_YEAR; i < year; i++) { + days += days_in_year(i); + } + + /* Months */ + for (i = 1; i < ct->mon; i++) { + days += days_in_month(year, i); + } + days += (ct->day - 1); + + *sec = (((time_t)days * 24 + ct->hour) * 60 + ct->min) * 60 + ct->sec; + } + return err; +} + +/* + * Translate time_t to clktime (such as year, month, day) + */ +static int32_t clk_ts_to_ct(time_t secs, struct clktime *ct) +{ + uint32_t i, year, days; + time_t rsec; /* remainder seconds */ + int32_t err = 0; + + days = secs / SECDAY; + rsec = secs % SECDAY; + + ct->dow = day_of_week(days); + + /* Substract out whole years, counting them in i. */ + for (year = POSIX_BASE_YEAR; days >= days_in_year(year); year++) { + days -= days_in_year(year); + } + ct->year = year; + + /* Substract out whole months, counting them in i. */ + for (i = 1; days >= days_in_month(year, i); i++) { + days -= days_in_month(year, i); + } + ct->mon = i; + + /* Days are what is left over (+1) from all that. */ + ct->day = days + 1; + + /* Hours, minutes, seconds are easy */ + ct->hour = rsec / 3600U; + rsec = rsec % 3600U; + ct->min = rsec / 60U; + rsec = rsec % 60U; + ct->sec = rsec; + + /* time_t is defined as int32_t, so year should not be more than 2037. */ + if (ct->mon > 12U || ct->year > 2037 || ct->day > days_in_month(ct->year, ct->mon)) { + RTC_ERR("Invalid vRTC param mon %d, year %d, day %d\n", ct->mon, ct->year, ct->day); + err = -EINVAL; + } + return err; +} + +/* + * Calculate second value from rtcdev register info which save in vrtc. + */ +static time_t rtc_to_secs(const struct acrn_vrtc *vrtc) +{ + struct clktime ct; + time_t second = VRTC_BROKEN_TIME; + const struct rtcdev *rtc= &vrtc->rtcdev; + int32_t err = 0; + + do { + ct.sec = rtc->sec; + ct.min = rtc->min; + ct.hour = rtc->hour; + ct.day = rtc->day_of_month; + ct.mon = rtc->month; + + /* + * Ignore 'rtc->dow' because some guests like Linux don't bother + * setting it at all while others like OpenBSD/i386 set it incorrectly. + * + * clock_ct_to_ts() does not depend on 'ct.dow' anyways so ignore it. + */ + ct.dow = -1; + + ct.year = rtc->century * 100 + rtc->year; + if (ct.year < POSIX_BASE_YEAR) { + RTC_ERR("Invalid RTC century %#x/%d\n", rtc->century, + ct.year); + break; + } + + err = clk_ct_to_ts(&ct, &second); + if (err) { + RTC_ERR("Invalid RTC clocktime.date %04d-%02d-%02d\n", + ct.year, ct.mon, ct.day); + RTC_ERR("Invalid RTC clocktime.time %02d:%02d:%02d\n", + ct.hour, ct.min, ct.sec); + break; + } + } while (false); + + return second; +} + +/* + * Translate second value to rtcdev register info and save it in vrtc. + */ +static void secs_to_rtc(time_t rtctime, struct acrn_vrtc *vrtc) +{ + struct clktime ct; + struct rtcdev *rtc; + + if (rtctime > 0 && clk_ts_to_ct(rtctime, &ct) == 0) { + rtc = &vrtc->rtcdev; + rtc->sec = ct.sec; + rtc->min = ct.min; + rtc->hour = ct.hour; + rtc->day_of_week = ct.dow + 1; + rtc->day_of_month = ct.day; + rtc->month = ct.mon; + rtc->year = ct.year % 100; + rtc->century = ct.year / 100; + } +} + +/* + * If the base_rtctime is valid, calculate current time by add tsc offset and offset_rtctime. + */ +static time_t vrtc_get_current_time(struct acrn_vrtc *vrtc) +{ + uint64_t offset; + time_t second = VRTC_BROKEN_TIME; + + if (vrtc->base_rtctime > 0) { + offset = (cpu_ticks() - vrtc->base_tsc) / (get_tsc_khz() * 1000U); + second = vrtc->base_rtctime + (time_t)offset; + } + return second; +} #define CMOS_ADDR_PORT 0x70U #define CMOS_DATA_PORT 0x71U -#define RTC_STATUSA 0x0AU /* status register A */ -#define RTCSA_TUP 0x80U /* time update, don't look now */ - static spinlock_t cmos_lock = { .head = 0U, .tail = 0U }; static uint8_t cmos_read(uint8_t addr) @@ -51,18 +308,34 @@ static uint8_t cmos_get_reg_val(uint8_t addr) static bool vrtc_read(struct acrn_vcpu *vcpu, uint16_t addr, __unused size_t width) { uint8_t offset; + time_t current; + struct acrn_vrtc *vrtc = &vcpu->vm->vrtc; struct acrn_pio_request *pio_req = &vcpu->req.reqs.pio_request; struct acrn_vm *vm = vcpu->vm; + bool ret = true; - offset = vm->vrtc_offset; + offset = vm->vrtc.addr; if (addr == CMOS_ADDR_PORT) { - pio_req->value = vm->vrtc_offset; + pio_req->value = offset; } else { - pio_req->value = cmos_get_reg_val(offset); + if (is_service_vm(vm)) { + pio_req->value = cmos_get_reg_val(offset); + } else { + if (offset <= RTC_CENTURY) { + current = vrtc_get_current_time(vrtc); + secs_to_rtc(current, vrtc); + + pio_req->value = *((uint8_t *)&vm->vrtc.rtcdev + offset); + RTC_DEBUG("read 0x%x, 0x%x", offset, pio_req->value); + } else { + RTC_ERR("vrtc read invalid addr 0x%x", offset); + ret = false; + } + } } - return true; + return ret; } /** @@ -73,19 +346,45 @@ static bool vrtc_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width, uint32_t value) { if ((width == 1U) && (addr == CMOS_ADDR_PORT)) { - vcpu->vm->vrtc_offset = (uint8_t)value & 0x7FU; + vcpu->vm->vrtc.addr = (uint8_t)value & 0x7FU; } return true; } +static void vrtc_set_basetime(struct acrn_vrtc *vrtc) +{ + struct rtcdev *vrtcdev = &vrtc->rtcdev; + + /* + * Read base time from physical rtc. + */ + vrtcdev->sec = cmos_get_reg_val(RTC_SEC); + vrtcdev->min = cmos_get_reg_val(RTC_MIN); + vrtcdev->hour = cmos_get_reg_val(RTC_HRS); + vrtcdev->day_of_month = cmos_get_reg_val(RTC_DAY); + vrtcdev->month = cmos_get_reg_val(RTC_MONTH); + vrtcdev->year = cmos_get_reg_val(RTC_YEAR); + vrtcdev->century = cmos_get_reg_val(RTC_CENTURY); + vrtcdev->reg_a = cmos_get_reg_val(RTC_STATUSA) & (~RTCSA_TUP); + vrtcdev->reg_b = cmos_get_reg_val(RTC_STATUSB); + vrtcdev->reg_c = cmos_get_reg_val(RTC_INTR); + vrtcdev->reg_d = cmos_get_reg_val(RTC_STATUSD); + + vrtc->base_rtctime = rtc_to_secs(vrtc); +} + void vrtc_init(struct acrn_vm *vm) { struct vm_io_range range = { .base = CMOS_ADDR_PORT, .len = 2U}; /* Initializing the CMOS RAM offset to 0U */ - vm->vrtc_offset = 0U; + vm->vrtc.addr = 0U; + vm->vrtc.vm = vm; register_pio_emulation_handler(vm, RTC_PIO_IDX, &range, vrtc_read, vrtc_write); + + vrtc_set_basetime(&vm->vrtc); + vm->vrtc.base_tsc = cpu_ticks(); } diff --git a/hypervisor/include/arch/x86/asm/guest/vm.h b/hypervisor/include/arch/x86/asm/guest/vm.h index 589d0a913..c05b9b212 100644 --- a/hypervisor/include/arch/x86/asm/guest/vm.h +++ b/hypervisor/include/arch/x86/asm/guest/vm.h @@ -21,6 +21,7 @@ #include <vpic.h> #include <asm/guest/vmx_io.h> #include <vuart.h> +#include <vrtc.h> #include <asm/guest/trusty.h> #include <asm/guest/vcpuid.h> #include <vpci.h> @@ -169,7 +170,7 @@ struct acrn_vm { uint32_t vcpuid_entry_nr, vcpuid_level, vcpuid_xlevel; struct vcpuid_entry vcpuid_entries[MAX_VM_VCPUID_ENTRIES]; struct acrn_vpci vpci; - uint8_t vrtc_offset; + struct acrn_vrtc vrtc; uint64_t intr_inject_delay_delta; /* delay of intr injection */ } __aligned(PAGE_SIZE); diff --git a/hypervisor/include/dm/mc146818rtc.h b/hypervisor/include/dm/mc146818rtc.h new file mode 100644 index 000000000..023bbe599 --- /dev/null +++ b/hypervisor/include/dm/mc146818rtc.h @@ -0,0 +1,64 @@ +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Copyright (C) 2022 Intel Corporation. + * + * This code is derived from software contributed to Berkeley by + * William Jolitz. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)rtc.h 7.1 (Berkeley) 5/12/91 + * $FreeBSD$ + */ + +#ifndef _MC146818_RTC_H_ +#define _MC146818_RTC_H_ + +/* + * MC146818 RTC Register locations + */ + +#define RTC_SEC 0x00 /* seconds */ +#define RTC_SECALRM 0x01 /* seconds alarm */ +#define RTC_MIN 0x02 /* minutes */ +#define RTC_MINALRM 0x03 /* minutes alarm */ +#define RTC_HRS 0x04 /* hours */ +#define RTC_HRSALRM 0x05 /* hours alarm */ +#define RTC_WDAY 0x06 /* week day */ +#define RTC_DAY 0x07 /* day of month */ +#define RTC_MONTH 0x08 /* month of year */ +#define RTC_YEAR 0x09 /* year in century*/ +#define RTC_CENTURY 0x32 /* century */ + +#define RTC_STATUSA 0x0a /* status register A */ +#define RTCSA_TUP 0x80 /* time update, don't look now */ + +#define RTC_STATUSB 0x0b /* status register B */ +#define RTC_INTR 0x0c /* status register C (R) interrupt source */ +#define RTC_STATUSD 0x0d /* status register D (R) Lost Power */ + +#endif /* _MC146818_RTC_H_ */ diff --git a/hypervisor/include/dm/vrtc.h b/hypervisor/include/dm/vrtc.h new file mode 100644 index 000000000..fff2559d2 --- /dev/null +++ b/hypervisor/include/dm/vrtc.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 Intel Corporation. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef VRTC_H +#define VRTC_H + +typedef int32_t time_t; + +/* Register layout of the RTC */ +struct rtcdev { + uint8_t sec; + uint8_t alarm_sec; + uint8_t min; + uint8_t alarm_min; + uint8_t hour; + uint8_t alarm_hour; + uint8_t day_of_week; + uint8_t day_of_month; + uint8_t month; + uint8_t year; + uint8_t reg_a; + uint8_t reg_b; + uint8_t reg_c; + uint8_t reg_d; + uint8_t res[36]; + uint8_t century; +}; + +struct acrn_vrtc { + struct acrn_vm *vm; + uint32_t addr; /* RTC register to read or write */ + + time_t base_rtctime; /* Base time calulated from physical rtc register. */ + uint64_t base_tsc; /* Base tsc value */ + + struct rtcdev rtcdev; /* RTC register */ +}; + +#endif /* VRTC_H */ -- 2.25.1
|
|
[PATCH v6 9/9] hv: vRTC monotonic growth
Zhao, Yuanyuan
For physical RTC is monotonic growth, ensure vRTC monotonicity.
Periodical calibration and physical RTC modification may have impact. Check it before reading Signed-off-by: Yuanyuan Zhao <yuanyuan.zhao@...> --- hypervisor/dm/vrtc.c | 8 ++++++++ hypervisor/include/dm/vrtc.h | 1 + 2 files changed, 9 insertions(+) diff --git a/hypervisor/dm/vrtc.c b/hypervisor/dm/vrtc.c index fda4e332d..b1e35aa17 100644 --- a/hypervisor/dm/vrtc.c +++ b/hypervisor/dm/vrtc.c @@ -390,6 +390,11 @@ static time_t vrtc_get_current_time(struct acrn_vrtc *vrtc) if (vrtc->base_rtctime > 0) { offset = (cpu_ticks() - vrtc->base_tsc) / (get_tsc_khz() * 1000U); second = vrtc->base_rtctime + vrtc->offset_rtctime + (time_t)offset; + if (second < vrtc->last_rtctime) { + second = vrtc->last_rtctime; + } else { + vrtc->last_rtctime = second; + } } spinlock_release(&vrtc_rebase_lock); return second; @@ -593,6 +598,7 @@ static bool vrtc_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width, after = rtc_to_secs(vrtc); spinlock_obtain(&vrtc_rebase_lock); vrtc->offset_rtctime += after - current; + vrtc->last_rtctime = VRTC_BROKEN_TIME; spinlock_release(&vrtc_rebase_lock); break; } @@ -632,6 +638,7 @@ static void vrtc_update_basetime(time_t physical_time, time_t offset) spinlock_obtain(&vrtc_rebase_lock); vm->vrtc.base_tsc = cpu_ticks(); vm->vrtc.base_rtctime = physical_time; + vm->vrtc.last_rtctime = VRTC_BROKEN_TIME; vm->vrtc.offset_rtctime += offset; spinlock_release(&vrtc_rebase_lock); } @@ -685,6 +692,7 @@ static void vrtc_set_basetime(struct acrn_vrtc *vrtc) current = rtc_to_secs(vrtc); spinlock_obtain(&vrtc_rebase_lock); vrtc->base_rtctime = current; + vrtc->last_rtctime = VRTC_BROKEN_TIME; spinlock_release(&vrtc_rebase_lock); } diff --git a/hypervisor/include/dm/vrtc.h b/hypervisor/include/dm/vrtc.h index b760ce445..fa57eb542 100644 --- a/hypervisor/include/dm/vrtc.h +++ b/hypervisor/include/dm/vrtc.h @@ -35,6 +35,7 @@ struct acrn_vrtc { time_t base_rtctime; /* Base time calulated from physical rtc register. */ time_t offset_rtctime; /* RTC offset against base time. */ + time_t last_rtctime; /* Last RTC time, to keep monotonicity. */ uint64_t base_tsc; /* Base tsc value */ -- 2.25.1
|
|
[PATCH v6 4/9] hv: add vRTC reg_b and reg_c support
Zhao, Yuanyuan
During setting RTC time, driver will halt RTC update. So support
bit 7 of reg_b. When it set to 0, time will be updated. And when it's set to 1, rtc will keep the second it was set. In the process of getting RTC time, driver sets alarm interrupt, waits 1s, and get alarm interrupt flag. So support alarm interrupt flag update. If alarm interrupt is enabled (bit 5, reg_b set to 1), interrupt flag register will be set (bit 7 & bit 5 of reg_c) at appropriate time. v5->v6: commmit of alarm trigger. Signed-off-by: Yuanyuan Zhao <yuanyuan.zhao@...> --- hypervisor/dm/vrtc.c | 74 +++++++++++++++++++++++++++-- hypervisor/include/dm/mc146818rtc.h | 5 ++ 2 files changed, 76 insertions(+), 3 deletions(-) diff --git a/hypervisor/dm/vrtc.c b/hypervisor/dm/vrtc.c index 79ee2439d..b6ebcc49c 100644 --- a/hypervisor/dm/vrtc.c +++ b/hypervisor/dm/vrtc.c @@ -422,6 +422,45 @@ static uint8_t cmos_get_reg_val(uint8_t addr) return reg; } +#define TRIGGER_ALARM (RTCIR_ALARM | RTCIR_INT) +#define RTC_DELTA 1 /* For RTC and system time may out of sync for no more than 1s */ +static inline bool rtc_halted(struct acrn_vrtc *rtc) +{ + return ((rtc->rtcdev.reg_b & RTCSB_HALT) != 0U); +} + +static uint8_t vrtc_get_reg_c(struct acrn_vrtc *vrtc) +{ + uint8_t ret = vrtc->rtcdev.reg_c; + struct rtcdev *rtc = &vrtc->rtcdev; + time_t current, alarm; + + if ((rtc->reg_b & RTCSB_AINTR) != 0U) { + current = rtc->hour * 3600 + rtc->min * 60 + rtc->sec; + alarm = rtc->alarm_hour * 3600 + rtc->alarm_min * 60 + rtc->alarm_sec; + + if (current >= alarm - RTC_DELTA && current <= alarm + RTC_DELTA) { + /* + * Linux RTC driver will trigger alarm interrupt when getting + * RTC time, and then read the interrupt flag register. If the value was not + * correct, read failure will occurs. So if alarm interrupt is enabled + * and rtc time is in alarm time scale, set the interrupt flag. The + * interrupt is not acturally triggered for driver will read the register + * proactively. + */ + ret |= TRIGGER_ALARM; + } + } + + vrtc->rtcdev.reg_c = 0; + return ret; +} + +static void vrtc_set_reg_b(struct acrn_vrtc *vrtc, uint8_t newval) +{ + vrtc->rtcdev.reg_b = newval; +} + /** * @pre vcpu != NULL * @pre vcpu->vm != NULL @@ -435,7 +474,7 @@ static bool vrtc_read(struct acrn_vcpu *vcpu, uint16_t addr, __unused size_t wid struct acrn_vm *vm = vcpu->vm; bool ret = true; - offset = vm->vrtc.addr; + offset = vrtc->addr; if (addr == CMOS_ADDR_PORT) { pio_req->value = offset; @@ -447,7 +486,11 @@ static bool vrtc_read(struct acrn_vcpu *vcpu, uint16_t addr, __unused size_t wid current = vrtc_get_current_time(vrtc); secs_to_rtc(current, vrtc); - pio_req->value = *((uint8_t *)&vm->vrtc.rtcdev + offset); + if(offset == 0xCU) { + pio_req->value = vrtc_get_reg_c(vrtc); + } else { + pio_req->value = *((uint8_t *)&vrtc->rtcdev + offset); + } RTC_DEBUG("read 0x%x, 0x%x", offset, pio_req->value); } else { RTC_ERR("vrtc read invalid addr 0x%x", offset); @@ -466,8 +509,33 @@ static bool vrtc_read(struct acrn_vcpu *vcpu, uint16_t addr, __unused size_t wid static bool vrtc_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width, uint32_t value) { + struct acrn_vrtc *vrtc = &vcpu->vm->vrtc; + if ((width == 1U) && (addr == CMOS_ADDR_PORT)) { - vcpu->vm->vrtc.addr = (uint8_t)value & 0x7FU; + vrtc->addr = (uint8_t)(value & 0x7FU); + } else { + if (!is_service_vm(vcpu->vm)) { + switch (vrtc->addr) { + case RTC_STATUSA: + case RTC_INTR: + case RTC_STATUSD: + RTC_DEBUG("RTC reg_%x set to %#x (ignored)\n", vrtc->addr, value); + break; + case RTC_STATUSB: + vrtc_set_reg_b(vrtc, value); + RTC_DEBUG("RTC reg_b set to %#x\n", value); + break; + case RTC_SECALRM: + case RTC_MINALRM: + /* FALLTHRU */ + case RTC_HRSALRM: + *((uint8_t *)&vrtc->rtcdev + vrtc->addr) = (uint8_t)(value & 0x7FU); + RTC_DEBUG("RTC alarm reg(%d) set to %#x (ignored)\n", vrtc->addr, value); + break; + default: + break; + } + } } return true; diff --git a/hypervisor/include/dm/mc146818rtc.h b/hypervisor/include/dm/mc146818rtc.h index b23f90d34..1681cc960 100644 --- a/hypervisor/include/dm/mc146818rtc.h +++ b/hypervisor/include/dm/mc146818rtc.h @@ -60,8 +60,13 @@ #define RTC_STATUSB 0x0b /* status register B */ #define RTCSB_24HR 0x02 /* 0 = 12 hours, 1 = 24 hours */ #define RTCSB_BCD 0x04 /* 0 = BCD, 1 = Binary coded time */ +#define RTCSB_AINTR 0x20 /* 1 = enable alarm interrupt */ +#define RTCSB_HALT 0x80 /* stop clock updates */ #define RTC_INTR 0x0c /* status register C (R) interrupt source */ +#define RTCIR_ALARM 0x20 /* alarm intr */ +#define RTCIR_INT 0x80 /* interrupt output signal */ + #define RTC_STATUSD 0x0d /* status register D (R) Lost Power */ #endif /* _MC146818_RTC_H_ */ -- 2.25.1
|
|
[PATCH v6 7/9] hv: vrtc calibrate function
Zhao, Yuanyuan
For TSC's precision (20~100ppm) is lower than physical RTC (less than 20ppm),
vRTC need to be calibrated by physical RTC. A timer tiggers calibration for vRTC every 3 hours. This can improve efficiency because physical RTC can be read once and calibrate all vRTC. Signed-off-by: Yuanyuan Zhao <yuanyuan.zhao@...> --- hypervisor/dm/vrtc.c | 80 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 3 deletions(-) diff --git a/hypervisor/dm/vrtc.c b/hypervisor/dm/vrtc.c index 9157a8fc2..3a21f219f 100644 --- a/hypervisor/dm/vrtc.c +++ b/hypervisor/dm/vrtc.c @@ -51,6 +51,8 @@ struct clktime { uint32_t dow; /* day of week (0 - 6; 0 = Sunday) */ }; +static spinlock_t vrtc_rebase_lock = { .head = 0U, .tail = 0U }; + #define POSIX_BASE_YEAR 1970 #define SECDAY (24 * 60 * 60) #define SECYR (SECDAY * 365) @@ -381,10 +383,12 @@ static time_t vrtc_get_current_time(struct acrn_vrtc *vrtc) uint64_t offset; time_t second = VRTC_BROKEN_TIME; + spinlock_obtain(&vrtc_rebase_lock); if (vrtc->base_rtctime > 0) { offset = (cpu_ticks() - vrtc->base_tsc) / (get_tsc_khz() * 1000U); second = vrtc->base_rtctime + vrtc->offset_rtctime + (time_t)offset; } + spinlock_release(&vrtc_rebase_lock); return second; } @@ -568,7 +572,9 @@ static bool vrtc_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width, *((uint8_t *)&vrtc->rtcdev + vrtc->addr) = (uint8_t)(value & 0xFFU); current = vrtc_get_current_time(vrtc); after = rtc_to_secs(vrtc); + spinlock_obtain(&vrtc_rebase_lock); vrtc->offset_rtctime += after - current; + spinlock_release(&vrtc_rebase_lock); break; } } @@ -577,9 +583,70 @@ static bool vrtc_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width, return true; } +#define CALIBRATE_PERIOD (3 * 3600 * 1000) /* By ms, totally 3 hours. */ +static struct hv_timer calibrate_timer; + +static time_t vrtc_get_physical_rtc_time(struct acrn_vrtc *vrtc) +{ + uint8_t *rtc = (uint8_t *)&vrtc->rtcdev; + + *(rtc + RTC_SEC) = cmos_get_reg_val(RTC_SEC); + *(rtc + RTC_MIN) = cmos_get_reg_val(RTC_MIN); + *(rtc + RTC_HRS) = cmos_get_reg_val(RTC_HRS); + *(rtc + RTC_DAY) = cmos_get_reg_val(RTC_DAY); + *(rtc + RTC_MONTH) = cmos_get_reg_val(RTC_MONTH); + *(rtc + RTC_YEAR) = cmos_get_reg_val(RTC_YEAR); + *(rtc + RTC_CENTURY) = cmos_get_reg_val(RTC_CENTURY); + *(rtc + RTC_STATUSB) = cmos_get_reg_val(RTC_STATUSB); + + return rtc_to_secs(vrtc); +} + +static void vrtc_update_basetime(time_t physical_time, time_t offset) +{ + struct acrn_vm *vm; + uint32_t vm_id; + + for (vm_id = 0U; vm_id < CONFIG_MAX_VM_NUM; vm_id++) { + vm = get_vm_from_vmid(vm_id); + if (is_rt_vm(vm) || is_prelaunched_vm(vm)) { + spinlock_obtain(&vrtc_rebase_lock); + vm->vrtc.base_tsc = cpu_ticks(); + vm->vrtc.base_rtctime = physical_time; + vm->vrtc.offset_rtctime += offset; + spinlock_release(&vrtc_rebase_lock); + } + } +} + +static void calibrate_timer_callback(__unused void *data) +{ + struct acrn_vrtc temp_vrtc; + time_t physical_time = vrtc_get_physical_rtc_time(&temp_vrtc); + + vrtc_update_basetime(physical_time, 0); +} + +static void calibrate_setup_timer(void) +{ + uint64_t period_in_cycle, fire_tsc; + + period_in_cycle = TICKS_PER_MS * CALIBRATE_PERIOD; + fire_tsc = cpu_ticks() + period_in_cycle; + initialize_timer(&calibrate_timer, + calibrate_timer_callback, NULL, + fire_tsc, period_in_cycle); + + /* Start an periodic timer */ + if (add_timer(&calibrate_timer) != 0) { + pr_err("Failed to add calibrate timer"); + } +} + static void vrtc_set_basetime(struct acrn_vrtc *vrtc) { struct rtcdev *vrtcdev = &vrtc->rtcdev; + time_t current; /* * Read base time from physical rtc. @@ -596,7 +663,10 @@ static void vrtc_set_basetime(struct acrn_vrtc *vrtc) vrtcdev->reg_c = cmos_get_reg_val(RTC_INTR); vrtcdev->reg_d = cmos_get_reg_val(RTC_STATUSD); - vrtc->base_rtctime = rtc_to_secs(vrtc); + current = rtc_to_secs(vrtc); + spinlock_obtain(&vrtc_rebase_lock); + vrtc->base_rtctime = current; + spinlock_release(&vrtc_rebase_lock); } void vrtc_init(struct acrn_vm *vm) @@ -610,6 +680,10 @@ void vrtc_init(struct acrn_vm *vm) vm->vrtc.vm = vm; register_pio_emulation_handler(vm, RTC_PIO_IDX, &range, vrtc_read, vrtc_write); - vrtc_set_basetime(&vm->vrtc); - vm->vrtc.base_tsc = cpu_ticks(); + if (is_service_vm(vm)) { + calibrate_setup_timer(); + } else { + vrtc_set_basetime(&vm->vrtc); + vm->vrtc.base_tsc = cpu_ticks(); + } } -- 2.25.1
|
|
[PATCH v6 5/9] hv: add Service VM write physical RTC register
Zhao, Yuanyuan
Service VM write physical RTC register.
Both RTC time modify and Configuration register is available. Signed-off-by: Yuanyuan Zhao <yuanyuan.zhao@...> Reviewed-by: Junjie Mao <junjie.mao@...> --- hypervisor/dm/vrtc.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/hypervisor/dm/vrtc.c b/hypervisor/dm/vrtc.c index b6ebcc49c..cb1de7423 100644 --- a/hypervisor/dm/vrtc.c +++ b/hypervisor/dm/vrtc.c @@ -399,6 +399,12 @@ static uint8_t cmos_read(uint8_t addr) return pio_read8(CMOS_DATA_PORT); } +static void cmos_write(uint8_t addr, uint8_t value) +{ + pio_write8(addr, CMOS_ADDR_PORT); + pio_write8(value, CMOS_DATA_PORT); +} + static bool cmos_update_in_progress(void) { return (cmos_read(RTC_STATUSA) & RTCSA_TUP)?1:0; @@ -422,6 +428,22 @@ static uint8_t cmos_get_reg_val(uint8_t addr) return reg; } +static void cmos_set_reg_val(uint8_t addr, uint8_t value) +{ + int32_t tries = 2000; + + spinlock_obtain(&cmos_lock); + + /* Make sure an update isn't in progress */ + while (cmos_update_in_progress() && (tries != 0)) { + tries -= 1; + } + + cmos_write(addr, value); + + spinlock_release(&cmos_lock); +} + #define TRIGGER_ALARM (RTCIR_ALARM | RTCIR_INT) #define RTC_DELTA 1 /* For RTC and system time may out of sync for no more than 1s */ static inline bool rtc_halted(struct acrn_vrtc *rtc) @@ -514,7 +536,9 @@ static bool vrtc_write(struct acrn_vcpu *vcpu, uint16_t addr, size_t width, if ((width == 1U) && (addr == CMOS_ADDR_PORT)) { vrtc->addr = (uint8_t)(value & 0x7FU); } else { - if (!is_service_vm(vcpu->vm)) { + if (is_service_vm(vcpu->vm)) { + cmos_set_reg_val(vrtc->addr, (uint8_t)(value & 0xFFU)); + } else { switch (vrtc->addr) { case RTC_STATUSA: case RTC_INTR: -- 2.25.1
|
|
[PATCH v6 3/9] hv: support 12 hour mode
Zhao, Yuanyuan
Add support for hour format.
Bit 1 of register B indicates the hour byte format. When it's 0, twelve-hour mode is selected. Then the seventh bit of hour register presents AM as 0 and PM as 1. Signed-off-by: Yuanyuan Zhao <yuanyuan.zhao@...> Reviewed-by: Junjie Mao <junjie.mao@...> --- hypervisor/dm/vrtc.c | 76 +++++++++++++++++++++++++++-- hypervisor/include/dm/mc146818rtc.h | 1 + 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/hypervisor/dm/vrtc.c b/hypervisor/dm/vrtc.c index b9c9d1f4d..79ee2439d 100644 --- a/hypervisor/dm/vrtc.c +++ b/hypervisor/dm/vrtc.c @@ -239,12 +239,12 @@ static time_t rtc_to_secs(const struct acrn_vrtc *vrtc) struct clktime ct; time_t second = VRTC_BROKEN_TIME; const struct rtcdev *rtc= &vrtc->rtcdev; - int32_t err = 0; + int32_t err = 0, hour = 0, pm = 0; uint32_t century = 0, year = 0; do { if (rtcget(rtc, rtc->sec, &ct.sec) < 0 || rtcget(rtc, rtc->min, &ct.min) < 0 || - rtcget(rtc, rtc->hour, &ct.hour) < 0 || rtcget(rtc, rtc->day_of_month, &ct.day) < 0 || + rtcget(rtc, rtc->day_of_month, &ct.day) < 0 || rtcget(rtc, rtc->month, &ct.mon) < 0 || rtcget(rtc, rtc->year, &year) < 0 || rtcget(rtc, rtc->century, ¢ury) < 0) { RTC_ERR("Invalid RTC sec %#x hour %#x day %#x mon %#x year %#x century %#x\n", @@ -254,6 +254,47 @@ static time_t rtc_to_secs(const struct acrn_vrtc *vrtc) break; } + /* + * If 12 hour format is inuse, translate it to 24 hour format here. + */ + pm = 0; + hour = rtc->hour; + if ((rtc->reg_b & RTCSB_24HR) == 0) { + if (hour & 0x80U) { + hour &= ~0x80U; + pm = 1; + } + } + err = rtcget(rtc, hour, &ct.hour); + if (err) { + RTC_ERR("Invalid RTC hour %#x/%d\n", rtc->hour, ct.hour); + break; + } + if ((rtc->reg_b & RTCSB_24HR) == 0) { + if (ct.hour >= 1 && ct.hour <= 12) { + /* + * Convert from 12-hour format to internal 24-hour + * representation as follows: + * + * 12-hour format ct.hour + * 12 AM 0 + * 1 - 11 AM 1 - 11 + * 12 PM 12 + * 1 - 11 PM 13 - 23 + */ + if (ct.hour == 12) { + ct.hour = 0; + } + if (pm) { + ct.hour += 12; + } + } else { + RTC_ERR("Invalid RTC 12-hour format %#x/%d\n", + rtc->hour, ct.hour); + break; + } + } + /* * Ignore 'rtc->dow' because some guests like Linux don't bother * setting it at all while others like OpenBSD/i386 set it incorrectly. @@ -289,12 +330,41 @@ static void secs_to_rtc(time_t rtctime, struct acrn_vrtc *vrtc) { struct clktime ct; struct rtcdev *rtc; + int32_t hour; if (rtctime > 0 && clk_ts_to_ct(rtctime, &ct) == 0) { rtc = &vrtc->rtcdev; rtc->sec = rtcset(rtc, ct.sec); rtc->min = rtcset(rtc, ct.min); - rtc->hour = rtcset(rtc, ct.hour); + + if (rtc->reg_b & RTCSB_24HR) { + hour = ct.hour; + } else { + /* + * Convert to the 12-hour format. + */ + switch (ct.hour) { + case 0: /* 12 AM */ + case 12: /* 12 PM */ + hour = 12; + break; + default: + /* + * The remaining 'ct.hour' values are interpreted as: + * [1 - 11] -> 1 - 11 AM + * [13 - 23] -> 1 - 11 PM + */ + hour = ct.hour % 12; + break; + } + } + + rtc->hour = rtcset(rtc, hour); + + if ((rtc->reg_b & RTCSB_24HR) == 0 && ct.hour >= 12) { + rtc->hour |= 0x80; /* set MSB to indicate PM */ + } + rtc->day_of_week = rtcset(rtc, ct.dow + 1); rtc->day_of_month = rtcset(rtc, ct.day); rtc->month = rtcset(rtc, ct.mon); diff --git a/hypervisor/include/dm/mc146818rtc.h b/hypervisor/include/dm/mc146818rtc.h index 4baf9de69..b23f90d34 100644 --- a/hypervisor/include/dm/mc146818rtc.h +++ b/hypervisor/include/dm/mc146818rtc.h @@ -58,6 +58,7 @@ #define RTCSA_TUP 0x80 /* time update, don't look now */ #define RTC_STATUSB 0x0b /* status register B */ +#define RTCSB_24HR 0x02 /* 0 = 12 hours, 1 = 24 hours */ #define RTCSB_BCD 0x04 /* 0 = BCD, 1 = Binary coded time */ #define RTC_INTR 0x0c /* status register C (R) interrupt source */ -- 2.25.1
|
|
Re: [PATCH] hv: update SSRAM regions EPT memory type to WB
Yonghua Huang
Hi Fei,
toggle quoted messageShow quoted text
-----Original Message----- Make sense. With memory type UC in EPT table, any write to SSRAM region will flush SSRAM cache buffer and make this region cache unlocked. Thanks for comments. -Yonghua acrn_vm *vm)
|
|
Re: [PATCH v5 4/8] hv: add vRTC reg_b and reg_c support
Zhao, Yuanyuan
On 5/7/2022 2:00 PM, Junjie Mao wrote:
Yuanyuan Zhao <yuanyuan.zhao@...> writes:If alarm interrut is enabled and rtc time is in alarm time scale,During setting RTC time, driver will halt RTC update. So supportI do not see any change to the comment here. At least highlight that set the interrupt flag. The interrupt is not acturally triggered for driver will read the register.
|
|
Re: [PATCH v4 7/8] hv: vrtc calibrate function
Junjie Mao
"Zhao, Yuanyuan" <yuanyuan.zhao@...> writes:
On 5/7/2022 1:54 PM, Junjie Mao wrote:We are referring to different problems. I'm talking about the"Zhao, Yuanyuan" <yuanyuan.zhao@...> writes:I think monotonicity is not a problem that only we encounter, becuse RTCOn 5/6/2022 10:23 AM, Junjie Mao wrote:There is no restriction that RTC must be read after an alarm interrupt."Zhao, Yuanyuan" <yuanyuan.zhao@...> writes:system time: 12:30:59 ----------------> get rtc time 12:30:58On 5/6/2022 9:46 AM, Junjie Mao wrote:How is monotonicity derived from the method above?Yuanyuan Zhao <yuanyuan.zhao@...> writes:Monotonicity ensured by calibration period:For TSC's precision (20~100ppm) is lower than physical RTC (less than 20ppm),`Calibrate` is a verb and thus makes `calibrate lock` misleading monotonicity of two consecutive readings from vRTC (regardless of why a VM does that), while you are talking about the calibration between vRTC and system time in the VM. Or I can shorten the calibrate period if you think it's necessary.Theoretically shortening the period cannot avoid this completely. An ad-hoc way is to memorize the last reading (in seconds, as 32-bit integer) of a vRTC and check against it when that VM reads vRTC again (as long as it does not modify the vRTC date and time after its last read). -- Best Regards Junjie Mao
|
|
Re: [PATCH v4 7/8] hv: vrtc calibrate function
Zhao, Yuanyuan
On 5/7/2022 1:54 PM, Junjie Mao wrote:
"Zhao, Yuanyuan" <yuanyuan.zhao@...> writes:I think monotonicity is not a problem that only we encounter, becuse RTC graduation is 1s. Under normal scenario, the use of calibrate system time by RTC may also cause system time rollback though the difference is less than 1s. So trigger alarm and get time again is a necessary option.On 5/6/2022 10:23 AM, Junjie Mao wrote:There is no restriction that RTC must be read after an alarm interrupt."Zhao, Yuanyuan" <yuanyuan.zhao@...> writes:system time: 12:30:59 ----------------> get rtc time 12:30:58On 5/6/2022 9:46 AM, Junjie Mao wrote:How is monotonicity derived from the method above?Yuanyuan Zhao <yuanyuan.zhao@...> writes:Monotonicity ensured by calibration period:For TSC's precision (20~100ppm) is lower than physical RTC (less than 20ppm),`Calibrate` is a verb and thus makes `calibrate lock` misleading Or I can shorten the calibrate period if you think it's necessary.
|
|
Re: [PATCH v5 6/8] hv: add time modification of vrtc
Junjie Mao
Yuanyuan Zhao <yuanyuan.zhao@...> writes:
VRTC for hv used to ignore writes to vRTC register.Reviewed-by: Junjie Mao <junjie.mao@...> ----- Best Regards Junjie Mao
|
|
Re: [PATCH v5 4/8] hv: add vRTC reg_b and reg_c support
Junjie Mao
Yuanyuan Zhao <yuanyuan.zhao@...> writes:
During setting RTC time, driver will halt RTC update. So supportI do not see any change to the comment here. At least highlight that alarm interrupts are not implemented, but only the alarm flag, and why that is sufficient for now. -- Best Regards Junjie Mao + */
|
|
Re: [PATCH v5 3/8] hv: support 12 hour mode
Junjie Mao
Yuanyuan Zhao <yuanyuan.zhao@...> writes:
Add support for hour format.Reviewed-by: Junjie Mao <junjie.mao@...> One stylish comment below. ---Fix the space issue in the FIRST patch that introduces it. -- Best Regards Junjie Mao uint32_t century = 0, year = 0;
|
|