[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH v3 2/4 gnumach] Add HPET timer for small accurate delays
From: |
Samuel Thibault |
Subject: |
Re: [PATCH v3 2/4 gnumach] Add HPET timer for small accurate delays |
Date: |
Thu, 8 Feb 2024 00:28:17 +0100 |
User-agent: |
NeoMutt/20170609 (1.8.3) |
Applied, thanks!
Could you check with your mailer how to prevent it from using the
quoted-printable encoding? git am often has troubles applying your
patches, and I have to fix the patch by hand, it's really unconvenient.
Samuel
Damien Zammit, le mer. 07 févr. 2024 05:02:15 +0000, a ecrit:
> TESTED: This works in qemu correctly
> TESTED: This works on an AMD board with ACPI v2.0 correctly
>
> ---
> i386/i386/apic.c | 89 +++++++++++++++++++++++++++++++++++
> i386/i386/apic.h | 5 ++
> i386/i386at/acpi_parse_apic.c | 35 ++++++++++----
> i386/i386at/acpi_parse_apic.h | 23 +++++++++
> i386/i386at/model_dep.c | 5 ++
> 5 files changed, 149 insertions(+), 8 deletions(-)
>
> diff --git a/i386/i386/apic.c b/i386/i386/apic.c
> index feb49c85..0b5be50f 100644
> --- a/i386/i386/apic.c
> +++ b/i386/i386/apic.c
> @@ -26,6 +26,10 @@
> #include <kern/printf.h>
> #include <kern/kalloc.h>
>
> +/*
> + * Period of HPET timer in nanoseconds
> + */
> +uint32_t hpet_period_nsec;
>
> /*
> * This dummy structure is needed so that CPU_NUMBER can be called
> @@ -362,3 +366,88 @@ lapic_eoi(void)
> {
> lapic->eoi.r = 0;
> }
> +
> +#define HPET32(x) *((volatile uint32_t *)((uint8_t *)hpet_addr + x))
> +#define HPET_CAP_PERIOD 0x04
> +#define HPET_CFG 0x10
> +# define HPET_CFG_ENABLE (1 << 0)
> +# define HPET_LEGACY_ROUTE (1 << 1)
> +#define HPET_COUNTER 0xf0
> +#define HPET_T0_CFG 0x100
> +# define HPET_T0_32BIT_MODE (1 << 8)
> +# define HPET_T0_VAL_SET (1 << 6)
> +# define HPET_T0_TYPE_PERIODIC (1 << 3)
> +# define HPET_T0_INT_ENABLE (1 << 2)
> +#define HPET_T0_COMPARATOR 0x108
> +
> +#define FSEC_PER_NSEC 1000000
> +#define NSEC_PER_USEC 1000
> +
> +/* This function sets up the HPET timer to be in
> + * 32 bit periodic mode and not generating any interrupts.
> + * The timer counts upwards and when it reaches 0xffffffff it
> + * wraps to zero. The timer ticks at a constant rate in nanoseconds which
> + * is stored in hpet_period_nsec variable.
> + */
> +void
> +hpet_init(void)
> +{
> + uint32_t period;
> + uint32_t val;
> +
> + assert(hpet_addr != 0);
> +
> + /* Find out how often the HPET ticks in nanoseconds */
> + period = HPET32(HPET_CAP_PERIOD);
> + hpet_period_nsec = period / FSEC_PER_NSEC;
> + printf("HPET ticks every %d nanoseconds\n", hpet_period_nsec);
> +
> + /* Disable HPET and legacy interrupt routing mode */
> + val = HPET32(HPET_CFG);
> + val = val & ~(HPET_LEGACY_ROUTE | HPET_CFG_ENABLE);
> + HPET32(HPET_CFG) = val;
> +
> + /* Clear the counter */
> + HPET32(HPET_COUNTER) = 0;
> +
> + /* Set up 32 bit periodic timer with no interrupts */
> + val = HPET32(HPET_T0_CFG);
> + val = (val & ~HPET_T0_INT_ENABLE) | HPET_T0_32BIT_MODE |
> HPET_T0_TYPE_PERIODIC | HPET_T0_VAL_SET;
> + HPET32(HPET_T0_CFG) = val;
> +
> + /* Set comparator to max */
> + HPET32(HPET_T0_COMPARATOR) = 0xffffffff;
> +
> + /* Enable the HPET */
> + HPET32(HPET_CFG) |= HPET_CFG_ENABLE;
> +
> + printf("HPET enabled\n");
> +}
> +
> +void
> +hpet_udelay(uint32_t us)
> +{
> + uint32_t start, now;
> + uint32_t max_delay_us = 0xffffffff / NSEC_PER_USEC;
> +
> + if (us > max_delay_us) {
> + printf("HPET ERROR: Delay too long, %d usec, truncating to %d
> usec\n",
> + us, max_delay_us);
> + us = max_delay_us;
> + }
> +
> + /* Convert us to HPET ticks */
> + us = (us * NSEC_PER_USEC) / hpet_period_nsec;
> +
> + start = HPET32(HPET_COUNTER);
> + do {
> + now = HPET32(HPET_COUNTER);
> + } while (now - start < us);
> +}
> +
> +void
> +hpet_mdelay(uint32_t ms)
> +{
> + hpet_udelay(ms * 1000);
> +}
> +
> diff --git a/i386/i386/apic.h b/i386/i386/apic.h
> index 29387d9d..9eef0d8b 100644
> --- a/i386/i386/apic.h
> +++ b/i386/i386/apic.h
> @@ -252,10 +252,15 @@ void calibrate_lapic_timer(void);
> void ioapic_toggle(int pin, int mask);
> void ioapic_configure(void);
>
> +void hpet_init(void);
> +void hpet_udelay(uint32_t us);
> +void hpet_mdelay(uint32_t ms);
> +
> extern int timer_pin;
> extern void intnull(int unit);
> extern volatile ApicLocalUnit* lapic;
> extern int cpu_id_lut[];
> +extern uint32_t *hpet_addr;
>
> #endif
>
> diff --git a/i386/i386at/acpi_parse_apic.c b/i386/i386at/acpi_parse_apic.c
> index dcd5da89..1cfc1791 100644
> --- a/i386/i386at/acpi_parse_apic.c
> +++ b/i386/i386at/acpi_parse_apic.c
> @@ -34,6 +34,7 @@
>
> static struct acpi_apic *apic_madt = NULL;
> unsigned lapic_addr;
> +uint32_t *hpet_addr;
>
> /*
> * acpi_print_info: shows by screen the ACPI's rsdp and rsdt virtual address
> @@ -292,28 +293,37 @@ acpi_get_xsdt(phys_addr_t rsdp_phys, int* acpi_xsdt_n)
> * and the number of entries of RSDT table.
> *
> * Returns a reference to APIC/MADT table if success, NULL if failure.
> + * Also sets hpet_addr to base address of HPET.
> */
> static struct acpi_apic*
> acpi_get_apic(struct acpi_rsdt *rsdt, int acpi_rsdt_n)
> {
> struct acpi_dhdr *descr_header;
> + struct acpi_apic *madt = NULL;
> int check_signature;
> + uint64_t map_addr;
>
> /* Search APIC entries in rsdt table. */
> for (int i = 0; i < acpi_rsdt_n; i++) {
> descr_header = (struct acpi_dhdr*)
> kmem_map_aligned_table(rsdt->entry[i], sizeof(struct acpi_dhdr),
>
> VM_PROT_READ);
>
> - /* Check if the entry contains an APIC. */
> + /* Check if the entry is a MADT */
> check_signature = acpi_check_signature(descr_header->signature,
> ACPI_APIC_SIG, 4*sizeof(uint8_t));
> + if (check_signature == ACPI_SUCCESS)
> + madt = (struct acpi_apic*) descr_header;
>
> + /* Check if the entry is a HPET */
> + check_signature = acpi_check_signature(descr_header->signature,
> ACPI_HPET_SIG, 4*sizeof(uint8_t));
> if (check_signature == ACPI_SUCCESS) {
> - /* If yes, return the APIC. */
> - return (struct acpi_apic*) descr_header;
> + map_addr = ((struct acpi_hpet *)descr_header)->address.addr64;
> + assert (map_addr != 0);
> + hpet_addr = (uint32_t *)kmem_map_aligned_table(map_addr, 1024,
> VM_PROT_READ | VM_PROT_WRITE);
> + printf("HPET at physical address 0x%llx\n", map_addr);
> }
> }
>
> - return NULL;
> + return madt;
> }
>
> /*
> @@ -323,28 +333,37 @@ acpi_get_apic(struct acpi_rsdt *rsdt, int acpi_rsdt_n)
> * and the number of entries of XSDT table.
> *
> * Returns a reference to APIC/MADT table if success, NULL if failure.
> + * Also sets hpet_addr to base address of HPET.
> */
> static struct acpi_apic*
> acpi_get_apic2(struct acpi_xsdt *xsdt, int acpi_xsdt_n)
> {
> struct acpi_dhdr *descr_header;
> + struct acpi_apic *madt = NULL;
> int check_signature;
> + uint64_t map_addr;
>
> /* Search APIC entries in rsdt table. */
> for (int i = 0; i < acpi_xsdt_n; i++) {
> descr_header = (struct acpi_dhdr*)
> kmem_map_aligned_table(xsdt->entry[i], sizeof(struct acpi_dhdr),
>
> VM_PROT_READ);
>
> - /* Check if the entry contains an APIC. */
> + /* Check if the entry is an APIC. */
> check_signature = acpi_check_signature(descr_header->signature,
> ACPI_APIC_SIG, 4*sizeof(uint8_t));
> + if (check_signature == ACPI_SUCCESS)
> + madt = (struct acpi_apic *)descr_header;
>
> + /* Check if the entry is a HPET. */
> + check_signature = acpi_check_signature(descr_header->signature,
> ACPI_HPET_SIG, 4*sizeof(uint8_t));
> if (check_signature == ACPI_SUCCESS) {
> - /* If yes, return the APIC. */
> - return (struct acpi_apic*) descr_header;
> + map_addr = ((struct acpi_hpet *)descr_header)->address.addr64;
> + assert (map_addr != 0);
> + hpet_addr = (uint32_t *)kmem_map_aligned_table(map_addr, 1024,
> VM_PROT_READ | VM_PROT_WRITE);
> + printf("HPET at physical address 0x%llx\n", map_addr);
> }
> }
>
> - return NULL;
> + return madt;
> }
>
> /*
> diff --git a/i386/i386at/acpi_parse_apic.h b/i386/i386at/acpi_parse_apic.h
> index df36bb31..85e01170 100644
> --- a/i386/i386at/acpi_parse_apic.h
> +++ b/i386/i386at/acpi_parse_apic.h
> @@ -91,6 +91,14 @@ struct acpi_xsdt {
> uint64_t entry[0];
> } __attribute__((__packed__));
>
> +struct acpi_address {
> + uint8_t is_io;
> + uint8_t reg_width;
> + uint8_t reg_offset;
> + uint8_t reserved;
> + uint64_t addr64;
> +} __attribute__((__packed__));
> +
> /* APIC table signature. */
> #define ACPI_APIC_SIG "APIC"
>
> @@ -170,6 +178,21 @@ struct acpi_apic_irq_override {
> uint16_t flags;
> } __attribute__((__packed__));
>
> +
> +#define ACPI_HPET_SIG "HPET"
> +
> +/*
> + * HPET High Precision Event Timer structure
> + */
> +struct acpi_hpet {
> + struct acpi_dhdr header;
> + uint32_t id;
> + struct acpi_address address;
> + uint8_t sequence;
> + uint16_t minimum_tick;
> + uint8_t flags;
> +} __attribute__((__packed__));
> +
> int acpi_apic_init(void);
> void acpi_print_info(phys_addr_t rsdp, void *rsdt, int acpi_rsdt_n);
>
> diff --git a/i386/i386at/model_dep.c b/i386/i386at/model_dep.c
> index 173b99f5..b5f56c7d 100644
> --- a/i386/i386at/model_dep.c
> +++ b/i386/i386at/model_dep.c
> @@ -229,6 +229,11 @@ void machine_init(void)
> */
> gdt_descr_tmp.linear_base += apboot_addr;
> apboot_jmp_offset += apboot_addr;
> +
> + /*
> + * Initialize the HPET
> + */
> + hpet_init();
> #endif
> }
>
> --
> 2.43.0
>
>
>
--
Samuel
---
Pour une évaluation indépendante, transparente et rigoureuse !
Je soutiens la Commission d'Évaluation de l'Inria.