From 5e0b8d508ed51004bd836384293be00950ee62c9 Mon Sep 17 00:00:00 2001 From: Pasha Date: Tue, 20 Feb 2024 18:49:50 +0000 Subject: init gnumach copy --- i386/i386at/acpi_parse_apic.c | 650 +++++++++ i386/i386at/acpi_parse_apic.h | 201 +++ i386/i386at/autoconf.c | 149 ++ i386/i386at/autoconf.h | 43 + i386/i386at/biosmem.c | 1070 ++++++++++++++ i386/i386at/biosmem.h | 109 ++ i386/i386at/boothdr.S | 179 +++ i386/i386at/com.c | 900 ++++++++++++ i386/i386at/com.h | 86 ++ i386/i386at/comreg.h | 139 ++ i386/i386at/conf.c | 172 +++ i386/i386at/cons_conf.c | 63 + i386/i386at/cram.h | 86 ++ i386/i386at/disk.h | 89 ++ i386/i386at/elf.h | 61 + i386/i386at/i8250.h | 134 ++ i386/i386at/idt.h | 53 + i386/i386at/immc.c | 134 ++ i386/i386at/immc.h | 31 + i386/i386at/int_init.c | 82 ++ i386/i386at/int_init.h | 35 + i386/i386at/interrupt.S | 142 ++ i386/i386at/ioapic.c | 463 +++++++ i386/i386at/kd.c | 3059 +++++++++++++++++++++++++++++++++++++++++ i386/i386at/kd.h | 744 ++++++++++ i386/i386at/kd_event.c | 392 ++++++ i386/i386at/kd_event.h | 62 + i386/i386at/kd_mouse.c | 800 +++++++++++ i386/i386at/kd_mouse.h | 72 + i386/i386at/kd_queue.c | 109 ++ i386/i386at/kd_queue.h | 86 ++ i386/i386at/kdasm.S | 145 ++ i386/i386at/kdsoft.h | 209 +++ i386/i386at/lpr.c | 285 ++++ i386/i386at/lpr.h | 66 + i386/i386at/mem.c | 42 + i386/i386at/mem.h | 24 + i386/i386at/model_dep.c | 674 +++++++++ i386/i386at/model_dep.h | 39 + i386/i386at/pic_isa.c | 56 + i386/i386at/rtc.c | 242 ++++ i386/i386at/rtc.h | 143 ++ 42 files changed, 12320 insertions(+) create mode 100644 i386/i386at/acpi_parse_apic.c create mode 100644 i386/i386at/acpi_parse_apic.h create mode 100644 i386/i386at/autoconf.c create mode 100644 i386/i386at/autoconf.h create mode 100644 i386/i386at/biosmem.c create mode 100644 i386/i386at/biosmem.h create mode 100644 i386/i386at/boothdr.S create mode 100644 i386/i386at/com.c create mode 100644 i386/i386at/com.h create mode 100644 i386/i386at/comreg.h create mode 100644 i386/i386at/conf.c create mode 100644 i386/i386at/cons_conf.c create mode 100644 i386/i386at/cram.h create mode 100644 i386/i386at/disk.h create mode 100644 i386/i386at/elf.h create mode 100644 i386/i386at/i8250.h create mode 100644 i386/i386at/idt.h create mode 100644 i386/i386at/immc.c create mode 100644 i386/i386at/immc.h create mode 100644 i386/i386at/int_init.c create mode 100644 i386/i386at/int_init.h create mode 100644 i386/i386at/interrupt.S create mode 100644 i386/i386at/ioapic.c create mode 100644 i386/i386at/kd.c create mode 100644 i386/i386at/kd.h create mode 100644 i386/i386at/kd_event.c create mode 100644 i386/i386at/kd_event.h create mode 100644 i386/i386at/kd_mouse.c create mode 100644 i386/i386at/kd_mouse.h create mode 100644 i386/i386at/kd_queue.c create mode 100644 i386/i386at/kd_queue.h create mode 100644 i386/i386at/kdasm.S create mode 100644 i386/i386at/kdsoft.h create mode 100644 i386/i386at/lpr.c create mode 100644 i386/i386at/lpr.h create mode 100644 i386/i386at/mem.c create mode 100644 i386/i386at/mem.h create mode 100644 i386/i386at/model_dep.c create mode 100644 i386/i386at/model_dep.h create mode 100644 i386/i386at/pic_isa.c create mode 100644 i386/i386at/rtc.c create mode 100644 i386/i386at/rtc.h (limited to 'i386/i386at') diff --git a/i386/i386at/acpi_parse_apic.c b/i386/i386at/acpi_parse_apic.c new file mode 100644 index 0000000..1cfc179 --- /dev/null +++ b/i386/i386at/acpi_parse_apic.c @@ -0,0 +1,650 @@ +/* acpi_parse_apic.h - ACPI-MADT table parser. Source file + Copyright (C) 2018 Juan Bosco Garcia + Copyright (C) 2019 2020 Almudena Garcia Jurado-Centurion + Written by Juan Bosco Garcia and Almudena Garcia Jurado-Centurion + + This file is part of Min_SMP. + + Min_SMP 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 2, or (at your option) + any later version. + + Min_SMP 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, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include /* memcmp, memcpy... */ + +#include /* uint16_t, uint32_t... */ + +#include /* machine_slot */ + +#include /* printf */ +#include +#include /* phystokv */ +#include /* lapic, ioapic... */ +#include +#include + +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 + * and the number of entries stored in RSDT table. + * + * Receives as input the references of RSDP and RSDT tables, + * and the number of entries stored in RSDT. + */ +void +acpi_print_info(phys_addr_t rsdp, void *rsdt, int acpi_rsdt_n) +{ + + printf("ACPI:\n"); + printf(" rsdp = 0x%lx\n", rsdp); + printf(" rsdt/xsdt = 0x%p (n = %d)\n", rsdt, acpi_rsdt_n); +} + +/* + * acpi_checksum: calculates the checksum of an ACPI table. + * Receives as input the virtual address of the table. + * + * Returns 0 if success, other value if error. + */ +static uint8_t +acpi_checksum(void *addr, uint32_t length) +{ + uint8_t *bytes = addr; + uint8_t checksum = 0; + unsigned int i; + + /* Sum all bytes of addr */ + for (i = 0; i < length; i++) + checksum += bytes[i]; + + return checksum; +} + +/* + * acpi_check_signature: check if a signature match with the signature of its table. + * + * Receive as parameter both signatures: table signature, the signature which needs to check, + * and real signature, the genuine signature of the table. + * + * Return 0 if success, other if error. + */ + +static int +acpi_check_signature(const uint8_t table_signature[], const char *real_signature, uint8_t length) +{ + return memcmp(table_signature, real_signature, length); +} + + +/* + * acpi_check_rsdp: + * check if the RDSP "candidate" table is the real RSDP table. + * + * Compare the table signature with the ACPI signature for this table + * and check is the checksum is correct. + * + * Receives as input the reference of RSDT table. + * + * Preconditions: RSDP pointer must not be NULL. + * + * Returns 1 if ACPI 1.0 and sets sdt_base + * Returns 2 if ACPI >= 2.0 and sets sdt_base + */ +static int8_t +acpi_check_rsdp(struct acpi_rsdp2 *rsdp, phys_addr_t *sdt_base) +{ + int is_rsdp; + uint8_t cksum; + + /* Check if rsdp signature match with the ACPI RSDP signature. */ + is_rsdp = acpi_check_signature(rsdp->v1.signature, ACPI_RSDP_SIG, 8*sizeof(uint8_t)); + + if (is_rsdp != ACPI_SUCCESS) + return ACPI_BAD_SIGNATURE; + + if (rsdp->v1.revision == 0) { + // ACPI 1.0 + *sdt_base = rsdp->v1.rsdt_addr; + printf("ACPI v1.0\n"); + cksum = acpi_checksum((void *)(&rsdp->v1), sizeof(struct acpi_rsdp)); + + if (cksum != 0) + return ACPI_BAD_CHECKSUM; + + return 1; + + } else if (rsdp->v1.revision == 2) { + // ACPI >= 2.0 + *sdt_base = rsdp->xsdt_addr; + printf("ACPI >= v2.0\n"); + cksum = acpi_checksum((void *)rsdp, sizeof(struct acpi_rsdp2)); + + if (cksum != 0) + return ACPI_BAD_CHECKSUM; + + return 2; + } + + return ACPI_NO_RSDP; +} + +/* + * acpi_check_rsdp_align: check if the RSDP address is aligned. + * Preconditions: The address must not be NULL + * + * Returns ACPI_SUCCESS (0) if success, ACPI_BAD_ALIGN if error + */ + +static int8_t +acpi_check_rsdp_align(void *addr) +{ + /* check alignment. */ + if ((uintptr_t)addr & (ACPI_RSDP_ALIGN-1)) + return ACPI_BAD_ALIGN; + + return ACPI_SUCCESS; +} + +/* + * acpi_search_rsdp: search the rsdp table in a memory range. + * + * Receives as input the initial virtual address, and the lenght + * of memory range. + * + * Preconditions: The start address (addr) must be aligned. + * + * Returns the physical address of rsdp structure if success, 0 if failure. + */ +static phys_addr_t +acpi_search_rsdp(void *addr, uint32_t length, int *is_64bit) +{ + void *end; + int version = 0; + phys_addr_t sdt_base = 0; + + /* Search RDSP in memory space between addr and addr+lenght. */ + for (end = addr+length; addr < end; addr += ACPI_RSDP_ALIGN) { + + /* Check if the current memory block stores the RDSP. */ + if ((addr != NULL) && ((version = acpi_check_rsdp(addr, &sdt_base)) > 0)) { + /* If yes, return RSDT/XSDT address */ + *is_64bit = (version == 2); + return sdt_base; + } + } + + return 0; +} + +/* + * acpi_get_rsdp: tries to find the RSDP table, + * searching It in many memory ranges, as It's written in ACPI Specification. + * + * Returns the reference to RDSP structure if success, 0 if failure. + */ +static phys_addr_t +acpi_get_rsdp(int *is_64bit) +{ + uint16_t *start = 0; + phys_addr_t base = 0; + phys_addr_t rsdp = 0; + + /* EDBA start address. */ + start = (uint16_t*) phystokv(0x040e); + base = phystokv((*start) << 4); /* address = paragraph number * 16 */ + + /* check alignment. */ + if (acpi_check_rsdp_align((void *)base) == ACPI_BAD_ALIGN) + return 0; + rsdp = acpi_search_rsdp((void *)base, 1024, is_64bit); + + if (rsdp == 0) { + /* If RSDP isn't in EDBA, search in the BIOS read-only memory space between 0E0000h and 0FFFFFh */ + rsdp = acpi_search_rsdp((void *)phystokv(0xe0000), 0x100000 - 0x0e0000, is_64bit); + } + + return rsdp; +} + +/* + * acpi_get_rsdt: Get RSDT table reference from RSDP entries. + * + * Receives as input a reference for RSDP table + * and a reference to store the number of entries of RSDT. + * + * Returns the reference to RSDT table if success, NULL if error. + */ +static struct acpi_rsdt* +acpi_get_rsdt(phys_addr_t rsdp_phys, int* acpi_rsdt_n) +{ + struct acpi_rsdt *rsdt = NULL; + int signature_check; + + rsdt = (struct acpi_rsdt*) kmem_map_aligned_table(rsdp_phys, sizeof(struct acpi_rsdt), VM_PROT_READ); + + /* Check if the RSDT mapping is fine. */ + if (rsdt == NULL) + return NULL; + + /* Check is rsdt signature is equals to ACPI RSDT signature. */ + signature_check = acpi_check_signature(rsdt->header.signature, ACPI_RSDT_SIG, + 4*sizeof(uint8_t)); + + if (signature_check != ACPI_SUCCESS) + return NULL; + + /* Calculated number of elements stored in rsdt. */ + *acpi_rsdt_n = (rsdt->header.length - sizeof(rsdt->header)) + / sizeof(rsdt->entry[0]); + + return rsdt; +} + +/* + * acpi_get_xsdt: Get XSDT table reference from RSDPv2 entries. + * + * Receives as input a reference for RSDPv2 table + * and a reference to store the number of entries of XSDT. + * + * Returns the reference to XSDT table if success, NULL if error. + */ +static struct acpi_xsdt* +acpi_get_xsdt(phys_addr_t rsdp_phys, int* acpi_xsdt_n) +{ + struct acpi_xsdt *xsdt = NULL; + int signature_check; + + xsdt = (struct acpi_xsdt*) kmem_map_aligned_table(rsdp_phys, sizeof(struct acpi_xsdt), VM_PROT_READ); + + /* Check if the RSDT mapping is fine. */ + if (xsdt == NULL) + return NULL; + + /* Check is rsdt signature is equals to ACPI RSDT signature. */ + signature_check = acpi_check_signature(xsdt->header.signature, ACPI_XSDT_SIG, + 4*sizeof(uint8_t)); + + if (signature_check != ACPI_SUCCESS) + return NULL; + + /* Calculated number of elements stored in rsdt. */ + *acpi_xsdt_n = (xsdt->header.length - sizeof(xsdt->header)) + / sizeof(xsdt->entry[0]); + + return xsdt; +} + +/* + * acpi_get_apic: get MADT/APIC table from RSDT entries. + * + * Receives as input the RSDT initial address, + * 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 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) { + 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 madt; +} + +/* + * acpi_get_apic2: get MADT/APIC table from XSDT entries. + * + * Receives as input the XSDT initial address, + * 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 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) { + 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 madt; +} + +/* + * acpi_add_lapic: add a new Local APIC to cpu_to_lapic array + * and increase the number of cpus. + * + * Receives as input the Local APIC entry in MADT/APIC table. + */ +static void +acpi_apic_add_lapic(struct acpi_apic_lapic *lapic_entry) +{ + /* If cpu flag is correct */ + if (lapic_entry->flags & 0x1) { + /* Add cpu to processors' list. */ + apic_add_cpu(lapic_entry->apic_id); + } + +} + +/* + * apic_add_ioapic: add a new IOAPIC to IOAPICS array + * and increase the number of IOAPIC. + * + * Receives as input the IOAPIC entry in MADT/APIC table. + */ + +static void +acpi_apic_add_ioapic(struct acpi_apic_ioapic *ioapic_entry) +{ + IoApicData io_apic; + + /* Fill IOAPIC structure with its main fields */ + io_apic.apic_id = ioapic_entry->apic_id; + io_apic.addr = ioapic_entry->addr; + io_apic.gsi_base = ioapic_entry->gsi_base; + io_apic.ioapic = (ApicIoUnit *)kmem_map_aligned_table(ioapic_entry->addr, + sizeof(ApicIoUnit), + VM_PROT_READ | VM_PROT_WRITE); + io_apic.ioapic->select.r = APIC_IO_VERSION; + io_apic.ngsis = ((io_apic.ioapic->window.r >> APIC_IO_ENTRIES_SHIFT) & 0xff) + 1; + + /* Insert IOAPIC in the list. */ + apic_add_ioapic(io_apic); +} + + +/* + * apic_add_ioapic: add a new IOAPIC to IOAPICS list + * and increase the number of IOAPIC. + * + * Receives as input the IOAPIC entry in MADT/APIC table. + */ + +static void +acpi_apic_add_irq_override(struct acpi_apic_irq_override* irq_override) +{ + IrqOverrideData irq_over; + + /* Fills IRQ override structure with its fields */ + irq_over.bus = irq_override->bus; + irq_over.irq = irq_override->irq; + irq_over.gsi = irq_override->gsi; + irq_over.flags = irq_override->flags; + + /* Insert IRQ override in the list */ + apic_add_irq_override(irq_over); +} + + +/* + * apic_parse_table: parse the MADT/APIC table. + * + * Read the APIC/MADT table entry to entry, + * registering the APIC structures (Local APIC, IOAPIC or IRQ override) entries in their lists. + */ + +static int +acpi_apic_parse_table(struct acpi_apic *apic) +{ + struct acpi_apic_dhdr *apic_entry = NULL; + vm_offset_t end = 0; + uint8_t numcpus = 1; + + /* Get the address of first APIC entry */ + apic_entry = (struct acpi_apic_dhdr*) apic->entry; + + /* Get the end address of APIC table */ + end = (vm_offset_t) apic + apic->header.length; + + printf("APIC entry=0x%p end=0x%x\n", apic_entry, end); + + /* Initialize number of cpus */ + numcpus = apic_get_numcpus(); + + /* Search in APIC entry. */ + while ((vm_offset_t)apic_entry < end) { + struct acpi_apic_lapic *lapic_entry; + struct acpi_apic_ioapic *ioapic_entry; + struct acpi_apic_irq_override *irq_override_entry; + + printf("APIC entry=0x%p end=0x%x\n", apic_entry, end); + /* Check entry type. */ + switch(apic_entry->type) { + + /* If APIC entry is a CPU's Local APIC. */ + case ACPI_APIC_ENTRY_LAPIC: + if(numcpus < NCPUS) { + /* Store Local APIC data. */ + lapic_entry = (struct acpi_apic_lapic*) apic_entry; + acpi_apic_add_lapic(lapic_entry); + } + break; + + /* If APIC entry is an IOAPIC. */ + case ACPI_APIC_ENTRY_IOAPIC: + + /* Store IOAPIC data. */ + ioapic_entry = (struct acpi_apic_ioapic*) apic_entry; + acpi_apic_add_ioapic(ioapic_entry); + + break; + + /* If APIC entry is a IRQ Override. */ + case ACPI_APIC_ENTRY_IRQ_OVERRIDE: + + /* Store IRQ Override data. */ + irq_override_entry = (struct acpi_apic_irq_override*) apic_entry; + acpi_apic_add_irq_override(irq_override_entry); + break; + + /* FIXME: There is another unhandled case */ + default: + printf("Unhandled APIC entry type 0x%x\n", apic_entry->type); + break; + } + + /* Get next APIC entry. */ + apic_entry = (struct acpi_apic_dhdr*)((vm_offset_t) apic_entry + + apic_entry->length); + + /* Update number of cpus. */ + numcpus = apic_get_numcpus(); + } + + return ACPI_SUCCESS; +} + + +/* + * acpi_apic_setup: parses the APIC/MADT table, to find the Local APIC and IOAPIC structures + * and the common address for Local APIC. + * + * Receives as input a reference for APIC/MADT table. + * Returns 0 if success. + * + * Fills the cpu_to_lapic and ioapics array, indexed by Kernel ID + * with a relationship between Kernel ID and APIC ID, + * and map the Local APIC common address, to fill the lapic reference. + * + * Precondition: The APIC pointer must not be NULL + */ + +static int +acpi_apic_setup(struct acpi_apic *apic) +{ + ApicLocalUnit* lapic_unit; + uint8_t ncpus, nioapics; + + /* map common lapic address */ + lapic_addr = apic->lapic_addr; + lapic_unit = kmem_map_aligned_table(apic->lapic_addr, sizeof(ApicLocalUnit), + VM_PROT_READ | VM_PROT_WRITE); + + if (lapic_unit == NULL) + return ACPI_NO_LAPIC; + + apic_lapic_init(lapic_unit); + acpi_apic_parse_table(apic); + + ncpus = apic_get_numcpus(); + nioapics = apic_get_num_ioapics(); + + if (ncpus == 0 || nioapics == 0 || ncpus > NCPUS) + return ACPI_APIC_FAILURE; + + /* Refit the apic-cpu array. */ + if(ncpus < NCPUS) { + int refit = apic_refit_cpulist(); + if (refit != 0) + return ACPI_FIT_FAILURE; + } + + apic_generate_cpu_id_lut(); + + return ACPI_SUCCESS; +} + +/* + * acpi_apic_init: find the MADT/APIC table in ACPI tables + * and parses It to find Local APIC and IOAPIC structures. + * Each Local APIC stores the info and control structores for a cpu. + * The IOAPIC controls the communication of the processors with the I/O devices. + * + * Returns 0 if success, -1 if error. + */ +int +acpi_apic_init(void) +{ + phys_addr_t rsdp = 0; + struct acpi_rsdt *rsdt = 0; + struct acpi_xsdt *xsdt = 0; + int acpi_rsdt_n; + int ret_acpi_setup; + int apic_init_success = 0; + int is_64bit = 0; + uint8_t checksum; + + /* Try to get the RSDP physical address. */ + rsdp = acpi_get_rsdp(&is_64bit); + if (rsdp == 0) + return ACPI_NO_RSDP; + + if (!is_64bit) { + /* Try to get the RSDT pointer. */ + rsdt = acpi_get_rsdt(rsdp, &acpi_rsdt_n); + if (rsdt == NULL) + return ACPI_NO_RSDT; + + checksum = acpi_checksum((void *)rsdt, rsdt->header.length); + if (checksum != 0) + return ACPI_BAD_CHECKSUM; + + /* Try to get the APIC table pointer. */ + apic_madt = acpi_get_apic(rsdt, acpi_rsdt_n); + if (apic_madt == NULL) + return ACPI_NO_APIC; + + checksum = acpi_checksum((void *)apic_madt, apic_madt->header.length); + if (checksum != 0) + return ACPI_BAD_CHECKSUM; + + /* Print the ACPI tables addresses. */ + acpi_print_info(rsdp, rsdt, acpi_rsdt_n); + + } else { + /* Try to get the XSDT pointer. */ + xsdt = acpi_get_xsdt(rsdp, &acpi_rsdt_n); + if (xsdt == NULL) + return ACPI_NO_RSDT; + + checksum = acpi_checksum((void *)xsdt, xsdt->header.length); + if (checksum != 0) + return ACPI_BAD_CHECKSUM; + + /* Try to get the APIC table pointer. */ + apic_madt = acpi_get_apic2(xsdt, acpi_rsdt_n); + if (apic_madt == NULL) + return ACPI_NO_APIC; + + checksum = acpi_checksum((void *)apic_madt, apic_madt->header.length); + if (checksum != 0) + return ACPI_BAD_CHECKSUM; + + /* Print the ACPI tables addresses. */ + acpi_print_info(rsdp, xsdt, acpi_rsdt_n); + } + + apic_init_success = apic_data_init(); + if (apic_init_success != ACPI_SUCCESS) + return ACPI_APIC_FAILURE; + + /* + * Starts the parsing of APIC table, to find the APIC structures. + * and enumerate them. This function also find the common Local APIC address. + */ + ret_acpi_setup = acpi_apic_setup(apic_madt); + if (ret_acpi_setup != ACPI_SUCCESS) + return ret_acpi_setup; + + /* Prints a table with the list of each cpu and each IOAPIC with its APIC ID. */ + apic_print_info(); + + return ACPI_SUCCESS; +} diff --git a/i386/i386at/acpi_parse_apic.h b/i386/i386at/acpi_parse_apic.h new file mode 100644 index 0000000..85e0117 --- /dev/null +++ b/i386/i386at/acpi_parse_apic.h @@ -0,0 +1,201 @@ +/* acpi_parse_apic.h - ACPI-MADT table parser. Header file + Copyright (C) 2018 Juan Bosco Garcia + Copyright (C) 2019 2020 Almudena Garcia Jurado-Centurion + Written by Juan Bosco Garcia and Almudena Garcia Jurado-Centurion + + This file is part of Min_SMP. + + Min_SMP 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 2, or (at your option) + any later version. + + Min_SMP 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, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifndef __ACPI_H__ +#define __ACPI_H__ + +#include + +enum ACPI_RETURN { + ACPI_BAD_CHECKSUM = -1, + ACPI_BAD_ALIGN = -2, + ACPI_NO_RSDP = -3, + ACPI_NO_RSDT = -4, + ACPI_BAD_SIGNATURE = -5, + ACPI_NO_APIC = -6, + ACPI_NO_LAPIC = -7, + ACPI_APIC_FAILURE = -8, + ACPI_FIT_FAILURE = -9, + ACPI_SUCCESS = 0, +}; + +#define ACPI_RSDP_ALIGN 16 +#define ACPI_RSDP_SIG "RSD PTR " + +struct acpi_rsdp { + uint8_t signature[8]; + uint8_t checksum; + uint8_t oem_id[6]; + uint8_t revision; + uint32_t rsdt_addr; +} __attribute__((__packed__)); + +struct acpi_rsdp2 { + struct acpi_rsdp v1; + uint32_t length; + uint64_t xsdt_addr; + uint8_t checksum; + uint8_t reserved[3]; +} __attribute__((__packed__)); + +/* + * RSDT Entry Header + * + * Header which stores the descriptors of tables pointed from RDSP's Entry Field + * Includes the signature of the table, to identify each table. + * + * In MADT, the signature is 'APIC'. + */ +struct acpi_dhdr { + uint8_t signature[4]; + uint32_t length; + uint8_t revision; + uint8_t checksum; + uint8_t oem_id[6]; + uint8_t oem_table_id[8]; + uint32_t oem_revision; + uint8_t creator_id[4]; + uint32_t creator_revision; +} __attribute__((__packed__)); + + +#define ACPI_RSDT_SIG "RSDT" + +struct acpi_rsdt { + struct acpi_dhdr header; + uint32_t entry[0]; +} __attribute__((__packed__)); + +#define ACPI_XSDT_SIG "XSDT" + +struct acpi_xsdt { + struct acpi_dhdr header; + 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" + +/* Types value for MADT entries: Local APIC, IOAPIC and IRQ Override. */ +enum ACPI_APIC_ENTRY_TYPE { + ACPI_APIC_ENTRY_LAPIC = 0, + ACPI_APIC_ENTRY_IOAPIC = 1, + ACPI_APIC_ENTRY_IRQ_OVERRIDE = 2, + ACPI_APIC_ENTRY_NONMASK_IRQ = 4 +}; + +/* + * APIC descriptor header + * Define the type of the structure (Local APIC, I/O APIC or others). + * Type: Local APIC (0), I/O APIC (1). + */ +struct acpi_apic_dhdr { + uint8_t type; + uint8_t length; +} __attribute__((__packed__)); + + +/* + * Multiple APIC Description Table (MADT) + * + * Describes the APIC structures which exist in the machine. + * Includes the common address where Local APIC is mapped in main memory. + * + * Entry field stores the descriptors of APIC structures. + */ +struct acpi_apic { + struct acpi_dhdr header; /* Header, which stores the descriptor for RDST's Entry field. */ + uint32_t lapic_addr; /* Local Interrupt Controller Address. */ + uint32_t flags; + struct acpi_apic_dhdr entry[0]; /* Interrupt Controller Structure */ +} __attribute__((__packed__)); + +/* + * Processor Local APIC Structure + * + * Stores information about APIC ID, flags and ACPI Processor UID + */ + +struct acpi_apic_lapic { + struct acpi_apic_dhdr header; + uint8_t processor_id; /* ACPI Processor UID */ + uint8_t apic_id; + uint32_t flags; +} __attribute__((__packed__)); + + +/* + * I/O APIC Structure + * + * Stores information about APIC ID, and I/O APIC tables + */ + +struct acpi_apic_ioapic { + struct acpi_apic_dhdr header; + uint8_t apic_id; + uint8_t reserved; + uint32_t addr; + uint32_t gsi_base; +} __attribute__((__packed__)); + +/* + * IRQ Override structure + * + * Stores information about IRQ override, with busses and IRQ. + */ + +struct acpi_apic_irq_override { + struct acpi_apic_dhdr header; + uint8_t bus; + uint8_t irq; + uint32_t gsi; + 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); + +extern unsigned lapic_addr; + +#endif /* __ACPI_H__ */ diff --git a/i386/i386at/autoconf.c b/i386/i386at/autoconf.c new file mode 100644 index 0000000..5c69988 --- /dev/null +++ b/i386/i386at/autoconf.c @@ -0,0 +1,149 @@ +/* + * Mach Operating System + * Copyright (c) 1993,1992,1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +#include +#include +#include +#include +#include +#ifdef APIC +# include +#else +# include +#endif +#include + +/* initialization typecasts */ +#define SPL_FIVE (vm_offset_t)SPL5 +#define SPL_SIX (vm_offset_t)SPL6 +#define SPL_TTY (vm_offset_t)SPLTTY + + +#if NCOM > 0 +extern struct bus_driver comdriver; +#include +#endif /* NCOM */ + +#if NLPR > 0 +extern struct bus_driver lprdriver; +#include +#endif /* NLPR */ + +struct bus_ctlr bus_master_init[] = { + +/* driver name unit intr address len phys_address + adaptor alive flags spl pic */ + + {0} +}; + + +struct bus_device bus_device_init[] = { + +/* driver name unit intr address am phys_address + adaptor alive ctlr slave flags *mi *next sysdep sysdep */ + +#if NCOM > 0 + {&comdriver, "com", 0, comintr, 0x3f8, 8, 0x3f8, + '?', 0, -1, -1, 0, 0, 0, SPL_TTY, 4}, + {&comdriver, "com", 1, comintr, 0x2f8, 8, 0x2f8, + '?', 0, -1, -1, 0, 0, 0, SPL_TTY, 3}, + {&comdriver, "com", 2, comintr, 0x3e8, 8, 0x3e8, + '?', 0, -1, -1, 0, 0, 0, SPL_TTY, 5}, +#endif /* NCOM > 0 */ + +#ifdef MACH_LPR +#if NLPR > 0 + {&lprdriver, "lpr", 0, lprintr, 0x378, 3, 0x378, + '?', 0, -1, -1, 0, 0, 0, SPL_TTY, 7}, + {&lprdriver, "lpr", 0, lprintr, 0x278, 3, 0x278, + '?', 0, -1, -1, 0, 0, 0, SPL_TTY, 7}, + {&lprdriver, "lpr", 0, lprintr, 0x3bc, 3, 0x3bc, + '?', 0, -1, -1, 0, 0, 0, SPL_TTY, 7}, +#endif /* NLPR > 0 */ +#endif /* MACH_LPR */ + + {0} +}; + +/* + * probeio: + * + * Probe and subsequently attach devices out on the AT bus. + * + * + */ +void probeio(void) +{ + struct bus_device *device; + struct bus_ctlr *master; + int i = 0; + + for (master = bus_master_init; master->driver; master++) + { + if (configure_bus_master(master->name, master->address, + master->phys_address, i, "atbus")) + i++; + } + + for (device = bus_device_init; device->driver; device++) + { + /* ignore what we (should) have found already */ + if (device->alive || device->ctlr >= 0) + continue; + if (configure_bus_device(device->name, device->address, + device->phys_address, i, "atbus")) + i++; + } + +#if MACH_TTD + /* + * Initialize Remote kernel debugger. + */ + ttd_init(); +#endif /* MACH_TTD */ +} + +void take_dev_irq( + const struct bus_device *dev) +{ + int pic = (int)dev->sysdep1; + + if (ivect[pic] == intnull) { + iunit[pic] = dev->unit; + ivect[pic] = dev->intr; + } else { + printf("The device below will clobber IRQ %d (%p).\n", pic, ivect[pic]); + printf("You have two devices at the same IRQ.\n"); + printf("This won't work. Reconfigure your hardware and try again.\n"); + printf("%s%d: port = %zx, spl = %zd, pic = %d.\n", + dev->name, dev->unit, dev->address, + dev->sysdep, dev->sysdep1); + while (1); + } + + unmask_irq(pic); +} diff --git a/i386/i386at/autoconf.h b/i386/i386at/autoconf.h new file mode 100644 index 0000000..81fc5da --- /dev/null +++ b/i386/i386at/autoconf.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * 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 2, 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, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Barry deFreese. + */ +/* + * Device auto configuration. + * + */ + +#ifndef _AUTOCONF_H_ +#define _AUTOCONF_H_ + +#include +#include + +/* + * probeio: + * + * Probe and subsequently attach devices out on the AT bus. + * + * + */ +void probeio(void); + +void take_dev_irq( + const struct bus_device *dev); + +#endif /* _AUTOCONF_H_ */ diff --git a/i386/i386at/biosmem.c b/i386/i386at/biosmem.c new file mode 100644 index 0000000..937c0e3 --- /dev/null +++ b/i386/i386at/biosmem.c @@ -0,0 +1,1070 @@ +/* + * Copyright (c) 2010-2014 Richard Braun. + * + * 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 2 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG 0 + +#define __boot +#define __bootdata +#define __init + +#define boot_memmove memmove +#define boot_panic(s) panic("%s", s) +#define boot_strlen strlen + +#define BOOT_CGAMEM phystokv(0xb8000) +#define BOOT_CGACHARS (80 * 25) +#define BOOT_CGACOLOR 0x7 + +#define BIOSMEM_MAX_BOOT_DATA 64 + +/* + * Boot data descriptor. + * + * The start and end addresses must not be page-aligned, since there + * could be more than one range inside a single page. + */ +struct biosmem_boot_data { + phys_addr_t start; + phys_addr_t end; + boolean_t temporary; +}; + +/* + * Sorted array of boot data descriptors. + */ +static struct biosmem_boot_data biosmem_boot_data_array[BIOSMEM_MAX_BOOT_DATA] + __bootdata; +static unsigned int biosmem_nr_boot_data __bootdata; + +/* + * Maximum number of entries in the BIOS memory map. + * + * Because of adjustments of overlapping ranges, the memory map can grow + * to twice this size. + */ +#define BIOSMEM_MAX_MAP_SIZE 128 + +/* + * Memory range types. + */ +#define BIOSMEM_TYPE_AVAILABLE 1 +#define BIOSMEM_TYPE_RESERVED 2 +#define BIOSMEM_TYPE_ACPI 3 +#define BIOSMEM_TYPE_NVS 4 +#define BIOSMEM_TYPE_UNUSABLE 5 +#define BIOSMEM_TYPE_DISABLED 6 + +/* + * Bitmask corresponding to memory ranges that require narrowing + * to page boundaries. + */ +#define BIOSMEM_MASK_NARROW (((1u << BIOSMEM_TYPE_AVAILABLE) | \ + (1u << BIOSMEM_TYPE_NVS) | \ + (1u << BIOSMEM_TYPE_DISABLED))) + +/* + * Helper macro to test if range type needs narrowing. + */ +#define BIOSMEM_NEEDS_NARROW(t) ((1u << t) & BIOSMEM_MASK_NARROW) + +/* + * Memory map entry. + */ +struct biosmem_map_entry { + uint64_t base_addr; + uint64_t length; + unsigned int type; +}; + +/* + * Memory map built from the information passed by the boot loader. + * + * If the boot loader didn't pass a valid memory map, a simple map is built + * based on the mem_lower and mem_upper multiboot fields. + */ +static struct biosmem_map_entry biosmem_map[BIOSMEM_MAX_MAP_SIZE * 2] + __bootdata; +static unsigned int biosmem_map_size __bootdata; + +/* + * Contiguous block of physical memory. + */ +struct biosmem_segment { + phys_addr_t start; + phys_addr_t end; +}; + +/* + * Physical segment boundaries. + */ +static struct biosmem_segment biosmem_segments[VM_PAGE_MAX_SEGS] __bootdata; + +/* + * Boundaries of the simple bootstrap heap. + * + * This heap is located above BIOS memory. + */ +static phys_addr_t biosmem_heap_start __bootdata; +static phys_addr_t biosmem_heap_bottom __bootdata; +static phys_addr_t biosmem_heap_top __bootdata; +static phys_addr_t biosmem_heap_end __bootdata; + +/* + * Boot allocation policy. + * + * Top-down allocations are normally preferred to avoid unnecessarily + * filling the DMA segment. + */ +static boolean_t biosmem_heap_topdown __bootdata; + +static char biosmem_panic_inval_boot_data[] __bootdata + = "biosmem: invalid boot data"; +static char biosmem_panic_too_many_boot_data[] __bootdata + = "biosmem: too many boot data ranges"; +static char biosmem_panic_too_big_msg[] __bootdata + = "biosmem: too many memory map entries"; +#ifndef MACH_HYP +static char biosmem_panic_setup_msg[] __bootdata + = "biosmem: unable to set up the early memory allocator"; +#endif /* MACH_HYP */ +static char biosmem_panic_noseg_msg[] __bootdata + = "biosmem: unable to find any memory segment"; +static char biosmem_panic_inval_msg[] __bootdata + = "biosmem: attempt to allocate 0 page"; +static char biosmem_panic_nomem_msg[] __bootdata + = "biosmem: unable to allocate memory"; + +void __boot +biosmem_register_boot_data(phys_addr_t start, phys_addr_t end, + boolean_t temporary) +{ + unsigned int i; + + if (start >= end) { + boot_panic(biosmem_panic_inval_boot_data); + } + + if (biosmem_nr_boot_data == ARRAY_SIZE(biosmem_boot_data_array)) { + boot_panic(biosmem_panic_too_many_boot_data); + } + + for (i = 0; i < biosmem_nr_boot_data; i++) { + /* Check if the new range overlaps */ + if ((end > biosmem_boot_data_array[i].start) + && (start < biosmem_boot_data_array[i].end)) { + + /* + * If it does, check whether it's part of another range. + * For example, this applies to debugging symbols directly + * taken from the kernel image. + */ + if ((start >= biosmem_boot_data_array[i].start) + && (end <= biosmem_boot_data_array[i].end)) { + + /* + * If it's completely included, make sure that a permanent + * range remains permanent. + * + * XXX This means that if one big range is first registered + * as temporary, and a smaller range inside of it is + * registered as permanent, the bigger range becomes + * permanent. It's not easy nor useful in practice to do + * better than that. + */ + if (biosmem_boot_data_array[i].temporary != temporary) { + biosmem_boot_data_array[i].temporary = FALSE; + } + + return; + } + + boot_panic(biosmem_panic_inval_boot_data); + } + + if (end <= biosmem_boot_data_array[i].start) { + break; + } + } + + boot_memmove(&biosmem_boot_data_array[i + 1], + &biosmem_boot_data_array[i], + (biosmem_nr_boot_data - i) * sizeof(*biosmem_boot_data_array)); + + biosmem_boot_data_array[i].start = start; + biosmem_boot_data_array[i].end = end; + biosmem_boot_data_array[i].temporary = temporary; + biosmem_nr_boot_data++; +} + +static void __init +biosmem_unregister_boot_data(phys_addr_t start, phys_addr_t end) +{ + unsigned int i; + + if (start >= end) { + panic("%s", biosmem_panic_inval_boot_data); + } + + assert(biosmem_nr_boot_data != 0); + + for (i = 0; biosmem_nr_boot_data; i++) { + if ((start == biosmem_boot_data_array[i].start) + && (end == biosmem_boot_data_array[i].end)) { + break; + } + } + + if (i == biosmem_nr_boot_data) { + return; + } + +#if DEBUG + printf("biosmem: unregister boot data: %llx:%llx\n", + (unsigned long long)biosmem_boot_data_array[i].start, + (unsigned long long)biosmem_boot_data_array[i].end); +#endif /* DEBUG */ + + biosmem_nr_boot_data--; + + boot_memmove(&biosmem_boot_data_array[i], + &biosmem_boot_data_array[i + 1], + (biosmem_nr_boot_data - i) * sizeof(*biosmem_boot_data_array)); +} + +#ifndef MACH_HYP + +static void __boot +biosmem_map_adjust_alignment(struct biosmem_map_entry *e) +{ + uint64_t end = e->base_addr + e->length; + + if (BIOSMEM_NEEDS_NARROW(e->type)) { + e->base_addr = vm_page_round (e->base_addr); + e->length = vm_page_trunc (end) - e->base_addr; + } +} + +static void __boot +biosmem_map_build(const struct multiboot_raw_info *mbi) +{ + struct multiboot_raw_mmap_entry *mb_entry, *mb_end; + struct biosmem_map_entry *start, *entry, *end; + unsigned long addr; + + addr = phystokv(mbi->mmap_addr); + mb_entry = (struct multiboot_raw_mmap_entry *)addr; + mb_end = (struct multiboot_raw_mmap_entry *)(addr + mbi->mmap_length); + start = biosmem_map; + entry = start; + end = entry + BIOSMEM_MAX_MAP_SIZE; + + while ((mb_entry < mb_end) && (entry < end)) { + entry->base_addr = mb_entry->base_addr; + entry->length = mb_entry->length; + entry->type = mb_entry->type; + + mb_entry = (void *)mb_entry + sizeof(mb_entry->size) + mb_entry->size; + + biosmem_map_adjust_alignment(entry); + entry++; + } + + biosmem_map_size = entry - start; +} + +static void __boot +biosmem_map_build_simple(const struct multiboot_raw_info *mbi) +{ + struct biosmem_map_entry *entry; + + entry = biosmem_map; + entry->base_addr = 0; + entry->length = mbi->mem_lower << 10; + entry->type = BIOSMEM_TYPE_AVAILABLE; + biosmem_map_adjust_alignment(entry); + + entry++; + entry->base_addr = BIOSMEM_END; + entry->length = mbi->mem_upper << 10; + entry->type = BIOSMEM_TYPE_AVAILABLE; + biosmem_map_adjust_alignment(entry); + + biosmem_map_size = 2; +} + +#endif /* MACH_HYP */ + +static int __boot +biosmem_map_entry_is_invalid(const struct biosmem_map_entry *entry) +{ + return (entry->base_addr + entry->length) <= entry->base_addr; +} + +static void __boot +biosmem_map_filter(void) +{ + struct biosmem_map_entry *entry; + unsigned int i; + + i = 0; + + while (i < biosmem_map_size) { + entry = &biosmem_map[i]; + + if (biosmem_map_entry_is_invalid(entry)) { + biosmem_map_size--; + boot_memmove(entry, entry + 1, + (biosmem_map_size - i) * sizeof(*entry)); + continue; + } + + i++; + } +} + +static void __boot +biosmem_map_sort(void) +{ + struct biosmem_map_entry tmp; + unsigned int i, j; + + /* + * Simple insertion sort. + */ + for (i = 1; i < biosmem_map_size; i++) { + tmp = biosmem_map[i]; + + for (j = i - 1; j < i; j--) { + if (biosmem_map[j].base_addr < tmp.base_addr) + break; + + biosmem_map[j + 1] = biosmem_map[j]; + } + + biosmem_map[j + 1] = tmp; + } +} + +static void __boot +biosmem_map_adjust(void) +{ + struct biosmem_map_entry tmp, *a, *b, *first, *second; + uint64_t a_end, b_end, last_end; + unsigned int i, j, last_type; + + biosmem_map_filter(); + + /* + * Resolve overlapping areas, giving priority to most restrictive + * (i.e. numerically higher) types. + */ + for (i = 0; i < biosmem_map_size; i++) { + a = &biosmem_map[i]; + a_end = a->base_addr + a->length; + + j = i + 1; + + while (j < biosmem_map_size) { + b = &biosmem_map[j]; + b_end = b->base_addr + b->length; + + if ((a->base_addr >= b_end) || (a_end <= b->base_addr)) { + j++; + continue; + } + + if (a->base_addr < b->base_addr) { + first = a; + second = b; + } else { + first = b; + second = a; + } + + if (a_end > b_end) { + last_end = a_end; + last_type = a->type; + } else { + last_end = b_end; + last_type = b->type; + } + + tmp.base_addr = second->base_addr; + tmp.length = MIN(a_end, b_end) - tmp.base_addr; + tmp.type = MAX(a->type, b->type); + first->length = tmp.base_addr - first->base_addr; + second->base_addr += tmp.length; + second->length = last_end - second->base_addr; + second->type = last_type; + + /* + * Filter out invalid entries. + */ + if (biosmem_map_entry_is_invalid(a) + && biosmem_map_entry_is_invalid(b)) { + *a = tmp; + biosmem_map_size--; + memmove(b, b + 1, (biosmem_map_size - j) * sizeof(*b)); + continue; + } else if (biosmem_map_entry_is_invalid(a)) { + *a = tmp; + j++; + continue; + } else if (biosmem_map_entry_is_invalid(b)) { + *b = tmp; + j++; + continue; + } + + if (tmp.type == a->type) + first = a; + else if (tmp.type == b->type) + first = b; + else { + + /* + * If the overlapping area can't be merged with one of its + * neighbors, it must be added as a new entry. + */ + + if (biosmem_map_size >= ARRAY_SIZE(biosmem_map)) + boot_panic(biosmem_panic_too_big_msg); + + biosmem_map[biosmem_map_size] = tmp; + biosmem_map_size++; + j++; + continue; + } + + if (first->base_addr > tmp.base_addr) + first->base_addr = tmp.base_addr; + + first->length += tmp.length; + j++; + } + } + + biosmem_map_sort(); +} + +/* + * Find addresses of physical memory within a given range. + * + * This function considers the memory map with the [*phys_start, *phys_end] + * range on entry, and returns the lowest address of physical memory + * in *phys_start, and the highest address of unusable memory immediately + * following physical memory in *phys_end. + * + * These addresses are normally used to establish the range of a segment. + */ +static int __boot +biosmem_map_find_avail(phys_addr_t *phys_start, phys_addr_t *phys_end) +{ + const struct biosmem_map_entry *entry, *map_end; + phys_addr_t seg_start, seg_end; + uint64_t start, end; + + seg_start = (phys_addr_t)-1; + seg_end = (phys_addr_t)-1; + map_end = biosmem_map + biosmem_map_size; + + for (entry = biosmem_map; entry < map_end; entry++) { + if (entry->type != BIOSMEM_TYPE_AVAILABLE) + continue; + + start = vm_page_round(entry->base_addr); + + if (start >= *phys_end) + break; + + end = vm_page_trunc(entry->base_addr + entry->length); + + if ((start < end) && (start < *phys_end) && (end > *phys_start)) { + if (seg_start == (phys_addr_t)-1) + seg_start = start; + + seg_end = end; + } + } + + if ((seg_start == (phys_addr_t)-1) || (seg_end == (phys_addr_t)-1)) + return -1; + + if (seg_start > *phys_start) + *phys_start = seg_start; + + if (seg_end < *phys_end) + *phys_end = seg_end; + + return 0; +} + +static void __boot +biosmem_set_segment(unsigned int seg_index, phys_addr_t start, phys_addr_t end) +{ + biosmem_segments[seg_index].start = start; + biosmem_segments[seg_index].end = end; +} + +static phys_addr_t __boot +biosmem_segment_end(unsigned int seg_index) +{ + return biosmem_segments[seg_index].end; +} + +static phys_addr_t __boot +biosmem_segment_size(unsigned int seg_index) +{ + return biosmem_segments[seg_index].end - biosmem_segments[seg_index].start; +} + +static int __boot +biosmem_find_avail_clip(phys_addr_t *avail_start, phys_addr_t *avail_end, + phys_addr_t data_start, phys_addr_t data_end) +{ + phys_addr_t orig_end; + + assert(data_start < data_end); + + orig_end = data_end; + data_start = vm_page_trunc(data_start); + data_end = vm_page_round(data_end); + + if (data_end < orig_end) { + boot_panic(biosmem_panic_inval_boot_data); + } + + if ((data_end <= *avail_start) || (data_start >= *avail_end)) { + return 0; + } + + if (data_start > *avail_start) { + *avail_end = data_start; + } else { + if (data_end >= *avail_end) { + return -1; + } + + *avail_start = data_end; + } + + return 0; +} + +/* + * Find available memory in the given range. + * + * The search starts at the given start address, up to the given end address. + * If a range is found, it is stored through the avail_startp and avail_endp + * pointers. + * + * The range boundaries are page-aligned on return. + */ +static int __boot +biosmem_find_avail(phys_addr_t start, phys_addr_t end, + phys_addr_t *avail_start, phys_addr_t *avail_end) +{ + phys_addr_t orig_start; + unsigned int i; + int error; + + assert(start <= end); + + orig_start = start; + start = vm_page_round(start); + end = vm_page_trunc(end); + + if ((start < orig_start) || (start >= end)) { + return -1; + } + + *avail_start = start; + *avail_end = end; + + for (i = 0; i < biosmem_nr_boot_data; i++) { + error = biosmem_find_avail_clip(avail_start, avail_end, + biosmem_boot_data_array[i].start, + biosmem_boot_data_array[i].end); + + if (error) { + return -1; + } + } + + return 0; +} + +#ifndef MACH_HYP + +static void __boot +biosmem_setup_allocator(const struct multiboot_raw_info *mbi) +{ + phys_addr_t heap_start, heap_end, max_heap_start, max_heap_end; + phys_addr_t start, end; + int error; + + /* + * Find some memory for the heap. Look for the largest unused area in + * upper memory, carefully avoiding all boot data. + */ + end = vm_page_trunc((mbi->mem_upper + 1024) << 10); + + if (end > VM_PAGE_DIRECTMAP_LIMIT) + end = VM_PAGE_DIRECTMAP_LIMIT; + + max_heap_start = 0; + max_heap_end = 0; + start = BIOSMEM_END; + + for (;;) { + error = biosmem_find_avail(start, end, &heap_start, &heap_end); + + if (error) { + break; + } + + if ((heap_end - heap_start) > (max_heap_end - max_heap_start)) { + max_heap_start = heap_start; + max_heap_end = heap_end; + } + + start = heap_end; + } + + if (max_heap_start >= max_heap_end) + boot_panic(biosmem_panic_setup_msg); + + biosmem_heap_start = max_heap_start; + biosmem_heap_end = max_heap_end; + biosmem_heap_bottom = biosmem_heap_start; + biosmem_heap_top = biosmem_heap_end; + biosmem_heap_topdown = TRUE; + + /* Prevent biosmem_free_usable() from releasing the heap */ + biosmem_register_boot_data(biosmem_heap_start, biosmem_heap_end, FALSE); +} + +#endif /* MACH_HYP */ + +static void __boot +biosmem_bootstrap_common(void) +{ + phys_addr_t phys_start, phys_end; + int error; + + biosmem_map_adjust(); + + phys_start = BIOSMEM_BASE; + phys_end = VM_PAGE_DMA_LIMIT; + error = biosmem_map_find_avail(&phys_start, &phys_end); + + if (error) + boot_panic(biosmem_panic_noseg_msg); + +#if !defined(MACH_HYP) && NCPUS > 1 + /* + * Grab an early page for AP boot code which needs to be below 1MB. + */ + assert (phys_start < 0x100000); + apboot_addr = phys_start; + phys_start += PAGE_SIZE; +#endif + + biosmem_set_segment(VM_PAGE_SEG_DMA, phys_start, phys_end); + + phys_start = VM_PAGE_DMA_LIMIT; + +#ifdef VM_PAGE_DMA32_LIMIT +#if VM_PAGE_DMA32_LIMIT < VM_PAGE_DIRECTMAP_LIMIT + phys_end = VM_PAGE_DMA32_LIMIT; + error = biosmem_map_find_avail(&phys_start, &phys_end); + + if (error) + return; + + biosmem_set_segment(VM_PAGE_SEG_DMA32, phys_start, phys_end); + + phys_start = VM_PAGE_DMA32_LIMIT; +#endif +#endif /* VM_PAGE_DMA32_LIMIT */ + + phys_end = VM_PAGE_DIRECTMAP_LIMIT; + error = biosmem_map_find_avail(&phys_start, &phys_end); + + if (error) + return; + + biosmem_set_segment(VM_PAGE_SEG_DIRECTMAP, phys_start, phys_end); + + phys_start = VM_PAGE_DIRECTMAP_LIMIT; + +#ifdef VM_PAGE_DMA32_LIMIT +#if VM_PAGE_DMA32_LIMIT > VM_PAGE_DIRECTMAP_LIMIT + phys_end = VM_PAGE_DMA32_LIMIT; + error = biosmem_map_find_avail(&phys_start, &phys_end); + + if (error) + return; + + biosmem_set_segment(VM_PAGE_SEG_DMA32, phys_start, phys_end); + + phys_start = VM_PAGE_DMA32_LIMIT; +#endif +#endif /* VM_PAGE_DMA32_LIMIT */ + + phys_end = VM_PAGE_HIGHMEM_LIMIT; + error = biosmem_map_find_avail(&phys_start, &phys_end); + + if (error) + return; + + biosmem_set_segment(VM_PAGE_SEG_HIGHMEM, phys_start, phys_end); +} + +#ifdef MACH_HYP + +void +biosmem_xen_bootstrap(void) +{ + struct biosmem_map_entry *entry; + + entry = biosmem_map; + entry->base_addr = 0; + entry->length = boot_info.nr_pages << PAGE_SHIFT; + entry->type = BIOSMEM_TYPE_AVAILABLE; + + biosmem_map_size = 1; + + biosmem_bootstrap_common(); + + biosmem_heap_start = _kvtophys(boot_info.pt_base) + + (boot_info.nr_pt_frames + 3) * 0x1000; + biosmem_heap_end = boot_info.nr_pages << PAGE_SHIFT; + +#ifndef __LP64__ + if (biosmem_heap_end > VM_PAGE_DIRECTMAP_LIMIT) + biosmem_heap_end = VM_PAGE_DIRECTMAP_LIMIT; +#endif /* __LP64__ */ + + biosmem_heap_bottom = biosmem_heap_start; + biosmem_heap_top = biosmem_heap_end; + + /* + * XXX Allocation on Xen are initially bottom-up : + * At the "start of day", only 512k are available after the boot + * data. The pmap module then creates a 4g mapping so all physical + * memory is available, but it uses this allocator to do so. + * Therefore, it must return pages from this small 512k regions + * first. + */ + biosmem_heap_topdown = FALSE; + + /* + * Prevent biosmem_free_usable() from releasing the Xen boot information + * and the heap. + */ + biosmem_register_boot_data(0, biosmem_heap_end, FALSE); +} + +#else /* MACH_HYP */ + +void __boot +biosmem_bootstrap(const struct multiboot_raw_info *mbi) +{ + if (mbi->flags & MULTIBOOT_LOADER_MMAP) + biosmem_map_build(mbi); + else + biosmem_map_build_simple(mbi); + + biosmem_bootstrap_common(); + biosmem_setup_allocator(mbi); +} + +#endif /* MACH_HYP */ + +unsigned long __boot +biosmem_bootalloc(unsigned int nr_pages) +{ + unsigned long addr, size; + + size = vm_page_ptoa(nr_pages); + + if (size == 0) + boot_panic(biosmem_panic_inval_msg); + + if (biosmem_heap_topdown) { + addr = biosmem_heap_top - size; + + if ((addr < biosmem_heap_start) || (addr > biosmem_heap_top)) { + boot_panic(biosmem_panic_nomem_msg); + } + + biosmem_heap_top = addr; + } else { + unsigned long end; + + addr = biosmem_heap_bottom; + end = addr + size; + + if ((end > biosmem_heap_end) || (end < biosmem_heap_bottom)) { + boot_panic(biosmem_panic_nomem_msg); + } + + biosmem_heap_bottom = end; + } + + return addr; +} + +phys_addr_t __boot +biosmem_directmap_end(void) +{ + if (biosmem_segment_size(VM_PAGE_SEG_DIRECTMAP) != 0) + return biosmem_segment_end(VM_PAGE_SEG_DIRECTMAP); +#if defined(VM_PAGE_DMA32_LIMIT) && (VM_PAGE_DMA32_LIMIT < VM_PAGE_DIRECTMAP_LIMIT) + if (biosmem_segment_size(VM_PAGE_SEG_DMA32) != 0) + return biosmem_segment_end(VM_PAGE_SEG_DMA32); +#endif + return biosmem_segment_end(VM_PAGE_SEG_DMA); +} + +static const char * __init +biosmem_type_desc(unsigned int type) +{ + switch (type) { + case BIOSMEM_TYPE_AVAILABLE: + return "available"; + case BIOSMEM_TYPE_RESERVED: + return "reserved"; + case BIOSMEM_TYPE_ACPI: + return "ACPI"; + case BIOSMEM_TYPE_NVS: + return "ACPI NVS"; + case BIOSMEM_TYPE_UNUSABLE: + return "unusable"; + default: + return "unknown (reserved)"; + } +} + +static void __init +biosmem_map_show(void) +{ + const struct biosmem_map_entry *entry, *end; + + printf("biosmem: physical memory map:\n"); + + for (entry = biosmem_map, end = entry + biosmem_map_size; + entry < end; + entry++) + printf("biosmem: %018"PRIx64":%018"PRIx64", %s\n", entry->base_addr, + entry->base_addr + entry->length, + biosmem_type_desc(entry->type)); + +#if DEBUG + printf("biosmem: heap: %llx:%llx\n", + (unsigned long long)biosmem_heap_start, + (unsigned long long)biosmem_heap_end); +#endif +} + +static void __init +biosmem_load_segment(struct biosmem_segment *seg, uint64_t max_phys_end) +{ + phys_addr_t phys_start, phys_end, avail_start, avail_end; + unsigned int seg_index; + + phys_start = seg->start; + phys_end = seg->end; + seg_index = seg - biosmem_segments; + + if (phys_end > max_phys_end) { + if (max_phys_end <= phys_start) { + printf("biosmem: warning: segment %s physically unreachable, " + "not loaded\n", vm_page_seg_name(seg_index)); + return; + } + + printf("biosmem: warning: segment %s truncated to %#"PRIx64"\n", + vm_page_seg_name(seg_index), max_phys_end); + phys_end = max_phys_end; + } + + vm_page_load(seg_index, phys_start, phys_end); + + /* + * Clip the remaining available heap to fit it into the loaded + * segment if possible. + */ + + if ((biosmem_heap_top > phys_start) && (biosmem_heap_bottom < phys_end)) { + if (biosmem_heap_bottom >= phys_start) { + avail_start = biosmem_heap_bottom; + } else { + avail_start = phys_start; + } + + if (biosmem_heap_top <= phys_end) { + avail_end = biosmem_heap_top; + } else { + avail_end = phys_end; + } + + vm_page_load_heap(seg_index, avail_start, avail_end); + } +} + +void __init +biosmem_setup(void) +{ + struct biosmem_segment *seg; + unsigned int i; + + biosmem_map_show(); + + for (i = 0; i < ARRAY_SIZE(biosmem_segments); i++) { + if (biosmem_segment_size(i) == 0) + break; + + seg = &biosmem_segments[i]; + biosmem_load_segment(seg, VM_PAGE_HIGHMEM_LIMIT); + } +} + +static void __init +biosmem_unregister_temporary_boot_data(void) +{ + struct biosmem_boot_data *data; + unsigned int i; + + for (i = 0; i < biosmem_nr_boot_data; i++) { + data = &biosmem_boot_data_array[i]; + + if (!data->temporary) { + continue; + } + + biosmem_unregister_boot_data(data->start, data->end); + i = (unsigned int)-1; + } +} + +static void __init +biosmem_free_usable_range(phys_addr_t start, phys_addr_t end) +{ + struct vm_page *page; + +#if DEBUG + printf("biosmem: release to vm_page: %llx:%llx (%lluk)\n", + (unsigned long long)start, (unsigned long long)end, + (unsigned long long)((end - start) >> 10)); +#endif + + while (start < end) { + page = vm_page_lookup_pa(start); + assert(page != NULL); + vm_page_manage(page); + start += PAGE_SIZE; + } +} + +static void __init +biosmem_free_usable_entry(phys_addr_t start, phys_addr_t end) +{ + phys_addr_t avail_start, avail_end; + int error; + + for (;;) { + error = biosmem_find_avail(start, end, &avail_start, &avail_end); + + if (error) { + break; + } + + biosmem_free_usable_range(avail_start, avail_end); + start = avail_end; + } +} + +void __init +biosmem_free_usable(void) +{ + struct biosmem_map_entry *entry; + uint64_t start, end; + unsigned int i; + + biosmem_unregister_temporary_boot_data(); + + for (i = 0; i < biosmem_map_size; i++) { + entry = &biosmem_map[i]; + + if (entry->type != BIOSMEM_TYPE_AVAILABLE) + continue; + + start = vm_page_round(entry->base_addr); + + if (start >= VM_PAGE_HIGHMEM_LIMIT) + break; + + end = vm_page_trunc(entry->base_addr + entry->length); + + if (end > VM_PAGE_HIGHMEM_LIMIT) { + end = VM_PAGE_HIGHMEM_LIMIT; + } + + if (start < BIOSMEM_BASE) + start = BIOSMEM_BASE; + + if (start >= end) { + continue; + } + + biosmem_free_usable_entry(start, end); + } +} + +boolean_t +biosmem_addr_available(phys_addr_t addr) +{ + struct biosmem_map_entry *entry; + unsigned i; + + if (addr < BIOSMEM_BASE) + return FALSE; + + for (i = 0; i < biosmem_map_size; i++) { + entry = &biosmem_map[i]; + + if (addr >= entry->base_addr && addr < entry->base_addr + entry->length) + return entry->type == BIOSMEM_TYPE_AVAILABLE; + } + return FALSE; +} diff --git a/i386/i386at/biosmem.h b/i386/i386at/biosmem.h new file mode 100644 index 0000000..76ab23a --- /dev/null +++ b/i386/i386at/biosmem.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2010-2014 Richard Braun. + * + * 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 2 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 . + */ + +#ifndef _X86_BIOSMEM_H +#define _X86_BIOSMEM_H + +#include +#include + +/* + * Address where the address of the Extended BIOS Data Area segment can be + * found. + */ +#define BIOSMEM_EBDA_PTR 0x40e + +/* + * Significant low memory addresses. + * + * The first 64 KiB are reserved for various reasons (e.g. to preserve BIOS + * data and to work around data corruption on some hardware). + */ +#define BIOSMEM_BASE 0x010000 +#define BIOSMEM_BASE_END 0x0a0000 +#define BIOSMEM_EXT_ROM 0x0e0000 +#define BIOSMEM_ROM 0x0f0000 +#define BIOSMEM_END 0x100000 + +/* + * Report reserved addresses to the biosmem module. + * + * Once all boot data have been registered, the user can set up the + * early page allocator. + * + * If the range is marked temporary, it will be unregistered when + * biosmem_free_usable() is called, so that pages that used to store + * these boot data may be released to the VM system. + */ +void biosmem_register_boot_data(phys_addr_t start, phys_addr_t end, + boolean_t temporary); + +/* + * Initialize the early page allocator. + * + * This function uses the memory map provided by the boot loader along + * with the registered boot data addresses to set up a heap of free pages + * of physical memory. + * + * Note that on Xen, this function registers all the Xen boot information + * as boot data itself. + */ +#ifdef MACH_HYP +void biosmem_xen_bootstrap(void); +#else /* MACH_HYP */ +void biosmem_bootstrap(const struct multiboot_raw_info *mbi); +#endif /* MACH_HYP */ + +/* + * Allocate contiguous physical pages during bootstrap. + * + * The pages returned are guaranteed to be part of the direct physical + * mapping when paging is enabled. + * + * This function should only be used to allocate initial page table pages. + * Those pages are later loaded into the VM system (as reserved pages) + * which means they can be freed like other regular pages. Users should + * fix up the type of those pages once the VM system is initialized. + */ +unsigned long biosmem_bootalloc(unsigned int nr_pages); + +/* + * Return the limit of physical memory that can be directly mapped. + */ +phys_addr_t biosmem_directmap_end(void); + +/* + * Set up physical memory based on the information obtained during bootstrap + * and load it in the VM system. + */ +void biosmem_setup(void); + +/* + * Free all usable memory. + * + * This function releases all pages that aren't used by boot data and have + * not already been loaded into the VM system. + */ +void biosmem_free_usable(void); + +/* + * Tell whether this address is marked as available in the biosmem and thus used + * for usable memory. + */ +boolean_t biosmem_addr_available(phys_addr_t addr); + +#endif /* _X86_BIOSMEM_H */ diff --git a/i386/i386at/boothdr.S b/i386/i386at/boothdr.S new file mode 100644 index 0000000..daaf57d --- /dev/null +++ b/i386/i386at/boothdr.S @@ -0,0 +1,179 @@ + +#include +#include +#include +#include + + /* + * This section will be put first into .text. See also i386/ldscript. + */ + .section .text.start,"ax" + + /* We should never be entered this way. */ + .globl start,_start +start: +_start: + jmp boot_entry + + /* MultiBoot header - see multiboot.h. */ +#define MULTIBOOT_MAGIC 0x1BADB002 +#ifdef __ELF__ +#define MULTIBOOT_FLAGS 0x00000003 +#else /* __ELF__ */ +#define MULTIBOOT_FLAGS 0x00010003 +#endif /* __ELF__ */ + P2ALIGN(2) +boot_hdr: + .long MULTIBOOT_MAGIC + .long MULTIBOOT_FLAGS + /* + * The next item here is the checksum. + * XX this works OK until we need at least the 30th bit. + */ + .long - (MULTIBOOT_MAGIC+MULTIBOOT_FLAGS) +#ifndef __ELF__ /* a.out kludge */ + .long boot_hdr /* header_addr */ + .long _start /* load_addr */ + .long _edata /* load_end_addr */ + .long _end /* bss_end_addr */ + .long boot_entry /* entry */ +#endif /* __ELF__ */ + +boot_entry: + movl $percpu_array - KERNELBASE, %eax + movw %ax, boot_percpu_low - KERNELBASE + shr $16, %eax + movb %al, boot_percpu_med - KERNELBASE + shr $8, %ax + movb %al, boot_percpu_high - KERNELBASE + + /* use segmentation to offset ourself. */ + lgdt boot_gdt_descr - KERNELBASE + ljmp $0x8,$0f +0: + movw $0x0,%ax + movw %ax,%ds + movw %ax,%es + movw %ax,%fs + movw %ax,%gs + movw $0x10,%ax + movw %ax,%ds + movw %ax,%es + movw %ax,%ss + movw $0x68,%ax + movw %ax,%gs + + /* Switch to our own interrupt stack. */ + movl $solid_intstack+INTSTACK_SIZE-4, %esp + andl $0xfffffff0,%esp + + /* Enable local apic in xAPIC mode */ + xorl %eax, %eax + xorl %edx, %edx + movl $APIC_MSR, %ecx + rdmsr + orl $APIC_MSR_ENABLE, %eax + orl $APIC_MSR_BSP, %eax + andl $(~APIC_MSR_X2APIC), %eax + movl $APIC_MSR, %ecx + wrmsr + + /* Reset EFLAGS to a known state. */ + pushl $0 + popf + + /* Clear uninitialized data. */ + lea _edata,%edi + lea _end,%ecx + subl %edi,%ecx + xorl %eax,%eax + rep + stosb + + /* Push the boot_info pointer to be the second argument. */ + pushl %ebx + + /* Fix ifunc entries */ + movl $__rel_iplt_start,%esi + movl $__rel_iplt_end,%edi +iplt_cont: + cmpl %edi,%esi + jae iplt_done + movl (%esi),%ebx /* r_offset */ + movb 4(%esi),%al /* info */ + cmpb $42,%al /* IRELATIVE */ + jnz iplt_next + call *(%ebx) /* call ifunc */ + movl %eax,(%ebx) /* fixed address */ +iplt_next: + addl $8,%esi + jmp iplt_cont +iplt_done: + + /* Jump into C code. */ + call EXT(c_boot_entry) + +.align 16 + .word 0 +boot_gdt_descr: + .word 14*8-1 + .long boot_gdt - KERNELBASE +.align 16 +boot_gdt: + /* 0 */ + .quad 0 + + /* boot CS = 0x08 */ + .word 0xffff + .word (-KERNELBASE) & 0xffff + .byte ((-KERNELBASE) >> 16) & 0xff + .byte ACC_PL_K | ACC_CODE_R | ACC_P + .byte ((SZ_32 | SZ_G) << 4) | 0xf + .byte ((-KERNELBASE) >> 24) & 0xff + + /* boot DS = 0x10 */ + .word 0xffff + .word (-KERNELBASE) & 0xffff + .byte ((-KERNELBASE) >> 16) & 0xff + .byte ACC_PL_K | ACC_DATA_W | ACC_P + .byte ((SZ_32 | SZ_G) << 4) | 0xf + .byte ((-KERNELBASE) >> 24) & 0xff + + /* LDT = 0x18 */ + .quad 0 + + /* TSS = 0x20 */ + .quad 0 + + /* USER_LDT = 0x28 */ + .quad 0 + + /* USER_TSS = 0x30 */ + .quad 0 + + /* LINEAR = 0x38 */ + .quad 0 + + /* FPREGS = 0x40 */ + .quad 0 + + /* USER_GDT = 0x48 and 0x50 */ + .quad 0 + .quad 0 + + /* USER_TSS64 = 0x58 */ + .quad 0 + + /* USER_TSS64 = 0x60 */ + .quad 0 + + /* boot GS = 0x68 */ + .word 0xffff +boot_percpu_low: + .word 0 +boot_percpu_med: + .byte 0 + .byte ACC_PL_K | ACC_DATA_W | ACC_P + .byte ((SZ_32 | SZ_G) << 4) | 0xf +boot_percpu_high: + .byte 0 diff --git a/i386/i386at/com.c b/i386/i386at/com.c new file mode 100644 index 0000000..bfe353c --- /dev/null +++ b/i386/i386at/com.c @@ -0,0 +1,900 @@ +/* + * Mach Operating System + * Copyright (c) 1994,1993,1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +#if NCOM > 0 + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +static void comparam(int); + +static vm_offset_t com_std[NCOM] = { 0 }; +struct bus_device *cominfo[NCOM]; +struct bus_driver comdriver = { + comprobe, 0, comattach, 0, com_std, "com", cominfo, 0, 0, 0}; + +struct tty com_tty[NCOM]; +int commodem[NCOM]; +int comcarrier[NCOM] = {0, 0,}; +boolean_t comfifo[NCOM]; +boolean_t comtimer_active; +int comtimer_state[NCOM]; + +#define RCBAUD B115200 +static int rcline = -1; +static struct bus_device *comcndev; + +/* XX */ +extern char *kernel_cmdline; + +#define ISPEED B115200 +#define IFLAGS (EVENP|ODDP|ECHO|CRMOD|XTABS|LITOUT) + +u_short divisorreg[] = { + 0, 2304, 1536, 1047, /* 0, 50, 75, 110*/ + 857, 768, 576, 384, 192, /* 134.5, 150, 200, 300, 600*/ + 96, 64, 48, /* 1200, 1800, 2000, 2400 */ + 24, 12, /* 3600, 4800, 7200, 9600 */ + 6, 3, 2, 1}; /* 19200, 38400, 56000,115200 */ + + +/* + * + * Probes are called during kernel boot: return 1 to mean that + * the relevant device is present today. + * + */ +static int +comprobe_general(struct bus_device *dev, int noisy) +{ + u_short addr = dev->address; + int unit = dev->unit; + int oldctl, oldmsb; + char *type = "8250"; + int i; + + if ((unit < 0) || (unit >= NCOM)) { + printf("com %d out of range\n", unit); + return(0); + } + oldctl = inb(LINE_CTL(addr)); /* Save old value of LINE_CTL */ + oldmsb = inb(BAUD_MSB(addr)); /* Save old value of BAUD_MSB */ + outb(LINE_CTL(addr), 0); /* Select INTR_ENAB */ + outb(BAUD_MSB(addr), 0); + if (inb(BAUD_MSB(addr)) != 0) + { + outb(LINE_CTL(addr), oldctl); + outb(BAUD_MSB(addr), oldmsb); + return 0; + } + outb(LINE_CTL(addr), iDLAB); /* Select BAUD_MSB */ + outb(BAUD_MSB(addr), 255); + if (inb(BAUD_MSB(addr)) != 255) + { + outb(LINE_CTL(addr), oldctl); + outb(BAUD_MSB(addr), oldmsb); + return 0; + } + outb(LINE_CTL(addr), 0); /* Select INTR_ENAB */ + if (inb(BAUD_MSB(addr)) != 0) /* Check that it has kept its value*/ + { + outb(LINE_CTL(addr), oldctl); + outb(BAUD_MSB(addr), oldmsb); + return 0; + } + + /* Com port found, now check what chip it has */ + + for(i = 0; i < 256; i++) /* Is there Scratch register */ + { + outb(SCR(addr), i); + if (inb(SCR(addr)) != i) + break; + } + if (i == 256) + { /* Yes == 450 or 460 */ + outb(SCR(addr), 0); + type = "82450 or 16450"; + outb(FIFO_CTL(addr), iFIFOENA | iFIFO14CH); /* Enable fifo */ + if ((inb(FIFO_CTL(addr)) & iFIFO14CH) != 0) + { /* Was it successful */ + /* if both bits are not set then broken xx550 */ + if ((inb(FIFO_CTL(addr)) & iFIFO14CH) == iFIFO14CH) + { + type = "82550 or 16550"; + comfifo[unit] = TRUE; + } + else + { + type = "82550 or 16550 with non-working FIFO"; + } + outb(INTR_ID(addr), 0x00); /* Disable fifos */ + } + } + if (noisy) + printf("com%d: %s chip.\n", unit, type); + return 1; +} + +/* + * Probe routine for use during kernel startup when it is probing + * all of bus_device_init + */ +int +comprobe(vm_offset_t port, struct bus_ctlr *dev) +{ + return comprobe_general((struct bus_device *)dev, /*noisy*/ 0); +} + +/* + * Probe routine for use by the console + */ +int +comcnprobe(struct consdev *cp) +{ + struct bus_device *b; + int maj, unit, pri; + +#define CONSOLE_PARAMETER " console=com" + u_char *console = (u_char *) strstr(kernel_cmdline, CONSOLE_PARAMETER); + + if (console) + mach_atoi(console + strlen(CONSOLE_PARAMETER), &rcline); + + if (strncmp(kernel_cmdline, CONSOLE_PARAMETER + 1, + strlen(CONSOLE_PARAMETER) - 1) == 0) + mach_atoi((u_char*)kernel_cmdline + strlen(CONSOLE_PARAMETER) - 1, + &rcline); + + maj = 0; + unit = -1; + pri = CN_DEAD; + + for (b = bus_device_init; b->driver; b++) + if (strcmp(b->name, "com") == 0 + && b->unit == rcline + && comprobe_general(b, /*quiet*/ 0)) + { + /* Found one */ + comcndev = b; + unit = b->unit; + pri = CN_REMOTE; + break; + } + + cp->cn_dev = makedev(maj, unit); + cp->cn_pri = pri; + + return 0; +} + + +/* + * + * Device Attach's are called during kernel boot, but only if the matching + * device Probe returned a 1. + * + */ +void +comattach(struct bus_device *dev) +{ + u_char unit = dev->unit; + u_short addr = dev->address; + + if (unit >= NCOM) { + printf(", disabled by NCOM configuration\n"); + return; + } + + take_dev_irq(dev); + printf(", port = %zx, spl = %zu, pic = %d. (DOS COM%d)", + dev->address, dev->sysdep, dev->sysdep1, unit+1); + +/* comcarrier[unit] = addr->flags;*/ + commodem[unit] = 0; + + outb(INTR_ENAB(addr), 0); + outb(MODEM_CTL(addr), 0); + while (!(inb(INTR_ID(addr))&1)) { + (void) inb(LINE_STAT (addr)); /* reset overrun error etc */ + (void) inb(TXRX (addr)); /* reset data-ready */ + (void) inb(MODEM_STAT(addr)); /* reset modem status reg */ + } +} + +/* + * Attach/init routine for console. This isn't called by + * configure_bus_device which sets the alive, adaptor, and minfo + * fields of the bus_device struct (comattach is), therefore we do + * that by hand. + */ +int +comcninit(struct consdev *cp) +{ + u_char unit = comcndev->unit; + u_short addr = comcndev->address; + + take_dev_irq(comcndev); + + comcndev->alive = 1; + comcndev->adaptor = 0; + cominfo[minor(cp->cn_dev)] = comcndev; + + outb(LINE_CTL(addr), iDLAB); + outb(BAUD_LSB(addr), divisorreg[RCBAUD] & 0xff); + outb(BAUD_MSB(addr), divisorreg[RCBAUD] >>8); + outb(LINE_CTL(addr), i8BITS); + outb(INTR_ENAB(addr), 0); + outb(MODEM_CTL(addr), iDTR|iRTS|iOUT2); + + { + char msg[128]; + volatile unsigned char *p = (volatile unsigned char *)phystokv(0xb8000); + int i; + + sprintf(msg, " **** using COM port %d for console ****", + unit+1); + for (i = 0; msg[i]; i++) { + p[2*i] = msg[i]; + p[2*i+1] = (0<<7) /* blink */ + | (0x0<<4) /* bg */ + | (1<<3) /* hi-intensity */ + | 0x4; /* fg */ + } + } + + return 0; +} + +/* + * Probe for COM after autoconfiguration. + * Used to handle PCMCIA modems, which may appear + * at any time. + */ +static boolean_t com_reprobe( + int unit) +{ + struct bus_device *device; + + /* + * Look for COM device in the device + * initialization list. It must not be alive + * (otherwise we would have opened it already). + */ + for (device = bus_device_init; device->driver; device++) { + if (device->driver == &comdriver && device->unit == unit && + !device->alive && device->ctlr == (char)-1) + { + /* + * Found an entry for com port . + * Probe it. + */ + if (configure_bus_device(device->name, + device->address, + device->phys_address, + 0, + "atbus")) + return TRUE; + } + } + return FALSE; +} + +io_return_t comopen( + dev_t dev, + int flag, + io_req_t ior) +{ + int unit = minor(dev); + u_short addr; + struct bus_device *isai; + struct tty *tp; + spl_t s; + io_return_t result; + + if (unit >= NCOM) + return D_NO_SUCH_DEVICE; /* no such device */ + if ((isai = cominfo[unit]) == 0 || isai->alive == 0) { + /* + * Try to probe it again + */ + if (!com_reprobe(unit)) + return D_NO_SUCH_DEVICE; + if ((isai = cominfo[unit]) == 0 || isai->alive == 0) + return D_NO_SUCH_DEVICE; + } + tp = &com_tty[unit]; + + if ((tp->t_state & (TS_ISOPEN|TS_WOPEN)) == 0) { + ttychars(tp); + tp->t_addr = (char *)isai->address; + tp->t_dev = dev; + tp->t_oproc = comstart; + tp->t_stop = comstop; + tp->t_mctl = commctl; + tp->t_getstat = comgetstat; + tp->t_setstat = comsetstat; + if (tp->t_ispeed == 0) { + tp->t_ispeed = ISPEED; + tp->t_ospeed = ISPEED; + tp->t_flags = IFLAGS; + tp->t_state &= ~TS_BUSY; + } + } +/*rvb tp->t_state |= TS_WOPEN; */ + if ((tp->t_state & TS_ISOPEN) == 0) + comparam(unit); + addr = (uintptr_t)tp->t_addr; + + s = spltty(); + if (!comcarrier[unit]) /* not originating */ + tp->t_state |= TS_CARR_ON; + else { + int modem_stat = inb(MODEM_STAT(addr)); + if (modem_stat & iRLSD) + tp->t_state |= TS_CARR_ON; + else + tp->t_state &= ~TS_CARR_ON; + fix_modem_state(unit, modem_stat); + } + splx(s); + + result = char_open(dev, tp, flag, ior); + + if (!comtimer_active) { + comtimer_active = TRUE; + comtimer(NULL); + } + + s = spltty(); + while(!(inb(INTR_ID(addr))&1)) { /* while pending interrupts */ + (void) inb(LINE_STAT (addr)); /* reset overrun error */ + (void) inb(TXRX (addr)); /* reset data-ready */ + (void) inb(MODEM_STAT(addr)); /* reset modem status */ + } + splx(s); + return result; +} + +void comclose(dev_t dev, int flag) +{ + struct tty *tp = &com_tty[minor(dev)]; + u_short addr = (uintptr_t)tp->t_addr; + + ttyclose(tp); + if (tp->t_state&TS_HUPCLS || (tp->t_state&TS_ISOPEN)==0) { + outb(INTR_ENAB(addr), 0); + outb(MODEM_CTL(addr), 0); + tp->t_state &= ~TS_BUSY; + commodem[minor(dev)] = 0; + if (comfifo[minor(dev)] != 0) + outb(INTR_ID(addr), 0x00); /* Disable fifos */ + } + return; +} + +io_return_t comread(dev_t dev, io_req_t ior) +{ + return char_read(&com_tty[minor(dev)], ior); +} + +io_return_t comwrite(dev_t dev, io_req_t ior) +{ + return char_write(&com_tty[minor(dev)], ior); +} + +io_return_t comportdeath(dev_t dev, mach_port_t port) +{ + return (tty_portdeath(&com_tty[minor(dev)], (ipc_port_t)port)); +} + +io_return_t +comgetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, /* pointer to OUT array */ + mach_msg_type_number_t *count /* out */ + ) +{ + io_return_t result = D_SUCCESS; + int unit = minor(dev); + + switch (flavor) { + case TTY_MODEM: + fix_modem_state(unit, inb(MODEM_STAT(cominfo[unit]->address))); + *data = commodem[unit]; + *count = 1; + break; + default: + result = tty_get_status(&com_tty[unit], flavor, data, count); + break; + } + return (result); +} + +io_return_t +comsetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, + mach_msg_type_number_t count) +{ + io_return_t result = D_SUCCESS; + int unit = minor(dev); + struct tty *tp = &com_tty[unit]; + + switch (flavor) { + case TTY_SET_BREAK: + commctl(tp, TM_BRK, DMBIS); + break; + case TTY_CLEAR_BREAK: + commctl(tp, TM_BRK, DMBIC); + break; + case TTY_MODEM: + commctl(tp, *data, DMSET); + break; + default: + result = tty_set_status(&com_tty[unit], flavor, data, count); + if (result == D_SUCCESS && flavor == TTY_STATUS) + comparam(unit); + return (result); + } + return (D_SUCCESS); +} + +void +comintr(int unit) +{ + struct tty *tp = &com_tty[unit]; + u_short addr = cominfo[unit]->address; + static char comoverrun = 0; + char c, line, intr_id; + int line_stat; + + while (! ((intr_id=(inb(INTR_ID(addr))&MASKi)) & 1)) + switch (intr_id) { + case MODi: + /* modem change */ + commodem_intr(unit, inb(MODEM_STAT(addr))); + break; + + case TRAi: + comtimer_state[unit] = 0; + tp->t_state &= ~(TS_BUSY|TS_FLUSH); + tt_write_wakeup(tp); + (void) comstart(tp); + break; + case RECi: + case CTIi: /* Character timeout indication */ + if (tp->t_state&TS_ISOPEN) { + int escape = 0; + while ((line = inb(LINE_STAT(addr))) & iDR) { + c = inb(TXRX(addr)); + + if (c == 0x1b) { + escape = 1; + continue; + } + +#if MACH_KDB + if (escape && c == 'D'-('A'-1)) + /* ctrl-alt-d pressed, + invoke debugger */ + kdb_kintr(); + else +#endif /* MACH_KDB */ + if (escape) { + ttyinput(0x1b, tp); + ttyinput(c, tp); + } + else + ttyinput(c, tp); + + escape = 0; + } + + if (escape) + /* just escape */ + ttyinput(0x1b, tp); + } else + tt_open_wakeup(tp); + break; + case LINi: + line_stat = inb(LINE_STAT(addr)); + + if ((line_stat & iPE) && + ((tp->t_flags&(EVENP|ODDP)) == EVENP || + (tp->t_flags&(EVENP|ODDP)) == ODDP)) { + /* parity error */; + } else if (line_stat&iOR && !comoverrun) { + printf("com%d: overrun\n", unit); + comoverrun = 1; + } else if (line_stat & (iFE | iBRKINTR)) { + /* framing error or break */ + ttyinput(tp->t_breakc, tp); + } + break; + } +} + +static void +comparam(int unit) +{ + struct tty *tp = &com_tty[unit]; + u_short addr = (uintptr_t)tp->t_addr; + spl_t s = spltty(); + int mode; + + if (tp->t_ispeed == B0) { + tp->t_state |= TS_HUPCLS; + outb(MODEM_CTL(addr), iOUT2); + commodem[unit] = 0; + splx(s); + return; + } + + /* Do input buffering */ + if (tp->t_ispeed >= B300) + tp->t_state |= TS_MIN; + + outb(LINE_CTL(addr), iDLAB); + outb(BAUD_LSB(addr), divisorreg[tp->t_ispeed] & 0xff); + outb(BAUD_MSB(addr), divisorreg[tp->t_ispeed] >> 8); + + if (tp->t_flags & (RAW|LITOUT|PASS8)) + mode = i8BITS; + else + mode = i7BITS | iPEN; + if (tp->t_flags & EVENP) + mode |= iEPS; + if (tp->t_ispeed == B110) + /* + * 110 baud uses two stop bits - + * all other speeds use one + */ + mode |= iSTB; + + outb(LINE_CTL(addr), mode); + + outb(INTR_ENAB(addr), iTX_ENAB|iRX_ENAB|iMODEM_ENAB|iERROR_ENAB); + if (comfifo[unit]) + outb(FIFO_CTL(addr), iFIFOENA|iFIFO14CH); + outb(MODEM_CTL(addr), iDTR|iRTS|iOUT2); + commodem[unit] |= (TM_DTR|TM_RTS); + splx(s); +} + +int comst_1, comst_2, comst_3, comst_4, comst_5 = 14; + +void +comstart(struct tty *tp) +{ + int nch; +#if 0 + int i; +#endif + + if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP|TS_BUSY)) { +comst_1++; + return; + } + if ((!queue_empty(&tp->t_delayed_write)) && + (tp->t_outq.c_cc <= TTLOWAT(tp))) { +comst_2++; + tt_write_wakeup(tp); + } + if (!tp->t_outq.c_cc) { +comst_3++; + return; + } + +#if 0 + i = (comfifo[minor(tp->t_dev)]) ? /*14*/comst_5 : 1; + + tp->t_state |= TS_BUSY; + while (i-- > 0) { + nch = getc(&tp->t_outq); + if (nch == -1) break; + if ((nch & 0200) && ((tp->t_flags & LITOUT) == 0)) { + timeout(ttrstrt, (char *)tp, (nch & 0x7f) + 6); + tp->t_state |= TS_TIMEOUT; +comst_4++; + return(0); + } + outb(TXRX((uintptr_t)tp->t_addr), nch); + } +#else + nch = getc(&tp->t_outq); + if (nch == -1) + return; + if ((nch & 0200) && ((tp->t_flags & LITOUT) == 0)) { + timeout((timer_func_t *)ttrstrt, (char *)tp, (nch & 0x7f) + 6); + tp->t_state |= TS_TIMEOUT; +comst_4++; + return; + } + outb(TXRX((uintptr_t)tp->t_addr), nch); + tp->t_state |= TS_BUSY; +#endif +} + +/* Check for stuck xmitters */ +int comtimer_interval = 5; + +void +comtimer(void * param) +{ + spl_t s = spltty(); + struct tty *tp = com_tty; + int i, nch; + + for (i = 0; i < NCOM; i++, tp++) { + if ((tp->t_state & TS_ISOPEN) == 0) + continue; + if (!tp->t_outq.c_cc) + continue; + if (++comtimer_state[i] < 2) + continue; + /* Its stuck */ +printf("Tty %p was stuck\n", tp); + nch = getc(&tp->t_outq); + outb(TXRX((uintptr_t)tp->t_addr), nch); + } + + splx(s); + timeout(comtimer, 0, comtimer_interval*hz); +} + +/* + * Set receive modem state from modem status register. + */ +void +fix_modem_state( + int unit, + int modem_stat) +{ + int stat = 0; + + if (modem_stat & iCTS) + stat |= TM_CTS; /* clear to send */ + if (modem_stat & iDSR) + stat |= TM_DSR; /* data set ready */ + if (modem_stat & iRI) + stat |= TM_RNG; /* ring indicator */ + if (modem_stat & iRLSD) + stat |= TM_CAR; /* carrier? */ + + commodem[unit] = (commodem[unit] & ~(TM_CTS|TM_DSR|TM_RNG|TM_CAR)) + | stat; +} + +/* + * Modem change (input signals) + */ +void +commodem_intr( + int unit, + int stat) +{ + int changed; + + changed = commodem[unit]; + fix_modem_state(unit, stat); + stat = commodem[unit]; + + /* Assumption: if the other party can handle + modem signals then it should handle all + the necessary ones. Else fix the cable. */ + + changed ^= stat; /* what changed ? */ + + if (changed & TM_CTS) + tty_cts( &com_tty[unit], stat & TM_CTS ); + +#if 0 + if (changed & TM_CAR) + ttymodem( &com_tty[unit], stat & TM_CAR ); +#endif + +} + +/* + * Set/get modem bits + */ +int +commctl( + struct tty *tp, + int bits, + int how) +{ + spl_t s; + int unit; + vm_offset_t dev_addr; + int b = 0; /* Suppress gcc warning */ + + unit = minor(tp->t_dev); + + if (bits == TM_HUP) { /* close line (internal) */ + bits = TM_DTR | TM_RTS; + how = DMBIC; + } + + if (how == DMGET) return commodem[unit]; + + dev_addr = cominfo[unit]->address; + + s = spltty(); + + switch (how) { + case DMSET: + b = bits; break; + case DMBIS: + b = commodem[unit] | bits; break; + case DMBIC: + b = commodem[unit] & ~bits; break; + } + commodem[unit] = b; + + if (bits & TM_BRK) { + if (b & TM_BRK) { + outb(LINE_CTL(dev_addr), inb(LINE_CTL(dev_addr)) | iSETBREAK); + } else { + outb(LINE_CTL(dev_addr), inb(LINE_CTL(dev_addr)) & ~iSETBREAK); + } + } + +#if 0 + /* do I need to do something on this ? */ + if (bits & TM_LE) { /* line enable */ + } +#endif +#if 0 + /* Unsupported */ + if (bits & TM_ST) { /* secondary transmit */ + } + if (bits & TM_SR) { /* secondary receive */ + } +#endif + if (bits & (TM_DTR|TM_RTS)) { /* data terminal ready, request to send */ + how = iOUT2; + if (b & TM_DTR) how |= iDTR; + if (b & TM_RTS) how |= iRTS; + outb(MODEM_CTL(dev_addr), how); + } + + splx(s); + + /* the rest are inputs */ + return commodem[unit]; +} + +void +comstop( + struct tty *tp, + int flags) +{ + if ((tp->t_state & TS_BUSY) && (tp->t_state & TS_TTSTOP) == 0) + tp->t_state |= TS_FLUSH; +} + +/* + * + * Code to be called from debugger. + * + */ +void compr_addr(vm_offset_t addr) +{ + /* The two line_stat prints may show different values, since + * touching some of the registers constitutes changing them. + */ + printf("LINE_STAT(%zu) %x\n", + LINE_STAT(addr), inb(LINE_STAT(addr))); + + printf("TXRX(%zu) %x, INTR_ENAB(%zu) %x, INTR_ID(%zu) %x, LINE_CTL(%zu) %x,\n\ +MODEM_CTL(%zu) %x, LINE_STAT(%zu) %x, MODEM_STAT(%zu) %x\n", + TXRX(addr), inb(TXRX(addr)), + INTR_ENAB(addr), inb(INTR_ENAB(addr)), + INTR_ID(addr), inb(INTR_ID(addr)), + LINE_CTL(addr), inb(LINE_CTL(addr)), + MODEM_CTL(addr), inb(MODEM_CTL(addr)), + LINE_STAT(addr), inb(LINE_STAT(addr)), + MODEM_STAT(addr),inb(MODEM_STAT(addr))); +} + +int compr(int unit) +{ + compr_addr(cominfo[unit]->address); + return(0); +} + +int +comgetc(int unit) +{ + u_short addr = (u_short)(cominfo[unit]->address); + spl_t s = spltty(); + int c; + + while((inb(LINE_STAT(addr)) & iDR) == 0) ; + + c = inb(TXRX(addr)); + splx(s); + return c; +} + +/* + * Routines for the console + */ +int +comcnputc(dev_t dev, int c) +{ + u_short addr = (u_short)(cominfo[minor(dev)]->address); + + /* Wait for transmitter to empty */ + while((inb(LINE_STAT(addr)) & iTHRE) == 0) + continue; + + /* send the char */ + if (c == '\n') + comcnputc(dev, '\r'); + outb(addr, c); + + return 0; +} + +int +comcngetc(dev_t dev, int wait) +{ + u_short addr = (u_short)(cominfo[minor(dev)]->address); + int c; + + while((inb(LINE_STAT(addr)) & iDR) == 0) + if (! wait) + return 0; + + c = inb(TXRX(addr)); + return c & 0x7f; +} + +#endif /* NCOM */ diff --git a/i386/i386at/com.h b/i386/i386at/com.h new file mode 100644 index 0000000..3be2930 --- /dev/null +++ b/i386/i386at/com.h @@ -0,0 +1,86 @@ +/* + * Communication functions + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * 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 2, 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, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Barry deFreese. + */ +/* + * Communication functions. + * + */ + +#ifndef _COM_H_ +#define _COM_H_ + +#include +#include +#include +#include + +/* + * Set receive modem state from modem status register. + */ +extern void fix_modem_state(int unit, int modem_stat); + +extern void comtimer(void * param); + +/* + * Modem change (input signals) + */ +extern void commodem_intr(int unit, int stat); + +extern int comgetc(int unit); + +extern int comcnprobe(struct consdev *cp); +extern int comcninit(struct consdev *cp); +extern int comcngetc(dev_t dev, int wait); +extern int comcnputc(dev_t dev, int c); +extern void comintr(int unit); + +int comprobe(vm_offset_t port, struct bus_ctlr *dev); +int commctl(struct tty *tp, int bits, int how); +void comstart(struct tty *tp); +void comstop(struct tty *tp, int flags); +void comattach(struct bus_device *dev); + +extern io_return_t +comgetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, + mach_msg_type_number_t *count); + +extern io_return_t +comsetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, + mach_msg_type_number_t count); + +#if MACH_KDB +extern void kdb_kintr(void); +extern void compr_addr(vm_offset_t addr); +extern int compr(int unit); +#endif /* MACH_KDB */ + +extern io_return_t comopen(dev_t dev, int flag, io_req_t ior); +extern void comclose(dev_t dev, int flag); +extern io_return_t comread(dev_t dev, io_req_t ior); +extern io_return_t comwrite(dev_t dev, io_req_t ior); +extern io_return_t comportdeath(dev_t dev, mach_port_t port); + +#endif /* _COM_H_ */ diff --git a/i386/i386at/comreg.h b/i386/i386at/comreg.h new file mode 100644 index 0000000..7356574 --- /dev/null +++ b/i386/i386at/comreg.h @@ -0,0 +1,139 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Olivetti serial port driver v1.0 + * Copyright Ing. C. Olivetti & C. S.p.A. 1988, 1989 + * All rights reserved. + * + */ +/* + Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc., +Cupertino, California. + + All Rights Reserved + + Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appears in all +copies and that both the copyright notice and this permission notice +appear in supporting documentation, and that the name of Olivetti +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + + OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, +NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef _COMREG_H_ +#define _COMREG_H_ + +#define TXRX(addr) (addr + 0) +#define BAUD_LSB(addr) (addr + 0) +#define BAUD_MSB(addr) (addr + 1) +#define INTR_ENAB(addr) (addr + 1) +#define INTR_ID(addr) (addr + 2) +#define FIFO_CTL(addr) (addr + 2) +#define LINE_CTL(addr) (addr + 3) +#define MODEM_CTL(addr) (addr + 4) +#define LINE_STAT(addr) (addr + 5) +#define MODEM_STAT(addr)(addr + 6) +#define SCR(addr) (addr + 7) + +#define MODi 0 +#define TRAi 2 +#define RECi 4 +#define LINi 6 +#define CTIi 0xc +#define MASKi 0xf + +/* line control register */ +#define iWLS0 0x01 /*word length select bit 0 */ +#define iWLS1 0x02 /*word length select bit 2 */ +#define iSTB 0x04 /* number of stop bits */ +#define iPEN 0x08 /* parity enable */ +#define iEPS 0x10 /* even parity select */ +#define iSP 0x20 /* stick parity */ +#define iSETBREAK 0x40 /* break key */ +#define iDLAB 0x80 /* divisor latch access bit */ +#define i5BITS 0x00 /* 5 bits per char */ +#define i6BITS 0x01 /* 6 bits per char */ +#define i7BITS 0x02 /* 7 bits per char */ +#define i8BITS 0x03 /* 8 bits per char */ + +/* line status register */ +#define iDR 0x01 /* data ready */ +#define iOR 0x02 /* overrun error */ +#define iPE 0x04 /* parity error */ +#define iFE 0x08 /* framing error */ +#define iBRKINTR 0x10 /* a break has arrived */ +#define iTHRE 0x20 /* tx hold reg is now empty */ +#define iTSRE 0x40 /* tx shift reg is now empty */ + +/* interrupt id regisger */ +#define iMODEM_INTR 0x01 +#define iTX_INTR 0x02 +#define iRX_INTR 0x04 +#define iERROR_INTR 0x08 + +/* interrupt enable register */ +#define iRX_ENAB 0x01 +#define iTX_ENAB 0x02 +#define iERROR_ENAB 0x04 +#define iMODEM_ENAB 0x08 + +/* modem control register */ +#define iDTR 0x01 /* data terminal ready */ +#define iRTS 0x02 /* request to send */ +#define iOUT1 0x04 /* COM aux line -not used */ +#define iOUT2 0x08 /* turns intr to 386 on/off */ +#define iLOOP 0x10 /* loopback for diagnostics */ + +/* modem status register */ +#define iDCTS 0x01 /* delta clear to send */ +#define iDDSR 0x02 /* delta data set ready */ +#define iTERI 0x04 /* trail edge ring indicator */ +#define iDRLSD 0x08 /* delta rx line sig detect */ +#define iCTS 0x10 /* clear to send */ +#define iDSR 0x20 /* data set ready */ +#define iRI 0x40 /* ring indicator */ +#define iRLSD 0x80 /* rx line sig detect */ + +/* fifo control register (only in 16550) */ +#define iFIFOENA 0x01 /* Enable fifos */ +#define iCLRRCVRFIFO 0x02 /* Clear receive fifo */ +#define iCLRXMITFIFO 0x04 /* Clear transmit fifo */ +#define iDMAMODE 0x08 /* DMA transfer enable */ +#define iFIFO1CH 0x00 /* Receive fifo trigger level 1 char */ +#define iFIFO4CH 0x40 /* Receive fifo trigger level 4 chars*/ +#define iFIFO8CH 0x80 /* Receive fifo trigger level 8 chars*/ +#define iFIFO14CH 0xc0 /* Receive fifo trigger level 14 chars*/ + +#endif /* _COMREG_H_ */ diff --git a/i386/i386at/conf.c b/i386/i386at/conf.c new file mode 100644 index 0000000..ecbf1e4 --- /dev/null +++ b/i386/i386at/conf.c @@ -0,0 +1,172 @@ +/* + * Mach Operating System + * Copyright (c) 1993-1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Device switch for i386 AT bus. + */ + +#include +#include +#include +#include + +#define timename "time" + +#ifndef MACH_HYP +#include +#define kdname "kd" + +#if NCOM > 0 +#include +#define comname "com" +#endif /* NCOM > 0 */ + +#if NLPR > 0 +#include +#define lprname "lpr" +#endif /* NLPR > 0 */ +#endif /* MACH_HYP */ + +#include +#define kbdname "kbd" + +#ifndef MACH_HYP +#include +#define mousename "mouse" + +#include +#define memname "mem" +#endif /* MACH_HYP */ + +#include +#define kmsgname "kmsg" + +#ifdef MACH_HYP +#include +#define hypcnname "hyp" +#endif /* MACH_HYP */ + +#include +#define irqname "irq" + +/* + * List of devices - console must be at slot 0 + */ +struct dev_ops dev_name_list[] = +{ + /*name, open, close, read, + write, getstat, setstat, mmap, + async_in, reset, port_death, subdev, + dev_info */ + + /* We don't assign a console here, when we find one via + cninit() we stick something appropriate here through the + indirect list */ + { "cn", nulldev_open, nulldev_close, nulldev_read, + nulldev_write, nulldev_getstat, nulldev_setstat, nomap, + nodev_async_in, nulldev_reset, nulldev_portdeath, 0, + nodev_info}, + +#ifndef MACH_HYP +#if ENABLE_IMMEDIATE_CONSOLE + { "immc", nulldev_open, nulldev_close, nulldev_read, + nulldev_write, nulldev_getstat, nulldev_setstat, + nomap, nodev_async_in, nulldev_reset, nulldev_portdeath, 0, + nodev_info }, +#endif /* ENABLE_IMMEDIATE_CONSOLE */ + { kdname, kdopen, kdclose, kdread, + kdwrite, kdgetstat, kdsetstat, kdmmap, + nodev_async_in, nulldev_reset, kdportdeath, 0, + nodev_info }, +#endif /* MACH_HYP */ + + { timename, timeopen, timeclose, nulldev_read, + nulldev_write, nulldev_getstat, nulldev_setstat, timemmap, + nodev_async_in, nulldev_reset, nulldev_portdeath, 0, + nodev_info }, + +#ifndef MACH_HYP +#if NCOM > 0 + { comname, comopen, comclose, comread, + comwrite, comgetstat, comsetstat, nomap, + nodev_async_in, nulldev_reset, comportdeath, 0, + nodev_info }, +#endif + +#ifdef MACH_LPR + { lprname, lpropen, lprclose, lprread, + lprwrite, lprgetstat, lprsetstat, nomap, + nodev_async_in, nulldev_reset, lprportdeath, 0, + nodev_info }, +#endif + + { mousename, mouseopen, mouseclose, mouseread, + nulldev_write, mousegetstat, nulldev_setstat, nomap, + nodev_async_in, nulldev_reset, nulldev_portdeath, 0, + nodev_info }, + + { kbdname, kbdopen, kbdclose, kbdread, + nulldev_write, kbdgetstat, kbdsetstat, nomap, + nodev_async_in, nulldev_reset, nulldev_portdeath, 0, + nodev_info }, + + { memname, nulldev_open, nulldev_close, nulldev_read, + nulldev_write, nulldev_getstat, nulldev_setstat, memmmap, + nodev_async_in, nulldev_reset, nulldev_portdeath, 0, + nodev_info }, +#endif /* MACH_HYP */ + +#ifdef MACH_KMSG + { kmsgname, kmsgopen, kmsgclose, kmsgread, + nulldev_write, kmsggetstat, nulldev_setstat, nomap, + nodev_async_in, nulldev_reset, nulldev_portdeath, 0, + nodev_info }, +#endif + +#ifdef MACH_HYP + { hypcnname, hypcnopen, hypcnclose, hypcnread, + hypcnwrite, hypcngetstat, hypcnsetstat, nomap, + nodev_async_in, nulldev_reset, hypcnportdeath, 0, + nodev_info }, +#endif /* MACH_HYP */ + + { irqname, nulldev_open, nulldev_close, nulldev_read, + nulldev_write,nulldev_getstat,nulldev_setstat, nomap, + nodev_async_in, nulldev_reset, nulldev_portdeath,0, + nodev_info }, + +}; +int dev_name_count = sizeof(dev_name_list)/sizeof(dev_name_list[0]); + +/* + * Indirect list. + */ +struct dev_indirect dev_indirect_list[] = { + + /* console */ + { "console", &dev_name_list[0], 0 } +}; +int dev_indirect_count = sizeof(dev_indirect_list) + / sizeof(dev_indirect_list[0]); diff --git a/i386/i386at/cons_conf.c b/i386/i386at/cons_conf.c new file mode 100644 index 0000000..1d7dd38 --- /dev/null +++ b/i386/i386at/cons_conf.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 1988-1994, The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Utah $Hdr: cons_conf.c 1.7 94/12/14$ + */ + +/* + * This entire table could be autoconfig()ed but that would mean that + * the kernel's idea of the console would be out of sync with that of + * the standalone boot. I think it best that they both use the same + * known algorithm unless we see a pressing need otherwise. + */ +#include +#include + +#ifdef MACH_HYP +#include +#else /* MACH_HYP */ +#include "kd.h" +#if NCOM > 0 +#include "com.h" +#endif +#endif /* MACH_HYP */ + +#if ENABLE_IMMEDIATE_CONSOLE +#include "immc.h" +#endif /* ENABLE_IMMEDIATE_CONSOLE */ + +/* + * The rest of the consdev fields are filled in by the respective + * cnprobe routine. + */ +struct consdev constab[] = { +#ifdef MACH_HYP + {"hyp", hypcnprobe, hypcninit, hypcngetc, hypcnputc}, +#else /* MACH_HYP */ +#if ENABLE_IMMEDIATE_CONSOLE + {"immc", immc_cnprobe, immc_cninit, immc_cngetc, immc_cnputc}, +#endif /* ENABLE_IMMEDIATE_CONSOLE */ + {"kd", kdcnprobe, kdcninit, kdcngetc, kdcnputc}, +#if NCOM > 0 + {"com", comcnprobe, comcninit, comcngetc, comcnputc}, +#endif +#endif /* MACH_HYP */ + {0} +}; diff --git a/i386/i386at/cram.h b/i386/i386at/cram.h new file mode 100644 index 0000000..ac40cf1 --- /dev/null +++ b/i386/i386at/cram.h @@ -0,0 +1,86 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * cram.h + */ + +/* + Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc., +Cupertino, California. + + All Rights Reserved + + Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appears in all +copies and that both the copyright notice and this permission notice +appear in supporting documentation, and that the name of Olivetti +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + + OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, +NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef _CRAM_H_ +#define _CRAM_H_ + +/* XXX: this conflicts with read/writing the RTC */ + +/* + * outb(CMOS_ADDR, addr); + * result = inb(CMOS_DATA); + * + * where "addr" tells what value you want to read (some are listed + * below). Interrupts should be disabled while you do this. + */ + +/* I/O ports */ + +#define CMOS_ADDR 0x70 /* port for CMOS ram address */ +#define CMOS_DATA 0x71 /* port for CMOS ram data */ + + +/* Addresses, related masks, and potential results */ + +#define CMOS_SHUTDOWN 0xf +#define CM_NORM_RST 0x0 +#define CM_LOAD_SYS 0x4 +#define CM_JMP_467 0xa + +#define CMOS_EB 0x14 /* read Equipment Byte */ +#define CM_SCRMSK 0x30 /* mask for EB query to get screen */ +#define CM_EGA_VGA 0x00 /* "not CGA or MONO" */ +#define CM_CGA_40 0x10 +#define CM_CGA_80 0x20 +#define CM_MONO_80 0x30 + +#endif /* _CRAM_H_ */ diff --git a/i386/i386at/disk.h b/i386/i386at/disk.h new file mode 100644 index 0000000..c558375 --- /dev/null +++ b/i386/i386at/disk.h @@ -0,0 +1,89 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + Copyright 1988, 1989 by Intel Corporation, Santa Clara, California. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appears in all +copies and that both the copyright notice and this permission notice +appear in supporting documentation, and that the name of Intel +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + +INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, +NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/* + * disk.h + */ + +#ifndef _DISK_H_ +#define _DISK_H_ + +#define V_NUMPAR 16 /* maximum number of partitions */ + +#define VTOC_SANE 0x600DDEEE /* Indicates a sane VTOC */ +#define PDLOCATION 29 /* location of VTOC */ + +#define LBLLOC 1 /* label block for xxxbsd */ + +struct localpartition { + u_int p_flag; /*permision flags*/ + long p_start; /*physical start sector no of partition*/ + long p_size; /*# of physical sectors in partition*/ +}; +typedef struct localpartition localpartition_t; + +struct evtoc { + u_int fill0[6]; + u_int cyls; /*number of cylinders per drive*/ + u_int tracks; /*number tracks per cylinder*/ + u_int sectors; /*number sectors per track*/ + u_int fill1[13]; + u_int version; /*layout version*/ + u_int alt_ptr; /*byte offset of alternates table*/ + u_short alt_len; /*byte length of alternates table*/ + u_int sanity; /*to verify vtoc sanity*/ + u_int xcyls; /*number of cylinders per drive*/ + u_int xtracks; /*number tracks per cylinder*/ + u_int xsectors; /*number sectors per track*/ + u_short nparts; /*number of partitions*/ + u_short fill2; /*pad for 286 compiler*/ + char label[40]; + struct localpartition part[V_NUMPAR];/*partition headers*/ + char fill[512-352]; +}; + +#endif /* _DISK_H_ */ diff --git a/i386/i386at/elf.h b/i386/i386at/elf.h new file mode 100644 index 0000000..26f4d87 --- /dev/null +++ b/i386/i386at/elf.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2013 Richard Braun. + * + * 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 2 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 . + */ + +#ifndef _X86_ELF_H +#define _X86_ELF_H + +#define ELF_SHT_SYMTAB 2 +#define ELF_SHT_STRTAB 3 + +struct elf_shdr { + unsigned int name; + unsigned int type; + unsigned int flags; + unsigned long addr; + unsigned long offset; + unsigned int size; + unsigned int link; + unsigned int info; + unsigned int addralign; + unsigned int entsize; +}; + +#ifdef __LP64__ + +struct elf_sym { + unsigned int name; + unsigned char info; + unsigned char other; + unsigned short shndx; + unsigned long value; + unsigned long size; +}; + +#else /* __LP64__ */ + +struct elf_sym { + unsigned int name; + unsigned long value; + unsigned long size; + unsigned char info; + unsigned char other; + unsigned short shndx; +}; + +#endif /* __LP64__ */ + +#endif /* _X86_ELF_H */ diff --git a/i386/i386at/i8250.h b/i386/i386at/i8250.h new file mode 100644 index 0000000..9b8a801 --- /dev/null +++ b/i386/i386at/i8250.h @@ -0,0 +1,134 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + Copyright 1988, 1989 by Intel Corporation, Santa Clara, California. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appears in all +copies and that both the copyright notice and this permission notice +appear in supporting documentation, and that the name of Intel +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + +INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, +NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/* + * Header file for i8250 chip + */ + +#ifndef _I8250_H_ +#define _I8250_H_ + +/* port offsets from the base i/o address */ + +#define RDAT 0 +#define RIE 1 +#define RID 2 +#define RFC 2 +#define RLC 3 +#define RMC 4 +#define RLS 5 +#define RMS 6 +#define RDLSB 0 +#define RDMSB 1 + +/* interrupt control register */ + +#define IERD 0x01 /* read int */ +#define IETX 0x02 /* xmit int */ +#define IELS 0x04 /* line status int */ +#define IEMS 0x08 /* modem int */ + +/* interrupt status register */ + +#define IDIP 0x01 /* not interrupt pending */ +#define IDMS 0x00 /* modem int */ +#define IDTX 0x02 /* xmit int */ +#define IDRD 0x04 /* read int */ +#define IDLS 0x06 /* line status int */ +#define IDMASK 0x0f /* interrupt ID mask */ + +/* line control register */ + +#define LC5 0x00 /* word length 5 */ +#define LC6 0x01 /* word length 6 */ +#define LC7 0x02 /* word length 7 */ +#define LC8 0x03 /* word length 8 */ +#define LCSTB 0x04 /* 2 stop */ +#define LCPEN 0x08 /* parity enable */ +#define LCEPS 0x10 /* even parity select */ +#define LCSP 0x20 /* stick parity */ +#define LCBRK 0x40 /* send break */ +#define LCDLAB 0x80 /* divisor latch access bit */ +#define LCPAR 0x38 /* parity mask */ + +/* line status register */ + +#define LSDR 0x01 /* data ready */ +#define LSOR 0x02 /* overrun error */ +#define LSPE 0x04 /* parity error */ +#define LSFE 0x08 /* framing error */ +#define LSBI 0x10 /* break interrupt */ +#define LSTHRE 0x20 /* xmit holding reg empty */ +#define LSTSRE 0x40 /* xmit shift reg empty */ + +/* modem control register */ + +#define MCDTR 0x01 /* DTR */ +#define MCRTS 0x02 /* RTS */ +#define MCOUT1 0x04 /* OUT1 */ +#define MCOUT2 0x08 /* OUT2 */ +#define MCLOOP 0x10 /* loopback */ + +/* modem status register */ + +#define MSDCTS 0x01 /* delta CTS */ +#define MSDDSR 0x02 /* delta DSR */ +#define MSTERI 0x04 /* delta RE */ +#define MSDRLSD 0x08 /* delta CD */ +#define MSCTS 0x10 /* CTS */ +#define MSDSR 0x20 /* DSR */ +#define MSRI 0x40 /* RE */ +#define MSRLSD 0x80 /* CD */ + +/* divisor latch register settings for various baud rates */ + +#define BCNT1200 0x60 +#define BCNT2400 0x30 +#define BCNT4800 0x18 +#define BCNT9600 0x0c + +#endif /* _I8250_H_ */ diff --git a/i386/i386at/idt.h b/i386/i386at/idt.h new file mode 100644 index 0000000..19e0abe --- /dev/null +++ b/i386/i386at/idt.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 1994 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ + +#ifndef _I386AT_IDT_ +#define _I386AT_IDT_ + +/* There are 256 interrupt vectors on x86, + * the first 32 are taken by cpu faults */ +#define IDTSZ (0x100) + +/* PIC sits at 0x20-0x2f */ +#define PIC_INT_BASE 0x20 + +/* IOAPIC sits at 0x30-0x47 */ +#define IOAPIC_INT_BASE 0x30 + +/* IOAPIC spurious interrupt vector set to 0xff */ +#define IOAPIC_SPURIOUS_BASE 0xff + +/* Remote -> local AST requests */ +#define CALL_AST_CHECK 0xfa + +/* Currently for TLB shootdowns */ +#define CALL_PMAP_UPDATE 0xfb + +#include + +#ifndef __ASSEMBLER__ +extern void idt_init (void); +extern void ap_idt_init (int cpu); +#endif /* __ASSEMBLER__ */ + +#endif /* _I386AT_IDT_ */ diff --git a/i386/i386at/immc.c b/i386/i386at/immc.c new file mode 100644 index 0000000..00fc973 --- /dev/null +++ b/i386/i386at/immc.c @@ -0,0 +1,134 @@ +/* + * Copyright (c) 1995-1994 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ + +#if ENABLE_IMMEDIATE_CONSOLE + +#include +#include +#include +#include + +/* This is a special "feature" (read: kludge) + intended for use only for kernel debugging. + It enables an extremely simple console output mechanism + that sends text straight to CGA/EGA/VGA video memory. + It has the nice property of being functional right from the start, + so it can be used to debug things that happen very early + before any devices are initialized. */ + +boolean_t immediate_console_enable = TRUE; + +/* + * XXX we assume that pcs *always* have a console + */ +int +immc_cnprobe(struct consdev *cp) +{ + int maj, unit, pri; + + maj = 0; + unit = 0; + pri = CN_INTERNAL; + + cp->cn_dev = makedev(maj, unit); + cp->cn_pri = pri; + return 0; +} + +int +immc_cninit(struct consdev *cp) +{ + return 0; +} + +int immc_cnmaygetc(void) +{ + return -1; +} + +int +immc_cngetc(dev_t dev, int wait) +{ + if (wait) { + int c; + while ((c = immc_cnmaygetc()) < 0) + continue; + return c; + } + else + return immc_cnmaygetc(); +} + +int +immc_cnputc(dev_t dev, int c) +{ + static int ofs = -1; + + if (!immediate_console_enable) + return -1; + if (ofs < 0 || ofs >= 80) + { + ofs = 0; + immc_cnputc(dev, '\n'); + } + + if (c == '\n') + { + memmove((void *) phystokv(0xb8000), + (void *) phystokv(0xb8000+80*2), 80*2*24); + memset((void *) phystokv((0xb8000+80*2*24)), 0, 80*2); + ofs = 0; + } + else if (c == '\r') + { + ofs = 0; + } + else if (c == '\t') + { + ofs = (ofs & ~7) + 8; + } + else + { + volatile unsigned char *p; + + if (ofs >= 80) + { + immc_cnputc(dev, '\r'); + immc_cnputc(dev, '\n'); + } + + p = (void *) phystokv(0xb8000 + 80*2*24 + ofs*2); + p[0] = c; + p[1] = 0x0f; + ofs++; + } + return 0; +} + +void +immc_romputc(char c) +{ + immc_cnputc (0, c); +} + +#endif /* ENABLE_IMMEDIATE_CONSOLE */ diff --git a/i386/i386at/immc.h b/i386/i386at/immc.h new file mode 100644 index 0000000..dc802c8 --- /dev/null +++ b/i386/i386at/immc.h @@ -0,0 +1,31 @@ +/* Declarations for the immediate console. + + Copyright (C) 2015 Free Software Foundation, Inc. + + This file is part of the GNU Mach. + + The GNU Mach 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 2, or (at + your option) any later version. + + The GNU Mach 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 the GNU Mach. If not, see . */ + +#ifndef _IMMC_H_ +#define _IMMC_H_ + +#include + +int immc_cnprobe(struct consdev *cp); +int immc_cninit(struct consdev *cp); +int immc_cngetc(dev_t dev, int wait); +int immc_cnputc(dev_t dev, int c); +void immc_romputc(char c); + +#endif /* _IMMC_H_ */ diff --git a/i386/i386at/int_init.c b/i386/i386at/int_init.c new file mode 100644 index 0000000..5c8fce6 --- /dev/null +++ b/i386/i386at/int_init.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 1994 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ + +#include +#include +#include +#include +#include +#ifdef APIC +#include +#endif + +/* defined in locore.S */ +extern vm_offset_t int_entry_table[]; + +static void +int_fill(struct real_gate *myidt) +{ + int i; +#ifndef APIC + int base = PIC_INT_BASE; + int nirq = 16; +#else + int base = IOAPIC_INT_BASE; + int nirq = NINTR; +#endif + + for (i = 0; i < nirq; i++) { + fill_idt_gate(myidt, base + i, + int_entry_table[i], KERNEL_CS, + ACC_PL_K|ACC_INTR_GATE, 0); + } +#if NCPUS > 1 + fill_idt_gate(myidt, CALL_AST_CHECK, + int_entry_table[i], KERNEL_CS, + ACC_PL_K|ACC_INTR_GATE, 0); + i++; + fill_idt_gate(myidt, CALL_PMAP_UPDATE, + int_entry_table[i], KERNEL_CS, + ACC_PL_K|ACC_INTR_GATE, 0); + i++; +#endif +#ifdef APIC + fill_idt_gate(myidt, IOAPIC_SPURIOUS_BASE, + int_entry_table[i], KERNEL_CS, + ACC_PL_K|ACC_INTR_GATE, 0); + i++; +#endif +} + +void +int_init(void) +{ + int_fill(idt); +} + +#if NCPUS > 1 +void ap_int_init(int cpu) +{ + int_fill(mp_desc_table[cpu]->idt); +} +#endif diff --git a/i386/i386at/int_init.h b/i386/i386at/int_init.h new file mode 100644 index 0000000..3c11ebc --- /dev/null +++ b/i386/i386at/int_init.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * 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 2, 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, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Barry deFreese. + */ +/* + * Initialization functions. + * + */ + +#ifndef _INT_INIT_H_ +#define _INT_INIT_H_ + +#include + +#ifndef __ASSEMBLER__ +extern void int_init (void); +extern void ap_int_init (int cpu); +#endif /* __ASSEMBLER__ */ + +#endif /* _INT_INIT_H_ */ diff --git a/i386/i386at/interrupt.S b/i386/i386at/interrupt.S new file mode 100644 index 0000000..77424b4 --- /dev/null +++ b/i386/i386at/interrupt.S @@ -0,0 +1,142 @@ +/* + * Copyright (c) 1995 Shantanu Goel + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * THE AUTHOR ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. THE AUTHOR DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + */ + +#include + +#include +#ifdef APIC +# include +#else +# include +#endif +#include + +#define READ_ISR (OCW_TEMPLATE|READ_NEXT_RD|READ_IS_ONRD) + +/* + * Generic interrupt handler. + * + * On entry, %eax contains the irq number. + * + * Note: kdb_kintr needs to know our stack usage + */ + +#define S_REGS 28(%esp) +#define S_RET 24(%esp) +#define S_IRQ 20(%esp) +#define S_IPL 16(%esp) + +ENTRY(interrupt) +#ifdef APIC + cmpl $255,%eax /* was this a spurious intr? */ + jne 1f + ret /* if so, just return */ +1: +#endif + subl $24,%esp /* Two local variables + 4 parameters */ + movl %eax,S_IRQ /* save irq number */ + + call spl7 /* set ipl */ + movl %eax,S_IPL /* save previous ipl */ + + movl S_IRQ,%ecx /* restore irq number */ + +#if NCPUS > 1 + cmpl $CALL_PMAP_UPDATE,%ecx /* was this a SMP pmap_update request? */ + je _call_single + + cmpl $CALL_AST_CHECK,%ecx /* was this a SMP remote -> local ast request? */ + je _call_local_ast +#endif + +#ifndef APIC + movl $1,%eax + shll %cl,%eax /* get corresponding IRQ mask */ + orl EXT(curr_pic_mask),%eax /* add current mask */ + + cmpl $8,%ecx /* do we need to ack slave? */ + jl 1f /* no, only master */ + + /* EOI on slave */ + movb %ah,%al + outb %al,$(PIC_SLAVE_OCW) /* mask slave out */ + + movb $(SPECIFIC_EOI),%al /* specific EOI for this irq */ + andb $7,%cl /* irq number for the slave */ + orb %cl,%al /* combine them */ + outb %al,$(PIC_SLAVE_ICW) /* ack interrupt to slave */ + + movb $(SPECIFIC_EOI + I_AM_SLAVE_2),%al /* specific master EOI for cascaded slave */ + outb %al,$(PIC_MASTER_ICW) /* ack interrupt to master */ + + movl EXT(curr_pic_mask),%eax /* restore original mask */ + movb %ah,%al + outb %al,$(PIC_SLAVE_OCW) /* unmask slave */ + jmp 2f + +1: + /* EOI on master */ + outb %al,$(PIC_MASTER_OCW) /* mask master out */ + + movb $(SPECIFIC_EOI),%al /* specific EOI for this irq */ + orb %cl,%al /* combine with irq number */ + outb %al,$(PIC_MASTER_ICW) /* ack interrupt to master */ + + movl EXT(curr_pic_mask),%eax /* restore original mask */ + outb %al,$(PIC_MASTER_OCW) /* unmask master */ +2: +#else + movl %ecx,(%esp) /* load irq number as 1st arg */ + call EXT(ioapic_irq_eoi) /* ioapic irq specific EOI */ +#endif + + movl S_IPL,%eax + movl %eax,4(%esp) /* previous ipl as 2nd arg */ + + movl S_RET,%eax + movl %eax,8(%esp) /* return address as 3rd arg */ + + movl S_REGS,%eax + movl %eax,12(%esp) /* address of interrupted registers as 4th arg */ + + movl S_IRQ,%eax /* copy irq number */ + + shll $2,%eax /* irq * 4 */ + movl EXT(iunit)(%eax),%edx /* get device unit number */ + movl %edx,(%esp) /* unit number as 1st arg */ + + call *EXT(ivect)(%eax) /* call interrupt handler */ + +_completed: + movl S_IPL,%eax /* restore previous ipl */ + movl %eax,(%esp) + call splx_cli /* restore previous ipl */ + + addl $24,%esp /* pop local variables */ + ret + +#if NCPUS > 1 +_call_single: + call EXT(lapic_eoi) /* lapic EOI before the handler to allow extra update */ + call EXT(pmap_update_interrupt) + jmp _completed + +_call_local_ast: + call EXT(lapic_eoi) /* lapic EOI */ + call EXT(ast_check) /* AST check on this cpu */ + jmp _completed + +#endif +END(interrupt) diff --git a/i386/i386at/ioapic.c b/i386/i386at/ioapic.c new file mode 100644 index 0000000..2553a2c --- /dev/null +++ b/i386/i386at/ioapic.c @@ -0,0 +1,463 @@ +/* + * Copyright (C) 2019 Free Software Foundation, Inc. + * + * This file is part of GNU Mach. + * + * GNU Mach 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 2, or (at your option) + * any later version. + * + * GNU Mach 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* only for macros */ +#include +#include +#include +#include +#include + +static int has_irq_specific_eoi = 0; +int timer_pin; + +uint32_t lapic_timer_val = 0; +uint32_t calibrated_ticks = 0; + +spl_t curr_ipl[NCPUS] = {0}; +int spl_init = 0; + +def_simple_lock_irq_data(static, ioapic_lock) /* Lock for non-atomic window accesses to ioapic */ + +int iunit[NINTR] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + /* 2nd IOAPIC */ + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63 }; + +interrupt_handler_fn ivect[NINTR] = { + /* 00 */ (interrupt_handler_fn)hardclock, + /* 01 */ kdintr, /* kdintr, ... */ + /* 02 */ intnull, + /* 03 */ intnull, /* lnpoll, comintr, ... */ + + /* 04 */ intnull, /* comintr, ... */ + /* 05 */ intnull, /* comintr, wtintr, ... */ + /* 06 */ intnull, /* fdintr, ... */ + /* 07 */ intnull, /* qdintr, ... */ + + /* 08 */ intnull, + /* 09 */ intnull, /* ether */ + /* 10 */ intnull, + /* 11 */ intnull, + + /* 12 */ intnull, + /* 13 */ fpintr, /* always */ + /* 14 */ intnull, /* hdintr, ... */ + /* 15 */ intnull, /* ??? */ + + /* 16 */ intnull, /* PIRQA */ + /* 17 */ intnull, /* PIRQB */ + /* 18 */ intnull, /* PIRQC */ + /* 19 */ intnull, /* PIRQD */ + /* 20 */ intnull, /* PIRQE */ + /* 21 */ intnull, /* PIRQF */ + /* 22 */ intnull, /* PIRQG */ + /* 23 */ intnull, /* PIRQH */ + + /* 24 */ intnull, + /* 25 */ intnull, + /* 26 */ intnull, + /* 27 */ intnull, + /* 28 */ intnull, + /* 29 */ intnull, + /* 30 */ intnull, + /* 31 */ intnull, + + /* 32 */ intnull, + /* 33 */ intnull, + /* 34 */ intnull, + /* 35 */ intnull, + /* 36 */ intnull, + /* 37 */ intnull, + /* 38 */ intnull, + /* 39 */ intnull, + /* 40 */ intnull, + /* 41 */ intnull, + /* 42 */ intnull, + /* 43 */ intnull, + /* 44 */ intnull, + /* 45 */ intnull, + /* 46 */ intnull, + /* 47 */ intnull, + /* 48 */ intnull, + /* 49 */ intnull, + /* 50 */ intnull, + /* 51 */ intnull, + /* 52 */ intnull, + /* 53 */ intnull, + /* 54 */ intnull, + /* 55 */ intnull, + + /* 56 */ intnull, + /* 57 */ intnull, + /* 58 */ intnull, + /* 59 */ intnull, + /* 60 */ intnull, + /* 61 */ intnull, + /* 62 */ intnull, + /* 63 */ intnull, +}; + +void +picdisable(void) +{ + int i; + + asm("cli"); + for (i = 0; i < NCPUS; i++) + curr_ipl[i] = SPLHI; + + /* + ** Disable PIC + */ + outb ( PIC_SLAVE_OCW, PICS_MASK ); + outb ( PIC_MASTER_OCW, PICM_MASK ); +} + +void +intnull(int unit_dev) +{ + printf("intnull(%d)\n", unit_dev); +} + +static uint32_t +ioapic_read(uint8_t id, uint8_t reg) +{ + volatile ApicIoUnit *ioapic = apic_get_ioapic(id)->ioapic; + ioapic->select.r = reg; + return ioapic->window.r; +} + +static void +ioapic_write(uint8_t id, uint8_t reg, uint32_t value) +{ + volatile ApicIoUnit *ioapic = apic_get_ioapic(id)->ioapic; + ioapic->select.r = reg; + ioapic->window.r = value; +} + +static void +ioapic_read_entry(int apic, int pin, struct ioapic_route_entry *e) +{ + union ioapic_route_entry_union entry; + + entry.lo = ioapic_read(apic, APIC_IO_REDIR_LOW(pin)); + entry.hi = ioapic_read(apic, APIC_IO_REDIR_HIGH(pin)); + + *e = entry.both; +} + +/* Write the high word first because mask bit is in low word */ +static void +ioapic_write_entry(int apic, int pin, struct ioapic_route_entry e) +{ + union ioapic_route_entry_union entry = {{0, 0}}; + + entry.both = e; + ioapic_write(apic, APIC_IO_REDIR_HIGH(pin), entry.hi); + ioapic_write(apic, APIC_IO_REDIR_LOW(pin), entry.lo); +} + +/* When toggling the interrupt via mask, write low word only */ +static void +ioapic_toggle_entry(int apic, int pin, int mask) +{ + union ioapic_route_entry_union entry; + + spl_t s = simple_lock_irq(&ioapic_lock); + ioapic_read_entry(apic, pin, &entry.both); + entry.both.mask = mask & 0x1; + ioapic_write(apic, APIC_IO_REDIR_LOW(pin), entry.lo); + simple_unlock_irq(s, &ioapic_lock); +} + +static int +ioapic_version(int apic) +{ + return (ioapic_read(apic, APIC_IO_VERSION) >> APIC_IO_VERSION_SHIFT) & 0xff; +} + +static int +ioapic_gsis(int apic) +{ + return ((ioapic_read(apic, APIC_IO_VERSION) >> APIC_IO_ENTRIES_SHIFT) & 0xff) + 1; +} + +static void timer_expiry_callback(void *arg) +{ + volatile int *done = arg; + *done = 1; +} + +static uint32_t +timer_measure_10x_apic_hz(void) +{ + volatile int done = 0; + uint32_t start = 0xffffffff; + timer_elt_data_t tmp_timer; + tmp_timer.fcn = timer_expiry_callback; + tmp_timer.param = (void *)&done; + + printf("timer calibration..."); + + /* Set APIC timer */ + lapic->init_count.r = start; + + /* Delay for 10 ticks (10 * 1/hz seconds) */ + set_timeout(&tmp_timer, 10); + do { + cpu_pause(); + } while (!done); + + /* Stop APIC timer */ + lapic->lvt_timer.r |= LAPIC_DISABLE; + + printf(" done\n"); + + return start - lapic->cur_count.r; +} + +void +calibrate_lapic_timer(void) +{ + spl_t s; + + /* Set one-shot timer */ + lapic->divider_config.r = LAPIC_TIMER_DIVIDE_2; + lapic->lvt_timer.r = IOAPIC_INT_BASE; + + /* Measure number of APIC timer ticks in 10 mach ticks + * divide by 10 because we want to know how many in 1 tick */ + if (!calibrated_ticks) { + s = splhigh(); + spl0(); + calibrated_ticks = timer_measure_10x_apic_hz() / 10; + splx(s); + } +} + +void +lapic_enable_timer(void) +{ + /* Set up counter */ + lapic->init_count.r = calibrated_ticks; + lapic->divider_config.r = LAPIC_TIMER_DIVIDE_2; + + /* Set the timer to interrupt periodically on remapped timer GSI */ + lapic->lvt_timer.r = IOAPIC_INT_BASE | LAPIC_TIMER_PERIODIC; + + /* Some buggy hardware requires this set again */ + lapic->divider_config.r = LAPIC_TIMER_DIVIDE_2; + + /* Enable interrupts for the first time */ + printf("LAPIC timer configured on cpu%d\n", cpu_number()); +} + +void +ioapic_toggle(int pin, int mask) +{ + int apic = 0; + ioapic_toggle_entry(apic, pin, mask); +} + +void +ioapic_irq_eoi(int pin) +{ + int apic = 0; + union ioapic_route_entry_union oldentry, entry; + + if (pin == 0) + goto skip_specific_eoi; + + spl_t s = simple_lock_irq(&ioapic_lock); + + if (!has_irq_specific_eoi) { + /* Workaround for old IOAPICs with no specific EOI */ + + /* Mask the pin and change to edge triggered */ + ioapic_read_entry(apic, pin, &entry.both); + oldentry = entry; + entry.both.mask = IOAPIC_MASK_DISABLED; + entry.both.trigger = IOAPIC_EDGE_TRIGGERED; + ioapic_write_entry(apic, pin, entry.both); + + /* Restore level entry */ + ioapic_write_entry(apic, pin, oldentry.both); + } else { + volatile ApicIoUnit *ioapic = apic_get_ioapic(apic)->ioapic; + + ioapic_read_entry(apic, pin, &entry.both); + ioapic->eoi.r = entry.both.vector; + } + + simple_unlock_irq(s, &ioapic_lock); + +skip_specific_eoi: + lapic_eoi (); +} + +static unsigned int +override_irq(IrqOverrideData *override, union ioapic_route_entry_union *entry) +{ + if (override->flags & APIC_IRQ_OVERRIDE_TRIGGER_MASK) { + entry->both.trigger = (override->flags & APIC_IRQ_OVERRIDE_LEVEL_TRIGGERED) ? + IOAPIC_LEVEL_TRIGGERED : IOAPIC_EDGE_TRIGGERED; + } else { + if (override->bus == 0) { + /* ISA is edge-triggered by default */ + entry->both.trigger = IOAPIC_EDGE_TRIGGERED; + } else { + entry->both.trigger = IOAPIC_LEVEL_TRIGGERED; + } + } + + if (override->flags & APIC_IRQ_OVERRIDE_POLARITY_MASK) { + entry->both.polarity = (override->flags & APIC_IRQ_OVERRIDE_ACTIVE_LOW) ? + IOAPIC_ACTIVE_LOW : IOAPIC_ACTIVE_HIGH; + } else { + if (override->bus == 0) { + /* EISA is active-low for level-triggered interrupts */ + if (entry->both.trigger == IOAPIC_LEVEL_TRIGGERED) { + entry->both.polarity = IOAPIC_ACTIVE_LOW; + } else { + entry->both.polarity = IOAPIC_ACTIVE_HIGH; + } + } + } + printf("IRQ override: pin=%d gsi=%d trigger=%s polarity=%s\n", + override->irq, override->gsi, + entry->both.trigger == IOAPIC_LEVEL_TRIGGERED ? "LEVEL" : "EDGE", + entry->both.polarity == IOAPIC_ACTIVE_LOW ? "LOW" : "HIGH"); + + return override->gsi; +} + +void +ioapic_configure(void) +{ + /* Assume first IO APIC maps to GSI base 0 */ + int gsi, apic = 0, bsp = 0, pin; + IrqOverrideData *irq_over; + int timer_gsi; + int version = ioapic_version(apic); + int ngsis = ioapic_gsis(apic); + int ngsis2 = 0; + + if (version >= 0x20) { + has_irq_specific_eoi = 1; + } + + printf("IOAPIC version 0x%x\n", version); + + /* Disable IOAPIC interrupts and set spurious interrupt */ + lapic->spurious_vector.r = IOAPIC_SPURIOUS_BASE; + + union ioapic_route_entry_union entry = {{0, 0}}; + + entry.both.delvmode = IOAPIC_FIXED; + entry.both.destmode = IOAPIC_PHYSICAL; + entry.both.mask = IOAPIC_MASK_DISABLED; + entry.both.dest = apic_get_cpu_apic_id(bsp); + + for (pin = 0; pin < 16; pin++) { + gsi = pin; + + /* ISA legacy IRQs */ + entry.both.trigger = IOAPIC_EDGE_TRIGGERED; + entry.both.polarity = IOAPIC_ACTIVE_HIGH; + + if ((irq_over = acpi_get_irq_override(pin))) { + gsi = override_irq(irq_over, &entry); + } + entry.both.vector = IOAPIC_INT_BASE + gsi; + ioapic_write_entry(apic, pin, entry.both); + + /* Timer workaround for x86 */ + if (pin == 0) { + /* Save timer info */ + timer_gsi = gsi; + } else { + /* Remap timer irq */ + if (gsi == timer_gsi) { + timer_pin = pin; + /* Remap GSI base to timer pin so ivect[0] is the timer */ + entry.both.vector = IOAPIC_INT_BASE; + ioapic_write_entry(apic, timer_pin, entry.both); + /* Mask the duplicate pin 0 as we will be using timer_pin */ + mask_irq(0); + } + } + } + + for (pin = 16; pin < ngsis; pin++) { + gsi = pin; + + /* PCI IRQs PIRQ A-H */ + entry.both.trigger = IOAPIC_LEVEL_TRIGGERED; + entry.both.polarity = IOAPIC_ACTIVE_LOW; + + if ((irq_over = acpi_get_irq_override(pin))) { + gsi = override_irq(irq_over, &entry); + } + entry.both.vector = IOAPIC_INT_BASE + gsi; + ioapic_write_entry(apic, pin, entry.both); + } + + printf("IOAPIC 0 configured with GSI 0-%d\n", ngsis - 1); + + /* Second IOAPIC */ + if (apic_get_num_ioapics() > 1) { + apic = 1; + ngsis2 = ioapic_gsis(apic); + + for (pin = 0; pin < ngsis2; pin++) { + gsi = pin + ngsis; + + /* Defaults */ + entry.both.trigger = IOAPIC_LEVEL_TRIGGERED; + entry.both.polarity = IOAPIC_ACTIVE_LOW; + + if ((irq_over = acpi_get_irq_override(pin + ngsis))) { + gsi = override_irq(irq_over, &entry); + } + entry.both.vector = IOAPIC_INT_BASE + gsi; + ioapic_write_entry(apic, pin, entry.both); + } + + printf("IOAPIC 1 configured with GSI %d-%d\n", ngsis, ngsis + ngsis2 - 1); + } + + /* Start the IO APIC receiving interrupts */ + lapic_setup(); + lapic_enable(); +} diff --git a/i386/i386at/kd.c b/i386/i386at/kd.c new file mode 100644 index 0000000..2bea3c8 --- /dev/null +++ b/i386/i386at/kd.c @@ -0,0 +1,3059 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Olivetti Mach Console driver v0.0 + * Copyright Ing. C. Olivetti & C. S.p.A. 1988, 1989 + * All rights reserved. + * + */ +/* + Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc., +Cupertino, California. + + All Rights Reserved + + Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appears in all +copies and that both the copyright notice and this permission notice +appear in supporting documentation, and that the name of Olivetti +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + + OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, +NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/* + Copyright 1988, 1989 by Intel Corporation, Santa Clara, California. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appears in all +copies and that both the copyright notice and this permission notice +appear in supporting documentation, and that the name of Intel +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + +INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, +NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/* $ Header: $ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG 1 /* export feep() */ + +#if 0 +#define BROKEN_KEYBOARD_RESET +#endif + +struct tty kd_tty; +extern boolean_t rebootflag; + +static void charput(csrpos_t pos, char ch, char chattr); +static void charmvup(csrpos_t from, csrpos_t to, int count); +static void charmvdown(csrpos_t from, csrpos_t to, int count); +static void charclear(csrpos_t to, int count, char chattr); +static void charsetcursor(csrpos_t newpos); +static void kd_noopreset(void); + +/* + * These routines define the interface to the device-specific layer. + * See kdsoft.h for a more complete description of what each routine does. + */ +void (*kd_dput)(csrpos_t, char, char) = charput; /* put attributed char */ +void (*kd_dmvup)(csrpos_t, csrpos_t, int) = charmvup; /* block move up */ +void (*kd_dmvdown)(csrpos_t, csrpos_t, int) = charmvdown; /* block move down */ +void (*kd_dclear)(csrpos_t, int, char) = charclear; /* block clear */ +void (*kd_dsetcursor)(csrpos_t) = charsetcursor; + /* set cursor position on displayed page */ +void (*kd_dreset)(void) = kd_noopreset; /* prepare for reboot */ + +/* + * Globals used for both character-based controllers and bitmap-based + * controllers. Default is EGA. + */ + +vm_offset_t kd_bitmap_start = (vm_offset_t)0xa0000; /* XXX - put in kd.h */ +u_char *vid_start = (u_char *)EGA_START; + /* VM start of video RAM or frame buffer */ +csrpos_t kd_curpos = 0; /* set indirectly by kd_setpos--see kdsoft.h */ +short kd_lines = 25; +short kd_cols = 80; +char kd_attr = KA_NORMAL; /* current attribute */ +char kd_color = KA_NORMAL; +char kd_attrflags = 0; /* Not reverse, underline, blink */ + +/* + * kd_state shows the state of the modifier keys (ctrl, caps lock, + * etc.) It should normally be changed by calling set_kd_state(), so + * that the keyboard status LEDs are updated correctly. + */ +int kd_state = KS_NORMAL; +int kb_mode = KB_ASCII; /* event/ascii */ + +/* + * State for the keyboard "mouse". + */ +int kd_kbd_mouse = 0; +int kd_kbd_magic_scale = 6; +int kd_kbd_magic_button = 0; + +/* + * Some keyboard commands work by sending a command, waiting for an + * ack (handled by kdintr), then sending data, which generates a + * second ack. If we are in the middle of such a sequence, kd_ack + * shows what the ack is for. + * + * When a byte is sent to the keyboard, it is kept around in last_sent + * in case it needs to be resent. + * + * The rest of the variables here hold the data required to complete + * the sequence. + * + * XXX - the System V driver keeps a command queue, I guess in case we + * want to start a command while another is in progress. Is this + * something we should worry about? + */ +enum why_ack {NOT_WAITING, SET_LEDS, DATA_ACK}; +enum why_ack kd_ack = NOT_WAITING; + +u_char last_sent = 0; + +u_char kd_nextled = 0; + +/* + * We don't provide any mutex protection for this flag because we know + * that this module will have been initialized by the time multiple + * threads are running. + */ +boolean_t kd_initialized = FALSE; /* driver initialized? */ +boolean_t kd_extended = FALSE; + +/* Array for processing escape sequences. */ +#define K_MAXESC 32 +u_char esc_seq[K_MAXESC]; +u_char *esc_spt = (u_char *)0; + +/* + * This array maps scancodes to Ascii characters (or character + * sequences). + * Each row corresponds to one key. There are NUMOUTPUT bytes per key + * state. The states are ordered: Normal, SHIFT, CTRL, ALT, + * SHIFT/ALT. + */ + +/* This new keymap from Tudor Hulubei (tudor@cs.unh.edu) makes the + following changes to the keyboard driver: + + - Alt + key (m-key) returns `ESC key' instead of `ESC N key'. + - Backspace returns 0x7f instead of 0x08. + - Delete returns `ESC [ 9' instead of 0x7f. + - Alt + function keys return key sequences that are different + from the key sequences returned by the function keys alone. + This is done with the idea of allowing a terminal server to + implement multiple virtual consoles mapped on Alt+F1, Alt+F2, + etc, as in Linux. + + -- Derek Upham 1997/06/25 */ + +unsigned char key_map[NUMKEYS][WIDTH_KMAP] = { +{NC,NC,NC, NC,NC,NC, NC,NC,NC, NC,NC,NC, NC,NC,NC}, +{K_ESC,NC,NC, K_ESC,NC,NC, K_ESC,NC,NC, 0x1b,K_ESC,NC, K_ESC,NC,NC}, +{K_ONE,NC,NC, K_BANG,NC,NC, K_ONE,NC,NC, 0x1b,K_ONE,NC, 0x1b,0x4e,K_BANG}, +{K_TWO,NC,NC, K_ATSN,NC,NC, K_NUL,NC,NC, 0x1b,K_TWO,NC, 0x1b,0x4e,K_ATSN}, +{K_THREE,NC,NC, K_POUND,NC,NC, K_THREE,NC,NC, 0x1b,K_THREE,NC, 0x1b,0x4e,K_POUND}, +{K_FOUR,NC,NC, K_DOLLAR,NC,NC, K_FOUR,NC,NC, 0x1b,K_FOUR,NC, 0x1b,0x4e,K_DOLLAR}, +{K_FIVE,NC,NC, K_PERC,NC,NC, K_FIVE,NC,NC, 0x1b,K_FIVE,NC, 0x1b,0x4e,K_PERC}, +{K_SIX,NC,NC, K_CARET,NC,NC, K_RS,NC,NC, 0x1b,K_SIX,NC, 0x1b,0x4e,K_CARET}, +{K_SEVEN,NC,NC, K_AMPER,NC,NC, K_SEVEN,NC,NC, 0x1b,K_SEVEN,NC, 0x1b,0x4e,K_AMPER}, +{K_EIGHT,NC,NC, K_ASTER,NC,NC, K_EIGHT,NC,NC, 0x1b,K_EIGHT,NC, 0x1b,0x4e,K_ASTER}, +{K_NINE,NC,NC, K_LPAREN,NC,NC, K_NINE,NC,NC, 0x1b,K_NINE,NC, 0x1b,0x4e,K_LPAREN}, +{K_ZERO,NC,NC, K_RPAREN,NC,NC, K_ZERO,NC,NC, 0x1b,K_ZERO,NC, 0x1b,0x4e,K_RPAREN}, +{K_MINUS,NC,NC, K_UNDSC,NC,NC, K_US,NC,NC, 0x1b,K_MINUS,NC, 0x1b,0x4e,K_UNDSC}, +{K_EQL,NC,NC, K_PLUS,NC,NC, K_EQL,NC,NC, 0x1b,K_EQL,NC, 0x1b,0x4e,K_PLUS}, +{K_DEL,NC,NC, K_DEL,NC,NC, K_DEL,NC,NC, 0x1b,K_DEL,NC, K_DEL,NC,NC}, +{K_HT,NC,NC, K_GS,NC,NC, K_HT,NC,NC, 0x1b,K_HT,NC, K_GS,NC,NC}, +{K_q,NC,NC, K_Q,NC,NC, K_DC1,NC,NC, 0x1b,K_q,NC, 0x1b,0x4e,K_Q}, +{K_w,NC,NC, K_W,NC,NC, K_ETB,NC,NC, 0x1b,K_w,NC, 0x1b,0x4e,K_W}, +{K_e,NC,NC, K_E,NC,NC, K_ENQ,NC,NC, 0x1b,K_e,NC, 0x1b,0x4e,K_E}, +{K_r,NC,NC, K_R,NC,NC, K_DC2,NC,NC, 0x1b,K_r,NC, 0x1b,0x4e,K_R}, +{K_t,NC,NC, K_T,NC,NC, K_DC4,NC,NC, 0x1b,K_t,NC, 0x1b,0x4e,K_T}, +{K_y,NC,NC, K_Y,NC,NC, K_EM,NC,NC, 0x1b,K_y,NC, 0x1b,0x4e,K_Y}, +{K_u,NC,NC, K_U,NC,NC, K_NAK,NC,NC, 0x1b,K_u,NC, 0x1b,0x4e,K_U}, +{K_i,NC,NC, K_I,NC,NC, K_HT,NC,NC, 0x1b,K_i,NC, 0x1b,0x4e,K_I}, +{K_o,NC,NC, K_O,NC,NC, K_SI,NC,NC, 0x1b,K_o,NC, 0x1b,0x4e,K_O}, +{K_p,NC,NC, K_P,NC,NC, K_DLE,NC,NC, 0x1b,K_p,NC, 0x1b,0x4e,K_P}, +{K_LBRKT,NC,NC, K_LBRACE,NC,NC, K_ESC,NC,NC, 0x1b,K_LBRKT,NC, 0x1b,0x4e,K_LBRACE}, +{K_RBRKT,NC,NC, K_RBRACE,NC,NC, K_GS,NC,NC, 0x1b,K_RBRKT,NC, 0x1b,0x4e,K_RBRACE}, +{K_CR,NC,NC, K_CR,NC,NC, K_CR,NC,NC, 0x1b,K_CR,NC, K_CR,NC,NC}, +{K_SCAN,K_CTLSC,NC, K_SCAN,K_CTLSC,NC, K_SCAN,K_CTLSC,NC, K_SCAN,K_CTLSC,NC, K_SCAN,K_CTLSC,NC}, +{K_a,NC,NC, K_A,NC,NC, K_SOH,NC,NC, 0x1b,K_a,NC, 0x1b,0x4e,K_A}, +{K_s,NC,NC, K_S,NC,NC, K_DC3,NC,NC, 0x1b,K_s,NC, 0x1b,0x4e,K_S}, +{K_d,NC,NC, K_D,NC,NC, K_EOT,NC,NC, 0x1b,K_d,NC, 0x1b,0x4e,K_D}, +{K_f,NC,NC, K_F,NC,NC, K_ACK,NC,NC, 0x1b,K_f,NC, 0x1b,0x4e,K_F}, +{K_g,NC,NC, K_G,NC,NC, K_BEL,NC,NC, 0x1b,K_g,NC, 0x1b,0x4e,K_G}, +{K_h,NC,NC, K_H,NC,NC, K_BS,NC,NC, 0x1b,K_h,NC, 0x1b,0x4e,K_H}, +{K_j,NC,NC, K_J,NC,NC, K_LF,NC,NC, 0x1b,K_j,NC, 0x1b,0x4e,K_J}, +{K_k,NC,NC, K_K,NC,NC, K_VT,NC,NC, 0x1b,K_k,NC, 0x1b,0x4e,K_K}, +{K_l,NC,NC, K_L,NC,NC, K_FF,NC,NC, 0x1b,K_l,NC, 0x1b,0x4e,K_L}, +{K_SEMI,NC,NC, K_COLON,NC,NC, K_SEMI,NC,NC, 0x1b,K_SEMI,NC, 0x1b,0x4e,K_COLON}, +{K_SQUOTE,NC,NC,K_DQUOTE,NC,NC, K_SQUOTE,NC,NC,0x1b,K_SQUOTE,NC, 0x1b,0x4e,K_DQUOTE}, +{K_GRAV,NC,NC, K_TILDE,NC,NC, K_RS,NC,NC, 0x1b,K_GRAV,NC, 0x1b,0x4e,K_TILDE}, +{K_SCAN,K_LSHSC,NC, K_SCAN,K_LSHSC,NC, K_SCAN,K_LSHSC,NC, K_SCAN,K_LSHSC,NC, K_SCAN,K_LSHSC,NC}, +{K_BSLSH,NC,NC, K_PIPE,NC,NC, K_FS,NC,NC, 0x1b,K_BSLSH,NC, 0x1b,0x4e,K_PIPE}, +{K_z,NC,NC, K_Z,NC,NC, K_SUB,NC,NC, 0x1b,K_z,NC, 0x1b,0x4e,K_Z}, +{K_x,NC,NC, K_X,NC,NC, K_CAN,NC,NC, 0x1b,K_x,NC, 0x1b,0x4e,K_X}, +{K_c,NC,NC, K_C,NC,NC, K_ETX,NC,NC, 0x1b,K_c,NC, 0x1b,0x4e,K_C}, +{K_v,NC,NC, K_V,NC,NC, K_SYN,NC,NC, 0x1b,K_v,NC, 0x1b,0x4e,K_V}, +{K_b,NC,NC, K_B,NC,NC, K_STX,NC,NC, 0x1b,K_b,NC, 0x1b,0x4e,K_B}, +{K_n,NC,NC, K_N,NC,NC, K_SO,NC,NC, 0x1b,K_n,NC, 0x1b,0x4e,K_N}, +{K_m,NC,NC, K_M,NC,NC, K_CR,NC,NC, 0x1b,K_m,NC, 0x1b,0x4e,K_M}, +{K_COMMA,NC,NC, K_LTHN,NC,NC, K_COMMA,NC,NC, 0x1b,K_COMMA,NC, 0x1b,0x4e,K_LTHN}, +{K_PERIOD,NC,NC,K_GTHN,NC,NC, K_PERIOD,NC,NC,0x1b,K_PERIOD,NC, 0x1b,0x4e,K_GTHN}, +{K_SLASH,NC,NC, K_QUES,NC,NC, K_SLASH,NC,NC, 0x1b,K_SLASH,NC, 0x1b,0x4e,K_QUES}, +{K_SCAN,K_RSHSC,NC, K_SCAN,K_RSHSC,NC, K_SCAN,K_RSHSC,NC, K_SCAN,K_RSHSC,NC, K_SCAN,K_RSHSC,NC}, +{K_ASTER,NC,NC, K_ASTER,NC,NC, K_ASTER,NC,NC, 0x1b,K_ASTER,NC, 0x1b,0x4e,K_ASTER}, +{K_SCAN,K_ALTSC,NC, K_SCAN,K_ALTSC,NC, K_SCAN,K_ALTSC,NC, K_SCAN,K_ALTSC,NC, K_SCAN,K_ALTSC,NC}, +{K_SPACE,NC,NC, K_SPACE,NC,NC, K_NUL,NC,NC, 0x1b,K_SPACE,NC, K_SPACE,NC,NC}, +{K_SCAN,K_CLCKSC,NC, K_SCAN,K_CLCKSC,NC, K_SCAN,K_CLCKSC,NC, K_SCAN,K_CLCKSC,NC, K_SCAN,K_CLCKSC,NC}, +{K_F1, K_F1S, K_F1, K_F1A, K_F1S}, +{K_F2, K_F2S, K_F2, K_F2A, K_F2S}, +{K_F3, K_F3S, K_F3, K_F3A, K_F3S}, +{K_F4, K_F4S, K_F4, K_F4A, K_F4S}, +{K_F5, K_F5S, K_F5, K_F5A, K_F5S}, +{K_F6, K_F6S, K_F6, K_F6A, K_F6S}, +{K_F7, K_F7S, K_F7, K_F7A, K_F7S}, +{K_F8, K_F8S, K_F8, K_F8A, K_F8S}, +{K_F9, K_F9S, K_F9, K_F9A, K_F9S}, +{K_F10, K_F10S, K_F10, K_F10A, K_F10S}, +{K_SCAN,K_NLCKSC,NC, K_SCAN,K_NLCKSC,NC, K_SCAN,K_NLCKSC,NC, K_SCAN,K_NLCKSC,NC, K_SCAN,K_NLCKSC,NC}, +{K_SCRL, K_NUL,NC,NC, K_SCRL, K_SCRL, K_NUL,NC,NC}, +{K_HOME, K_SEVEN,NC,NC, K_HOME, K_HOME, 0x1b,0x4e,K_SEVEN}, +{K_UA, K_EIGHT,NC,NC, K_UA, K_UA, 0x1b,0x4e,K_EIGHT}, +{K_PUP, K_NINE,NC,NC, K_PUP, K_PUP, 0x1b,0x4e,K_NINE}, +{0x1b,0x5b,0x53, K_MINUS,NC,NC, 0x1b,0x5b,0x53, 0x1b,0x5b,0x53, 0x1b,0x4e,0x2d}, +{K_LA, K_FOUR,NC,NC, K_LA, K_LA, 0x1b,0x4e,K_FOUR}, +{0x1b,0x5b,0x47, K_FIVE,NC,NC, 0x1b,0x5b,0x47, 0x1b,0x5b,0x47, 0x1b,0x4e,0x35}, +{K_RA, K_SIX,NC,NC, K_RA, K_RA, 0x1b,0x4e,K_SIX}, +{0x1b,0x5b,0x54, K_PLUS,NC,NC, 0x1b,0x5b,0x54, 0x1b,0x5b,0x54, 0x1b,0x4e,0x2b}, +{K_END, K_ONE,NC,NC, K_END, K_END, 0x1b,0x4e,K_ONE}, +{K_DA, K_TWO,NC,NC, K_DA, K_DA, 0x1b,0x4e,K_TWO}, +{K_PDN, K_THREE,NC,NC, K_PDN, K_PDN, 0x1b,0x4e,K_THREE}, +{K_INS, K_ZERO,NC,NC, K_INS, K_INS, 0x1b,0x4e,K_ZERO}, +{0x1b,0x5b,0x39, K_PERIOD,NC,NC, K_DEL,NC,NC, K_DEL,NC,NC, 0x1b,0x4e,K_PERIOD}, +{NC,NC,NC, NC,NC,NC, NC,NC,NC, NC,NC,NC, NC,NC,NC}, +{NC,NC,NC, NC,NC,NC, NC,NC,NC, NC,NC,NC, NC,NC,NC}, +{NC,NC,NC, NC,NC,NC, NC,NC,NC, NC,NC,NC, NC,NC,NC}, +{K_F11, K_F11S, K_F11, K_F11A, K_F11S}, +{K_F12, K_F12S, K_F12, K_F12A, K_F12S} +}; + + +/* + * Globals used only for character-based controllers. + */ + +short kd_index_reg = EGA_IDX_REG; +short kd_io_reg = EGA_IO_REG; + + +/* + * Globals used only for bitmap-based controllers. See kdsoft.h for + * an explanation of what some of these variables are used for. + */ + +u_char *font_start = 0; /* starting addr of font */ + +short fb_width = 0; /* bits in frame buffer scan line */ +short fb_height = 0; /* scan lines in frame buffer*/ +short char_width = 0; /* bit width of 1 char */ +short char_height = 0; /* bit height of 1 char */ +short chars_in_font = 0; +short cursor_height = 0; /* bit height of cursor */ + +/* These initial values are simply guesses. */ +u_char char_black = 0; +u_char char_white = 0xff; + +short xstart = 0; +short ystart = 0; + +short char_byte_width = 0; /* char_width/NBBY */ +short fb_byte_width = 0; /* fb_width/NBBY */ +short font_byte_width = 0; /* num bytes in 1 scan line of font */ + +/* + * Switch for poll vs. interrupt. + */ +int kd_pollc = 0; + +#ifdef DEBUG +static void +pause(void) +{ + int i; + + for (i = 0; i < 50000; ++i) + ; +} + +/* + * feep: + * + * Ring the bell for a short time. + * Warning: uses outb(). You may prefer to use kd_debug_put. + */ +void +feep(void) +{ + kd_bellon(); + pause(); + kd_belloff(NULL); +} + +/* + * Put a debugging character on the screen. + * LOC=0 means put it in the bottom right corner, LOC=1 means put it + * one column to the left, etc. + */ +void +kd_debug_put( + int loc, + char c) +{ + csrpos_t pos = ONE_PAGE - (loc+1) * ONE_SPACE; + + (*kd_dput)(pos, c, KA_NORMAL); +} +#endif /* DEBUG */ + + +extern boolean_t mouse_in_use; +int old_kb_mode; + +void +cnpollc(boolean_t on) +{ + if (mouse_in_use) { + if (on) { + /* switch into X */ + old_kb_mode = kb_mode; + kb_mode = KB_ASCII; + X_kdb_enter(); + + kd_pollc++; + } else { + --kd_pollc; + + /* switch out of X */ + X_kdb_exit(); + kb_mode = old_kb_mode; + } + } else { + if (on) { + kd_pollc++; + } else { + --kd_pollc; + } + } +} + + + +/* + * kdopen: + * + * This opens the console driver and sets up the tty and other + * rudimentary stuff including calling the line discipline for + * setting up the device independent stuff for a tty driver. + * + * input: device number 'dev', and flag + * + * output: device is opened and setup + * + */ +int +kdopen( + dev_t dev, + int flag, + io_req_t ior) +{ + struct tty *tp; + spl_t o_pri; + + tp = &kd_tty; + o_pri = simple_lock_irq(&tp->t_lock); + if (!(tp->t_state & (TS_ISOPEN|TS_WOPEN))) { + /* XXX ttychars allocates memory */ + simple_unlock_nocheck(&tp->t_lock.slock); + ttychars(tp); + simple_lock_nocheck(&tp->t_lock.slock); + /* + * Special support for boot-time rc scripts, which don't + * stty the console. + */ + tp->t_oproc = kdstart; + tp->t_stop = kdstop; + tp->t_ospeed = tp->t_ispeed = B115200; + tp->t_flags = ODDP|EVENP|ECHO|CRMOD|XTABS|LITOUT; + kdinit(); + } + tp->t_state |= TS_CARR_ON; + simple_unlock_irq(o_pri, &tp->t_lock); + return (char_open(dev, tp, flag, ior)); +} + + +/* + * kdclose: + * + * This function merely executes the device independent code for + * closing the line discipline. + * + * input: device number 'dev', and flag + * + * output: device is closed + * + */ +/*ARGSUSED*/ +void +kdclose(dev_t dev, int flag) +{ + struct tty *tp; + + tp = &kd_tty; + { + spl_t s; + s = simple_lock_irq(&tp->t_lock); + ttyclose(tp); + simple_unlock_irq(s, &tp->t_lock); + } + + return; +} + + +/* + * kdread: + * + * This function executes the device independent code to read from + * the tty. + * + * input: device number 'dev' + * + * output: characters are read from tty clists + * + */ +/*ARGSUSED*/ +int +kdread(dev_t dev, io_req_t uio) +{ + struct tty *tp; + + tp = &kd_tty; + tp->t_state |= TS_CARR_ON; + return((*linesw[kd_tty.t_line].l_read)(tp, uio)); +} + + +/* + * kdwrite: + * + * This function does the device independent write action for this + * console (tty) driver. + * + * input: device number 'dev' + * + * output: characters are written to tty clists + * + */ +/*ARGSUSED*/ +int +kdwrite(dev_t dev, io_req_t uio) +{ + return((*linesw[kd_tty.t_line].l_write)(&kd_tty, uio)); +} + +/* + * Mmap. + */ + +/*ARGSUSED*/ +vm_offset_t +kdmmap(dev_t dev, vm_offset_t off, vm_prot_t prot) +{ + if (off >= (128*1024)) + return(-1); + + /* Get page frame number for the page to be mapped. */ + return(i386_btop(kd_bitmap_start+off)); +} + +int +kdportdeath( + dev_t dev, + mach_port_t port) +{ + return (tty_portdeath(&kd_tty, (ipc_port_t)port)); +} + +/*ARGSUSED*/ +io_return_t kdgetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, /* pointer to OUT array */ + mach_msg_type_number_t *count) /* OUT */ +{ + io_return_t result; + + switch (flavor) { + case KDGSTATE: + if (*count < 1) + return (D_INVALID_OPERATION); + *data = kd_state; + *count = 1; + result = D_SUCCESS; + break; + + case KDGKBENT: + result = kdgetkbent((struct kbentry *)data); + *count = sizeof(struct kbentry)/sizeof(int); + break; + + default: + result = tty_get_status(&kd_tty, flavor, data, count); + break; + } + return (result); +} + +/*ARGSUSED*/ +io_return_t kdsetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, + mach_msg_type_number_t count) +{ + io_return_t result; + + switch (flavor) { + case KDSKBENT: + if (count < sizeof(struct kbentry)/sizeof(int)) { + return (D_INVALID_OPERATION); + } + result = kdsetkbent((struct kbentry *)data, 0); + break; + + case KDSETBELL: + if (count < 1) + return (D_INVALID_OPERATION); + result = kdsetbell(*data, 0); + break; + + default: + result = tty_set_status(&kd_tty, flavor, data, count); + } + return (result); +} + + + +/* + * kdsetbell: + * + * Turn the bell on or off. Returns error code, if given bogus + * on/off value. + */ +int +kdsetbell( + int val, /* on or off */ + int flags) /* flags set for console */ +{ + int err = 0; + + if (val == KD_BELLON) + kd_bellon(); + else if (val == KD_BELLOFF) + kd_belloff(NULL); + else + err = D_INVALID_OPERATION; + + return(err); +} + +/* + * kdgetkbent: + * + * Get entry from key mapping table. Returns error code, if any. + */ +int +kdgetkbent(struct kbentry *kbent) +{ + u_char *cp; + spl_t o_pri = SPLKD(); /* probably superfluous */ + + cp = &key_map[kbent->kb_index][CHARIDX(kbent->kb_state)]; + kbent->kb_value[0] = *cp++; + kbent->kb_value[1] = *cp++; + kbent->kb_value[2] = *cp; + (void)splx(o_pri); + return(0); +} + + +/* + * kdsetkbent: + * + * Set entry in key mapping table. Return error code, if any. + */ +int +kdsetkbent( + struct kbentry *kbent, + int flags) /* flags set for console */ +{ + u_char *cp; + spl_t o_pri; + + o_pri = SPLKD(); + cp = &key_map[kbent->kb_index][CHARIDX(kbent->kb_state)]; + *cp++ = kbent->kb_value[0]; + *cp++ = kbent->kb_value[1]; + *cp = kbent->kb_value[2]; + (void)splx(o_pri); + return(0); +} + +/* + * kdintr: + * + * This function is the interrupt code for the driver. Since this is + * a special tty (console), interrupts are only for input, so we read in + * the character. If in ascii mode, we then do the mapping translation + * from the keyboard switch table and place the characters on the tty's + * input switch table. If in event mode, we create and queue a kd_event. + * + * input: interrupt vector 'vec' + * + * output: character or sequence is placed on appropriate queue + * + */ +/*ARGSUSED*/ +void +kdintr(int vec) +{ + struct tty *tp; + unsigned char c; + unsigned char scancode; + unsigned int char_idx; + boolean_t up = FALSE; /* key-up event */ + + if (kd_pollc) + return; /* kdb polling kbd */ + + if (!kd_initialized) + return; + + tp = &kd_tty; +#ifdef old + while ((inb(K_STATUS) & K_OBUF_FUL) == 0) + ; /* this should never loop */ +#else /* old */ + { + /* + * Allow for keyboards that raise interrupt before + * the character gets to the buffer. But don't wait + * forever if grabbing the character by polling leaves + * the interrupt on but buffer empty. + */ + /* + * Micronics VLB motherboard with 486DX2 can report keyboard + * interrupt before K_STATUS register indicates that the + * output buffer is full. Moreover, the bus won't settle w + * while we poll K_STATUS at speed. Temporary fix is to break + * out after safety runs out and pick up keyboard event. This + * should be fixed eventually by putting a 1us timout between + * inb's to K_STATUS and fix the pic initialization order to + * avoid bootup keyboard wedging (ie make kd a real device) + */ + int safety = 1000; + while ((inb(K_STATUS) & K_OBUF_FUL) == 0) + if (!safety--) break; /* XXX */ + } +#endif /* old */ + /* + * We may have seen a mouse event. + */ + if ((inb(K_STATUS) & 0x20) == 0x20) { + if (mouse_in_use) { + mouse_handle_byte((u_char)inb(K_RDWR)); + return; + } else { + printf("M%xI", inb(K_RDWR)); + return; + } + } + + scancode = inb(K_RDWR); + if (scancode == K_EXTEND && kb_mode != KB_EVENT) { + kd_extended = TRUE; + goto done; + } else if (scancode == K_RESEND) { + kd_resend(); + goto done; + } else if (scancode == K_ACKSC) { + kd_handle_ack(); + goto done; + } else if (kd_kbd_mouse && kd_kbd_magic(scancode)) { + goto done; + } else if (kdcheckmagic(scancode)) { + goto done; + } else if (kb_mode == KB_EVENT) { + kd_enqsc(scancode); + goto done; + } /* else... */ + + if (scancode & K_UP) { + up = TRUE; + scancode &= ~K_UP; + } + if (scancode < NUMKEYS) { + /* Lookup in map, then process. */ + char_idx = kdstate2idx(kd_state, kd_extended); + c = key_map[scancode][char_idx]; + if (c == K_SCAN) { + c = key_map[scancode][++char_idx]; + set_kd_state(do_modifier(kd_state, c, up)); + } else if (!up) { + /* regular key-down */ + unsigned int max; /* max index for char sequence */ + + max = char_idx + NUMOUTPUT; + char_idx++; + if (!kd_extended) { + if (kd_state&KS_CLKED) { + if (kd_isupper(c)) { + c += ('a' - 'A'); + max = char_idx; + } + else if (kd_islower(c)) { + c -= ('a' - 'A'); + max = char_idx; + } + } + /* + * Notice that even if the keypad is remapped, + * NumLock only effects the keys that are + * physically part of the keypad. Is this + * The Right Thing? + */ + if ((kd_state&KS_NLKED) && + (((K_HOMESC) <= scancode) && + (scancode <= (K_DELSC)))) { + char_idx = CHARIDX(SHIFT_STATE); + c = key_map[scancode][char_idx]; + max = char_idx + NUMOUTPUT; + char_idx++; + } + } + + /* + * here's where we actually put the char (or + * char sequence, for function keys) onto the + * input queue. + */ + for ( ; (c != K_DONE) && (char_idx <= max); + c = key_map[scancode][char_idx++]) { + (*linesw[tp->t_line].l_rint)(c, tp); + } + kd_extended = FALSE; + } + } + + done: + return; +} + +/* + * kd_handle_ack: + * + * For pending commands, complete the command. For data bytes, + * drop the ack on the floor. + */ +void +kd_handle_ack(void) +{ + switch (kd_ack) { + case SET_LEDS: + kd_setleds2(); + kd_ack = DATA_ACK; + break; + case DATA_ACK: + kd_ack = NOT_WAITING; + break; + case NOT_WAITING: + printf("unexpected ACK from keyboard\n"); + break; + default: + panic("bogus kd_ack\n"); + break; + } +} + +/* + * kd_resend: + * + * Resend a missed keyboard command or data byte. + */ +void +kd_resend(void) +{ + if (kd_ack == NOT_WAITING) + printf("unexpected RESEND from keyboard\n"); + else + kd_senddata(last_sent); +} + + +/* + * do_modifier: + * + * Change keyboard state according to which modifier key and + * whether it went down or up. + * + * input: the current state, the key, and the key's direction. + * The key can be any key, not just a modifier key. + * + * output: the new state + */ +int +do_modifier( + int state, + Scancode c, + boolean_t up) +{ + switch (c) { + case (K_ALTSC): + if (up) + state &= ~KS_ALTED; + else + state |= KS_ALTED; + kd_extended = FALSE; + break; +#ifndef ORC + case (K_CLCKSC): +#endif /* ORC */ + case (K_CTLSC): + if (up) + state &= ~KS_CTLED; + else + state |= KS_CTLED; + kd_extended = FALSE; + break; +#ifdef ORC + case (K_CLCKSC): + if (!up) + state ^= KS_CLKED; + break; +#endif /* ORC */ + case (K_NLCKSC): + if (!up) + state ^= KS_NLKED; + break; + case (K_LSHSC): + case (K_RSHSC): + if (up) + state &= ~KS_SHIFTED; + else + state |= KS_SHIFTED; + kd_extended = FALSE; + break; + } + + return(state); +} + + +/* + * kdcheckmagic: + * + * Check for magic keystrokes for invoking the debugger or + * rebooting or ... + * + * input: an unprocessed scancode + * + * output: TRUE if a magic key combination was recognized and + * processed. FALSE otherwise. + * + * side effects: + * various actions possible, depending on which keys are + * pressed. If the debugger is called, steps are taken + * to ensure that the system doesn't think the magic keys + * are still held down. + */ +boolean_t +kdcheckmagic(Scancode scancode) +{ + static int magic_state = KS_NORMAL; /* like kd_state */ + boolean_t up = FALSE; + + if (scancode == 0x46) /* scroll lock */ +/* if (scancode == 0x52) ** insert key */ + { + kd_kbd_mouse = !kd_kbd_mouse; + kd_kbd_magic_button = 0; + return(TRUE); + } + if (scancode & K_UP) { + up = TRUE; + scancode &= ~K_UP; + } + magic_state = do_modifier(magic_state, scancode, up); + + if ((magic_state&(KS_CTLED|KS_ALTED)) == (KS_CTLED|KS_ALTED)) { + switch (scancode) { +#if MACH_KDB + case K_dSC: /* ctl-alt-d */ + kdb_kintr(); /* invoke debugger */ + /* Returned from debugger, so reset kbd state. */ + (void)SPLKD(); + magic_state = KS_NORMAL; + if (kb_mode == KB_ASCII) + kd_state = KS_NORMAL; + /* setting leds kills kbd */ + else { + kd_enqsc(K_ALTSC | K_UP); + kd_enqsc(K_CTLSC | K_UP); + kd_enqsc(K_dSC | K_UP); + } + return(TRUE); + break; +#endif /* MACH_KDB */ + case K_DELSC: /* ctl-alt-del */ + /* if rebootflag is on, reboot the system */ + if (rebootflag) + kdreboot(); + break; + } + } + return(FALSE); +} + + +/* + * kdstate2idx: + * + * Return the value for the 2nd index into key_map that + * corresponds to the given state. + */ +unsigned int +kdstate2idx(unsigned int state, /* bit vector, not a state index */ + boolean_t extended) +{ + int state_idx = NORM_STATE; + + if ((!extended) && state != KS_NORMAL) { + if ((state&(KS_SHIFTED|KS_ALTED)) == (KS_SHIFTED|KS_ALTED)) + state_idx = SHIFT_ALT; + /* CTRL should have higher priority than SHIFT. That + way, CTRL-SHIFT-2 and CTRL-2 produce the same keycode. + --Derek Upham 1997/06/25 */ + else if (state&KS_CTLED) + state_idx = CTRL_STATE; + else if (state&KS_SHIFTED) + state_idx = SHIFT_STATE; + else if (state&KS_ALTED) + state_idx = ALT_STATE; + } + + return (CHARIDX(state_idx)); +} + +/* + * kdstart: + * + * This function does the general processing of characters and other + * operations for the device driver. The device independent portion of + * the tty driver calls this routine (it's setup in kdinit) with a + * given command. That command is then processed, and control is passed + * back to the kernel. + * + * input: tty pointer 'tp', and command to execute 'cmd' + * + * output: command is executed + * + * Entered and left at spltty. Drops priority to spl0 to display character. + * ASSUMES that it is never called from interrupt-driven code. + */ +void +kdstart(struct tty *tp) +{ + spl_t o_pri; + int ch; + + if (tp->t_state & TS_TTSTOP) + return; + for ( ; ; ) { + tp->t_state &= ~TS_BUSY; + if (tp->t_state & TS_TTSTOP) + break; + if ((tp->t_outq.c_cc <= 0) || (ch = getc(&tp->t_outq)) == -1) + break; + /* + * Drop priority for long screen updates. ttstart() calls us at + * spltty. + */ + o_pri = splsoftclock(); /* block timeout */ + kd_putc_esc(ch); + splx(o_pri); + } + if (tp->t_outq.c_cc <= TTLOWAT(tp)) { + tt_write_wakeup(tp); + } +} + +/*ARGSUSED*/ +void +kdstop( + struct tty *tp, + int flags) +{ + /* + * do nothing - all characters are output by one call to + * kdstart. + */ +} + +/* + * kdinit: + * + * This code initializes the structures and sets up the port registers + * for the console driver. + * + * Each bitmap-based graphics card is likely to require a unique + * way to determine the card's presence. The driver runs through + * each "special" card that it knows about and uses the first one + * that it finds. If it doesn't find any, it assumes that an + * EGA-like card is installed. + * + * input : None. Interrupts are assumed to be disabled + * output : Driver is initialized + * + */ +void +kdinit(void) +{ + unsigned char k_comm; /* keyboard command byte */ + + if (kd_initialized) + return; + + esc_spt = esc_seq; + kd_attr = KA_NORMAL; + + kd_attrflags = 0; + kd_color = KA_NORMAL; + /* + * board specific initialization: set up globals and kd_dxxx + * pointers, and synch displayed cursor with logical cursor. + */ + kd_xga_init(); + + /* get rid of any garbage in output buffer */ + if (inb(K_STATUS) & K_OBUF_FUL) + (void)inb(K_RDWR); + + kd_sendcmd(KC_CMD_READ); /* ask for the ctlr command byte */ + k_comm = kd_getdata(); + k_comm &= ~K_CB_DISBLE; /* clear keyboard disable bit */ + k_comm |= K_CB_ENBLIRQ; /* enable interrupt */ + kd_sendcmd(KC_CMD_WRITE); /* write new ctlr command byte */ + kd_senddata(k_comm); + unmask_irq(KBD_IRQ); + kd_initialized = TRUE; + +#if ENABLE_IMMEDIATE_CONSOLE + /* Now that we're set up, we no longer need or want the + immediate console. */ + { + extern boolean_t immediate_console_enable; + immediate_console_enable = FALSE; + } + + /* The immediate console printed stuff at the bottom of the + screen rather than at the cursor position, so that's where + we should start. */ + kd_setpos(ONE_PAGE - ONE_LINE); printf("\n"); +#endif /* ENABLE_IMMEDIATE_CONSOLE */ + + cnsetleds(kd_state = KS_NORMAL); + /* clear the LEDs AFTER we + enable the keyboard controller. + This keeps NUM-LOCK from being + set on the NEC Versa. */ + + /* Allocate the input buffer. */ + ttychars(&kd_tty); +} + +/* + * kd_belloff: + * + * This routine shuts the bell off, by sending the appropriate code + * to the speaker port. + * + * input : None + * output : bell is turned off + * + */ +static boolean_t kd_bellstate = FALSE; + +void +kd_belloff(void * param) +{ + unsigned char status; + + status = (inb(K_PORTB) & ~(K_SPKRDATA | K_ENABLETMR2)); + outb(K_PORTB, status); + kd_bellstate = FALSE; + return; +} + + +/* + * kd_bellon: + * + * This routine turns the bell on. + * + * input : None + * output : bell is turned on + * + */ +void +kd_bellon(void) +{ + unsigned char status; + + /* program timer 2 */ + outb(K_TMRCTL, K_SELTMR2 | K_RDLDTWORD | K_TSQRWAVE | K_TBINARY); + outb(K_TMR2, 1500 & 0xff); /* LSB */ + outb(K_TMR2, (int)1500 >> 8); /* MSB */ + + /* start speaker - why must we turn on K_SPKRDATA? */ + status = (inb(K_PORTB)| K_ENABLETMR2 | K_SPKRDATA); + outb(K_PORTB, status); + return; +} + +/* + * + * Function kd_putc_esc(): + * + * This function puts a character on the screen, handling escape + * sequences. + * + * input : character to be displayed (or part of an escape code) + * output : character is displayed, or some action is taken + * + */ +void +kd_putc_esc(u_char c) +{ + if (c == (K_ESC)) { + if (esc_spt == esc_seq) { + *(esc_spt++)=(K_ESC); + *(esc_spt) = '\0'; + } else { + kd_putc((K_ESC)); + esc_spt = esc_seq; + } + } else { + if (esc_spt - esc_seq) { + if (esc_spt - esc_seq > K_MAXESC - 1) + esc_spt = esc_seq; + else { + *(esc_spt++) = c; + *(esc_spt) = '\0'; + kd_parseesc(); + } + } else { + kd_putc(c); + } + } +} + +/* + * + * Function kd_putc(): + * + * This function simply puts a character on the screen. It does some + * special processing for linefeed, carriage return, backspace and + * the bell. + * + * input : character to be displayed + * output : character is displayed, or some action is taken + * + */ +int sit_for_0 = 1; + +void +kd_putc(u_char ch) +{ + if ((!ch) && sit_for_0) + return; + + switch (ch) { + case ((K_LF)): + kd_down(); + break; + case ((K_CR)): + kd_cr(); + break; + case ((K_BS)): + kd_left(); + break; + case ((K_HT)): + kd_tab(); + break; + case ((K_BEL)): + /* + * Similar problem to K_BS here (behavior might depend + * on tty setting). Also check LF and CR. + */ + if (!kd_bellstate) + { + kd_bellon(); + timeout(kd_belloff, 0, hz/8 ); + kd_bellstate = TRUE; + } + break; + default: + (*kd_dput)(kd_curpos, ch, kd_attr); + kd_right(); + break; + } + return; +} + + +/* + * kd_setpos: + * + * This function sets the software and hardware cursor position + * on the screen, using device-specific code to actually move and + * display the cursor. + * + * input : position on (or off) screen to move the cursor to + * output : cursor position is updated, screen has been scrolled + * if necessary to bring cursor position back onto + * screen. + * + */ +void +kd_setpos(csrpos_t newpos) +{ + if (newpos > ONE_PAGE) { + kd_scrollup(); + newpos = BOTTOM_LINE; + } + if (newpos < 0) { + kd_scrolldn(); + newpos = 0; + } + + (*kd_dsetcursor)(newpos); +} + + +/* + * kd_scrollup: + * + * This function scrolls the screen up one line using a DMA memory + * copy. + * + * input : None + * output : lines on screen appear to be shifted up one line + * + */ +void +kd_scrollup(void) +{ + csrpos_t to; + csrpos_t from; + int count; + + /* scroll up */ + to = 0; + from = ONE_LINE; + count = (ONE_PAGE - ONE_LINE)/ONE_SPACE; + (*kd_dmvup)(from, to, count); + + /* clear bottom line */ + to = BOTTOM_LINE; + count = ONE_LINE/ONE_SPACE; + (*kd_dclear)(to, count, kd_attr); + return; +} + + +/* + * kd_scrolldn: + * + * Scrolls the characters on the screen down one line. + * + * input : None + * output : Lines on screen appear to be moved down one line + * + */ +void +kd_scrolldn(void) +{ + csrpos_t to; + csrpos_t from; + int count; + + /* move down */ + to = ONE_PAGE - ONE_SPACE; + from = ONE_PAGE - ONE_LINE - ONE_SPACE; + count = (ONE_PAGE - ONE_LINE) / ONE_SPACE; + (*kd_dmvdown)(from, to, count); + + /* clear top line */ + to = 0; + count = ONE_LINE/ONE_SPACE; + (*kd_dclear)(to, count, kd_attr); + return; + +} + + +/* + * kd_parseesc: + * + * This routine begins the parsing of an escape sequence. It uses the + * escape sequence array and the escape spot pointer to handle + * asynchronous parsing of escape sequences. + * + * input : String of characters prepended by an escape + * output : Appropriate actions are taken depending on the string as + * defined by the ansi terminal specification + * + */ +void +kd_parseesc(void) +{ + u_char *escp; + + escp = esc_seq + 1; /* point to char following ESC */ + switch(*(escp)) { + case 'c': + kd_cls(); + kd_home(); + esc_spt = esc_seq; /* reset spot in ESC sequence */ + break; + case '[': + escp++; + kd_parserest(escp); + break; + case '\0': + break; /* not enough info yet */ + default: + kd_putc(*escp); + esc_spt = esc_seq; /* inv sequence char, reset */ + break; + } + return; + +} + + +/* kd_update_kd_attr: + * + * Updates kd_attr according to kd_attrflags and kd_color. + * This code has its origin from console.c and selection.h in + * linux 2.2 drivers/char/. + * Modified for GNU Mach by Marcus Brinkmann. + */ + +#define reverse_video_char(a) (((a) & 0x88) | ((((a) >> 4) | ((a) << 4)) & 0x77)) +static void +kd_update_kd_attr(void) +{ + kd_attr = kd_color; + if (kd_attrflags & KAX_UNDERLINE) + kd_attr = (kd_attr & 0xf0) | KAX_COL_UNDERLINE; + else if (kd_attrflags & KAX_DIM) + kd_attr = (kd_attr & 0xf0) | KAX_COL_DIM; + if (kd_attrflags & KAX_REVERSE) + kd_attr = reverse_video_char(kd_attr); + if (kd_attrflags & KAX_BLINK) + kd_attr ^= 0x80; + if (kd_attrflags & KAX_BOLD) + kd_attr ^= 0x08; +} + +/* color_table added by Julio Merino to take proper color order. + * I get this code from Linux 2.2 source code in file: + * linux/drivers/char/console.c + */ +unsigned char color_table[] = { 0, 4, 2, 6, 1, 5, 3, 7, + 8,12,10,14, 9,13,11,15 }; +/* + * kd_parserest: + * + * This function will complete the parsing of an escape sequence and + * call the appropriate support routine if it matches a character. This + * function could be greatly improved by using a function jump table, and + * removing this bulky switch statement. + * + * input : An string + * output : Appropriate action based on whether the string matches a + * sequence acceptable to the ansi terminal specification + * + */ +void +kd_parserest(u_char *cp) +{ + int number[16], npar = 0, i; + csrpos_t newpos; + + for(i=0;i<=15;i++) + number[i] = MACH_ATOI_DEFAULT; + + do { + cp += mach_atoi(cp, &number[npar]); + } while (*cp == ';' && ++npar <= 15 && cp++); + + switch(*cp) { + case 'm': + for (i=0;i<=npar;i++) + switch(number[i]) { + case MACH_ATOI_DEFAULT: + case 0: + kd_attrflags = 0; + kd_color = KA_NORMAL; + break; + case 1: + kd_attrflags |= KAX_BOLD; + kd_attrflags &= ~KAX_DIM; + break; + case 2: + kd_attrflags |= KAX_DIM; + kd_attrflags &= ~KAX_BOLD; + break; + case 4: + kd_attrflags |= KAX_UNDERLINE; + break; + case 5: + kd_attrflags |= KAX_BLINK; + break; + case 7: + kd_attrflags |= KAX_REVERSE; + break; + case 8: + kd_attrflags |= KAX_INVISIBLE; + break; + case 21: + case 22: + kd_attrflags &= ~(KAX_BOLD | KAX_DIM); + break; + case 24: + kd_attrflags &= ~KAX_UNDERLINE; + break; + case 25: + kd_attrflags &= ~KAX_BLINK; + break; + case 27: + kd_attrflags &= ~KAX_REVERSE; + break; + case 38: + kd_attrflags |= KAX_UNDERLINE; + kd_color = (kd_color & 0xf0) | (KA_NORMAL & 0x0f); + break; + case 39: + kd_attrflags &= ~KAX_UNDERLINE; + kd_color = (kd_color & 0xf0) | (KA_NORMAL & 0x0f); + break; + default: + if (number[i] >= 30 && number[i] <= 37) { + /* foreground color */ + kd_color = (kd_color & 0xf0) | color_table[(number[i] - 30)]; + } else if (number[i] >= 40 && number[i] <= 47) { + /* background color */ + kd_color = (kd_color & 0x0f) | (color_table[(number[i] - 40)] << 4); + } + break; + } + kd_update_kd_attr(); + esc_spt = esc_seq; + break; + case '@': + if (number[0] == MACH_ATOI_DEFAULT) + kd_insch(1); + else + kd_insch(number[0]); + esc_spt = esc_seq; + break; + case 'A': + if (number[0] == MACH_ATOI_DEFAULT) + kd_up(); + else + while (number[0]--) + kd_up(); + esc_spt = esc_seq; + break; + case 'B': + if (number[0] == MACH_ATOI_DEFAULT) + kd_down(); + else + while (number[0]--) + kd_down(); + esc_spt = esc_seq; + break; + case 'C': + if (number[0] == MACH_ATOI_DEFAULT) + kd_right(); + else + while (number[0]--) + kd_right(); + esc_spt = esc_seq; + break; + case 'D': + if (number[0] == MACH_ATOI_DEFAULT) + kd_left(); + else + while (number[0]--) + kd_left(); + esc_spt = esc_seq; + break; + case 'E': + kd_cr(); + if (number[0] == MACH_ATOI_DEFAULT) + kd_down(); + else + while (number[0]--) + kd_down(); + esc_spt = esc_seq; + break; + case 'F': + kd_cr(); + if (number[0] == MACH_ATOI_DEFAULT) + kd_up(); + else + while (number[0]--) + kd_up(); + esc_spt = esc_seq; + break; + case 'G': + if (number[0] == MACH_ATOI_DEFAULT) + number[0] = 0; + else + if (number[0] > 0) + --number[0]; /* because number[0] is from 1 */ + kd_setpos(BEG_OF_LINE(kd_curpos) + number[0] * ONE_SPACE); + esc_spt = esc_seq; + break; + case 'f': + case 'H': + if (number[0] == MACH_ATOI_DEFAULT && number[1] == MACH_ATOI_DEFAULT) + { + kd_home(); + esc_spt = esc_seq; + break; + } + if (number[0] == MACH_ATOI_DEFAULT) + number[0] = 0; + else if (number[0] > 0) + --number[0]; /* numbered from 1 */ + newpos = (number[0] * ONE_LINE); /* setup row */ + if (number[1] == MACH_ATOI_DEFAULT) + number[1] = 0; + else if (number[1] > 0) + number[1]--; + newpos += (number[1] * ONE_SPACE); /* setup column */ + if (newpos < 0) + newpos = 0; /* upper left */ + if (newpos > ONE_PAGE) + newpos = (ONE_PAGE - ONE_SPACE); /* lower right */ + kd_setpos(newpos); + esc_spt = esc_seq; + break; /* done or not ready */ + case 'J': + switch(number[0]) { + case MACH_ATOI_DEFAULT: + case 0: + kd_cltobcur(); /* clears from current + pos to bottom. + */ + break; + case 1: + kd_cltopcur(); /* clears from top to + current pos. + */ + break; + case 2: + kd_cls(); + break; + default: + break; + } + esc_spt = esc_seq; /* reset it */ + break; + case 'K': + switch(number[0]) { + case MACH_ATOI_DEFAULT: + case 0: + kd_cltoecur(); /* clears from current + pos to eoln. + */ + break; + case 1: + kd_clfrbcur(); /* clears from begin + of line to current + pos. + */ + break; + case 2: + kd_eraseln(); /* clear entire line */ + break; + default: + break; + } + esc_spt = esc_seq; + break; + case 'L': + if (number[0] == MACH_ATOI_DEFAULT) + kd_insln(1); + else + kd_insln(number[0]); + esc_spt = esc_seq; + break; + case 'M': + if (number[0] == MACH_ATOI_DEFAULT) + kd_delln(1); + else + kd_delln(number[0]); + esc_spt = esc_seq; + break; + case 'P': + if (number[0] == MACH_ATOI_DEFAULT) + kd_delch(1); + else + kd_delch(number[0]); + esc_spt = esc_seq; + break; + case 'S': + if (number[0] == MACH_ATOI_DEFAULT) + kd_scrollup(); + else + while (number[0]--) + kd_scrollup(); + esc_spt = esc_seq; + break; + case 'T': + if (number[0] == MACH_ATOI_DEFAULT) + kd_scrolldn(); + else + while (number[0]--) + kd_scrolldn(); + esc_spt = esc_seq; + break; + case 'X': + if (number[0] == MACH_ATOI_DEFAULT) + kd_erase(1); + else + kd_erase(number[0]); + esc_spt = esc_seq; + break; + case '\0': + break; /* not enough yet */ + default: + kd_putc(*cp); /* show inv character */ + esc_spt = esc_seq; /* inv entry, reset */ + break; + } + return; +} + +void +kd_tab(void) +{ + int i; + + for (i = 8 - (CURRENT_COLUMN(kd_curpos) % 8); i > 0; i--) { + kd_putc(' '); + } + +} + + +/* + * kd_cls: + * + * This function clears the screen with spaces and the current attribute. + * + * input : None + * output : Screen is cleared + * + */ +void +kd_cls(void) +{ + (*kd_dclear)(0, ONE_PAGE/ONE_SPACE, kd_attr); + return; +} + + +/* + * kd_home: + * + * This function will move the cursor to the home position on the screen, + * as well as set the internal cursor position (kd_curpos) to home. + * + * input : None + * output : Cursor position is moved + * + */ +void +kd_home(void) +{ + kd_setpos(0); + return; +} + + +/* + * kd_up: + * + * This function moves the cursor up one line position. + * + * input : None + * output : Cursor moves up one line, or screen is scrolled + * + */ +void +kd_up(void) +{ + if (kd_curpos < ONE_LINE) + kd_scrolldn(); + else + kd_setpos(kd_curpos - ONE_LINE); + return; +} + + +/* + * kd_down: + * + * This function moves the cursor down one line position. + * + * input : None + * output : Cursor moves down one line or the screen is scrolled + * + */ +void +kd_down(void) +{ + if (kd_curpos >= (ONE_PAGE - ONE_LINE)) + kd_scrollup(); + else + kd_setpos(kd_curpos + ONE_LINE); + return; +} + + +/* + * kd_right: + * + * This function moves the cursor one position to the right. + * + * input : None + * output : Cursor moves one position to the right + * + */ +void +kd_right(void) +{ + if (kd_curpos < (ONE_PAGE - ONE_SPACE)) + kd_setpos(kd_curpos + ONE_SPACE); + else { + kd_scrollup(); + kd_setpos(BEG_OF_LINE(kd_curpos)); + } + return; +} + + +/* + * kd_left: + * + * This function moves the cursor one position to the left. + * + * input : None + * output : Cursor moves one position to the left + * + */ +void +kd_left(void) +{ + if (0 < kd_curpos) + kd_setpos(kd_curpos - ONE_SPACE); + return; +} + + +/* + * kd_cr: + * + * This function moves the cursor to the beginning of the current + * line. + * + * input : None + * output : Cursor moves to the beginning of the current line + * + */ +void +kd_cr(void) +{ + kd_setpos(BEG_OF_LINE(kd_curpos)); + return; +} + + +/* + * kd_cltobcur: + * + * This function clears from the current cursor position to the bottom + * of the screen. + * + * input : None + * output : Screen is cleared from current cursor position to bottom + * + */ +void +kd_cltobcur(void) +{ + csrpos_t start; + int count; + + start = kd_curpos; + count = (ONE_PAGE - kd_curpos)/ONE_SPACE; + (*kd_dclear)(start, count, kd_attr); + return; +} + + +/* + * kd_cltopcur: + * + * This function clears from the current cursor position to the top + * of the screen. + * + * input : None + * output : Screen is cleared from current cursor position to top + * + */ +void +kd_cltopcur(void) +{ + int count; + + count = (kd_curpos + ONE_SPACE) / ONE_SPACE; + (*kd_dclear)(0, count, kd_attr); + return; +} + + +/* + * kd_cltoecur: + * + * This function clears from the current cursor position to eoln. + * + * input : None + * output : Line is cleared from current cursor position to eoln + * + */ +void +kd_cltoecur(void) +{ + csrpos_t i; + csrpos_t hold; + + hold = BEG_OF_LINE(kd_curpos) + ONE_LINE; + for (i = kd_curpos; i < hold; i += ONE_SPACE) { + (*kd_dput)(i, K_SPACE, kd_attr); + } +} + + +/* + * kd_clfrbcur: + * + * This function clears from the beginning of the line to the current + * cursor position. + * + * input : None + * output : Line is cleared from beginning to current position + * + */ +void +kd_clfrbcur(void) +{ + csrpos_t i; + + for (i = BEG_OF_LINE(kd_curpos); i <= kd_curpos; i += ONE_SPACE) { + (*kd_dput)(i, K_SPACE, kd_attr); + } +} + + +/* + * kd_delln: + * + * This function deletes 'number' lines on the screen by effectively + * scrolling the lines up and replacing the old lines with spaces. + * + * input : number of lines to delete + * output : lines appear to be deleted + * + */ +void +kd_delln(int number) +{ + csrpos_t to; + csrpos_t from; + int delbytes; /* num of bytes to delete */ + int count; /* num of words to move or fill */ + + if (number <= 0) + return; + + delbytes = number * ONE_LINE; + to = BEG_OF_LINE(kd_curpos); + if (to + delbytes >= ONE_PAGE) + delbytes = ONE_PAGE - to; + if (to + delbytes < ONE_PAGE) { + from = to + delbytes; + count = (ONE_PAGE - from) / ONE_SPACE; + (*kd_dmvup)(from, to, count); + } + + to = ONE_PAGE - delbytes; + count = delbytes / ONE_SPACE; + (*kd_dclear)(to, count, kd_attr); + return; +} + + +/* + * kd_insln: + * + * This function inserts a line above the current one by + * scrolling the current line and all the lines below it down. + * + * input : number of lines to insert + * output : New lines appear to be inserted + * + */ +void +kd_insln(int number) +{ + csrpos_t to; + csrpos_t from; + int count; + csrpos_t top; /* top of block to be moved */ + int insbytes; /* num of bytes inserted */ + + if (number <= 0) + return; + + top = BEG_OF_LINE(kd_curpos); + insbytes = number * ONE_LINE; + if (top + insbytes > ONE_PAGE) + insbytes = ONE_PAGE - top; + to = ONE_PAGE - ONE_SPACE; + from = to - insbytes; + if (from > top) { + count = (from - top + ONE_SPACE) / ONE_SPACE; + (*kd_dmvdown)(from, to, count); + } + + count = insbytes / ONE_SPACE; + (*kd_dclear)(top, count, kd_attr); + return; +} + + +/* + * kd_delch: + * + * This function deletes a number of characters from the current + * position in the line. + * + * input : number of characters to delete + * output : characters appear to be deleted + * + */ +void +kd_delch(int number) +{ + int count; /* num words moved/filled */ + int delbytes; /* bytes to delete */ + csrpos_t to; + csrpos_t from; + csrpos_t nextline; /* start of next line */ + + if (number <= 0) + return; + + nextline = BEG_OF_LINE(kd_curpos) + ONE_LINE; + delbytes = number * ONE_SPACE; + if (kd_curpos + delbytes > nextline) + delbytes = nextline - kd_curpos; + if (kd_curpos + delbytes < nextline) { + from = kd_curpos + delbytes; + to = kd_curpos; + count = (nextline - from) / ONE_SPACE; + (*kd_dmvup)(from, to, count); + } + + to = nextline - delbytes; + count = delbytes / ONE_SPACE; + (*kd_dclear)(to, count, kd_attr); + return; + +} + + +/* + * kd_erase: + * + * This function overwrites characters with a space starting with the + * current cursor position and ending in number spaces away. + * + * input : number of characters to erase + * output : characters appear to be blanked or erased + * + */ +void +kd_erase(int number) +{ + csrpos_t i; + csrpos_t stop; + + stop = kd_curpos + (ONE_SPACE * number); + if (stop > BEG_OF_LINE(kd_curpos) + ONE_LINE) + stop = BEG_OF_LINE(kd_curpos) + ONE_LINE; + for (i = kd_curpos; i < stop; i += ONE_SPACE) { + (*kd_dput)(i, K_SPACE, kd_attr); + } + return; +} + + +/* + * kd_eraseln: + * + * This function erases the current line with spaces. + * + * input : None + * output : Current line is erased + * + */ +void +kd_eraseln(void) +{ + csrpos_t i; + csrpos_t stop; + + stop = BEG_OF_LINE(kd_curpos) + ONE_LINE; + for (i = BEG_OF_LINE(kd_curpos); i < stop; i += ONE_SPACE) { + (*kd_dput)(i, K_SPACE, kd_attr); + } + return; +} + + +/* + * kd_insch: + * + * This function inserts a blank at the current cursor position + * and moves all other characters on the line over. + * + * input : number of blanks to insert + * output : Blanks are inserted at cursor position + * + */ +void +kd_insch(int number) +{ + csrpos_t to; + csrpos_t from; + int count; + csrpos_t nextline; /* start of next line */ + int insbytes; /* num of bytes inserted */ + + if (number <= 0) + return; + + nextline = BEG_OF_LINE(kd_curpos) + ONE_LINE; + insbytes = number * ONE_SPACE; + if (kd_curpos + insbytes > nextline) + insbytes = nextline - kd_curpos; + + to = nextline - ONE_SPACE; + from = to - insbytes; + if (from >= kd_curpos) { + count = (from - kd_curpos + ONE_SPACE) / ONE_SPACE; + (*kd_dmvdown)(from, to, count); + } + + count = insbytes / ONE_SPACE; + (*kd_dclear)(kd_curpos, count, kd_attr); + return; +} + + +/* + * kd_isupper, kd_islower: + * + * Didn't want to include ctype.h because it brings in stdio.h, and + * only want to see if the darn character is uppercase or lowercase. + * + * input : Character 'c' + * output : isuuper gives TRUE if character is uppercase, islower + * returns TRUE if character is lowercase + * + */ +boolean_t +kd_isupper(u_char c) +{ + if (('A' <= c) && (c <= 'Z')) + return(TRUE); + return(FALSE); +} + +boolean_t +kd_islower(u_char c) +{ + if (('a' <= c) && (c <= 'z')) + return(TRUE); + return(FALSE); +} + +/* + * kd_senddata: + * + * This function sends a byte to the keyboard RDWR port, but + * first waits until the input/output data buffer is clear before + * sending the data. Note that this byte can be either data or a + * keyboard command. + * + */ +void +kd_senddata(unsigned char ch) +{ + while (inb(K_STATUS) & K_IBUF_FUL) + ; + outb(K_RDWR, ch); + last_sent = ch; + return; +} + +/* + * kd_sendcmd: + * + * This function sends a command byte to the keyboard command + * port, but first waits until the input/output data buffer is + * clear before sending the data. + * + */ +void +kd_sendcmd(unsigned char ch) +{ + while (inb(K_STATUS) & K_IBUF_FUL) + ; + outb(K_CMD, ch); + return; +} + + +/* + * kd_getdata: + * + * This function returns a data byte from the keyboard RDWR port, + * after waiting until the port is flagged as having something to + * read. + */ +unsigned char +kd_getdata(void) +{ + while ((inb(K_STATUS) & K_OBUF_FUL) == 0) + ; + return(inb(K_RDWR)); +} + +void +kd_cmdreg_write(int val) +{ +int ch=KC_CMD_WRITE; + + while (inb(K_STATUS) & K_IBUF_FUL) + ; + outb(K_CMD, ch); + + while (inb(K_STATUS) & K_IBUF_FUL) + ; + outb(K_RDWR, val); +} + +void +kd_mouse_drain(void) +{ + int i; + while(inb(K_STATUS) & K_IBUF_FUL) + ; + while((i = inb(K_STATUS)) & K_OBUF_FUL) + printf("kbd: S = %x D = %x\n", i, inb(K_RDWR)); +} + +/* + * set_kd_state: + * + * Set kd_state and update the keyboard status LEDs. + */ +void +set_kd_state(int newstate) +{ + kd_state = newstate; + kd_setleds1(state2leds(newstate)); +} + +/* + * state2leds: + * + * Return a byte containing LED settings for the keyboard, given + * a state vector. + */ +u_char +state2leds(int state) +{ + u_char result = 0; + + if (state & KS_NLKED) + result |= K_LED_NUMLK; + if (state & KS_CLKED) + result |= K_LED_CAPSLK; + return(result); +} + +/* + * kd_setleds[12]: + * + * Set the keyboard LEDs according to the given byte. + */ +void +kd_setleds1(u_char val) +{ + if (kd_ack != NOT_WAITING) { +#ifdef MACH_KBD + printf("kd_setleds1: unexpected state (%d)\n", kd_ack); +#endif + return; + } + + kd_ack = SET_LEDS; + kd_nextled = val; + kd_senddata(K_CMD_LEDS); +} + +void +kd_setleds2(void) +{ + kd_senddata(kd_nextled); +} + + +/* + * cnsetleds: + * + * like kd_setleds[12], but not interrupt-based. + * Currently disabled because cngetc ignores caps lock and num + * lock anyway. + */ +void +cnsetleds(u_char val) +{ + kd_senddata(K_CMD_LEDS); + (void)kd_getdata(); /* XXX - assume is ACK */ + kd_senddata(val); + (void)kd_getdata(); /* XXX - assume is ACK */ +} + +void +kdreboot(void) +{ + (*kd_dreset)(); + +#ifndef BROKEN_KEYBOARD_RESET + kd_sendcmd(0xFE); /* XXX - magic # */ + delay(1000000); /* wait to see if anything happens */ +#endif + /* + * If that didn't work, then we'll just have to try and + * do it the hard way. + */ + cpu_shutdown(); +} + +static int which_button[] = {0, MOUSE_LEFT, MOUSE_MIDDLE, MOUSE_RIGHT}; +static struct mouse_motion moved; + +int +kd_kbd_magic(int scancode) +{ +int new_button = 0; + + if (kd_kbd_mouse == 2) + printf("sc = %x\n", scancode); + + switch (scancode) { +/* f1 f2 f3 */ + case 0x3d: + new_button++; + case 0x3c: + new_button++; + case 0x3b: + new_button++; + if (kd_kbd_magic_button && (new_button != kd_kbd_magic_button)) { + /* down w/o up */ + mouse_button(which_button[kd_kbd_magic_button], 1); + } + /* normal */ + if (kd_kbd_magic_button == new_button) { + mouse_button(which_button[new_button], 1); + kd_kbd_magic_button = 0; + } else { + mouse_button(which_button[new_button], 0); + kd_kbd_magic_button = new_button; + } + break; + +/* right left up down */ + case 0x4d: + moved.mm_deltaX = kd_kbd_magic_scale; + moved.mm_deltaY = 0; + mouse_moved(moved); + break; + case 0x4b: + moved.mm_deltaX = -kd_kbd_magic_scale; + moved.mm_deltaY = 0; + mouse_moved(moved); + break; + case 0x48: + moved.mm_deltaX = 0; + moved.mm_deltaY = kd_kbd_magic_scale; + mouse_moved(moved); + break; + case 0x50: + moved.mm_deltaX = 0; + moved.mm_deltaY = -kd_kbd_magic_scale; + mouse_moved(moved); + break; +/* home pageup end pagedown */ + case 0x47: + moved.mm_deltaX = -2*kd_kbd_magic_scale; + moved.mm_deltaY = 2*kd_kbd_magic_scale; + mouse_moved(moved); + break; + case 0x49: + moved.mm_deltaX = 2*kd_kbd_magic_scale; + moved.mm_deltaY = 2*kd_kbd_magic_scale; + mouse_moved(moved); + break; + case 0x4f: + moved.mm_deltaX = -2*kd_kbd_magic_scale; + moved.mm_deltaY = -2*kd_kbd_magic_scale; + mouse_moved(moved); + break; + case 0x51: + moved.mm_deltaX = 2*kd_kbd_magic_scale; + moved.mm_deltaY = -2*kd_kbd_magic_scale; + mouse_moved(moved); + break; + + default: + return 0; + } + return 1; +} + + + +/* + * Code specific to EGA/CGA/VGA boards. This code relies on the fact + * that the "slam" functions take a word count and ONE_SPACE takes up + * 1 word. + */ +#define SLAMBPW 2 /* bytes per word for "slam" fcns */ + +/* + * xga_getpos: + * + * This function returns the current hardware cursor position on the + * screen, scaled for compatibility with kd_curpos. + * + * input : None + * output : returns the value of cursor position on screen + * + */ +static csrpos_t +xga_getpos(void) + +{ + unsigned char low; + unsigned char high; + short pos; + + outb(kd_index_reg, C_HIGH); + high = inb(kd_io_reg); + outb(kd_index_reg, C_LOW); + low = inb(kd_io_reg); + pos = (0xff&low) + ((unsigned short)high<<8); + + return(ONE_SPACE * (csrpos_t)pos); +} + + +/* + * kd_xga_init: + * + * Initialization specific to character-based graphics adapters. + */ +void +kd_xga_init(void) +{ + unsigned char start, stop; + +#if 0 + unsigned char screen; + + /* XXX: this conflicts with read/writing the RTC */ + + outb(CMOS_ADDR, CMOS_EB); + screen = inb(CMOS_DATA) & CM_SCRMSK; + switch(screen) { + default: + printf("kd: unknown screen type, defaulting to EGA\n"); + /* FALLTHROUGH */ + case CM_EGA_VGA: +#endif + /* + * Here we'll want to query to bios on the card + * itself, because then we can figure out what + * type we have exactly. At this point we only + * know that the card is NOT CGA or MONO. For + * now, however, we assume backwards compatibility + * with 0xb8000 as the starting screen offset + * memory location for these cards. + * + */ + + vid_start = (u_char *)phystokv(EGA_START); + kd_index_reg = EGA_IDX_REG; + kd_io_reg = EGA_IO_REG; + kd_lines = 25; + kd_cols = 80; + kd_bitmap_start = 0xa0000; /* XXX - magic numbers */ + { /* XXX - is there a cleaner way to do this? */ + char *addr = (char *)phystokv(kd_bitmap_start); + int i; + for (i = 0; i < 200; i++) + addr[i] = 0x00; + } +#if 0 + break; + /* XXX: some buggy BIOSes report these... */ + case CM_CGA_40: + vid_start = (u_char *)phystokv(CGA_START); + kd_index_reg = CGA_IDX_REG; + kd_io_reg = CGA_IO_REG; + kd_lines = 25; + kd_cols = 40; + break; + case CM_CGA_80: + vid_start = (u_char *)phystokv(CGA_START); + kd_index_reg = CGA_IDX_REG; + kd_io_reg = CGA_IO_REG; + kd_lines = 25; + kd_cols = 80; + break; + case CM_MONO_80: + vid_start = (u_char *)phystokv(MONO_START); + kd_index_reg = MONO_IDX_REG; + kd_io_reg = MONO_IO_REG; + kd_lines = 25; + kd_cols = 80; + break; + } +#endif + + outb(kd_index_reg, C_START); + start = inb(kd_io_reg); + /* Make sure cursor is enabled */ + start &= ~0x20; + outb(kd_io_reg, start); + outb(kd_index_reg, C_STOP); + stop = inb(kd_io_reg); + + if (!start && !stop) + { + /* Some firmware seem not to be initializing the cursor size + * any more... Try using standard values. */ + outb(kd_index_reg, C_START); + outb(kd_io_reg, 14); + outb(kd_index_reg, C_STOP); + outb(kd_io_reg, 15); + } + + kd_setpos(xga_getpos()); +} + + +/* + * charput: + * + * Put attributed character for EGA/CGA/etc. + */ +static void +charput(csrpos_t pos, char ch, char chattr) +{ + *(vid_start + pos) = ch; + *(vid_start + pos + 1) = chattr; +} + + +/* + * charsetcursor: + * + * Set hardware cursor position for EGA/CGA/etc. + */ +static void +charsetcursor(csrpos_t newpos) +{ + short curpos; /* position, not scaled for attribute byte */ + + curpos = newpos / ONE_SPACE; + outb(kd_index_reg, C_HIGH); + outb(kd_io_reg, (u_char)(curpos>>8)); + outb(kd_index_reg, C_LOW); + outb(kd_io_reg, (u_char)(curpos&0xff)); + + kd_curpos = newpos; +} + + +/* + * charmvup: + * + * Block move up for EGA/CGA/etc. + */ +static void +charmvup(csrpos_t from, csrpos_t to, int count) +{ + kd_slmscu(vid_start+from, vid_start+to, count); +} + + +/* + * charmvdown: + * + * Block move down for EGA/CGA/etc. + */ +static void +charmvdown(csrpos_t from, csrpos_t to, int count) +{ + kd_slmscd(vid_start+from, vid_start+to, count); +} + + +/* + * charclear: + * + * Fast clear for CGA/EGA/etc. + */ +static void +charclear(csrpos_t to, int count, char chattr) +{ + kd_slmwd(vid_start+to, count, ((unsigned short)chattr<<8)+K_SPACE); +} + + +/* + * kd_noopreset: + * + * No-op reset routine for kd_dreset. + */ +static void +kd_noopreset(void) +{ +} + + +/* + * bmpput: Copy a character from the font to the frame buffer. + */ + +void +bmpput( + csrpos_t pos, + char ch, + char chattr) +{ + short xbit, ybit; /* u/l corner of char pos */ + u_char *to, *from; + short i, j; + u_char mask = (chattr == KA_REVERSE ? 0xff : 0); + + if ((u_char)ch >= chars_in_font) + ch = K_QUES; + + bmpch2bit(pos, &xbit, &ybit); + to = bit2fbptr(xbit, ybit); + from = font_start + ch * char_byte_width; + for (i = 0; i < char_height; ++i) { + for (j = 0; j < char_byte_width; ++j) + *(to+j) = *(from+j) ^ mask; + to += fb_byte_width; + from += font_byte_width; + } +} + +/* + * bmpcp1char: copy 1 char from one place in the frame buffer to + * another. + */ +static void +bmpcp1char( + csrpos_t from, + csrpos_t to) +{ + short from_xbit, from_ybit; + short to_xbit, to_ybit; + u_char *tp, *fp; + short i, j; + + bmpch2bit(from, &from_xbit, &from_ybit); + bmpch2bit(to, &to_xbit, &to_ybit); + + tp = bit2fbptr(to_xbit, to_ybit); + fp = bit2fbptr(from_xbit, from_ybit); + + for (i = 0; i < char_height; ++i) { + for (j = 0; j < char_byte_width; ++j) + *(tp+j) = *(fp+j); + tp += fb_byte_width; + fp += fb_byte_width; + } +} + +/* + * bmpvmup: Copy a block of character positions upwards. + */ +void +bmpmvup( + csrpos_t from, + csrpos_t to, + int count) +{ + short from_xbit, from_ybit; + short to_xbit, to_ybit; + short i; + + bmpch2bit(from, &from_xbit, &from_ybit); + bmpch2bit(to, &to_xbit, &to_ybit); + + if (from_xbit == xstart && to_xbit == xstart && count%kd_cols == 0) { + /* fast case - entire lines */ + from_xbit = to_xbit = 0; + bmppaintcsr(kd_curpos, char_black); /* don't copy cursor */ + count /= kd_cols; /* num lines */ + count *= fb_byte_width * (char_height+cursor_height); + kd_slmscu(bit2fbptr(from_xbit, from_ybit), + bit2fbptr(to_xbit, to_ybit), + count/SLAMBPW); + bmppaintcsr(kd_curpos, char_white); + } else { + /* slow case - everything else */ + for (i=0; i < count; ++i) { + bmpcp1char(from, to); + from += ONE_SPACE; + to += ONE_SPACE; + } + } +} + +/* + * bmpmvdown: copy a block of characters down. + */ +void +bmpmvdown( + csrpos_t from, + csrpos_t to, + int count) +{ + short from_xbit, from_ybit; + short to_xbit, to_ybit; + short i; + + bmpch2bit(from, &from_xbit, &from_ybit); + bmpch2bit(to, &to_xbit, &to_ybit); + + if (from_xbit == xstart + (kd_cols - 1) * char_width + && to_xbit == xstart + (kd_cols - 1) * char_width + && count%kd_cols == 0) { + /* fast case - entire lines*/ + from_xbit = to_xbit = 8 * (fb_byte_width - 1); + /* last byte on line */ + bmppaintcsr(kd_curpos, char_black); /* don't copy cursor */ + count /= kd_cols; /* num lines */ + count *= fb_byte_width * (char_height+cursor_height); + kd_slmscd(bit2fbptr(from_xbit, from_ybit), + bit2fbptr(to_xbit, to_ybit), + count/SLAMBPW); + bmppaintcsr(kd_curpos, char_white); + } else { + /* slow case - everything else */ + for (i=0; i < count; ++i) { + bmpcp1char(from, to); + from -= ONE_SPACE; + to -= ONE_SPACE; + } + } +} + +/* + * bmpclear: clear one or more character positions. + */ +void +bmpclear( + csrpos_t to, /* 1st char */ + int count, /* num chars */ + char chattr) /* reverse or normal */ +{ + short i; + u_short clearval; + u_short clearbyte = (chattr == KA_REVERSE ? char_white : char_black); + + clearval = (u_short)(clearbyte<<8) + clearbyte; + if (to == 0 && count >= kd_lines * kd_cols) { + /* fast case - entire page */ + kd_slmwd(vid_start, (fb_byte_width * fb_height)/SLAMBPW, + clearval); + } else + /* slow case */ + for (i = 0; i < count; ++i) { + bmpput(to, K_SPACE, chattr); + to += ONE_SPACE; + } +} + +/* + * bmpsetcursor: update the display and set the logical cursor. + */ +void +bmpsetcursor(csrpos_t pos) +{ + /* erase old cursor & paint new one */ + bmppaintcsr(kd_curpos, char_black); + bmppaintcsr(pos, char_white); + kd_curpos = pos; +} + +/* + * bmppaintcsr: paint cursor bits. + */ +void +bmppaintcsr( + csrpos_t pos, + u_char val) +{ + short xbit, ybit; + u_char *cp; + short line, byte; + + bmpch2bit(pos, &xbit, &ybit); + ybit += char_height; /* position at bottom of line */ + cp = bit2fbptr(xbit, ybit); + for (line = 0; line < cursor_height; ++line) { + for (byte = 0; byte < char_byte_width; ++byte) + *(cp+byte) = val; + cp += fb_byte_width; + } +} + +/* + * bmpch2bit: convert character position to x and y bit addresses. + * (0, 0) is the upper left corner. + */ +void +bmpch2bit( + csrpos_t pos, + short *xb, + short *yb) /* x, y bit positions, u/l corner */ +{ + short xch, ych; + + xch = (pos / ONE_SPACE) % kd_cols; + ych = pos / (ONE_SPACE * kd_cols); + *xb = xstart + xch * char_width; + *yb = ystart + ych * (char_height + cursor_height); +} + +/* + * bit2fbptr: return a pointer into the frame buffer corresponding to + * the bit address (x, y). + * Assumes that xb and yb don't point to the middle of a + * byte. + */ +u_char * +bit2fbptr( + short xb, + short yb) +{ + return(vid_start + yb * fb_byte_width + xb/8); +} + + +/* + * console stuff + */ + +/* + * XXX we assume that pcs *always* have a console + */ +int +kdcnprobe(struct consdev *cp) +{ + int maj, unit, pri; + + maj = 0; + unit = 0; + pri = CN_INTERNAL; + + cp->cn_dev = makedev(maj, unit); + cp->cn_pri = pri; + return 0; +} + +int +kdcninit(struct consdev *cp) +{ + kdinit(); + return 0; +} + +int +kdcngetc(dev_t dev, int wait) +{ + if (wait) { + int c; + while ((c = kdcnmaygetc()) < 0) + continue; + return c; + } + else + return kdcnmaygetc(); +} + +int +kdcnputc(dev_t dev, int c) +{ + if (!kd_initialized) + return -1; + + /* Note that tab is handled in kd_putc */ + if (c == '\n') + kd_putc('\r'); + kd_putc_esc(c); + + return 0; +} + +/* + * kdcnmaygetc: + * + * Get one character using polling, rather than interrupts. Used + * by the kernel debugger. Note that Caps Lock is ignored. + * Normally this routine is called with interrupts already + * disabled, but there is code in place so that it will be more + * likely to work even if interrupts are turned on. + */ +int +kdcnmaygetc(void) +{ + unsigned char c; + unsigned char scancode; + unsigned int char_idx; +#ifdef notdef + spl_t o_pri; +#endif + boolean_t up; + + if (! kd_initialized) + return -1; + + kd_extended = FALSE; +#ifdef notdef + o_pri = splhi(); +#endif + for ( ; ; ) { + if (!(inb(K_STATUS) & K_OBUF_FUL)) + return -1; + + up = FALSE; + /* + * We'd come here for mouse events in debugger, if + * the mouse were on. + */ + if ((inb(K_STATUS) & 0x20) == 0x20) { + printf("M%xP", inb(K_RDWR)); + continue; + } + scancode = inb(K_RDWR); + /* + * Handle extend modifier and + * ack/resend, otherwise we may never receive + * a key. + */ + if (scancode == K_EXTEND) { + kd_extended = TRUE; + continue; + } else if (scancode == K_RESEND) { + printf("cngetc: resend"); + kd_resend(); + continue; + } else if (scancode == K_ACKSC) { + printf("cngetc: handle_ack"); + kd_handle_ack(); + continue; + } + if (scancode & K_UP) { + up = TRUE; + scancode &= ~K_UP; + } + if (kd_kbd_mouse) + kd_kbd_magic(scancode); + if (scancode < NUMKEYS) { + /* Lookup in map, then process. */ + char_idx = kdstate2idx(kd_state, kd_extended); + c = key_map[scancode][char_idx]; + if (c == K_SCAN) { + c = key_map[scancode][++char_idx]; + kd_state = do_modifier(kd_state, c, up); +#ifdef notdef + cnsetleds(state2leds(kd_state)); +#endif + } else if (! up + && c == K_ESC + && key_map[scancode][char_idx+1] == 0x5b) { + /* As a convenience for the nice + people using our debugger, remap + some keys to the readline-like + shortcuts supported by dde. + + XXX This is a workaround for the + limited kernel getchar interface. + It is only used by the debugger. */ + c = key_map[scancode][char_idx+2]; + switch (c) { +#define _MAP(A,B,C) (C) +#define MAP(T) _MAP(T) +#define CTRL(c) ((c) & 0x1f) + case MAP(K_HOME): c = CTRL('a'); break; + case MAP(K_UA): c = CTRL('p'); break; + case MAP(K_LA): c = CTRL('b'); break; + case MAP(K_RA): c = CTRL('f'); break; + case MAP(K_DA): c = CTRL('n'); break; + case MAP(K_END): c = CTRL('e'); break; + /* delete */ + case 0x39: c = CTRL('d'); break; +#undef CTRL +#undef MAP +#undef _MAP + default: + /* Retain the old behavior. */ + c = K_ESC; + } + + return(c); + } else if (!up) { + /* regular key-down */ + if (c == K_CR) + c = K_LF; +#ifdef notdef + splx(o_pri); +#endif + return(c & 0177); + } + } + } +} diff --git a/i386/i386at/kd.h b/i386/i386at/kd.h new file mode 100644 index 0000000..5bfabce --- /dev/null +++ b/i386/i386at/kd.h @@ -0,0 +1,744 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* ********************************************************************** + File: kd.h + Description: definitions for AT keyboard/display driver + Authors: Eugene Kuerner, Adrienne Jardetzky, Mike Kupfer + + $ Header: $ + + Copyright Ing. C. Olivetti & C. S.p.A. 1988, 1989. + All rights reserved. +********************************************************************** */ +/* + Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc., +Cupertino, California. + + All Rights Reserved + + Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appears in all +copies and that both the copyright notice and this permission notice +appear in supporting documentation, and that the name of Olivetti +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + + OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, +NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/* + * This file contains defines and structures that implement hardware + * keyboard mapping into ansi defined output codes. Note that this + * is structured so that "re-mapping" of actual keys is allowed at + * anytime during execution of the console driver. And each scan code + * is potentially expanded into NUMKEYS characters. Which is programmable + * at runtime or whenever. + * + * 02 Nov 1988 orc!eugene + * + */ + +#ifndef _KD_H_ +#define _KD_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Where memory for various graphics adapters starts. + */ +#define EGA_START 0x0b8000 +#define CGA_START 0x0b8000 +#define MONO_START 0x0b0000 + +/* + * Common I/O ports. + */ +#define K_TMR0 0x40 /* timer 0, 1, or 2 value (r/w) */ +#define K_TMR1 0x41 +#define K_TMR2 0x42 +#define K_TMRCTL 0x43 /* timer control (write-only) */ +#define K_RDWR 0x60 /* keyboard data & cmds (read/write) */ +#define K_PORTB 0x61 /* r/w. speaker & status lines */ +#define K_STATUS 0x64 /* keybd status (read-only) */ +#define K_CMD 0x64 /* keybd ctlr command (write-only) */ + +/* + * I/O ports for various graphics adapters. + */ +#define EGA_IDX_REG 0x3d4 +#define EGA_IO_REG 0x3d5 +#define CGA_IDX_REG 0x3d4 +#define CGA_IO_REG 0x3d5 +#define MONO_IDX_REG 0x3b4 +#define MONO_IO_REG 0x3b5 + +/* + * Commands sent to graphics adapter. + */ +#define C_START 0x0a /* return cursor line start */ +#define C_STOP 0x0b /* return cursor line stop */ +#define C_LOW 0x0f /* return low byte of cursor addr */ +#define C_HIGH 0x0e /* high byte */ + +/* + * Bit definitions for K_STATUS port. + */ +#define K_OBUF_FUL 0x01 /* output (from keybd) buffer full */ +#define K_IBUF_FUL 0x02 /* input (to keybd) buffer full */ +#define K_SYSFLAG 0x04 /* "System Flag" */ +#define K_CMD_DATA 0x08 /* 1 = input buf has cmd, 0 = data */ +#define K_KBD_INHBT 0x10 /* 0 if keyboard inhibited */ + +/* + * Keyboard controller commands (sent to K_CMD port). + */ +#define KC_CMD_READ 0x20 /* read controller command byte */ +#define KC_CMD_WRITE 0x60 /* write controller command byte */ +#define KC_CMD_TEST 0xab /* test interface */ +#define KC_CMD_DUMP 0xac /* diagnostic dump */ +#define KC_CMD_DISBLE 0xad /* disable keyboard */ +#define KC_CMD_ENBLE 0xae /* enable keyboard */ +#define KC_CMD_RDKBD 0xc4 /* read keyboard ID */ +#define KC_CMD_ECHO 0xee /* used for diagnostic testing */ + +/* + * Keyboard commands (send to K_RDWR). + */ +#define K_CMD_LEDS 0xed /* set status LEDs (caps lock, etc.) */ + +/* + * Bit definitions for controller command byte (sent following + * K_CMD_WRITE command). + */ +#define K_CB_ENBLIRQ 0x01 /* enable data-ready intrpt */ +#define K_CB_SETSYSF 0x04 /* Set System Flag */ +#define K_CB_INHBOVR 0x08 /* Inhibit Override */ +#define K_CB_DISBLE 0x10 /* disable keyboard */ + +/* + * Bit definitions for "Indicator Status Byte" (sent after a + * K_CMD_LEDS command). If the bit is on, the LED is on. Undefined + * bit positions must be 0. + */ +#define K_LED_SCRLLK 0x1 /* scroll lock */ +#define K_LED_NUMLK 0x2 /* num lock */ +#define K_LED_CAPSLK 0x4 /* caps lock */ + +/* + * Bit definitions for "Miscellaneous port B" (K_PORTB). + */ +/* read/write */ +#define K_ENABLETMR2 0x01 /* enable output from timer 2 */ +#define K_SPKRDATA 0x02 /* direct input to speaker */ +#define K_ENABLEPRTB 0x04 /* "enable" port B */ +#define K_EIOPRTB 0x08 /* enable NMI on parity error */ +/* read-only */ +#define K_REFRESHB 0x10 /* refresh flag from INLTCONT PAL */ +#define K_OUT2B 0x20 /* timer 2 output */ +#define K_ICKB 0x40 /* I/O channel check (parity error) */ + +/* + * Bit definitions for timer control port (K_TMRCTL). + */ +/* select timer 0, 1, or 2. Don't mess with 0 or 1. */ +#define K_SELTMRMASK 0xc0 +#define K_SELTMR0 0x00 +#define K_SELTMR1 0x40 +#define K_SELTMR2 0x80 + +/* read/load control */ +#define K_RDLDTMRMASK 0x30 +#define K_HOLDTMR 0x00 /* freeze timer until read */ +#define K_RDLDTLSB 0x10 /* read/load LSB */ +#define K_RDLDTMSB 0x20 /* read/load MSB */ +#define K_RDLDTWORD 0x30 /* read/load LSB then MSB */ + +/* mode control */ +#define K_TMDCTLMASK 0x0e +#define K_TCOUNTINTR 0x00 /* "Term Count Intr" */ +#define K_TONESHOT 0x02 /* "Progr One-Shot" */ +#define K_TRATEGEN 0x04 /* "Rate Gen (/n)" */ +#define K_TSQRWAVE 0x06 /* "Sqr Wave Gen" */ +#define K_TSOFTSTRB 0x08 /* "Softw Trig Strob" */ +#define K_THARDSTRB 0x0a /* "Hardw Trig Strob" */ + +/* count mode */ +#define K_TCNTMDMASK 0x01 +#define K_TBINARY 0x00 /* 16-bit binary counter */ +#define K_TBCD 0x01 /* 4-decade BCD counter */ + + + +/* + * Fun definitions for displayed characters and characters read from + * the keyboard. + */ + +/* + * Attributes for character sent to display. + */ +#define KA_NORMAL 0x07 +#define KA_REVERSE 0x70 + +#define KAX_REVERSE 0x01 +#define KAX_UNDERLINE 0x02 +#define KAX_BLINK 0x04 +#define KAX_BOLD 0x08 +#define KAX_DIM 0x10 +#define KAX_INVISIBLE 0x20 + +#define KAX_COL_UNDERLINE 0x0f /* bright white */ +#define KAX_COL_DIM 0x08 /* gray */ + +/* + * For an EGA-like display, each character takes two bytes, one for the + * actual character, followed by one for its attributes. + * Be very careful if you change ONE_SPACE, as these constants are also used + * to define the device-independent display implemented by kd.c. + * (See kdsoft.h for more details on the device-independent display.) + */ +#define ONE_SPACE 2 /* bytes in 1 char, EGA-like display */ +#define BOTTOM_LINE 3840 /* 1st byte in last line of display */ +#define ONE_PAGE 4000 /* number of bytes in page */ +#define ONE_LINE 160 /* number of bytes in line */ + +#define BEG_OF_LINE(pos) ((pos) - (pos)%ONE_LINE) +#define CURRENT_COLUMN(pos) (((pos) % ONE_LINE) / ONE_SPACE) + +#define NUMKEYS 89 +#define NUMSTATES 5 /* NORM_STATE, ... */ +#define NUMOUTPUT 3 /* max size of byte seq from key */ +#define WIDTH_KMAP (NUMSTATES * NUMOUTPUT) + +/* + * Keyboard states. Used for KDGKBENT, KDSKBENT ioctl's. If you + * change these values, you should also rearrange the entries in + * key_map. + */ +/* "state indices" (for computing key_map index) */ +#define NORM_STATE 0 +#define SHIFT_STATE 1 +#define CTRL_STATE 2 +#define ALT_STATE 3 +#define SHIFT_ALT 4 +/* macro to convert from state index to actual key_map index */ +#define CHARIDX(sidx) ((sidx) * NUMOUTPUT) + /* where sidx is in [NORM_STATE ... SHIFT_ALT] */ + +/* "state bits" for kd_state vector */ +#define KS_NORMAL 0x00 +#define KS_SLKED 0x01 +#define KS_NLKED 0x02 +#define KS_CLKED 0x04 +#define KS_ALTED 0x08 +#define KS_SHIFTED 0x10 +#define KS_CTLED 0x20 + + +/* special codes */ +#define K_UP 0x80 /* OR'd in if key below is released */ +#define K_EXTEND 0xe0 /* marker for "extended" sequence */ +#define K_ACKSC 0xfa /* ack for keyboard command */ +#define K_RESEND 0xfe /* request to resend keybd cmd */ + +/* modifier keys */ +#define K_CTLSC 0x1d /* control down */ +#define K_LSHSC 0x2a /* left shift down */ +#define K_RSHSC 0x36 /* right shift down */ +#define K_ALTSC 0x38 /* alt key down */ +#define K_CLCKSC 0x3a /* caps lock */ +#define K_NLCKSC 0x45 /* num lock down */ + +/* "special keys" */ +#define K_BSSC 0x0e /* backspace */ +#define K_TABSC 0x0f /* tab */ +#define K_RETSC 0x1c /* return */ +#define K_SPSC 0x39 /* space */ +#define K_ESCSC 0x01 /* ESC */ + +/* alphabetic keys */ +#define K_qSC 0x10 +#define K_wSC 0x11 +#define K_eSC 0x12 +#define K_rSC 0x13 +#define K_tSC 0x14 +#define K_ySC 0x15 +#define K_uSC 0x16 +#define K_iSC 0x17 +#define K_oSC 0x18 +#define K_pSC 0x19 + +#define K_aSC 0x1e +#define K_sSC 0x1f +#define K_dSC 0x20 +#define K_fSC 0x21 +#define K_gSC 0x22 +#define K_hSC 0x23 +#define K_jSC 0x24 +#define K_kSC 0x25 +#define K_lSC 0x26 + +#define K_zSC 0x2c +#define K_xSC 0x2d +#define K_cSC 0x2e +#define K_vSC 0x2f +#define K_bSC 0x30 +#define K_nSC 0x31 +#define K_mSC 0x32 + +/* numbers and punctuation */ +#define K_ONESC 0x02 /* 1 */ +#define K_TWOSC 0x03 /* 2 */ +#define K_THREESC 0x04 /* 3 */ +#define K_FOURSC 0x05 /* 4 */ +#define K_FIVESC 0x06 /* 5 */ +#define K_SIXSC 0x07 /* 6 */ +#define K_SEVENSC 0x08 /* 7 */ +#define K_EIGHTSC 0x09 /* 8 */ +#define K_NINESC 0x0a /* 9 */ +#define K_ZEROSC 0x0b /* 0 */ + +#define K_MINUSSC 0x0c /* - */ +#define K_EQLSC 0x0d /* = */ +#define K_LBRKTSC 0x1a /* [ */ +#define K_RBRKTSC 0x1b /* ] */ +#define K_SEMISC 0x27 /* ; */ +#define K_SQUOTESC 0x28 /* ' */ +#define K_GRAVSC 0x29 /* ` */ +#define K_BSLSHSC 0x2b /* \ */ +#define K_COMMASC 0x33 /* , */ +#define K_PERIODSC 0x34 /* . */ +#define K_SLASHSC 0x35 /* / */ + +/* keypad keys */ +#define K_HOMESC 0x47 /* scancode for home */ +#define K_DELSC 0x53 /* scancode for del */ + +/* + * Ascii values and flag characters for key map. + * A function key is represented by the 3-byte char sequence that it + * corresponds to. + * Other mappable non-Ascii keys (e.g., "ctrl") are represented by a + * two-byte sequence: K_SCAN, followed by the key's scan code. + */ +#define K_DONE 0xffu /* must be same as NC */ +#define NC 0xffu /* No character defined */ + +#define K_SCAN 0xfeu /* followed by scan code */ + +/* ascii char set */ +#define K_NUL 0x00 /* Null character */ +#define K_SOH 0x01 +#define K_STX 0x02 +#define K_ETX 0x03 +#define K_EOT 0x04 +#define K_ENQ 0x05 +#define K_ACK 0x06 +#define K_BEL 0x07 /* bell character */ +#define K_BS 0x08 /* back space */ +#define K_HT 0x09 +#define K_LF 0x0a /* line feed */ +#define K_VT 0x0b +#define K_FF 0x0c +#define K_CR 0x0d /* carriage return */ +#define K_SO 0x0e +#define K_SI 0x0f +#define K_DLE 0x10 +#define K_DC1 0x11 +#define K_DC2 0x12 +#define K_DC3 0x13 +#define K_DC4 0x14 +#define K_NAK 0x15 +#define K_SYN 0x16 +#define K_ETB 0x17 +#define K_CAN 0x18 +#define K_EM 0x19 +#define K_SUB 0x1a +#define K_ESC 0x1b /* escape character */ +#define K_FS 0x1c +#define K_GS 0x1d +#define K_RS 0x1e +#define K_US 0x1f +#define K_SPACE 0x20 /* space character */ +#define K_BANG 0x21 /* ! */ +#define K_DQUOTE 0x22 /* " */ +#define K_POUND 0x23 /* # */ +#define K_DOLLAR 0x24 /* $ */ +#define K_PERC 0x25 /* % */ +#define K_AMPER 0x26 /* & */ +#define K_SQUOTE 0x27 /* ' */ +#define K_LPAREN 0x28 /* ( */ +#define K_RPAREN 0x29 /* ) */ +#define K_ASTER 0x2a /* * */ +#define K_PLUS 0x2b /* + */ +#define K_COMMA 0x2c /* , */ +#define K_MINUS 0x2d /* - */ +#define K_PERIOD 0x2e /* . */ +#define K_SLASH 0x2f /* / */ +#define K_ZERO 0x30 /* 0 */ +#define K_ONE 0x31 /* 1 */ +#define K_TWO 0x32 /* 2 */ +#define K_THREE 0x33 /* 3 */ +#define K_FOUR 0x34 /* 4 */ +#define K_FIVE 0x35 /* 5 */ +#define K_SIX 0x36 /* 6 */ +#define K_SEVEN 0x37 /* 7 */ +#define K_EIGHT 0x38 /* 8 */ +#define K_NINE 0x39 /* 9 */ +#define K_COLON 0x3a /* : */ +#define K_SEMI 0x3b /* ; */ +#define K_LTHN 0x3c /* < */ +#define K_EQL 0x3d /* = */ +#define K_GTHN 0x3e /* > */ +#define K_QUES 0x3f /* ? */ +#define K_ATSN 0x40 /* @ */ +#define K_A 0x41 /* A */ +#define K_B 0x42 /* B */ +#define K_C 0x43 /* C */ +#define K_D 0x44 /* D */ +#define K_E 0x45 /* E */ +#define K_F 0x46 /* F */ +#define K_G 0x47 /* G */ +#define K_H 0x48 /* H */ +#define K_I 0x49 /* I */ +#define K_J 0x4a /* J */ +#define K_K 0x4b /* K */ +#define K_L 0x4c /* L */ +#define K_M 0x4d /* M */ +#define K_N 0x4e /* N */ +#define K_O 0x4f /* O */ +#define K_P 0x50 /* P */ +#define K_Q 0x51 /* Q */ +#define K_R 0x52 /* R */ +#define K_S 0x53 /* S */ +#define K_T 0x54 /* T */ +#define K_U 0x55 /* U */ +#define K_V 0x56 /* V */ +#define K_W 0x57 /* W */ +#define K_X 0x58 /* X */ +#define K_Y 0x59 /* Y */ +#define K_Z 0x5a /* Z */ +#define K_LBRKT 0x5b /* [ */ +#define K_BSLSH 0x5c /* \ */ +#define K_RBRKT 0x5d /* ] */ +#define K_CARET 0x5e /* ^ */ +#define K_UNDSC 0x5f /* _ */ +#define K_GRAV 0x60 /* ` */ +#define K_a 0x61 /* a */ +#define K_b 0x62 /* b */ +#define K_c 0x63 /* c */ +#define K_d 0x64 /* d */ +#define K_e 0x65 /* e */ +#define K_f 0x66 /* f */ +#define K_g 0x67 /* g */ +#define K_h 0x68 /* h */ +#define K_i 0x69 /* i */ +#define K_j 0x6a /* j */ +#define K_k 0x6b /* k */ +#define K_l 0x6c /* l */ +#define K_m 0x6d /* m */ +#define K_n 0x6e /* n */ +#define K_o 0x6f /* o */ +#define K_p 0x70 /* p */ +#define K_q 0x71 /* q */ +#define K_r 0x72 /* r */ +#define K_s 0x73 /* s */ +#define K_t 0x74 /* t */ +#define K_u 0x75 /* u */ +#define K_v 0x76 /* v */ +#define K_w 0x77 /* w */ +#define K_x 0x78 /* x */ +#define K_y 0x79 /* y */ +#define K_z 0x7a /* z */ +#define K_LBRACE 0x7b /* { */ +#define K_PIPE 0x7c /* | */ +#define K_RBRACE 0x7d /* } */ +#define K_TILDE 0x7e /* ~ */ +#define K_DEL 0x7f /* delete */ + +/* Ascii sequences to be generated by the named key */ +#define K_F1 0x1b,0x4f,0x50 +#define K_F1S 0x1b,0x4f,0x70 +#define K_F2 0x1b,0x4f,0x51 +#define K_F2S 0x1b,0x4f,0x71 +#define K_F3 0x1b,0x4f,0x52 +#define K_F3S 0x1b,0x4f,0x72 +#define K_F4 0x1b,0x4f,0x53 +#define K_F4S 0x1b,0x4f,0x73 +#define K_F5 0x1b,0x4f,0x54 +#define K_F5S 0x1b,0x4f,0x74 +#define K_F6 0x1b,0x4f,0x55 +#define K_F6S 0x1b,0x4f,0x75 +#define K_F7 0x1b,0x4f,0x56 +#define K_F7S 0x1b,0x4f,0x76 +#define K_F8 0x1b,0x4f,0x57 +#define K_F8S 0x1b,0x4f,0x77 +#define K_F9 0x1b,0x4f,0x58 +#define K_F9S 0x1b,0x4f,0x78 +#define K_F10 0x1b,0x4f,0x59 +#define K_F10S 0x1b,0x4f,0x79 +#define K_F11 0x1b,0x4f,0x5a +#define K_F11S 0x1b,0x4f,0x7a +#define K_F12 0x1b,0x4f,0x41 +#define K_F12S 0x1b,0x4f,0x61 + +/* These are the Alt-FxxA #defines. They work with the new keymap + -- Derek Upham 1997/06/25 */ +#define K_F1A 0x1b,0x4f,0x30 +#define K_F2A 0x1b,0x4f,0x31 +#define K_F3A 0x1b,0x4f,0x32 +#define K_F4A 0x1b,0x4f,0x33 +#define K_F5A 0x1b,0x4f,0x34 +#define K_F6A 0x1b,0x4f,0x35 +#define K_F7A 0x1b,0x4f,0x36 +#define K_F8A 0x1b,0x4f,0x37 +#define K_F9A 0x1b,0x4f,0x38 +#define K_F10A 0x1b,0x4f,0x39 +#define K_F11A 0x1b,0x4f,0x3a +#define K_F12A 0x1b,0x4f,0x3b + +#define K_SCRL 0x1b,0x5b,0x4d +#define K_HOME 0x1b,0x5b,0x48 +#define K_UA 0x1b,0x5b,0x41 +#define K_PUP 0x1b,0x5b,0x56 +#define K_LA 0x1b,0x5b,0x44 +#define K_RA 0x1b,0x5b,0x43 +#define K_END 0x1b,0x5b,0x59 +#define K_DA 0x1b,0x5b,0x42 +#define K_PDN 0x1b,0x5b,0x55 +#define K_INS 0x1b,0x5b,0x40 + +#define KBD_IRQ 1 + +/* + * This array maps scancodes to Ascii characters (or character + * sequences). + * The first index is the scancode. The first NUMOUTPUT characters + * (accessed using the second index) correspond to the key's char + * sequence for the Normal state. The next NUMOUTPUT characters + * are for the Shift state, then Ctrl, then Alt, then Shift/Alt. + */ +#ifdef KERNEL +extern u_char key_map[NUMKEYS][WIDTH_KMAP]; +#endif /* KERNEL */ + + + +/* + * These routines are declared here so that all the modules making + * up the kd driver agree on how to do locking. + */ + +#ifdef KERNEL +#include +#define SPLKD spltty +#endif /* KERNEL */ + + +/* + * Ioctl's on /dev/console. + */ + +/* + * KDGKBENT, KDSKBENT - Get and set keyboard table entry. Useful for + * remapping keys. + * + * KDGSTATE - Get the keyboard state variable, which flags the + * modifier keys (shift, ctrl, etc.) that are down. See + * KS_NORMAL et al above. Used for debugging. + * + * KDSETBELL - Turns the bell on or off. + */ + +#define KDGKBENT _IOWR('k', 1, struct kbentry) /* get keybd entry */ + +#define KDSKBENT _IOW('k', 2, struct kbentry) /* set keybd entry */ + +#define KDGSTATE _IOR('k', 3, int) /* get keybd state */ + +#define KDSETBELL _IOW('k', 4, int) /* turn bell on or off */ +# define KD_BELLON 1 +# define KD_BELLOFF 0 + +/* + * This struct is used for getting and setting key definitions. The + * values for kb_index are obtainable from the man page for + * keyboard(7) (though they should really be defined here!). + */ +struct kbentry { + u_char kb_state; /* which state to use */ + u_char kb_index; /* which keycode */ + u_char kb_value[NUMOUTPUT]; /* value to get/set */ +}; + + +/* + * Ioctl's on /dev/kbd. + */ + +#ifdef KERNEL +extern int kb_mode; +#endif + +struct X_kdb { + u_int *ptr; + u_int size; +}; + +#define K_X_KDB_ENTER _IOW('K', 16, struct X_kdb) +#define K_X_KDB_EXIT _IOW('K', 17, struct X_kdb) + +#define K_X_IN 0x01000000 +#define K_X_OUT 0x02000000 +#define K_X_BYTE 0x00010000 +#define K_X_WORD 0x00020000 +#define K_X_LONG 0x00040000 +#define K_X_TYPE 0x03070000 +#define K_X_PORT 0x0000ffff + +extern boolean_t kd_isupper (u_char); +extern boolean_t kd_islower (u_char); +extern void kd_senddata (unsigned char); +extern void kd_sendcmd (unsigned char); +extern void kd_cmdreg_write (int); +extern void kd_mouse_drain (void); +extern void set_kd_state (int); +extern void kd_setleds1 (u_char); +extern void kd_setleds2 (void); +extern void cnsetleds (u_char); +extern void kdreboot (void); +extern void kd_putc_esc (u_char); +extern void kd_putc (u_char); +extern void kd_parseesc (void); +extern void kd_down (void); +extern void kd_up (void); +extern void kd_cr (void); +extern void kd_tab (void); +extern void kd_left (void); +extern void kd_right (void); +extern void kd_scrollup (void); +extern void kd_scrolldn (void); +extern void kd_cls (void); +extern void kd_home (void); +extern void kd_insch (int number); +extern void kd_cltobcur (void); +extern void kd_cltopcur (void); +extern void kd_cltoecur (void); +extern void kd_clfrbcur (void); +extern void kd_eraseln (void); +extern void kd_insln (int); +extern void kd_delln (int); +extern void kd_delch (int); +extern void kd_erase (int); +extern void kd_bellon (void); +extern void kd_belloff (void *param); +extern void kdinit (void); +extern int kdsetkbent (struct kbentry *, int); +extern int kdgetkbent (struct kbentry *); +extern int kdsetbell (int, int); +extern void kd_resend (void); +extern void kd_handle_ack (void); +extern int kd_kbd_magic (int); +extern unsigned int kdstate2idx (unsigned int, boolean_t); +extern void kd_parserest (u_char *); +extern int kdcnprobe(struct consdev *cp); +extern int kdcninit(struct consdev *cp); +extern int kdcngetc(dev_t dev, int wait); +extern int kdcnmaygetc (void); +extern int kdcnputc(dev_t dev, int c); +extern void kd_setpos(csrpos_t newpos); + +extern void kd_slmwd (void *start, int count, int value); +extern void kd_slmscu (void *from, void *to, int count); +extern void kd_slmscd (void *from, void *to, int count); + +extern void kdintr(int vec); + +#if MACH_KDB +#include +#endif /* MACH_KDB */ + +extern int kdopen(dev_t dev, int flag, io_req_t ior); +extern void kdclose(dev_t dev, int flag); +extern int kdread(dev_t dev, io_req_t uio); +extern int kdwrite(dev_t dev, io_req_t uio); + +extern io_return_t kdgetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, + mach_msg_type_number_t *count); + +extern io_return_t kdsetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, + mach_msg_type_number_t count); + +extern int kdportdeath(dev_t dev, mach_port_t port); +extern vm_offset_t kdmmap(dev_t dev, vm_offset_t off, vm_prot_t prot); + +boolean_t kdcheckmagic(Scancode scancode); + +int do_modifier(int state, Scancode c, boolean_t up); + +/* + * Generic routines for bitmap devices (i.e., assume no hardware + * assist). Assumes a simple byte ordering (i.e., a byte at a lower + * address is to the left of the byte at the next higher address). + * For the 82786, this works anyway if the characters are 2 bytes + * wide. (more bubble gum and paper clips.) + * + * See the comments above (in i386at/kd.c) about SLAMBPW. + */ +void bmpch2bit(csrpos_t pos, short *xb, short *yb); +void bmppaintcsr(csrpos_t pos, u_char val); +u_char *bit2fbptr(short xb, short yb); + +unsigned char kd_getdata(void); +unsigned char state2leds(int state); + +void kdstart(struct tty *tp); +void kdstop(struct tty *tp, int flags); + +void kd_xga_init(void); + +#endif /* _KD_H_ */ diff --git a/i386/i386at/kd_event.c b/i386/i386at/kd_event.c new file mode 100644 index 0000000..247d95b --- /dev/null +++ b/i386/i386at/kd_event.c @@ -0,0 +1,392 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* ********************************************************************** + File: kd_event.c + Description: Driver for event interface to keyboard. + + $ Header: $ + + Copyright Ing. C. Olivetti & C. S.p.A. 1989. All rights reserved. +********************************************************************** */ +/* + Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc., +Cupertino, California. + + All Rights Reserved + + Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appears in all +copies and that both the copyright notice and this permission notice +appear in supporting documentation, and that the name of Olivetti +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + + OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, +NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#ifdef APIC +# include +#else +# include +#endif + +#include "kd_event.h" + +/* + * Code for /dev/kbd. The interrupt processing is done in kd.c, + * which calls into this module to enqueue scancode events when + * the keyboard is in Event mode. + */ + +/* + * Note: These globals are protected by raising the interrupt level + * via SPLKD. + */ + +kd_event_queue kbd_queue; /* queue of keyboard events */ +queue_head_t kbd_read_queue = { &kbd_read_queue, &kbd_read_queue }; + +static boolean_t initialized = FALSE; + + +/* + * kbdinit - set up event queue. + */ + +static void +kbdinit(void) +{ + spl_t s = SPLKD(); + + if (!initialized) { + kdq_reset(&kbd_queue); + initialized = TRUE; + } + splx(s); +} + + +/* + * kbdopen - Verify that open is read-only and remember process + * group leader. + */ + +/*ARGSUSED*/ +int +kbdopen(dev_t dev, int flags, io_req_t ior) +{ + spl_t o_pri = spltty(); + kdinit(); + splx(o_pri); + kbdinit(); + + return(0); +} + + +/* + * kbdclose - Make sure that the kd driver is in Ascii mode and + * reset various flags. + */ + +/*ARGSUSED*/ +void +kbdclose( + dev_t dev, + int flags) +{ + spl_t s = SPLKD(); + + kb_mode = KB_ASCII; + kdq_reset(&kbd_queue); + splx(s); +} + + +io_return_t kbdgetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, /* pointer to OUT array */ + mach_msg_type_number_t *count) /* OUT */ +{ + switch (flavor) { + case KDGKBDTYPE: + *data = KB_VANILLAKB; + *count = 1; + break; + case DEV_GET_SIZE: + data[DEV_GET_SIZE_DEVICE_SIZE] = 0; + data[DEV_GET_SIZE_RECORD_SIZE] = sizeof(kd_event); + *count = DEV_GET_SIZE_COUNT; + break; + default: + return (D_INVALID_OPERATION); + } + return (D_SUCCESS); +} + +io_return_t kbdsetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, + mach_msg_type_number_t count) +{ + switch (flavor) { + case KDSKBDMODE: + kb_mode = *data; + /* XXX - what to do about unread events? */ + /* XXX - should check that 'data' contains an OK valud */ + break; + case KDSETLEDS: + if (count != 1) + return (D_INVALID_OPERATION); + kd_setleds1 (*data); + break; + case K_X_KDB_ENTER: + return X_kdb_enter_init((unsigned int *)data, count); + case K_X_KDB_EXIT: + return X_kdb_exit_init((unsigned int *)data, count); + default: + return (D_INVALID_OPERATION); + } + return (D_SUCCESS); +} + + + +/* + * kbdread - dequeue and return any queued events. + */ +int +kbdread( + dev_t dev, + io_req_t ior) +{ + int err, count; + spl_t s; + + /* Check if IO_COUNT is a multiple of the record size. */ + if (ior->io_count % sizeof(kd_event) != 0) + return D_INVALID_SIZE; + + err = device_read_alloc(ior, (vm_size_t)ior->io_count); + if (err != KERN_SUCCESS) + return (err); + + s = SPLKD(); + if (kdq_empty(&kbd_queue)) { + if (ior->io_mode & D_NOWAIT) { + splx(s); + return (D_WOULD_BLOCK); + } + ior->io_done = kbd_read_done; + enqueue_tail(&kbd_read_queue, (queue_entry_t) ior); + splx(s); + return (D_IO_QUEUED); + } + count = 0; + while (!kdq_empty(&kbd_queue) && count < ior->io_count) { + kd_event *ev; + + ev = kdq_get(&kbd_queue); + *(kd_event *)(&ior->io_data[count]) = *ev; + count += sizeof(kd_event); + } + splx(s); + ior->io_residual = ior->io_count - count; + return (D_SUCCESS); +} + +boolean_t kbd_read_done(io_req_t ior) +{ + int count; + spl_t s; + + s = SPLKD(); + if (kdq_empty(&kbd_queue)) { + ior->io_done = kbd_read_done; + enqueue_tail(&kbd_read_queue, (queue_entry_t)ior); + splx(s); + return (FALSE); + } + + count = 0; + while (!kdq_empty(&kbd_queue) && count < ior->io_count) { + kd_event *ev; + + ev = kdq_get(&kbd_queue); + *(kd_event *)(&ior->io_data[count]) = *ev; + count += sizeof(kd_event); + } + splx(s); + + ior->io_residual = ior->io_count - count; + ds_read_done(ior); + + return (TRUE); +} + + + +/* + * kd_enqsc - enqueue a scancode. Should be called at SPLKD. + */ + +void +kd_enqsc(Scancode sc) +{ + kd_event ev; + + ev.type = KEYBD_EVENT; + /* Not used but we set it to avoid garbage */ + ev.unused_time.seconds = 0; + ev.unused_time.microseconds = 0; + ev.value.sc = sc; + kbd_enqueue(&ev); +} + + +/* + * kbd_enqueue - enqueue an event and wake up selecting processes, if + * any. Should be called at SPLKD. + */ + +void +kbd_enqueue(kd_event *ev) +{ + if (kdq_full(&kbd_queue)) + printf_once("kbd: queue full\n"); + else + kdq_put(&kbd_queue, ev); + + { + io_req_t ior; + while ((ior = (io_req_t)dequeue_head(&kbd_read_queue)) != 0) + iodone(ior); + } +} + +u_int X_kdb_enter_str[512], X_kdb_exit_str[512]; +int X_kdb_enter_len = 0, X_kdb_exit_len = 0; + +static void +kdb_in_out(const u_int *p) +{ + int t = p[0]; + + switch (t & K_X_TYPE) { + case K_X_IN|K_X_BYTE: + inb(t & K_X_PORT); + break; + + case K_X_IN|K_X_WORD: + inw(t & K_X_PORT); + break; + + case K_X_IN|K_X_LONG: + inl(t & K_X_PORT); + break; + + case K_X_OUT|K_X_BYTE: + outb(t & K_X_PORT, p[1]); + break; + + case K_X_OUT|K_X_WORD: + outw(t & K_X_PORT, p[1]); + break; + + case K_X_OUT|K_X_LONG: + outl(t & K_X_PORT, p[1]); + break; + } +} + +void +X_kdb_enter(void) +{ + u_int *u_ip, *endp; + + for (u_ip = X_kdb_enter_str, endp = &X_kdb_enter_str[X_kdb_enter_len]; + u_ip < endp; + u_ip += 2) + kdb_in_out(u_ip); +} + +void +X_kdb_exit(void) +{ + u_int *u_ip, *endp; + + for (u_ip = X_kdb_exit_str, endp = &X_kdb_exit_str[X_kdb_exit_len]; + u_ip < endp; + u_ip += 2) + kdb_in_out(u_ip); +} + +io_return_t +X_kdb_enter_init( + u_int *data, + u_int count) +{ + if (count * sizeof X_kdb_enter_str[0] > sizeof X_kdb_enter_str) + return D_INVALID_OPERATION; + + memcpy(X_kdb_enter_str, data, count * sizeof X_kdb_enter_str[0]); + X_kdb_enter_len = count; + return D_SUCCESS; +} + +io_return_t +X_kdb_exit_init( + u_int *data, + u_int count) +{ + if (count * sizeof X_kdb_exit_str[0] > sizeof X_kdb_exit_str) + return D_INVALID_OPERATION; + + memcpy(X_kdb_exit_str, data, count * sizeof X_kdb_exit_str[0]); + X_kdb_exit_len = count; + return D_SUCCESS; +} diff --git a/i386/i386at/kd_event.h b/i386/i386at/kd_event.h new file mode 100644 index 0000000..7e66f76 --- /dev/null +++ b/i386/i386at/kd_event.h @@ -0,0 +1,62 @@ +/* + * Keyboard event handlers + * Copyright (C) 2006 Free Software Foundation, Inc. + * + * 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 2, 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, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Barry deFreese. + */ +/* + * Keyboard event handling functions. + * + */ + +#ifndef _KD_EVENT_H_ +#define _KD_EVENT_H_ + +#include +#include +#include + +extern void X_kdb_enter (void); + +extern void X_kdb_exit (void); + +extern int kbdopen(dev_t dev, int flags, io_req_t ior); +extern void kbdclose(dev_t dev, int flags); +extern int kbdread(dev_t dev, io_req_t ior); + +extern io_return_t kbdgetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, + mach_msg_type_number_t *count); + +extern io_return_t kbdsetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, + mach_msg_type_number_t count); + +extern void kd_enqsc(Scancode sc); + +void kbd_enqueue(kd_event *ev); + +io_return_t X_kdb_enter_init(u_int *data, u_int count); +io_return_t X_kdb_exit_init(u_int *data, u_int count); + +boolean_t kbd_read_done(io_req_t ior); + +#endif /* _KD_EVENT_H_ */ diff --git a/i386/i386at/kd_mouse.c b/i386/i386at/kd_mouse.c new file mode 100644 index 0000000..9bd001c --- /dev/null +++ b/i386/i386at/kd_mouse.c @@ -0,0 +1,800 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* ********************************************************************** + File: kd_mouse.c + Description: mouse driver as part of keyboard/display driver + + $ Header: $ + + Copyright Ing. C. Olivetti & C. S.p.A. 1989. + All rights reserved. +********************************************************************** */ +/* + Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc., +Cupertino, California. + + All Rights Reserved + + Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appears in all +copies and that both the copyright notice and this permission notice +appear in supporting documentation, and that the name of Olivetti +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + + OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, +NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/* + * Hacked up support for serial mouse connected to COM1, using Mouse + * Systems 5-byte protocol at 1200 baud. This should work for + * Mouse Systems, SummaMouse, and Logitek C7 mice. + * + * The interface provided by /dev/mouse is a series of events as + * described in i386at/kd.h. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kd_mouse.h" + +static interrupt_handler_fn oldvect; /* old interrupt vector */ +static int oldunit; +extern struct bus_device *cominfo[]; + +kd_event_queue mouse_queue; /* queue of mouse events */ +boolean_t mouse_in_use = FALSE; +queue_head_t mouse_read_queue = { &mouse_read_queue, &mouse_read_queue }; + + +/* + * The state of the 3 buttons is encoded in the low-order 3 bits (both + * here and in other variables in the driver). + */ +u_char lastbuttons; /* previous state of mouse buttons */ +#define MOUSE_UP 1 +#define MOUSE_DOWN 0 +#define MOUSE_ALL_UP 0x7 + +int mouse_baud = BCNT1200; + +boolean_t mouse_char_cmd = FALSE; /* mouse response is to cmd */ +boolean_t mouse_char_wanted = FALSE; /* want mouse response */ +int mouse_char_index; /* mouse response */ + +#define IBM_MOUSE_IRQ 12 + +/* + * init_mouse_hw - initialize the serial port. + */ +static void +init_mouse_hw(dev_t unit, int mode) +{ + unsigned short base_addr = cominfo[unit]->address; + + outb(base_addr + RIE, 0); + outb(base_addr + RLC, LCDLAB); + outb(base_addr + RDLSB, mouse_baud & 0xff); + outb(base_addr + RDMSB, (mouse_baud >> 8) & 0xff); + outb(base_addr + RLC, mode); + outb(base_addr + RMC, MCDTR | MCRTS | MCOUT2); + outb(base_addr + RIE, IERD | IELS); +} + + +/* + * mouseopen - Verify that the request is read-only, initialize, + * and remember process group leader. + */ +/* + * Low 3 bits of minor are the com port #. + * The high 5 bits of minor are the mouse type + */ +#define MOUSE_SYSTEM_MOUSE 0 +#define MICROSOFT_MOUSE 1 +#define IBM_MOUSE 2 +#define NO_MOUSE 3 +#define LOGITECH_TRACKMAN 4 +#define MICROSOFT_MOUSE7 5 +static int mouse_type; +static int mousebufsize; +static int mousebufindex = 0; +int track_man[10]; + +/*ARGSUSED*/ +int +mouseopen(dev_t dev, int flags, io_req_t ior) +{ + if (mouse_in_use) + return (D_ALREADY_OPEN); + mouse_in_use = TRUE; /* locking? */ + kdq_reset(&mouse_queue); + lastbuttons = MOUSE_ALL_UP; + + switch (mouse_type = ((minor(dev) & 0xf8) >> 3)) { + case MICROSOFT_MOUSE7: + mousebufsize = 3; + serial_mouse_open(dev); + init_mouse_hw(dev&7, LC7); + break; + case MICROSOFT_MOUSE: + mousebufsize = 3; + serial_mouse_open(dev); + init_mouse_hw(dev&7, LC8); + break; + case MOUSE_SYSTEM_MOUSE: + mousebufsize = 5; + serial_mouse_open(dev); + init_mouse_hw(dev&7, LC8); + break; + case LOGITECH_TRACKMAN: + mousebufsize = 3; + serial_mouse_open(dev); + init_mouse_hw(dev&7, LC7); + track_man[0] = comgetc(dev&7); + track_man[1] = comgetc(dev&7); + if (track_man[0] != 0x4d && + track_man[1] != 0x33) { + printf("LOGITECH_TRACKMAN: NOT M3"); + } + break; + case IBM_MOUSE: + mousebufsize = 3; + kd_mouse_open(dev, IBM_MOUSE_IRQ); + ibm_ps2_mouse_open(dev); + break; + case NO_MOUSE: + break; + } + mousebufindex = 0; + return(0); +} + +void +serial_mouse_open(dev_t dev) +{ + int unit = minor(dev) & 0x7; + int mouse_pic = cominfo[unit]->sysdep1; + + spl_t s = splhi(); /* disable interrupts */ + + oldvect = ivect[mouse_pic]; + ivect[mouse_pic] = mouseintr; + + oldunit = iunit[mouse_pic]; + iunit[mouse_pic] = unit; + + /* XXX other arrays to init? */ + splx(s); /* XXX - should come after init? */ +} + +int mouse_packets = 0; + +void +kd_mouse_open( + dev_t dev, + int mouse_pic) +{ + spl_t s = splhi(); /* disable interrupts */ + + oldvect = ivect[mouse_pic]; + ivect[mouse_pic] = kdintr; + unmask_irq(mouse_pic); + splx(s); +} + +/* + * mouseclose - Disable interrupts on the serial port, reset driver flags, + * and restore the serial port interrupt vector. + */ +void +mouseclose( + dev_t dev, + int flags) +{ + switch (mouse_type) { + case MICROSOFT_MOUSE: + case MICROSOFT_MOUSE7: + case MOUSE_SYSTEM_MOUSE: + case LOGITECH_TRACKMAN: + serial_mouse_close(dev, flags); + break; + case IBM_MOUSE: + ibm_ps2_mouse_close(dev); + kd_mouse_close(dev, IBM_MOUSE_IRQ); + {int i = 20000; for (;i--;); } + kd_mouse_drain(); + break; + case NO_MOUSE: + break; + } + + kdq_reset(&mouse_queue); /* paranoia */ + mouse_in_use = FALSE; +} + +/*ARGSUSED*/ +void +serial_mouse_close( + dev_t dev, + int flags) +{ + spl_t o_pri = splhi(); /* mutex with open() */ + int unit = minor(dev) & 0x7; + int mouse_pic = cominfo[unit]->sysdep1; + unsigned short base_addr = cominfo[unit]->address; + + assert(ivect[mouse_pic] == mouseintr); + outb(base_addr + RIE, 0); /* disable serial port */ + outb(base_addr + RMC, 0); /* no rts */ + ivect[mouse_pic] = oldvect; + iunit[mouse_pic] = oldunit; + + (void)splx(o_pri); +} + +void +kd_mouse_close( + dev_t dev, + int mouse_pic) +{ + spl_t s = splhi(); + + mask_irq(mouse_pic); + ivect[mouse_pic] = oldvect; + splx(s); +} + +io_return_t mousegetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, /* pointer to OUT array */ + mach_msg_type_number_t *count) /* OUT */ +{ + switch (flavor) { + case DEV_GET_SIZE: + data[DEV_GET_SIZE_DEVICE_SIZE] = 0; + data[DEV_GET_SIZE_RECORD_SIZE] = sizeof(kd_event); + *count = DEV_GET_SIZE_COUNT; + break; + default: + return D_INVALID_OPERATION; + } + return D_SUCCESS; +} + + +/* + * mouseread - dequeue and return any queued events. + */ +int +mouseread( + dev_t dev, + io_req_t ior) +{ + int err, count; + spl_t s; + + /* Check if IO_COUNT is a multiple of the record size. */ + if (ior->io_count % sizeof(kd_event) != 0) + return D_INVALID_SIZE; + + err = device_read_alloc(ior, (vm_size_t)ior->io_count); + if (err != KERN_SUCCESS) + return (err); + + s = SPLKD(); + if (kdq_empty(&mouse_queue)) { + if (ior->io_mode & D_NOWAIT) { + splx(s); + return (D_WOULD_BLOCK); + } + ior->io_done = mouse_read_done; + enqueue_tail(&mouse_read_queue, (queue_entry_t)ior); + splx(s); + return (D_IO_QUEUED); + } + count = 0; + while (!kdq_empty(&mouse_queue) && count < ior->io_count) { + kd_event *ev; + + ev = kdq_get(&mouse_queue); + *(kd_event *)(&ior->io_data[count]) = *ev; + count += sizeof(kd_event); + } + splx(s); + ior->io_residual = ior->io_count - count; + return (D_SUCCESS); +} + +boolean_t mouse_read_done(io_req_t ior) +{ + int count; + spl_t s; + + s = SPLKD(); + if (kdq_empty(&mouse_queue)) { + ior->io_done = mouse_read_done; + enqueue_tail(&mouse_read_queue, (queue_entry_t)ior); + splx(s); + return (FALSE); + } + + count = 0; + while (!kdq_empty(&mouse_queue) && count < ior->io_count) { + kd_event *ev; + + ev = kdq_get(&mouse_queue); + *(kd_event *)(&ior->io_data[count]) = *ev; + count += sizeof(kd_event); + } + splx(s); + + ior->io_residual = ior->io_count - count; + ds_read_done(ior); + + return (TRUE); +} + + + +/* + * mouseintr - Get a byte and pass it up for handling. Called at SPLKD. + */ +void +mouseintr(int unit) +{ + unsigned short base_addr = cominfo[unit]->address; + unsigned char id, ls; + + /* get reason for interrupt and line status */ + id = inb(base_addr + RID); + ls = inb(base_addr + RLS); + + /* handle status changes */ + if (id == IDLS) { + if (ls & LSDR) { + inb(base_addr + RDAT); /* flush bad character */ + } + return; /* ignore status change */ + } + + if (id & IDRD) { + mouse_handle_byte((u_char)(inb(base_addr + RDAT) & 0xff)); + } +} + + +/* + * handle_byte - Accumulate bytes until we have an entire packet. + * If the mouse has moved or any of the buttons have changed state (up + * or down), enqueue the corresponding events. + * Called at SPLKD. + * XXX - magic numbers. + */ +int show_mouse_byte = 0; +/* + X down; middle down; middle up; X up 50 0 0; 50 0 0 22; 50 0 0 02; 40 0 0 + X down; middle down; X up; middle up 50 0 0; 50 0 0 22; 40 0 0 22; 40 0 0 2 + * + * The trick here is that all the while the middle button is down you get 4 byte + * packets with the last byte 0x22. When the middle button goes up you get a + * last packet with 0x02. + */ +int lastgitech = 0x40; /* figure whether the first 3 bytes imply */ + /* its time to expect a fourth */ +int fourthgitech = 0; /* look for the 4th byte; we must process it */ +int middlegitech = 0; /* what should the middle button be */ + +static u_char mousebuf[MOUSEBUFSIZE]; /* 5-byte packet from mouse */ + +void +mouse_handle_byte(u_char ch) +{ + if (show_mouse_byte) { + printf("%x(%c) ", ch, ch); + } + + if (mouse_char_cmd) { + /* + * Mouse character is response to command + */ + if (mousebufindex < mousebufsize) + mousebuf[mousebufindex++] = ch; + if (mouse_char_wanted) { + mouse_char_wanted = FALSE; + wakeup((vm_offset_t)&mousebuf); + } + return; + } + + if (mousebufindex == 0) { + switch (mouse_type) { + case MICROSOFT_MOUSE7: + if ((ch & 0x40) != 0x40) + return; + break; + case MICROSOFT_MOUSE: + if ((ch & 0xc0) != 0xc0) + return; + break; + case MOUSE_SYSTEM_MOUSE: + if ((ch & 0xf8) != 0x80) + return; + break; + case LOGITECH_TRACKMAN: + if (fourthgitech == 1) { + fourthgitech = 0; + if (ch & 0xf0) + middlegitech = 0x4; + else + middlegitech = 0x0; + mouse_packet_microsoft_mouse(mousebuf); + return; + } else if ((ch & 0xc0) != 0x40) + return; + break; + case IBM_MOUSE: + break; + } + } + + mousebuf[mousebufindex++] = ch; + if (mousebufindex < mousebufsize) + return; + + /* got a packet */ + mousebufindex = 0; + + switch (mouse_type) { + case MICROSOFT_MOUSE7: + case MICROSOFT_MOUSE: + mouse_packet_microsoft_mouse(mousebuf); + break; + case MOUSE_SYSTEM_MOUSE: + mouse_packet_mouse_system_mouse(mousebuf); + break; + case LOGITECH_TRACKMAN: + if ( mousebuf[1] || mousebuf[2] || + mousebuf[0] != lastgitech) { + mouse_packet_microsoft_mouse(mousebuf); + lastgitech = mousebuf[0] & 0xf0; + } else { + fourthgitech = 1; + } + break; + case IBM_MOUSE: + mouse_packet_ibm_ps2_mouse(mousebuf); + break; + } +} + +void +mouse_packet_mouse_system_mouse(u_char mousebuf[MOUSEBUFSIZE]) +{ + u_char buttons, buttonchanges; + struct mouse_motion moved; + + buttons = mousebuf[0] & 0x7; /* get current state of buttons */ + buttonchanges = buttons ^ lastbuttons; + moved.mm_deltaX = (char)mousebuf[1] + (char)mousebuf[3]; + moved.mm_deltaY = (char)mousebuf[2] + (char)mousebuf[4]; + + if (moved.mm_deltaX != 0 || moved.mm_deltaY != 0) + mouse_moved(moved); + + if (buttonchanges != 0) { + lastbuttons = buttons; + if (buttonchanges & 1) + mouse_button(MOUSE_RIGHT, buttons & 1); + if (buttonchanges & 2) + mouse_button(MOUSE_MIDDLE, (buttons & 2) >> 1); + if (buttonchanges & 4) + mouse_button(MOUSE_LEFT, (buttons & 4) >> 2); + } +} + +/* same as above for microsoft mouse */ +/* + * 3 byte microsoft format used + * + * 7 6 5 4 3 2 1 0 + * 1 1 L R Y7 Y6 X7 X6 + * 1 0 X5 X4 X3 X3 X1 X0 + * 1 0 Y5 Y4 Y3 Y2 Y1 Y0 + * + */ +void +mouse_packet_microsoft_mouse(u_char mousebuf[MOUSEBUFSIZE]) +{ + u_char buttons, buttonchanges; + struct mouse_motion moved; + + buttons = ((mousebuf[0] & 0x30) >> 4); + buttons |= middlegitech; + /* get current state of buttons */ +#ifdef gross_hack + if (buttons == 0x03) /* both buttons down */ + buttons = 0x04; +#endif /* gross_hack */ + buttons = (~buttons) & 0x07; /* convert to not pressed */ + + buttonchanges = buttons ^ lastbuttons; + moved.mm_deltaX = ((mousebuf[0] & 0x03) << 6) | (mousebuf[1] & 0x3F); + moved.mm_deltaY = ((mousebuf[0] & 0x0c) << 4) | (mousebuf[2] & 0x3F); + if (moved.mm_deltaX & 0x80) /* negative, in fact */ + moved.mm_deltaX = moved.mm_deltaX - 0x100; + if (moved.mm_deltaY & 0x80) /* negative, in fact */ + moved.mm_deltaY = moved.mm_deltaY - 0x100; + /* and finally the Y orientation is different for the microsoft mouse */ + moved.mm_deltaY = -moved.mm_deltaY; + + if (moved.mm_deltaX != 0 || moved.mm_deltaY != 0) + mouse_moved(moved); + + if (buttonchanges != 0) { + lastbuttons = buttons; + if (buttonchanges & 1) + mouse_button(MOUSE_RIGHT, (buttons & 1) ? + MOUSE_UP : MOUSE_DOWN); + if (buttonchanges & 2) + mouse_button(MOUSE_LEFT, (buttons & 2) ? + MOUSE_UP : MOUSE_DOWN); + if (buttonchanges & 4) + mouse_button(MOUSE_MIDDLE, (buttons & 4) ? + MOUSE_UP : MOUSE_DOWN); + } +} + +/* + * AUX device (PS2) open/close + */ + +/* + * Write character to mouse. Called at spltty. + */ +static void kd_mouse_write( + unsigned char ch) +{ + while (inb(K_STATUS) & K_IBUF_FUL) + continue; /* wait for 'input' port empty */ + outb(K_CMD, 0xd4); /* send next character to mouse */ + + while (inb(K_STATUS) & K_IBUF_FUL) + continue; /* wait for 'input' port empty */ + outb(K_RDWR, ch); /* send command to mouse */ +} + +/* + * Read next character from mouse, waiting for interrupt + * to deliver it. Called at spltty. + */ +static int kd_mouse_read(void) +{ + int ch; + + if (mouse_char_index >= mousebufsize) + return -1; + + while (mousebufindex <= mouse_char_index) { + mouse_char_wanted = TRUE; + assert_wait((event_t) &mousebuf, FALSE); + /* We are at tty SPL level, interrupts can not happen between + * assert_wait and thread_block. */ + thread_block((void (*)()) 0); + } + + ch = mousebuf[mouse_char_index++]; + + return ch; +} + +/* + * Prepare buffer for receiving next packet from mouse. + */ +static void kd_mouse_read_reset(void) +{ + mousebufindex = 0; + mouse_char_index = 0; +} + +void +ibm_ps2_mouse_open(dev_t dev) +{ + spl_t s = spltty(); + + lastbuttons = 0; + mouse_char_cmd = TRUE; /* responses are to commands */ + + kd_sendcmd(0xa8); /* enable mouse in kbd */ + + kd_cmdreg_write(0x47); /* allow mouse interrupts */ + /* magic number for ibm? */ + + kd_mouse_read_reset(); + kd_mouse_write(0xff); /* reset mouse */ + if (kd_mouse_read() != 0xfa) { + splx(s); + return; /* need ACK */ + } + + (void) kd_mouse_read(); /* discard 2-character mouse ID */ + (void) kd_mouse_read(); + + kd_mouse_read_reset(); + kd_mouse_write(0xea); /* set stream mode */ + if (kd_mouse_read() != 0xfa) { + splx(s); + return; /* need ACK */ + } + + kd_mouse_read_reset(); + kd_mouse_write(0xf4); /* enable */ + if (kd_mouse_read() != 0xfa) { + splx(s); + return; /* need ACK */ + } + + kd_mouse_read_reset(); + mouse_char_cmd = FALSE; /* now we get mouse packets */ + + splx(s); +} + +void +ibm_ps2_mouse_close(dev_t dev) +{ + spl_t s = spltty(); + + mouse_char_cmd = TRUE; /* responses are to commands */ + + kd_mouse_read_reset(); + kd_mouse_write(0xff); /* reset mouse */ + if (kd_mouse_read() == 0xfa) { + /* got ACK: discard 2-char mouse ID */ + (void) kd_mouse_read(); + (void) kd_mouse_read(); + } + + kd_sendcmd(0xa7); /* disable mouse in kbd */ + kd_cmdreg_write(0x65); /* disallow mouse interrupts */ + /* magic number for ibm? */ + + splx(s); +} + +/* + * 3 byte ibm ps2 format used + * + * 7 6 5 4 3 2 1 0 + * YO XO YS XS 1 M R L + * X7 X6 X5 X4 X3 X3 X1 X0 + * Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0 + * + */ +void +mouse_packet_ibm_ps2_mouse(u_char mousebuf[MOUSEBUFSIZE]) +{ + u_char buttons, buttonchanges; + struct mouse_motion moved; + + buttons = mousebuf[0] & 0x7; /* get current state of buttons */ + buttonchanges = buttons ^ lastbuttons; + moved.mm_deltaX = ((mousebuf[0]&0x10) ? 0xffffff00 : 0 ) | (u_char)mousebuf[1]; + moved.mm_deltaY = ((mousebuf[0]&0x20) ? 0xffffff00 : 0 ) | (u_char)mousebuf[2]; + if (mouse_packets) { + printf("(%x:%x:%x)", mousebuf[0], mousebuf[1], mousebuf[2]); + return; + } + + if (moved.mm_deltaX != 0 || moved.mm_deltaY != 0) + mouse_moved(moved); + + if (buttonchanges != 0) { + lastbuttons = buttons; + if (buttonchanges & 1) + mouse_button(MOUSE_LEFT, !(buttons & 1)); + if (buttonchanges & 2) + mouse_button(MOUSE_RIGHT, !((buttons & 2) >> 1)); + if (buttonchanges & 4) + mouse_button(MOUSE_MIDDLE, !((buttons & 4) >> 2)); + } +} + +/* + * Enqueue a mouse-motion event. Called at SPLKD. + */ +void +mouse_moved(struct mouse_motion where) +{ + kd_event ev; + + ev.type = MOUSE_MOTION; + /* Not used but we set it to avoid garbage */ + ev.unused_time.seconds = 0; + ev.unused_time.microseconds = 0; + ev.value.mmotion = where; + mouse_enqueue(&ev); +} + +/* + * Enqueue an event for mouse button press or release. Called at SPLKD. + */ +void +mouse_button( + kev_type which, + u_char direction) +{ + kd_event ev; + + ev.type = which; + ev.value.up = (direction == MOUSE_UP) ? TRUE : FALSE; + /* Not used but we set it to avoid garbage */ + ev.unused_time.seconds = 0; + ev.unused_time.microseconds = 0; + mouse_enqueue(&ev); +} + +/* + * mouse_enqueue - enqueue an event and wake up selecting processes, if + * any. Called at SPLKD. + */ + +void +mouse_enqueue(kd_event *ev) +{ + if (kdq_full(&mouse_queue)) + printf_once("mouse: queue full\n"); + else + kdq_put(&mouse_queue, ev); + + { + io_req_t ior; + while ((ior = (io_req_t)dequeue_head(&mouse_read_queue)) != 0) + iodone(ior); + } +} diff --git a/i386/i386at/kd_mouse.h b/i386/i386at/kd_mouse.h new file mode 100644 index 0000000..a9fb128 --- /dev/null +++ b/i386/i386at/kd_mouse.h @@ -0,0 +1,72 @@ +/* + * Mouse event handlers + * Copyright (C) 2006 Free Software Foundation, Inc. + * + * 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 2, 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, write to the Free Software + * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Barry deFreese. + */ +/* + * Mouse event handling functions. + * + */ + +#ifndef _KD_MOUSE_H_ +#define _KD_MOUSE_H_ + +#include + +#define MOUSEBUFSIZE 5 /* num bytes def'd by protocol */ + +extern void mouse_button (kev_type which, u_char direction); + +extern void mouse_enqueue (kd_event *ev); + +extern void mouse_moved (struct mouse_motion where); + +extern void mouse_handle_byte (u_char ch); + +extern void serial_mouse_open (dev_t dev); + +extern void serial_mouse_close (dev_t dev, int flags); + +extern void kd_mouse_open (dev_t dev, int mouse_pic); + +extern void kd_mouse_close (dev_t dev, int mouse_pic); + +extern void ibm_ps2_mouse_open (dev_t dev); + +extern void ibm_ps2_mouse_close (dev_t dev); + +extern void mouse_packet_microsoft_mouse (u_char mousebuf[MOUSEBUFSIZE]); + +extern void mouse_packet_mouse_system_mouse (u_char mousebuf[MOUSEBUFSIZE]); + +extern void mouse_packet_ibm_ps2_mouse (u_char mousebuf[MOUSEBUFSIZE]); + +extern int mouseopen(dev_t dev, int flags, io_req_t ior); +extern void mouseclose(dev_t dev, int flags); +extern int mouseread(dev_t dev, io_req_t ior); + +extern io_return_t mousegetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, + mach_msg_type_number_t *count); + +void mouseintr(int unit); +boolean_t mouse_read_done(io_req_t ior); + +#endif /* _KD_MOUSE_H_ */ diff --git a/i386/i386at/kd_queue.c b/i386/i386at/kd_queue.c new file mode 100644 index 0000000..ab399cd --- /dev/null +++ b/i386/i386at/kd_queue.c @@ -0,0 +1,109 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* ********************************************************************** + File: kd_queue.c + Description: Event queue code for keyboard/display (and mouse) driver. + + $ Header: $ + + Copyright Ing. C. Olivetti & C. S.p.A. 1989. + All rights reserved. +********************************************************************** */ +/* + Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc., +Cupertino, California. + + All Rights Reserved + + Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appears in all +copies and that both the copyright notice and this permission notice +appear in supporting documentation, and that the name of Olivetti +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + + OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, +NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + + +#include + +/* + * Notice that when adding an entry to the queue, the caller provides + * its own storage, which is copied into the queue. However, when + * removing an entry from the queue, the caller is given a pointer to a + * queue element. This means that the caller must either process the + * element or copy it into its own storage before unlocking the queue. + * + * These routines should be called only at a protected SPL. + */ + +#define q_next(index) (((index)+1) % KDQSIZE) + +boolean_t +kdq_empty(const kd_event_queue *q) +{ + return(q->firstfree == q->firstout); +} + +boolean_t +kdq_full(const kd_event_queue *q) +{ + return(q_next(q->firstfree) == q->firstout); +} + +void +kdq_put(kd_event_queue *q, kd_event *ev) +{ + kd_event *qp = q->events + q->firstfree; + + qp->type = ev->type; + qp->unused_time = ev->unused_time; + qp->value = ev->value; + q->firstfree = q_next(q->firstfree); +} + +kd_event * +kdq_get(kd_event_queue *q) +{ + kd_event *result = q->events + q->firstout; + + q->firstout = q_next(q->firstout); + return(result); +} + +void +kdq_reset(kd_event_queue *q) +{ + q->firstout = q->firstfree = 0; +} diff --git a/i386/i386at/kd_queue.h b/i386/i386at/kd_queue.h new file mode 100644 index 0000000..702efe8 --- /dev/null +++ b/i386/i386at/kd_queue.h @@ -0,0 +1,86 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* ********************************************************************** + File: kd_queue.h + Description: definitions for keybd/display Event queue + + $ Header: $ + + Copyright Ing. C. Olivetti & C. S.p.A. 1989. + All rights reserved. +********************************************************************** */ +/* + Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc., +Cupertino, California. + + All Rights Reserved + + Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appears in all +copies and that both the copyright notice and this permission notice +appear in supporting documentation, and that the name of Olivetti +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + + OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, +NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/* + * Definitions for keyboard/mouse events. + * + * The keyboard and mouse can be read as a stream of events. The event + * definition is the same in both cases, but only keyboard events will + * be generated by /dev/kbd, and only mouse events will be generated by + * /dev/mouse. + */ + +#ifndef _KD_QUEUE_H_ +#define _KD_QUEUE_H_ + +#include +#include + +#define KDQSIZE 100 /* is this a good size? */ + +typedef struct { + kd_event events[KDQSIZE]; + int firstfree, firstout; +} kd_event_queue; + +extern void kdq_put(kd_event_queue *, kd_event *); +extern void kdq_reset(kd_event_queue *); +extern boolean_t kdq_empty(const kd_event_queue *); +extern boolean_t kdq_full(const kd_event_queue *); +extern kd_event *kdq_get(kd_event_queue *); + +#endif /* _KD_QUEUE_H_ */ diff --git a/i386/i386at/kdasm.S b/i386/i386at/kdasm.S new file mode 100644 index 0000000..fd0e1c8 --- /dev/null +++ b/i386/i386at/kdasm.S @@ -0,0 +1,145 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Some inline code to speed up major block copies to and from the + * screen buffer. + * + * Copyright Ing. C. Olivetti & C. S.p.A. 1988, 1989. + * All rights reserved. + * + * orc!eugene 28 Oct 1988 + * + */ +/* + Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc., +Cupertino, California. + + All Rights Reserved + + Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appears in all +copies and that both the copyright notice and this permission notice +appear in supporting documentation, and that the name of Olivetti +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + + OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, +NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/* $ Header: $ */ + + +#include + +/* + * Function: kd_slmwd() + * + * This function "slams" a word (char/attr) into the screen memory using + * a block fill operation on the 386. + * + */ + +#define start B_ARG0 +#define count B_ARG1 +#define value B_ARG2 + +ENTRY(kd_slmwd) + pushl %ebp + movl %esp, %ebp + pushl %edi + + movl start, %edi + movl count, %ecx + movw value, %ax + cld + rep + stosw + + popl %edi + leave + ret +#undef start +#undef count +#undef value + +/* + * "slam up" + */ + +#define from B_ARG0 +#define to B_ARG1 +#define count B_ARG2 +ENTRY(kd_slmscu) + pushl %ebp + movl %esp, %ebp + pushl %esi + pushl %edi + + movl from, %esi + movl to, %edi + movl count, %ecx + cmpl %edi, %esi + cld + rep + movsw + + popl %edi + popl %esi + leave + ret + +/* + * "slam down" + */ +ENTRY(kd_slmscd) + pushl %ebp + movl %esp, %ebp + pushl %esi + pushl %edi + + movl from, %esi + movl to, %edi + movl count, %ecx + cmpl %edi, %esi + std + rep + movsw + cld + + popl %edi + popl %esi + leave + ret +#undef from +#undef to +#undef count diff --git a/i386/i386at/kdsoft.h b/i386/i386at/kdsoft.h new file mode 100644 index 0000000..79bfdb0 --- /dev/null +++ b/i386/i386at/kdsoft.h @@ -0,0 +1,209 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* ********************************************************************** + File: kdsoft.h + Description: Software structures for keyboard/display driver, shared with + drivers for specific graphics cards. + + $ Header: $ + + Copyright Ing. C. Olivetti & C. S.p.A. 1988, 1989. + All rights reserved. +********************************************************************** */ + +/* + Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc., +Cupertino, California. + + All Rights Reserved + + Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appears in all +copies and that both the copyright notice and this permission notice +appear in supporting documentation, and that the name of Olivetti +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + + OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, +NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef _KDSOFT_H_ +#define _KDSOFT_H_ + +/* + * Globals used for both character-based controllers and bitmap-based + * controllers. + */ +typedef short csrpos_t; /* cursor position, ONE_SPACE bytes per char */ + +extern u_char *vid_start; /* VM start of video RAM or frame buffer */ +extern csrpos_t kd_curpos; /* should be set only by kd_setpos */ +extern short kd_lines; /* num lines in tty display */ +extern short kd_cols; +extern char kd_attr; /* current character attribute */ + + +/* + * Globals used only for bitmap-based controllers. + * XXX - probably needs reworking for color. + */ + +/* + * This driver handles two types of graphics cards. The first type + * (e.g., EGA, CGA), treats the screen as a page of characters and + * has a hardware cursor. The second type (e.g., the Blit) treats the + * screen as a bitmap. A hardware cursor may be present, but it is + * ignored in favor of a software cursor. + * + * + * Most of the driver uses the following abstraction for the display: + * + * The cursor position is simply an index into a (logical) linear char + * array that wraps around at the end of each line. Each character + * takes up ONE_SPACE bytes. Values in [0..ONE_PAGE) are positions in + * the displayed page. Values < 0 and >= ONE_PAGE are off the page + * and require some scrolling to put the cursor back on the page. + * + * The kd_dxxx routines handle the conversion from this abstraction to + * what the hardware requires. + * + * (*kd_dput)(pos, ch, chattr) + * csrpos_t pos; + * char ch, chattr; + * Displays a character at "pos", where "ch" = the character to + * be displayed and "chattr" is its attribute byte. + * + * (*kd_dmvup)(from, to, count) + * csrpos_t from, to; + * int count; + * Does a (relatively) fast block transfer of characters upward. + * "count" is the number of character positions (not bytes) to move. + * "from" is the character position to start moving from (at the start + * of the block to be moved). "to" is the character position to start + * moving to. + * + * (*kd_dmvdown)(from, to, count) + * csrpos_t from, to; + * int count; + * "count" is the number of character positions (not bytes) to move. + * "from" is the character position to start moving from (at the end + * of the block to be moved). "to" is the character position to + * start moving to. + * + * (*kd_dclear)(to, count, chattr) + * csrpos_t, to; + * int count; + * char chattr; + * Erases "count" character positions, starting with "to". + * + * (*kd_dsetcursor)(pos) + * Sets kd_curpos and moves the displayed cursor to track it. "pos" + * should be in the range [0..ONE_PAGE). + * + * (*kd_dreset)() + * In some cases, the boot program expects the display to be in a + * particular state, and doing a soft reset (i.e., + * software-controlled reboot) doesn't put it into that state. For + * these cases, the machine-specific driver should provide a "reset" + * procedure, which will be called just before the kd code causes the + * system to reboot. + */ + +extern void bmpput(csrpos_t, char, char); +extern void bmpmvup(csrpos_t, csrpos_t, int); +extern void bmpmvdown(csrpos_t, csrpos_t, int); +extern void bmpclear(csrpos_t, int, char); +extern void bmpsetcursor(csrpos_t); + +extern void (*kd_dput)(csrpos_t, char, char); /* put attributed char */ +extern void (*kd_dmvup)(csrpos_t, csrpos_t, int); /* block move up */ +extern void (*kd_dmvdown)(csrpos_t, csrpos_t, int); /* block move down */ +extern void (*kd_dclear)(csrpos_t, int, char); /* block clear */ +extern void (*kd_dsetcursor)(csrpos_t); /* set cursor position on displayed page */ +extern void (*kd_dreset)(void); /* prepare for reboot */ + + +/* + * The following font layout is assumed: + * + * The top scan line of all the characters comes first. Then the + * second scan line, then the third, etc. + * + * ------ ... ---------|-----N--------|-------------- ... ----------- + * ------ ... ---------|-----N--------|-------------- ... ----------- + * . + * . + * . + * ------ ... ---------|-----N--------|-------------- ... ----------- + * + * In the picture, each line is a scan line from the font. Each scan + * line is stored in memory immediately after the previous one. The + * bits between the vertical lines are the bits for a single character + * (e.g., the letter "N"). + * There are "char_height" scan lines. Each character is "char_width" + * bits wide. We make the simplifying assumption that characters are + * on byte boundaries. (We also assume that a byte is 8 bits.) + */ + +extern u_char *font_start; /* starting addr of font */ + +extern short fb_width; /* bits in frame buffer scan line */ +extern short fb_height; /* scan lines in frame buffer*/ +extern short char_width; /* bit width of 1 char */ +extern short char_height; /* bit height of 1 char */ +extern short chars_in_font; +extern short cursor_height; /* bit height of cursor */ + /* char_height + cursor_height = line_height */ + +extern u_char char_black; /* 8 black (off) bits */ +extern u_char char_white; /* 8 white (on) bits */ + + +/* + * The tty emulation does not usually require the entire frame buffer. + * (xstart, ystart) is the bit address for the upper left corner of the + * tty "screen". + */ + +extern short xstart, ystart; + + +/* + * Accelerators for bitmap displays. + */ + +extern short char_byte_width; /* char_width/8 */ +extern short fb_byte_width; /* fb_width/8 */ +extern short font_byte_width; /* num bytes in 1 scan line of font */ + +#endif /* _KDSOFT_H_ */ diff --git a/i386/i386at/lpr.c b/i386/i386at/lpr.c new file mode 100644 index 0000000..f8d42f3 --- /dev/null +++ b/i386/i386at/lpr.c @@ -0,0 +1,285 @@ +/* + * Mach Operating System + * Copyright (c) 1993-1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Parallel port printer driver v1.0 + * All rights reserved. + */ + +#if NLPR > 0 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * Driver information for auto-configuration stuff. + */ + +struct bus_device *lprinfo[NLPR]; /* ??? */ + +static vm_offset_t lpr_std[NLPR] = { 0 }; +static struct bus_device *lpr_info[NLPR]; +struct bus_driver lprdriver = { + lprprobe, 0, lprattach, 0, lpr_std, "lpr", lpr_info, 0, 0, 0}; + +struct tty lpr_tty[NLPR]; + +int lpr_alive[NLPR]; + +int +lprprobe(vm_offset_t port, struct bus_ctlr *dev) +{ + u_short addr = (u_short) dev->address; + int unit = dev->unit; + int ret; + + if ((unit < 0) || (unit >= NLPR)) { + printf("com %d out of range\n", unit); + return(0); + } + + outb(INTR_ENAB(addr),0x07); + outb(DATA(addr),0xaa); + ret = inb(DATA(addr)) == 0xaa; + if (ret) { + if (lpr_alive[unit]) { + printf("lpr: Multiple alive entries for unit %d.\n", unit); + printf("lpr: Ignoring entry with address = %x .\n", addr); + ret = 0; + } else + lpr_alive[unit]++; + } + return(ret); +} + +void lprattach(struct bus_device *dev) +{ + u_char unit = dev->unit; + u_short addr = (u_short) dev->address; + + if (unit >= NLPR) { + printf(", disabled by NLPR configuration\n"); + return; + } + + take_dev_irq(dev); + printf(", port = %zx, spl = %zd, pic = %d.", + dev->address, dev->sysdep, dev->sysdep1); + lprinfo[unit] = dev; + + outb(INTR_ENAB(addr), inb(INTR_ENAB(addr)) & 0x0f); + + return; +} + +int +lpropen(dev_t dev, int flag, io_req_t ior) +{ + int unit = minor(dev); + struct bus_device *isai; + struct tty *tp; + u_short addr; + + if (unit >= NLPR) + return D_NO_SUCH_DEVICE; + + isai = lprinfo[unit]; + if (isai == NULL || !isai->alive) + return D_NO_SUCH_DEVICE; + + tp = &lpr_tty[unit]; + addr = (u_short) isai->address; + tp->t_dev = dev; + tp->t_addr = (void*) (natural_t) addr; + tp->t_oproc = lprstart; + tp->t_state |= TS_WOPEN; + tp->t_stop = lprstop; + tp->t_getstat = lprgetstat; + tp->t_setstat = lprsetstat; + if ((tp->t_state & TS_ISOPEN) == 0) + ttychars(tp); + outb(INTR_ENAB(addr), inb(INTR_ENAB(addr)) | 0x10); + tp->t_state |= TS_CARR_ON; + return (char_open(dev, tp, flag, ior)); +} + +void +lprclose(dev_t dev, int flag) +{ +int unit = minor(dev); +struct tty *tp = &lpr_tty[unit]; +u_short addr = (u_short) lprinfo[unit]->address; + + ttyclose(tp); + if (tp->t_state&TS_HUPCLS || (tp->t_state&TS_ISOPEN)==0) { + outb(INTR_ENAB(addr), inb(INTR_ENAB(addr)) & 0x0f); + tp->t_state &= ~TS_BUSY; + } +} + +int +lprread(dev_t dev, io_req_t ior) +{ + return char_read(&lpr_tty[minor(dev)], ior); +} + +int +lprwrite(dev_t dev, io_req_t ior) +{ + return char_write(&lpr_tty[minor(dev)], ior); +} + +int +lprportdeath(dev_t dev, mach_port_t port) +{ + return (tty_portdeath(&lpr_tty[minor(dev)], (ipc_port_t)port)); +} + +io_return_t +lprgetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, /* pointer to OUT array */ + mach_msg_type_number_t *count /* out */ + ) +{ + io_return_t result = D_SUCCESS; + int unit = minor(dev); + + switch (flavor) { + default: + result = tty_get_status(&lpr_tty[unit], flavor, data, count); + break; + } + return (result); +} + +io_return_t +lprsetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, + mach_msg_type_number_t count) +{ + io_return_t result = D_SUCCESS; + int unit = minor(dev); + + switch (flavor) { + default: + result = tty_set_status(&lpr_tty[unit], flavor, data, count); +/* if (result == D_SUCCESS && flavor == TTY_STATUS) + lprparam(unit); +*/ return (result); + } + return (D_SUCCESS); +} + +void lprintr(int unit) +{ + struct tty *tp = &lpr_tty[unit]; + + if ((tp->t_state & TS_ISOPEN) == 0) + return; + + tp->t_state &= ~TS_BUSY; + if (tp->t_state&TS_FLUSH) + tp->t_state &=~TS_FLUSH; + tt_write_wakeup(tp); + lprstart(tp); +} + +void lprstart(struct tty *tp) +{ + spl_t s = spltty(); + u_short addr = (natural_t) tp->t_addr; + int status = inb(STATUS(addr)); + int nch; + + if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP|TS_BUSY)) { + splx(s); + return; + } + + if (status & 0x20) { + printf("Printer out of paper!\n"); + splx(s); + return; + } + + if (tp->t_outq.c_cc <= TTLOWAT(tp)) { + tt_write_wakeup(tp); + } + if (tp->t_outq.c_cc == 0) { + splx(s); + return; + } + nch = getc(&tp->t_outq); + if (nch == -1) { + splx(s); + return; + } + if ((tp->t_flags & LITOUT) == 0 && (nch & 0200)) { + timeout((timer_func_t *)ttrstrt, (char *)tp, (nch & 0x7f) + 6); + tp->t_state |= TS_TIMEOUT; + return; + } + outb(DATA(addr), nch); + outb(INTR_ENAB(addr),inb(INTR_ENAB(addr)) | 0x01); + outb(INTR_ENAB(addr),inb(INTR_ENAB(addr)) & 0x1e); + tp->t_state |= TS_BUSY; + splx(s); + return; +} + +void +lprstop( + struct tty *tp, + int flags) +{ + if ((tp->t_state & TS_BUSY) && (tp->t_state & TS_TTSTOP) == 0) + tp->t_state |= TS_FLUSH; +} + +void +lprpr_addr(unsigned short addr) +{ + printf("DATA(%x) %x, STATUS(%x) %x, INTR_ENAB(%x) %x\n", + DATA(addr), inb(DATA(addr)), + STATUS(addr), inb(STATUS(addr)), + INTR_ENAB(addr), inb(INTR_ENAB(addr))); +} +#endif /* NLPR */ diff --git a/i386/i386at/lpr.h b/i386/i386at/lpr.h new file mode 100644 index 0000000..cab3016 --- /dev/null +++ b/i386/i386at/lpr.h @@ -0,0 +1,66 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Parallel port printer driver v1.0 + * All rights reserved. + */ + +#ifndef _LPRREG_H_ +#define _LPRREG_H_ + +#define DATA(addr) (addr + 0) +#define STATUS(addr) (addr + 1) +#define INTR_ENAB(addr) (addr + 2) + +extern void lprintr(int unit); +int lprprobe(vm_offset_t port, struct bus_ctlr *dev); +void lprstop(struct tty *tp, int flags); +void lprstart(struct tty *tp); +void lprattach(struct bus_device *dev); + +extern io_return_t +lprgetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, + mach_msg_type_number_t *count); + +extern io_return_t +lprsetstat( + dev_t dev, + dev_flavor_t flavor, + dev_status_t data, + mach_msg_type_number_t count); + +void lprpr_addr(unsigned short addr); + +extern int lpropen(dev_t dev, int flag, io_req_t ior); +extern void lprclose(dev_t dev, int flag); +extern int lprread(dev_t dev, io_req_t ior); +extern int lprwrite(dev_t dev, io_req_t ior); +extern int lprportdeath(dev_t dev, mach_port_t port); + +#endif /* _LPRREG_H_ */ diff --git a/i386/i386at/mem.c b/i386/i386at/mem.c new file mode 100644 index 0000000..f46fc03 --- /dev/null +++ b/i386/i386at/mem.c @@ -0,0 +1,42 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +#include +#include +#include +#include + +/* This provides access to any memory that is not main RAM */ + +/*ARGSUSED*/ +vm_offset_t +memmmap(dev_t dev, vm_offset_t off, vm_prot_t prot) +{ + if (biosmem_addr_available(off)) + return -1; + + return i386_btop(off); +} diff --git a/i386/i386at/mem.h b/i386/i386at/mem.h new file mode 100644 index 0000000..a5b4aef --- /dev/null +++ b/i386/i386at/mem.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2013 Free Software Foundation. + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _MEM_H_ +#define _MEM_H_ + +extern vm_offset_t memmmap(dev_t dev, vm_offset_t off, vm_prot_t prot); + +#endif /* _MEM_H_ */ diff --git a/i386/i386at/model_dep.c b/i386/i386at/model_dep.c new file mode 100644 index 0000000..edb5b48 --- /dev/null +++ b/i386/i386at/model_dep.c @@ -0,0 +1,674 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989, 1988 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * File: model_dep.c + * Author: Avadis Tevanian, Jr., Michael Wayne Young + * + * Copyright (C) 1986, Avadis Tevanian, Jr., Michael Wayne Young + * + * Basic initialization for I386 - ISA bus machines. + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef MACH_XEN +#include +#include +#include +#include +#endif /* MACH_XEN */ + +#if ENABLE_IMMEDIATE_CONSOLE +#include "immc.h" +#endif /* ENABLE_IMMEDIATE_CONSOLE */ + +/* Location of the kernel's symbol table. + Both of these are 0 if none is available. */ +#if MACH_KDB +#include +#include + +/* ELF section header */ +static unsigned elf_shdr_num; +static vm_size_t elf_shdr_size; +static vm_offset_t elf_shdr_addr; +static unsigned elf_shdr_shndx; + +#endif /* MACH_KDB */ + +#define RESERVED_BIOS 0x10000 + +/* A copy of the multiboot info structure passed by the boot loader. */ +#ifdef MACH_XEN +struct start_info boot_info; +#ifdef MACH_PSEUDO_PHYS +unsigned long *mfn_list; +#if VM_MIN_KERNEL_ADDRESS != LINEAR_MIN_KERNEL_ADDRESS +unsigned long *pfn_list = (void*) PFN_LIST; +#endif +#endif /* MACH_PSEUDO_PHYS */ +#if VM_MIN_KERNEL_ADDRESS != LINEAR_MIN_KERNEL_ADDRESS +unsigned long la_shift = VM_MIN_KERNEL_ADDRESS; +#endif +#else /* MACH_XEN */ +struct multiboot_raw_info boot_info; +#endif /* MACH_XEN */ + +/* Command line supplied to kernel. */ +char *kernel_cmdline = ""; + +extern char version[]; + +/* Realmode temporary GDT */ +extern struct pseudo_descriptor gdt_descr_tmp; + +/* Realmode relocated jmp */ +extern uint32_t apboot_jmp_offset; + +/* If set, reboot the system on ctrl-alt-delete. */ +boolean_t rebootflag = FALSE; /* exported to kdintr */ + +#ifdef LINUX_DEV +extern void linux_init(void); +#endif + +/* + * Find devices. The system is alive. + */ +void machine_init(void) +{ + /* + * Make more free memory. + * + * This is particularly important for the Linux drivers which + * require available DMA memory. + */ + biosmem_free_usable(); + + /* + * Set up to use floating point. + */ + init_fpu(); + +#ifdef MACH_HYP + hyp_init(); +#else /* MACH_HYP */ +#if defined(APIC) + int err; + + err = acpi_apic_init(); + if (err) { + printf("acpi_apic_init failed with %d\n", err); + for (;;); + } +#endif +#if (NCPUS > 1) + smp_init(); +#endif +#if defined(APIC) + ioapic_configure(); +#endif + clkstart(); + + /* + * Initialize the console. + */ + cninit(); + +#ifdef LINUX_DEV + /* + * Initialize Linux drivers. + */ + linux_init(); +#endif + /* + * Find the devices + */ + probeio(); +#endif /* MACH_HYP */ + + /* + * Get the time + */ + inittodr(); + +#ifndef MACH_HYP + /* + * Tell the BIOS not to clear and test memory. + */ + *(unsigned short *)phystokv(0x472) = 0x1234; +#endif /* MACH_HYP */ + +#if VM_MIN_KERNEL_ADDRESS == 0 + /* + * Unmap page 0 to trap NULL references. + * + * Note that this breaks accessing some BIOS areas stored there. + */ + pmap_unmap_page_zero(); +#endif + +#if NCPUS > 1 + /* + * Patch the realmode gdt with the correct offset and the first jmp to + * protected mode with the correct target. + */ + gdt_descr_tmp.linear_base += apboot_addr; + apboot_jmp_offset += apboot_addr; + + /* + * Initialize the HPET + */ + hpet_init(); +#endif +} + +/* Conserve power on processor CPU. */ +void machine_idle (int cpu) +{ +#ifdef MACH_HYP + hyp_idle(); +#else /* MACH_HYP */ + assert (cpu == cpu_number ()); + asm volatile ("hlt" : : : "memory"); +#endif /* MACH_HYP */ +} + +void machine_relax (void) +{ + asm volatile ("rep; nop" : : : "memory"); +} + +/* + * Halt a cpu. + */ +void halt_cpu(void) +{ +#ifdef MACH_HYP + hyp_halt(); +#else /* MACH_HYP */ + asm volatile("cli"); + while (TRUE) + machine_idle (cpu_number ()); +#endif /* MACH_HYP */ +} + +/* + * Halt the system or reboot. + */ +void halt_all_cpus(boolean_t reboot) +{ + if (reboot) { +#ifdef MACH_HYP + hyp_reboot(); +#endif /* MACH_HYP */ + kdreboot(); + } + else { + rebootflag = TRUE; +#ifdef MACH_HYP + hyp_halt(); +#endif /* MACH_HYP */ + printf("Shutdown completed successfully, now in tight loop.\n"); + printf("You can safely power off the system or hit ctl-alt-del to reboot\n"); + (void) spl0(); + } + while (TRUE) + machine_idle (cpu_number ()); +} + +void db_halt_cpu(void) +{ + halt_all_cpus(0); +} + +void db_reset_cpu(void) +{ + halt_all_cpus(1); +} + +#ifndef MACH_HYP + +static void +register_boot_data(const struct multiboot_raw_info *mbi) +{ + struct multiboot_raw_module *mod; + struct elf_shdr *shdr; + unsigned long tmp; + unsigned int i; + + extern char _start[], _end[]; + + biosmem_register_boot_data(_kvtophys(&_start), _kvtophys(&_end), FALSE); + + /* cmdline and modules are moved to a safe place by i386at_init. */ + + if ((mbi->flags & MULTIBOOT_LOADER_CMDLINE) && (mbi->cmdline != 0)) { + biosmem_register_boot_data(mbi->cmdline, + mbi->cmdline + + strlen((void *)phystokv(mbi->cmdline)) + 1, TRUE); + } + + if (mbi->flags & MULTIBOOT_LOADER_MODULES && mbi->mods_count) { + i = mbi->mods_count * sizeof(struct multiboot_raw_module); + biosmem_register_boot_data(mbi->mods_addr, mbi->mods_addr + i, TRUE); + + tmp = phystokv(mbi->mods_addr); + + for (i = 0; i < mbi->mods_count; i++) { + mod = (struct multiboot_raw_module *)tmp + i; + if (mod->mod_end != mod->mod_start) + biosmem_register_boot_data(mod->mod_start, mod->mod_end, TRUE); + + if (mod->string != 0) { + biosmem_register_boot_data(mod->string, + mod->string + + strlen((void *)phystokv(mod->string)) + 1, + TRUE); + } + } + } + + if (mbi->flags & MULTIBOOT_LOADER_SHDR) { + tmp = mbi->shdr_num * mbi->shdr_size; + if (tmp != 0) + biosmem_register_boot_data(mbi->shdr_addr, mbi->shdr_addr + tmp, FALSE); + + tmp = phystokv(mbi->shdr_addr); + + for (i = 0; i < mbi->shdr_num; i++) { + shdr = (struct elf_shdr *)(tmp + (i * mbi->shdr_size)); + + if ((shdr->type != ELF_SHT_SYMTAB) + && (shdr->type != ELF_SHT_STRTAB)) + continue; + + if (shdr->size != 0) + biosmem_register_boot_data(shdr->addr, shdr->addr + shdr->size, FALSE); + } + } +} + +#endif /* MACH_HYP */ + +/* + * Basic PC VM initialization. + * Turns on paging and changes the kernel segments to use high linear addresses. + */ +static void +i386at_init(void) +{ + /* + * Initialize the PIC prior to any possible call to an spl. + */ +#ifndef MACH_HYP +# ifdef APIC + picdisable(); +# else + picinit(); +# endif +#else /* MACH_HYP */ + hyp_intrinit(); +#endif /* MACH_HYP */ + spl_init = 1; + + /* + * Read memory map and load it into the physical page allocator. + */ +#ifdef MACH_HYP + biosmem_xen_bootstrap(); +#else /* MACH_HYP */ + register_boot_data((struct multiboot_raw_info *) &boot_info); + biosmem_bootstrap((struct multiboot_raw_info *) &boot_info); +#endif /* MACH_HYP */ + +#ifdef MACH_XEN + kernel_cmdline = (char*) boot_info.cmd_line; +#else /* MACH_XEN */ + vm_offset_t addr; + + /* Copy content pointed by boot_info before losing access to it when it + * is too far in physical memory. + * Also avoids leaving them in precious areas such as DMA memory. */ + if (boot_info.flags & MULTIBOOT_CMDLINE) { + int len = strlen ((char*)phystokv(boot_info.cmdline)) + 1; + if (! init_alloc_aligned(round_page(len), &addr)) + panic("could not allocate memory for multiboot command line"); + kernel_cmdline = (char*) phystokv(addr); + memcpy(kernel_cmdline, (void *)phystokv(boot_info.cmdline), len); + boot_info.cmdline = addr; + } + + if (boot_info.flags & MULTIBOOT_MODS && boot_info.mods_count) { + struct multiboot_raw_module *m; + int i; + + if (! init_alloc_aligned( + round_page(boot_info.mods_count * sizeof(*m)), &addr)) + panic("could not allocate memory for multiboot modules"); + m = (void*) phystokv(addr); + memcpy(m, (void*) phystokv(boot_info.mods_addr), boot_info.mods_count * sizeof(*m)); + boot_info.mods_addr = addr; + + for (i = 0; i < boot_info.mods_count; i++) { + vm_size_t size = m[i].mod_end - m[i].mod_start; + if (! init_alloc_aligned(round_page(size), &addr)) + panic("could not allocate memory for multiboot " + "module %d", i); + memcpy((void*) phystokv(addr), (void*) phystokv(m[i].mod_start), size); + m[i].mod_start = addr; + m[i].mod_end = addr + size; + + size = strlen((char*) phystokv(m[i].string)) + 1; + if (! init_alloc_aligned(round_page(size), &addr)) + panic("could not allocate memory for multiboot " + "module command line %d", i); + memcpy((void*) phystokv(addr), (void*) phystokv(m[i].string), size); + m[i].string = addr; + } + } +#endif /* MACH_XEN */ + + /* + * Initialize kernel physical map, mapping the + * region from loadpt to avail_start. + * Kernel virtual address starts at VM_KERNEL_MIN_ADDRESS. + * XXX make the BIOS page (page 0) read-only. + */ + pmap_bootstrap(); + + /* + * Load physical segments into the VM system. + * The early allocation functions become unusable after + * this point. + */ + biosmem_setup(); + + pmap_make_temporary_mapping(); + +#ifndef MACH_HYP + /* Turn paging on. + * Also set the WP bit so that on 486 or better processors + * page-level write protection works in kernel mode. + */ + set_cr0(get_cr0() | CR0_PG | CR0_WP); + set_cr0(get_cr0() & ~(CR0_CD | CR0_NW)); + if (CPU_HAS_FEATURE(CPU_FEATURE_PGE)) + set_cr4(get_cr4() | CR4_PGE); +#endif /* MACH_HYP */ + flush_instr_queue(); +#ifdef MACH_PV_PAGETABLES + pmap_clear_bootstrap_pagetable((void *)boot_info.pt_base); +#endif /* MACH_PV_PAGETABLES */ + + /* + * Initialize and activate the real i386 protected-mode structures. + */ + gdt_init(); + idt_init(); +#ifndef MACH_HYP + int_init(); +#endif /* MACH_HYP */ + ldt_init(); + ktss_init(); + +#ifndef MACH_XEN + init_percpu(0); +#endif +#if NCPUS > 1 + /* Initialize SMP structures in the master processor */ + mp_desc_init(0); +#endif // NCPUS + + pmap_remove_temporary_mapping(); + +#ifdef MACH_XEN + hyp_p2m_init(); +#endif /* MACH_XEN */ + + interrupt_stack_alloc(); +} + +/* + * C boot entrypoint - called by boot_entry in boothdr.S. + * Running in flat mode, but without paging yet. + */ +void c_boot_entry(vm_offset_t bi) +{ +#if ENABLE_IMMEDIATE_CONSOLE + romputc = immc_romputc; +#endif /* ENABLE_IMMEDIATE_CONSOLE */ + + /* Stash the boot_image_info pointer. */ + boot_info = *(typeof(boot_info)*)phystokv(bi); + int cpu_type; + + /* Before we do _anything_ else, print the hello message. + If there are no initialized console devices yet, + it will be stored and printed at the first opportunity. */ + printf("%s", version); + printf("\n"); + +#ifdef MACH_XEN + printf("Running on %s.\n", boot_info.magic); + if (boot_info.flags & SIF_PRIVILEGED) + panic("Mach can't run as dom0."); +#ifdef MACH_PSEUDO_PHYS + mfn_list = (void*)boot_info.mfn_list; +#endif +#else /* MACH_XEN */ + +#if MACH_KDB + /* + * Locate the kernel's symbol table, if the boot loader provided it. + * We need to do this before i386at_init() + * so that the symbol table's memory won't be stomped on. + */ + if ((boot_info.flags & MULTIBOOT_ELF_SHDR) + && boot_info.shdr_num) + { + elf_shdr_num = boot_info.shdr_num; + elf_shdr_size = boot_info.shdr_size; + elf_shdr_addr = (vm_offset_t)phystokv(boot_info.shdr_addr); + elf_shdr_shndx = boot_info.shdr_strndx; + + printf("ELF section header table at %08" PRIxPTR "\n", elf_shdr_addr); + } +#endif /* MACH_KDB */ +#endif /* MACH_XEN */ + + cpu_type = discover_x86_cpu_type (); + + /* + * Do basic VM initialization + */ + i386at_init(); + +#if MACH_KDB + /* + * Initialize the kernel debugger's kernel symbol table. + */ + if (elf_shdr_num) + { + elf_db_sym_init(elf_shdr_num,elf_shdr_size, + elf_shdr_addr, elf_shdr_shndx, + "mach", NULL); + } +#endif /* MACH_KDB */ + + machine_slot[0].is_cpu = TRUE; + machine_slot[0].cpu_subtype = CPU_SUBTYPE_AT386; + +#if defined(__x86_64__) && !defined(USER32) + machine_slot[0].cpu_type = CPU_TYPE_X86_64; +#else + switch (cpu_type) + { + default: + printf("warning: unknown cpu type %d, assuming i386\n", cpu_type); + case 3: + machine_slot[0].cpu_type = CPU_TYPE_I386; + break; + case 4: + machine_slot[0].cpu_type = CPU_TYPE_I486; + break; + case 5: + machine_slot[0].cpu_type = CPU_TYPE_PENTIUM; + break; + case 6: + case 15: + machine_slot[0].cpu_type = CPU_TYPE_PENTIUMPRO; + break; + } +#endif + + /* + * Start the system. + */ + setup_main(); + +} + +#include +#include +#include + +vm_offset_t +timemmap(dev_t dev, vm_offset_t off, vm_prot_t prot) +{ + extern time_value_t *mtime; + + if (prot & VM_PROT_WRITE) return (-1); + + return (i386_btop(pmap_extract(pmap_kernel(), (vm_offset_t) mtime))); +} + +void +startrtclock(void) +{ +#ifdef APIC + unmask_irq(timer_pin); + calibrate_lapic_timer(); + if (cpu_number() != 0) { + lapic_enable_timer(); + } +#else + clkstart(); +#ifndef MACH_HYP + unmask_irq(0); +#endif +#endif +} + +void +inittodr(void) +{ + time_value64_t new_time; + uint64_t newsecs; + + (void) readtodc(&newsecs); + new_time.seconds = newsecs; + new_time.nanoseconds = 0; + + { + spl_t s = splhigh(); + time = new_time; + splx(s); + } +} + +void +resettodr(void) +{ + writetodc(); +} + +boolean_t +init_alloc_aligned(vm_size_t size, vm_offset_t *addrp) +{ + *addrp = biosmem_bootalloc(vm_page_atop(vm_page_round(size))); + + if (*addrp == 0) + return FALSE; + + return TRUE; +} + +/* Grab a physical page: + the standard memory allocation mechanism + during system initialization. */ +vm_offset_t +pmap_grab_page(void) +{ + vm_offset_t addr; + if (!init_alloc_aligned(PAGE_SIZE, &addr)) + panic("Not enough memory to initialize Mach"); + return addr; +} diff --git a/i386/i386at/model_dep.h b/i386/i386at/model_dep.h new file mode 100644 index 0000000..3d5b664 --- /dev/null +++ b/i386/i386at/model_dep.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2013 Free Software Foundation. + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _MODEL_DEP_H_ +#define _MODEL_DEP_H_ + +#include +#include + +/* + * Interrupt stack. + */ +extern vm_offset_t int_stack_top[NCPUS], int_stack_base[NCPUS]; + +/* Check whether P points to the per-cpu interrupt stack. */ +#define ON_INT_STACK(P, CPU) (((P) & ~(INTSTACK_SIZE-1)) == int_stack_base[CPU]) + +extern vm_offset_t timemmap(dev_t dev, vm_offset_t off, vm_prot_t prot); + +void inittodr(void); + +boolean_t init_alloc_aligned(vm_size_t size, vm_offset_t *addrp); + +#endif /* _MODEL_DEP_H_ */ diff --git a/i386/i386at/pic_isa.c b/i386/i386at/pic_isa.c new file mode 100644 index 0000000..1e5ac10 --- /dev/null +++ b/i386/i386at/pic_isa.c @@ -0,0 +1,56 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +#include +#include +#include +#include +#include +#include + +/* These interrupts are always present */ + +interrupt_handler_fn ivect[NINTR] = { + /* 00 */ (interrupt_handler_fn)hardclock, /* always */ + /* 01 */ kdintr, /* kdintr, ... */ + /* 02 */ intnull, + /* 03 */ intnull, /* lnpoll, comintr, ... */ + + /* 04 */ intnull, /* comintr, ... */ + /* 05 */ intnull, /* comintr, wtintr, ... */ + /* 06 */ intnull, /* fdintr, ... */ + /* 07 */ intnull, /* qdintr, ... */ + + /* 08 */ intnull, + /* 09 */ intnull, /* ether */ + /* 10 */ intnull, + /* 11 */ intnull, + + /* 12 */ intnull, + /* 13 */ fpintr, /* always */ + /* 14 */ intnull, /* hdintr, ... */ + /* 15 */ intnull, /* ??? */ +}; diff --git a/i386/i386at/rtc.c b/i386/i386at/rtc.c new file mode 100644 index 0000000..1930beb --- /dev/null +++ b/i386/i386at/rtc.c @@ -0,0 +1,242 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* + Copyright 1988, 1989 by Intel Corporation, Santa Clara, California. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appears in all +copies and that both the copyright notice and this permission notice +appear in supporting documentation, and that the name of Intel +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + +INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, +NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include + +/* time of day stored in RTC are currently between 1970 and 2070. Update that + * before 2070 please. */ +#define CENTURY_START 1970 + +static boolean_t first_rtcopen_ever = TRUE; + +static void +rtcinit(void) +{ + outb(RTC_ADDR, RTC_A); + outb(RTC_DATA, RTC_DIV2 | RTC_RATE6); + outb(RTC_ADDR, RTC_B); + outb(RTC_DATA, RTC_HM); +} + + +static int +rtcget(struct rtc_st *st) +{ + unsigned char *regs = (unsigned char *)st; + if (first_rtcopen_ever) { + rtcinit(); + first_rtcopen_ever = FALSE; + } + outb(RTC_ADDR, RTC_D); + if ((inb(RTC_DATA) & RTC_VRT) == 0) return(-1); + outb(RTC_ADDR, RTC_A); + while (inb(RTC_DATA) & RTC_UIP) /* busy wait */ + outb(RTC_ADDR, RTC_A); + load_rtc(regs); + return(0); +} + +static void +rtcput(struct rtc_st *st) +{ + unsigned char *regs = (unsigned char *)st; + unsigned char x; + + if (first_rtcopen_ever) { + rtcinit(); + first_rtcopen_ever = FALSE; + } + outb(RTC_ADDR, RTC_B); + x = inb(RTC_DATA); + outb(RTC_ADDR, RTC_B); + outb(RTC_DATA, x | RTC_SET); + save_rtc(regs); + outb(RTC_ADDR, RTC_B); + outb(RTC_DATA, x & ~RTC_SET); +} + + +static int month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +static int +yeartoday(int year) +{ + if (year%4) + /* Not divisible by 4, not bissextile */ + return 365; + + /* Divisible by 4 */ + if (year % 100) + /* Not divisible by 100, bissextile */ + return 366; + + /* Divisible by 100 */ + if (year % 400) + /* Not divisible by 400, not bissextile */ + return 365; + + /* Divisible by 400 */ + /* Rules for 2000 and further have not been officially decided yet. + * 2000 was made bissextile. */ + return 366; +} + +static int +hexdectodec(char n) +{ + return(((n>>4)&0x0F)*10 + (n&0x0F)); +} + +static char +dectohexdec(int n) +{ + return((char)(((n/10)<<4)&0xF0) | ((n%10)&0x0F)); +} + +int +readtodc(uint64_t *tp) +{ + struct rtc_st rtclk; + time_t n; + int sec, min, hr, dom, mon, yr; + int i, days = 0; + spl_t ospl; + + ospl = splclock(); + if (rtcget(&rtclk)) { + splx(ospl); + return(-1); + } + splx (ospl); + + sec = hexdectodec(rtclk.rtc_sec); + min = hexdectodec(rtclk.rtc_min); + hr = hexdectodec(rtclk.rtc_hr); + dom = hexdectodec(rtclk.rtc_dom); + mon = hexdectodec(rtclk.rtc_mon); + yr = hexdectodec(rtclk.rtc_yr); + yr = (yr < CENTURY_START%100) ? + yr+CENTURY_START-CENTURY_START%100+100 : + yr+CENTURY_START-CENTURY_START%100; + + if (yr >= CENTURY_START+90) { + printf("FIXME: we are approaching %u, update CENTURY_START\n", CENTURY_START); + } + + printf("RTC time is %04u-%02u-%02u %02u:%02u:%02u\n", yr, mon, dom, hr, min, sec); + + n = sec + 60 * min + 3600 * hr; + n += (dom - 1) * 3600 * 24; + + if (yeartoday(yr) == 366) + month[1] = 29; + for (i = mon - 2; i >= 0; i--) + days += month[i]; + month[1] = 28; + /* Epoch shall be 1970 January 1st */ + for (i = 1970; i < yr; i++) + days += yeartoday(i); + n += days * 3600 * 24; + + + *tp = n; + + return(0); +} + +int +writetodc(void) +{ + struct rtc_st rtclk; + time_t n; + int diff, i, j; + spl_t ospl; + + ospl = splclock(); + if (rtcget(&rtclk)) { + splx(ospl); + return(-1); + } + splx(ospl); + + diff = 0; + n = (time.seconds - diff) % (3600 * 24); /* hrs+mins+secs */ + rtclk.rtc_sec = dectohexdec(n%60); + n /= 60; + rtclk.rtc_min = dectohexdec(n%60); + rtclk.rtc_hr = dectohexdec(n/60); + + n = (time.seconds - diff) / (3600 * 24); /* days */ + rtclk.rtc_dow = (n + 4) % 7; /* 1/1/70 is Thursday */ + + /* Epoch shall be 1970 January 1st */ + for (j = 1970, i = yeartoday(j); n >= i; j++, i = yeartoday(j)) + n -= i; + + rtclk.rtc_yr = dectohexdec(j % 100); + + if (i == 366) + month[1] = 29; + for (i = 0; n >= month[i]; i++) + n -= month[i]; + month[1] = 28; + rtclk.rtc_mon = dectohexdec(++i); + + rtclk.rtc_dom = dectohexdec(++n); + + ospl = splclock(); + rtcput(&rtclk); + splx(ospl); + + return(0); +} diff --git a/i386/i386at/rtc.h b/i386/i386at/rtc.h new file mode 100644 index 0000000..5379722 --- /dev/null +++ b/i386/i386at/rtc.h @@ -0,0 +1,143 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + Copyright 1988, 1989 by Intel Corporation, Santa Clara, California. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appears in all +copies and that both the copyright notice and this permission notice +appear in supporting documentation, and that the name of Intel +not be used in advertising or publicity pertaining to distribution +of the software without specific, written prior permission. + +INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, +IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, +NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION +WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef _RTC_H_ +#define _RTC_H_ + +#define RTC_ADDR 0x70 /* I/O port address for register select */ +#define RTC_DATA 0x71 /* I/O port address for data read/write */ + +/* + * Register A definitions + */ +#define RTC_A 0x0a /* register A address */ +#define RTC_UIP 0x80 /* Update in progress bit */ +#define RTC_DIV0 0x00 /* Time base of 4.194304 MHz */ +#define RTC_DIV1 0x10 /* Time base of 1.048576 MHz */ +#define RTC_DIV2 0x20 /* Time base of 32.768 KHz */ +#define RTC_RATE6 0x06 /* interrupt rate of 976.562 */ + +/* + * Register B definitions + */ +#define RTC_B 0x0b /* register B address */ +#define RTC_SET 0x80 /* stop updates for time set */ +#define RTC_PIE 0x40 /* Periodic interrupt enable */ +#define RTC_AIE 0x20 /* Alarm interrupt enable */ +#define RTC_UIE 0x10 /* Update ended interrupt enable */ +#define RTC_SQWE 0x08 /* Square wave enable */ +#define RTC_DM 0x04 /* Date mode, 1 = binary, 0 = BCD */ +#define RTC_HM 0x02 /* hour mode, 1 = 24 hour, 0 = 12 hour */ +#define RTC_DSE 0x01 /* Daylight savings enable */ + +/* + * Register C definitions + */ +#define RTC_C 0x0c /* register C address */ +#define RTC_IRQF 0x80 /* IRQ flag */ +#define RTC_PF 0x40 /* PF flag bit */ +#define RTC_AF 0x20 /* AF flag bit */ +#define RTC_UF 0x10 /* UF flag bit */ + +/* + * Register D definitions + */ +#define RTC_D 0x0d /* register D address */ +#define RTC_VRT 0x80 /* Valid RAM and time bit */ + +#define RTC_NREG 0x0e /* number of RTC registers */ +#define RTC_NREGP 0x0a /* number of RTC registers to set time */ + +#define RTCRTIME _IOR('c', 0x01, struct rtc_st) /* Read time from RTC */ +#define RTCSTIME _IOW('c', 0x02, struct rtc_st) /* Set time into RTC */ + +struct rtc_st { + char rtc_sec; + char rtc_asec; + char rtc_min; + char rtc_amin; + char rtc_hr; + char rtc_ahr; + char rtc_dow; + char rtc_dom; + char rtc_mon; + char rtc_yr; + char rtc_statusa; + char rtc_statusb; + char rtc_statusc; + char rtc_statusd; +}; + +/* + * this macro reads contents of real time clock to specified buffer + */ +#define load_rtc(regs) \ +{\ + int i; \ + \ + for (i = 0; i < RTC_NREG; i++) { \ + outb(RTC_ADDR, i); \ + regs[i] = inb(RTC_DATA); \ + } \ +} + +/* + * this macro writes contents of specified buffer to real time clock + */ +#define save_rtc(regs) \ +{ \ + int i; \ + for (i = 0; i < RTC_NREGP; i++) { \ + outb(RTC_ADDR, i); \ + outb(RTC_DATA, regs[i]);\ + } \ +} + +extern int readtodc(uint64_t *tp); +extern int writetodc(void); + +#endif /* _RTC_H_ */ -- cgit v1.2.1